General bugfixes and style cleanup

This commit is contained in:
Justin Kotalik 2016-08-23 13:46:39 -07:00
parent 74b0b7945d
commit 0cb7445921
35 changed files with 333 additions and 281 deletions

View File

@ -37,7 +37,7 @@ namespace RewriteSample
return RuleResult.Continue;
}));
app.Run(context => context.Response.WriteAsync(context.Request.Path));
app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}"));
}
public static void Main(string[] args)

View File

@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Rewrite
public static RewriteOptions RewriteToHttps(this RewriteOptions options, int? sslPort, bool stopProcessing)
{
options.Rules.Add(new RewriteToHttpsRule {SSLPort = sslPort, stopProcessing = stopProcessing });
options.Rules.Add(new RewriteToHttpsRule {SSLPort = sslPort, StopProcessing = stopProcessing });
return options;
}

View File

@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
{
public class RewriteToHttpsRule : Rule
{
public bool stopProcessing { get; set; }
public bool StopProcessing { get; set; }
public int? SSLPort { get; set; }
public override RuleResult ApplyRule(RewriteContext context)
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
context.HttpContext.Request.Scheme = "https";
context.HttpContext.Request.Host = host;
return stopProcessing ? RuleResult.StopRules: RuleResult.Continue;
return StopProcessing ? RuleResult.StopRules: RuleResult.Continue;
}
return RuleResult.Continue;
}

View File

