From 4fa6ed37922e934232f3d01a29d2ad864d5cf7b7 Mon Sep 17 00:00:00 2001 From: David Peden Date: Fri, 3 Feb 2017 18:20:41 -0600 Subject: [PATCH] Added support for IIS global rules (#169) --- .../IISUrlRewrite/IISUrlRewriteRule.cs | 12 +++++ .../Internal/IISUrlRewrite/InputParser.cs | 17 +++---- .../Internal/IISUrlRewrite/ServerVariables.cs | 14 ++++-- .../IISUrlRewrite/UrlRewriteFileParser.cs | 35 ++++++-------- .../IISUrlRewrite/UrlRewriteRuleBuilder.cs | 4 +- .../PatternSegments/GlobalRuleUrlSegment.cs | 15 ++++++ .../Internal/PatternSegments/UrlSegment.cs | 2 +- .../RewriteMiddleware.cs | 7 +-- .../IISUrlRewrite/FileParserTests.cs | 37 +++++++++++++-- .../IISUrlRewrite/InputParserTests.cs | 12 ++--- .../IISUrlRewrite/MiddleWareTests.cs | 29 ++++++++++++ .../IISUrlRewrite/ServerVariableTests.cs | 32 +++++++------ .../GlobalRuleUrlSegmentTests.cs | 47 +++++++++++++++++++ .../PatternSegments/UrlSegmentTests.cs | 25 ++++++---- 14 files changed, 219 insertions(+), 69 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs create mode 100644 test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs index d2c9b41acd..86a9faf1c6 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs @@ -14,18 +14,30 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite public IList Conditions { get; } public UrlAction Action { get; } public bool TrackAllCaptures { get; } + public bool Global { get; } public IISUrlRewriteRule(string name, UrlMatch initialMatch, IList conditions, UrlAction action, bool trackAllCaptures) + : this(name, initialMatch, conditions, action, trackAllCaptures, false) + { + } + + public IISUrlRewriteRule(string name, + UrlMatch initialMatch, + IList conditions, + UrlAction action, + bool trackAllCaptures, + bool global) { Name = name; InitialMatch = initialMatch; Conditions = conditions; Action = action; TrackAllCaptures = trackAllCaptures; + Global = global; } public virtual void ApplyRule(RewriteContext context) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs index 8a944972da..c99790cb43 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs @@ -18,8 +18,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite /// compare to the condition. Can contain server variables, back references, etc. /// /// + /// /// A new , containing a list of - public Pattern ParseInputString(string testString) + public Pattern ParseInputString(string testString, bool global) { if (testString == null) { @@ -27,10 +28,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } 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(); while (context.Next()) @@ -43,7 +44,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite // missing { throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index)); } - ParseParameter(context, results); + ParseParameter(context, results, global); } else if (context.Current == CloseBrace) { @@ -59,7 +60,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite return new Pattern(results); } - private static void ParseParameter(ParserContext context, IList results) + private static void ParseParameter(ParserContext context, IList results, bool global) { context.Mark(); // 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. parameter = context.Capture(); - results.Add(ServerVariables.FindServerVariable(parameter, context)); + results.Add(ServerVariables.FindServerVariable(parameter, context, global)); return; } else if (context.Current == Colon) @@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { case "ToLower": { - var pattern = ParseString(context); + var pattern = ParseString(context, global); results.Add(new ToLowerSegment(pattern)); // 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": { - var pattern = ParseString(context); + var pattern = ParseString(context, global); results.Add(new UrlEncodeSegment(pattern)); if (context.Current != CloseBrace) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs index 12367e142c..54df27a19e 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs @@ -9,7 +9,15 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { public static class ServerVariables { - public static PatternSegment FindServerVariable(string serverVariable, ParserContext context) + /// + /// Returns the matching for the given + /// + /// The server variable + /// The parser context which is utilized when an exception is thrown + /// Indicates if the rule being parsed is a global rule + /// Thrown when the server variable is unknown + /// The matching + public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, bool global) { switch (serverVariable) { @@ -35,7 +43,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite case "HTTP_CONNECTION": return new HeaderSegment(HeaderNames.Connection); case "HTTP_URL": - return new UrlSegment(); + return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment(); case "HTTPS": return new IsHttpsUrlSegment(); case "LOCAL_ADDR": @@ -53,7 +61,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite case "REQUEST_FILENAME": return new RequestFileNameSegment(); case "REQUEST_URI": - return new UrlSegment(); + return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment(); default: throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index)); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs index 02ecde3562..e4d8296c7c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs @@ -22,40 +22,33 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite if (xmlRoot != null) { var result = new List(); - // TODO Global rules are currently not treated differently than normal rules, fix. - // See: https://github.com/aspnet/BasicMiddleware/issues/59 - ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result); - ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result); + ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, global: true); + ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result, global: false); return result; } return null; } - private void ParseRules(XElement rules, IList result) + private void ParseRules(XElement rules, IList result, bool global) { if (rules == null) { 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)) { var builder = new UrlRewriteRuleBuilder(); - ParseRuleAttributes(rule, builder); + ParseRuleAttributes(rule, builder, global); 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; @@ -84,8 +77,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } ParseMatch(match, builder, patternSyntax); - ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax); - ParseUrlAction(action, builder, stopProcessing); + ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax, global); + ParseUrlAction(action, builder, stopProcessing, global); } 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); } - private void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) + private void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool global) { if (conditions == null) { @@ -114,11 +107,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite 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 negate = ParseBool(condition, RewriteTags.Negate, defaultValue: false); @@ -133,7 +126,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value; try { - var input = _inputParser.ParseInputString(parsedInputString); + var input = _inputParser.ParseInputString(parsedInputString, global); builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures); } 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 redirectType = ParseEnum(urlAction, RewriteTags.RedirectType, RedirectType.Permanent); @@ -160,7 +153,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite try { - var input = _inputParser.ParseInputString(url); + var input = _inputParser.ParseInputString(url, global); builder.AddUrlAction(input, actionType, appendQuery, stopProcessing, (int)redirectType); } catch (FormatException formatException) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs index e7587ba5da..26951d5f9c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs @@ -23,14 +23,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite private bool _matchAny; private bool _trackAllCaptures; - public IISUrlRewriteRule Build() + public IISUrlRewriteRule Build(bool global) { if (_initialMatch == null || _action == null) { 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( diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs new file mode 100644 index 0000000000..1a83837b98 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs index 005a335f8c..d655268c03 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs @@ -10,4 +10,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments return context.HttpContext.Request.Path; } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs index 953e5896c5..cc27a90ab2 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs @@ -5,6 +5,8 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; using Microsoft.AspNetCore.Rewrite.Logging; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Internal; @@ -75,11 +77,10 @@ namespace Microsoft.AspNetCore.Rewrite foreach (var rule in _options.Rules) { rule.ApplyRule(rewriteContext); - var currentUrl = new Lazy(() => context.Request.Path + context.Request.QueryString); switch (rewriteContext.Result) { case RuleResult.ContinueRules: - _logger.RewriteMiddlewareRequestContinueResults(currentUrl.Value); + _logger.RewriteMiddlewareRequestContinueResults(context.Request.GetEncodedUrl()); break; case RuleResult.EndResponse: _logger.RewriteMiddlewareRequestResponseComplete( @@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.Rewrite context.Response.StatusCode); return TaskCache.CompletedTask; case RuleResult.SkipRemainingRules: - _logger.RewriteMiddlewareRequestStopRules(currentUrl.Value); + _logger.RewriteMiddlewareRequestStopRules(context.Request.GetEncodedUrl()); return _next(context); default: throw new ArgumentOutOfRangeException($"Invalid rule termination {rewriteContext.Result}"); diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs index 1a8aaeab43..edc93076a4 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs @@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var condList = new List(); condList.Add(new Condition { - Input = new InputParser().ParseInputString("{HTTPS}"), + Input = new InputParser().ParseInputString("{HTTPS}", global: false), Match = new RegexMatch(new Regex("^OFF$"), false) }); @@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var condList = new List(); condList.Add(new Condition { - Input = new InputParser().ParseInputString("{HTTPS}"), + Input = new InputParser().ParseInputString("{HTTPS}", global: false), Match = new RegexMatch(new Regex("^OFF$"), false) }); @@ -128,6 +128,37 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite AssertUrlRewriteRuleEquality(expected, res); } + [Fact] + public void Should_parse_global_rules() + { + // arrange + var xml = @" + + + + + + + + + + + + + + + + "; + + // 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. private IISUrlRewriteRule CreateTestRule(List conditions, 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, - 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? diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs index 590785c3d3..ea3d4819d0 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite public void InputParser_ParseLiteralString() { 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); } @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("foo/", 1)] 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); } @@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("hey/{R:1}/{C:1}", "hey/foo/foo")] 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()); Assert.Equal(expected, result); } @@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("hey/ToLower:/what", "hey/ToLower:/what")] 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()); Assert.Equal(expected, result); } @@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("hey/{UrlEncode:}", "hey/%3Chey%3E")] 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()); Assert.Equal(expected, result); } @@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("{HTTPS")] public void FormatExceptionsOnBadSyntax(string testString) { - Assert.Throws(() => new InputParser().ParseInputString(testString)); + Assert.Throws(() => new InputParser().ParseInputString(testString, global: false)); } private RewriteContext CreateTestRewriteContext() diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs index b2724e8d58..a720a58b65 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.TestHost; using Microsoft.Net.Http.Headers; 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); } + + [Fact] + public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri() + { + var xml = @" + + + + + + + + + + "; + 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); + } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs index 1d0f0154df..c8716e144f 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs @@ -13,22 +13,25 @@ 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) + [InlineData("CONTENT_LENGTH", "10", false)] + [InlineData("CONTENT_TYPE", "json", false)] + [InlineData("HTTP_ACCEPT", "accept", false)] + [InlineData("HTTP_COOKIE", "cookie", false)] + [InlineData("HTTP_HOST", "example.com", false)] + [InlineData("HTTP_REFERER", "referer", false)] + [InlineData("HTTP_USER_AGENT", "useragent", false)] + [InlineData("HTTP_CONNECTION", "connection", false)] + [InlineData("HTTP_URL", "/foo", false)] + [InlineData("HTTP_URL", "http://example.com/foo?bar=1", true)] + [InlineData("QUERY_STRING", "bar=1", false)] + [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 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); // Assert Assert.Equal(expected, lookup); @@ -37,6 +40,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite private RewriteContext CreateTestHttpContext() { var context = new DefaultHttpContext(); + context.Request.Scheme = "http"; context.Request.Host = new HostString("example.com"); context.Request.Path = PathString.FromUriComponent("/foo"); context.Request.QueryString = QueryString.FromUriComponent("?bar=1"); @@ -68,7 +72,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var context = new DefaultHttpContext(); var rewriteContext = new RewriteContext { HttpContext = context }; 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); Assert.Equal(string.Empty, lookup); diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs new file mode 100644 index 0000000000..ca41021c75 --- /dev/null +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/UrlSegmentTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/UrlSegmentTests.cs index d23c2a5fb4..ee53d56a42 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/UrlSegmentTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/UrlSegmentTests.cs @@ -9,18 +9,27 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments { public class UrlSegmentTests { - [Fact] - public void LocalPortSegment_AssertSegmentIsCorrect() + [Theory] + [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 - var segement = new UrlSegment(); - var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; - context.HttpContext.Request.Path = new PathString("/foo/bar"); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Scheme = scheme; + httpContext.Request.Host = new HostString(host, port); + httpContext.Request.Path = new PathString(path); + + var context = new RewriteContext { HttpContext = httpContext }; + context.HttpContext = httpContext; + // Act - var results = segement.Evaluate(context, null, null); + var segment = new UrlSegment(); + var results = segment.Evaluate(context, null, null); // Assert - Assert.Equal("/foo/bar", results); + Assert.Equal(expectedResult, results); } } -} +} \ No newline at end of file