diff --git a/samples/RewriteSample/Rewrite.txt b/samples/RewriteSample/Rewrite.txt
index ed2ae33ce1..574f251600 100644
--- a/samples/RewriteSample/Rewrite.txt
+++ b/samples/RewriteSample/Rewrite.txt
@@ -1,7 +1,6 @@
# Ensure Https
RewriteCond %{HTTPS} off
-# U is a new flag to represent full URL rewrites
-RewriteRule ^(.*)$ https://www.example.com$1 [L,U]
+RewriteRule ^(.*)$ https://www.example.com$1 [L]
# Rewrite path with additional sub directory
RewriteRule ^(.*)$ /foo$1
diff --git a/samples/RewriteSample/Startup.cs b/samples/RewriteSample/Startup.cs
index b83d5677e7..1dbcb048e5 100644
--- a/samples/RewriteSample/Startup.cs
+++ b/samples/RewriteSample/Startup.cs
@@ -27,18 +27,17 @@ namespace RewriteSample
// AddFunctionalRule(Func);
// TODO make this startup do something useful.
app.UseRewriter(new RewriteOptions()
- .ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
- .ImportFromModRewrite(hostingEnv, "Rewrite.txt")
- .RedirectToHttps(StatusCodes.Status307TemporaryRedirect)
- .RewriteRule("/foo/(.*)/bar", "{R:1}/bar")
- .AddRule(ctx =>
- {
- ctx.HttpContext.Request.Path = "/index";
- return RuleResult.Continue;
- }));
+ .ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
+ .ImportFromModRewrite(hostingEnv, "Rewrite.txt")
+ .RedirectToHttps(StatusCodes.Status307TemporaryRedirect)
+ .RewriteRule("/foo/(.*)/bar", "{R:1}/bar")
+ .AddRule(ctx =>
+ {
+ ctx.HttpContext.Request.Path = "/index";
+ return RuleResult.Continue;
+ }));
app.Run(context => context.Response.WriteAsync(context.Request.Path));
-
}
public static void Main(string[] args)
diff --git a/samples/RewriteSample/UrlRewrite.xml b/samples/RewriteSample/UrlRewrite.xml
index 4f5bac7a31..9ef1097666 100644
--- a/samples/RewriteSample/UrlRewrite.xml
+++ b/samples/RewriteSample/UrlRewrite.xml
@@ -1,12 +1,8 @@
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectToHttpsRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectToHttpsRule.cs
index 3dd9d6df39..04b1c8dd97 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectToHttpsRule.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RedirectToHttpsRule.cs
@@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
public int StatusCode { get; set; }
public override RuleResult ApplyRule(RewriteContext context)
{
-
- // TODO this only does http to https, add more features in the future.
if (!context.HttpContext.Request.IsHttps)
{
var host = context.HttpContext.Request.Host;
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteToHttpsRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteToHttpsRule.cs
index cccb76e6d1..e276baa85b 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteToHttpsRule.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteToHttpsRule.cs
@@ -1,4 +1,6 @@
-
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
@@ -10,7 +12,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
public int? SSLPort { get; set; }
public override RuleResult ApplyRule(RewriteContext context)
{
- // TODO this only does http to https, add more features in the future.
if (!context.HttpContext.Request.IsHttps)
{
var host = context.HttpContext.Request.Host;
@@ -27,14 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
context.HttpContext.Request.Scheme = "https";
context.HttpContext.Request.Host = host;
- if (stopProcessing)
- {
- return RuleResult.StopRules;
- }
- else
- {
- return RuleResult.Continue;
- }
+ return stopProcessing ? RuleResult.StopRules: RuleResult.Continue;
}
return RuleResult.Continue;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Condition.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs
similarity index 74%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Condition.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs
index f6541d48f9..5c60e38d82 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Condition.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/Condition.cs
@@ -1,15 +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.UrlRewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class Condition
{
public Pattern Input { get; set; }
public UrlMatch Match { get; set; }
+ public bool OrNext { get; set; }
+
public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
- var pattern = Input.Evaluate(context.HttpContext, ruleMatch, condMatch);
+ var pattern = Input.Evaluate(context, ruleMatch, condMatch);
return Match.Evaluate(pattern, context);
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/Conditions.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/Conditions.cs
new file mode 100644
index 0000000000..d8154c6b1d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/Conditions.cs
@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal
+{
+ public class Conditions
+ {
+ public List ConditionList { get; set; } = new List();
+
+ public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch)
+ {
+ MatchResults prevCond = null;
+ var orSucceeded = false;
+ foreach (var condition in ConditionList)
+ {
+ if (orSucceeded && condition.OrNext)
+ {
+ continue;
+ }
+ else if (orSucceeded)
+ {
+ orSucceeded = false;
+ continue;
+ }
+
+ prevCond = condition.Evaluate(context, ruleMatch, prevCond);
+
+ if (condition.OrNext)
+ {
+ orSucceeded = prevCond.Success;
+ continue;
+ }
+ else if (!prevCond.Success)
+ {
+ return prevCond;
+ }
+ }
+ return prevCond;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/MatchResults.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs
similarity index 55%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/MatchResults.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs
index 6f182109f8..71eb20b8da 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/MatchResults.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/MatchResults.cs
@@ -3,10 +3,13 @@
using System.Text.RegularExpressions;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
+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 GroupCollection BackReference { get; set; }
public bool Success { get; set; }
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Condition.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Condition.cs
deleted file mode 100644
index e1af53bda7..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Condition.cs
+++ /dev/null
@@ -1,18 +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.
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- public class Condition
- {
- public Pattern TestStringSegments { get; }
- public ConditionExpression ConditionExpression { get; }
- public ConditionFlags Flags { get; }
- public Condition(Pattern testStringSegments, ConditionExpression conditionRegex, ConditionFlags flags)
- {
- TestStringSegments = testStringSegments;
- ConditionExpression = conditionRegex;
- Flags = flags;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionBuilder.cs
deleted file mode 100644
index 743b2d8a33..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionBuilder.cs
+++ /dev/null
@@ -1,94 +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 System;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- public class ConditionBuilder
- {
- private Pattern _testString;
- private ParsedModRewriteExpression _pce;
- private ConditionFlags _flags;
-
- public ConditionBuilder(string conditionString)
- {
- var tokens = Tokenizer.Tokenize(conditionString);
- if (tokens.Count == 3)
- {
- CreateCondition(tokens[1], tokens[2], flagsString: null);
- }
- else if (tokens.Count == 4)
- {
- CreateCondition(tokens[1], tokens[2], tokens[3]);
- }
- else
- {
- throw new FormatException("Invalid number of tokens.");
- }
- }
-
- public ConditionBuilder(string testString, string condition)
- {
- CreateCondition(testString, condition, flagsString: null);
- }
-
- public ConditionBuilder(string testString, string condition, string flags)
- {
- CreateCondition(testString, condition, flags);
- }
-
- public Condition Build()
- {
- var expression = ExpressionCreator.CreateConditionExpression(_pce, _flags);
- return new Condition(_testString, expression, _flags);
- }
-
- private void CreateCondition(string testString, string condition, string flagsString)
- {
- _testString = ConditionTestStringParser.ParseConditionTestString(testString);
- _pce = ConditionPatternParser.ParseActionCondition(condition);
- _flags = FlagParser.ParseConditionFlags(flagsString);
- }
-
- public void SetFlag(string flag)
- {
- SetFlag(flag, value: null);
- }
-
- public void SetFlag(ConditionFlagType flag)
- {
- SetFlag(flag, value: null);
- }
-
- public void SetFlag(string flag, string value)
- {
- if (_flags == null)
- {
- _flags = new ConditionFlags();
- }
- _flags.SetFlag(flag, value);
- }
-
- public void SetFlag(ConditionFlagType flag, string value)
- {
- if (_flags == null)
- {
- _flags = new ConditionFlags();
- }
- _flags.SetFlag(flag, value);
- }
-
- public void SetFlags(string flags)
- {
- if (_flags == null)
- {
- _flags = FlagParser.ParseConditionFlags(flags);
- }
- else
- {
- FlagParser.ParseConditionFlags(flags, _flags);
- }
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionExpression.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionExpression.cs
deleted file mode 100644
index 4d16a03ede..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionExpression.cs
+++ /dev/null
@@ -1,29 +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 System.Text.RegularExpressions;
-using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- ///
- /// Represents the ConditionPattern for a mod_rewrite rule.
- ///
- public class ConditionExpression
- {
- public Operand Operand { get; set; }
- public bool Invert { get; set; }
-
- ///
- /// Checks if a condition matches the context.
- ///
- /// The UrlRewriteContext.
- /// The previous condition results (for backreferences).
- /// The testString created from the .
- /// If the testString satisfies the condition
- public bool? CheckConditionExpression(RewriteContext context, Match previous, string testString)
- {
- return Operand.CheckOperation(previous, testString, context.FileProvider) ^ Invert;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionFlagType.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionFlagType.cs
deleted file mode 100644
index 06c7559090..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionFlagType.cs
+++ /dev/null
@@ -1,12 +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.
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- public enum ConditionFlagType
- {
- NoCase,
- Or,
- NoVary
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionFlags.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionFlags.cs
deleted file mode 100644
index e9687f8dce..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionFlags.cs
+++ /dev/null
@@ -1,101 +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 System;
-using System.Collections.Generic;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- // TODO Refactor Condition Flags and Rule Flags under base flag class
- public class ConditionFlags
- {
- private IDictionary _conditionFlagLookup = new Dictionary(StringComparer.OrdinalIgnoreCase) {
- { "nc", ConditionFlagType.NoCase},
- { "nocase", ConditionFlagType.NoCase },
- { "or", ConditionFlagType.Or},
- { "ornext", ConditionFlagType.Or },
- { "nv", ConditionFlagType.NoVary},
- { "novary", ConditionFlagType.NoVary}
- };
-
- public IDictionary FlagDictionary { get; }
-
- public ConditionFlags(IDictionary flags)
- {
- FlagDictionary = flags;
- }
-
- public ConditionFlags()
- {
- FlagDictionary = new Dictionary();
- }
- public void SetFlag(string flag)
- {
- SetFlag(flag, null);
- }
-
- public void SetFlag(string flag, string value)
- {
- ConditionFlagType res;
- if (!_conditionFlagLookup.TryGetValue(flag, out res))
- {
- throw new ArgumentException("Invalid flag");
- }
- SetFlag(res, value);
- }
-
- public void SetFlag(ConditionFlagType flag, string value)
- {
- if (value == null)
- {
- value = string.Empty;
- }
- FlagDictionary[flag] = value;
- }
-
- public string GetFlag(ConditionFlagType flag)
- {
- CleanupResources();
- string res;
- if (!FlagDictionary.TryGetValue(flag, out res))
- {
- return null;
- }
- return res;
- }
-
- public string this[ConditionFlagType flag]
- {
- get
- {
- string res;
- if (!FlagDictionary.TryGetValue(flag, out res))
- {
- return null;
- }
- return res;
- }
- set
- {
- FlagDictionary[flag] = value ?? string.Empty;
- }
- }
-
- public bool HasFlag(ConditionFlagType flag)
- {
- CleanupResources();
- string res;
- return FlagDictionary.TryGetValue(flag, out res);
- }
-
- // If this method is called, all flags have been processed,
- // therefore to clean up memory, delete dictionary.
- private void CleanupResources()
- {
- if (_conditionFlagLookup != null)
- {
- _conditionFlagLookup = null;
- }
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionPatternParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionPatternParser.cs
index 2d14d9c7d0..8279da7831 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionPatternParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionPatternParser.cs
@@ -26,17 +26,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
///
/// The CondPattern portion of a mod_rewrite RewriteCond.
/// A new parsed condition.
- public static ParsedModRewriteExpression ParseActionCondition(string condition)
+ public static ParsedModRewriteInput ParseActionCondition(string condition)
{
- if (condition == null)
+ if (condition == null)
{
condition = string.Empty;
}
var context = new ParserContext(condition);
- var results = new ParsedModRewriteExpression();
+ var results = new ParsedModRewriteInput();
if (!context.Next())
{
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
// If we hit a !, make sure the condition is inverted when resolving the string
@@ -45,88 +45,90 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
results.Invert = true;
if (!context.Next())
{
- throw new FormatException(context.Error());
+ // Dangling !
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
}
// Control Block for strings. Set the operation and type fields based on the sign
+ // Switch on current character
switch (context.Current)
{
case Greater:
if (!context.Next())
{
// Dangling ">"
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
if (context.Current == EqualSign)
{
if (!context.Next())
{
// Dangling ">="
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
- results.Operation = OperationType.GreaterEqual;
- results.Type = ConditionType.StringComp;
+ results.OperationType = OperationType.GreaterEqual;
+ results.ConditionType = ConditionType.StringComp;
}
else
{
- results.Operation = OperationType.Greater;
- results.Type = ConditionType.StringComp;
+ results.OperationType = OperationType.Greater;
+ results.ConditionType = ConditionType.StringComp;
}
break;
case Less:
if (!context.Next())
{
// Dangling "<"
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
if (context.Current == EqualSign)
{
if (!context.Next())
{
// Dangling "<="
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
- results.Operation = OperationType.LessEqual;
- results.Type = ConditionType.StringComp;
+ results.OperationType = OperationType.LessEqual;
+ results.ConditionType = ConditionType.StringComp;
}
else
{
- results.Operation = OperationType.Less;
- results.Type = ConditionType.StringComp;
+ results.OperationType = OperationType.Less;
+ results.ConditionType = ConditionType.StringComp;
}
break;
case EqualSign:
if (!context.Next())
{
// Dangling "="
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
- results.Operation = OperationType.Equal;
- results.Type = ConditionType.StringComp;
+ results.OperationType = OperationType.Equal;
+ results.ConditionType = ConditionType.StringComp;
break;
case Dash:
results = ParseProperty(context, results.Invert);
- if (results.Type == ConditionType.PropertyTest)
+ if (results.ConditionType == ConditionType.PropertyTest)
{
return results;
}
context.Next();
break;
default:
- results.Type = ConditionType.Regex;
+ results.ConditionType = ConditionType.Regex;
break;
}
// Capture the rest of the string guarantee validity.
- results.Operand = (condition.Substring(context.GetIndex()));
+ results.Operand = condition.Substring(context.GetIndex());
if (IsValidActionCondition(results))
{
return results;
}
else
{
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
}
}
@@ -137,37 +139,37 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
///
///
///
- public static ParsedModRewriteExpression ParseProperty(ParserContext context, bool invert)
+ public static ParsedModRewriteInput ParseProperty(ParserContext context, bool invert)
{
if (!context.Next())
{
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
+
switch (context.Current)
{
case 'd':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.Directory, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.Directory, operand: null);
case 'f':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.RegularFile, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.RegularFile, operand: null);
case 'F':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.ExistingFile, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.ExistingFile, operand: null);
case 'h':
case 'L':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, operand: null);
case 's':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.Size, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.Size, operand: null);
case 'U':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.ExistingUrl, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.ExistingUrl, operand: null);
case 'x':
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.Executable, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.Executable, operand: null);
case 'e':
-
if (!context.Next() || context.Current != 'q')
{
// Illegal statement.
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
- return new ParsedModRewriteExpression(invert, ConditionType.IntComp, OperationType.Equal, null);
+ return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Equal, operand: null);
case 'g':
if (!context.Next())
{
@@ -175,47 +177,49 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
}
if (context.Current == 't')
{
- return new ParsedModRewriteExpression(invert, ConditionType.IntComp, OperationType.Greater, null);
+ return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Greater, operand: null);
}
else if (context.Current == 'e')
{
- return new ParsedModRewriteExpression(invert, ConditionType.IntComp, OperationType.GreaterEqual, null);
+ return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.GreaterEqual, operand: null);
}
else
{
throw new FormatException(context.Error());
}
case 'l':
+ // name conflict with -l and -lt/-le, so the assumption is if there is no
+ // charcters after -l, we assume it a symbolic link
if (!context.Next())
{
- return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, null);
+ return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, operand: null);
}
if (context.Current == 't')
{
- return new ParsedModRewriteExpression(invert, ConditionType.IntComp, OperationType.Less, null);
+ return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.Less, operand: null);
}
else if (context.Current == 'e')
{
- return new ParsedModRewriteExpression(invert, ConditionType.IntComp, OperationType.LessEqual, null);
+ return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.LessEqual, operand: null);
}
else
{
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
case 'n':
if (!context.Next() || context.Current != 'e')
{
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
- return new ParsedModRewriteExpression(invert, ConditionType.IntComp, OperationType.NotEqual, null);
+ return new ParsedModRewriteInput(invert, ConditionType.IntComp, OperationType.NotEqual, operand: null);
default:
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
}
- private static bool IsValidActionCondition(ParsedModRewriteExpression results)
+ private static bool IsValidActionCondition(ParsedModRewriteInput results)
{
- if (results.Type == ConditionType.IntComp)
+ if (results.ConditionType == ConditionType.IntComp)
{
// If the type is an integer, verify operand is actually an int
int res;
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ExpressionCreator.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ExpressionCreator.cs
deleted file mode 100644
index 0fab8c0d3f..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ExpressionCreator.cs
+++ /dev/null
@@ -1,127 +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 System;
-using System.Text.RegularExpressions;
-using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- ///
- /// Converts a parsed expression into a mod_rewrite condition.
- ///
- public class ExpressionCreator
- {
- private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
- public static ConditionExpression CreateConditionExpression(ParsedModRewriteExpression pce, ConditionFlags flags)
- {
- var condExp = new ConditionExpression();
- condExp.Invert = pce.Invert;
- if (pce.Type == ConditionType.Regex)
- {
- // TODO make nullable?
- if (flags != null && flags.HasFlag(ConditionFlagType.NoCase))
- {
- condExp.Operand = new RegexOperand(new Regex(pce.Operand, RegexOptions.IgnoreCase | RegexOptions.Compiled, RegexTimeout));
- }
- else
- {
- condExp.Operand = new RegexOperand(new Regex(pce.Operand, RegexOptions.Compiled, RegexTimeout));
- }
- }
- else if (pce.Type == ConditionType.IntComp)
- {
- switch (pce.Operation)
- {
- case OperationType.Equal:
- condExp.Operand = new IntegerOperand(pce.Operand, IntegerOperationType.Equal);
- break;
- case OperationType.Greater:
- condExp.Operand = new IntegerOperand(pce.Operand, IntegerOperationType.Greater);
- break;
- case OperationType.GreaterEqual:
- condExp.Operand = new IntegerOperand(pce.Operand, IntegerOperationType.GreaterEqual);
- break;
- case OperationType.Less:
- condExp.Operand = new IntegerOperand(pce.Operand, IntegerOperationType.Less);
- break;
- case OperationType.LessEqual:
- condExp.Operand = new IntegerOperand(pce.Operand, IntegerOperationType.LessEqual);
- break;
- case OperationType.NotEqual:
- condExp.Operand = new IntegerOperand(pce.Operand, IntegerOperationType.NotEqual);
- break;
- default:
- throw new ArgumentException("No defined operation for integer comparison.");
- }
- }
- else if (pce.Type == ConditionType.StringComp)
- {
- switch (pce.Operation)
- {
- case OperationType.Equal:
- condExp.Operand = new StringOperand(pce.Operand, StringOperationType.Equal);
- break;
- case OperationType.Greater:
- condExp.Operand = new StringOperand(pce.Operand, StringOperationType.Greater);
- break;
- case OperationType.GreaterEqual:
- condExp.Operand = new StringOperand(pce.Operand, StringOperationType.GreaterEqual);
- break;
- case OperationType.Less:
- condExp.Operand = new StringOperand(pce.Operand, StringOperationType.Less);
- break;
- case OperationType.LessEqual:
- condExp.Operand = new StringOperand(pce.Operand, StringOperationType.LessEqual);
- break;
- default:
- throw new ArgumentException("No defined operation for string comparison.");
- }
- }
- else
- {
- switch (pce.Operation)
- {
- case OperationType.Directory:
- condExp.Operand = new PropertyOperand(PropertyOperationType.Directory);
- break;
- case OperationType.RegularFile:
- condExp.Operand = new PropertyOperand(PropertyOperationType.RegularFile);
- break;
- case OperationType.ExistingFile:
- condExp.Operand = new PropertyOperand(PropertyOperationType.ExistingFile);
- break;
- case OperationType.SymbolicLink:
- condExp.Operand = new PropertyOperand(PropertyOperationType.SymbolicLink);
- break;
- case OperationType.Size:
- condExp.Operand = new PropertyOperand(PropertyOperationType.Size);
- break;
- case OperationType.ExistingUrl:
- condExp.Operand = new PropertyOperand(PropertyOperationType.ExistingUrl);
- break;
- case OperationType.Executable:
- condExp.Operand = new PropertyOperand(PropertyOperationType.Executable);
- break;
- default:
- throw new ArgumentException("No defined operation for property comparison.");
- }
- }
- return condExp;
- }
- public static RuleExpression CreateRuleExpression(ParsedModRewriteExpression pce, RuleFlags flags)
- {
- var ruleExp = new RuleExpression();
- ruleExp.Invert = pce.Invert;
- if (flags.HasFlag(RuleFlagType.NoCase))
- {
- ruleExp.Operand = new RegexOperand(new Regex(pce.Operand, RegexOptions.IgnoreCase | RegexOptions.Compiled, RegexTimeout));
- }
- else
- {
- ruleExp.Operand = new RegexOperand(new Regex(pce.Operand, RegexOptions.Compiled, RegexTimeout));
- }
- return ruleExp;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FileParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FileParser.cs
index 3cee0d1e72..de38c0ce3b 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FileParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FileParser.cs
@@ -4,23 +4,20 @@
using System;
using System.Collections.Generic;
using System.IO;
-using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
- ///
- ///
- ///
- public static class FileParser
+ public class FileParser
{
- public static List Parse(TextReader input)
+ public List Parse(TextReader input)
{
string line = null;
var rules = new List();
- var conditions = new List();
- // TODO consider passing Itokenizer and Ifileparser and provide implementations
+ var builder = new RuleBuilder();
+ var lineNum = 0;
while ((line = input.ReadLine()) != null)
{
+ lineNum++;
if (string.IsNullOrEmpty(line))
{
continue;
@@ -33,71 +30,65 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
if (tokens.Count > 4)
{
// This means the line didn't have an appropriate format, throw format exception
- throw new FormatException();
+ throw new FormatException(Resources.FormatError_ModRewriteParseError("Too many tokens on line", lineNum));
}
- // TODO make a new class called rule parser that does and either return an exception or return the rule.
+
switch (tokens[0])
{
case "RewriteBase":
- throw new NotSupportedException();
- //if (tokens.Count == 2)
- //{
- // ModRewriteBase.Base = tokens[1];
- //}
- //else
- //{
- // throw new FormatException("");
- //}
- //break;
+ throw new NotImplementedException("RewriteBase is not implemented");
case "RewriteCond":
+ try
{
- ConditionBuilder builder = null;
- if (tokens.Count == 3)
+ var pattern = TestStringParser.Parse(tokens[1]);
+ var condActionParsed = ConditionPatternParser.ParseActionCondition(tokens[2]);
+
+ var flags = new Flags();
+ if (tokens.Count == 4)
{
- builder = new ConditionBuilder(tokens[1], tokens[2]);
+ flags = FlagParser.Parse(tokens[3]);
}
- else if (tokens.Count == 4)
- {
- builder = new ConditionBuilder(tokens[1], tokens[2], tokens[3]);
- }
- else
- {
- throw new FormatException();
- }
- conditions.Add(builder.Build());
- break;
+
+ builder.AddConditionFromParts(pattern, condActionParsed, flags);
}
+ catch (FormatException formatException)
+ {
+ throw new FormatException(Resources.FormatError_ModRewriteGeneralParseError(lineNum), formatException);
+ }
+ break;
case "RewriteRule":
+ try
{
- RuleBuilder builder = null;
- if (tokens.Count == 3)
+ var regex = RuleRegexParser.ParseRuleRegex(tokens[1]);
+ var pattern = TestStringParser.Parse(tokens[2]);
+
+ // TODO see if we can have flags be null.
+ var flags = new Flags();
+ if (tokens.Count == 4)
{
- builder = new RuleBuilder(tokens[1], tokens[2]);
+ flags = FlagParser.Parse(tokens[3]);
}
- else if (tokens.Count == 4)
- {
- builder = new RuleBuilder(tokens[1], tokens[2], tokens[3]);
- }
- else
- {
- throw new FormatException();
- }
- builder.AddConditions(conditions);
+
+ builder.AddMatch(regex, flags);
+ builder.AddAction(pattern, flags);
rules.Add(builder.Build());
- conditions = new List();
- break;
+ builder = new RuleBuilder();
+ }
+ catch (FormatException formatException)
+ {
+ throw new FormatException(Resources.FormatError_ModRewriteGeneralParseError(lineNum), formatException);
}
+ break;
case "RewriteMap":
- throw new NotImplementedException("RewriteMaps to be added soon.");
+ throw new NotImplementedException("RewriteMap to be added soon.");
case "RewriteEngine":
// Explicitly do nothing here, no notion of turning on regex engine.
break;
default:
- throw new FormatException(tokens[0]);
+ throw new FormatException(Resources.FormatError_ModRewriteParseError("Unrecognized keyword: " + tokens[0], lineNum));
}
}
return rules;
}
-
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs
index 4053ea8d7e..8252c89637 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs
@@ -5,99 +5,38 @@ using System;
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
- ///
- /// Parses the flags
- ///
public class FlagParser
- {
- // TODO Refactor Rule and Condition Flags under IFlags
- public static RuleFlags ParseRuleFlags(string flagString)
- {
- var flags = new RuleFlags();
- ParseRuleFlags(flagString, flags);
- return flags;
- }
-
- public static void ParseRuleFlags(string flagString, RuleFlags flags)
+ {
+ public static Flags Parse(string flagString)
{
if (string.IsNullOrEmpty(flagString))
{
- return;
+ return null;
}
+
// Check that flags are contained within []
if (!flagString.StartsWith("[") || !flagString.EndsWith("]"))
{
throw new FormatException();
}
- // Illegal syntax to have any spaces.
- var tokens = flagString.Substring(1, flagString.Length - 2).Split(',');
- // Go through tokens and verify they have meaning.
- // Flags can be KVPs, delimited by '='.
- foreach (string token in tokens)
- {
- if (string.IsNullOrEmpty(token))
- {
- continue;
- }
- string[] kvp = token.Split('=');
- if (kvp.Length == 1)
- {
- flags.SetFlag(kvp[0], null);
- }
- else if (kvp.Length == 2)
- {
- flags.SetFlag(kvp[0], kvp[1]);
- }
- else
- {
- throw new FormatException();
- }
- }
- }
- public static ConditionFlags ParseConditionFlags(string flagString)
- {
- var flags = new ConditionFlags();
- ParseConditionFlags(flagString, flags);
- return flags;
- }
-
- public static void ParseConditionFlags(string flagString, ConditionFlags flags)
- {
- if (string.IsNullOrEmpty(flagString))
- {
- return;
- }
- // Check that flags are contained within []
- if (!flagString.StartsWith("[") || !flagString.EndsWith("]"))
- {
- throw new FormatException();
- }
// Lexing esque step to split all flags.
- // Illegal syntax to have any spaces.
+ // Invalid syntax to have any spaces.
var tokens = flagString.Substring(1, flagString.Length - 2).Split(',');
- // Go through tokens and verify they have meaning.
- // Flags can be KVPs, delimited by '='.
+ var flags = new Flags();
foreach (string token in tokens)
{
- if (string.IsNullOrEmpty(token))
+ var hasPayload = token.Split('=');
+ if (hasPayload.Length == 2)
{
- continue;
- }
- string[] kvp = token.Split('=');
- if (kvp.Length == 1)
- {
- flags.SetFlag(kvp[0], null);
- }
- else if (kvp.Length == 2)
- {
- flags.SetFlag(kvp[0], kvp[1]);
+ flags.SetFlag(hasPayload[0], hasPayload[1]);
}
else
{
- throw new FormatException();
+ flags.SetFlag(hasPayload[0], string.Empty);
}
}
+ return flags;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleFlagType.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagType.cs
similarity index 87%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleFlagType.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagType.cs
index acdb289d2b..85919a7a41 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleFlagType.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagType.cs
@@ -3,7 +3,7 @@
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
- public enum RuleFlagType
+ public enum FlagType
{
EscapeBackreference,
Chain,
@@ -28,8 +28,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
QSLast,
Redirect,
Skip,
- Type,
- // Non-modrewrite rule
- FullUrl
+ Type
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Flags.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Flags.cs
new file mode 100644
index 0000000000..29d3ea5fc5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Flags.cs
@@ -0,0 +1,125 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
+{
+ // For more information of flags, and what flags we currently support:
+ // https://github.com/aspnet/BasicMiddleware/issues/66
+ // http://httpd.apache.org/docs/current/expr.html#vars
+ public class Flags
+ {
+ private static IDictionary _ruleFlagLookup = new Dictionary(StringComparer.OrdinalIgnoreCase) {
+ { "b", FlagType.EscapeBackreference},
+ { "c", FlagType.Chain },
+ { "chain", FlagType.Chain},
+ { "co", FlagType.Cookie },
+ { "cookie", FlagType.Cookie },
+ { "dpi", FlagType.DiscardPath },
+ { "discardpath", FlagType.DiscardPath },
+ { "e", FlagType.Env},
+ { "env", FlagType.Env},
+ { "end", FlagType.End },
+ { "f", FlagType.Forbidden },
+ { "forbidden", FlagType.Forbidden },
+ { "g", FlagType.Gone },
+ { "gone", FlagType.Gone },
+ { "h", FlagType.Handler },
+ { "handler", FlagType.Handler },
+ { "l", FlagType.Last },
+ { "last", FlagType.Last },
+ { "n", FlagType.Next },
+ { "next", FlagType.Next },
+ { "nc", FlagType.NoCase },
+ { "nocase", FlagType.NoCase },
+ { "ne", FlagType.NoEscape },
+ { "noescape", FlagType.NoEscape },
+ { "ns", FlagType.NoSubReq },
+ { "nosubreq", FlagType.NoSubReq },
+ { "p", FlagType.Proxy },
+ { "proxy", FlagType.Proxy },
+ { "pt", FlagType.PassThrough },
+ { "passthrough", FlagType.PassThrough },
+ { "qsa", FlagType.QSAppend },
+ { "qsappend", FlagType.QSAppend },
+ { "qsd", FlagType.QSDiscard },
+ { "qsdiscard", FlagType.QSDiscard },
+ { "qsl", FlagType.QSLast },
+ { "qslast", FlagType.QSLast },
+ { "r", FlagType.Redirect },
+ { "redirect", FlagType.Redirect },
+ { "s", FlagType.Skip },
+ { "skip", FlagType.Skip },
+ { "t", FlagType.Type },
+ { "type", FlagType.Type },
+ };
+
+ public IDictionary FlagDictionary { get; }
+
+ public Flags(IDictionary flags)
+ {
+ FlagDictionary = flags;
+ }
+
+ public Flags()
+ {
+ FlagDictionary = new Dictionary();
+ }
+
+ public void SetFlag(string flag, string value)
+ {
+ FlagType res;
+ if (!_ruleFlagLookup.TryGetValue(flag, out res))
+ {
+ throw new FormatException("Unrecognized flag");
+ }
+ SetFlag(res, value);
+ }
+
+ public void SetFlag(FlagType flag, string value)
+ {
+ if (value == null)
+ {
+ value = string.Empty;
+ }
+ FlagDictionary[flag] = value;
+ }
+
+ public bool GetValue(FlagType flag, out string value)
+ {
+ string res;
+ if (!FlagDictionary.TryGetValue(flag, out res))
+ {
+ value = null;
+ return false;
+ }
+ value = res;
+ return true;
+ }
+
+ public string this[FlagType flag]
+ {
+ get
+ {
+ string res;
+ if (!FlagDictionary.TryGetValue(flag, out res))
+ {
+ return null;
+ }
+ return res;
+ }
+ set
+ {
+ FlagDictionary[flag] = value ?? string.Empty;
+ }
+ }
+
+ public bool HasFlag(FlagType flag)
+ {
+ string res;
+ return FlagDictionary.TryGetValue(flag, out res);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRedirectAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRedirectAction.cs
new file mode 100644
index 0000000000..35c2ecd0df
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRedirectAction.cs
@@ -0,0 +1,77 @@
+// 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.Rewrite.Internal;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
+{
+ public class ModRewriteRedirectAction : UrlAction
+ {
+ public int StatusCode { get; }
+ public bool QueryStringAppend { get; }
+ public bool QueryStringDelete { get; }
+ public bool EscapeBackReferences { get; }
+
+ public ModRewriteRedirectAction(
+ int statusCode,
+ Pattern pattern,
+ bool queryStringAppend,
+ bool queryStringDelete,
+ bool escapeBackReferences)
+ {
+ StatusCode = statusCode;
+ Url = pattern;
+ QueryStringAppend = queryStringAppend;
+ QueryStringDelete = queryStringDelete;
+ EscapeBackReferences = escapeBackReferences;
+ }
+
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ var pattern = Url.Evaluate(context, ruleMatch, condMatch);
+ if (EscapeBackReferences)
+ {
+ // TODO right way to escape backreferences?
+ pattern = Uri.EscapeDataString(pattern);
+ }
+ context.HttpContext.Response.StatusCode = StatusCode;
+
+ // url can either contain the full url or the path and query
+ // always add to location header.
+ // TODO check for false positives
+ var split = pattern.IndexOf('?');
+ if (split >= 0)
+ {
+ QueryString query;
+ if (QueryStringAppend)
+ {
+ query = context.HttpContext.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
+ }
+ else
+ {
+ query = new QueryString(pattern.Substring(split));
+ }
+
+ // not using the response.redirect here because status codes may be 301, 302, 307, 308
+ context.HttpContext.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
+ }
+ else
+ {
+ // If the request url has a query string and the target does not, append the query string
+ // by default.
+ if (QueryStringDelete)
+ {
+ context.HttpContext.Response.Headers[HeaderNames.Location] = pattern;
+ }
+ else
+ {
+ context.HttpContext.Response.Headers[HeaderNames.Location] = pattern + context.HttpContext.Request.QueryString;
+ }
+ }
+ return RuleResult.ResponseComplete;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRewriteAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRewriteAction.cs
new file mode 100644
index 0000000000..2fd6a0940c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ModRewriteRewriteAction.cs
@@ -0,0 +1,98 @@
+// 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.Http.Extensions;
+using Microsoft.AspNetCore.Rewrite.Internal;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
+{
+ public class ModRewriteRewriteAction : UrlAction
+ {
+ private readonly string ForwardSlash = "/";
+ public RuleResult Result { get; }
+ public bool QueryStringAppend { get; }
+ public bool QueryStringDelete { get; }
+ public bool EscapeBackReferences { get; }
+
+ public ModRewriteRewriteAction(
+ RuleResult result,
+ Pattern pattern,
+ bool queryStringAppend,
+ bool queryStringDelete,
+ bool escapeBackReferences)
+ {
+ Result = result;
+ Url = pattern;
+ QueryStringAppend = queryStringAppend;
+ QueryStringDelete = queryStringDelete;
+ EscapeBackReferences = escapeBackReferences;
+ }
+
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ var pattern = Url.Evaluate(context, ruleMatch, condMatch);
+
+ // TODO PERF, substrings, object creation, etc.
+ if (pattern.IndexOf("://") >= 0)
+ {
+ string scheme = null;
+ HostString host;
+ PathString path;
+ QueryString query;
+ FragmentString fragment;
+ UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment);
+
+ if (query.HasValue)
+ {
+ if (QueryStringAppend)
+ {
+ context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add(query);
+ }
+ else
+ {
+ context.HttpContext.Request.QueryString = query;
+ }
+ }
+ else if (QueryStringDelete)
+ {
+ context.HttpContext.Request.QueryString = QueryString.Empty;
+ }
+
+ context.HttpContext.Request.Scheme = scheme;
+ context.HttpContext.Request.Host = host;
+ context.HttpContext.Request.Path = path;
+ }
+ else
+ {
+ // TODO fix with redirect action logic
+ var split = pattern.IndexOf('?');
+ if (split >= 0)
+ {
+ var path = pattern.Substring(0, split);
+ if (path.StartsWith(ForwardSlash))
+ {
+ context.HttpContext.Request.Path = PathString.FromUriComponent(path);
+ }
+ else
+ {
+ context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + path);
+ }
+ context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
+ }
+ else
+ {
+ if (pattern.StartsWith(ForwardSlash))
+ {
+ context.HttpContext.Request.Path = PathString.FromUriComponent(pattern);
+ }
+ else
+ {
+ context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + pattern);
+ }
+ }
+ }
+ return Result;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/Operand.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/Operand.cs
deleted file mode 100644
index 565cd932fc..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/Operand.cs
+++ /dev/null
@@ -1,13 +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 System.Text.RegularExpressions;
-using Microsoft.Extensions.FileProviders;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
-{
- public abstract class Operand
- {
- public abstract bool? CheckOperation(Match previous, string concatTestString, IFileProvider fileProvider);
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/PropertyOperand.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/PropertyOperand.cs
deleted file mode 100644
index fd7f649e7d..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/PropertyOperand.cs
+++ /dev/null
@@ -1,44 +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 System;
-using System.Text.RegularExpressions;
-using Microsoft.Extensions.FileProviders;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
-{
- public class PropertyOperand : Operand
- {
- public PropertyOperationType Operation { get; }
-
- public PropertyOperand(PropertyOperationType operation)
- {
- Operation = operation;
- }
- public override bool? CheckOperation(Match previous, string testString, IFileProvider fileProvider)
- {
- switch(Operation)
- {
- case PropertyOperationType.Directory:
- return fileProvider.GetFileInfo(testString).IsDirectory;
- case PropertyOperationType.RegularFile:
- return fileProvider.GetFileInfo(testString).Exists;
- case PropertyOperationType.Size:
- var fileInfo = fileProvider.GetFileInfo(testString);
- return fileInfo.Exists && fileInfo.Length > 0;
- case PropertyOperationType.ExistingUrl:
- throw new NotSupportedException("No support for internal sub requests.");
- case PropertyOperationType.ExistingFile:
- throw new NotSupportedException("No support for internal sub requests.");
- case PropertyOperationType.SymbolicLink:
- throw new NotSupportedException("No support for checking symbolic links.");
- case PropertyOperationType.Executable:
- throw new NotSupportedException("No support for checking executable permissions.");
- default:
- return false;
- }
- }
- }
-
-
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/PropertyOperation.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/PropertyOperation.cs
deleted file mode 100644
index f151c1db64..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/PropertyOperation.cs
+++ /dev/null
@@ -1,17 +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.
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
-{
- public enum PropertyOperationType
- {
- None,
- Directory,
- RegularFile,
- ExistingFile,
- SymbolicLink,
- Size,
- ExistingUrl,
- Executable
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/RegexOperand.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/RegexOperand.cs
deleted file mode 100644
index e7159b8e43..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/RegexOperand.cs
+++ /dev/null
@@ -1,24 +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 System.Text.RegularExpressions;
-using Microsoft.Extensions.FileProviders;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
-{
- public class RegexOperand : Operand
- {
- public Regex RegexOperation { get; }
-
- public RegexOperand(Regex regex)
- {
- RegexOperation = regex;
- }
-
- public override bool? CheckOperation(Match previous, string concatTestString, IFileProvider fileProvider)
- {
- previous = RegexOperation.Match(concatTestString);
- return previous.Success;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/StringOperand.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/StringOperand.cs
deleted file mode 100644
index a48b929eff..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/StringOperand.cs
+++ /dev/null
@@ -1,40 +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 System;
-using System.Text.RegularExpressions;
-using Microsoft.Extensions.FileProviders;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
-{
- public class StringOperand : Operand
- {
- public string Value { get; set; }
- public StringOperationType Operation { get; set; }
-
- public StringOperand(string value, StringOperationType operation)
- {
- Value = value;
- Operation = operation;
- }
-
- public override bool? CheckOperation(Match previous, string concatTestString, IFileProvider fileProvider)
- {
- switch (Operation)
- {
- case StringOperationType.Equal:
- return concatTestString.CompareTo(Value) == 0;
- case StringOperationType.Greater:
- return concatTestString.CompareTo(Value) > 0;
- case StringOperationType.GreaterEqual:
- return concatTestString.CompareTo(Value) >= 0;
- case StringOperationType.Less:
- return concatTestString.CompareTo(Value) < 0;
- case StringOperationType.LessEqual:
- return concatTestString.CompareTo(Value) <= 0;
- default:
- return null;
- }
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ParsedConditionExpression.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ParsedModRewriteCondition.cs
similarity index 50%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ParsedConditionExpression.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ParsedModRewriteCondition.cs
index 5bb69e6e8d..e854343fe5 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ParsedConditionExpression.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ParsedModRewriteCondition.cs
@@ -3,20 +3,21 @@
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
- public class ParsedModRewriteExpression
+ public class ParsedModRewriteInput
{
public bool Invert { get; set; }
- public ConditionType Type { get; set; }
- public OperationType Operation { get; set; }
+ public ConditionType ConditionType { get; set; }
+ public OperationType OperationType { get; set; }
public string Operand { get; set; }
- public ParsedModRewriteExpression(bool invert, ConditionType type, OperationType operation, string operand)
+
+ public ParsedModRewriteInput() { }
+
+ public ParsedModRewriteInput(bool invert, ConditionType conditionType, OperationType operationType, string operand)
{
Invert = invert;
- Type = type;
- Operation = operation;
+ ConditionType = conditionType;
+ OperationType = operationType;
Operand = operand;
}
-
- public ParsedModRewriteExpression() { }
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Pattern.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Pattern.cs
deleted file mode 100644
index 8d7de10791..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Pattern.cs
+++ /dev/null
@@ -1,67 +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 System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- ///
- /// Contains a sequence of pattern segments, which on obtaining the context, will create the appropriate
- /// test string and condition for rules and conditions.
- ///
- public class Pattern
- {
- private IReadOnlyList Segments { get; }
- ///
- /// Creates a new Pattern
- ///
- /// List of pattern segments which will be applied.
- public Pattern(IReadOnlyList segments)
- {
- Segments = segments;
- }
-
- ///
- /// Creates the appropriate test string from the Httpcontext and Segments.
- ///
- ///
- ///
- ///
- ///
- public string GetPattern(HttpContext context, Match ruleMatch, Match prevCondition)
- {
- var res = new StringBuilder();
- foreach (var segment in Segments)
- {
- // TODO handle case when segment.Variable is 0 in rule and condition
- switch (segment.Type)
- {
- case SegmentType.Literal:
- res.Append(segment.Variable);
- break;
- case SegmentType.ServerParameter:
- res.Append(ServerVariables.Resolve(segment.Variable, context));
- break;
- case SegmentType.RuleParameter:
- var ruleParam = ruleMatch.Groups[segment.Variable];
- if (ruleParam != null)
- {
- res.Append(ruleParam);
- }
- break;
- case SegmentType.ConditionParameter:
- var condParam = prevCondition.Groups[segment.Variable];
- if (condParam != null)
- {
- res.Append(condParam);
- }
- break;
- }
- }
- return res.ToString();
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/PatternSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/PatternSegment.cs
deleted file mode 100644
index 8a59179b5a..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/PatternSegment.cs
+++ /dev/null
@@ -1,26 +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.
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- ///
- /// A Pattern segment contains a portion of the test string/ substitution segment with a type associated.
- /// This type can either be: Regex, Rule Variable, Condition Variable, or a Server Variable.
- ///
- public class PatternSegment
- {
- public string Variable { get; } // TODO make this a range s.t. we don't copy the string.
- public SegmentType Type { get; }
-
- ///
- /// Create a Pattern segment.
- ///
- ///
- ///
- public PatternSegment(string variable, SegmentType type)
- {
- Variable = variable;
- Type = type;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs
index 9c288b00c3..0510803c4f 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs
@@ -3,119 +3,221 @@
using System;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.PreActions;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public class RuleBuilder
{
- private ParsedModRewriteExpression _pce;
- private List _conditions;
- private RuleFlags _flags;
- private Pattern _patterns;
+ private Conditions _conditions;
+ private UrlAction _action;
+ private UrlMatch _match;
+ private List _preActions;
+
+ private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
+
public ModRewriteRule Build()
{
- var ruleExpression = ExpressionCreator.CreateRuleExpression(_pce, _flags);
- return new ModRewriteRule(_conditions, ruleExpression, _patterns, _flags);
+ if (_action == null || _match == null)
+ {
+ // TODO throw an exception here, find apporpriate exception
+ }
+ return new ModRewriteRule(_match, _conditions, _action, _preActions);
}
- public RuleBuilder(string initialRule, string transformation) :
- this(initialRule, transformation, flags: null)
- {
- }
- public RuleBuilder(string rule)
+ public void AddRule(string rule)
{
+ // TODO
var tokens = Tokenizer.Tokenize(rule);
- if (tokens.Count == 3)
+ var regex = RuleRegexParser.ParseRuleRegex(tokens[1]);
+ var pattern = TestStringParser.Parse(tokens[2]);
+ var flags = new Flags();
+ if (tokens.Count == 4)
{
- CreateRule(tokens[1], tokens[2], flags: null);
+ flags = FlagParser.Parse(tokens[3]);
}
- else if (tokens.Count == 4)
+ AddMatch(regex, flags);
+ AddAction(pattern, flags);
+ }
+
+ public void AddConditionFromParts(
+ Pattern pattern,
+ ParsedModRewriteInput input,
+ Flags flags)
+ {
+ if (_conditions == null)
{
- CreateRule(tokens[1], tokens[2], tokens[3]);
+ _conditions = new Conditions();
+ }
+
+ var condition = new Condition();
+
+ condition.OrNext = flags.HasFlag(FlagType.Or);
+ condition.Input = pattern;
+
+ if (input.ConditionType == ConditionType.Regex)
+ {
+ // TODO make nullable?
+ 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
{
- throw new ArgumentException();
+ 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:
+ // TODO change exception
+ throw new ArgumentException("Invalid operation for property comparison.");
+ }
}
+ _conditions.ConditionList.Add(condition);
}
- public RuleBuilder(string initialRule, string transformation, string flags)
+ public void AddMatch(
+ ParsedModRewriteInput input,
+ Flags flags)
{
- CreateRule(initialRule, transformation, flags);
- }
-
- public void CreateRule(string initialRule, string transformation, string flags)
- {
- _pce = RuleRegexParser.ParseRuleRegex(initialRule);
- _patterns = ConditionTestStringParser.ParseConditionTestString(transformation);
- _flags = FlagParser.ParseRuleFlags(flags);
- }
-
- public void AddCondition(string condition)
- {
- if (_conditions == null)
+ if (flags.HasFlag(FlagType.NoCase))
{
- _conditions = new List();
- }
- var condBuilder = new ConditionBuilder(condition);
- _conditions.Add(condBuilder.Build());
- }
-
- public void AddCondition(Condition condition)
- {
- if (_conditions == null)
- {
- _conditions = new List();
- }
- _conditions.Add(condition);
- }
-
- public void AddConditions(List conditions)
- {
- if (_conditions == null)
- {
- _conditions = new List();
- }
- _conditions.AddRange(conditions);
- }
-
- public void SetFlag(string flag)
- {
- SetFlag(flag, value: null);
- }
-
- public void SetFlag(RuleFlagType flag)
- {
- SetFlag(flag, value: null);
- }
-
- public void SetFlag(string flag, string value)
- {
- if (_flags == null)
- {
- _flags = new RuleFlags();
- }
- _flags.SetFlag(flag, value);
- }
-
- public void SetFlag(RuleFlagType flag, string value)
- {
- if (_flags == null)
- {
- _flags = new RuleFlags();
- }
- _flags.SetFlag(flag, value);
- }
-
- public void SetFlags(string flags)
- {
- if (_flags == null)
- {
- _flags = FlagParser.ParseRuleFlags(flags);
+ _match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout), input.Invert);
}
else
{
- FlagParser.ParseRuleFlags(flags, _flags);
+ _match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled, RegexTimeout), input.Invert);
+ }
+ }
+
+ public void AddAction(
+ Pattern pattern,
+ Flags flags)
+ {
+ // first create pre conditions
+ if (_preActions == null)
+ {
+ _preActions = new List();
+ }
+
+ string flag;
+ if (flags.GetValue(FlagType.Cookie, out flag))
+ {
+ // parse cookie
+ _preActions.Add(new ChangeCookiePreAction(flag));
+ }
+
+ if (flags.GetValue(FlagType.Env, out flag))
+ {
+ // parse env
+ _preActions.Add(new ChangeEnvironmentPreAction(flag));
+ }
+
+ if (flags.HasFlag(FlagType.Forbidden))
+ {
+ _action = new ForbiddenAction();
+ }
+ else if (flags.HasFlag(FlagType.Gone))
+ {
+ _action = new GoneAction();
+ }
+ else
+ {
+ var escapeBackReference = flags.HasFlag(FlagType.EscapeBackreference);
+ var queryStringAppend = flags.HasFlag(FlagType.QSAppend);
+ var queryStringDelete = flags.HasFlag(FlagType.QSDiscard);
+
+ // is redirect?
+ string statusCode;
+ if (flags.GetValue(FlagType.Redirect, out statusCode))
+ {
+ int res;
+ if (!int.TryParse(statusCode, out res))
+ {
+ throw new FormatException(Resources.FormatError_InputParserInvalidInteger(statusCode, -1));
+ }
+ _action = new ModRewriteRedirectAction(res, pattern, queryStringAppend, queryStringDelete, escapeBackReference);
+ }
+ else
+ {
+ var last = flags.HasFlag(FlagType.End) || flags.HasFlag(FlagType.Last);
+ var redirect = last ? RuleResult.StopRules : RuleResult.Continue;
+ _action = new ModRewriteRewriteAction(redirect, pattern, queryStringAppend, queryStringDelete, escapeBackReference);
+ }
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleExpression.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleExpression.cs
deleted file mode 100644
index 1c29b18f0e..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleExpression.cs
+++ /dev/null
@@ -1,13 +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.Rewrite.Internal.ModRewrite.Operands;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- public class RuleExpression
- {
- public RegexOperand Operand { get; set; }
- public bool Invert { get; set; }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleFlags.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleFlags.cs
deleted file mode 100644
index 1c2d632d41..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleFlags.cs
+++ /dev/null
@@ -1,133 +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 System;
-using System.Collections.Generic;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
-{
- public class RuleFlags
- {
- private IDictionary _ruleFlagLookup = new Dictionary(StringComparer.OrdinalIgnoreCase) {
- { "b", RuleFlagType.EscapeBackreference},
- { "c", RuleFlagType.Chain },
- { "chain", RuleFlagType.Chain},
- { "co", RuleFlagType.Cookie },
- { "cookie", RuleFlagType.Cookie },
- { "dpi", RuleFlagType.DiscardPath },
- { "discardpath", RuleFlagType.DiscardPath },
- { "e", RuleFlagType.Env},
- { "env", RuleFlagType.Env},
- { "end", RuleFlagType.End },
- { "f", RuleFlagType.Forbidden },
- { "forbidden", RuleFlagType.Forbidden },
- { "g", RuleFlagType.Gone },
- { "gone", RuleFlagType.Gone },
- { "h", RuleFlagType.Handler },
- { "handler", RuleFlagType.Handler },
- { "l", RuleFlagType.Last },
- { "last", RuleFlagType.Last },
- { "n", RuleFlagType.Next },
- { "next", RuleFlagType.Next },
- { "nc", RuleFlagType.NoCase },
- { "nocase", RuleFlagType.NoCase },
- { "ne", RuleFlagType.NoEscape },
- { "noescape", RuleFlagType.NoEscape },
- { "ns", RuleFlagType.NoSubReq },
- { "nosubreq", RuleFlagType.NoSubReq },
- { "p", RuleFlagType.Proxy },
- { "proxy", RuleFlagType.Proxy },
- { "pt", RuleFlagType.PassThrough },
- { "passthrough", RuleFlagType.PassThrough },
- { "qsa", RuleFlagType.QSAppend },
- { "qsappend", RuleFlagType.QSAppend },
- { "qsd", RuleFlagType.QSDiscard },
- { "qsdiscard", RuleFlagType.QSDiscard },
- { "qsl", RuleFlagType.QSLast },
- { "qslast", RuleFlagType.QSLast },
- { "r", RuleFlagType.Redirect },
- { "redirect", RuleFlagType.Redirect },
- { "s", RuleFlagType.Skip },
- { "skip", RuleFlagType.Skip },
- { "t", RuleFlagType.Type },
- { "type", RuleFlagType.Type },
- // TODO make this a load bool instead of a flag for the file and rules.
- { "u", RuleFlagType.FullUrl },
- { "url", RuleFlagType.FullUrl }
- };
-
- public IDictionary FlagDictionary { get; }
-
- public RuleFlags(IDictionary flags)
- {
- // TODO use ref to check dictionary equality
- FlagDictionary = flags;
- }
-
- public RuleFlags()
- {
- FlagDictionary = new Dictionary();
- }
-
- public void SetFlag(string flag, string value)
- {
- RuleFlagType res;
- if (!_ruleFlagLookup.TryGetValue(flag, out res))
- {
- throw new FormatException("Invalid flag");
- }
- SetFlag(res, value);
- }
- public void SetFlag(RuleFlagType flag, string value)
- {
- if (value == null)
- {
- value = string.Empty;
- }
- FlagDictionary[flag] = value;
- }
-
- public string GetValue(RuleFlagType flag)
- {
- CleanupResources();
- string res;
- if (!FlagDictionary.TryGetValue(flag, out res))
- {
- return null;
- }
- return res;
- }
-
- public string this[RuleFlagType flag]
- {
- get
- {
- string res;
- if (!FlagDictionary.TryGetValue(flag, out res))
- {
- return null;
- }
- return res;
- }
- set
- {
- FlagDictionary[flag] = value ?? string.Empty;
- }
- }
-
- public bool HasFlag(RuleFlagType flag)
- {
- CleanupResources();
- string res;
- return FlagDictionary.TryGetValue(flag, out res);
- }
-
- private void CleanupResources()
- {
- if (_ruleFlagLookup != null)
- {
- _ruleFlagLookup = null;
- }
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleRegexParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleRegexParser.cs
index 3d8254e44d..e063b36bc4 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleRegexParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleRegexParser.cs
@@ -7,19 +7,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public static class RuleRegexParser
{
- public static ParsedModRewriteExpression ParseRuleRegex(string regex)
+ public static ParsedModRewriteInput ParseRuleRegex(string regex)
{
- if (regex == null || regex == String.Empty)
+ if (regex == null || regex == string.Empty)
{
- throw new FormatException();
+ throw new FormatException("Regex expression is null");
}
if (regex.StartsWith("!"))
{
- return new ParsedModRewriteExpression { Invert = true, Operand = regex.Substring(1) };
+ return new ParsedModRewriteInput { Invert = true, Operand = regex.Substring(1) };
}
else
{
- return new ParsedModRewriteExpression { Invert = false, Operand = regex};
+ return new ParsedModRewriteInput { Invert = false, Operand = regex};
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/SegmentType.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/SegmentType.cs
index b8cbb3f3c3..2e86cde344 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/SegmentType.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/SegmentType.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
public enum SegmentType
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ServerVariables.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ServerVariables.cs
index ec219142d2..5b664f4172 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ServerVariables.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ServerVariables.cs
@@ -2,11 +2,8 @@
// 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.Globalization;
-using System.Net.Sockets;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
@@ -16,164 +13,112 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
///
public static class ServerVariables
{
- public static HashSet ValidServerVariables = new HashSet()
- {
- "HTTP_ACCEPT",
- "HTTP_COOKIE",
- "HTTP_FORWARDED",
- "HTTP_HOST",
- "HTTP_PROXY_CONNECTION",
- "HTTP_REFERER",
- "HTTP_USER_AGENT",
- "AUTH_TYPE",
- "CONN_REMOTE_ADDR",
- "CONTEXT_PREFIX",
- "CONTEXT_DOCUMENT_ROOT",
- "IPV6",
- "PATH_INFO",
- "QUERY_STRING",
- "REMOTE_ADDR",
- "REMOTE_HOST",
- "REMOTE_IDENT",
- "REMOTE_PORT",
- "REMOTE_USER",
- "REQUEST_METHOD",
- "SCRIPT_FILENAME",
- "DOCUMENT_ROOT",
- "SCRIPT_GROUP",
- "SCRIPT_USER",
- "SERVER_ADDR",
- "SERVER_ADMIN",
- "SERVER_NAME",
- "SERVER_PORT",
- "SERVER_PROTOCOL",
- "SERVER_SOFTWARE",
- "TIME_YEAR",
- "TIME_MON",
- "TIME_DAY",
- "TIME_HOUR",
- "TIME_MIN",
- "TIME_SEC",
- "TIME_WDAY",
- "TIME",
- "API_VERSION",
- "HTTPS",
- "IS_SUBREQ",
- "REQUEST_FILENAME",
- "REQUEST_SCHEME",
- "REQUEST_URI",
- "THE_REQUEST"
-
- };
///
/// Translates mod_rewrite server variables strings to an enum of different server variables.
///
/// The server variable string.
- /// The HttpContext context.
+ /// The Parser context
/// The appropriate enum if the server variable exists, else ServerVariable.None
- public static string Resolve(string variable, HttpContext context)
+ public static PatternSegment FindServerVariable(string variable, ParserContext context)
{
- // TODO talk about perf here
switch (variable)
{
case "HTTP_ACCEPT":
- return context.Request.Headers[HeaderNames.Accept];
+ return new HeaderSegment(HeaderNames.Accept);
case "HTTP_COOKIE":
- return context.Request.Headers[HeaderNames.Cookie];
- case "HTTP_FORWARDED":
- return context.Request.Headers["Forwarded"];
+ return new HeaderSegment(HeaderNames.Cookie);
case "HTTP_HOST":
- return context.Request.Headers[HeaderNames.Host];
- case "HTTP_PROXY_CONNECTION":
- return context.Request.Headers[HeaderNames.ProxyAuthenticate];
+ return new HeaderSegment(HeaderNames.Host);
case "HTTP_REFERER":
- return context.Request.Headers[HeaderNames.Referer];
+ return new HeaderSegment(HeaderNames.Referer);
case "HTTP_USER_AGENT":
- return context.Request.Headers[HeaderNames.UserAgent];
+ return new HeaderSegment(HeaderNames.UserAgent);
+ case "HTTP_CONNECTION":
+ return new HeaderSegment(HeaderNames.Connection);
+ case "HTTP_FORWARDED":
+ return new HeaderSegment("Forwarded");
case "AUTH_TYPE":
- throw new NotImplementedException();
+ throw new NotImplementedException("Auth-Type server variable is not supported");
case "CONN_REMOTE_ADDR":
- return context.Connection.RemoteIpAddress?.ToString();
+ return new RemoteAddressSegment();
case "CONTEXT_PREFIX":
- throw new NotImplementedException();
+ throw new NotImplementedException("Context-prefix server variable is not supported");
case "CONTEXT_DOCUMENT_ROOT":
- throw new NotImplementedException();
+ throw new NotImplementedException("Context-Document-Root server variable is not supported");
case "IPV6":
- return context.Connection.LocalIpAddress.AddressFamily == AddressFamily.InterNetworkV6 ? "on" : "off";
+ return new IsIPV6Segment();
case "PATH_INFO":
- throw new NotImplementedException();
+ throw new NotImplementedException("Path-Info server variable is not supported");
case "QUERY_STRING":
- return context.Request.QueryString.Value;
+ return new QueryStringSegment();
case "REMOTE_ADDR":
- return context.Connection.RemoteIpAddress?.ToString();
+ return new RemoteAddressSegment();
case "REMOTE_HOST":
- throw new NotImplementedException();
+ throw new NotImplementedException("Remote-Host server variable is not supported");
case "REMOTE_IDENT":
- throw new NotImplementedException();
+ throw new NotImplementedException("Remote-Identity server variable is not supported");
case "REMOTE_PORT":
- return context.Connection.RemotePort.ToString(CultureInfo.InvariantCulture);
+ return new RemotePortSegment();
case "REMOTE_USER":
- throw new NotImplementedException();
+ throw new NotImplementedException("Remote-User server variable is not supported");
case "REQUEST_METHOD":
- return context.Request.Method;
+ return new RequestMethodSegment();
case "SCRIPT_FILENAME":
- throw new NotImplementedException();
+ throw new NotImplementedException("Script-Filename server variable is not supported");
case "DOCUMENT_ROOT":
- throw new NotImplementedException();
+ throw new NotImplementedException("Document-Root server variable is not supported");
case "SCRIPT_GROUP":
- throw new NotImplementedException();
+ throw new NotImplementedException("Script-Group server variable is not supported");
case "SCRIPT_USER":
- throw new NotImplementedException();
+ throw new NotImplementedException("Script-User server variable is not supported");
case "SERVER_ADDR":
- return context.Connection.LocalIpAddress?.ToString();
+ return new LocalAddressSegment();
case "SERVER_ADMIN":
- throw new NotImplementedException();
+ throw new NotImplementedException("Server-Admin server variable is not supported");
case "SERVER_NAME":
- throw new NotImplementedException();
+ throw new NotImplementedException("Server-Name server variable is not supported");
case "SERVER_PORT":
- return context.Connection.LocalPort.ToString(CultureInfo.InvariantCulture);
+ return new LocalPortSegment();
case "SERVER_PROTOCOL":
- return context.Features.Get()?.Protocol;
+ return new ServerProtocolSegment();
case "SERVER_SOFTWARE":
- throw new NotImplementedException();
+ throw new NotImplementedException("Server-Software server variable is not supported");
case "TIME_YEAR":
- return DateTimeOffset.UtcNow.Year.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME_MON":
- return DateTimeOffset.UtcNow.Month.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME_DAY":
- return DateTimeOffset.UtcNow.Day.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME_HOUR":
- return DateTimeOffset.UtcNow.Hour.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME_MIN":
- return DateTimeOffset.UtcNow.Minute.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME_SEC":
- return DateTimeOffset.UtcNow.Second.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME_WDAY":
- return ((int) DateTimeOffset.UtcNow.DayOfWeek).ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "TIME":
- return DateTimeOffset.UtcNow.ToString(CultureInfo.InvariantCulture);
+ return new DateTimeSegment(variable);
case "API_VERSION":
throw new NotImplementedException();
case "HTTPS":
- return context.Request.IsHttps ? "on" : "off";
+ return new IsHttpsModSegment();
case "HTTP2":
- return context.Request.Scheme == "http2" ? "on" : "off";
+ throw new NotImplementedException("Http2 server variable is not supported");
case "IS_SUBREQ":
// TODO maybe can do this? context.Request.HttpContext ?
- throw new NotImplementedException();
+ throw new NotImplementedException("Is-Subrequest server variable is not supported");
case "REQUEST_FILENAME":
- return context.Request.Path.Value.Substring(1);
+ return new RequestFileNameSegment();
case "REQUEST_SCHEME":
- return context.Request.Scheme;
+ return new SchemeSegment();
case "REQUEST_URI":
- // TODO This isn't an ideal solution. What this assumes is that all conditions don't have a leading slash before it.
- return context.Request.Path.Value.Substring(1);
+ return new UrlSegment();
case "THE_REQUEST":
- // TODO
- throw new NotImplementedException();
+ throw new NotImplementedException("The-Request server variable is not supported");
default:
- return null;
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(variable, context.Index));
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionTestStringParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/TestStringParser.cs
similarity index 72%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionTestStringParser.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/TestStringParser.cs
index e3f8b1a18e..4564c3cba1 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/ConditionTestStringParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/TestStringParser.cs
@@ -4,13 +4,14 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
///
/// Parses the TestString segment of the mod_rewrite condition.
///
- public class ConditionTestStringParser
+ public class TestStringParser
{
private const char Percent = '%';
private const char Dollar = '$';
@@ -30,7 +31,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
/// %1
/// $1
/// A new , containing a list of
- public static Pattern ParseConditionTestString(string testString)
+ /// http://httpd.apache.org/docs/current/mod/mod_rewrite.html
+ public static Pattern Parse(string testString)
{
if (testString == null)
{
@@ -45,12 +47,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
// This is a server parameter, parse for a condition variable
if (!context.Next())
{
- throw new FormatException(context.Error());
- }
- if (!ParseConditionParameter(context, results))
- {
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(testString, context.Index));
}
+ ParseConditionParameter(context, results);
}
else if (context.Current == Dollar)
{
@@ -58,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
// and create a new Pattern Segment.
if (!context.Next())
{
- throw new FormatException(context.Error());
+ throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
}
context.Mark();
if (context.Current >= '0' && context.Current <= '9')
@@ -66,21 +65,24 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
context.Next();
var ruleVariable = context.Capture();
context.Back();
- results.Add(new PatternSegment(ruleVariable, SegmentType.RuleParameter));
+ int parsedIndex;
+ if (!int.TryParse(ruleVariable, out parsedIndex))
+ {
+ // TODO this should always pass, remove try parse?
+ throw new FormatException(Resources.FormatError_InputParserInvalidInteger(ruleVariable, context.Index));
+ }
+ results.Add(new RuleMatchSegment(parsedIndex));
}
else
{
- throw new FormatException(context.Error());
+ 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
- if (!ParseLiteral(context, results))
- {
- throw new FormatException(context.Error());
- }
+ ParseLiteral(context, results);
}
}
return new Pattern(results);
@@ -95,7 +97,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
/// The ParserContext
/// The List of results which the new condition parameter will be added.
/// true
- private static bool ParseConditionParameter(ParserContext context, List results)
+ private static void ParseConditionParameter(ParserContext context, List results)
{
// Parse { }
if (context.Current == OpenBrace)
@@ -104,34 +106,26 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
if (!context.Next())
{
// Dangling {
- return false;
+ throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
}
context.Mark();
while (context.Current != CloseBrace)
{
if (!context.Next())
{
- // No closing } for the server variable
- return false;
+ throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
}
else if (context.Current == Colon)
{
// Have a segmented look up Ex: HTTP:xxxx
// TODO
+ throw new NotImplementedException("Segmented Lookups no implemented");
}
}
// Need to verify server variable captured exists
var rawServerVariable = context.Capture();
- if (IsValidServerVariable(rawServerVariable))
- {
- results.Add(new PatternSegment(rawServerVariable, SegmentType.ServerParameter));
- }
- else
- {
- // invalid.
- return false;
- }
+ results.Add(ServerVariables.FindServerVariable(rawServerVariable, context));
}
else if (context.Current >= '0' && context.Current <= '9')
{
@@ -143,14 +137,18 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
// Once we leave this method, the while loop will call next again. Because
// capture is exclusive, we need to go one past the end index, capture, and then go back.
context.Back();
- results.Add(new PatternSegment(rawConditionParameter, SegmentType.ConditionParameter));
+ int parsedIndex;
+ if (!int.TryParse(rawConditionParameter, out parsedIndex))
+ {
+ throw new FormatException(Resources.FormatError_InputParserInvalidInteger(rawConditionParameter, context.Index));
+ }
+ results.Add(new ConditionMatchSegment(parsedIndex));
}
else
{
// illegal escape of a character
- return false;
+ throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
}
- return true;
}
///
@@ -159,7 +157,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
///
///
///
- private static bool ParseLiteral(ParserContext context, List results)
+ private static void ParseLiteral(ParserContext context, List results)
{
context.Mark();
string literal;
@@ -177,29 +175,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
break;
}
}
-
- if (IsValidLiteral(context, literal))
- {
- // add results
- results.Add(new PatternSegment(literal, SegmentType.Literal));
- return true;
- }
- else
- {
- return false;
- }
- }
-
- private static bool IsValidLiteral(ParserContext context, string literal)
- {
- // TODO Once escape characters are discussed, figure this out.
- return true;
- }
-
- private static bool IsValidServerVariable(string variable)
- {
- // TODO Once escape characters are discussed, figure this out.
- return ServerVariables.ValidServerVariables.Contains(variable);
+ // add results
+ results.Add(new LiteralSegment(literal));
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Tokenizer.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Tokenizer.cs
index 18979e29a0..a13bdeb9ef 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Tokenizer.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Tokenizer.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
{
@@ -50,7 +49,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
if (!context.Next())
{
// Means that a character was not escaped appropriately Ex: "foo\"
- throw new ArgumentException();
+ throw new FormatException("Invalid escaper character in string " + rule);
}
}
else if (context.Current == Space || context.Current == Tab)
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs
index 529ae08eb5..6d7e090b9a 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs
@@ -1,208 +1,54 @@
// 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 Microsoft.Net.Http.Headers;
-using System.Text.RegularExpressions;
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Rewrite.Internal;
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class ModRewriteRule : Rule
{
- public List Conditions { get; set; } = new List();
- public string Description { get; set; } = string.Empty;
- public RuleExpression InitialRule { get; set; }
- public Pattern Transform { get; set; }
- public RuleFlags Flags { get; set; } = new RuleFlags();
- public ModRewriteRule() { }
+ public UrlMatch InitialMatch { get; set; }
+ public Conditions Conditions { get; set; }
+ public UrlAction Action { get; set; }
+ public List PreActions { get; set; }
- public ModRewriteRule(List conditions, RuleExpression initialRule, Pattern transforms, RuleFlags flags, string description = "")
+ public ModRewriteRule(UrlMatch initialMatch, Conditions conditions, UrlAction urlAction, List preActions)
{
Conditions = conditions;
- InitialRule = initialRule;
- Transform = transforms;
- Flags = flags;
- Description = description;
+ InitialMatch = initialMatch;
+ Action = urlAction;
+ PreActions = preActions;
}
public override RuleResult ApplyRule(RewriteContext context)
{
// 1. Figure out which section of the string to match for the initial rule.
- var results = InitialRule.Operand.RegexOperation.Match(context.HttpContext.Request.Path.ToString());
+ var initMatchRes = InitialMatch.Evaluate(context.HttpContext.Request.Path, context);
- string flagRes = null;
- if (CheckMatchResult(results.Success))
+ if (!initMatchRes.Success)
{
return RuleResult.Continue;
}
- if (Flags.HasFlag(RuleFlagType.EscapeBackreference))
+ MatchResults condMatchRes = null;
+ if (Conditions != null)
{
- // TODO Escape Backreferences here.
- }
-
- // 2. Go through all conditions and compare them to the created string
- var previous = Match.Empty;
-
- if (!CheckCondition(context, results, previous))
- {
- return RuleResult.Continue;
- }
- // TODO add chained flag
-
- // at this point, our rule passed, we can now apply the on match function
- var result = Transform.GetPattern(context.HttpContext, results, previous);
-
- if (Flags.HasFlag(RuleFlagType.QSDiscard))
- {
- context.HttpContext.Request.QueryString = new QueryString();
- }
-
- if ((flagRes = Flags.GetValue(RuleFlagType.Cookie)) != null)
- {
- // TODO CreateCookies(context);
- // context.HttpContext.Response.Cookies.Append()
- // Make sure this in on compile.
- }
-
- if ((flagRes = Flags.GetValue(RuleFlagType.Env)) != null)
- {
- // TODO CreateEnv(context)
- // context.HttpContext...
- }
-
- if ((flagRes = Flags.GetValue(RuleFlagType.Next)) != null)
- {
- // TODO Next flag
- }
-
- if (Flags.HasFlag(RuleFlagType.Forbidden))
- {
- context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
- return RuleResult.ResponseComplete;
- }
- else if (Flags.HasFlag(RuleFlagType.Gone))
- {
- context.HttpContext.Response.StatusCode = StatusCodes.Status410Gone;
- return RuleResult.ResponseComplete;
- }
- else if (result == "-")
- {
- // TODO set url to result.
- }
- else if (Flags.HasFlag(RuleFlagType.QSAppend))
- {
- context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add(new QueryString(result));
- }
-
- if ((flagRes = Flags.GetValue(RuleFlagType.Redirect)) != null)
- {
- int parsedInt;
- if (!int.TryParse(flagRes, out parsedInt))
- {
- // TODO PERF parse the status code when the flag is parsed rather than per request
- throw new FormatException("Trying to parse non-int in integer comparison.");
- }
- context.HttpContext.Response.StatusCode = parsedInt;
- if (Flags.HasFlag(RuleFlagType.FullUrl))
- {
- // TODO review escaping
- context.HttpContext.Response.Headers[HeaderNames.Location] = result;
- }
- else
- {
- // TODO str cat is bad, polish, review escaping
- if (result.StartsWith("/"))
- {
- context.HttpContext.Response.Headers[HeaderNames.Location] = result + context.HttpContext.Request.QueryString;
- }
- else
- {
- context.HttpContext.Response.Headers[HeaderNames.Location] = "/" + result + context.HttpContext.Request.QueryString;
- }
- }
- return RuleResult.ResponseComplete;
- }
- else
- {
- if (Flags.HasFlag(RuleFlagType.FullUrl))
- {
- ModifyHttpContextFromUri(context.HttpContext, result);
- }
- else
- {
- if (result.StartsWith("/"))
- {
- context.HttpContext.Request.Path = new PathString(result);
- }
- else
- {
- context.HttpContext.Request.Path = new PathString("/" + result);
- }
- }
- if (Flags.HasFlag(RuleFlagType.Last) || Flags.HasFlag(RuleFlagType.End))
- {
- return RuleResult.StopRules;
- }
- else
+ condMatchRes = Conditions.Evaluate(context, initMatchRes);
+ if (!condMatchRes.Success)
{
return RuleResult.Continue;
}
}
- }
- private bool CheckMatchResult(bool? result)
- {
- if (result == null)
+ // At this point, we know our rule passed, first apply pre conditions,
+ // which can modify things like the cookie or env, and then apply the action
+ foreach (var preAction in PreActions)
{
- return false;
- }
- return !(result.Value ^ InitialRule.Invert);
- }
-
- private bool CheckCondition(RewriteContext context, Match results, Match previous)
- {
- if (Conditions == null)
- {
- return true;
+ preAction.ApplyAction(context.HttpContext, initMatchRes, condMatchRes);
}
- // TODO Visitor pattern here?
- foreach (var condition in Conditions)
- {
- var concatTestString = condition.TestStringSegments.GetPattern(context.HttpContext, results, previous);
- var match = condition.ConditionExpression.CheckConditionExpression(context, previous, concatTestString);
-
- if (match == null)
- {
- return false;
- }
-
- if (!match.Value && !(condition.Flags.HasFlag(ConditionFlagType.Or)))
- {
- return false;
- }
- }
- return true;
- }
-
- private void ModifyHttpContextFromUri(HttpContext context, string uriString)
- {
- var uri = new Uri(uriString);
- // TODO this is ugly, fix in later push.
- // TODO super bad for perf, cache/locally store these and update httpcontext after all rules are applied.
- var pathBase = PathString.FromUriComponent(uri);
- if (!pathBase.Value.StartsWith(context.Request.PathBase))
- {
- // cannot distinguish between path base and path.
- throw new NotSupportedException("Modified path base from mod_rewrite rule");
- }
- context.Request.Host = HostString.FromUriComponent(uri);
- context.Request.Path = PathString.FromUriComponent(uri);
- context.Request.QueryString = QueryString.FromUriComponent(uri);
- context.Request.Scheme = uri.Scheme;
+ return Action.ApplyAction(context, initMatchRes, condMatchRes);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ParserContext.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ParserContext.cs
index 18b7833889..815f3012d8 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ParserContext.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ParserContext.cs
@@ -8,19 +8,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
///
public class ParserContext
{
- private readonly string _template;
+ public readonly string Template;
public int Index { get; set; }
private int? _mark;
public ParserContext(string condition)
{
- _template = condition;
+ Template = condition;
Index = -1;
}
public char Current
{
- get { return (Index < _template.Length && Index >= 0) ? _template[Index] : (char)0; }
+ get { return (Index < Template.Length && Index >= 0) ? Template[Index] : (char)0; }
}
public bool Back()
@@ -30,12 +30,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
public bool Next()
{
- return ++Index < _template.Length;
+ return ++Index < Template.Length;
}
public bool HasNext()
{
- return (Index + 1) < _template.Length;
+ return (Index + 1) < Template.Length;
}
public void Mark()
@@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
// TODO make this return a range rather than a string.
if (_mark.HasValue)
{
- var value = _template.Substring(_mark.Value, Index - _mark.Value);
+ var value = Template.Substring(_mark.Value, Index - _mark.Value);
_mark = null;
return value;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Pattern.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs
similarity index 61%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Pattern.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs
index a796da468f..2c6367ea67 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Pattern.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/Pattern.cs
@@ -2,10 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
-using System.Text;
-using Microsoft.AspNetCore.Http;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class Pattern
{
@@ -15,16 +13,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
PatternSegments = patternSegments;
}
- public string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
- var strBuilder = new StringBuilder();
-
// TODO consider thread static for string builder - DAVID PERF
foreach (var pattern in PatternSegments)
{
- strBuilder.Append(pattern.Evaluate(context, ruleMatch, condMatch));
+ context.Builder.Append(pattern.Evaluate(context, ruleMatch, condMatch));
}
- return strBuilder.ToString();
+ var retVal = context.Builder.ToString();
+ context.Builder.Clear();
+ return retVal;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs
similarity index 62%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs
index 820d5cdfb4..6ea96b915f 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegment.cs
@@ -2,12 +2,13 @@
// 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.UrlRewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public abstract class PatternSegment
{
// Match from prevRule, Match from prevCond
- public abstract string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch);
+ public abstract string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch);
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/ConditionMatchSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs
similarity index 67%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/ConditionMatchSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs
index 1c65243e16..057164a4cc 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/ConditionMatchSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ConditionMatchSegment.cs
@@ -1,9 +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;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class ConditionMatchSegment : PatternSegment
{
@@ -14,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
Index = index;
}
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return condMatch?.BackReference[Index]?.Value;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs
new file mode 100644
index 0000000000..bb535d5178
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/DateTimeSegment.cs
@@ -0,0 +1,81 @@
+// 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.Globalization;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
+{
+ public class DateTimeSegment : PatternSegment
+ {
+ private DateTimePortion _portion;
+
+ public DateTimeSegment(string segment)
+ {
+ switch(segment)
+ {
+ case "TIME_YEAR":
+ _portion = DateTimePortion.Year;
+ break;
+ case "TIME_MON":
+ _portion = DateTimePortion.Month;
+ break;
+ case "TIME_DAY":
+ _portion = DateTimePortion.Day;
+ break;
+ case "TIME_HOUR":
+ _portion = DateTimePortion.Day;
+ break;
+ case "TIME_MIN":
+ _portion = DateTimePortion.Day;
+ break;
+ case "TIME_SEC":
+ _portion = DateTimePortion.Day;
+ break;
+ case "TIME_WDAY":
+ _portion = DateTimePortion.Day;
+ break;
+ case "TIME":
+ _portion = DateTimePortion.Day;
+ break;
+ default:
+ throw new FormatException("Unsupported segment: " + segment);
+ }
+ }
+
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ switch (_portion) {
+ case DateTimePortion.Year:
+ return DateTimeOffset.UtcNow.Year.ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.Month:
+ return DateTimeOffset.UtcNow.Month.ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.Day:
+ return DateTimeOffset.UtcNow.Day.ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.Hour:
+ return DateTimeOffset.UtcNow.Hour.ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.Minute:
+ return DateTimeOffset.UtcNow.Minute.ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.Second:
+ return DateTimeOffset.UtcNow.Second.ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.DayOfWeek:
+ return ((int)DateTimeOffset.UtcNow.DayOfWeek).ToString(CultureInfo.InvariantCulture);
+ case DateTimePortion.Time:
+ return DateTimeOffset.UtcNow.ToString(CultureInfo.InvariantCulture);
+ default:
+ return string.Empty;
+ }
+ }
+
+ private enum DateTimePortion {
+ Year,
+ Month,
+ Day,
+ Hour,
+ Minute,
+ Second,
+ DayOfWeek,
+ Time
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/HeaderSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs
similarity index 59%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/HeaderSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs
index c83b2cf6ad..116dca22c4 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/HeaderSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/HeaderSegment.cs
@@ -1,9 +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;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class HeaderSegment : PatternSegment
{
@@ -14,9 +12,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
Header = header;
}
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
- return context.Request.Headers[Header];
+ return context.HttpContext.Request.Headers[Header];
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs
new file mode 100644
index 0000000000..c5a72eef55
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class IsHttpsModSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Request.IsHttps ? "on" : "off";
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsSegment.cs
new file mode 100644
index 0000000000..be940dd284
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class IsHttpsSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Request.IsHttps ? "ON" : "OFF";
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs
new file mode 100644
index 0000000000..8131ae59a5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsIPV6Segment.cs
@@ -0,0 +1,20 @@
+// 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.Net.Sockets;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
+{
+
+ public class IsIPV6Segment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ if (context.HttpContext.Connection.RemoteIpAddress == null)
+ {
+ return "off";
+ }
+ return context.HttpContext.Connection.RemoteIpAddress.AddressFamily == AddressFamily.InterNetworkV6 ? "on" : "off";
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/LiteralSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs
similarity index 66%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/LiteralSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs
index 390459c6c8..676a9e3d29 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/LiteralSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LiteralSegment.cs
@@ -1,9 +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;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class LiteralSegment : PatternSegment
{
@@ -14,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
Literal = literal;
}
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return Literal;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs
new file mode 100644
index 0000000000..5fc5ef8c95
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalAddressSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class LocalAddressSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Connection.LocalIpAddress?.ToString();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs
new file mode 100644
index 0000000000..d7f62e2905
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/LocalPortSegment.cs
@@ -0,0 +1,15 @@
+// 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.Globalization;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
+{
+ public class LocalPortSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Connection.LocalPort.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs
new file mode 100644
index 0000000000..86341bc5a8
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/QueryStringSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class QueryStringSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Request.QueryString.ToString();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs
new file mode 100644
index 0000000000..c18ec14ab7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemoteAddressSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class RemoteAddressSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Connection.RemoteIpAddress?.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs
new file mode 100644
index 0000000000..9c0e0257bb
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RemotePortSegment.cs
@@ -0,0 +1,15 @@
+// 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.Globalization;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
+{
+ public class RemotePortSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Connection.RemotePort.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RequestFilenameSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs
similarity index 50%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RequestFilenameSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs
index 8d467684f6..fe74df5c2b 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RequestFilenameSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestFilenameSegment.cs
@@ -1,15 +1,13 @@
// 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;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class RequestFileNameSegment : PatternSegment
{
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
- return context.Request.Path;
+ return context.HttpContext.Request.Path;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs
new file mode 100644
index 0000000000..b10103ebf5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RequestMethodSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class RequestMethodSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Request.Method;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RuleMatchSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs
similarity index 67%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RuleMatchSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs
index b8384aaa49..e54c08000b 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RuleMatchSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/RuleMatchSegment.cs
@@ -1,9 +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;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class RuleMatchSegment : PatternSegment
{
@@ -14,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
Index = index;
}
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return ruleMatch?.BackReference[Index]?.Value;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs
new file mode 100644
index 0000000000..cd2bff5c06
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/SchemeSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class SchemeSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Request.Scheme;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs
new file mode 100644
index 0000000000..d2b1d8e6ec
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ServerProtocolSegment.cs
@@ -0,0 +1,15 @@
+// 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.Features;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
+{
+ public class ServerProtocolSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Features.Get()?.Protocol;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/ToLowerSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs
similarity index 51%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/ToLowerSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs
index b2c9d85679..c61e056331 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/ToLowerSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/ToLowerSegment.cs
@@ -1,9 +1,9 @@
// 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 System.Text;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class ToLowerSegment : PatternSegment
{
@@ -14,9 +14,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
Pattern = pattern;
}
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
+ // 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);
+ context.Builder = tempBuilder;
return pattern.ToLowerInvariant();
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/UrlEncodeSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs
similarity index 63%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/UrlEncodeSegment.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs
index 83edb5019c..f64bb33cc6 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/UrlEncodeSegment.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlEncodeSegment.cs
@@ -1,10 +1,10 @@
// 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.Text;
using System.Text.Encodings.Web;
-using Microsoft.AspNetCore.Http;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
+namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
{
public class UrlEncodeSegment : PatternSegment
{
@@ -15,9 +15,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
Pattern = pattern;
}
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ 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);
+ context.Builder = tempBuilder;
return UrlEncoder.Default.Encode(pattern);
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs
new file mode 100644
index 0000000000..da8adce3f7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/UrlSegment.cs
@@ -0,0 +1,13 @@
+// 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.PatternSegments
+{
+ public class UrlSegment : PatternSegment
+ {
+ public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ return context.HttpContext.Request.Path;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs
new file mode 100644
index 0000000000..08b1014c1c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs
@@ -0,0 +1,11 @@
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal
+{
+ public abstract class PreAction
+ {
+ public abstract void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeCookiePreAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeCookiePreAction.cs
new file mode 100644
index 0000000000..13d2dd3ba9
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeCookiePreAction.cs
@@ -0,0 +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 Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PreActions
+{
+ public class ChangeCookiePreAction : PreAction
+ {
+ public ChangeCookiePreAction(string cookie)
+ {
+ // TODO
+ throw new NotImplementedException(cookie);
+ }
+
+ public override void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ // modify the cookies
+
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeEnvironmentPreAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeEnvironmentPreAction.cs
new file mode 100644
index 0000000000..79e84bde7f
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeEnvironmentPreAction.cs
@@ -0,0 +1,22 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.PreActions
+{
+ public class ChangeEnvironmentPreAction : PreAction
+ {
+ public ChangeEnvironmentPreAction(string env)
+ {
+ // TODO
+ throw new NotImplementedException();
+ }
+
+ public override void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ // Do stuff to modify the env
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs
similarity index 57%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlAction.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs
index 489f4a6b53..f1676c7d79 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlAction.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlAction.cs
@@ -2,12 +2,13 @@
// 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.UrlRewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public abstract class UrlAction
{
public Pattern Url { get; set; }
- public abstract RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch);
+ public abstract RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch);
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs
new file mode 100644
index 0000000000..43589509f3
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ForbiddenAction.cs
@@ -0,0 +1,16 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
+{
+ public class ForbiddenAction : UrlAction
+ {
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
+ return RuleResult.ResponseComplete;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs
new file mode 100644
index 0000000000..d56eb09744
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/GoneAction.cs
@@ -0,0 +1,16 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
+{
+ public class GoneAction : UrlAction
+ {
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
+ {
+ context.HttpContext.Response.StatusCode = StatusCodes.Status410Gone;
+ return RuleResult.ResponseComplete;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RedirectAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs
similarity index 58%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RedirectAction.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs
index a990c346dc..245d1020f1 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RedirectAction.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs
@@ -2,10 +2,9 @@
// 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;
using Microsoft.Net.Http.Headers;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
{
public class RedirectAction : UrlAction
{
@@ -16,11 +15,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
Url = pattern;
}
- public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
- context.Response.StatusCode = StatusCode;
+ context.HttpContext.Response.StatusCode = StatusCode;
// url can either contain the full url or the path and query
// always add to location header.
@@ -28,13 +27,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
var split = pattern.IndexOf('?');
if (split >= 0)
{
- var query = context.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
- // not using the response.redirect here because status codes may be 301, 302, 307, 308
- context.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
+ var query = context.HttpContext.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
+ // not using the HttpContext.Response.redirect here because status codes may be 301, 302, 307, 308
+ context.HttpContext.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
}
else
{
- context.Response.Headers[HeaderNames.Location] = pattern;
+ context.HttpContext.Response.Headers[HeaderNames.Location] = pattern;
}
return RuleResult.ResponseComplete;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RedirectClearQueryAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectClearQueryAction.cs
similarity index 67%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RedirectClearQueryAction.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectClearQueryAction.cs
index a6439811c5..c48bb00202 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RedirectClearQueryAction.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectClearQueryAction.cs
@@ -2,10 +2,9 @@
// 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;
using Microsoft.Net.Http.Headers;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
{
public class RedirectClearQueryAction : UrlAction
{
@@ -16,13 +15,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
Url = pattern;
}
- public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
- context.Response.StatusCode = StatusCode;
+ context.HttpContext.Response.StatusCode = StatusCode;
// we are clearing the query, so just put the pattern in the location header
- context.Response.Headers[HeaderNames.Location] = pattern;
+ context.HttpContext.Response.Headers[HeaderNames.Location] = pattern;
return RuleResult.ResponseComplete;
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RewriteAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs
similarity index 58%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RewriteAction.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs
index 105b023dd8..1574c2310e 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/RewriteAction.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs
@@ -4,7 +4,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
{
public class RewriteAction : UrlAction
{
@@ -19,28 +19,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
ClearQuery = clearQuery;
}
- public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
if (ClearQuery)
{
- context.Request.QueryString = new QueryString();
+ context.HttpContext.Request.QueryString = new QueryString();
}
// TODO PERF, substrings, object creation, etc.
if (pattern.IndexOf("://") >= 0)
{
string scheme = null;
- var host = new HostString();
- var path = new PathString();
- var query = new QueryString();
- var fragment = new FragmentString();
+ HostString host;
+ PathString path;
+ QueryString query;
+ FragmentString fragment;
UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment);
- context.Request.Scheme = scheme;
- context.Request.Host = host;
- context.Request.Path = path;
- context.Request.QueryString = query.Add(context.Request.QueryString);
+ context.HttpContext.Request.Scheme = scheme;
+ context.HttpContext.Request.Host = host;
+ context.HttpContext.Request.Path = path;
+ context.HttpContext.Request.QueryString = query.Add(context.HttpContext.Request.QueryString);
}
else
{
@@ -50,23 +50,23 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
var path = pattern.Substring(0, split);
if (path.StartsWith(ForwardSlash))
{
- context.Request.Path = new PathString(path);
+ context.HttpContext.Request.Path = PathString.FromUriComponent(path);
}
else
{
- context.Request.Path = new PathString(ForwardSlash + path);
+ context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + path);
}
- context.Request.QueryString = context.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
+ context.HttpContext.Request.QueryString = context.HttpContext.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
}
else
{
if (pattern.StartsWith(ForwardSlash))
{
- context.Request.Path = new PathString(pattern);
+ context.HttpContext.Request.Path = PathString.FromUriComponent(pattern);
}
else
{
- context.Request.Path = new PathString(ForwardSlash + pattern);
+ context.HttpContext.Request.Path = PathString.FromUriComponent(ForwardSlash + pattern);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/VoidAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs
similarity index 61%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/VoidAction.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs
index 5098a06ee6..7bf14afb11 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlActions/VoidAction.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/VoidAction.cs
@@ -2,14 +2,13 @@
// 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;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
{
public class VoidAction : UrlAction
{
// Explicitly say that nothing happens
- public override RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
+ public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
{
return RuleResult.Continue;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatch.cs
similarity index 85%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatch.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatch.cs
index 5cd97d9c94..428c24ea75 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatch.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatch.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public abstract class UrlMatch
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/ExactMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs
similarity index 91%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/ExactMatch.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs
index a42fe3e6a1..39b6449524 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/ExactMatch.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/ExactMatch.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class ExactMatch : UrlMatch
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/FileSizeMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/FileSizeMatch.cs
new file mode 100644
index 0000000000..d2d6397408
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/FileSizeMatch.cs
@@ -0,0 +1,19 @@
+// 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.UrlMatches
+{
+ public class FileSizeMatch : UrlMatch
+ {
+ public FileSizeMatch(bool negate)
+ {
+ Negate = negate;
+ }
+
+ public override MatchResults Evaluate(string input, RewriteContext context)
+ {
+ var fileInfo = context.FileProvider.GetFileInfo(input);
+ return fileInfo.Exists && fileInfo.Length > 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/IntegerOperand.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IntegerMatch.cs
similarity index 52%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/IntegerOperand.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IntegerMatch.cs
index 227d1c2bbe..185c8de53e 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/IntegerOperand.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IntegerMatch.cs
@@ -3,22 +3,20 @@
using System;
using System.Globalization;
-using System.Text.RegularExpressions;
-using Microsoft.Extensions.FileProviders;
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
- public class IntegerOperand : Operand
+ public class IntegerMatch : UrlMatch
{
public int Value { get; }
public IntegerOperationType Operation { get; }
- public IntegerOperand(int value, IntegerOperationType operation)
+ public IntegerMatch(int value, IntegerOperationType operation)
{
Value = value;
Operation = operation;
}
- public IntegerOperand(string value, IntegerOperationType operation)
+ public IntegerMatch(string value, IntegerOperationType operation)
{
int compValue;
if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out compValue))
@@ -29,27 +27,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
Operation = operation;
}
- public override bool? CheckOperation(Match previous, string testString, IFileProvider fileProvider)
+ public override MatchResults Evaluate(string input, RewriteContext context)
{
int compValue;
- if (!int.TryParse(testString, NumberStyles.None, CultureInfo.InvariantCulture, out compValue))
+ if (!int.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out compValue))
{
- return false;
+ return MatchResults.EmptyFailure;
}
+
switch (Operation)
{
case IntegerOperationType.Equal:
- return compValue == Value;
+ return compValue == Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.Greater:
- return compValue > Value;
+ return compValue > Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.GreaterEqual:
- return compValue >= Value;
+ return compValue >= Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.Less:
- return compValue < Value;
+ return compValue < Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.LessEqual:
- return compValue <= Value;
+ return compValue <= Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
case IntegerOperationType.NotEqual:
- return compValue != Value;
+ return compValue != Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
default:
return null;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/IntegerOperation.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IntegerOperation.cs
similarity index 82%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/IntegerOperation.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IntegerOperation.cs
index 873846e123..fce53e3844 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/IntegerOperation.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IntegerOperation.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public enum IntegerOperationType
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/IsDirectoryMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IsDirectoryMatch.cs
similarity index 89%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/IsDirectoryMatch.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IsDirectoryMatch.cs
index 9c1068ada0..97d1177714 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/IsDirectoryMatch.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IsDirectoryMatch.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class IsDirectoryMatch : UrlMatch
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/IsFileMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IsFileMatch.cs
similarity index 88%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/IsFileMatch.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IsFileMatch.cs
index ebce79b655..6dc8d9ea0b 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/IsFileMatch.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/IsFileMatch.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class IsFileMatch : UrlMatch
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/RegexMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs
similarity index 91%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/RegexMatch.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs
index a1859a9370..a932d869f6 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlMatches/RegexMatch.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/RegexMatch.cs
@@ -4,7 +4,7 @@
using System;
using System.Text.RegularExpressions;
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public class RegexMatch : UrlMatch
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/StringMatch.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/StringMatch.cs
new file mode 100644
index 0000000000..81332401fe
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/StringMatch.cs
@@ -0,0 +1,36 @@
+// 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.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)
+ {
+ Value = value;
+ Operation = operation;
+ }
+
+ public override MatchResults Evaluate(string input, RewriteContext context)
+ {
+ switch (Operation)
+ {
+ case StringOperationType.Equal:
+ 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;
+ case StringOperationType.GreaterEqual:
+ 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;
+ case StringOperationType.LessEqual:
+ return string.Compare(input, Value, IgnoreCase) <= 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/StringOperation.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/StringOperation.cs
similarity index 81%
rename from src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/StringOperation.cs
rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/StringOperation.cs
index a35a51bf9e..15ff45d5f0 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/Operands/StringOperation.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlMatches/StringOperation.cs
@@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
+namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
{
public enum StringOperationType
{
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Conditions.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Conditions.cs
deleted file mode 100644
index b9ffa5b26c..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/Conditions.cs
+++ /dev/null
@@ -1,27 +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 System.Collections.Generic;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
-{
- public class Conditions
- {
- public List ConditionList { get; set; } = new List();
- public LogicalGrouping MatchType { get; set; } // default is MatchAll
- public bool TrackingAllCaptures { get; set; }
-
- public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch)
- {
- MatchResults prevCond = null;
- var success = true;
- foreach (var condition in ConditionList)
- {
- var res = condition.Evaluate(context, ruleMatch, prevCond);
- success = (MatchType == LogicalGrouping.MatchAll ? (success && res.Success) : (success || res.Success));
- prevCond = res;
- }
- return new MatchResults { Success = success, BackReference = prevCond?.BackReference };
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/InputParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/InputParser.cs
index 08b666634b..a4e33665a3 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/InputParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/InputParser.cs
@@ -3,12 +3,11 @@
using System;
using System.Collections.Generic;
-using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments;
+using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
- ///
- ///
public class InputParser
{
private const char Colon = ':';
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/IsHttpsSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/IsHttpsSegment.cs
deleted file mode 100644
index 5ffece8e11..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/IsHttpsSegment.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
-{
- public class IsHttpsSegment : PatternSegment
- {
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
- {
- return context.Request.IsHttps ? "ON" : "OFF";
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/LocalAddressSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/LocalAddressSegment.cs
deleted file mode 100644
index 10d77aeb02..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/LocalAddressSegment.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
-{
- public class LocalAddressSegment : PatternSegment
- {
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
- {
- return context.Connection.LocalIpAddress?.ToString();
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/QueryStringSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/QueryStringSegment.cs
deleted file mode 100644
index d5081a4e6a..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/QueryStringSegment.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
-{
- public class QueryStringSegment : PatternSegment
- {
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
- {
- return context.Request.QueryString.ToString();
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RemoteAddressSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RemoteAddressSegment.cs
deleted file mode 100644
index 499b5a3e91..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RemoteAddressSegment.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
-{
- public class RemoteAddressSegment : PatternSegment
- {
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
- {
- return context.Connection.RemoteIpAddress?.ToString();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RemotePortSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RemotePortSegment.cs
deleted file mode 100644
index 25456dc102..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/RemotePortSegment.cs
+++ /dev/null
@@ -1,16 +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 System.Globalization;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
-{
- public class RemotePortSegment : PatternSegment
- {
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
- {
- return context.Connection.RemotePort.ToString(CultureInfo.InvariantCulture);
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/UrlSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/UrlSegment.cs
deleted file mode 100644
index dca3bfb5de..0000000000
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/PatternSegments/UrlSegment.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
-{
- public class UrlSegment : PatternSegment
- {
- public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
- {
- return context.Request.Path;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs
index 9fa1a84bcc..aa5845a872 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs
@@ -2,7 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments;
+using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
@@ -15,9 +16,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
// TODO Add all server variables here.
case "ALL_RAW":
- throw new NotImplementedException();
+ throw new NotImplementedException("All-Raw server variable not implemented");
case "APP_POOL_ID":
- throw new NotImplementedException();
+ throw new NotImplementedException("All-Pool-Id server variable not implemented");
case "CONTENT_LENGTH":
return new HeaderSegment(HeaderNames.ContentLength);
case "CONTENT_TYPE":
@@ -47,7 +48,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
case "REMOTE_ADDR":
return new RemoteAddressSegment();
case "REMOTE_HOST":
- throw new NotImplementedException();
+ throw new NotImplementedException("Remote-Host server variable not implemented");
case "REMOTE_PORT":
return new RemotePortSegment();
case "REQUEST_FILENAME":
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteFileParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteFileParser.cs
index 4f9e0bbc22..39703c5e2e 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteFileParser.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteFileParser.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
+using Microsoft.AspNetCore.Rewrite.Internal;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
@@ -111,8 +112,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax);
}
-
-
private static void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
{
if (conditions == null)
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs
index ccee529c0a..2c4b9814ab 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/UrlRewriteRuleBuilder.cs
@@ -5,8 +5,9 @@ using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
-using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
+using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
@@ -20,6 +21,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
private UrlMatch _initialMatch;
private Conditions _conditions;
private UrlAction _action;
+ private bool _matchAny;
public UrlRewriteRule Build()
{
@@ -96,10 +98,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
// TODO make this take two overloads and handle regex vs non regex case.
public void AddUrlCondition(Pattern input, string pattern, PatternSyntax patternSyntax, MatchType matchType, bool ignoreCase, bool negate)
{
+ // If there are no conditions specified,
if (_conditions == null)
{
AddUrlConditions(LogicalGrouping.MatchAll, trackingAllCaptures: false);
}
+
switch (patternSyntax)
{
case PatternSyntax.ECMAScript:
@@ -123,17 +127,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
regex = new Regex(pattern, RegexOptions.Compiled, RegexTimeout);
}
- _conditions.ConditionList.Add(new Condition { Input = input, Match = new RegexMatch(regex, negate) });
+ _conditions.ConditionList.Add(new Condition { Input = input, Match = new RegexMatch(regex, negate), OrNext = _matchAny});
break;
}
case MatchType.IsDirectory:
{
- _conditions.ConditionList.Add(new Condition { Input = input, Match = new IsDirectoryMatch(negate) });
+ _conditions.ConditionList.Add(new Condition { Input = input, Match = new IsDirectoryMatch(negate), OrNext = _matchAny });
break;
}
case MatchType.IsFile:
{
- _conditions.ConditionList.Add(new Condition { Input = input, Match = new IsFileMatch(negate) });
+ _conditions.ConditionList.Add(new Condition { Input = input, Match = new IsFileMatch(negate), OrNext = _matchAny });
break;
}
default:
@@ -149,7 +153,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
throw new FormatException("Match does not have an associated pattern attribute in condition");
}
- _conditions.ConditionList.Add(new Condition { Input = input, Match = new ExactMatch(ignoreCase, pattern, negate) });
+ _conditions.ConditionList.Add(new Condition { Input = input, Match = new ExactMatch(ignoreCase, pattern, negate), OrNext = _matchAny });
break;
default:
throw new FormatException("Unrecognized pattern syntax");
@@ -160,8 +164,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
{
var conditions = new Conditions();
conditions.ConditionList = new List();
- conditions.MatchType = logicalGrouping;
- conditions.TrackingAllCaptures = trackingAllCaptures;
+ _matchAny = logicalGrouping == LogicalGrouping.MatchAny;
_conditions = conditions;
}
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewriteRule.cs
index 4295548204..35ed34065f 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewriteRule.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewriteRule.cs
@@ -1,7 +1,12 @@
// 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.UrlRewrite
+using System;
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+using Microsoft.AspNetCore.Rewrite.Internal;
+
+namespace Microsoft.AspNetCore.Rewrite.Internal
{
public class UrlRewriteRule : Rule
{
@@ -39,7 +44,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
}
// at this point we know the rule passed, evaluate the replacement.
- return Action.ApplyAction(context.HttpContext, initMatchRes, condMatchRes);
+ return Action.ApplyAction(context, initMatchRes, condMatchRes);
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Rewrite/ModRewriteExtensions.cs b/src/Microsoft.AspNetCore.Rewrite/ModRewriteExtensions.cs
index c8af9432cc..77dc019956 100644
--- a/src/Microsoft.AspNetCore.Rewrite/ModRewriteExtensions.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/ModRewriteExtensions.cs
@@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Rewrite
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
using (var stream = File.OpenRead(path))
{
- options.Rules.AddRange(FileParser.Parse(new StreamReader(stream)));
+ options.Rules.AddRange(new FileParser().Parse(new StreamReader(stream)));
};
return options;
}
@@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite
{
throw new ArgumentNullException(nameof(reader));
}
- options.Rules.AddRange(FileParser.Parse(reader));
+ options.Rules.AddRange(new FileParser().Parse(reader));
return options;
}
@@ -79,7 +79,8 @@ namespace Microsoft.AspNetCore.Rewrite
throw new ArgumentNullException(nameof(rule));
}
- var builder = new RuleBuilder(rule);
+ var builder = new RuleBuilder();
+ builder.AddRule(rule);
options.Rules.Add(builder.Build());
return options;
}
diff --git a/src/Microsoft.AspNetCore.Rewrite/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Rewrite/Properties/Resources.Designer.cs
index 3be99059c0..4ab56f4ce3 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/Properties/Resources.Designer.cs
@@ -106,6 +106,38 @@ namespace Microsoft.AspNetCore.Rewrite
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserUnrecognizedParameter"), p0, p1);
}
+ ///
+ /// Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.
+ ///
+ internal static string Error_ModRewriteParseError
+ {
+ get { return GetString("Error_ModRewriteParseError"); }
+ }
+
+ ///
+ /// Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.
+ ///
+ internal static string FormatError_ModRewriteParseError(object p0, object p1)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("Error_ModRewriteParseError"), p0, p1);
+ }
+
+ ///
+ /// Could not parse the mod_rewrite file. Line number '{0}'.
+ ///
+ internal static string Error_ModRewriteGeneralParseError
+ {
+ get { return GetString("Error_ModRewriteGeneralParseError"); }
+ }
+
+ ///
+ /// Could not parse the mod_rewrite file. Line number '{0}'.
+ ///
+ internal static string FormatError_ModRewriteGeneralParseError(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("Error_ModRewriteGeneralParseError"), p0);
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNetCore.Rewrite/Resources.resx b/src/Microsoft.AspNetCore.Rewrite/Resources.resx
index ae034489ee..cb7277289a 100644
--- a/src/Microsoft.AspNetCore.Rewrite/Resources.resx
+++ b/src/Microsoft.AspNetCore.Rewrite/Resources.resx
@@ -135,4 +135,10 @@
Unrecognized parameter type: '{0}', terminated at string index: '{1}'
+
+ Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.
+
+
+ Could not parse the mod_rewrite file. Line number '{0}'.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs
index 12b1da7a48..be7108787d 100644
--- a/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs
+++ b/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs
@@ -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.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
@@ -13,5 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite
{
public HttpContext HttpContext { get; set; }
public IFileProvider FileProvider { get; set; }
+ // PERF: share the same string builder per request
+ internal StringBuilder Builder { get; set; } = new StringBuilder(64);
}
}
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ConditionActionTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ConditionActionTest.cs
index d49d9ef695..d0d7ac7f57 100644
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ConditionActionTest.cs
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ConditionActionTest.cs
@@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
var results = ConditionPatternParser.ParseActionCondition(condition);
- var expected = new ParsedModRewriteExpression { Operation = operation, Type = conditionType, Operand = variable, Invert = false };
+ var expected = new ParsedModRewriteInput { OperationType = operation, ConditionType = conditionType, Operand = variable, Invert = false };
Assert.True(CompareConditions(results, expected));
}
@@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
var condition = @"(.*)";
var results = ConditionPatternParser.ParseActionCondition(condition);
- var expected = new ParsedModRewriteExpression { Type = ConditionType.Regex, Operand = "(.*)", Invert = false };
+ var expected = new ParsedModRewriteInput { ConditionType = ConditionType.Regex, Operand = "(.*)", Invert = false };
Assert.True(CompareConditions(results, expected));
}
@@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
var results = ConditionPatternParser.ParseActionCondition(condition);
- var expected = new ParsedModRewriteExpression { Type = cond, Operation = operation , Invert = false };
+ var expected = new ParsedModRewriteInput { ConditionType = cond, OperationType = operation , Invert = false };
Assert.True(CompareConditions(results, expected));
}
@@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
var results = ConditionPatternParser.ParseActionCondition(condition);
- var expected = new ParsedModRewriteExpression { Type = cond, Operation = operation, Invert = true };
+ var expected = new ParsedModRewriteInput { ConditionType = cond, OperationType = operation, Invert = true };
Assert.True(CompareConditions(results, expected));
}
@@ -79,15 +79,15 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
{
var results = ConditionPatternParser.ParseActionCondition(condition);
- var expected = new ParsedModRewriteExpression { Type = cond, Operation = operation, Invert = false, Operand = variable };
+ var expected = new ParsedModRewriteInput { ConditionType = cond, OperationType = operation, Invert = false, Operand = variable };
Assert.True(CompareConditions(results, expected));
}
// TODO negative tests
- private bool CompareConditions(ParsedModRewriteExpression i1, ParsedModRewriteExpression i2)
+ private bool CompareConditions(ParsedModRewriteInput i1, ParsedModRewriteInput i2)
{
- if (i1.Operation != i2.Operation ||
- i1.Type != i2.Type ||
+ if (i1.OperationType != i2.OperationType ||
+ i1.ConditionType != i2.ConditionType ||
i1.Operand != i2.Operand ||
i1.Invert != i2.Invert)
{
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FlagParserTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FlagParserTest.cs
index 572f376f2f..39c6cedb24 100644
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FlagParserTest.cs
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FlagParserTest.cs
@@ -13,10 +13,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
[Fact]
public void FlagParser_CheckSingleTerm()
{
- var results = FlagParser.ParseRuleFlags("[NC]");
- var dict = new Dictionary();
- dict.Add(RuleFlagType.NoCase, string.Empty);
- var expected = new RuleFlags(dict);
+ var results = FlagParser.Parse("[NC]");
+ var dict = new Dictionary();
+ dict.Add(FlagType.NoCase, string.Empty);
+ var expected = new Flags(dict);
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
}
@@ -24,12 +24,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
[Fact]
public void FlagParser_CheckManyTerms()
{
- var results = FlagParser.ParseRuleFlags("[NC,F,L]");
- var dict = new Dictionary();
- dict.Add(RuleFlagType.NoCase, string.Empty);
- dict.Add(RuleFlagType.Forbidden, string.Empty);
- dict.Add(RuleFlagType.Last, string.Empty);
- var expected = new RuleFlags(dict);
+ var results = FlagParser.Parse("[NC,F,L]");
+ var dict = new Dictionary();
+ dict.Add(FlagType.NoCase, string.Empty);
+ dict.Add(FlagType.Forbidden, string.Empty);
+ dict.Add(FlagType.Last, string.Empty);
+ var expected = new Flags(dict);
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
}
@@ -37,12 +37,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
[Fact]
public void FlagParser_CheckManyTermsWithEquals()
{
- var results = FlagParser.ParseRuleFlags("[NC,F,R=301]");
- var dict = new Dictionary();
- dict.Add(RuleFlagType.NoCase, string.Empty);
- dict.Add(RuleFlagType.Forbidden, string.Empty);
- dict.Add(RuleFlagType.Redirect, "301");
- var expected = new RuleFlags(dict);
+ var results = FlagParser.Parse("[NC,F,R=301]");
+ var dict = new Dictionary();
+ dict.Add(FlagType.NoCase, string.Empty);
+ dict.Add(FlagType.Forbidden, string.Empty);
+ dict.Add(FlagType.Redirect, "301");
+ var expected = new Flags(dict);
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
}
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FormatExceptionTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FormatExceptionTests.cs
new file mode 100644
index 0000000000..73d9f3c4c6
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/FormatExceptionTests.cs
@@ -0,0 +1,38 @@
+// 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 Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
+{
+ public class FormatExceptionTests
+ {
+ [Theory]
+ [InlineData(@"RewriteCond 1 2\", @"Invalid escaper character in string RewriteCond 1 2\")]
+ [InlineData("BadExpression 1 2 3 4", "Could not parse the mod_rewrite file. Message: 'Too many tokens on line'. Line number '1'.")]
+ [InlineData("RewriteCond % 2", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %{ 2", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %{asdf} 2", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %z 2", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond $ 2", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond $z 2", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 !", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 >", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 >=", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 <", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 <=", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 =", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 -", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 -a", "Could not parse the mod_rewrite file. Line number '1'.")]
+ [InlineData("RewriteCond %1 -getemp", "Could not parse the mod_rewrite file. Line number '1'.")]
+ public void ThrowFormatExceptionWithCorrectMessage(string input, string expected)
+ {
+ // Arrange, Act, Assert
+ var ex = Assert.Throws(() => new FileParser().Parse(new StringReader(input)));
+ Assert.Equal(ex.Message, expected);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteConditionBuilderTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteConditionBuilderTest.cs
deleted file mode 100644
index 05f610046a..0000000000
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteConditionBuilderTest.cs
+++ /dev/null
@@ -1,50 +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.Rewrite.Internal.ModRewrite;
-using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
-{
- // This file tests an input of a list of tokens and verifies that the appropriate condition is obtained
- public class ModRewriteConditionBuilderTest
- {
- [Fact]
- public void ConditionBuilder_PassInNoFlagsFlagsEmpty()
- {
- var conditionString = "RewriteCond /$1 /hello";
- var builder = new ConditionBuilder(conditionString);
- var results = builder.Build();
-
- //var expected = new Condition(
- // new Pattern(
- // new List() {
- // new PatternSegment("/", SegmentType.Literal),
- // new PatternSegment("1", SegmentType.RuleParameter)
- // }),
- // new ConditionExpression { Operand = new RegexOperand {Regex = new Regex("/hello") } },
- // new ConditionFlags());
- var expected = (new ConditionBuilder("/$1", "/hello")).Build();
-
- Assert.True(results.Flags.FlagDictionary.Count == 0);
- Assert.True(results.Flags.FlagDictionary.Count == expected.Flags.FlagDictionary.Count);
- Assert.True((results.ConditionExpression.Operand is RegexOperand)
- && (expected.ConditionExpression.Operand is RegexOperand));
- }
-
- [Fact]
- public void ConditionBuilder_PassInFlagsFlagsExist()
- {
- var conditionString = "RewriteCond /$1 /hello [NC]";
- var builder = new ConditionBuilder(conditionString);
- var results = builder.Build();
- var expected = (new ConditionBuilder("/$1", "/hello", "[NC]")).Build();
-
- Assert.True(results.Flags.FlagDictionary.Count == 1);
- Assert.True(results.Flags.FlagDictionary.Count == expected.Flags.FlagDictionary.Count);
- Assert.True((results.ConditionExpression.Operand is RegexOperand)
- && (expected.ConditionExpression.Operand is RegexOperand));
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteFlagTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteFlagTest.cs
deleted file mode 100644
index ddc7329b37..0000000000
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteFlagTest.cs
+++ /dev/null
@@ -1,88 +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 System.Text.RegularExpressions;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
-using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
-using Microsoft.AspNetCore.Rewrite.Internal;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
-{
- public class ModRewriteFlagTest
- {
- // Flag tests
- [Fact]
- public void ModRewriteRule_Check403OnForbiddenFlag()
- {
- var context = new RewriteContext { HttpContext = CreateRequest("/", "/hey/hello") };
- var rule = new ModRewriteRule
- {
- InitialRule = new RuleExpression { Operand = new RegexOperand(new Regex("/hey/(.*)")) , Invert = false },
- Transform = ConditionTestStringParser.ParseConditionTestString("/$1"),
- Flags = FlagParser.ParseRuleFlags("[F]")
- };
- var res = rule.ApplyRule(context);
- Assert.True(res.Result == RuleTerminiation.ResponseComplete);
- Assert.True(context.HttpContext.Response.StatusCode == 403);
- }
-
- [Fact]
- public void ModRewriteRule_Check410OnGoneFlag()
- {
- var context = new RewriteContext { HttpContext = CreateRequest("/", "/hey/hello") };
- var rule = new ModRewriteRule
- {
- InitialRule = new RuleExpression { Operand = new RegexOperand(new Regex("/hey/(.*)")), Invert = false },
- Transform = ConditionTestStringParser.ParseConditionTestString("/$1"),
- Flags = FlagParser.ParseRuleFlags("[G]")
- };
- var res = rule.ApplyRule(context);
- Assert.True(res.Result == RuleTerminiation.ResponseComplete);
- Assert.True(context.HttpContext.Response.StatusCode == 410);
- }
-
- [Fact]
- public void ModRewriteRule_CheckLastFlag()
- {
- var context = new RewriteContext { HttpContext = CreateRequest("/", "/hey/hello") };
- var rule = new ModRewriteRule
- {
- InitialRule = new RuleExpression { Operand = new RegexOperand(new Regex("/hey/(.*)")), Invert = false },
- Transform = ConditionTestStringParser.ParseConditionTestString("/$1"),
- Flags = FlagParser.ParseRuleFlags("[L]")
- };
- var res = rule.ApplyRule(context);
- Assert.True(res.Result == RuleTerminiation.StopRules);
- Assert.True(context.HttpContext.Request.Path.Equals(new PathString("/hello")));
- }
-
-
- [Fact]
- public void ModRewriteRule_CheckRedirectFlag()
- {
- // TODO fix this test.
- var context = new RewriteContext { HttpContext = CreateRequest("/", "/hey/hello") };
- var rule = new ModRewriteRule
- {
- InitialRule = new RuleExpression { Operand = new RegexOperand(new Regex("/hey/(.*)")), Invert = false },
- Transform = ConditionTestStringParser.ParseConditionTestString("/$1"),
- Flags = FlagParser.ParseRuleFlags("[G]")
- };
- var res = rule.ApplyRule(context);
- Assert.True(res.Result == RuleTerminiation.ResponseComplete);
- Assert.True(context.HttpContext.Response.StatusCode == 410);
- }
-
- private HttpContext CreateRequest(string basePath, string requestPath, string requestQuery = "", string hostName = "")
- {
- HttpContext context = new DefaultHttpContext();
- context.Request.PathBase = new PathString(basePath);
- context.Request.Path = new PathString(requestPath);
- context.Request.QueryString = new QueryString(requestQuery);
- context.Request.Host = new HostString(hostName);
- return context;
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs
index 90a5d6dba1..62903412d5 100644
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs
@@ -66,12 +66,6 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
Assert.Equal(response, "/what");
}
- [Theory]
- [InlineData("", true)]
- public void Invoke_StringComparisonTests(string input, bool expected)
- {
-
- }
[Fact]
public async Task Invoke_ShouldIgnoreComments()
@@ -89,8 +83,6 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
Assert.Equal(response, "/hey/hello");
}
-
- // TODO Add tests to check '//' being handled appropriately.
[Fact]
public async Task Invoke_ShouldRewriteHomepage()
@@ -184,10 +176,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
}
[Fact]
- public async Task Invoke_CheckFullUrlWithUFlagOnlyPath()
+ public async Task Invoke_CheckFullUrlWithOnlyPath()
{
var options = new RewriteOptions()
- .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/ [U]"));
+ .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/"));
var builder = new WebHostBuilder()
.Configure(app =>
{
@@ -205,7 +197,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
public async Task Invoke_CheckFullUrlWithUFlag()
{
var options = new RewriteOptions()
- .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/ [U]"));
+ .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/"));
var builder = new WebHostBuilder()
.Configure(app =>
{
@@ -223,7 +215,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
public async Task Invoke_CheckModFileConditions()
{
var options = new RewriteOptions()
- .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/ [U]"));
+ .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/"));
var builder = new WebHostBuilder()
.Configure(app =>
{
@@ -242,7 +234,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
public async Task Invoke_EnsureHttps(string input)
{
var options = new RewriteOptions()
- .ImportFromModRewrite(new StringReader("RewriteCond %{REQUEST_URI} ^foo/ \nRewriteCond %{HTTPS} !on \nRewriteRule ^(.*)$ https://www.example.com$1 [R=301,L,U]"));
+ .ImportFromModRewrite(new StringReader("RewriteCond %{REQUEST_URI} /foo/ \nRewriteCond %{HTTPS} !on \nRewriteRule ^(.*)$ https://www.example.com$1 [R=301,L]"));
var builder = new WebHostBuilder()
.Configure(app =>
{
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs
index 214bc87991..e9599a43d4 100644
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/FileParserTests.cs
@@ -5,9 +5,9 @@ using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Rewrite.Internal;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
+using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
-using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
-using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
using Xunit;
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
@@ -155,9 +155,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
},
Conditions = new Conditions
{
- ConditionList = conditions,
- MatchType = condGrouping,
- TrackingAllCaptures = condTracking
+ ConditionList = conditions
}
};
}
@@ -184,8 +182,6 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
}
else
{
- Assert.Equal(r1.Conditions.MatchType, r2.Conditions.MatchType);
- Assert.Equal(r1.Conditions.TrackingAllCaptures, r2.Conditions.TrackingAllCaptures);
Assert.Equal(r1.Conditions.ConditionList.Count, r2.Conditions.ConditionList.Count);
for (var j = 0; j < r1.Conditions.ConditionList.Count; j++)
{
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/InputParserTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/InputParserTests.cs
index 31f6a2e984..0fa95393c0 100644
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/InputParserTests.cs
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/InputParserTests.cs
@@ -4,6 +4,7 @@
using System;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
using Xunit;
@@ -45,7 +46,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
public void EvaluateBackReferenceRule(string testString, string expected)
{
var middle = InputParser.ParseInputString(testString);
- var result = middle.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
+ var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch(), CreateTestCondMatch());
Assert.Equal(result, expected);
}
@@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
public void EvaluatToLowerRule(string testString, string expected)
{
var middle = InputParser.ParseInputString(testString);
- var result = middle.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
+ var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch(), CreateTestCondMatch());
Assert.Equal(result, expected);
}
@@ -67,7 +68,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
public void EvaluatUriEncodeRule(string testString, string expected)
{
var middle = InputParser.ParseInputString(testString);
- var result = middle.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
+ var result = middle.Evaluate(CreateTestRewriteContext(), CreateTestRuleMatch(), CreateTestCondMatch());
Assert.Equal(result, expected);
}
@@ -88,12 +89,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
Assert.Throws(() => InputParser.ParseInputString(testString));
}
- private HttpContext CreateTestHttpContext()
+ private RewriteContext CreateTestRewriteContext()
{
- HttpContext context = new DefaultHttpContext();
+ var context = new DefaultHttpContext();
// TODO add fields if necessary
- return context;
+ return new RewriteContext { HttpContext = context, FileProvider = null };
}
private MatchResults CreateTestRuleMatch()
diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/ServerVariableTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/ServerVariableTests.cs
index 6f11a3c21f..e0847cda75 100644
--- a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/ServerVariableTests.cs
+++ b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/ServerVariableTests.cs
@@ -3,6 +3,7 @@
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Rewrite.Internal;
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
using Microsoft.Net.Http.Headers;
using Xunit;
@@ -32,7 +33,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
Assert.Equal(expected, lookup);
}
- private HttpContext CreateTestHttpContext()
+ private RewriteContext CreateTestHttpContext()
{
var context = new DefaultHttpContext();
context.Request.Host = new HostString("example.com");
@@ -45,7 +46,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
context.Request.Headers[HeaderNames.Referer] = "referer";
context.Request.Headers[HeaderNames.UserAgent] = "useragent";
context.Request.Headers[HeaderNames.Connection] = "connection";
- return context;
+ return new RewriteContext { HttpContext = context };
}
private MatchResults CreateTestRuleMatch()