Make condition collection a first class citizen (#198)

This commit is contained in:
David Peden 2017-02-13 17:35:20 -06:00 committed by Mikael Mengistu
parent b254f42d19
commit ae7d0b9582
24 changed files with 423 additions and 224 deletions

View File

@ -22,7 +22,6 @@ namespace RewriteSample
.AddApacheModRewrite(env.ContentRootFileProvider, "Rewrite.txt"); .AddApacheModRewrite(env.ContentRootFileProvider, "Rewrite.txt");
app.UseRewriter(options); app.UseRewriter(options);
app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}")); app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}"));
} }

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
BackReferenceCollection condBackReferences = null; BackReferenceCollection condBackReferences = null;
if (Conditions != null) if (Conditions != null)
{ {
var condResult = ConditionHelper.Evaluate(Conditions, context, initMatchRes.BackReferences); var condResult = ConditionEvaluator.Evaluate(Conditions, context, initMatchRes.BackReferences);
if (!condResult.Success) if (!condResult.Success)
{ {
context.Logger?.ModRewriteDidNotMatchRule(); context.Logger?.ModRewriteDidNotMatchRule();
@ -51,4 +51,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
} }
} }
} }
} }

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 public class Condition
{ {
@ -15,4 +15,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
return Match.Evaluate(pattern, context); return Match.Evaluate(pattern, context);
} }
} }
} }

View File

@ -3,9 +3,9 @@
using System.Collections.Generic; 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<Condition> conditions, RewriteContext context, BackReferenceCollection backReferences) public static MatchResults Evaluate(IEnumerable<Condition> conditions, RewriteContext context, BackReferenceCollection backReferences)
{ {
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
return condResult; return condResult;
} }
if (condResult.Success && trackAllCaptures && prevBackReferences!= null) if (condResult.Success && trackAllCaptures && prevBackReferences != null)
{ {
prevBackReferences.Add(currentBackReferences); prevBackReferences.Add(currentBackReferences);
currentBackReferences = prevBackReferences; currentBackReferences = prevBackReferences;
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
prevBackReferences = currentBackReferences; prevBackReferences = currentBackReferences;
} }
return new MatchResults { BackReferences = prevBackReferences, Success = condResult.Success }; ; return new MatchResults { BackReferences = prevBackReferences, Success = condResult.Success };
} }
} }
} }

View File

@ -222,4 +222,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite
} }
} }
} }
} }

View File

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

View File

@ -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<Condition>
{
private readonly List<Condition> _conditions = new List<Condition>();
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<Condition> conditions)
{
if (conditions != null)
{
_conditions.AddRange(conditions);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return _conditions.GetEnumerator();
}
public IEnumerator<Condition> GetEnumerator()
{
return _conditions.GetEnumerator();
}
}
}

