diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs index ffb907f304..a6d03b7fba 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ApacheModRewrite/ApacheModRewriteRule.cs @@ -30,11 +30,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite return; } - MatchResults condMatchRes = null; + BackReferenceCollection condBackReferences = null; if (Conditions != null) { - condMatchRes = ConditionHelper.Evaluate(Conditions, context, initMatchRes); - if (!condMatchRes.Success) + var condResult = ConditionHelper.Evaluate(Conditions, context, initMatchRes.BackReferences); + if (!condResult.Success) { context.Logger?.ModRewriteDidNotMatchRule(); return; @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite foreach (var action in Actions) { - action.ApplyAction(context, initMatchRes, condMatchRes); + action.ApplyAction(context, initMatchRes?.BackReferences, condBackReferences); } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/BackReferenceCollection.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/BackReferenceCollection.cs new file mode 100644 index 0000000000..32b91a19ce --- /dev/null +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/BackReferenceCollection.cs @@ -0,0 +1,53 @@ +// 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.Generic; +using System.Text.RegularExpressions; + +namespace Microsoft.AspNetCore.Rewrite.Internal +{ + public class BackReferenceCollection + { + private List _backReferences = new List(); + + public BackReferenceCollection(GroupCollection references) + { + if (references != null) + { + for (var i = 0; i < references.Count; i++) + { + _backReferences.Add(references[i].Value); + } + } + } + + public BackReferenceCollection(string reference) + { + _backReferences.Add(reference); + } + + public string this[int index] + { + get + { + if (index < _backReferences.Count) + { + return _backReferences[index]; + } + else + { + throw new IndexOutOfRangeException($"Cannot access back reference at index {index}. Only {_backReferences.Count} back references were captured."); + } + } + } + + public void Add(BackReferenceCollection references) + { + if (references != null) + { + _backReferences.AddRange(references._backReferences); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs index 5c60e38d82..314e63a62c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs @@ -9,9 +9,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal public UrlMatch Match { get; set; } public bool OrNext { get; set; } - public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public MatchResults Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { - var pattern = Input.Evaluate(context, ruleMatch, condMatch); + var pattern = Input.Evaluate(context, ruleBackReferences, conditionBackReferences); return Match.Evaluate(pattern, context); } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs index 4407ec3ce8..7a3c4365c1 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ConditionEvaluator.cs @@ -7,10 +7,15 @@ namespace Microsoft.AspNetCore.Rewrite.Internal { public static class ConditionHelper { - - public static MatchResults Evaluate(IEnumerable conditions, RewriteContext context, MatchResults ruleMatch) + public static MatchResults Evaluate(IEnumerable conditions, RewriteContext context, BackReferenceCollection backReferences) { - MatchResults prevCond = null; + return Evaluate(conditions, context, backReferences, trackAllCaptures: false); + } + + public static MatchResults Evaluate(IEnumerable conditions, RewriteContext context, BackReferenceCollection backReferences, bool trackAllCaptures) + { + BackReferenceCollection prevBackReferences = null; + MatchResults condResult = null; var orSucceeded = false; foreach (var condition in conditions) { @@ -24,18 +29,27 @@ namespace Microsoft.AspNetCore.Rewrite.Internal continue; } - prevCond = condition.Evaluate(context, ruleMatch, prevCond); - + condResult = condition.Evaluate(context, backReferences, prevBackReferences); + var currentBackReferences = condResult.BackReferences; if (condition.OrNext) { - orSucceeded = prevCond.Success; + orSucceeded = condResult.Success; } - else if (!prevCond.Success) + else if (!condResult.Success) { - return prevCond; + return condResult; } + + if (condResult.Success && trackAllCaptures && prevBackReferences!= null) + { + prevBackReferences.Add(currentBackReferences); + currentBackReferences = prevBackReferences; + } + + prevBackReferences = currentBackReferences; } - return prevCond; + + return new MatchResults { BackReferences = prevBackReferences, Success = condResult.Success }; ; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs index 3dc0bce3f4..d2c9b41acd 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/IISUrlRewriteRule.cs @@ -13,16 +13,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite public UrlMatch InitialMatch { get; } public IList Conditions { get; } public UrlAction Action { get; } + public bool TrackAllCaptures { get; } public IISUrlRewriteRule(string name, UrlMatch initialMatch, IList conditions, - UrlAction action) + UrlAction action, + bool trackAllCaptures) { Name = name; InitialMatch = initialMatch; Conditions = conditions; Action = action; + TrackAllCaptures = trackAllCaptures; } public virtual void ApplyRule(RewriteContext context) @@ -46,11 +49,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite return; } - MatchResults condMatchRes = null; + MatchResults condResult = null; if (Conditions != null) { - condMatchRes = ConditionHelper.Evaluate(Conditions, context, initMatchResults); - if (!condMatchRes.Success) + condResult = ConditionHelper.Evaluate(Conditions, context, initMatchResults.BackReferences, TrackAllCaptures); + if (!condResult.Success) { context.Logger?.UrlRewriteDidNotMatchRule(Name); return; @@ -59,7 +62,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite context.Logger?.UrlRewriteMatchedRule(Name); // at this point we know the rule passed, evaluate the replacement. - Action.ApplyAction(context, initMatchResults, condMatchRes); + Action.ApplyAction(context, initMatchResults?.BackReferences, condResult?.BackReferences); } } } \ 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 889d3e209a..02ecde3562 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteFileParser.cs @@ -110,19 +110,15 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite var grouping = ParseEnum(conditions, RewriteTags.LogicalGrouping, LogicalGrouping.MatchAll); var trackAllCaptures = ParseBool(conditions, RewriteTags.TrackAllCaptures, defaultValue: false); - if (trackAllCaptures) - { - throw new NotSupportedException("Support for trackAllCaptures has not been implemented yet"); - } builder.AddUrlConditions(grouping, trackAllCaptures); foreach (var cond in conditions.Elements(RewriteTags.Add)) { - ParseCondition(cond, builder, patternSyntax); + ParseCondition(cond, builder, patternSyntax, trackAllCaptures); } } - private void ParseCondition(XElement condition, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) + private void ParseCondition(XElement condition, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax, bool trackAllCaptures) { var ignoreCase = ParseBool(condition, RewriteTags.IgnoreCase, defaultValue: true); var negate = ParseBool(condition, RewriteTags.Negate, defaultValue: false); @@ -138,7 +134,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite try { var input = _inputParser.ParseInputString(parsedInputString); - builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate); + builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures); } catch (FormatException formatException) { diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs index f6603b2df8..e7587ba5da 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/IISUrlRewrite/UrlRewriteRuleBuilder.cs @@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite private IList _conditions; private UrlAction _action; private bool _matchAny; + private bool _trackAllCaptures; public IISUrlRewriteRule Build() { @@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match"); } - return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action); + return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, _trackAllCaptures); } public void AddUrlAction( @@ -85,12 +86,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite } } - public void AddUrlCondition(Pattern input, string pattern, PatternSyntax patternSyntax, MatchType matchType, bool ignoreCase, bool negate) + public void AddUrlCondition(Pattern input, string pattern, PatternSyntax patternSyntax, MatchType matchType, bool ignoreCase, bool negate, bool trackAllCaptures) { // If there are no conditions specified if (_conditions == null) { - AddUrlConditions(LogicalGrouping.MatchAll, trackAllCaptures: false); + AddUrlConditions(LogicalGrouping.MatchAll, trackAllCaptures); } switch (patternSyntax) @@ -148,6 +149,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite { _conditions = new List(); _matchAny = logicalGrouping == LogicalGrouping.MatchAny; + _trackAllCaptures = trackAllCaptures; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs index 71eb20b8da..13c505d8f0 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs @@ -7,10 +7,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal { public class MatchResults { - public static readonly MatchResults EmptySuccess = new MatchResults { BackReference = null, Success = true }; - public static readonly MatchResults EmptyFailure = new MatchResults { BackReference = null, Success = false }; + public static readonly MatchResults EmptySuccess = new MatchResults { Success = true }; + public static readonly MatchResults EmptyFailure = new MatchResults { Success = false }; - public GroupCollection BackReference { get; set; } public bool Success { get; set; } + public BackReferenceCollection BackReferences { get; set; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs index 590bdccdb4..fe33dfa628 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs @@ -13,11 +13,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal PatternSegments = patternSegments; } - public string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { foreach (var pattern in PatternSegments) { - context.Builder.Append(pattern.Evaluate(context, ruleMatch, condMatch)); + context.Builder.Append(pattern.Evaluate(context, ruleBackReferences, conditionBackReferences)); } var retVal = context.Builder.ToString(); context.Builder.Clear(); diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs index 0cc7b7ddce..ef2b342065 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs @@ -5,6 +5,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal { public abstract class PatternSegment { - public abstract string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch); + public abstract string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences); } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs index 5ca4cc2b97..6c131e2c1c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs @@ -12,9 +12,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments _index = index; } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { - return condMatch?.BackReference[_index].Value; + return conditionBackReferences?[_index]; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs index 2d5202eeb8..588a19de27 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs @@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments } } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReference) { switch (_portion) { diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs index 5327c969fb..83bb5918e9 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments _header = header; } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.Headers[_header]; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs index 3b3f39339d..ba42a8ce03 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs @@ -5,9 +5,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class IsHttpsModSegment : PatternSegment { - // Note: Mod rewrite pattern matches on lower case "on" and "off" + // Note: Mod rewrite pattern matches on lower case "on" and "off" // while IIS looks for capitalized "ON" and "OFF" - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.IsHttps ? "on" : "off"; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs index 21beccc746..106bc089c7 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs @@ -4,10 +4,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class IsHttpsUrlSegment : PatternSegment - { - // Note: Mod rewrite pattern matches on lower case "on" and "off" + { + // Note: Mod rewrite pattern matches on lower case "on" and "off" // while IIS looks for capitalized "ON" and "OFF" - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.IsHttps ? "ON" : "OFF"; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs index 8131ae59a5..858dd45c85 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments public class IsIPV6Segment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { if (context.HttpContext.Connection.RemoteIpAddress == null) { diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs index 17d240db4a..c89818c09b 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments _literal = literal; } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return _literal; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs index 5fc5ef8c95..5b6e0a2c66 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class LocalAddressSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Connection.LocalIpAddress?.ToString(); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs index d7f62e2905..dc39080723 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class LocalPortSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Connection.LocalPort.ToString(CultureInfo.InvariantCulture); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs index bb1118e864..c2b2c56c39 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class QueryStringSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackRefernces, BackReferenceCollection conditionBackReferences) { var queryString = context.HttpContext.Request.QueryString.ToString(); diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs index c18ec14ab7..acdb9f3a41 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class RemoteAddressSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Connection.RemoteIpAddress?.ToString(); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs index 9c0e0257bb..720e5493ca 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class RemotePortSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Connection.RemotePort.ToString(CultureInfo.InvariantCulture); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs index fe74df5c2b..fb3615b9c0 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class RequestFileNameSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.Path; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs index b10103ebf5..ffd106380c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class RequestMethodSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.Method; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs index 2b008c6109..cdd4b38e24 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs @@ -12,9 +12,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments _index = index; } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { - return ruleMatch?.BackReference[_index].Value; + return ruleBackReferences[_index]; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs index cd2bff5c06..bea7073308 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class SchemeSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.Scheme; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs index d2b1d8e6ec..bf7f355194 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class ServerProtocolSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Features.Get()?.Protocol; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs index 0588b2b526..dec2029658 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs @@ -14,13 +14,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments _pattern = pattern; } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { // PERF as we share the string builder across the context, we need to make a new one here to evaluate // lowercase segments. var tempBuilder = context.Builder; context.Builder = new StringBuilder(64); - var pattern = _pattern.Evaluate(context, ruleMatch, condMatch); + var pattern = _pattern.Evaluate(context, ruleBackReferences, conditionBackReferences); context.Builder = tempBuilder; return pattern.ToLowerInvariant(); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs index e5ad2aac30..dc99dd4d51 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments _pattern = pattern; } - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { var oldBuilder = context.Builder; // PERF @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments // we provided a new string builder and evaluate the new pattern, // and restore it after evaluation. context.Builder = new StringBuilder(64); - var pattern = _pattern.Evaluate(context, ruleMatch, condMatch); + var pattern = _pattern.Evaluate(context, ruleBackReferences, conditionBackReferences); context.Builder = oldBuilder; return UrlEncoder.Default.Encode(pattern); } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs index da8adce3f7..005a335f8c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class UrlSegment : PatternSegment { - public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override string Evaluate(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { return context.HttpContext.Request.Path; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs index a833ee9b17..b01e0c1bd8 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs @@ -6,6 +6,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal public abstract class UrlAction { protected Pattern Url { get; set; } - public abstract void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch); + public abstract void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences); } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/AbortAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/AbortAction.cs index e0ac36396c..32367f5f7b 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/AbortAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/AbortAction.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { public class AbortAction : UrlAction { - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { context.HttpContext.Abort(); context.Result = RuleResult.EndResponse; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs index 640a022672..c758111ca1 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions public bool Secure { get; set; } public bool HttpOnly { get; set; } - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { var options = GetOrCreateOptions(); context.HttpContext.Response.Cookies.Append(Name, Value ?? string.Empty, options); diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs index 8e4058cc97..3a1c14e75f 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { public class ForbiddenAction : UrlAction { - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden; context.Result = RuleResult.EndResponse; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs index a1e7b964e6..f85cd22e3b 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { public class GoneAction : UrlAction { - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { context.HttpContext.Response.StatusCode = StatusCodes.Status410Gone; context.Result = RuleResult.EndResponse; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs index 06eba46b04..f3ac9d3196 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs @@ -41,9 +41,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { } - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { - var pattern = Url.Evaluate(context, ruleMatch, condMatch); + var pattern = Url.Evaluate(context, ruleBackReferences, conditionBackReferences); var response = context.HttpContext.Response; var pathBase = context.HttpContext.Request.PathBase; if (EscapeBackReferences) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs index bd9a7fd63b..8f9c5818d3 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs @@ -43,9 +43,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions } - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { - var pattern = Url.Evaluate(context, ruleMatch, condMatch); + var pattern = Url.Evaluate(context, ruleBackReferences, conditionBackReferences); var request = context.HttpContext.Request; if (string.IsNullOrEmpty(pattern)) diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs index 6023b23a7a..95a092a0f8 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions Result = result; } // Explicitly say that nothing happens - public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, BackReferenceCollection ruleBackReferences, BackReferenceCollection conditionBackReferences) { context.Result = Result; } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs index 58cc914b84..23db120580 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs @@ -18,7 +18,15 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches public override MatchResults Evaluate(string pattern, RewriteContext context) { var pathMatch = string.Compare(pattern, _stringMatch, _ignoreCase); - return new MatchResults { Success = ((pathMatch == 0) != Negate) }; + var success = ((pathMatch == 0) != Negate); + if (success) + { + return new MatchResults { Success = success, BackReferences = new BackReferenceCollection(pattern) }; + } + else + { + return MatchResults.EmptyFailure; + } } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs index eb880c18ec..131eea8f9d 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches public override MatchResults Evaluate(string pattern, RewriteContext context) { var res = _match.Match(pattern); - return new MatchResults { BackReference = res.Groups, Success = (res.Success != Negate) }; + return new MatchResults { BackReferences = new BackReferenceCollection(res.Groups), Success = (res.Success != Negate) }; } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ApacheModRewrite/ModRewriteMiddlewareTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ApacheModRewrite/ModRewriteMiddlewareTest.cs index 3b1d962751..f22f340f18 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/ApacheModRewrite/ModRewriteMiddlewareTest.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/ApacheModRewrite/ModRewriteMiddlewareTest.cs @@ -136,10 +136,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite Assert.Equal("/", response); } - [Fact] - public async Task Invoke_BackReferencesShouldBeApplied() + [Theory] + [InlineData("http://www.foo.org/homepage.aspx", @"RewriteRule (.*)\.aspx $1.php", "/homepage.php")] + [InlineData("http://www.foo.org/pages/homepage.aspx", @"RewriteRule (.*)/(.*)\.aspx $2.php", "/homepage.php")] + public async Task Invoke_BackReferencesShouldBeApplied(string url, string rule, string expected) { - var options = new RewriteOptions().AddApacheModRewrite(new StringReader(@"RewriteRule (.*)\.aspx $1.php")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader(rule)); var builder = new WebHostBuilder() .Configure(app => { @@ -148,9 +150,9 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite }); var server = new TestServer(builder); - var response = await server.CreateClient().GetStringAsync("http://www.foo.org/homepage.aspx"); + var response = await server.CreateClient().GetStringAsync(url); - Assert.Equal("/homepage.php", response); + Assert.Equal(expected, response); } [Theory] diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs index dd55225c2b..1a8aaeab43 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FileParserTests.cs @@ -147,7 +147,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)); + new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url), queryStringAppend: false), trackAllCaptures: false); } // TODO make rules comparable? diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs index 575135b543..c14f062e64 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/FormatExceptionHandlingTests.cs @@ -264,26 +264,5 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input))); Assert.Equal(expected, ex.Message); } - - [Theory] - [InlineData( -@" - - - - - - - - - -", - "Support for trackAllCaptures has not been implemented yet")] - public void ThrowNotSupportedExceptionWithCorrectMessage(string input, string expected) - { - // Arrange, Act, Assert - var ex = Assert.Throws(() => new UrlRewriteFileParser().Parse(new StringReader(input))); - Assert.Equal(expected, ex.Message); - } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs index df59ae4596..590785c3d3 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/InputParserTests.cs @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite public void EvaluateBackReferenceRule(string testString, string expected) { var middle = new InputParser().ParseInputString(testString); - var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch(), CreateTestCondMatch()); + var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); Assert.Equal(expected, result); } @@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite public void EvaluatToLowerRule(string testString, string expected) { var middle = new InputParser().ParseInputString(testString); - var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch(), CreateTestCondMatch()); + var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); Assert.Equal(expected, result); } @@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite public void EvaluatUriEncodeRule(string testString, string expected) { var middle = new InputParser().ParseInputString(testString); - var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch(), CreateTestCondMatch()); + var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); Assert.Equal(expected, result); } @@ -95,16 +95,16 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite return new RewriteContext { HttpContext = context, StaticFileProvider = null }; } - private MatchResults CreateTestRuleMatch() + private BackReferenceCollection CreateTestRuleBackReferences() { var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)"); - return new MatchResults { BackReference = match.Groups, Success = match.Success }; + return new BackReferenceCollection(match.Groups); } - private MatchResults CreateTestCondMatch() + private BackReferenceCollection CreateTestCondBackReferences() { var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)"); - return new MatchResults { BackReference = match.Groups, Success = match.Success }; + return new BackReferenceCollection(match.Groups); } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs index 2951b9777e..b2724e8d58 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/MiddleWareTests.cs @@ -364,7 +364,92 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var response = await server.CreateClient().GetAsync("hey/hello"); - Assert.Equal("/hey/hello/", response.Headers.Location.OriginalString); ; + Assert.Equal("/hey/hello/", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task VerifyTrackAllCaptures() + { + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" + + + + + + + + + + + + ")); + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRewriter(options); + }); + var server = new TestServer(builder); + + var response = await server.CreateClient().GetAsync("article/23?p1=123&p2=abc"); + + Assert.Equal("/blogposts/article/abc", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task VerifyTrackAllCapturesRuleAndConditionCapture() + { + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" + + + + + + + + + + + + ")); + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRewriter(options); + }); + var server = new TestServer(builder); + + var response = await server.CreateClient().GetAsync("article/23?p1=123&p2=abc"); + + Assert.Equal("/blog/article/23/abc", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ThrowIndexOutOfRangeExceptionWithCorrectMessage() + { + // Arrange, Act, Assert + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" + + + + + + + + + + + + ")); + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseRewriter(options); + }); + var server = new TestServer(builder); + + var ex = await Assert.ThrowsAsync(() => server.CreateClient().GetAsync("article/23?p1=123&p2=abc")); + + Assert.Equal("Cannot access back reference at index 9. Only 5 back references were captured.", ex.Message); } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs index 19c49d8a66..1d0f0154df 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/ServerVariableTests.cs @@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite // Arrange and Act var testParserContext = new ParserContext("test"); var serverVar = ServerVariables.FindServerVariable(variable, testParserContext); - var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch()); + var lookup = serverVar.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch().BackReferences, CreateTestCondMatch().BackReferences); // Assert Assert.Equal(expected, lookup); } @@ -53,13 +53,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite private MatchResults CreateTestRuleMatch() { var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)"); - return new MatchResults { BackReference = match.Groups, Success = match.Success }; + return new MatchResults { BackReferences = new BackReferenceCollection(match.Groups), Success = match.Success }; } private MatchResults CreateTestCondMatch() { var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)"); - return new MatchResults { BackReference = match.Groups, Success = match.Success }; + return new MatchResults { BackReferences = new BackReferenceCollection(match.Groups), Success = match.Success }; } [Fact] @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite var rewriteContext = new RewriteContext { HttpContext = context }; var testParserContext = new ParserContext("test"); var serverVar = ServerVariables.FindServerVariable("QUERY_STRING", testParserContext); - var lookup = serverVar.Evaluate(rewriteContext, CreateTestRuleMatch(), CreateTestCondMatch()); + 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 87fcfdbae0..592492a17f 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/UrlRewriteApplicationTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/IISUrlRewrite/UrlRewriteApplicationTests.cs @@ -49,5 +49,28 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite rules.FirstOrDefault().ApplyRule(context); Assert.Equal(RuleResult.ContinueRules, context.Result); } + + [Fact] + public void ApplyRule_TrackAllCaptures() + { + var xml = new StringReader(@" + + + + + + + + + + "); + var rules = new UrlRewriteFileParser().Parse(xml); + + Assert.Equal(rules.Count, 1); + Assert.True(rules[0].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/ConditionMatchSegmentTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/ConditionMatchSegmentTests.cs index 51208cbba5..a67113954f 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/ConditionMatchSegmentTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/ConditionMatchSegmentTests.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments var segment = new ConditionMatchSegment(index); // Act - var results = segment.Evaluate(null, null, condMatch); + var results = segment.Evaluate(null, null, condMatch.BackReferences); // Assert Assert.Equal(expected, results); @@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments private static MatchResults CreateTestMatch() { var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)"); - return new MatchResults {BackReference = match.Groups, Success = match.Success}; + return new MatchResults { BackReferences = new BackReferenceCollection(match.Groups), Success = match.Success }; } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/RuleMatchSegmentTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/RuleMatchSegmentTests.cs index 8e4d8ccfc4..56189c9bee 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/RuleMatchSegmentTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/RuleMatchSegmentTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments var segment = new RuleMatchSegment(index); // Act - var results = segment.Evaluate(null, ruleMatch, null); + var results = segment.Evaluate(null, ruleMatch.BackReferences, null); // Assert Assert.Equal(expected, results); @@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments private static MatchResults CreateTestMatch() { var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)"); - return new MatchResults { BackReference = match.Groups, Success = match.Success }; + return new MatchResults { BackReferences = new BackReferenceCollection(match.Groups), Success = match.Success }; } } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/project.json b/test/Microsoft.AspNetCore.Rewrite.Tests/project.json index 7120835f39..725a723427 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/project.json +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/project.json @@ -5,6 +5,7 @@ }, "dependencies": { "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.Hosting": "1.2.0-*", "Microsoft.AspNetCore.Rewrite": "1.1.0-*", "Microsoft.AspNetCore.TestHost": "1.2.0-*", "Microsoft.Extensions.Logging.Testing": "1.2.0-*",