Adding support for trackAllCaptures (#178)

This commit is contained in:
Chris R 2016-12-20 15:36:10 -08:00 committed by Mikael Mengistu
parent 46adbc3e8f
commit f813ee4d97
50 changed files with 284 additions and 118 deletions

View File

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

View File

@ -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<string> _backReferences = new List<string>();
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);
}
}
}
}

View File

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

View File

@ -7,10 +7,15 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
{
public static class ConditionHelper
{
public static MatchResults Evaluate(IEnumerable<Condition> conditions, RewriteContext context, MatchResults ruleMatch)
public static MatchResults Evaluate(IEnumerable<Condition> conditions, RewriteContext context, BackReferenceCollection backReferences)
{
MatchResults prevCond = null;
return Evaluate(conditions, context, backReferences, trackAllCaptures: false);
}
public static MatchResults Evaluate(IEnumerable<Condition> 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 }; ;
}
}
}

View File

@ -13,16 +13,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
public UrlMatch InitialMatch { get; }
public IList<Condition> Conditions { get; }
public UrlAction Action { get; }
public bool TrackAllCaptures { get; }
public IISUrlRewriteRule(string name,
UrlMatch initialMatch,
IList<Condition> 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);
}
}
}

View File

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

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
private IList<Condition> _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<Condition>();
_matchAny = logicalGrouping == LogicalGrouping.MatchAny;
_trackAllCaptures = trackAllCaptures;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<IHttpRequestFeature>()?.Protocol;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -264,26 +264,5 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
var ex = Assert.Throws<FormatException>(() => new UrlRewriteFileParser().Parse(new StringReader(input)));
Assert.Equal(expected, ex.Message);
}
[Theory]
[InlineData(
@"<rewrite>
<rules>
<rule name=""Remove trailing slash"">
<match url = ""(.*)/$""/>
<conditions trackAllCaptures=""true"">
<add input=""{REQUEST_FILENAME}""/>
</conditions>
<action type=""Redirect"" url =""{R:1}"" />
</rule>
</rules>
</rewrite>",
"Support for trackAllCaptures has not been implemented yet")]
public void ThrowNotSupportedExceptionWithCorrectMessage(string input, string expected)
{
// Arrange, Act, Assert
var ex = Assert.Throws<NotSupportedException>(() => new UrlRewriteFileParser().Parse(new StringReader(input)));
Assert.Equal(expected, ex.Message);
}
}
}

View File

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

View File

@ -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(@"<rewrite>
<rules>
<rule name=""Test"">
<match url=""(.*)"" ignoreCase=""false"" />
<conditions trackAllCaptures = ""true"" >
<add input=""{REQUEST_URI}"" pattern=""^/([a-zA-Z]+)/([0-9]+)$"" />
<add input=""{QUERY_STRING}"" pattern=""p2=([a-z]+)"" />
</conditions>
<action type=""Redirect"" url =""blogposts/{C:1}/{C:4}"" />
<!--rewrite action uses back - references to both conditions -->
</rule>
</rules>
</rewrite>"));
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(@"<rewrite>
<rules>
<rule name=""Test"">
<match url=""(.*)"" ignoreCase=""false"" />
<conditions trackAllCaptures = ""true"" >
<add input=""{REQUEST_URI}"" pattern=""^/([a-zA-Z]+)/([0-9]+)$"" />
<add input=""{QUERY_STRING}"" pattern=""p2=([a-z]+)"" />
</conditions>
<action type=""Redirect"" url =""blog/{R:0}/{C:4}"" />
<!--rewrite action uses back - references to both conditions -->
</rule>
</rules>
</rewrite>"));
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(@"<rewrite>
<rules>
<rule name=""Test"">
<match url=""(.*)"" ignoreCase=""false"" />
<conditions trackAllCaptures = ""true"" >
<add input=""{REQUEST_URI}"" pattern=""^/([a-zA-Z]+)/([0-9]+)$"" />
<add input=""{QUERY_STRING}"" pattern=""p2=([a-z]+)"" />
</conditions>
<action type=""Redirect"" url =""blog/{R:0}/{C:9}"" />
<!--rewrite action uses back - references to both conditions -->
</rule>
</rules>
</rewrite>"));
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRewriter(options);
});
var server = new TestServer(builder);
var ex = await Assert.ThrowsAsync<IndexOutOfRangeException>(() => 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);
}
}
}

View File

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

View File

@ -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(@"<rewrite>
<rules>
<rule name=""Test"">
<match url = ""(.*)"" ignoreCase=""false"" />
<conditions trackAllCaptures = ""true"" >
<add input = ""{REQUEST_URI}"" pattern = ""^/([a-zA-Z]+)/([0-9]+)/$"" />
</conditions >
<action type = ""None""/>
</rule>
</rules>
</rewrite>");
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);
}
}
}

View File

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

View File

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

View File

@ -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-*",