Mod_rewrite refactor with cleanup
This commit is contained in:
parent
903949cc74
commit
7a56073835
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Query String Rewrite">
|
||||
<match url="page\.asp$" />
|
||||
<conditions>
|
||||
<add input="{QUERY_STRING}" pattern="p1=(\d+)" />
|
||||
<add input="##{C:1}##_{QUERY_STRING}" pattern="##([^#]+)##_.*p2=(\d+)" />
|
||||
</conditions>
|
||||
<action type="rewrite" url="newpage.aspx?param1={C:1}&param2={C:2}" appendQueryString="false"/>
|
||||
<rule name="Example" stopProcessing="true">
|
||||
<match url="(.*)" />
|
||||
<action type="Rewrite" url="http://example.com/{R:1}" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Condition> ConditionList { get; set; } = new List<Condition>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the ConditionPattern for a mod_rewrite rule.
|
||||
/// </summary>
|
||||
public class ConditionExpression
|
||||
{
|
||||
public Operand Operand { get; set; }
|
||||
public bool Invert { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a condition matches the context.
|
||||
/// </summary>
|
||||
/// <param name="context">The UrlRewriteContext.</param>
|
||||
/// <param name="previous">The previous condition results (for backreferences).</param>
|
||||
/// <param name="testString">The testString created from the <see cref="Pattern"/>.</param>
|
||||
/// <returns>If the testString satisfies the condition</returns>
|
||||
public bool? CheckConditionExpression(RewriteContext context, Match previous, string testString)
|
||||
{
|
||||
return Operand.CheckOperation(previous, testString, context.FileProvider) ^ Invert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<string, ConditionFlagType> _conditionFlagLookup = new Dictionary<string, ConditionFlagType>(StringComparer.OrdinalIgnoreCase) {
|
||||
{ "nc", ConditionFlagType.NoCase},
|
||||
{ "nocase", ConditionFlagType.NoCase },
|
||||
{ "or", ConditionFlagType.Or},
|
||||
{ "ornext", ConditionFlagType.Or },
|
||||
{ "nv", ConditionFlagType.NoVary},
|
||||
{ "novary", ConditionFlagType.NoVary}
|
||||
};
|
||||
|
||||
public IDictionary<ConditionFlagType, string> FlagDictionary { get; }
|
||||
|
||||
public ConditionFlags(IDictionary<ConditionFlagType, string> flags)
|
||||
{
|
||||
FlagDictionary = flags;
|
||||
}
|
||||
|
||||
public ConditionFlags()
|
||||
{
|
||||
FlagDictionary = new Dictionary<ConditionFlagType, string>();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,17 +26,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
|||
/// </summary>
|
||||
/// <param name="condition">The CondPattern portion of a mod_rewrite RewriteCond.</param>
|
||||
/// <returns>A new parsed condition.</returns>
|
||||
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
|
|||
/// <param name="context"></param>
|
||||
/// <param name="invert"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a parsed expression into a mod_rewrite condition.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,23 +4,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class FileParser
|
||||
public class FileParser
|
||||
{
|
||||
public static List<Rule> Parse(TextReader input)
|
||||
public List<Rule> Parse(TextReader input)
|
||||
{
|
||||
string line = null;
|
||||
var rules = new List<Rule>();
|
||||
var conditions = new List<Condition>();
|
||||
// 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<Condition>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,99 +5,38 @@ using System;
|
|||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses the flags
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<string, FlagType> _ruleFlagLookup = new Dictionary<string, FlagType>(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<FlagType, string> FlagDictionary { get; }
|
||||
|
||||
public Flags(IDictionary<FlagType, string> flags)
|
||||
{
|
||||
FlagDictionary = flags;
|
||||
}
|
||||
|
||||
public Flags()
|
||||
{
|
||||
FlagDictionary = new Dictionary<FlagType, string>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a sequence of pattern segments, which on obtaining the context, will create the appropriate
|
||||
/// test string and condition for rules and conditions.
|
||||
/// </summary>
|
||||
public class Pattern
|
||||
{
|
||||
private IReadOnlyList<PatternSegment> Segments { get; }
|
||||
/// <summary>
|
||||
/// Creates a new Pattern
|
||||
/// </summary>
|
||||
/// <param name="segments">List of pattern segments which will be applied.</param>
|
||||
public Pattern(IReadOnlyList<PatternSegment> segments)
|
||||
{
|
||||
Segments = segments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the appropriate test string from the Httpcontext and Segments.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="ruleMatch"></param>
|
||||
/// <param name="prevCondition"></param>
|
||||
/// <returns></returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class PatternSegment
|
||||
{
|
||||
public string Variable { get; } // TODO make this a range s.t. we don't copy the string.
|
||||
public SegmentType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a Pattern segment.
|
||||
/// </summary>
|
||||
/// <param name="variable"></param>
|
||||
/// <param name="type"></param>
|
||||
public PatternSegment(string variable, SegmentType type)
|
||||
{
|
||||
Variable = variable;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Condition> _conditions;
|
||||
private RuleFlags _flags;
|
||||
private Pattern _patterns;
|
||||
private Conditions _conditions;
|
||||
private UrlAction _action;
|
||||
private UrlMatch _match;
|
||||
private List<PreAction> _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<Condition>();
|
||||
}
|
||||
var condBuilder = new ConditionBuilder(condition);
|
||||
_conditions.Add(condBuilder.Build());
|
||||
}
|
||||
|
||||
public void AddCondition(Condition condition)
|
||||
{
|
||||
if (_conditions == null)
|
||||
{
|
||||
_conditions = new List<Condition>();
|
||||
}
|
||||
_conditions.Add(condition);
|
||||
}
|
||||
|
||||
public void AddConditions(List<Condition> conditions)
|
||||
{
|
||||
if (_conditions == null)
|
||||
{
|
||||
_conditions = new List<Condition>();
|
||||
}
|
||||
_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<PreAction>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<string, RuleFlagType> _ruleFlagLookup = new Dictionary<string, RuleFlagType>(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<RuleFlagType, string> FlagDictionary { get; }
|
||||
|
||||
public RuleFlags(IDictionary<RuleFlagType, string> flags)
|
||||
{
|
||||
// TODO use ref to check dictionary equality
|
||||
FlagDictionary = flags;
|
||||
}
|
||||
|
||||
public RuleFlags()
|
||||
{
|
||||
FlagDictionary = new Dictionary<RuleFlagType, string>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
|||
/// </summary>
|
||||
public static class ServerVariables
|
||||
{
|
||||
public static HashSet<string> ValidServerVariables = new HashSet<string>()
|
||||
{
|
||||
"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"
|
||||
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Translates mod_rewrite server variables strings to an enum of different server variables.
|
||||
/// </summary>
|
||||
/// <param name="variable">The server variable string.</param>
|
||||
/// <param name="context">The HttpContext context.</param>
|
||||
/// <param name="context">The Parser context</param>
|
||||
/// <returns>The appropriate enum if the server variable exists, else ServerVariable.None</returns>
|
||||
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<IHttpRequestFeature>()?.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses the TestString segment of the mod_rewrite condition.
|
||||
/// </summary>
|
||||
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</param>
|
||||
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
|
||||
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
|
|||
/// <param name="context">The ParserContext</param>
|
||||
/// <param name="results">The List of results which the new condition parameter will be added.</param>
|
||||
/// <returns>true </returns>
|
||||
private static bool ParseConditionParameter(ParserContext context, List<PatternSegment> results)
|
||||
private static void ParseConditionParameter(ParserContext context, List<PatternSegment> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -159,7 +157,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
|||
/// <param name="context"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <returns></returns>
|
||||
private static bool ParseLiteral(ParserContext context, List<PatternSegment> results)
|
||||
private static void ParseLiteral(ParserContext context, List<PatternSegment> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Condition> Conditions { get; set; } = new List<Condition>();
|
||||
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<PreAction> PreActions { get; set; }
|
||||
|
||||
public ModRewriteRule(List<Condition> conditions, RuleExpression initialRule, Pattern transforms, RuleFlags flags, string description = "")
|
||||
public ModRewriteRule(UrlMatch initialMatch, Conditions conditions, UrlAction urlAction, List<PreAction> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<IHttpRequestFeature>()?.Protocol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -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<Condition> ConditionList { get; set; } = new List<Condition>();
|
||||
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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public class InputParser
|
||||
{
|
||||
private const char Colon = ':';
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Condition>();
|
||||
conditions.MatchType = logicalGrouping;
|
||||
conditions.TrackingAllCaptures = trackingAllCaptures;
|
||||
_matchAny = logicalGrouping == LogicalGrouping.MatchAny;
|
||||
_conditions = conditions;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,38 @@ namespace Microsoft.AspNetCore.Rewrite
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserUnrecognizedParameter"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.
|
||||
/// </summary>
|
||||
internal static string Error_ModRewriteParseError
|
||||
{
|
||||
get { return GetString("Error_ModRewriteParseError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatError_ModRewriteParseError(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_ModRewriteParseError"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the mod_rewrite file. Line number '{0}'.
|
||||
/// </summary>
|
||||
internal static string Error_ModRewriteGeneralParseError
|
||||
{
|
||||
get { return GetString("Error_ModRewriteGeneralParseError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not parse the mod_rewrite file. Line number '{0}'.
|
||||
/// </summary>
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -135,4 +135,10 @@
|
|||
<data name="Error_InputParserUnrecognizedParameter" xml:space="preserve">
|
||||
<value>Unrecognized parameter type: '{0}', terminated at string index: '{1}'</value>
|
||||
</data>
|
||||
<data name="Error_ModRewriteParseError" xml:space="preserve">
|
||||
<value>Could not parse the mod_rewrite file. Message: '{0}'. Line number '{1}'.</value>
|
||||
</data>
|
||||
<data name="Error_ModRewriteGeneralParseError" xml:space="preserve">
|
||||
<value>Could not parse the mod_rewrite file. Line number '{0}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
|||
[Fact]
|
||||
public void FlagParser_CheckSingleTerm()
|
||||
{
|
||||
var results = FlagParser.ParseRuleFlags("[NC]");
|
||||
var dict = new Dictionary<RuleFlagType, string>();
|
||||
dict.Add(RuleFlagType.NoCase, string.Empty);
|
||||
var expected = new RuleFlags(dict);
|
||||
var results = FlagParser.Parse("[NC]");
|
||||
var dict = new Dictionary<FlagType, string>();
|
||||
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<RuleFlagType, string>();
|
||||
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<FlagType, string>();
|
||||
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<RuleFlagType, string>();
|
||||
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<FlagType, string>();
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<FormatException>(() => new FileParser().Parse(new StringReader(input)));
|
||||
Assert.Equal(ex.Message, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue