Updating tests, Exception handling cleanup.
This commit is contained in:
parent
5052a94cf7
commit
4ce3b8da6a
|
|
@ -9,43 +9,43 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
public class ParserContext
|
||||
{
|
||||
private readonly string _template;
|
||||
private int _index;
|
||||
public int Index { get; set; }
|
||||
private int? _mark;
|
||||
|
||||
public ParserContext(string condition)
|
||||
{
|
||||
_template = condition;
|
||||
_index = -1;
|
||||
Index = -1;
|
||||
}
|
||||
|
||||
public char Current
|
||||
{
|
||||
get { return (_index < _template.Length && _index >= 0) ? _template[_index] : (char)0; }
|
||||
get { return (Index < _template.Length && Index >= 0) ? _template[Index] : (char)0; }
|
||||
}
|
||||
|
||||
public bool Back()
|
||||
{
|
||||
return --_index >= 0;
|
||||
return --Index >= 0;
|
||||
}
|
||||
|
||||
public bool Next()
|
||||
{
|
||||
return ++_index < _template.Length;
|
||||
return ++Index < _template.Length;
|
||||
}
|
||||
|
||||
public bool HasNext()
|
||||
{
|
||||
return (_index + 1) < _template.Length;
|
||||
return (Index + 1) < _template.Length;
|
||||
}
|
||||
|
||||
public void Mark()
|
||||
{
|
||||
_mark = _index;
|
||||
_mark = Index;
|
||||
}
|
||||
|
||||
public int GetIndex()
|
||||
{
|
||||
return _index;
|
||||
return Index;
|
||||
}
|
||||
|
||||
public string Capture()
|
||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
// TODO make this return a range rather than a string.
|
||||
if (_mark.HasValue)
|
||||
{
|
||||
var value = _template.Substring(_mark.Value, _index - _mark.Value);
|
||||
var value = _template.Substring(_mark.Value, Index - _mark.Value);
|
||||
_mark = null;
|
||||
return value;
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
}
|
||||
public string Error()
|
||||
{
|
||||
return string.Format("Syntax Error at index: ", _index, " with character: ", Current);
|
||||
return string.Format("Syntax Error at index: ", Index, " with character: ", Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
// This is a server parameter, parse for a condition variable
|
||||
if (!context.Next())
|
||||
{
|
||||
throw new FormatException(context.Error());
|
||||
// missing {
|
||||
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||
}
|
||||
ParseParameter(context, results);
|
||||
}
|
||||
|
|
@ -100,10 +101,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
// has processed the new string.
|
||||
if (context.Current != CloseBrace)
|
||||
{
|
||||
throw new FormatException("Lacking close brace for parameter at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "UrlDecode":
|
||||
{
|
||||
throw new NotImplementedException("UrlDecode is not supported.");
|
||||
|
|
@ -115,10 +116,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
|
||||
if (context.Current != CloseBrace)
|
||||
{
|
||||
throw new FormatException("Lacking close brace for parameter at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "R":
|
||||
{
|
||||
var index = GetBackReferenceIndex(context);
|
||||
|
|
@ -132,17 +133,18 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
return;
|
||||
}
|
||||
default:
|
||||
throw new FormatException("Unrecognized parameter type: " + parameter + ", terminated at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(parameter, context.Index));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||
}
|
||||
|
||||
private static int GetBackReferenceIndex(ParserContext context)
|
||||
{
|
||||
if (!context.Next())
|
||||
{
|
||||
throw new FormatException("No index avaible for backreference at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
|
||||
}
|
||||
|
||||
context.Mark();
|
||||
|
|
@ -150,7 +152,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
{
|
||||
if (!context.Next())
|
||||
{
|
||||
throw new FormatException("Lacking close brace for parameter at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,12 +160,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
int index;
|
||||
if (!int.TryParse(res, out index))
|
||||
{
|
||||
throw new FormatException("Syntax error, invalid integer in response parameter at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserInvalidInteger(res, context.Index));
|
||||
}
|
||||
|
||||
if (index > 9 || index < 0)
|
||||
{
|
||||
throw new FormatException("Invalid integer into backreference " + index + "at index: " + context.GetIndex());
|
||||
throw new FormatException(Resources.FormatError_InputParserIndexOutOfRange(res, context.Index));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
return new HeaderSegment(HeaderNames.Cookie);
|
||||
case "HTTP_HOST":
|
||||
return new HeaderSegment(HeaderNames.Host);
|
||||
case "HTTP_PROXY_CONNECTION":
|
||||
return new HeaderSegment(HeaderNames.ProxyAuthenticate);
|
||||
case "HTTP_REFERER":
|
||||
return new HeaderSegment(HeaderNames.Referer);
|
||||
case "HTTP_USER_AGENT":
|
||||
|
|
@ -42,6 +40,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
return new IsHttpsSegment();
|
||||
case "LOCAL_ADDR":
|
||||
return new LocalAddressSegment();
|
||||
case "HTTP_PROXY_CONNECTION":
|
||||
throw new NotSupportedException("Proxy Connections not supported");
|
||||
case "QUERY_STRING":
|
||||
return new QueryStringSegment();
|
||||
case "REMOTE_ADDR":
|
||||
|
|
@ -52,6 +52,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
return new RemotePortSegment();
|
||||
case "REQUEST_FILENAME":
|
||||
return new RequestFileNameSegment();
|
||||
case "REQUEST_URI":
|
||||
return new UrlSegment();
|
||||
default:
|
||||
throw new FormatException("Unrecognized server variable.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
|||
{
|
||||
public class RewriteAction : UrlAction
|
||||
{
|
||||
private readonly string ForwardSlash = "/";
|
||||
public RuleTerminiation Result { get; }
|
||||
public bool ClearQuery { get; }
|
||||
|
||||
|
|
@ -46,12 +47,27 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
|||
var split = pattern.IndexOf('?');
|
||||
if (split >= 0)
|
||||
{
|
||||
context.Request.Path = new PathString("/" + pattern.Substring(0, split));
|
||||
var path = pattern.Substring(0, split);
|
||||
if (path.StartsWith(ForwardSlash))
|
||||
{
|
||||
context.Request.Path = new PathString(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Request.Path = new PathString(ForwardSlash + path);
|
||||
}
|
||||
context.Request.QueryString = context.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Request.Path = new PathString("/" + pattern);
|
||||
if (pattern.StartsWith(ForwardSlash))
|
||||
{
|
||||
context.Request.Path = new PathString(pattern);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Request.Path = new PathString(ForwardSlash + pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new RuleResult { Result = Result };
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
|
||||
|
|
@ -18,8 +19,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
|
||||
public static List<UrlRewriteRule> Parse(TextReader reader)
|
||||
{
|
||||
var temp = XDocument.Load(reader);
|
||||
var xmlRoot = temp.Descendants(RewriteTags.Rewrite).FirstOrDefault();
|
||||
var xmlDoc = XDocument.Load(reader, LoadOptions.SetLineInfo);
|
||||
var xmlRoot = xmlDoc.Descendants(RewriteTags.Rewrite).FirstOrDefault();
|
||||
|
||||
if (xmlRoot != null)
|
||||
{
|
||||
|
|
@ -44,7 +45,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
{
|
||||
var res = new UrlRewriteRule();
|
||||
SetRuleAttributes(rule, res);
|
||||
CreateUrlAction(rule.Element(RewriteTags.Action), res, isGlobalRule);
|
||||
var action = rule.Element(RewriteTags.Action);
|
||||
if (action == null)
|
||||
{
|
||||
ThrowUrlFormatException(rule, "Rule does not have an associated action attribute");
|
||||
}
|
||||
CreateUrlAction(action, res, isGlobalRule);
|
||||
if (res.Enabled)
|
||||
{
|
||||
result.Add(res);
|
||||
|
|
@ -74,18 +80,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
{
|
||||
res.StopProcessing = stopProcessing;
|
||||
}
|
||||
|
||||
CreateMatch(rule.Element(RewriteTags.Match), res);
|
||||
var match = rule.Element(RewriteTags.Match);
|
||||
if (match == null)
|
||||
{
|
||||
ThrowUrlFormatException(rule, "Cannot have rule without match");
|
||||
}
|
||||
CreateMatch(match, res);
|
||||
CreateConditions(rule.Element(RewriteTags.Conditions), res);
|
||||
}
|
||||
|
||||
private static void CreateMatch(XElement match, UrlRewriteRule res)
|
||||
{
|
||||
if (match == null)
|
||||
{
|
||||
throw new FormatException("Rules must have an associated match.");
|
||||
}
|
||||
|
||||
var matchRes = new ParsedUrlMatch();
|
||||
|
||||
bool parBool;
|
||||
|
|
@ -100,6 +105,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
}
|
||||
|
||||
var parsedInputString = match.Attribute(RewriteTags.Url)?.Value;
|
||||
if (parsedInputString == null)
|
||||
{
|
||||
ThrowUrlFormatException(match, "Match must have Url Attribute");
|
||||
}
|
||||
|
||||
switch (res.PatternSyntax)
|
||||
{
|
||||
|
|
@ -178,10 +187,18 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
var parsedString = condition.Attribute(RewriteTags.Input)?.Value;
|
||||
if (parsedString == null)
|
||||
{
|
||||
throw new FormatException("Null input for condition");
|
||||
ThrowUrlFormatException(condition, "Conditions must have an input attribute");
|
||||
}
|
||||
|
||||
var input = InputParser.ParseInputString(parsedString);
|
||||
Pattern input = null;
|
||||
try
|
||||
{
|
||||
input = InputParser.ParseInputString(parsedString);
|
||||
}
|
||||
catch (FormatException fe)
|
||||
{
|
||||
ThrowUrlFormatException(condition, fe.Message, fe);
|
||||
}
|
||||
|
||||
switch (res.PatternSyntax)
|
||||
{
|
||||
|
|
@ -194,7 +211,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
parsedString = condition.Attribute(RewriteTags.Pattern)?.Value;
|
||||
if (parsedString == null)
|
||||
{
|
||||
throw new FormatException("Pattern match does not have an associated pattern attribute in condition.");
|
||||
ThrowUrlFormatException(condition, "Match does not have an associated pattern attribute in condition");
|
||||
|
||||
}
|
||||
Regex regex = null;
|
||||
|
||||
|
|
@ -221,32 +239,30 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
}
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unrecognized matchType.");
|
||||
// TODO don't this this can ever be thrown
|
||||
ThrowUrlFormatException(condition, "Unrecognized matchType");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case PatternSyntax.WildCard:
|
||||
throw new NotImplementedException("Wildcard syntax is not supported.");
|
||||
throw new NotImplementedException("Wildcard syntax is not supported");
|
||||
case PatternSyntax.ExactMatch:
|
||||
parsedString = condition.Attribute(RewriteTags.Pattern)?.Value;
|
||||
if (parsedString == null)
|
||||
{
|
||||
throw new FormatException("Pattern match does not have an associated pattern attribute in condition.");
|
||||
ThrowUrlFormatException(condition, "Pattern match does not have an associated pattern attribute in condition");
|
||||
}
|
||||
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new ExactMatch(parsedCondRes.IgnoreCase, parsedString, parsedCondRes.Negate) });
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unrecognized pattern syntax.");
|
||||
ThrowUrlFormatException(condition, "Unrecognized pattern syntax");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateUrlAction(XElement urlAction, UrlRewriteRule res, bool globalRule)
|
||||
{
|
||||
if (urlAction == null)
|
||||
{
|
||||
throw new FormatException("Action is a required element of a rule.");
|
||||
}
|
||||
|
||||
var actionRes = new ParsedUrlAction();
|
||||
|
||||
|
|
@ -273,12 +289,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
actionRes.RedirectType = redirectType;
|
||||
}
|
||||
|
||||
actionRes.Url = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
|
||||
try
|
||||
{
|
||||
actionRes.Url = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
|
||||
}
|
||||
catch (FormatException fe)
|
||||
{
|
||||
ThrowUrlFormatException(urlAction, fe.Message, fe);
|
||||
}
|
||||
|
||||
CreateUrlActionFromParsedAction(actionRes, globalRule, res);
|
||||
CreateUrlActionFromParsedAction(urlAction, actionRes, globalRule, res);
|
||||
}
|
||||
|
||||
public static void CreateUrlActionFromParsedAction(ParsedUrlAction actionRes, bool globalRule, UrlRewriteRule res)
|
||||
private static void CreateUrlActionFromParsedAction(XElement urlAction, ParsedUrlAction actionRes, bool globalRule, UrlRewriteRule res)
|
||||
{
|
||||
switch (actionRes.Type)
|
||||
{
|
||||
|
|
@ -306,11 +329,26 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
}
|
||||
break;
|
||||
case ActionType.AbortRequest:
|
||||
throw new FormatException("Abort requests are not supported.");
|
||||
ThrowUrlFormatException(urlAction, "Abort Requests are not supported.");
|
||||
break;
|
||||
case ActionType.CustomResponse:
|
||||
// TODO
|
||||
throw new FormatException("Custom Responses are not supported");
|
||||
ThrowUrlFormatException(urlAction, "Custom Responses are not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowUrlFormatException(XElement element, string message)
|
||||
{
|
||||
var line = ((IXmlLineInfo)element).LineNumber;
|
||||
var col = ((IXmlLineInfo)element).LinePosition;
|
||||
throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col));
|
||||
}
|
||||
private static void ThrowUrlFormatException(XElement element, string message, Exception ex)
|
||||
{
|
||||
var line = ((IXmlLineInfo)element).LineNumber;
|
||||
var col = ((IXmlLineInfo)element).LinePosition;
|
||||
throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNetCore.Rewrite
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNetCore.Rewrite.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.
|
||||
/// </summary>
|
||||
internal static string Error_UrlRewriteParseError
|
||||
{
|
||||
get { return GetString("Error_UrlRewriteParseError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatError_UrlRewriteParseError(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_UrlRewriteParseError"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index out of range for backreference: '{0}' at string index: '{1}'
|
||||
/// </summary>
|
||||
internal static string Error_InputParserIndexOutOfRange
|
||||
{
|
||||
get { return GetString("Error_InputParserIndexOutOfRange"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index out of range for backreference: '{0}' at string index: '{1}'
|
||||
/// </summary>
|
||||
internal static string FormatError_InputParserIndexOutOfRange(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserIndexOutOfRange"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot parse '{0}' to integer at string index: '{1}'
|
||||
/// </summary>
|
||||
internal static string Error_InputParserInvalidInteger
|
||||
{
|
||||
get { return GetString("Error_InputParserInvalidInteger"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot parse '{0}' to integer at string index: '{1}'
|
||||
/// </summary>
|
||||
internal static string FormatError_InputParserInvalidInteger(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserInvalidInteger"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing close brace for parameter at string index: '{0}'
|
||||
/// </summary>
|
||||
internal static string Error_InputParserMissingCloseBrace
|
||||
{
|
||||
get { return GetString("Error_InputParserMissingCloseBrace"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing close brace for parameter at string index: '{0}'
|
||||
/// </summary>
|
||||
internal static string FormatError_InputParserMissingCloseBrace(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserMissingCloseBrace"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing backreference for parameter at string index: '{0}'
|
||||
/// </summary>
|
||||
internal static string Error_InputParserNoBackreference
|
||||
{
|
||||
get { return GetString("Error_InputParserNoBackreference"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Missing backreference for parameter at string index: '{0}'
|
||||
/// </summary>
|
||||
internal static string FormatError_InputParserNoBackreference(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserNoBackreference"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unrecognized parameter type: '{0}', terminated at string index: '{1}'
|
||||
/// </summary>
|
||||
internal static string Error_InputParserUnrecognizedParameter
|
||||
{
|
||||
get { return GetString("Error_InputParserUnrecognizedParameter"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unrecognized parameter type: '{0}', terminated at string index: '{1}'
|
||||
/// </summary>
|
||||
internal static string FormatError_InputParserUnrecognizedParameter(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserUnrecognizedParameter"), p0, p1);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Error_UrlRewriteParseError" xml:space="preserve">
|
||||
<value>Could not parse the UrlRewrite file. Message: '{0}'. Line number '{1}': '{2}'.</value>
|
||||
</data>
|
||||
<data name="Error_InputParserIndexOutOfRange" xml:space="preserve">
|
||||
<value>Index out of range for backreference: '{0}' at string index: '{1}'</value>
|
||||
</data>
|
||||
<data name="Error_InputParserInvalidInteger" xml:space="preserve">
|
||||
<value>Cannot parse '{0}' to integer at string index: '{1}'</value>
|
||||
</data>
|
||||
<data name="Error_InputParserMissingCloseBrace" xml:space="preserve">
|
||||
<value>Missing close brace for parameter at string index: '{0}'</value>
|
||||
</data>
|
||||
<data name="Error_InputParserNoBackreference" xml:space="preserve">
|
||||
<value>Missing backreference for parameter at string index: '{0}'</value>
|
||||
</data>
|
||||
<data name="Error_InputParserUnrecognizedParameter" xml:space="preserve">
|
||||
<value>Unrecognized parameter type: '{0}', terminated at string index: '{1}'</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||
{
|
||||
public class FormatExceptionHandlingTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Cannot have rule without match'. Line number '3': '10'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<action type=""Rewrite"" url =""{"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Missing close brace for parameter at string index: '1''. Line number '5': '14'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<action type=""AbortRequest"" url ="""" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Abort Requests are not supported.'. Line number '5': '14'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<action type=""CustomResponse"" url ="""" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Custom Responses are not supported'. Line number '5': '14'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Match must have Url Attribute'. Line number '4': '14'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS"" pattern=""^OFF$"" />
|
||||
</conditions>
|
||||
<action type=""Rewrite"" url =""foo"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Missing close brace for parameter at string index: '6''. Line number '6': '18'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<conditions>
|
||||
<add pattern=""^OFF$"" />
|
||||
</conditions>
|
||||
<action type=""Rewrite"" url =""foo"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Conditions must have an input attribute'. Line number '6': '18'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS}"" />
|
||||
</conditions>
|
||||
<action type=""Rewrite"" url =""foo"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Match does not have an associated pattern attribute in condition'. Line number '6': '18'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS}"" patternSyntax=""ExactMatch""/>
|
||||
</conditions>
|
||||
<action type=""Rewrite"" url =""foo"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Match does not have an associated pattern attribute in condition'. Line number '6': '18'.")]
|
||||
public void ThrowFormatExceptionWithCorrectMessage(string input, string expected)
|
||||
{
|
||||
// Arrange, Act, Assert
|
||||
var ex = Assert.Throws<FormatException>(() => UrlRewriteFileParser.Parse(new StringReader(input)));
|
||||
Assert.Equal(ex.Message, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +82,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
[InlineData("{foo:1")]
|
||||
[InlineData("{UrlEncode:{R:}}")]
|
||||
[InlineData("{UrlEncode:{R:1}")]
|
||||
[InlineData("{HTTPS")]
|
||||
public void FormatExceptionsOnBadSyntax(string testString)
|
||||
{
|
||||
Assert.Throws<FormatException>(() => InputParser.ParseInputString(testString));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||
{
|
||||
public class ServerVariableTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("CONTENT_LENGTH", "10")]
|
||||
[InlineData("CONTENT_TYPE", "json")]
|
||||
[InlineData("HTTP_ACCEPT", "accept")]
|
||||
[InlineData("HTTP_COOKIE", "cookie")]
|
||||
[InlineData("HTTP_HOST", "example.com")]
|
||||
[InlineData("HTTP_REFERER", "referer")]
|
||||
[InlineData("HTTP_USER_AGENT", "useragent")]
|
||||
[InlineData("HTTP_CONNECTION", "connection")]
|
||||
[InlineData("HTTP_URL", "/foo")]
|
||||
[InlineData("QUERY_STRING", "?bar=1")]
|
||||
[InlineData("REQUEST_FILENAME", "/foo")]
|
||||
public void CheckServerVariableParsingAndApplication(string variable, string expected)
|
||||
{
|
||||
// Arrange and Act
|
||||
var serverVar = ServerVariables.FindServerVariable(variable);
|
||||
var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
|
||||
// Assert
|
||||
Assert.Equal(expected, lookup);
|
||||
}
|
||||
|
||||
private HttpContext CreateTestHttpContext()
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.Host = new HostString("example.com");
|
||||
context.Request.Path = new PathString("/foo");
|
||||
context.Request.QueryString = new QueryString("?bar=1");
|
||||
context.Request.ContentLength = 10;
|
||||
context.Request.ContentType = "json";
|
||||
context.Request.Headers[HeaderNames.Accept] = "accept";
|
||||
context.Request.Headers[HeaderNames.Cookie] = "cookie";
|
||||
context.Request.Headers[HeaderNames.Referer] = "referer";
|
||||
context.Request.Headers[HeaderNames.UserAgent] = "useragent";
|
||||
context.Request.Headers[HeaderNames.Connection] = "connection";
|
||||
return context;
|
||||
}
|
||||
|
||||
private MatchResults CreateTestRuleMatch()
|
||||
{
|
||||
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
|
||||
return new MatchResults { BackReference = match.Groups, Success = match.Success };
|
||||
}
|
||||
|
||||
private MatchResults CreateTestCondMatch()
|
||||
{
|
||||
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
|
||||
return new MatchResults { BackReference = match.Groups, Success = match.Success };
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue