Added support for IIS global rules (#169)

This commit is contained in:
David Peden 2017-02-03 18:20:41 -06:00 committed by Mikael Mengistu
parent 24a95f6c5d
commit 4fa6ed3792
14 changed files with 219 additions and 69 deletions

View File

@ -14,18 +14,30 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
public IList<Condition> Conditions { get; } public IList<Condition> Conditions { get; }
public UrlAction Action { get; } public UrlAction Action { get; }
public bool TrackAllCaptures { get; } public bool TrackAllCaptures { get; }
public bool Global { get; }
public IISUrlRewriteRule(string name, public IISUrlRewriteRule(string name,
UrlMatch initialMatch, UrlMatch initialMatch,
IList<Condition> conditions, IList<Condition> conditions,
UrlAction action, UrlAction action,
bool trackAllCaptures) bool trackAllCaptures)
: this(name, initialMatch, conditions, action, trackAllCaptures, false)
{
}
public IISUrlRewriteRule(string name,
UrlMatch initialMatch,
IList<Condition> conditions,
UrlAction action,
bool trackAllCaptures,
bool global)
{ {
Name = name; Name = name;
InitialMatch = initialMatch; InitialMatch = initialMatch;
Conditions = conditions; Conditions = conditions;
Action = action; Action = action;
TrackAllCaptures = trackAllCaptures; TrackAllCaptures = trackAllCaptures;
Global = global;
} }
public virtual void ApplyRule(RewriteContext context) public virtual void ApplyRule(RewriteContext context)

View File

@ -18,8 +18,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
/// compare to the condition. Can contain server variables, back references, etc. /// compare to the condition. Can contain server variables, back references, etc.
/// </summary> /// </summary>
/// <param name="testString"></param> /// <param name="testString"></param>
/// <param name="global"></param>
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns> /// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
public Pattern ParseInputString(string testString) public Pattern ParseInputString(string testString, bool global)
{ {
if (testString == null) if (testString == null)
{ {
@ -27,10 +28,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
var context = new ParserContext(testString); var context = new ParserContext(testString);
return ParseString(context); return ParseString(context, global);
} }
private static Pattern ParseString(ParserContext context) private static Pattern ParseString(ParserContext context, bool global)
{ {
var results = new List<PatternSegment>(); var results = new List<PatternSegment>();
while (context.Next()) while (context.Next())
@ -43,7 +44,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
// missing { // missing {
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index)); throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
} }
ParseParameter(context, results); ParseParameter(context, results, global);
} }
else if (context.Current == CloseBrace) else if (context.Current == CloseBrace)
{ {
@ -59,7 +60,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
return new Pattern(results); return new Pattern(results);
} }
private static void ParseParameter(ParserContext context, IList<PatternSegment> results) private static void ParseParameter(ParserContext context, IList<PatternSegment> results, bool global)
{ {
context.Mark(); context.Mark();
// Four main cases: // Four main cases:
@ -75,7 +76,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{ {
// This is just a server variable, so we do a lookup and verify the server variable exists. // This is just a server variable, so we do a lookup and verify the server variable exists.
parameter = context.Capture(); parameter = context.Capture();
results.Add(ServerVariables.FindServerVariable(parameter, context)); results.Add(ServerVariables.FindServerVariable(parameter, context, global));
return; return;
} }
else if (context.Current == Colon) else if (context.Current == Colon)
@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{ {
case "ToLower": case "ToLower":
{ {
var pattern = ParseString(context); var pattern = ParseString(context, global);
results.Add(new ToLowerSegment(pattern)); results.Add(new ToLowerSegment(pattern));
// at this point, we expect our context to be on the ending closing brace, // at this point, we expect our context to be on the ending closing brace,
@ -105,7 +106,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
case "UrlEncode": case "UrlEncode":
{ {
var pattern = ParseString(context); var pattern = ParseString(context, global);
results.Add(new UrlEncodeSegment(pattern)); results.Add(new UrlEncodeSegment(pattern));
if (context.Current != CloseBrace) if (context.Current != CloseBrace)

View File

@ -9,7 +9,15 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{ {
public static class ServerVariables public static class ServerVariables
{ {
public static PatternSegment FindServerVariable(string serverVariable, ParserContext context) /// <summary>
/// Returns the matching <see cref="PatternSegment"/> for the given <paramref name="serverVariable"/>
/// </summary>
/// <param name="serverVariable">The server variable</param>
/// <param name="context">The parser context which is utilized when an exception is thrown</param>
/// <param name="global">Indicates if the rule being parsed is a global rule</param>
/// <exception cref="FormatException">Thrown when the server variable is unknown</exception>
/// <returns>The matching <see cref="PatternSegment"/></returns>
public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, bool global)
{ {
switch (serverVariable) switch (serverVariable)
{ {
@ -35,7 +43,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
case "HTTP_CONNECTION": case "HTTP_CONNECTION":
return new HeaderSegment(HeaderNames.Connection); return new HeaderSegment(HeaderNames.Connection);
case "HTTP_URL": case "HTTP_URL":
return new UrlSegment(); return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment();
case "HTTPS": case "HTTPS":
return new IsHttpsUrlSegment(); return new IsHttpsUrlSegment();
case "LOCAL_ADDR": case "LOCAL_ADDR":
@ -53,7 +61,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
case "REQUEST_FILENAME": case "REQUEST_FILENAME":
return new RequestFileNameSegment(); return new RequestFileNameSegment();
case "REQUEST_URI": case "REQUEST_URI":
return new UrlSegment(); return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment();
default: default:
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index)); throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index));
} }

View File

@ -22,40 +22,33 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
if (xmlRoot != null) if (xmlRoot != null)
{ {
var result = new List<IISUrlRewriteRule>(); var result = new List<IISUrlRewriteRule>();
// TODO Global rules are currently not treated differently than normal rules, fix. ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, global: true);
// See: https://github.com/aspnet/BasicMiddleware/issues/59 ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result, global: false);
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result);
ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result);
return result; return result;
} }
return null; return null;
} }
private void ParseRules(XElement rules, IList<IISUrlRewriteRule> result) private void ParseRules(XElement rules, IList<IISUrlRewriteRule> result, bool global)
{ {
if (rules == null) if (rules == null)
{ {
return; return;
} }
if (string.Equals(rules.Name.ToString(), "GlobalRules", StringComparison.OrdinalIgnoreCase))
{
throw new NotSupportedException("Support for global rules has not been implemented yet");
}
foreach (var rule in rules.Elements(RewriteTags.Rule)) foreach (var rule in rules.Elements(RewriteTags.Rule))
{ {
var builder = new UrlRewriteRuleBuilder(); var builder = new UrlRewriteRuleBuilder();
ParseRuleAttributes(rule, builder); ParseRuleAttributes(rule, builder, global);
if (builder.Enabled) if (builder.Enabled)
{ {
result.Add(builder.Build()); result.Add(builder.Build(global));
} }
} }
} }
private void ParseRuleAttributes(XElement rule, UrlRewriteRuleBuilder builder) private void ParseRuleAttributes(XElement rule, UrlRewriteRuleBuilder builder, bool global)
{ {
builder.Name = rule.Attribute(RewriteTags.Name)?.Value; builder.Name = rule.Attribute(RewriteTags.Name)?.Value;
@ -84,8 +77,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
ParseMatch(match, builder, patternSyntax); ParseMatch(match, builder, patternSyntax);
ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax); ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax, global);
ParseUrlAction(action, builder, stopProcessing); ParseUrlAction(action, builder, stopProcessing, global);
} }
private void ParseMatch(XElement match, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) private void ParseMatch(XElement match, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
@ -101,7 +94,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax); builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax);
} }
private void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) private void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool global)
{ {
if (conditions == null) if (conditions == null)
{ {
@ -114,11 +107,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
foreach (var cond in conditions.Elements(RewriteTags.Add)) foreach (var cond in conditions.Elements(RewriteTags.Add))
{ {
ParseCondition(cond, builder, patternSyntax, trackAllCaptures); ParseCondition(cond, builder, patternSyntax, trackAllCaptures, global);
} }
} }
private void ParseCondition(XElement condition, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool trackAllCaptures) private void ParseCondition(XElement condition, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool trackAllCaptures, bool global)
{ {
var ignoreCase = ParseBool(condition, RewriteTags.IgnoreCase, defaultValue: true); var ignoreCase = ParseBool(condition, RewriteTags.IgnoreCase, defaultValue: true);
var negate = ParseBool(condition, RewriteTags.Negate, defaultValue: false); var negate = ParseBool(condition, RewriteTags.Negate, defaultValue: false);
@ -133,7 +126,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value; var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value;
try try
{ {
var input = _inputParser.ParseInputString(parsedInputString); var input = _inputParser.ParseInputString(parsedInputString, global);
builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures); builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures);
} }
catch (FormatException formatException) catch (FormatException formatException)
@ -142,7 +135,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
} }
private void ParseUrlAction(XElement urlAction, UrlRewriteRuleBuilder builder, bool stopProcessing) private void ParseUrlAction(XElement urlAction, UrlRewriteRuleBuilder builder, bool stopProcessing, bool global)
{ {
var actionType = ParseEnum(urlAction, RewriteTags.Type, ActionType.None); var actionType = ParseEnum(urlAction, RewriteTags.Type, ActionType.None);
var redirectType = ParseEnum(urlAction, RewriteTags.RedirectType, RedirectType.Permanent); var redirectType = ParseEnum(urlAction, RewriteTags.RedirectType, RedirectType.Permanent);
@ -160,7 +153,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
try try
{ {
var input = _inputParser.ParseInputString(url); var input = _inputParser.ParseInputString(url, global);
builder.AddUrlAction(input, actionType, appendQuery, stopProcessing, (int)redirectType); builder.AddUrlAction(input, actionType, appendQuery, stopProcessing, (int)redirectType);
} }
catch (FormatException formatException) catch (FormatException formatException)

View File

@ -23,14 +23,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
private bool _matchAny; private bool _matchAny;
private bool _trackAllCaptures; private bool _trackAllCaptures;
public IISUrlRewriteRule Build() public IISUrlRewriteRule Build(bool global)
{ {
if (_initialMatch == null || _action == null) if (_initialMatch == null || _action == null)
{ {
throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match"); throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match");
} }
return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, _trackAllCaptures); return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, _trackAllCaptures, global);
} }
public void AddUrlAction( public void AddUrlAction(

View File

@ -0,0 +1,15 @@
// 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 Microsoft.AspNetCore.Http.Extensions;
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class GlobalRuleUrlSegment : PatternSegment
{
public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences)
{
return context.HttpContext.Request.GetEncodedUrl();
}
}
}