@ -172,19 +172,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
case 'g':
if (!context.Next())
{
throw new FormatException(context.Error());
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
if (context.Current == 't')
switch (context.Current)
{
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Greater, operand: null);
}
else if (context.Current == 'e')
{
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.GreaterEqual, operand: null);
}
else
{
throw new FormatException(context.Error());
case 't':
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Greater, operand: null);
case 'e':
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.GreaterEqual, operand: null);
default:
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
case 'l':
// name conflict with -l and -lt/-le, so the assumption is if there is no
@ -193,17 +190,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, operand: null);
}
if (context.Current == 't')
switch (context.Current)
{
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Less, operand: null);
}
else if (context.Current == 'e')
{
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.LessEqual, operand: null);
}
else
{
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
case 't':
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Less, operand: null);
case 'e':
return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.LessEqual, operand: null);
default:
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
case 'n':
if (!context.Next() || context.Current != 'e')

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public List<Rule> Parse(TextReader input)
{
string line = null;
string line;
var rules = new List<Rule>();
var builder = new RuleBuilder();
var lineNum = 0;

View File

@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
// Invalid syntax to have any spaces.
var tokens = flagString.Substring(1, flagString.Length - 2).Split(',');
var flags = new Flags();
foreach (string token in tokens)
foreach (var token in tokens)
{
var hasPayload = token.Split('=');

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@ -33,9 +34,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
// TODO PERF, substrings, object creation, etc.
if (pattern.IndexOf("://") >= 0)
if (pattern.IndexOf("://", StringComparison.Ordinal) >= 0)
{
string scheme = null;
string scheme;
HostString host;
PathString path;
QueryString query;

View File

@ -62,91 +62,90 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
condition.OrNext = flags.HasFlag(FlagType.Or);
condition.Input = pattern;
if (input.ConditionType == ConditionType.Regex)
switch (input.ConditionType)
{
if (flags.HasFlag(FlagType.NoCase))
{
condition.Match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout), input.Invert);
}
else
{
condition.Match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled, RegexTimeout), input.Invert);
}
}
else if (input.ConditionType == ConditionType.IntComp)
{
switch (input.OperationType)
{
case OperationType.Equal:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.Equal);
break;
case OperationType.Greater:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.Greater);
break;
case OperationType.GreaterEqual:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.GreaterEqual);
break;
case OperationType.Less:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.Less);
break;
case OperationType.LessEqual:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.LessEqual);
break;
case OperationType.NotEqual:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.NotEqual);
break;
default:
throw new ArgumentException("Invalid operation for integer comparison.");
}
}
else if (input.ConditionType == ConditionType.StringComp)
{
switch (input.OperationType)
{
case OperationType.Equal:
condition.Match = new StringMatch(input.Operand, StringOperationType.Equal);
break;
case OperationType.Greater:
condition.Match = new StringMatch(input.Operand, StringOperationType.Greater);
break;
case OperationType.GreaterEqual:
condition.Match = new StringMatch(input.Operand, StringOperationType.GreaterEqual);
break;
case OperationType.Less:
condition.Match = new StringMatch(input.Operand, StringOperationType.Less);
break;
case OperationType.LessEqual:
condition.Match = new StringMatch(input.Operand, StringOperationType.LessEqual);
break;
default:
throw new ArgumentException("Invalid operation for string comparison.");
}
}
else
{
switch (input.OperationType)
{
case OperationType.Directory:
condition.Match = new IsDirectoryMatch(input.Invert);
break;
case OperationType.RegularFile:
condition.Match = new IsFileMatch(input.Invert);
break;
case OperationType.ExistingFile:
condition.Match = new IsFileMatch(input.Invert);
break;
case OperationType.SymbolicLink:
throw new NotImplementedException("Symbolic links are not implemented");
case OperationType.Size:
condition.Match = new FileSizeMatch(input.Invert);
break;
case OperationType.ExistingUrl:
throw new NotImplementedException("Existing Url lookups not implemented");
case OperationType.Executable:
throw new NotImplementedException("Executable Property search is not implemented");
default:
throw new ArgumentException("Invalid operation for property comparison.");
}
case ConditionType.Regex:
if (flags.HasFlag(FlagType.NoCase))
{
condition.Match = new RegexMatch(new Regex(input.Operand, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout), input.Invert);
}
else
{
condition.Match = new RegexMatch(new Regex(input.Operand, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout), input.Invert);
}
break;
case ConditionType.IntComp:
switch (input.OperationType)
{
case OperationType.Equal:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.Equal);
break;
case OperationType.Greater:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.Greater);
break;
case OperationType.GreaterEqual:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.GreaterEqual);
break;
case OperationType.Less:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.Less);
break;
case OperationType.LessEqual:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.LessEqual);
break;
case OperationType.NotEqual:
condition.Match = new IntegerMatch(input.Operand, IntegerOperationType.NotEqual);
break;
default:
throw new ArgumentException("Invalid operation for integer comparison.");
}
break;
case ConditionType.StringComp:
switch (input.OperationType)
{
case OperationType.Equal:
condition.Match = new StringMatch(input.Operand, StringOperationType.Equal, input.Invert);
break;
case OperationType.Greater:
condition.Match = new StringMatch(input.Operand, StringOperationType.Greater, input.Invert);
break;
case OperationType.GreaterEqual:
condition.Match = new StringMatch(input.Operand, StringOperationType.GreaterEqual, input.Invert);
break;
case OperationType.Less:
condition.Match = new StringMatch(input.Operand, StringOperationType.Less, input.Invert);
break;
case OperationType.LessEqual:
condition.Match = new StringMatch(input.Operand, StringOperationType.LessEqual, input.Invert);
break;
default:
throw new ArgumentException("Invalid operation for string comparison.");
}
break;
default:
switch (input.OperationType)
{
case OperationType.Directory:
condition.Match = new IsDirectoryMatch(input.Invert);
break;
case OperationType.RegularFile:
condition.Match = new IsFileMatch(input.Invert);
break;
case OperationType.ExistingFile:
condition.Match = new IsFileMatch(input.Invert);
break;
case OperationType.SymbolicLink:
throw new NotImplementedException("Symbolic links are not implemented");
case OperationType.Size:
condition.Match = new FileSizeMatch(input.Invert);
break;
case OperationType.ExistingUrl:
throw new NotImplementedException("Existing Url lookups not implemented");
case OperationType.Executable:
throw new NotImplementedException("Executable Property search is not implemented");
default:
throw new ArgumentException("Invalid operation for property comparison.");
}
break;
}
_conditions.ConditionList.Add(condition);
}
@ -157,11 +156,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
if (flags.HasFlag(FlagType.NoCase))
{
_match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout), input.Invert);
_match = new RegexMatch(new Regex(input.Operand, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout), input.Invert);
}
else
{
_match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled, RegexTimeout), input.Invert);
_match = new RegexMatch(new Regex(input.Operand, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout), input.Invert);
}
}

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public ParsedModRewriteInput ParseRuleRegex(string regex)
{
if (regex == null || regex == string.Empty)
if (string.IsNullOrEmpty(regex))
{
throw new FormatException("Regex expression is null");
}

View File

@ -14,7 +14,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
private const char Percent = '%';
private const char Dollar = '$';
private const char Space = ' ';
private const char Colon = ':';
private const char OpenBrace = '{';
private const char CloseBrace = '}';
@ -41,43 +40,41 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
var results = new List<PatternSegment>();
while (context.Next())
{
if (context.Current == Percent)
switch (context.Current)
{
// This is a server parameter, parse for a condition variable
if (!context.Next())
{
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(testString, context.Index));
}
ParseConditionParameter(context, results);
}
else if (context.Current == Dollar)
{
// This is a parameter from the rule, verify that it is a number from 0 to 9 directly after it
// and create a new Pattern Segment.
if (!context.Next())
{
throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
}
context.Mark();
if (context.Current >= '0' && context.Current <= '9')
{
context.Next();
var ruleVariable = context.Capture();
context.Back();
var parsedIndex = int.Parse(ruleVariable);
case Percent:
// This is a server parameter, parse for a condition variable
if (!context.Next())
{
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(testString, context.Index));
}
ParseConditionParameter(context, results);
break;
case Dollar:
// This is a parameter from the rule, verify that it is a number from 0 to 9 directly after it
// and create a new Pattern Segment.
if (!context.Next())
{
throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
}
context.Mark();
if (context.Current >= '0' && context.Current <= '9')
{
context.Next();
var ruleVariable = context.Capture();
context.Back();
var parsedIndex = int.Parse(ruleVariable);
results.Add(new RuleMatchSegment(parsedIndex));
}
else
{
throw new FormatException(Resources.FormatError_InputParserInvalidInteger(testString, context.Index));
}
}
else
{
// Parse for literals, which will return on either the end of the test string
// or when it hits a special character
ParseLiteral(context, results);
results.Add(new RuleMatchSegment(parsedIndex));
}
else
{
throw new FormatException(Resources.FormatError_InputParserInvalidInteger(testString, context.Index));
}
break;
default:
ParseLiteral(context, results);
break;
}
}
return new Pattern(results);

