Mod_rewrite refactor with cleanup
This commit is contained in:
parent
903949cc74
commit
7a56073835
|
|
@ -1,7 +1,6 @@
|
||||||
# Ensure Https
|
# Ensure Https
|
||||||
RewriteCond %{HTTPS} off
|
RewriteCond %{HTTPS} off
|
||||||
# U is a new flag to represent full URL rewrites
|
RewriteRule ^(.*)$ https://www.example.com$1 [L]
|
||||||
RewriteRule ^(.*)$ https://www.example.com$1 [L,U]
|
|
||||||
|
|
||||||
# Rewrite path with additional sub directory
|
# Rewrite path with additional sub directory
|
||||||
RewriteRule ^(.*)$ /foo$1
|
RewriteRule ^(.*)$ /foo$1
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,17 @@ namespace RewriteSample
|
||||||
// AddFunctionalRule(Func);
|
// AddFunctionalRule(Func);
|
||||||
// TODO make this startup do something useful.
|
// TODO make this startup do something useful.
|
||||||
app.UseRewriter(new RewriteOptions()
|
app.UseRewriter(new RewriteOptions()
|
||||||
.ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
|
.ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
|
||||||
.ImportFromModRewrite(hostingEnv, "Rewrite.txt")
|
.ImportFromModRewrite(hostingEnv, "Rewrite.txt")
|
||||||
.RedirectToHttps(StatusCodes.Status307TemporaryRedirect)
|
.RedirectToHttps(StatusCodes.Status307TemporaryRedirect)
|
||||||
.RewriteRule("/foo/(.*)/bar", "{R:1}/bar")
|
.RewriteRule("/foo/(.*)/bar", "{R:1}/bar")
|
||||||
.AddRule(ctx =>
|
.AddRule(ctx =>
|
||||||
{
|
{
|
||||||
ctx.HttpContext.Request.Path = "/index";
|
ctx.HttpContext.Request.Path = "/index";
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.Run(context => context.Response.WriteAsync(context.Request.Path));
|
app.Run(context => context.Response.WriteAsync(context.Request.Path));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
<rewrite>
|
<rewrite>
|
||||||
<rules>
|
<rules>
|
||||||
<rule name="Query String Rewrite">
|
<rule name="Example" stopProcessing="true">
|
||||||
<match url="page\.asp$" />
|
<match url="(.*)" />
|
||||||
<conditions>
|
<action type="Rewrite" url="http://example.com/{R:1}" />
|
||||||
<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>
|
</rule>
|
||||||
</rules>
|
</rules>
|
||||||
</rewrite>
|
</rewrite>
|
||||||
|
|
@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { get; set; }
|
||||||
public override RuleResult ApplyRule(RewriteContext context)
|
public override RuleResult ApplyRule(RewriteContext context)
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO this only does http to https, add more features in the future.
|
|
||||||
if (!context.HttpContext.Request.IsHttps)
|
if (!context.HttpContext.Request.IsHttps)
|
||||||
{
|
{
|
||||||
var host = context.HttpContext.Request.Host;
|
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;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||||
|
|
@ -10,7 +12,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||||
public int? SSLPort { get; set; }
|
public int? SSLPort { get; set; }
|
||||||
public override RuleResult ApplyRule(RewriteContext context)
|
public override RuleResult ApplyRule(RewriteContext context)
|
||||||
{
|
{
|
||||||
// TODO this only does http to https, add more features in the future.
|
|
||||||
if (!context.HttpContext.Request.IsHttps)
|
if (!context.HttpContext.Request.IsHttps)
|
||||||
{
|
{
|
||||||
var host = context.HttpContext.Request.Host;
|
var host = context.HttpContext.Request.Host;
|
||||||
|
|
@ -27,14 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||||
|
|
||||||
context.HttpContext.Request.Scheme = "https";
|
context.HttpContext.Request.Scheme = "https";
|
||||||
context.HttpContext.Request.Host = host;
|
context.HttpContext.Request.Host = host;
|
||||||
if (stopProcessing)
|
return stopProcessing ? RuleResult.StopRules: RuleResult.Continue;
|
||||||
{
|
|
||||||
return RuleResult.StopRules;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return RuleResult.Continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class Condition
|
public class Condition
|
||||||
{
|
{
|
||||||
public Pattern Input { get; set; }
|
public Pattern Input { get; set; }
|
||||||
public UrlMatch Match { get; set; }
|
public UrlMatch Match { get; set; }
|
||||||
|
public bool OrNext { get; set; }
|
||||||
|
|
||||||
public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
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);
|
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;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class MatchResults
|
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 GroupCollection BackReference { get; set; }
|
||||||
public bool Success { 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>
|
/// </summary>
|
||||||
/// <param name="condition">The CondPattern portion of a mod_rewrite RewriteCond.</param>
|
/// <param name="condition">The CondPattern portion of a mod_rewrite RewriteCond.</param>
|
||||||
/// <returns>A new parsed condition.</returns>
|
/// <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;
|
condition = string.Empty;
|
||||||
}
|
}
|
||||||
var context = new ParserContext(condition);
|
var context = new ParserContext(condition);
|
||||||
var results = new ParsedModRewriteExpression();
|
var results = new ParsedModRewriteInput();
|
||||||
if (!context.Next())
|
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
|
// 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;
|
results.Invert = true;
|
||||||
if (!context.Next())
|
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
|
// Control Block for strings. Set the operation and type fields based on the sign
|
||||||
|
// Switch on current character
|
||||||
switch (context.Current)
|
switch (context.Current)
|
||||||
{
|
{
|
||||||
case Greater:
|
case Greater:
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Dangling ">"
|
// Dangling ">"
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
|
||||||
}
|
}
|
||||||
if (context.Current == EqualSign)
|
if (context.Current == EqualSign)
|
||||||
{
|
{
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Dangling ">="
|
// Dangling ">="
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
|
||||||
}
|
}
|
||||||
results.Operation = OperationType.GreaterEqual;
|
results.OperationType = OperationType.GreaterEqual;
|
||||||
results.Type = ConditionType.StringComp;
|
results.ConditionType = ConditionType.StringComp;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
results.Operation = OperationType.Greater;
|
results.OperationType = OperationType.Greater;
|
||||||
results.Type = ConditionType.StringComp;
|
results.ConditionType = ConditionType.StringComp;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Less:
|
case Less:
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Dangling "<"
|
// Dangling "<"
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
|
||||||
}
|
}
|
||||||
if (context.Current == EqualSign)
|
if (context.Current == EqualSign)
|
||||||
{
|
{
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Dangling "<="
|
// Dangling "<="
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
|
||||||
}
|
}
|
||||||
results.Operation = OperationType.LessEqual;
|
results.OperationType = OperationType.LessEqual;
|
||||||
results.Type = ConditionType.StringComp;
|
results.ConditionType = ConditionType.StringComp;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
results.Operation = OperationType.Less;
|
results.OperationType = OperationType.Less;
|
||||||
results.Type = ConditionType.StringComp;
|
results.ConditionType = ConditionType.StringComp;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EqualSign:
|
case EqualSign:
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Dangling "="
|
// Dangling "="
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(condition, context.Index));
|
||||||
}
|
}
|
||||||
results.Operation = OperationType.Equal;
|
results.OperationType = OperationType.Equal;
|
||||||
results.Type = ConditionType.StringComp;
|
results.ConditionType = ConditionType.StringComp;
|
||||||
break;
|
break;
|
||||||
case Dash:
|
case Dash:
|
||||||
results = ParseProperty(context, results.Invert);
|
results = ParseProperty(context, results.Invert);
|
||||||
if (results.Type == ConditionType.PropertyTest)
|
if (results.ConditionType == ConditionType.PropertyTest)
|
||||||
{
|
{
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
context.Next();
|
context.Next();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
results.Type = ConditionType.Regex;
|
results.ConditionType = ConditionType.Regex;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture the rest of the string guarantee validity.
|
// Capture the rest of the string guarantee validity.
|
||||||
results.Operand = (condition.Substring(context.GetIndex()));
|
results.Operand = condition.Substring(context.GetIndex());
|
||||||
if (IsValidActionCondition(results))
|
if (IsValidActionCondition(results))
|
||||||
{
|
{
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
else
|
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="context"></param>
|
||||||
/// <param name="invert"></param>
|
/// <param name="invert"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static ParsedModRewriteExpression ParseProperty(ParserContext context, bool invert)
|
public static ParsedModRewriteInput ParseProperty(ParserContext context, bool invert)
|
||||||
{
|
{
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (context.Current)
|
switch (context.Current)
|
||||||
{
|
{
|
||||||
case 'd':
|
case 'd':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.Directory, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.Directory, operand: null);
|
||||||
case 'f':
|
case 'f':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.RegularFile, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.RegularFile, operand: null);
|
||||||
case 'F':
|
case 'F':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.ExistingFile, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.ExistingFile, operand: null);
|
||||||
case 'h':
|
case 'h':
|
||||||
case 'L':
|
case 'L':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.SymbolicLink, operand: null);
|
||||||
case 's':
|
case 's':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.Size, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.Size, operand: null);
|
||||||
case 'U':
|
case 'U':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.ExistingUrl, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.ExistingUrl, operand: null);
|
||||||
case 'x':
|
case 'x':
|
||||||
return new ParsedModRewriteExpression(invert, ConditionType.PropertyTest, OperationType.Executable, null);
|
return new ParsedModRewriteInput(invert, ConditionType.PropertyTest, OperationType.Executable, operand: null);
|
||||||
case 'e':
|
case 'e':
|
||||||
|
|
||||||
if (!context.Next() || context.Current != 'q')
|
if (!context.Next() || context.Current != 'q')
|
||||||
{
|
{
|
||||||
// Illegal statement.
|
// 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':
|
case 'g':
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
|
|
@ -175,47 +177,49 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
}
|
}
|
||||||
if (context.Current == 't')
|
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')
|
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
|
else
|
||||||
{
|
{
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(context.Error());
|
||||||
}
|
}
|
||||||
case 'l':
|
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())
|
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')
|
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')
|
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
|
else
|
||||||
{
|
{
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
|
||||||
}
|
}
|
||||||
case 'n':
|
case 'n':
|
||||||
if (!context.Next() || context.Current != 'e')
|
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:
|
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
|
// If the type is an integer, verify operand is actually an int
|
||||||
int res;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class FileParser
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class FileParser
|
|
||||||
{
|
{
|
||||||
public static List<Rule> Parse(TextReader input)
|
public List<Rule> Parse(TextReader input)
|
||||||
{
|
{
|
||||||
string line = null;
|
string line = null;
|
||||||
var rules = new List<Rule>();
|
var rules = new List<Rule>();
|
||||||
var conditions = new List<Condition>();
|
var builder = new RuleBuilder();
|
||||||
// TODO consider passing Itokenizer and Ifileparser and provide implementations
|
var lineNum = 0;
|
||||||
while ((line = input.ReadLine()) != null)
|
while ((line = input.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
|
lineNum++;
|
||||||
if (string.IsNullOrEmpty(line))
|
if (string.IsNullOrEmpty(line))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -33,71 +30,65 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
if (tokens.Count > 4)
|
if (tokens.Count > 4)
|
||||||
{
|
{
|
||||||
// This means the line didn't have an appropriate format, throw format exception
|
// 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])
|
switch (tokens[0])
|
||||||
{
|
{
|
||||||
case "RewriteBase":
|
case "RewriteBase":
|
||||||
throw new NotSupportedException();
|
throw new NotImplementedException("RewriteBase is not implemented");
|
||||||
//if (tokens.Count == 2)
|
|
||||||
//{
|
|
||||||
// ModRewriteBase.Base = tokens[1];
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// throw new FormatException("");
|
|
||||||
//}
|
|
||||||
//break;
|
|
||||||
case "RewriteCond":
|
case "RewriteCond":
|
||||||
|
try
|
||||||
{
|
{
|
||||||
ConditionBuilder builder = null;
|
var pattern = TestStringParser.Parse(tokens[1]);
|
||||||
if (tokens.Count == 3)
|
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.AddConditionFromParts(pattern, condActionParsed, flags);
|
||||||
builder = new ConditionBuilder(tokens[1], tokens[2], tokens[3]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
conditions.Add(builder.Build());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
catch (FormatException formatException)
|
||||||
|
{
|
||||||
|
throw new FormatException(Resources.FormatError_ModRewriteGeneralParseError(lineNum), formatException);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "RewriteRule":
|
case "RewriteRule":
|
||||||
|
try
|
||||||
{
|
{
|
||||||
RuleBuilder builder = null;
|
var regex = RuleRegexParser.ParseRuleRegex(tokens[1]);
|
||||||
if (tokens.Count == 3)
|
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.AddMatch(regex, flags);
|
||||||
builder = new RuleBuilder(tokens[1], tokens[2], tokens[3]);
|
builder.AddAction(pattern, flags);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
builder.AddConditions(conditions);
|
|
||||||
rules.Add(builder.Build());
|
rules.Add(builder.Build());
|
||||||
conditions = new List<Condition>();
|
builder = new RuleBuilder();
|
||||||
break;
|
}
|
||||||
|
catch (FormatException formatException)
|
||||||
|
{
|
||||||
|
throw new FormatException(Resources.FormatError_ModRewriteGeneralParseError(lineNum), formatException);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case "RewriteMap":
|
case "RewriteMap":
|
||||||
throw new NotImplementedException("RewriteMaps to be added soon.");
|
throw new NotImplementedException("RewriteMap to be added soon.");
|
||||||
case "RewriteEngine":
|
case "RewriteEngine":
|
||||||
// Explicitly do nothing here, no notion of turning on regex engine.
|
// Explicitly do nothing here, no notion of turning on regex engine.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new FormatException(tokens[0]);
|
throw new FormatException(Resources.FormatError_ModRewriteParseError("Unrecognized keyword: " + tokens[0], lineNum));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,99 +5,38 @@ using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Parses the flags
|
|
||||||
/// </summary>
|
|
||||||
public class FlagParser
|
public class FlagParser
|
||||||
{
|
{
|
||||||
// TODO Refactor Rule and Condition Flags under IFlags
|
public static Flags Parse(string flagString)
|
||||||
public static RuleFlags ParseRuleFlags(string flagString)
|
|
||||||
{
|
|
||||||
var flags = new RuleFlags();
|
|
||||||
ParseRuleFlags(flagString, flags);
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ParseRuleFlags(string flagString, RuleFlags flags)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(flagString))
|
if (string.IsNullOrEmpty(flagString))
|
||||||
{
|
{
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that flags are contained within []
|
// Check that flags are contained within []
|
||||||
if (!flagString.StartsWith("[") || !flagString.EndsWith("]"))
|
if (!flagString.StartsWith("[") || !flagString.EndsWith("]"))
|
||||||
{
|
{
|
||||||
throw new FormatException();
|
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.
|
// 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(',');
|
var tokens = flagString.Substring(1, flagString.Length - 2).Split(',');
|
||||||
// Go through tokens and verify they have meaning.
|
var flags = new Flags();
|
||||||
// Flags can be KVPs, delimited by '='.
|
|
||||||
foreach (string token in tokens)
|
foreach (string token in tokens)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(token))
|
var hasPayload = token.Split('=');
|
||||||
|
if (hasPayload.Length == 2)
|
||||||
{
|
{
|
||||||
continue;
|
flags.SetFlag(hasPayload[0], hasPayload[1]);
|
||||||
}
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
throw new FormatException();
|
flags.SetFlag(hasPayload[0], string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
public enum RuleFlagType
|
public enum FlagType
|
||||||
{
|
{
|
||||||
EscapeBackreference,
|
EscapeBackreference,
|
||||||
Chain,
|
Chain,
|
||||||
|
|
@ -28,8 +28,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
QSLast,
|
QSLast,
|
||||||
Redirect,
|
Redirect,
|
||||||
Skip,
|
Skip,
|
||||||
Type,
|
Type
|
||||||
// Non-modrewrite rule
|
|
||||||
FullUrl
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
public class ParsedModRewriteExpression
|
public class ParsedModRewriteInput
|
||||||
{
|
{
|
||||||
public bool Invert { get; set; }
|
public bool Invert { get; set; }
|
||||||
public ConditionType Type { get; set; }
|
public ConditionType ConditionType { get; set; }
|
||||||
public OperationType Operation { get; set; }
|
public OperationType OperationType { get; set; }
|
||||||
public string Operand { 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;
|
Invert = invert;
|
||||||
Type = type;
|
ConditionType = conditionType;
|
||||||
Operation = operation;
|
OperationType = operationType;
|
||||||
Operand = operand;
|
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;
|
||||||
using System.Collections.Generic;
|
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
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
public class RuleBuilder
|
public class RuleBuilder
|
||||||
{
|
{
|
||||||
private ParsedModRewriteExpression _pce;
|
private Conditions _conditions;
|
||||||
private List<Condition> _conditions;
|
private UrlAction _action;
|
||||||
private RuleFlags _flags;
|
private UrlMatch _match;
|
||||||
private Pattern _patterns;
|
private List<PreAction> _preActions;
|
||||||
|
|
||||||
|
private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
|
||||||
|
|
||||||
public ModRewriteRule Build()
|
public ModRewriteRule Build()
|
||||||
{
|
{
|
||||||
var ruleExpression = ExpressionCreator.CreateRuleExpression(_pce, _flags);
|
if (_action == null || _match == null)
|
||||||
return new ModRewriteRule(_conditions, ruleExpression, _patterns, _flags);
|
{
|
||||||
|
// TODO throw an exception here, find apporpriate exception
|
||||||
|
}
|
||||||
|
return new ModRewriteRule(_match, _conditions, _action, _preActions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RuleBuilder(string initialRule, string transformation) :
|
public void AddRule(string rule)
|
||||||
this(initialRule, transformation, flags: null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public RuleBuilder(string rule)
|
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
var tokens = Tokenizer.Tokenize(rule);
|
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
|
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);
|
if (flags.HasFlag(FlagType.NoCase))
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
_conditions = new List<Condition>();
|
_match = new RegexMatch(new Regex(input.Operand, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout), input.Invert);
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else
|
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 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("!"))
|
if (regex.StartsWith("!"))
|
||||||
{
|
{
|
||||||
return new ParsedModRewriteExpression { Invert = true, Operand = regex.Substring(1) };
|
return new ParsedModRewriteInput { Invert = true, Operand = regex.Substring(1) };
|
||||||
}
|
}
|
||||||
else
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
public enum SegmentType
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
using System.Globalization;
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
using System.Net.Sockets;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
|
|
@ -16,164 +13,112 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ServerVariables
|
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>
|
/// <summary>
|
||||||
/// Translates mod_rewrite server variables strings to an enum of different server variables.
|
/// Translates mod_rewrite server variables strings to an enum of different server variables.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="variable">The server variable string.</param>
|
/// <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>
|
/// <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)
|
switch (variable)
|
||||||
{
|
{
|
||||||
case "HTTP_ACCEPT":
|
case "HTTP_ACCEPT":
|
||||||
return context.Request.Headers[HeaderNames.Accept];
|
return new HeaderSegment(HeaderNames.Accept);
|
||||||
case "HTTP_COOKIE":
|
case "HTTP_COOKIE":
|
||||||
return context.Request.Headers[HeaderNames.Cookie];
|
return new HeaderSegment(HeaderNames.Cookie);
|
||||||
case "HTTP_FORWARDED":
|
|
||||||
return context.Request.Headers["Forwarded"];
|
|
||||||
case "HTTP_HOST":
|
case "HTTP_HOST":
|
||||||
return context.Request.Headers[HeaderNames.Host];
|
return new HeaderSegment(HeaderNames.Host);
|
||||||
case "HTTP_PROXY_CONNECTION":
|
|
||||||
return context.Request.Headers[HeaderNames.ProxyAuthenticate];
|
|
||||||
case "HTTP_REFERER":
|
case "HTTP_REFERER":
|
||||||
return context.Request.Headers[HeaderNames.Referer];
|
return new HeaderSegment(HeaderNames.Referer);
|
||||||
case "HTTP_USER_AGENT":
|
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":
|
case "AUTH_TYPE":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Auth-Type server variable is not supported");
|
||||||
case "CONN_REMOTE_ADDR":
|
case "CONN_REMOTE_ADDR":
|
||||||
return context.Connection.RemoteIpAddress?.ToString();
|
return new RemoteAddressSegment();
|
||||||
case "CONTEXT_PREFIX":
|
case "CONTEXT_PREFIX":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Context-prefix server variable is not supported");
|
||||||
case "CONTEXT_DOCUMENT_ROOT":
|
case "CONTEXT_DOCUMENT_ROOT":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Context-Document-Root server variable is not supported");
|
||||||
case "IPV6":
|
case "IPV6":
|
||||||
return context.Connection.LocalIpAddress.AddressFamily == AddressFamily.InterNetworkV6 ? "on" : "off";
|
return new IsIPV6Segment();
|
||||||
case "PATH_INFO":
|
case "PATH_INFO":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Path-Info server variable is not supported");
|
||||||
case "QUERY_STRING":
|
case "QUERY_STRING":
|
||||||
return context.Request.QueryString.Value;
|
return new QueryStringSegment();
|
||||||
case "REMOTE_ADDR":
|
case "REMOTE_ADDR":
|
||||||
return context.Connection.RemoteIpAddress?.ToString();
|
return new RemoteAddressSegment();
|
||||||
case "REMOTE_HOST":
|
case "REMOTE_HOST":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Remote-Host server variable is not supported");
|
||||||
case "REMOTE_IDENT":
|
case "REMOTE_IDENT":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Remote-Identity server variable is not supported");
|
||||||
case "REMOTE_PORT":
|
case "REMOTE_PORT":
|
||||||
return context.Connection.RemotePort.ToString(CultureInfo.InvariantCulture);
|
return new RemotePortSegment();
|
||||||
case "REMOTE_USER":
|
case "REMOTE_USER":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Remote-User server variable is not supported");
|
||||||
case "REQUEST_METHOD":
|
case "REQUEST_METHOD":
|
||||||
return context.Request.Method;
|
return new RequestMethodSegment();
|
||||||
case "SCRIPT_FILENAME":
|
case "SCRIPT_FILENAME":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Script-Filename server variable is not supported");
|
||||||
case "DOCUMENT_ROOT":
|
case "DOCUMENT_ROOT":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Document-Root server variable is not supported");
|
||||||
case "SCRIPT_GROUP":
|
case "SCRIPT_GROUP":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Script-Group server variable is not supported");
|
||||||
case "SCRIPT_USER":
|
case "SCRIPT_USER":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Script-User server variable is not supported");
|
||||||
case "SERVER_ADDR":
|
case "SERVER_ADDR":
|
||||||
return context.Connection.LocalIpAddress?.ToString();
|
return new LocalAddressSegment();
|
||||||
case "SERVER_ADMIN":
|
case "SERVER_ADMIN":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Server-Admin server variable is not supported");
|
||||||
case "SERVER_NAME":
|
case "SERVER_NAME":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Server-Name server variable is not supported");
|
||||||
case "SERVER_PORT":
|
case "SERVER_PORT":
|
||||||
return context.Connection.LocalPort.ToString(CultureInfo.InvariantCulture);
|
return new LocalPortSegment();
|
||||||
case "SERVER_PROTOCOL":
|
case "SERVER_PROTOCOL":
|
||||||
return context.Features.Get<IHttpRequestFeature>()?.Protocol;
|
return new ServerProtocolSegment();
|
||||||
case "SERVER_SOFTWARE":
|
case "SERVER_SOFTWARE":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Server-Software server variable is not supported");
|
||||||
case "TIME_YEAR":
|
case "TIME_YEAR":
|
||||||
return DateTimeOffset.UtcNow.Year.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME_MON":
|
case "TIME_MON":
|
||||||
return DateTimeOffset.UtcNow.Month.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME_DAY":
|
case "TIME_DAY":
|
||||||
return DateTimeOffset.UtcNow.Day.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME_HOUR":
|
case "TIME_HOUR":
|
||||||
return DateTimeOffset.UtcNow.Hour.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME_MIN":
|
case "TIME_MIN":
|
||||||
return DateTimeOffset.UtcNow.Minute.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME_SEC":
|
case "TIME_SEC":
|
||||||
return DateTimeOffset.UtcNow.Second.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME_WDAY":
|
case "TIME_WDAY":
|
||||||
return ((int) DateTimeOffset.UtcNow.DayOfWeek).ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "TIME":
|
case "TIME":
|
||||||
return DateTimeOffset.UtcNow.ToString(CultureInfo.InvariantCulture);
|
return new DateTimeSegment(variable);
|
||||||
case "API_VERSION":
|
case "API_VERSION":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
case "HTTPS":
|
case "HTTPS":
|
||||||
return context.Request.IsHttps ? "on" : "off";
|
return new IsHttpsModSegment();
|
||||||
case "HTTP2":
|
case "HTTP2":
|
||||||
return context.Request.Scheme == "http2" ? "on" : "off";
|
throw new NotImplementedException("Http2 server variable is not supported");
|
||||||
case "IS_SUBREQ":
|
case "IS_SUBREQ":
|
||||||
// TODO maybe can do this? context.Request.HttpContext ?
|
// TODO maybe can do this? context.Request.HttpContext ?
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Is-Subrequest server variable is not supported");
|
||||||
case "REQUEST_FILENAME":
|
case "REQUEST_FILENAME":
|
||||||
return context.Request.Path.Value.Substring(1);
|
return new RequestFileNameSegment();
|
||||||
case "REQUEST_SCHEME":
|
case "REQUEST_SCHEME":
|
||||||
return context.Request.Scheme;
|
return new SchemeSegment();
|
||||||
case "REQUEST_URI":
|
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 new UrlSegment();
|
||||||
return context.Request.Path.Value.Substring(1);
|
|
||||||
case "THE_REQUEST":
|
case "THE_REQUEST":
|
||||||
// TODO
|
throw new NotImplementedException("The-Request server variable is not supported");
|
||||||
throw new NotImplementedException();
|
|
||||||
default:
|
default:
|
||||||
return null;
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(variable, context.Index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses the TestString segment of the mod_rewrite condition.
|
/// Parses the TestString segment of the mod_rewrite condition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConditionTestStringParser
|
public class TestStringParser
|
||||||
{
|
{
|
||||||
private const char Percent = '%';
|
private const char Percent = '%';
|
||||||
private const char Dollar = '$';
|
private const char Dollar = '$';
|
||||||
|
|
@ -30,7 +31,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
/// %1
|
/// %1
|
||||||
/// $1</param>
|
/// $1</param>
|
||||||
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
|
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
|
||||||
public static Pattern ParseConditionTestString(string testString)
|
/// http://httpd.apache.org/docs/current/mod/mod_rewrite.html
|
||||||
|
public static Pattern Parse(string testString)
|
||||||
{
|
{
|
||||||
if (testString == null)
|
if (testString == null)
|
||||||
{
|
{
|
||||||
|
|
@ -45,12 +47,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
// This is a server parameter, parse for a condition variable
|
// This is a server parameter, parse for a condition variable
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(testString, context.Index));
|
||||||
}
|
|
||||||
if (!ParseConditionParameter(context, results))
|
|
||||||
{
|
|
||||||
throw new FormatException(context.Error());
|
|
||||||
}
|
}
|
||||||
|
ParseConditionParameter(context, results);
|
||||||
}
|
}
|
||||||
else if (context.Current == Dollar)
|
else if (context.Current == Dollar)
|
||||||
{
|
{
|
||||||
|
|
@ -58,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
// and create a new Pattern Segment.
|
// and create a new Pattern Segment.
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
|
||||||
}
|
}
|
||||||
context.Mark();
|
context.Mark();
|
||||||
if (context.Current >= '0' && context.Current <= '9')
|
if (context.Current >= '0' && context.Current <= '9')
|
||||||
|
|
@ -66,21 +65,24 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
context.Next();
|
context.Next();
|
||||||
var ruleVariable = context.Capture();
|
var ruleVariable = context.Capture();
|
||||||
context.Back();
|
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
|
else
|
||||||
{
|
{
|
||||||
throw new FormatException(context.Error());
|
throw new FormatException(Resources.FormatError_InputParserInvalidInteger(testString, context.Index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Parse for literals, which will return on either the end of the test string
|
// Parse for literals, which will return on either the end of the test string
|
||||||
// or when it hits a special character
|
// or when it hits a special character
|
||||||
if (!ParseLiteral(context, results))
|
ParseLiteral(context, results);
|
||||||
{
|
|
||||||
throw new FormatException(context.Error());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Pattern(results);
|
return new Pattern(results);
|
||||||
|
|
@ -95,7 +97,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
/// <param name="context">The ParserContext</param>
|
/// <param name="context">The ParserContext</param>
|
||||||
/// <param name="results">The List of results which the new condition parameter will be added.</param>
|
/// <param name="results">The List of results which the new condition parameter will be added.</param>
|
||||||
/// <returns>true </returns>
|
/// <returns>true </returns>
|
||||||
private static bool ParseConditionParameter(ParserContext context, List<PatternSegment> results)
|
private static void ParseConditionParameter(ParserContext context, List<PatternSegment> results)
|
||||||
{
|
{
|
||||||
// Parse { }
|
// Parse { }
|
||||||
if (context.Current == OpenBrace)
|
if (context.Current == OpenBrace)
|
||||||
|
|
@ -104,34 +106,26 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Dangling {
|
// Dangling {
|
||||||
return false;
|
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||||
}
|
}
|
||||||
context.Mark();
|
context.Mark();
|
||||||
while (context.Current != CloseBrace)
|
while (context.Current != CloseBrace)
|
||||||
{
|
{
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// No closing } for the server variable
|
throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
else if (context.Current == Colon)
|
else if (context.Current == Colon)
|
||||||
{
|
{
|
||||||
// Have a segmented look up Ex: HTTP:xxxx
|
// Have a segmented look up Ex: HTTP:xxxx
|
||||||
// TODO
|
// TODO
|
||||||
|
throw new NotImplementedException("Segmented Lookups no implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to verify server variable captured exists
|
// Need to verify server variable captured exists
|
||||||
var rawServerVariable = context.Capture();
|
var rawServerVariable = context.Capture();
|
||||||
if (IsValidServerVariable(rawServerVariable))
|
results.Add(ServerVariables.FindServerVariable(rawServerVariable, context));
|
||||||
{
|
|
||||||
results.Add(new PatternSegment(rawServerVariable, SegmentType.ServerParameter));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// invalid.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (context.Current >= '0' && context.Current <= '9')
|
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
|
// 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.
|
// capture is exclusive, we need to go one past the end index, capture, and then go back.
|
||||||
context.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
|
else
|
||||||
{
|
{
|
||||||
// illegal escape of a character
|
// illegal escape of a character
|
||||||
return false;
|
throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(context.Template, context.Index));
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -159,7 +157,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <param name="results"></param>
|
/// <param name="results"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static bool ParseLiteral(ParserContext context, List<PatternSegment> results)
|
private static void ParseLiteral(ParserContext context, List<PatternSegment> results)
|
||||||
{
|
{
|
||||||
context.Mark();
|
context.Mark();
|
||||||
string literal;
|
string literal;
|
||||||
|
|
@ -177,29 +175,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// add results
|
||||||
if (IsValidLiteral(context, literal))
|
results.Add(new LiteralSegment(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
|
|
@ -50,7 +49,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
if (!context.Next())
|
if (!context.Next())
|
||||||
{
|
{
|
||||||
// Means that a character was not escaped appropriately Ex: "foo\"
|
// 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)
|
else if (context.Current == Space || context.Current == Tab)
|
||||||
|
|
|
||||||
|
|
@ -1,208 +1,54 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class ModRewriteRule : Rule
|
public class ModRewriteRule : Rule
|
||||||
{
|
{
|
||||||
public List<Condition> Conditions { get; set; } = new List<Condition>();
|
public UrlMatch InitialMatch { get; set; }
|
||||||
public string Description { get; set; } = string.Empty;
|
public Conditions Conditions { get; set; }
|
||||||
public RuleExpression InitialRule { get; set; }
|
public UrlAction Action { get; set; }
|
||||||
public Pattern Transform { get; set; }
|
public List<PreAction> PreActions { get; set; }
|
||||||
public RuleFlags Flags { get; set; } = new RuleFlags();
|
|
||||||
public ModRewriteRule() { }
|
|
||||||
|
|
||||||
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;
|
Conditions = conditions;
|
||||||
InitialRule = initialRule;
|
InitialMatch = initialMatch;
|
||||||
Transform = transforms;
|
Action = urlAction;
|
||||||
Flags = flags;
|
PreActions = preActions;
|
||||||
Description = description;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override RuleResult ApplyRule(RewriteContext context)
|
public override RuleResult ApplyRule(RewriteContext context)
|
||||||
{
|
{
|
||||||
// 1. Figure out which section of the string to match for the initial rule.
|
// 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 (!initMatchRes.Success)
|
||||||
if (CheckMatchResult(results.Success))
|
|
||||||
{
|
{
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Flags.HasFlag(RuleFlagType.EscapeBackreference))
|
MatchResults condMatchRes = null;
|
||||||
|
if (Conditions != null)
|
||||||
{
|
{
|
||||||
// TODO Escape Backreferences here.
|
condMatchRes = Conditions.Evaluate(context, initMatchRes);
|
||||||
}
|
if (!condMatchRes.Success)
|
||||||
|
|
||||||
// 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
|
|
||||||
{
|
{
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckMatchResult(bool? result)
|
// 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
|
||||||
if (result == null)
|
foreach (var preAction in PreActions)
|
||||||
{
|
{
|
||||||
return false;
|
preAction.ApplyAction(context.HttpContext, initMatchRes, condMatchRes);
|
||||||
}
|
|
||||||
return !(result.Value ^ InitialRule.Invert);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckCondition(RewriteContext context, Match results, Match previous)
|
|
||||||
{
|
|
||||||
if (Conditions == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Visitor pattern here?
|
return Action.ApplyAction(context, initMatchRes, condMatchRes);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,19 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ParserContext
|
public class ParserContext
|
||||||
{
|
{
|
||||||
private readonly string _template;
|
public readonly string Template;
|
||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
private int? _mark;
|
private int? _mark;
|
||||||
|
|
||||||
public ParserContext(string condition)
|
public ParserContext(string condition)
|
||||||
{
|
{
|
||||||
_template = condition;
|
Template = condition;
|
||||||
Index = -1;
|
Index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public char Current
|
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()
|
public bool Back()
|
||||||
|
|
@ -30,12 +30,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
|
|
||||||
public bool Next()
|
public bool Next()
|
||||||
{
|
{
|
||||||
return ++Index < _template.Length;
|
return ++Index < Template.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasNext()
|
public bool HasNext()
|
||||||
{
|
{
|
||||||
return (Index + 1) < _template.Length;
|
return (Index + 1) < Template.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Mark()
|
public void Mark()
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
// TODO make this return a range rather than a string.
|
// TODO make this return a range rather than a string.
|
||||||
if (_mark.HasValue)
|
if (_mark.HasValue)
|
||||||
{
|
{
|
||||||
var value = _template.Substring(_mark.Value, Index - _mark.Value);
|
var value = Template.Substring(_mark.Value, Index - _mark.Value);
|
||||||
_mark = null;
|
_mark = null;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class Pattern
|
public class Pattern
|
||||||
{
|
{
|
||||||
|
|
@ -15,16 +13,16 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
PatternSegments = patternSegments;
|
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
|
// TODO consider thread static for string builder - DAVID PERF
|
||||||
foreach (var pattern in PatternSegments)
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public abstract class PatternSegment
|
public abstract class PatternSegment
|
||||||
{
|
{
|
||||||
// Match from prevRule, Match from prevCond
|
// 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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
|
||||||
{
|
{
|
||||||
public class ConditionMatchSegment : PatternSegment
|
public class ConditionMatchSegment : PatternSegment
|
||||||
{
|
{
|
||||||
|
|
@ -14,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
||||||
Index = index;
|
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;
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
|
||||||
{
|
{
|
||||||
public class HeaderSegment : PatternSegment
|
public class HeaderSegment : PatternSegment
|
||||||
{
|
{
|
||||||
|
|
@ -14,9 +12,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
||||||
Header = header;
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
|
||||||
{
|
{
|
||||||
public class LiteralSegment : PatternSegment
|
public class LiteralSegment : PatternSegment
|
||||||
{
|
{
|
||||||
|
|
@ -14,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
||||||
Literal = literal;
|
Literal = literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Evaluate(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
|
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
||||||
{
|
{
|
||||||
return Literal;
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
|
||||||
{
|
{
|
||||||
public class RequestFileNameSegment : PatternSegment
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
|
||||||
{
|
{
|
||||||
public class RuleMatchSegment : PatternSegment
|
public class RuleMatchSegment : PatternSegment
|
||||||
{
|
{
|
||||||
|
|
@ -14,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
||||||
Index = index;
|
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;
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using System.Text;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
{
|
{
|
||||||
public class ToLowerSegment : PatternSegment
|
public class ToLowerSegment : PatternSegment
|
||||||
{
|
{
|
||||||
|
|
@ -14,9 +14,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
||||||
Pattern = pattern;
|
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);
|
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
|
||||||
|
context.Builder = tempBuilder;
|
||||||
return pattern.ToLowerInvariant();
|
return pattern.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
using System.Text.Encodings.Web;
|
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
|
public class UrlEncodeSegment : PatternSegment
|
||||||
{
|
{
|
||||||
|
|
@ -15,9 +15,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.PatternSegments
|
||||||
Pattern = pattern;
|
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);
|
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
|
||||||
|
context.Builder = tempBuilder;
|
||||||
return UrlEncoder.Default.Encode(pattern);
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public abstract class UrlAction
|
public abstract class UrlAction
|
||||||
{
|
{
|
||||||
public Pattern Url { get; set; }
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
{
|
{
|
||||||
public class RedirectAction : UrlAction
|
public class RedirectAction : UrlAction
|
||||||
{
|
{
|
||||||
|
|
@ -16,11 +15,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
||||||
Url = pattern;
|
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);
|
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
|
// url can either contain the full url or the path and query
|
||||||
// always add to location header.
|
// always add to location header.
|
||||||
|
|
@ -28,13 +27,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
||||||
var split = pattern.IndexOf('?');
|
var split = pattern.IndexOf('?');
|
||||||
if (split >= 0)
|
if (split >= 0)
|
||||||
{
|
{
|
||||||
var query = context.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
|
var query = context.HttpContext.Request.QueryString.Add(new QueryString(pattern.Substring(split)));
|
||||||
// not using the response.redirect here because status codes may be 301, 302, 307, 308
|
// not using the HttpContext.Response.redirect here because status codes may be 301, 302, 307, 308
|
||||||
context.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
|
context.HttpContext.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.Response.Headers[HeaderNames.Location] = pattern;
|
context.HttpContext.Response.Headers[HeaderNames.Location] = pattern;
|
||||||
}
|
}
|
||||||
return RuleResult.ResponseComplete;
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
{
|
{
|
||||||
public class RedirectClearQueryAction : UrlAction
|
public class RedirectClearQueryAction : UrlAction
|
||||||
{
|
{
|
||||||
|
|
@ -16,13 +15,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
||||||
Url = pattern;
|
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);
|
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
|
// 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;
|
return RuleResult.ResponseComplete;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
{
|
{
|
||||||
public class RewriteAction : UrlAction
|
public class RewriteAction : UrlAction
|
||||||
{
|
{
|
||||||
|
|
@ -19,28 +19,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
||||||
ClearQuery = clearQuery;
|
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);
|
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
|
||||||
|
|
||||||
if (ClearQuery)
|
if (ClearQuery)
|
||||||
{
|
{
|
||||||
context.Request.QueryString = new QueryString();
|
context.HttpContext.Request.QueryString = new QueryString();
|
||||||
}
|
}
|
||||||
// TODO PERF, substrings, object creation, etc.
|
// TODO PERF, substrings, object creation, etc.
|
||||||
if (pattern.IndexOf("://") >= 0)
|
if (pattern.IndexOf("://") >= 0)
|
||||||
{
|
{
|
||||||
string scheme = null;
|
string scheme = null;
|
||||||
var host = new HostString();
|
HostString host;
|
||||||
var path = new PathString();
|
PathString path;
|
||||||
var query = new QueryString();
|
QueryString query;
|
||||||
var fragment = new FragmentString();
|
FragmentString fragment;
|
||||||
UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment);
|
UriHelper.FromAbsolute(pattern, out scheme, out host, out path, out query, out fragment);
|
||||||
|
|
||||||
context.Request.Scheme = scheme;
|
context.HttpContext.Request.Scheme = scheme;
|
||||||
context.Request.Host = host;
|
context.HttpContext.Request.Host = host;
|
||||||
context.Request.Path = path;
|
context.HttpContext.Request.Path = path;
|
||||||
context.Request.QueryString = query.Add(context.Request.QueryString);
|
context.HttpContext.Request.QueryString = query.Add(context.HttpContext.Request.QueryString);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -50,23 +50,23 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
||||||
var path = pattern.Substring(0, split);
|
var path = pattern.Substring(0, split);
|
||||||
if (path.StartsWith(ForwardSlash))
|
if (path.StartsWith(ForwardSlash))
|
||||||
{
|
{
|
||||||
context.Request.Path = new PathString(path);
|
context.HttpContext.Request.Path = PathString.FromUriComponent(path);
|
||||||
}
|
}
|
||||||
else
|
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
|
else
|
||||||
{
|
{
|
||||||
if (pattern.StartsWith(ForwardSlash))
|
if (pattern.StartsWith(ForwardSlash))
|
||||||
{
|
{
|
||||||
context.Request.Path = new PathString(pattern);
|
context.HttpContext.Request.Path = PathString.FromUriComponent(pattern);
|
||||||
}
|
}
|
||||||
else
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
{
|
{
|
||||||
public class VoidAction : UrlAction
|
public class VoidAction : UrlAction
|
||||||
{
|
{
|
||||||
// Explicitly say that nothing happens
|
// 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;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public abstract class UrlMatch
|
public abstract class UrlMatch
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public class ExactMatch : UrlMatch
|
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;
|
||||||
using System.Globalization;
|
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 int Value { get; }
|
||||||
public IntegerOperationType Operation { get; }
|
public IntegerOperationType Operation { get; }
|
||||||
public IntegerOperand(int value, IntegerOperationType operation)
|
public IntegerMatch(int value, IntegerOperationType operation)
|
||||||
{
|
{
|
||||||
Value = value;
|
Value = value;
|
||||||
Operation = operation;
|
Operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntegerOperand(string value, IntegerOperationType operation)
|
public IntegerMatch(string value, IntegerOperationType operation)
|
||||||
{
|
{
|
||||||
int compValue;
|
int compValue;
|
||||||
if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out compValue))
|
if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out compValue))
|
||||||
|
|
@ -29,27 +27,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
|
||||||
Operation = operation;
|
Operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool? CheckOperation(Match previous, string testString, IFileProvider fileProvider)
|
public override MatchResults Evaluate(string input, RewriteContext context)
|
||||||
{
|
{
|
||||||
int compValue;
|
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)
|
switch (Operation)
|
||||||
{
|
{
|
||||||
case IntegerOperationType.Equal:
|
case IntegerOperationType.Equal:
|
||||||
return compValue == Value;
|
return compValue == Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
case IntegerOperationType.Greater:
|
case IntegerOperationType.Greater:
|
||||||
return compValue > Value;
|
return compValue > Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
case IntegerOperationType.GreaterEqual:
|
case IntegerOperationType.GreaterEqual:
|
||||||
return compValue >= Value;
|
return compValue >= Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
case IntegerOperationType.Less:
|
case IntegerOperationType.Less:
|
||||||
return compValue < Value;
|
return compValue < Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
case IntegerOperationType.LessEqual:
|
case IntegerOperationType.LessEqual:
|
||||||
return compValue <= Value;
|
return compValue <= Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
case IntegerOperationType.NotEqual:
|
case IntegerOperationType.NotEqual:
|
||||||
return compValue != Value;
|
return compValue != Value ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public enum IntegerOperationType
|
public enum IntegerOperationType
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public class IsDirectoryMatch : UrlMatch
|
public class IsDirectoryMatch : UrlMatch
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public class IsFileMatch : UrlMatch
|
public class IsFileMatch : UrlMatch
|
||||||
{
|
{
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public class RegexMatch : UrlMatch
|
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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public enum StringOperationType
|
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;
|
||||||
using System.Collections.Generic;
|
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
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// </summary>
|
|
||||||
public class InputParser
|
public class InputParser
|
||||||
{
|
{
|
||||||
private const char Colon = ':';
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
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;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
|
|
@ -15,9 +16,9 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
// TODO Add all server variables here.
|
// TODO Add all server variables here.
|
||||||
case "ALL_RAW":
|
case "ALL_RAW":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("All-Raw server variable not implemented");
|
||||||
case "APP_POOL_ID":
|
case "APP_POOL_ID":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("All-Pool-Id server variable not implemented");
|
||||||
case "CONTENT_LENGTH":
|
case "CONTENT_LENGTH":
|
||||||
return new HeaderSegment(HeaderNames.ContentLength);
|
return new HeaderSegment(HeaderNames.ContentLength);
|
||||||
case "CONTENT_TYPE":
|
case "CONTENT_TYPE":
|
||||||
|
|
@ -47,7 +48,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
case "REMOTE_ADDR":
|
case "REMOTE_ADDR":
|
||||||
return new RemoteAddressSegment();
|
return new RemoteAddressSegment();
|
||||||
case "REMOTE_HOST":
|
case "REMOTE_HOST":
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException("Remote-Host server variable not implemented");
|
||||||
case "REMOTE_PORT":
|
case "REMOTE_PORT":
|
||||||
return new RemotePortSegment();
|
return new RemotePortSegment();
|
||||||
case "REQUEST_FILENAME":
|
case "REQUEST_FILENAME":
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
|
|
@ -111,8 +112,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax);
|
builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
|
private static void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
|
||||||
{
|
{
|
||||||
if (conditions == null)
|
if (conditions == null)
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
|
|
@ -20,6 +21,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
private UrlMatch _initialMatch;
|
private UrlMatch _initialMatch;
|
||||||
private Conditions _conditions;
|
private Conditions _conditions;
|
||||||
private UrlAction _action;
|
private UrlAction _action;
|
||||||
|
private bool _matchAny;
|
||||||
|
|
||||||
public UrlRewriteRule Build()
|
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.
|
// 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)
|
public void AddUrlCondition(Pattern input, string pattern, PatternSyntax patternSyntax, MatchType matchType, bool ignoreCase, bool negate)
|
||||||
{
|
{
|
||||||
|
// If there are no conditions specified,
|
||||||
if (_conditions == null)
|
if (_conditions == null)
|
||||||
{
|
{
|
||||||
AddUrlConditions(LogicalGrouping.MatchAll, trackingAllCaptures: false);
|
AddUrlConditions(LogicalGrouping.MatchAll, trackingAllCaptures: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (patternSyntax)
|
switch (patternSyntax)
|
||||||
{
|
{
|
||||||
case PatternSyntax.ECMAScript:
|
case PatternSyntax.ECMAScript:
|
||||||
|
|
@ -123,17 +127,17 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
regex = new Regex(pattern, RegexOptions.Compiled, RegexTimeout);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case MatchType.IsDirectory:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case MatchType.IsFile:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -149,7 +153,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
throw new FormatException("Match does not have an associated pattern attribute in condition");
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new FormatException("Unrecognized pattern syntax");
|
throw new FormatException("Unrecognized pattern syntax");
|
||||||
|
|
@ -160,8 +164,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
var conditions = new Conditions();
|
var conditions = new Conditions();
|
||||||
conditions.ConditionList = new List<Condition>();
|
conditions.ConditionList = new List<Condition>();
|
||||||
conditions.MatchType = logicalGrouping;
|
_matchAny = logicalGrouping == LogicalGrouping.MatchAny;
|
||||||
conditions.TrackingAllCaptures = trackingAllCaptures;
|
|
||||||
_conditions = conditions;
|
_conditions = conditions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class UrlRewriteRule : Rule
|
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.
|
// 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);
|
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
|
||||||
using (var stream = File.OpenRead(path))
|
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;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(reader));
|
throw new ArgumentNullException(nameof(reader));
|
||||||
}
|
}
|
||||||
options.Rules.AddRange(FileParser.Parse(reader));
|
options.Rules.AddRange(new FileParser().Parse(reader));
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +79,8 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
throw new ArgumentNullException(nameof(rule));
|
throw new ArgumentNullException(nameof(rule));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new RuleBuilder(rule);
|
var builder = new RuleBuilder();
|
||||||
|
builder.AddRule(rule);
|
||||||
options.Rules.Add(builder.Build());
|
options.Rules.Add(builder.Build());
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,38 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("Error_InputParserUnrecognizedParameter"), p0, p1);
|
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)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -135,4 +135,10 @@
|
||||||
<data name="Error_InputParserUnrecognizedParameter" xml:space="preserve">
|
<data name="Error_InputParserUnrecognizedParameter" xml:space="preserve">
|
||||||
<value>Unrecognized parameter type: '{0}', terminated at string index: '{1}'</value>
|
<value>Unrecognized parameter type: '{0}', terminated at string index: '{1}'</value>
|
||||||
</data>
|
</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>
|
</root>
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
||||||
|
|
@ -13,5 +14,7 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
public HttpContext HttpContext { get; set; }
|
public HttpContext HttpContext { get; set; }
|
||||||
public IFileProvider FileProvider { 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 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));
|
Assert.True(CompareConditions(results, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
var condition = @"(.*)";
|
var condition = @"(.*)";
|
||||||
var results = ConditionPatternParser.ParseActionCondition(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));
|
Assert.True(CompareConditions(results, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
{
|
{
|
||||||
var results = ConditionPatternParser.ParseActionCondition(condition);
|
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));
|
Assert.True(CompareConditions(results, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
{
|
{
|
||||||
var results = ConditionPatternParser.ParseActionCondition(condition);
|
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));
|
Assert.True(CompareConditions(results, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,15 +79,15 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
{
|
{
|
||||||
var results = ConditionPatternParser.ParseActionCondition(condition);
|
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));
|
Assert.True(CompareConditions(results, expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO negative tests
|
// TODO negative tests
|
||||||
private bool CompareConditions(ParsedModRewriteExpression i1, ParsedModRewriteExpression i2)
|
private bool CompareConditions(ParsedModRewriteInput i1, ParsedModRewriteInput i2)
|
||||||
{
|
{
|
||||||
if (i1.Operation != i2.Operation ||
|
if (i1.OperationType != i2.OperationType ||
|
||||||
i1.Type != i2.Type ||
|
i1.ConditionType != i2.ConditionType ||
|
||||||
i1.Operand != i2.Operand ||
|
i1.Operand != i2.Operand ||
|
||||||
i1.Invert != i2.Invert)
|
i1.Invert != i2.Invert)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FlagParser_CheckSingleTerm()
|
public void FlagParser_CheckSingleTerm()
|
||||||
{
|
{
|
||||||
var results = FlagParser.ParseRuleFlags("[NC]");
|
var results = FlagParser.Parse("[NC]");
|
||||||
var dict = new Dictionary<RuleFlagType, string>();
|
var dict = new Dictionary<FlagType, string>();
|
||||||
dict.Add(RuleFlagType.NoCase, string.Empty);
|
dict.Add(FlagType.NoCase, string.Empty);
|
||||||
var expected = new RuleFlags(dict);
|
var expected = new Flags(dict);
|
||||||
|
|
||||||
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
|
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
|
||||||
}
|
}
|
||||||
|
|
@ -24,12 +24,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FlagParser_CheckManyTerms()
|
public void FlagParser_CheckManyTerms()
|
||||||
{
|
{
|
||||||
var results = FlagParser.ParseRuleFlags("[NC,F,L]");
|
var results = FlagParser.Parse("[NC,F,L]");
|
||||||
var dict = new Dictionary<RuleFlagType, string>();
|
var dict = new Dictionary<FlagType, string>();
|
||||||
dict.Add(RuleFlagType.NoCase, string.Empty);
|
dict.Add(FlagType.NoCase, string.Empty);
|
||||||
dict.Add(RuleFlagType.Forbidden, string.Empty);
|
dict.Add(FlagType.Forbidden, string.Empty);
|
||||||
dict.Add(RuleFlagType.Last, string.Empty);
|
dict.Add(FlagType.Last, string.Empty);
|
||||||
var expected = new RuleFlags(dict);
|
var expected = new Flags(dict);
|
||||||
|
|
||||||
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
|
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
|
||||||
}
|
}
|
||||||
|
|
@ -37,12 +37,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FlagParser_CheckManyTermsWithEquals()
|
public void FlagParser_CheckManyTermsWithEquals()
|
||||||
{
|
{
|
||||||
var results = FlagParser.ParseRuleFlags("[NC,F,R=301]");
|
var results = FlagParser.Parse("[NC,F,R=301]");
|
||||||
var dict = new Dictionary<RuleFlagType, string>();
|
var dict = new Dictionary<FlagType, string>();
|
||||||
dict.Add(RuleFlagType.NoCase, string.Empty);
|
dict.Add(FlagType.NoCase, string.Empty);
|
||||||
dict.Add(RuleFlagType.Forbidden, string.Empty);
|
dict.Add(FlagType.Forbidden, string.Empty);
|
||||||
dict.Add(RuleFlagType.Redirect, "301");
|
dict.Add(FlagType.Redirect, "301");
|
||||||
var expected = new RuleFlags(dict);
|
var expected = new Flags(dict);
|
||||||
|
|
||||||
Assert.True(DictionaryContentsEqual(results.FlagDictionary, expected.FlagDictionary));
|
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