View File

@ -5,6 +5,8 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite;
using Microsoft.AspNetCore.Rewrite.Logging; using Microsoft.AspNetCore.Rewrite.Logging;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
@ -75,11 +77,10 @@ namespace Microsoft.AspNetCore.Rewrite
foreach (var rule in _options.Rules) foreach (var rule in _options.Rules)
{ {
rule.ApplyRule(rewriteContext); rule.ApplyRule(rewriteContext);
var currentUrl = new Lazy<string>(() => context.Request.Path + context.Request.QueryString);
switch (rewriteContext.Result) switch (rewriteContext.Result)
{ {
case RuleResult.ContinueRules: case RuleResult.ContinueRules:
_logger.RewriteMiddlewareRequestContinueResults(currentUrl.Value); _logger.RewriteMiddlewareRequestContinueResults(context.Request.GetEncodedUrl());
break; break;
case RuleResult.EndResponse: case RuleResult.EndResponse:
_logger.RewriteMiddlewareRequestResponseComplete( _logger.RewriteMiddlewareRequestResponseComplete(
@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.Rewrite
context.Response.StatusCode); context.Response.StatusCode);
return TaskCache.CompletedTask; return TaskCache.CompletedTask;
case RuleResult.SkipRemainingRules: case RuleResult.SkipRemainingRules:
_logger.RewriteMiddlewareRequestStopRules(currentUrl.Value); _logger.RewriteMiddlewareRequestStopRules(context.Request.GetEncodedUrl());
return _next(context); return _next(context);
default: default:
throw new ArgumentOutOfRangeException($"Invalid rule termination {rewriteContext.Result}"); throw new ArgumentOutOfRangeException($"Invalid rule termination {rewriteContext.Result}");

View File

@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
var condList = new List<Condition>(); var condList = new List<Condition>();
condList.Add(new Condition condList.Add(new Condition
{ {
Input = new InputParser().ParseInputString("{HTTPS}"), Input = new InputParser().ParseInputString("{HTTPS}", global: false),
Match = new RegexMatch(new Regex("^OFF$"), false) Match = new RegexMatch(new Regex("^OFF$"), false)
}); });
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
var condList = new List<Condition>(); var condList = new List<Condition>();
condList.Add(new Condition condList.Add(new Condition
{ {
Input = new InputParser().ParseInputString("{HTTPS}"), Input = new InputParser().ParseInputString("{HTTPS}", global: false),
Match = new RegexMatch(new Regex("^OFF$"), false) Match = new RegexMatch(new Regex("^OFF$"), false)
}); });
@ -128,6 +128,37 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
AssertUrlRewriteRuleEquality(expected, res); AssertUrlRewriteRuleEquality(expected, res);
} }
[Fact]
public void Should_parse_global_rules()
{
// arrange
var xml = @"<rewrite>
<globalRules>
<rule name=""httpsOnly"" patternSyntax=""ECMAScript"" stopProcessing=""true"">
<match url="".*"" />
<conditions logicalGrouping=""MatchAll"" trackAllCaptures=""false"">
<add input=""{HTTPS}"" pattern=""off"" />
</conditions>
<action type=""Redirect"" url=""https://{HTTP_HOST}{REQUEST_URI}"" />
</rule>
</globalRules>
<rules>
<rule name=""Rewrite to article.aspx"">
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
<action type=""Rewrite"" url =""article.aspx?id={R:1}&amp;title={R:2}"" />
</rule>
</rules>
</rewrite>";
// act
var rules = new UrlRewriteFileParser().Parse(new StringReader(xml));
// assert
Assert.Equal(2, rules.Count);
Assert.True(rules[0].Global);
Assert.False(rules[1].Global);
}
// Creates a rule with appropriate default values of the url rewrite rule. // Creates a rule with appropriate default values of the url rewrite rule.
private IISUrlRewriteRule CreateTestRule(List<Condition> conditions, private IISUrlRewriteRule CreateTestRule(List<Condition> conditions,
LogicalGrouping condGrouping = LogicalGrouping.MatchAll, LogicalGrouping condGrouping = LogicalGrouping.MatchAll,
@ -147,7 +178,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
) )
{ {
return new IISUrlRewriteRule(name, new RegexMatch(new Regex("^OFF$"), false), conditions, return new IISUrlRewriteRule(name, new RegexMatch(new Regex("^OFF$"), false), conditions,
new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url), queryStringAppend: false), trackAllCaptures: false); new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url, global: false), queryStringAppend: false), trackAllCaptures: false);
} }
// TODO make rules comparable? // TODO make rules comparable?

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
public void InputParser_ParseLiteralString() public void InputParser_ParseLiteralString()
{ {
var testString = "hello/hey/what"; var testString = "hello/hey/what";
var result = new InputParser().ParseInputString(testString); var result = new InputParser().ParseInputString(testString, global: false);
Assert.Equal(1, result.PatternSegments.Count); Assert.Equal(1, result.PatternSegments.Count);
} }
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("foo/", 1)] [InlineData("foo/", 1)]
public void InputParser_ParseStringWithBackReference(string testString, int expected) public void InputParser_ParseStringWithBackReference(string testString, int expected)
{ {
var result = new InputParser().ParseInputString(testString); var result = new InputParser().ParseInputString(testString, global: false);
Assert.Equal(expected, result.PatternSegments.Count); Assert.Equal(expected, result.PatternSegments.Count);
} }
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("hey/{R:1}/{C:1}", "hey/foo/foo")] [InlineData("hey/{R:1}/{C:1}", "hey/foo/foo")]
public void EvaluateBackReferenceRule(string testString, string expected) public void EvaluateBackReferenceRule(string testString, string expected)
{ {
var middle = new InputParser().ParseInputString(testString); var middle = new InputParser().ParseInputString(testString, global: false);
var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences());
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("hey/ToLower:/what", "hey/ToLower:/what")] [InlineData("hey/ToLower:/what", "hey/ToLower:/what")]
public void EvaluatToLowerRule(string testString, string expected) public void EvaluatToLowerRule(string testString, string expected)
{ {
var middle = new InputParser().ParseInputString(testString); var middle = new InputParser().ParseInputString(testString, global: false);
var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences());
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("hey/{UrlEncode:<hey>}", "hey/%3Chey%3E")] [InlineData("hey/{UrlEncode:<hey>}", "hey/%3Chey%3E")]
public void EvaluatUriEncodeRule(string testString, string expected) public void EvaluatUriEncodeRule(string testString, string expected)
{ {
var middle = new InputParser().ParseInputString(testString); var middle = new InputParser().ParseInputString(testString, global: false);
var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences());
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("{HTTPS")] [InlineData("{HTTPS")]
public void FormatExceptionsOnBadSyntax(string testString) public void FormatExceptionsOnBadSyntax(string testString)
{ {
Assert.Throws<FormatException>(() => new InputParser().ParseInputString(testString)); Assert.Throws<FormatException>(() => new InputParser().ParseInputString(testString, global: false));
} }
private RewriteContext CreateTestRewriteContext() private RewriteContext CreateTestRewriteContext()

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Xunit; using Xunit;
@ -451,5 +452,33 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
Assert.Equal("Cannot access back reference at index 9. Only 5 back references were captured.", ex.Message); Assert.Equal("Cannot access back reference at index 9. Only 5 back references were captured.", ex.Message);
} }
[Fact]
public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri()
{
var xml = @"<rewrite>
<globalRules>
<rule name=""Test"" patternSyntax=""ECMAScript"" stopProcessing=""true"">
<match url="".*"" />
<conditions logicalGrouping=""MatchAll"" trackAllCaptures=""false"">
<add input=""{REQUEST_URI}"" pattern=""^http://localhost/([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})(/.*)"" />
</conditions>
<action type=""Rewrite"" url=""http://www.test.com{C:2}"" />
</rule>
</globalRules>
</rewrite>";
var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(xml));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync(context.Request.GetEncodedUrl()));
});
var server = new TestServer(builder);
var response = await server.CreateClient().GetStringAsync($"http://localhost/{Guid.NewGuid()}/foo/bar");
Assert.Equal("http://www.test.com/foo/bar", response);
}
} }
} }