View File

@ -15,6 +15,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
private const char Space = ' ';
private const char Escape = '\\';
private const char Tab = '\t';
private const char Quote = '"';
/// <summary>
/// Splits a string on whitespace, ignoring spaces, creating into a list of strings.
@ -36,49 +37,51 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
context.Mark();
while (true)
{
if (context.Current == Escape)
switch (context.Current)
{
// Need to progress such that the next character is not evaluated.
if (!context.Next())
{
// Means that a character was not escaped appropriately Ex: "foo\"
throw new FormatException($"Invalid escaper character in string: {rule}");
}
}
else if (context.Current == '"')
{
// Ignore all characters until the next quote is hit
if (!context.Next())
{
throw new FormatException($"Mismatched number of quotes: {rule}");
}
while (context.Current != '"')
{
case Escape:
// Need to progress such that the next character is not evaluated.
if (!context.Next())
{
// Means that a character was not escaped appropriately Ex: "foo\"
throw new FormatException($"Invalid escaper character in string: {rule}");
}
break;
case Quote:
// Ignore all characters until the next quote is hit
if (!context.Next())
{
throw new FormatException($"Mismatched number of quotes: {rule}");
}
}
}
else if (context.Current == Space || context.Current == Tab)
{
// time to capture!
var token = context.Capture();
if (!string.IsNullOrEmpty(token))
{
tokens.Add(token);
while (context.Current == Space || context.Current == Tab)
while (context.Current != Quote)
{
if (!context.Next())
{
// At end of string, we can return at this point.
RemoveQuotesAndEscapeCharacters(tokens);
return tokens;
throw new FormatException($"Mismatched number of quotes: {rule}");
}
}
context.Mark();
context.Back();
}
break;
case Space:
case Tab:
// time to capture!
var token = context.Capture();
if (!string.IsNullOrEmpty(token))
{
tokens.Add(token);
do
{
if (!context.Next())
{
// At end of string, we can return at this point.
RemoveQuotesAndEscapeCharacters(tokens);
return tokens;
}
} while (context.Current == Space || context.Current == Tab);
context.Mark();
context.Back();
}
break;
}
if (!context.Next())
{
@ -99,9 +102,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
// Need to remove leading and trailing slashes if they exist.
// This is on start-up, so more forgivening towards substrings/ new strings
// If this is a perf/memory problem, discuss later.
private void RemoveQuotesAndEscapeCharacters(List<string> tokens)
private static void RemoveQuotesAndEscapeCharacters(IList<string> tokens)
{
for (int i = 0; i < tokens.Count; i++)
for (var i = 0; i < tokens.Count; i++)
{
var token = tokens[i];
var trimmed = token.Trim('\"');

View File

@ -18,10 +18,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
Index = -1;
}
public char Current
{
get { return (Index < Template.Length && Index >= 0) ? Template[Index] : (char)0; }
}
public char Current => (Index < Template.Length && Index >= 0) ? Template[Index] : (char)0;
public bool Back()
{
@ -62,10 +59,5 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
return null;
}
}
public string Error()
{
return string.Format("Syntax Error at index: ", Index, " with character: ", Current);
}
}
}

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
public class Pattern
{
public IList<PatternSegment> PatternSegments { get; }
public Pattern(List<PatternSegment> patternSegments)
public Pattern(IList<PatternSegment> patternSegments)
{
PatternSegments = patternSegments;
}

View File

@ -5,16 +5,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class ConditionMatchSegment : PatternSegment
{
public int Index { get; set; }
private readonly int _index;
public ConditionMatchSegment(int index)
{
Index = index;
_index = index;
}
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return condMatch?.BackReference[Index]?.Value;
return condMatch?.BackReference[_index].Value;
}
}
}

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class DateTimeSegment : PatternSegment
{
private DateTimePortion _portion;
private readonly DateTimePortion _portion;
public DateTimeSegment(string segment)
{

View File

@ -5,16 +5,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class HeaderSegment : PatternSegment
{
public string Header { get; set; }
private readonly string _header;
public HeaderSegment(string header)
{
Header = header;
_header = header;
}
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return context.HttpContext.Request.Headers[Header];
return context.HttpContext.Request.Headers[_header];
}
}
}

