diff --git a/samples/RewriteSample/Startup.cs b/samples/RewriteSample/Startup.cs index 2d423eb6c6..c5b1ceb895 100644 --- a/samples/RewriteSample/Startup.cs +++ b/samples/RewriteSample/Startup.cs @@ -22,7 +22,6 @@ namespace RewriteSample .AddApacheModRewrite(env.ContentRootFileProvider, "Rewrite.txt"); app.UseRewriter(options); - app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}")); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs index a6d03b7fba..03b3a1ab6c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs @@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite BackReferenceCollection condBackReferences = null; if (Conditions != null) { - var condResult = ConditionHelper.Evaluate(Conditions, context, initMatchRes.BackReferences); + var condResult = ConditionEvaluator.Evaluate(Conditions, context, initMatchRes.BackReferences); if (!condResult.Success) { context.Logger?.ModRewriteDidNotMatchRule(); @@ -51,4 +51,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/Condition.cs similarity index 90% rename from src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs rename to src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/Condition.cs index 314e63a62c..01db0eb1bd 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/Condition.cs @@ -1,7 +1,7 @@ // 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. -namespace Microsoft.AspNetCore.Rewrite.Internal +namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite { public class Condition { @@ -15,4 +15,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal return Match.Evaluate(pattern, context); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ConditionEvaluator.cs similarity index 91% rename from src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs rename to src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ConditionEvaluator.cs index 7a3c4365c1..0038c5bab2 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ConditionEvaluator.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; -namespace Microsoft.AspNetCore.Rewrite.Internal +namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite { - public static class ConditionHelper + public static class ConditionEvaluator { public static MatchResults Evaluate(IEnumerable conditions, RewriteContext context, BackReferenceCollection backReferences) { @@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal return condResult; } - if (condResult.Success && trackAllCaptures && prevBackReferences!= null) + if (condResult.Success && trackAllCaptures && prevBackReferences != null) { prevBackReferences.Add(currentBackReferences); currentBackReferences = prevBackReferences; @@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal prevBackReferences = currentBackReferences; } - return new MatchResults { BackReferences = prevBackReferences, Success = condResult.Success }; ; + return new MatchResults { BackReferences = prevBackReferences, Success = condResult.Success }; } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/RuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/RuleBuilder.cs index 638192b602..33473ddbf7 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/RuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/RuleBuilder.cs @@ -222,4 +222,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/Condition.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/Condition.cs new file mode 100644 index 0000000000..18a24c1925 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/Condition.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite +{ + public class Condition + { + public Pattern Input { get; set; } + public UrlMatch Match { get; set; } + + public MatchResults Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) + { + var pattern = Input.Evaluate(context, ruleBackReferences, conditionBackReferences); + return Match.Evaluate(pattern, context); + } + } +} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ConditionCollection.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ConditionCollection.cs new file mode 100644 index 0000000000..3aec4fe960 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ConditionCollection.cs @@ -0,0 +1,68 @@ +// 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.Collections; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite +{ + public class ConditionCollection : IEnumerable + { + private readonly List _conditions = new List(); + + public LogicalGrouping Grouping { get; } + public bool TrackAllCaptures { get; } + + public ConditionCollection() + :this(LogicalGrouping.MatchAll, trackAllCaptures: false) + { + } + + public ConditionCollection(LogicalGrouping grouping, bool trackAllCaptures) + { + Grouping = grouping; + TrackAllCaptures = trackAllCaptures; + } + + public int Count => _conditions.Count; + + public Condition this[int index] + { + get + { + if (index < _conditions.Count) + { + return _conditions[index]; + } + throw new IndexOutOfRangeException($"Cannot access condition at index {index}. Only {_conditions.Count} conditions were captured."); + } + } + + public void Add(Condition condition) + { + if (condition != null) + { + _conditions.Add(condition); + } + } + + public void AddConditions(IEnumerable conditions) + { + if (conditions != null) + { + _conditions.AddRange(conditions); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _conditions.GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return _conditions.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ConditionEvaluator.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ConditionEvaluator.cs new file mode 100644 index 0000000000..2d50739e78 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ConditionEvaluator.cs @@ -0,0 +1,49 @@ +// 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. + +namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite +{ + public static class ConditionEvaluator + { + public static MatchResults Evaluate(ConditionCollection conditions, RewriteContext context, BackReferenceCollection backReferences) + { + BackReferenceCollection prevBackReferences = null; + MatchResults condResult = null; + var orSucceeded = false; + foreach (var condition in conditions) + { + if (orSucceeded && conditions.Grouping == LogicalGrouping.MatchAny) + { + continue; + } + + if (orSucceeded) + { + orSucceeded = false; + continue; + } + + condResult = condition.Evaluate(context, backReferences, prevBackReferences); + var currentBackReferences = condResult.BackReferences; + if (conditions.Grouping == LogicalGrouping.MatchAny) + { + orSucceeded = condResult.Success; + } + else if (!condResult.Success) + { + return condResult; + } + + if (condResult.Success && conditions.TrackAllCaptures && prevBackReferences!= null) + { + prevBackReferences.Add(currentBackReferences); + currentBackReferences = prevBackReferences; + } + + prevBackReferences = currentBackReferences; + } + + return new MatchResults { BackReferences = prevBackReferences, Success = condResult.Success }; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs index 86a9faf1c6..678486d592 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs @@ -1,7 +1,6 @@ // 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.Collections.Generic; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Rewrite.Logging; @@ -11,32 +10,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { public string Name { get; } public UrlMatch InitialMatch { get; } - public IList Conditions { get; } + public ConditionCollection 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) + ConditionCollection conditions, + UrlAction action) + : this(name, initialMatch, conditions, action, false) { } public IISUrlRewriteRule(string name, UrlMatch initialMatch, - IList conditions, + ConditionCollection conditions, UrlAction action, - bool trackAllCaptures, bool global) { Name = name; InitialMatch = initialMatch; Conditions = conditions; Action = action; - TrackAllCaptures = trackAllCaptures; Global = global; } @@ -64,7 +59,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite MatchResults condResult = null; if (Conditions != null) { - condResult = ConditionHelper.Evaluate(Conditions, context, initMatchResults.BackReferences, TrackAllCaptures); + condResult = ConditionEvaluator.Evaluate(Conditions, context, initMatchResults.BackReferences); if (!condResult.Success) { context.Logger?.UrlRewriteDidNotMatchRule(Name); diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs index d73e561d50..7dc3f8d638 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/InputParser.cs @@ -28,9 +28,20 @@ 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, bool global) + public Pattern ParseInputString(string testString) + { + return ParseInputString(testString, UriMatchPart.Path); + } + + /// + /// Creates a pattern, which is a template to create a new test string to + /// compare to the condition. Can contain server variables, back references, etc. + /// + /// + /// When testString evaluates to a URL segment, specify which part of the URI to evaluate. + /// A new , containing a list of + public Pattern ParseInputString(string testString, UriMatchPart uriMatchPart) { if (testString == null) { @@ -38,10 +49,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } var context = new ParserContext(testString); - return ParseString(context, global); + return ParseString(context, uriMatchPart); } - private Pattern ParseString(ParserContext context, bool global) + private Pattern ParseString(ParserContext context, UriMatchPart uriMatchPart) { var results = new List(); while (context.Next()) @@ -54,7 +65,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite // missing { throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index)); } - ParseParameter(context, results, global); + ParseParameter(context, results, uriMatchPart); } else if (context.Current == CloseBrace) { @@ -70,7 +81,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite return new Pattern(results); } - private void ParseParameter(ParserContext context, IList results, bool global) + private void ParseParameter(ParserContext context, IList results, UriMatchPart uriMatchPart) { context.Mark(); // Four main cases: @@ -86,7 +97,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, global)); + results.Add(ServerVariables.FindServerVariable(parameter, context, uriMatchPart)); return; } else if (context.Current == Colon) @@ -98,7 +109,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { case "ToLower": { - var pattern = ParseString(context, global); + var pattern = ParseString(context, uriMatchPart); results.Add(new ToLowerSegment(pattern)); // at this point, we expect our context to be on the ending closing brace, @@ -116,7 +127,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } case "UrlEncode": { - var pattern = ParseString(context, global); + var pattern = ParseString(context, uriMatchPart); results.Add(new UrlEncodeSegment(pattern)); if (context.Current != CloseBrace) @@ -141,7 +152,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite var rewriteMap = _rewriteMaps?[parameter]; if (rewriteMap != null) { - var pattern = ParseString(context, global); + var pattern = ParseString(context, uriMatchPart); results.Add(new RewriteMapSegment(rewriteMap, pattern)); return; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs index 54df27a19e..6bfdc59156 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/ServerVariables.cs @@ -14,10 +14,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite /// /// The server variable /// The parser context which is utilized when an exception is thrown - /// Indicates if the rule being parsed is a global rule + /// Indicates whether the full URI or the path should be evaluated for URL segments /// Thrown when the server variable is unknown /// The matching - public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, bool global) + public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, UriMatchPart uriMatchPart) { switch (serverVariable) { @@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite case "HTTP_CONNECTION": return new HeaderSegment(HeaderNames.Connection); case "HTTP_URL": - return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment(); + return new UrlSegment(uriMatchPart); case "HTTPS": return new IsHttpsUrlSegment(); case "LOCAL_ADDR": @@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite case "REQUEST_FILENAME": return new RequestFileNameSegment(); case "REQUEST_URI": - return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment(); + return new UrlSegment(uriMatchPart); default: throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index)); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UriMatchCondition.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UriMatchCondition.cs new file mode 100644 index 0000000000..9f39d46670 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UriMatchCondition.cs @@ -0,0 +1,22 @@ +// 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.Text.RegularExpressions; +using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; + +namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite +{ + public class UriMatchCondition : Condition + { + public UriMatchCondition(InputParser inputParser, string input, string pattern, UriMatchPart uriMatchPart, bool ignoreCase, bool negate) + { + var regex = new Regex( + pattern, + ignoreCase ? RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase : RegexOptions.CultureInvariant | RegexOptions.Compiled, + TimeSpan.FromMilliseconds(1)); + Input = inputParser.ParseInputString(input, uriMatchPart); + Match = new RegexMatch(regex, negate); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UriMatchPart.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UriMatchPart.cs new file mode 100644 index 0000000000..25da940689 --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UriMatchPart.cs @@ -0,0 +1,11 @@ +// 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. + +namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite +{ + public enum UriMatchPart + { + Full, + Path + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs index 0cf093b091..2a5b0f24a9 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Xml.Linq; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; +using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { @@ -45,17 +46,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite foreach (var rule in rules.Elements(RewriteTags.Rule)) { - var builder = new UrlRewriteRuleBuilder(); - ParseRuleAttributes(rule, builder, global); + var builder = new UrlRewriteRuleBuilder { Global = global }; + ParseRuleAttributes(rule, builder); if (builder.Enabled) { - result.Add(builder.Build(global)); + result.Add(builder.Build()); } } } - private void ParseRuleAttributes(XElement rule, UrlRewriteRuleBuilder builder, bool global) + private void ParseRuleAttributes(XElement rule, UrlRewriteRuleBuilder builder) { builder.Name = rule.Attribute(RewriteTags.Name)?.Value; @@ -84,8 +85,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } ParseMatch(match, builder, patternSyntax); - ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax, global); - ParseUrlAction(action, builder, stopProcessing, global); + ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax); + ParseUrlAction(action, builder, stopProcessing); } private void ParseMatch(XElement match, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) @@ -101,7 +102,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax); } - private void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool global) + private void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) { if (conditions == null) { @@ -110,32 +111,76 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite var grouping = ParseEnum(conditions, RewriteTags.LogicalGrouping, LogicalGrouping.MatchAll); var trackAllCaptures = ParseBool(conditions, RewriteTags.TrackAllCaptures, defaultValue: false); - builder.AddUrlConditions(grouping, trackAllCaptures); + builder.ConfigureConditionBehavior(grouping, trackAllCaptures); foreach (var cond in conditions.Elements(RewriteTags.Add)) { - ParseCondition(cond, builder, patternSyntax, trackAllCaptures, global); + ParseCondition(cond, builder, patternSyntax); } } - private void ParseCondition(XElement condition, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool trackAllCaptures, bool global) + private void ParseCondition(XElement conditionElement, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) { - var ignoreCase = ParseBool(condition, RewriteTags.IgnoreCase, defaultValue: true); - var negate = ParseBool(condition, RewriteTags.Negate, defaultValue: false); - var matchType = ParseEnum(condition, RewriteTags.MatchType, MatchType.Pattern); - var parsedInputString = condition.Attribute(RewriteTags.Input)?.Value; + var ignoreCase = ParseBool(conditionElement, RewriteTags.IgnoreCase, defaultValue: true); + var negate = ParseBool(conditionElement, RewriteTags.Negate, defaultValue: false); + var matchType = ParseEnum(conditionElement, RewriteTags.MatchType, MatchType.Pattern); + var parsedInputString = conditionElement.Attribute(RewriteTags.Input)?.Value; if (parsedInputString == null) { - throw new InvalidUrlRewriteFormatException(condition, "Conditions must have an input attribute"); + throw new InvalidUrlRewriteFormatException(conditionElement, "Conditions must have an input attribute"); } - var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value; - var input = _inputParser.ParseInputString(parsedInputString, global); - builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures); + var parsedPatternString = conditionElement.Attribute(RewriteTags.Pattern)?.Value; + Condition condition; + + switch (patternSyntax) + { + case PatternSyntax.ECMAScript: + { + switch (matchType) + { + case MatchType.Pattern: + { + if (string.IsNullOrEmpty(parsedPatternString)) + { + throw new FormatException("Match does not have an associated pattern attribute in condition"); + } + condition = new UriMatchCondition(_inputParser, parsedInputString, parsedPatternString, builder.UriMatchPart, ignoreCase, negate); + break; + } + case MatchType.IsDirectory: + { + condition = new Condition { Input = _inputParser.ParseInputString(parsedInputString, builder.UriMatchPart), Match = new IsDirectoryMatch(negate) }; + break; + } + case MatchType.IsFile: + { + condition = new Condition { Input = _inputParser.ParseInputString(parsedInputString, builder.UriMatchPart), Match = new IsFileMatch(negate) }; + break; + } + default: + throw new FormatException("Unrecognized matchType"); + } + break; + } + case PatternSyntax.Wildcard: + throw new NotSupportedException("Wildcard syntax is not supported"); + case PatternSyntax.ExactMatch: + if (string.IsNullOrEmpty(parsedPatternString)) + { + throw new FormatException("Match does not have an associated pattern attribute in condition"); + } + condition = new Condition { Input = _inputParser.ParseInputString(parsedInputString, builder.UriMatchPart), Match = new ExactMatch(ignoreCase, parsedPatternString, negate) }; + break; + default: + throw new FormatException("Unrecognized pattern syntax"); + } + + builder.AddUrlCondition(condition); } - private void ParseUrlAction(XElement urlAction, UrlRewriteRuleBuilder builder, bool stopProcessing, bool global) + private void ParseUrlAction(XElement urlAction, UrlRewriteRuleBuilder builder, bool stopProcessing) { var actionType = ParseEnum(urlAction, RewriteTags.Type, ActionType.None); UrlAction action; @@ -156,7 +201,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } } - var urlPattern = _inputParser.ParseInputString(url, global); + var urlPattern = _inputParser.ParseInputString(url, builder.UriMatchPart); var appendQuery = ParseBool(urlAction, RewriteTags.AppendQueryString, defaultValue: true); if (actionType == ActionType.Rewrite) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs index 8cf927e7b2..ea855fb4a4 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs @@ -14,21 +14,21 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite public string Name { get; set; } public bool Enabled { get; set; } + public bool Global { get; set; } + public UriMatchPart UriMatchPart => Global ? UriMatchPart.Full : UriMatchPart.Path; private UrlMatch _initialMatch; - private IList _conditions; + private ConditionCollection _conditions; private UrlAction _action; - private bool _matchAny; - private bool _trackAllCaptures; - public IISUrlRewriteRule Build(bool global) + public IISUrlRewriteRule Build() { if (_initialMatch == null || _action == null) { throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match"); } - return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, _trackAllCaptures, global); + return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, Global); } public void AddUrlAction(UrlAction action) @@ -66,70 +66,31 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } } - public void AddUrlCondition(Pattern input, string pattern, PatternSyntax patternSyntax, MatchType matchType, bool ignoreCase, bool negate, bool trackAllCaptures) + public void ConfigureConditionBehavior(LogicalGrouping logicalGrouping, bool trackAllCaptures) + { + _conditions = new ConditionCollection(logicalGrouping, trackAllCaptures); + } + + public void AddUrlCondition(Condition condition) { - // If there are no conditions specified if (_conditions == null) { - AddUrlConditions(LogicalGrouping.MatchAll, trackAllCaptures); + throw new InvalidOperationException($"You must first configure condition behavior by calling {nameof(ConfigureConditionBehavior)}"); } - - switch (patternSyntax) + if (condition == null) { - case PatternSyntax.ECMAScript: - { - switch (matchType) - { - case MatchType.Pattern: - { - if (string.IsNullOrEmpty(pattern)) - { - throw new FormatException("Match does not have an associated pattern attribute in condition"); - } - - var regex = new Regex( - pattern, - ignoreCase ? RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase : - RegexOptions.CultureInvariant | RegexOptions.Compiled, - RegexTimeout); - - _conditions.Add(new Condition { Input = input, Match = new RegexMatch(regex, negate), OrNext = _matchAny }); - break; - } - case MatchType.IsDirectory: - { - _conditions.Add(new Condition { Input = input, Match = new IsDirectoryMatch(negate), OrNext = _matchAny }); - break; - } - case MatchType.IsFile: - { - _conditions.Add(new Condition { Input = input, Match = new IsFileMatch(negate), OrNext = _matchAny }); - break; - } - default: - throw new FormatException("Unrecognized matchType"); - } - break; - } - case PatternSyntax.Wildcard: - throw new NotSupportedException("Wildcard syntax is not supported"); - case PatternSyntax.ExactMatch: - if (pattern == null) - { - throw new FormatException("Match does not have an associated pattern attribute in condition"); - } - _conditions.Add(new Condition { Input = input, Match = new ExactMatch(ignoreCase, pattern, negate), OrNext = _matchAny }); - break; - default: - throw new FormatException("Unrecognized pattern syntax"); + throw new ArgumentNullException(nameof(condition)); } + _conditions.Add(condition); } - public void AddUrlConditions(LogicalGrouping logicalGrouping, bool trackAllCaptures) + public void AddUrlConditions(IEnumerable conditions) { - _conditions = new List(); - _matchAny = logicalGrouping == LogicalGrouping.MatchAny; - _trackAllCaptures = trackAllCaptures; + if (_conditions == null) + { + throw new InvalidOperationException($"You must first configure condition behavior by calling {nameof(ConfigureConditionBehavior)}"); + } + _conditions.AddConditions(conditions); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs deleted file mode 100644 index 1a83837b98..0000000000 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/GlobalRuleUrlSegment.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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 d655268c03..431d5cd5fb 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs @@ -1,13 +1,28 @@ // 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; +using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; + namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class UrlSegment : PatternSegment { + private readonly UriMatchPart _uriMatchPart; + + public UrlSegment() + : this(UriMatchPart.Path) + { + } + + public UrlSegment(UriMatchPart uriMatchPart) + { + _uriMatchPart = uriMatchPart; + } + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { - return context.HttpContext.Request.Path; + return _uriMatchPart == UriMatchPart.Full ? context.HttpContext.Request.GetEncodedUrl() : (string)context.HttpContext.Request.Path; } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs index edc93076a4..18f1a246d0 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Rewrite.Internal; -using Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; @@ -29,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite "; var expected = new List(); - expected.Add(CreateTestRule(new List(), + expected.Add(CreateTestRule(new ConditionCollection(), url: "^article/([0-9]+)/([_0-9a-z-]+)", name: "Rewrite to article.aspx", actionType: ActionType.Rewrite, @@ -58,10 +57,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite "; - var condList = new List(); + var condList = new ConditionCollection(); condList.Add(new Condition { - Input = new InputParser().ParseInputString("{HTTPS}", global: false), + Input = new InputParser().ParseInputString("{HTTPS}"), Match = new RegexMatch(new Regex("^OFF$"), false) }); @@ -102,10 +101,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite "; - var condList = new List(); + var condList = new ConditionCollection(); condList.Add(new Condition { - Input = new InputParser().ParseInputString("{HTTPS}", global: false), + Input = new InputParser().ParseInputString("{HTTPS}"), Match = new RegexMatch(new Regex("^OFF$"), false) }); @@ -160,9 +159,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite } // Creates a rule with appropriate default values of the url rewrite rule. - private IISUrlRewriteRule CreateTestRule(List conditions, - LogicalGrouping condGrouping = LogicalGrouping.MatchAll, - bool condTracking = false, + private IISUrlRewriteRule CreateTestRule(ConditionCollection conditions, string name = "", bool enabled = true, PatternSyntax patternSyntax = PatternSyntax.ECMAScript, @@ -174,11 +171,17 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite string pattern = "", bool appendQueryString = false, bool rewrittenUrl = false, + bool global = false, + UriMatchPart uriMatchPart = UriMatchPart.Path, RedirectType redirectType = RedirectType.Permanent ) { - return new IISUrlRewriteRule(name, new RegexMatch(new Regex("^OFF$"), false), conditions, - new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url, global: false), queryStringAppend: false), trackAllCaptures: false); + return new IISUrlRewriteRule( + name, + new RegexMatch(new Regex("^OFF$"), negate), + conditions, + new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url, uriMatchPart), queryStringAppend: false), + global); } // 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 d636f152e8..62246a8948 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite public void InputParser_ParseLiteralString() { var testString = "hello/hey/what"; - var result = new InputParser().ParseInputString(testString, global: false); + var result = new InputParser().ParseInputString(testString, UriMatchPart.Path); Assert.Equal(1, result.PatternSegments.Count); } @@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("foo/", 1)] public void InputParser_ParseStringWithBackReference(string testString, int expected) { - var result = new InputParser().ParseInputString(testString, global: false); + var result = new InputParser().ParseInputString(testString, UriMatchPart.Path); Assert.Equal(expected, result.PatternSegments.Count); } @@ -47,7 +47,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, global: false); + var middle = new InputParser().ParseInputString(testString, UriMatchPart.Path); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); Assert.Equal(expected, result); } @@ -60,7 +60,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, global: false); + var middle = new InputParser().ParseInputString(testString, UriMatchPart.Path); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); Assert.Equal(expected, result); } @@ -69,7 +69,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, global: false); + var middle = new InputParser().ParseInputString(testString, UriMatchPart.Path); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); Assert.Equal(expected, result); } @@ -88,13 +88,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [InlineData("{HTTPS")] public void FormatExceptionsOnBadSyntax(string testString) { - Assert.Throws(() => new InputParser().ParseInputString(testString, global: false)); + Assert.Throws(() => new InputParser().ParseInputString(testString, UriMatchPart.Path)); } [Fact] public void Should_throw_FormatException_if_no_rewrite_maps_are_defined() { - Assert.Throws(() => new InputParser(null).ParseInputString("{apiMap:{R:1}}", global: false)); + Assert.Throws(() => new InputParser(null).ParseInputString("{apiMap:{R:1}}", UriMatchPart.Path)); } [Fact] @@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite const string undefinedMapName = "apiMap"; var map = new IISRewriteMap(definedMapName); var maps = new IISRewriteMapCollection { map }; - Assert.Throws(() => new InputParser(maps).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", global: false)); + Assert.Throws(() => new InputParser(maps).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", UriMatchPart.Path)); } [Fact] @@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var maps = new IISRewriteMapCollection { map }; var inputString = $"{{{expectedMapName}:{{R:1}}}}"; - var pattern = new InputParser(maps).ParseInputString(inputString, global: false); + var pattern = new InputParser(maps).ParseInputString(inputString, UriMatchPart.Path); Assert.Equal(1, pattern.PatternSegments.Count); var segment = pattern.PatternSegments.Single(); diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs index 006c88f4e7..542cd88060 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs @@ -2,14 +2,19 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.IO; using System.Net; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Rewrite.Internal; using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; +using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; +using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; using Microsoft.AspNetCore.TestHost; using Microsoft.Net.Http.Headers; using Xunit; @@ -456,8 +461,9 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite } [Fact] - public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri() + public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri_ParsedRule() { + // arrange var xml = @" @@ -478,8 +484,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite }); var server = new TestServer(builder); + // act var response = await server.CreateClient().GetStringAsync($"http://localhost/{Guid.NewGuid()}/foo/bar"); + // assert Assert.Equal("http://www.test.com/foo/bar", response); } @@ -543,5 +551,52 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite Assert.Equal("reason", response.ReasonPhrase); Assert.Equal("description", content); } + + [Theory] + [InlineData(@"^http://localhost(/.*)", "http://localhost/foo/bar", UriMatchPart.Path)] + [InlineData(@"^http://localhost(/.*)", "http://www.test.com/foo/bar", UriMatchPart.Full)] + public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri_CodedRule(string conditionInputPattern, string expectedResult, UriMatchPart uriMatchPart) + { + // arrange + var inputParser = new InputParser(); + + var ruleBuilder = new UrlRewriteRuleBuilder + { + Name = "test", + Global = false + }; + ruleBuilder.AddUrlMatch(".*"); + + var condition = new UriMatchCondition( + inputParser, + "{REQUEST_URI}", + conditionInputPattern, + uriMatchPart, + ignoreCase: true, + negate: false); + ruleBuilder.ConfigureConditionBehavior(LogicalGrouping.MatchAll, trackAllCaptures: true); + ruleBuilder.AddUrlCondition(condition); + + var action = new RewriteAction( + RuleResult.SkipRemainingRules, + inputParser.ParseInputString(@"http://www.test.com{C:1}", uriMatchPart), + queryStringAppend: false); + ruleBuilder.AddUrlAction(action); + + var options = new RewriteOptions().Add(ruleBuilder.Build()); + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRewriter(options); + app.Run(context => context.Response.WriteAsync(context.Request.GetEncodedUrl())); + }); + var server = new TestServer(builder); + + // act + var response = await server.CreateClient().GetStringAsync("http://localhost/foo/bar"); + + // assert + Assert.Equal(expectedResult, response); + } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs index c8716e144f..b9acf1442c 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs @@ -13,25 +13,25 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite public class ServerVariableTests { [Theory] - [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) + [InlineData("CONTENT_LENGTH", "10", UriMatchPart.Path)] + [InlineData("CONTENT_TYPE", "json", UriMatchPart.Path)] + [InlineData("HTTP_ACCEPT", "accept", UriMatchPart.Path)] + [InlineData("HTTP_COOKIE", "cookie", UriMatchPart.Path)] + [InlineData("HTTP_HOST", "example.com", UriMatchPart.Path)] + [InlineData("HTTP_REFERER", "referer", UriMatchPart.Path)] + [InlineData("HTTP_USER_AGENT", "useragent", UriMatchPart.Path)] + [InlineData("HTTP_CONNECTION", "connection", UriMatchPart.Path)] + [InlineData("HTTP_URL", "/foo", UriMatchPart.Path)] + [InlineData("HTTP_URL", "http://example.com/foo?bar=1", UriMatchPart.Full)] + [InlineData("QUERY_STRING", "bar=1", UriMatchPart.Path)] + [InlineData("REQUEST_FILENAME", "/foo", UriMatchPart.Path)] + [InlineData("REQUEST_URI", "/foo", UriMatchPart.Path)] + [InlineData("REQUEST_URI", "http://example.com/foo?bar=1", UriMatchPart.Full)] + public void CheckServerVariableParsingAndApplication(string variable, string expected, UriMatchPart uriMatchPart) { // Arrange and Act var testParserContext = new ParserContext("test"); - var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, global); + var serverVar = ServerVariables.FindServerVariable(variable, testParserContext, uriMatchPart); var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); // Assert Assert.Equal(expected, lookup); @@ -72,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, global: false); + var serverVar = ServerVariables.FindServerVariable("QUERY_STRING", testParserContext, UriMatchPart.Path); var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); Assert.Equal(string.Empty, lookup); diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/UrlRewriteApplicationTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/UrlRewriteApplicationTests.cs index 592492a17f..ff9e9f8a59 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/UrlRewriteApplicationTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/UrlRewriteApplicationTests.cs @@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var rules = new UrlRewriteFileParser().Parse(xml); Assert.Equal(rules.Count, 1); - Assert.True(rules[0].TrackAllCaptures); + Assert.True(rules[0].Conditions.TrackAllCaptures); var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; rules.FirstOrDefault().ApplyRule(context); Assert.Equal(RuleResult.ContinueRules, context.Result); diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs deleted file mode 100644 index ca41021c75..0000000000 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/GlobalRuleUrlSegmentTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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 ee53d56a42..e85c5662b4 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/UrlSegmentTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/UrlSegmentTests.cs @@ -2,6 +2,7 @@ // 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.IISUrlRewrite; using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments; using Xunit; @@ -10,10 +11,19 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments public class UrlSegmentTests { [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) + [InlineData("http", "localhost", 80, null, UriMatchPart.Path, "")] + [InlineData("http", "localhost", 80, "", UriMatchPart.Path, "")] + [InlineData("http", "localhost", 80, "/foo/bar", UriMatchPart.Path, "/foo/bar")] + [InlineData("http", "localhost", 80, "/foo:bar", UriMatchPart.Path, "/foo:bar")] + [InlineData("http", "localhost", 80, "/foo bar", UriMatchPart.Path, "/foo%20bar")] + [InlineData("http", "localhost", 80, null, UriMatchPart.Full, "http://localhost:80/")] + [InlineData("http", "localhost", 80, "", UriMatchPart.Full, "http://localhost:80/")] + [InlineData("http", "localhost", 80, "/foo:bar", UriMatchPart.Full, "http://localhost:80/foo:bar")] + [InlineData("http", "localhost", 80, "/foo bar", UriMatchPart.Full, "http://localhost:80/foo%20bar")] + [InlineData("http", "localhost", 80, "/foo/bar", UriMatchPart.Full, "http://localhost:80/foo/bar")] + [InlineData("http", "localhost", 81, "/foo/bar", UriMatchPart.Full, "http://localhost:81/foo/bar")] + [InlineData("https", "localhost", 443, "/foo/bar", UriMatchPart.Full, "https://localhost:443/foo/bar")] + public void AssertSegmentIsCorrect(string scheme, string host, int port, string path, UriMatchPart uriMatchPart, string expectedResult) { // Arrange var httpContext = new DefaultHttpContext(); @@ -25,7 +35,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments context.HttpContext = httpContext; // Act - var segment = new UrlSegment(); + var segment = new UrlSegment(uriMatchPart); var results = segment.Evaluate(context, null, null); // Assert