Updating tests, Exception handling cleanup.

This commit is contained in:
Justin Kotalik 2016-08-05 16:43:56 -07:00
parent 5052a94cf7
commit 4ce3b8da6a
10 changed files with 557 additions and 51 deletions

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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.");
}

View File

@ -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 };

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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));

View File

@ -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 };
}
}
}