View File

@ -5,16 +5,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class LiteralSegment : PatternSegment
{
public string Literal { get; set; }
private readonly string _literal;
public LiteralSegment(string literal)
{
Literal = literal;
_literal = literal;
}
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return Literal;
return _literal;
}
}
}

View File

@ -5,16 +5,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class RuleMatchSegment : PatternSegment
{
public int Index { get; set; }
private readonly int _index;
public RuleMatchSegment(int index)
{
Index = index;
_index = index;
}
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return ruleMatch?.BackReference[Index]?.Value;
return ruleMatch?.BackReference[_index].Value;
}
}
}

View File

@ -7,11 +7,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class ToLowerSegment : PatternSegment
{
public Pattern Pattern { get; set; }
private readonly Pattern _pattern;
public ToLowerSegment(Pattern pattern)
{
Pattern = pattern;
_pattern = pattern;
}
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
// lowercase segments.
var tempBuilder = context.Builder;
context.Builder = new StringBuilder(64);
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
var pattern = _pattern.Evaluate(context, ruleMatch, condMatch);
context.Builder = tempBuilder;
return pattern.ToLowerInvariant();
}

View File

@ -8,18 +8,18 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class UrlEncodeSegment : PatternSegment
{
public Pattern Pattern { get; set; }
private readonly Pattern _pattern;
public UrlEncodeSegment(Pattern pattern)
{
Pattern = pattern;
_pattern = pattern;
}
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var tempBuilder = context.Builder;
context.Builder = new StringBuilder(64);
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
var pattern = _pattern.Evaluate(context, ruleMatch, condMatch);
context.Builder = tempBuilder;
return UrlEncoder.Default.Encode(pattern);
}

View File