View File

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

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Http;
using Microsoft.AspNetCore.Rewrite.Logging; using Microsoft.AspNetCore.Rewrite.Logging;
@ -11,32 +10,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{ {
public string Name { get; } public string Name { get; }
public UrlMatch InitialMatch { get; } public UrlMatch InitialMatch { get; }
public IList<Condition> Conditions { get; } public ConditionCollection Conditions { get; }
public UrlAction Action { get; } public UrlAction Action { get; }
public bool TrackAllCaptures { get; }
public bool Global { get; } public bool Global { get; }
public IISUrlRewriteRule(string name, public IISUrlRewriteRule(string name,
UrlMatch initialMatch, UrlMatch initialMatch,
IList<Condition> conditions, ConditionCollection conditions,
UrlAction action, UrlAction action)
bool trackAllCaptures) : this(name, initialMatch, conditions, action, false)
: this(name, initialMatch, conditions, action, trackAllCaptures, false)
{ {
} }
public IISUrlRewriteRule(string name, public IISUrlRewriteRule(string name,
UrlMatch initialMatch, UrlMatch initialMatch,
IList<Condition> conditions, ConditionCollection conditions,
UrlAction action, UrlAction action,
bool trackAllCaptures,
bool global) bool global)
{ {
Name = name; Name = name;
InitialMatch = initialMatch; InitialMatch = initialMatch;
Conditions = conditions; Conditions = conditions;
Action = action; Action = action;
TrackAllCaptures = trackAllCaptures;
Global = global; Global = global;
} }
@ -64,7 +59,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
MatchResults condResult = null; MatchResults condResult = null;
if (Conditions != null) if (Conditions != null)
{ {
condResult = ConditionHelper.Evaluate(Conditions, context, initMatchResults.BackReferences, TrackAllCaptures); condResult = ConditionEvaluator.Evaluate(Conditions, context, initMatchResults.BackReferences);
if (!condResult.Success) if (!condResult.Success)
{ {
context.Logger?.UrlRewriteDidNotMatchRule(Name); context.Logger?.UrlRewriteDidNotMatchRule(Name);

View File

@ -28,9 +28,20 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
/// compare to the condition. Can contain server variables, back references, etc. /// compare to the condition. Can contain server variables, back references, etc.
/// </summary> /// </summary>
/// <param name="testString"></param> /// <param name="testString"></param>
/// <param name="global"></param>
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns> /// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
public Pattern ParseInputString(string testString, bool global) public Pattern ParseInputString(string testString)
{
return ParseInputString(testString, UriMatchPart.Path);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="testString"></param>
/// <param name="uriMatchPart">When testString evaluates to a URL segment, specify which part of the URI to evaluate.</param>
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
public Pattern ParseInputString(string testString, UriMatchPart uriMatchPart)
{ {
if (testString == null) if (testString == null)
{ {
@ -38,10 +49,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
var context = new ParserContext(testString); 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<PatternSegment>(); var results = new List<PatternSegment>();
while (context.Next()) while (context.Next())
@ -54,7 +65,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
// missing { // missing {
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index)); throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
} }
ParseParameter(context, results, global); ParseParameter(context, results, uriMatchPart);
} }
else if (context.Current == CloseBrace) else if (context.Current == CloseBrace)
{ {
@ -70,7 +81,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
return new Pattern(results); return new Pattern(results);
} }
private void ParseParameter(ParserContext context, IList<PatternSegment> results, bool global) private void ParseParameter(ParserContext context, IList<PatternSegment> results, UriMatchPart uriMatchPart)
{ {
context.Mark(); context.Mark();
// Four main cases: // 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. // This is just a server variable, so we do a lookup and verify the server variable exists.
parameter = context.Capture(); parameter = context.Capture();
results.Add(ServerVariables.FindServerVariable(parameter, context, global)); results.Add(ServerVariables.FindServerVariable(parameter, context, uriMatchPart));
return; return;
} }
else if (context.Current == Colon) else if (context.Current == Colon)
@ -98,7 +109,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{ {
case "ToLower": case "ToLower":
{ {
var pattern = ParseString(context, global); var pattern = ParseString(context, uriMatchPart);
results.Add(new ToLowerSegment(pattern)); results.Add(new ToLowerSegment(pattern));
// at this point, we expect our context to be on the ending closing brace, // at this point, we expect our context to be on the ending closing brace,
@ -116,7 +127,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
case "UrlEncode": case "UrlEncode":
{ {
var pattern = ParseString(context, global); var pattern = ParseString(context, uriMatchPart);
results.Add(new UrlEncodeSegment(pattern)); results.Add(new UrlEncodeSegment(pattern));
if (context.Current != CloseBrace) if (context.Current != CloseBrace)
@ -141,7 +152,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
var rewriteMap = _rewriteMaps?[parameter]; var rewriteMap = _rewriteMaps?[parameter];
if (rewriteMap != null) if (rewriteMap != null)
{ {
var pattern = ParseString(context, global); var pattern = ParseString(context, uriMatchPart);
results.Add(new RewriteMapSegment(rewriteMap, pattern)); results.Add(new RewriteMapSegment(rewriteMap, pattern));
return; return;
} }

View File

@ -14,10 +14,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
/// </summary> /// </summary>
/// <param name="serverVariable">The server variable</param> /// <param name="serverVariable">The server variable</param>
/// <param name="context">The parser context which is utilized when an exception is thrown</param> /// <param name="context">The parser context which is utilized when an exception is thrown</param>
/// <param name="global">Indicates if the rule being parsed is a global rule</param> /// <param name="uriMatchPart">Indicates whether the full URI or the path should be evaluated for URL segments</param>
/// <exception cref="FormatException">Thrown when the server variable is unknown</exception> /// <exception cref="FormatException">Thrown when the server variable is unknown</exception>
/// <returns>The matching <see cref="PatternSegment"/></returns> /// <returns>The matching <see cref="PatternSegment"/></returns>
public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, bool global) public static PatternSegment FindServerVariable(string serverVariable, ParserContext context, UriMatchPart uriMatchPart)
{ {
switch (serverVariable) switch (serverVariable)
{ {
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
case "HTTP_CONNECTION": case "HTTP_CONNECTION":
return new HeaderSegment(HeaderNames.Connection); return new HeaderSegment(HeaderNames.Connection);
case "HTTP_URL": case "HTTP_URL":
return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment(); return new UrlSegment(uriMatchPart);
case "HTTPS": case "HTTPS":
return new IsHttpsUrlSegment(); return new IsHttpsUrlSegment();
case "LOCAL_ADDR": case "LOCAL_ADDR":
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
case "REQUEST_FILENAME": case "REQUEST_FILENAME":
return new RequestFileNameSegment(); return new RequestFileNameSegment();
case "REQUEST_URI": case "REQUEST_URI":
return global ? (PatternSegment)new GlobalRuleUrlSegment() : (PatternSegment)new UrlSegment(); return new UrlSegment(uriMatchPart);
default: default:
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index)); throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(serverVariable, context.Index));
} }

View File

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

View File

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

View File

@ -7,6 +7,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
{ {
@ -45,17 +46,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
foreach (var rule in rules.Elements(RewriteTags.Rule)) foreach (var rule in rules.Elements(RewriteTags.Rule))
{ {
var builder = new UrlRewriteRuleBuilder(); var builder = new UrlRewriteRuleBuilder { Global = global };
ParseRuleAttributes(rule, builder, global); ParseRuleAttributes(rule, builder);
if (builder.Enabled) 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; builder.Name = rule.Attribute(RewriteTags.Name)?.Value;
@ -84,8 +85,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
} }
ParseMatch(match, builder, patternSyntax); ParseMatch(match, builder, patternSyntax);
ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax, global); ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax);
ParseUrlAction(action, builder, stopProcessing, global); ParseUrlAction(action, builder, stopProcessing);
} }
private void ParseMatch(XElement match, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax) 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); 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) if (conditions == null)
{ {
@ -110,32 +111,76 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
var grouping = ParseEnum(conditions, RewriteTags.LogicalGrouping, LogicalGrouping.MatchAll); var grouping = ParseEnum(conditions, RewriteTags.LogicalGrouping, LogicalGrouping.MatchAll);
var trackAllCaptures = ParseBool(conditions, RewriteTags.TrackAllCaptures, defaultValue: false); var trackAllCaptures = ParseBool(conditions, RewriteTags.TrackAllCaptures, defaultValue: false);
builder.AddUrlConditions(grouping, trackAllCaptures); builder.ConfigureConditionBehavior(grouping, trackAllCaptures);
foreach (var cond in conditions.Elements(RewriteTags.Add)) 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 ignoreCase = ParseBool(conditionElement, RewriteTags.IgnoreCase, defaultValue: true);
var negate = ParseBool(condition, RewriteTags.Negate, defaultValue: false); var negate = ParseBool(conditionElement, RewriteTags.Negate, defaultValue: false);
var matchType = ParseEnum(condition, RewriteTags.MatchType, MatchType.Pattern); var matchType = ParseEnum(conditionElement, RewriteTags.MatchType, MatchType.Pattern);
var parsedInputString = condition.Attribute(RewriteTags.Input)?.Value; var parsedInputString = conditionElement.Attribute(RewriteTags.Input)?.Value;
if (parsedInputString == null) 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 parsedPatternString = conditionElement.Attribute(RewriteTags.Pattern)?.Value;
var input = _inputParser.ParseInputString(parsedInputString, global); Condition condition;
builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate, trackAllCaptures);
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); var actionType = ParseEnum(urlAction, RewriteTags.Type, ActionType.None);
UrlAction action; 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); var appendQuery = ParseBool(urlAction, RewriteTags.AppendQueryString, defaultValue: true);
if (actionType == ActionType.Rewrite) if (actionType == ActionType.Rewrite)

View File

@ -14,21 +14,21 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }
public bool Global { get; set; }
public UriMatchPart UriMatchPart => Global ? UriMatchPart.Full : UriMatchPart.Path;
private UrlMatch _initialMatch; private UrlMatch _initialMatch;
private IList<Condition> _conditions; private ConditionCollection _conditions;
private UrlAction _action; private UrlAction _action;
private bool _matchAny;
private bool _trackAllCaptures;
public IISUrlRewriteRule Build(bool global) public IISUrlRewriteRule Build()
{ {
if (_initialMatch == null || _action == null) if (_initialMatch == null || _action == null)
{ {
throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match"); throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match");
} }
return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, _trackAllCaptures, global); return new IISUrlRewriteRule(Name, _initialMatch, _conditions, _action, Global);
} }
public void AddUrlAction(UrlAction action) 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) if (_conditions == null)
{ {
AddUrlConditions(LogicalGrouping.MatchAll, trackAllCaptures); throw new InvalidOperationException($"You must first configure condition behavior by calling {nameof(ConfigureConditionBehavior)}");
} }
if (condition == null)
switch (patternSyntax)
{ {
case PatternSyntax.ECMAScript: throw new ArgumentNullException(nameof(condition));
{
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");
} }
_conditions.Add(condition);
} }
public void AddUrlConditions(LogicalGrouping logicalGrouping, bool trackAllCaptures) public void AddUrlConditions(IEnumerable<Condition> conditions)
{ {
_conditions = new List<Condition>(); if (_conditions == null)
_matchAny = logicalGrouping == LogicalGrouping.MatchAny; {
_trackAllCaptures = trackAllCaptures; throw new InvalidOperationException($"You must first configure condition behavior by calling {nameof(ConfigureConditionBehavior)}");
}
_conditions.AddConditions(conditions);
} }
} }
} }