View File

@ -13,22 +13,25 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
public class ServerVariableTests public class ServerVariableTests
{ {
[Theory] [Theory]
[InlineData("CONTENT_LENGTH", "10")] [InlineData("CONTENT_LENGTH", "10", false)]
[InlineData("CONTENT_TYPE", "json")] [InlineData("CONTENT_TYPE", "json", false)]
[InlineData("HTTP_ACCEPT", "accept")] [InlineData("HTTP_ACCEPT", "accept", false)]
[InlineData("HTTP_COOKIE", "cookie")] [InlineData("HTTP_COOKIE", "cookie", false)]
[InlineData("HTTP_HOST", "example.com")] [InlineData("HTTP_HOST", "example.com", false)]
[InlineData("HTTP_REFERER", "referer")] [InlineData("HTTP_REFERER", "referer", false)]
[InlineData("HTTP_USER_AGENT", "useragent")] [InlineData("HTTP_USER_AGENT", "useragent", false)]
[InlineData("HTTP_CONNECTION", "connection")] [InlineData("HTTP_CONNECTION", "connection", false)]
[InlineData("HTTP_URL", "/foo")] [InlineData("HTTP_URL", "/foo", false)]
[InlineData("QUERY_STRING", "bar=1")] [InlineData("HTTP_URL", "http://example.com/foo?bar=1", true)]
[InlineData("REQUEST_FILENAME", "/foo")] [InlineData("QUERY_STRING", "bar=1", false)]
public void CheckServerVariableParsingAndApplication(string variable, string expected) [InlineData("REQUEST_FILENAME", "/foo", false)]
[InlineData("REQUEST_URI", "/foo", false)]
[InlineData("REQUEST_URI", "http://example.com/foo?bar=1", true)]
public void CheckServerVariableParsingAndApplication(string variable, string expected, bool global)
{ {
// Arrange and Act // Arrange and Act
var testParserContext = new ParserContext("test"); var testParserContext = new ParserContext("test");
var serverVar = ServerVariables.FindServerVariable(variable, testParserContext); var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, global);
var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences);
// Assert // Assert
Assert.Equal(expected, lookup); Assert.Equal(expected, lookup);
@ -37,6 +40,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
private RewriteContext CreateTestHttpContext() private RewriteContext CreateTestHttpContext()
{ {
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
context.Request.Scheme = "http";
context.Request.Host = new HostString("example.com"); context.Request.Host = new HostString("example.com");
context.Request.Path = PathString.FromUriComponent("/foo"); context.Request.Path = PathString.FromUriComponent("/foo");
context.Request.QueryString = QueryString.FromUriComponent("?bar=1"); context.Request.QueryString = QueryString.FromUriComponent("?bar=1");
@ -68,7 +72,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var rewriteContext = new RewriteContext { HttpContext = context }; var rewriteContext = new RewriteContext { HttpContext = context };
var testParserContext = new ParserContext("test"); var testParserContext = new ParserContext("test");
var serverVar = ServerVariables.FindServerVariable("QUERY_STRING", testParserContext); var serverVar = ServerVariables.FindServerVariable("QUERY_STRING", testParserContext, global: false);
var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences);
Assert.Equal(string.Empty, lookup); Assert.Equal(string.Empty, lookup);

View File

@ -0,0 +1,47 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
using Xunit;
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
{
public class GlobalRuleUrlSegmentTests
{
[Theory]
[InlineData("http", "localhost", 80, null, null, "http://localhost:80/")]
[InlineData("http", "localhost", 80, "/foo/bar", null, "http://localhost:80/foo/bar")]
[InlineData("http", "localhost", 80, "/foo bar", null, "http://localhost:80/foo%20bar")]
[InlineData("http", "localhost", 81, "/foo/bar", null, "http://localhost:81/foo/bar")]
[InlineData("http", "localhost", 80, null, "?foo=bar", "http://localhost:80/?foo=bar")]
[InlineData("https", "localhost", 443, "/foo/bar", null, "https://localhost:443/foo/bar")]
public void AssertSegmentIsCorrect(string scheme, string host, int port, string path, string queryString, string expectedResult)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Scheme = scheme;
httpContext.Request.Host = new HostString(host, port);
if (!string.IsNullOrEmpty(path))
{
httpContext.Request.Path = new PathString(path);
}
if (!string.IsNullOrEmpty(queryString))
{
httpContext.Request.QueryString = new QueryString(queryString);
}
var context = new RewriteContext { HttpContext = httpContext };
context.HttpContext = httpContext;
// Act
var segment = new GlobalRuleUrlSegment();
var results = segment.Evaluate(context, null, null);
// Assert
Assert.Equal(expectedResult, results);
}
}
}

View File

@ -9,18 +9,27 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
{ {
public class UrlSegmentTests public class UrlSegmentTests
{ {
[Fact] [Theory]
public void LocalPortSegment_AssertSegmentIsCorrect() [InlineData("http", "localhost", 80, "/foo/bar", "/foo/bar")]
[InlineData("http", "localhost", 80, "/foo:bar", "/foo:bar")]
[InlineData("http", "localhost", 80, "/foo bar", "/foo%20bar")]
public void AssertSegmentIsCorrect(string scheme, string host, int port, string path, string expectedResult)
{ {
// Arrange // Arrange
var segement = new UrlSegment(); var httpContext = new DefaultHttpContext();
var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; httpContext.Request.Scheme = scheme;
context.HttpContext.Request.Path = new PathString("/foo/bar"); httpContext.Request.Host = new HostString(host, port);
httpContext.Request.Path = new PathString(path);
var context = new RewriteContext { HttpContext = httpContext };
context.HttpContext = httpContext;
// Act // Act
var results = segement.Evaluate(context, null, null); var segment = new UrlSegment();
var results = segment.Evaluate(context, null, null);
// Assert // Assert
Assert.Equal("/foo/bar", results); Assert.Equal(expectedResult, results);
} }
} }
} }