@ -1,6 +1,7 @@

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
namespace Microsoft.AspNetCore.Rewrite.Internal
{

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@ -30,7 +31,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
// TODO PERF, substrings, object creation, etc.
if (pattern.IndexOf("://") >= 0)
{
string scheme = null;
string scheme;
HostString host;
PathString path;
QueryString query;

View File

@ -5,10 +5,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
{
public class VoidAction : UrlAction
{
private readonly RuleResult _results;
public VoidAction(RuleResult results)
{
_results = results;
}
// Explicitly say that nothing happens
public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return RuleResult.Continue;
return _results;
}
}
}

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
{
public abstract class UrlMatch
{
public bool Negate { get; set; }
protected bool Negate { get; set; }
public abstract MatchResults Evaluate(string input, RewriteContext context);
}
}

View File

@ -5,19 +5,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class ExactMatch : UrlMatch
{
public bool IgnoreCase { get; }
public string StringMatch { get; }
private readonly bool _ignoreCase;
private readonly string _stringMatch;
public ExactMatch(bool ignoreCase, string input, bool negate)
{
IgnoreCase = ignoreCase;
StringMatch = input;
_ignoreCase = ignoreCase;
_stringMatch = input;
Negate = negate;
}
public override MatchResults Evaluate(string pattern, RewriteContext context)
{
var pathMatch = string.Compare(pattern, StringMatch, IgnoreCase);
var pathMatch = string.Compare(pattern, _stringMatch, _ignoreCase);
return new MatchResults { Success = ((pathMatch == 0) != Negate) };
}
}

View File

@ -8,12 +8,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class IntegerMatch : UrlMatch
{
public int Value { get; }
public IntegerOperationType Operation { get; }
private readonly int _value;
private readonly IntegerOperationType _operation;
public IntegerMatch(int value, IntegerOperationType operation)
{
Value = value;
Operation = operation;
_value = value;
_operation = operation;
}
public IntegerMatch(string value, IntegerOperationType operation)
@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
throw new FormatException("Syntax error for integers in comparison.");
}
Value = compValue;
Operation = operation;
_value = compValue;
_operation = operation;
}
public override MatchResults Evaluate(string input, RewriteContext context)
@ -35,20 +35,20 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
return MatchResults.EmptyFailure;
}
switch (Operation)
switch (_operation)
{
case IntegerOperationType.Equal:
return compValue == Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return compValue == _value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.Greater:
return compValue > Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return compValue > _value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.GreaterEqual:
return compValue >= Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return compValue >= _value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.Less:
return compValue < Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return compValue < _value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.LessEqual:
return compValue <= Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return compValue <= _value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.NotEqual:
return compValue != Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return compValue != _value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
default:
return null;
}

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class IsDirectoryMatch : UrlMatch
{
public IsDirectoryMatch( bool negate)
public IsDirectoryMatch(bool negate)
{
Negate = negate;
}

View File

@ -1,26 +1,23 @@
// 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;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class RegexMatch : UrlMatch
{
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
public Regex Match { get; }
private readonly Regex _match;
public RegexMatch(Regex match, bool negate)
{
Match = match;
_match = match;
Negate = negate;
}
public override MatchResults Evaluate(string pattern, RewriteContext context)
{
var res = Match.Match(pattern);
var res = _match.Match(pattern);
return new MatchResults { BackReference = res.Groups, Success = (res.Success != Negate)};
}
}

View File

@ -5,29 +5,31 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class StringMatch : UrlMatch
{
public string Value { get; set; }
public StringOperationType Operation { get; set; }
public bool IgnoreCase { get; set; }
public StringMatch(string value, StringOperationType operation)
private readonly string _value;
private readonly StringOperationType _operation;
private readonly bool _ignoreCase;
public StringMatch(string value, StringOperationType operation, bool ignoreCase)
{
Value = value;
Operation = operation;
_value = value;
_operation = operation;
_ignoreCase = ignoreCase;
}
public override MatchResults Evaluate(string input, RewriteContext context)
{
switch (Operation)
switch (_operation)
{
case StringOperationType.Equal:
return string.Compare(input, Value, IgnoreCase) == 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return string.Compare(input, _value, _ignoreCase) == 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case StringOperationType.Greater:
return string.Compare(input, Value, IgnoreCase) > 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return string.Compare(input, _value, _ignoreCase) > 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case StringOperationType.GreaterEqual:
return string.Compare(input, Value, IgnoreCase) >= 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return string.Compare(input, _value, _ignoreCase) >= 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case StringOperationType.Less:
return string.Compare(input, Value, IgnoreCase) < 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return string.Compare(input, _value, _ignoreCase) < 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case StringOperationType.LessEqual:
return string.Compare(input, Value, IgnoreCase) <= 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
return string.Compare(input, _value, _ignoreCase) <= 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
default:
return null;
}

View File

@ -12,9 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
public class FileParser
{
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
private InputParser _inputParser = new InputParser();
private readonly InputParser _inputParser = new InputParser();
public List<UrlRewriteRule> Parse(TextReader reader)
{
@ -168,12 +166,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value;
Pattern input = null;
try
{
input = _inputParser.ParseInputString(parsedInputString);
var input = _inputParser.ParseInputString(parsedInputString);
builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate);
}
catch (FormatException formatException)
{
@ -212,14 +208,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
}
}
private void ThrowUrlFormatException(XElement element, string message)
private static void ThrowUrlFormatException(XElement element, string message)
{
var line = ((IXmlLineInfo)element).LineNumber;
var col = ((IXmlLineInfo)element).LinePosition;
throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col));
}
private void ThrowUrlFormatException(XElement element, string message, Exception ex)
private static void ThrowUrlFormatException(XElement element, string message, Exception ex)
{
var line = ((IXmlLineInfo)element).LineNumber;
var col = ((IXmlLineInfo)element).LinePosition;

View File

@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
return index;
}
private static bool ParseLiteral(ParserContext context, List<PatternSegment> results)
private static void ParseLiteral(ParserContext context, List<PatternSegment> results)
{
context.Mark();
string literal;
@ -186,7 +186,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
}
results.Add(new LiteralSegment(literal));
return true;
}
}
}

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
switch (actionType)
{
case ActionType.None:
_action = new VoidAction();
_action = new VoidAction(stopProcessing ? RuleResult.StopRules : RuleResult.Continue);
break;
case ActionType.Rewrite:
if (appendQueryString)
@ -79,12 +79,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
if (ignoreCase)
{
var regex = new Regex(input, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
var regex = new Regex(input, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
_initialMatch = new RegexMatch(regex, negate);
}
else
{
var regex = new Regex(input, RegexOptions.Compiled, RegexTimeout);
var regex = new Regex(input, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout);
_initialMatch = new RegexMatch(regex, negate);
}
break;
@ -121,11 +121,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
Regex regex = null;
if (ignoreCase)
{
regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
}
else
{
regex = new Regex(pattern, RegexOptions.Compiled, RegexTimeout);
regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout);
}
_conditions.ConditionList.Add(new Condition { Input = input, Match = new RegexMatch(regex, negate), OrNext = _matchAny});

View File

@ -1,10 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.Internal
{
@ -26,9 +23,18 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
// Due to the path string always having a leading slash,
// remove it from the path before regex comparison
// TODO may need to check if there is a leading slash and remove conditionally
var initMatchRes = InitialMatch.Evaluate(context.HttpContext.Request.Path.ToString().Substring(1), context);
var path = context.HttpContext.Request.Path;
MatchResults initMatchResults;
if (path == PathString.Empty)
{
initMatchResults = InitialMatch.Evaluate(path.ToString(), context);
}
else
{
initMatchResults = InitialMatch.Evaluate(path.ToString().Substring(1), context);
}
if (!initMatchRes.Success)
if (!initMatchResults.Success)
{
return RuleResult.Continue;
}
@ -36,7 +42,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
MatchResults condMatchRes = null;
if (Conditions != null)
{
condMatchRes = Conditions.Evaluate(context, initMatchRes);
condMatchRes = Conditions.Evaluate(context, initMatchResults);
if (!condMatchRes.Success)
{
return RuleResult.Continue;
@ -44,7 +50,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
}
// at this point we know the rule passed, evaluate the replacement.
return Action.ApplyAction(context, initMatchRes, condMatchRes);
return Action.ApplyAction(context, initMatchResults, condMatchRes);
}
}
}

View File

@ -69,6 +69,8 @@ namespace Microsoft.AspNetCore.Rewrite
return CompletedTask;
case RuleTerminiation.StopRules:
return _next(context);
default:
throw new ArgumentOutOfRangeException($"Invalid rule termination {result}");
}
}
return _next(context);