View File

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

View File

@ -1,13 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{ {
public class UrlSegment : PatternSegment 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) 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;
} }
} }
} }

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.Internal; using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.Internal.ApacheModRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite;
@ -29,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
</rewrite>"; </rewrite>";
var expected = new List<IISUrlRewriteRule>(); var expected = new List<IISUrlRewriteRule>();
expected.Add(CreateTestRule(new List<Condition>(), expected.Add(CreateTestRule(new ConditionCollection(),
url: "^article/([0-9]+)/([_0-9a-z-]+)", url: "^article/([0-9]+)/([_0-9a-z-]+)",
name: "Rewrite to article.aspx", name: "Rewrite to article.aspx",
actionType: ActionType.Rewrite, actionType: ActionType.Rewrite,
@ -58,10 +57,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
</rules> </rules>
</rewrite>"; </rewrite>";
var condList = new List<Condition>(); var condList = new ConditionCollection();
condList.Add(new Condition condList.Add(new Condition
{ {
Input = new InputParser().ParseInputString("{HTTPS}", global: false), Input = new InputParser().ParseInputString("{HTTPS}"),
Match = new RegexMatch(new Regex("^OFF$"), false) Match = new RegexMatch(new Regex("^OFF$"), false)
}); });
@ -102,10 +101,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
</rules> </rules>
</rewrite>"; </rewrite>";
var condList = new List<Condition>(); var condList = new ConditionCollection();
condList.Add(new Condition condList.Add(new Condition
{ {
Input = new InputParser().ParseInputString("{HTTPS}", global: false), Input = new InputParser().ParseInputString("{HTTPS}"),
Match = new RegexMatch(new Regex("^OFF$"), false) 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. // Creates a rule with appropriate default values of the url rewrite rule.
private IISUrlRewriteRule CreateTestRule(List<Condition> conditions, private IISUrlRewriteRule CreateTestRule(ConditionCollection conditions,
LogicalGrouping condGrouping = LogicalGrouping.MatchAll,
bool condTracking = false,
string name = "", string name = "",
bool enabled = true, bool enabled = true,
PatternSyntax patternSyntax = PatternSyntax.ECMAScript, PatternSyntax patternSyntax = PatternSyntax.ECMAScript,
@ -174,11 +171,17 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
string pattern = "", string pattern = "",
bool appendQueryString = false, bool appendQueryString = false,
bool rewrittenUrl = false, bool rewrittenUrl = false,
bool global = false,
UriMatchPart uriMatchPart = UriMatchPart.Path,
RedirectType redirectType = RedirectType.Permanent RedirectType redirectType = RedirectType.Permanent
) )
{ {
return new IISUrlRewriteRule(name, new RegexMatch(new Regex("^OFF$"), false), conditions, return new IISUrlRewriteRule(
new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url, global: false), queryStringAppend: false), trackAllCaptures: false); name,
new RegexMatch(new Regex("^OFF$"), negate),
conditions,
new RewriteAction(RuleResult.ContinueRules, new InputParser().ParseInputString(url, uriMatchPart), queryStringAppend: false),
global);
} }
// TODO make rules comparable? // TODO make rules comparable?

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
public void InputParser_ParseLiteralString() public void InputParser_ParseLiteralString()
{ {
var testString = "hello/hey/what"; var testString = "hello/hey/what";
var result = new InputParser().ParseInputString(testString, global: false); var result = new InputParser().ParseInputString(testString, UriMatchPart.Path);
Assert.Equal(1, result.PatternSegments.Count); Assert.Equal(1, result.PatternSegments.Count);
} }
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("foo/", 1)] [InlineData("foo/", 1)]
public void InputParser_ParseStringWithBackReference(string testString, int expected) public void InputParser_ParseStringWithBackReference(string testString, int expected)
{ {
var result = new InputParser().ParseInputString(testString, global: false); var result = new InputParser().ParseInputString(testString, UriMatchPart.Path);
Assert.Equal(expected, result.PatternSegments.Count); 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")] [InlineData("hey/{R:1}/{C:1}", "hey/foo/foo")]
public void EvaluateBackReferenceRule(string testString, string expected) public void EvaluateBackReferenceRule(string testString, string expected)
{ {
var middle = new InputParser().ParseInputString(testString, global: false); var middle = new InputParser().ParseInputString(testString, UriMatchPart.Path);
var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences());
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("hey/ToLower:/what", "hey/ToLower:/what")] [InlineData("hey/ToLower:/what", "hey/ToLower:/what")]
public void EvaluatToLowerRule(string testString, string expected) public void EvaluatToLowerRule(string testString, string expected)
{ {
var middle = new InputParser().ParseInputString(testString, global: false); var middle = new InputParser().ParseInputString(testString, UriMatchPart.Path);
var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences());
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("hey/{UrlEncode:<hey>}", "hey/%3Chey%3E")] [InlineData("hey/{UrlEncode:<hey>}", "hey/%3Chey%3E")]
public void EvaluatUriEncodeRule(string testString, string expected) public void EvaluatUriEncodeRule(string testString, string expected)
{ {
var middle = new InputParser().ParseInputString(testString, global: false); var middle = new InputParser().ParseInputString(testString, UriMatchPart.Path);
var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences()); var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleBackReferences(), CreateTestCondBackReferences());
Assert.Equal(expected, result); Assert.Equal(expected, result);
} }
@ -88,13 +88,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
[InlineData("{HTTPS")] [InlineData("{HTTPS")]
public void FormatExceptionsOnBadSyntax(string testString) public void FormatExceptionsOnBadSyntax(string testString)
{ {
Assert.Throws<FormatException>(() => new InputParser().ParseInputString(testString, global: false)); Assert.Throws<FormatException>(() => new InputParser().ParseInputString(testString, UriMatchPart.Path));
} }
[Fact] [Fact]
public void Should_throw_FormatException_if_no_rewrite_maps_are_defined() public void Should_throw_FormatException_if_no_rewrite_maps_are_defined()
{ {
Assert.Throws<FormatException>(() => new InputParser(null).ParseInputString("{apiMap:{R:1}}", global: false)); Assert.Throws<FormatException>(() => new InputParser(null).ParseInputString("{apiMap:{R:1}}", UriMatchPart.Path));
} }
[Fact] [Fact]
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
const string undefinedMapName = "apiMap"; const string undefinedMapName = "apiMap";
var map = new IISRewriteMap(definedMapName); var map = new IISRewriteMap(definedMapName);
var maps = new IISRewriteMapCollection { map }; var maps = new IISRewriteMapCollection { map };
Assert.Throws<FormatException>(() => new InputParser(maps).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", global: false)); Assert.Throws<FormatException>(() => new InputParser(maps).ParseInputString($"{{{undefinedMapName}:{{R:1}}}}", UriMatchPart.Path));
} }
[Fact] [Fact]
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
var maps = new IISRewriteMapCollection { map }; var maps = new IISRewriteMapCollection { map };
var inputString = $"{{{expectedMapName}:{{R:1}}}}"; 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); Assert.Equal(1, pattern.PatternSegments.Count);
var segment = pattern.PatternSegments.Single(); var segment = pattern.PatternSegments.Single();

View File

@ -2,14 +2,19 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite; using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Xunit; using Xunit;
@ -456,8 +461,9 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
} }
[Fact] [Fact]
public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri() public async Task Invoke_GlobalRuleConditionMatchesAgainstFullUri_ParsedRule()
{ {
// arrange
var xml = @"<rewrite> var xml = @"<rewrite>
<globalRules> <globalRules>
<rule name=""Test"" patternSyntax=""ECMAScript"" stopProcessing=""true""> <rule name=""Test"" patternSyntax=""ECMAScript"" stopProcessing=""true"">
@ -478,8 +484,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
}); });
var server = new TestServer(builder); var server = new TestServer(builder);
// act
var response = await server.CreateClient().GetStringAsync($"http://localhost/{Guid.NewGuid()}/foo/bar"); var response = await server.CreateClient().GetStringAsync($"http://localhost/{Guid.NewGuid()}/foo/bar");
// assert
Assert.Equal("http://www.test.com/foo/bar", response); 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("reason", response.ReasonPhrase);
Assert.Equal("description", content); 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);
}
} }
} }