View File

@ -0,0 +1,56 @@
// 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.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
using Microsoft.AspNetCore.TestHost;
using Xunit;
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
{
// TODO add more of these
public class UrlRewriteApplicationTests
{
[Fact]
public void ApplyRule_AssertStopProcessingFlagWillTerminateOnNoAction()
{
var xml = new StringReader(@"<rewrite>
<rules>
<rule name=""Test"" stopProcessing=""true"">
<match url = ""(.*)""/>
<action type = ""None""/>
</rule>
</rules>
</rewrite>");
var rules = new FileParser().Parse(xml);
Assert.Equal(rules.Count, 1);
var ruleResults = rules.FirstOrDefault().ApplyRule(new RewriteContext {HttpContext = new DefaultHttpContext()});
Assert.Equal(ruleResults.Result, RuleTerminiation.StopRules);
}
[Fact]
public void ApplyRule_AssertNoTerminateFlagWillNotTerminateOnNoAction()
{
var xml = new StringReader(@"<rewrite>
<rules>
<rule name=""Test"">
<match url = ""(.*)"" ignoreCase=""false"" />
<action type = ""None""/>
</rule>
</rules>
</rewrite>");
var rules = new FileParser().Parse(xml);
Assert.Equal(rules.Count, 1);
var ruleResults = rules.FirstOrDefault().ApplyRule(new RewriteContext { HttpContext = new DefaultHttpContext() });
Assert.Equal(ruleResults.Result, RuleTerminiation.Continue);
}
}
}