View File

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

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
var rules = new UrlRewriteFileParser().Parse(xml); var rules = new UrlRewriteFileParser().Parse(xml);
Assert.Equal(rules.Count, 1); Assert.Equal(rules.Count, 1);
Assert.True(rules[0].TrackAllCaptures); Assert.True(rules[0].Conditions.TrackAllCaptures);
var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
rules.FirstOrDefault().ApplyRule(context); rules.FirstOrDefault().ApplyRule(context);
Assert.Equal(RuleResult.ContinueRules, context.Result); Assert.Equal(RuleResult.ContinueRules, context.Result);

View File

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

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Http;
using Microsoft.AspNetCore.Rewrite.Internal.IISUrlRewrite;
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments; using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
using Xunit; using Xunit;
@ -10,10 +11,19 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
public class UrlSegmentTests public class UrlSegmentTests
{ {
[Theory] [Theory]
[InlineData("http", "localhost", 80, "/foo/bar", "/foo/bar")] [InlineData("http", "localhost", 80, null, UriMatchPart.Path, "")]
[InlineData("http", "localhost", 80, "/foo:bar", "/foo:bar")] [InlineData("http", "localhost", 80, "", UriMatchPart.Path, "")]
[InlineData("http", "localhost", 80, "/foo bar", "/foo%20bar")] [InlineData("http", "localhost", 80, "/foo/bar", UriMatchPart.Path, "/foo/bar")]
public void AssertSegmentIsCorrect(string scheme, string host, int port, string path, string expectedResult) [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 // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -25,7 +35,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
context.HttpContext = httpContext; context.HttpContext = httpContext;
// Act // Act
var segment = new UrlSegment(); var segment = new UrlSegment(uriMatchPart);
var results = segment.Evaluate(context, null, null); var results = segment.Evaluate(context, null, null);
// Assert // Assert