IIS UrlRewrite parsing.
This commit is contained in:
parent
4687aad61e
commit
a62e327c23
|
|
@ -13,6 +13,7 @@ EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99B72A07-32D6-434D-B44D-D064E3C13E08}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99B72A07-32D6-434D-B44D-D064E3C13E08}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
global.json = global.json
|
global.json = global.json
|
||||||
|
NuGetPackageVerifier.json = NuGetPackageVerifier.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Buffering", "src\Microsoft.AspNetCore.Buffering\Microsoft.AspNetCore.Buffering.xproj", "{2363D0DD-A3BF-437E-9B64-B33AE132D875}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Buffering", "src\Microsoft.AspNetCore.Buffering\Microsoft.AspNetCore.Buffering.xproj", "{2363D0DD-A3BF-437E-9B64-B33AE132D875}"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
],
|
],
|
||||||
"packages": {
|
"packages": {
|
||||||
"Microsoft.AspNetCore.Buffering": { },
|
"Microsoft.AspNetCore.Buffering": { },
|
||||||
"Microsoft.AspNetCore.HttpOverrides": { }
|
"Microsoft.AspNetCore.HttpOverrides": { },
|
||||||
|
"Microsoft.AspNetCore.Rewrite": { }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Default": { // Rules to run for packages not listed in any other set.
|
"Default": { // Rules to run for packages not listed in any other set.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
# Ensure Https
|
# Ensure Https
|
||||||
RewriteCond %{REQUEST_URI} ^foo/
|
|
||||||
RewriteCond %{HTTPS} off
|
RewriteCond %{HTTPS} off
|
||||||
# U is a new flag to represent full URL rewrites
|
# U is a new flag to represent full URL rewrites
|
||||||
RewriteRule ^(.*)$ https://www.example.com$1 [L,U]
|
RewriteRule ^(.*)$ https://www.example.com$1 [L,U]
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,13 @@ namespace RewriteSample
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment hostingEnv)
|
||||||
{
|
{
|
||||||
app.UseRewriter(new UrlRewriteOptions()
|
app.UseRewriter(new UrlRewriteOptions()
|
||||||
.ImportFromModRewrite("Rewrite.txt"));
|
.ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
|
||||||
|
.ImportFromModRewrite(hostingEnv, "Rewrite.txt"));
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<rewrite>
|
||||||
|
<rules>
|
||||||
|
<rule name="Fail bad requests">
|
||||||
|
<match url=".*"/>
|
||||||
|
<conditions>
|
||||||
|
<add input="{HTTP_HOST}" pattern="localhost" negate="true" />
|
||||||
|
</conditions>
|
||||||
|
<action type="AbortRequest" />
|
||||||
|
</rule>
|
||||||
|
<rule name="Redirect from blog">
|
||||||
|
<match url="^blog/([_0-9a-z-]+)/([0-9]+)" />
|
||||||
|
<action type="Redirect" url="article/{R:2}/{R:1}" redirectType="Found" />
|
||||||
|
</rule>
|
||||||
|
<rule name="Rewrite to article.aspx">
|
||||||
|
<match url="^article/([0-9]+)/([_0-9a-z-]+)" />
|
||||||
|
<action type="Rewrite" url="article.aspx?id={R:1}&title={R:2}" />
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</rewrite>
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Rewrite.ModRewrite;
|
using Microsoft.AspNetCore.Rewrite.ModRewrite;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
|
|
@ -13,17 +14,27 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
/// Imports rules from a mod_rewrite file and adds the rules to current rules.
|
/// Imports rules from a mod_rewrite file and adds the rules to current rules.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The UrlRewrite options.</param>
|
/// <param name="options">The UrlRewrite options.</param>
|
||||||
|
/// <param name="hostingEnv"></param>
|
||||||
/// <param name="filePath">The path to the file containing mod_rewrite rules.</param>
|
/// <param name="filePath">The path to the file containing mod_rewrite rules.</param>
|
||||||
public static UrlRewriteOptions ImportFromModRewrite(this UrlRewriteOptions options, string filePath)
|
public static UrlRewriteOptions ImportFromModRewrite(this UrlRewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
|
||||||
{
|
{
|
||||||
// TODO use IHostingEnvironment as param.
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("UrlRewriteOptions is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostingEnv == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("HostingEnvironment is null");
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(filePath))
|
if (string.IsNullOrEmpty(filePath))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(nameof(filePath));
|
throw new ArgumentException(nameof(filePath));
|
||||||
}
|
}
|
||||||
// TODO IHosting to fix!
|
|
||||||
|
|
||||||
using (var stream = File.OpenRead(filePath))
|
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
|
||||||
|
using (var stream = File.OpenRead(path))
|
||||||
{
|
{
|
||||||
options.Rules.AddRange(FileParser.Parse(new StreamReader(stream)));
|
options.Rules.AddRange(FileParser.Parse(new StreamReader(stream)));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Text.RegularExpressions;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.ModRewrite;
|
using Microsoft.AspNetCore.Rewrite.ModRewrite;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite.ModRewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains a sequence of pattern segments, which on obtaining the context, will create the appropriate
|
/// Contains a sequence of pattern segments, which on obtaining the context, will create the appropriate
|
||||||
|
|
|
||||||
|
|
@ -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.ModRewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Pattern segment contains a portion of the test string/ substitution segment with a type associated.
|
/// A Pattern segment contains a portion of the test string/ substitution segment with a type associated.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Rewrite.Operands
|
||||||
{
|
{
|
||||||
throw new FormatException("Syntax error for integers in comparison.");
|
throw new FormatException("Syntax error for integers in comparison.");
|
||||||
}
|
}
|
||||||
|
Value = compValue;
|
||||||
Operation = operation;
|
Operation = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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.UrlRewrite
|
||||||
|
{
|
||||||
|
public enum ActionType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Rewrite,
|
||||||
|
Redirect,
|
||||||
|
CustomResponse,
|
||||||
|
AbortRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public class Condition
|
||||||
|
{
|
||||||
|
public Pattern Input { get; set; }
|
||||||
|
public Regex MatchPattern { get; set; }
|
||||||
|
public bool Negate { get; set; }
|
||||||
|
public bool IgnoreCase { get; set; } = true;
|
||||||
|
public MatchType MatchType { get; set; } = MatchType.Pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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.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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public class InitialMatch
|
||||||
|
{
|
||||||
|
public Regex Url { get; set; } // TODO must be a non-empty string, throw in check after parsing?
|
||||||
|
public bool IgnoreCase { get; set; } = true;
|
||||||
|
public bool Negate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
public class InputParser
|
||||||
|
{
|
||||||
|
private const char Colon = ':';
|
||||||
|
private const char OpenBrace = '{';
|
||||||
|
private const char CloseBrace = '}';
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a pattern, which is a template to create a new test string to
|
||||||
|
/// compare to the condition. Can contain server variables, back references, etc.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="testString"></param>
|
||||||
|
/// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
|
||||||
|
public static Pattern ParseInputString(string testString)
|
||||||
|
{
|
||||||
|
if (testString == null)
|
||||||
|
{
|
||||||
|
testString = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = new ParserContext(testString);
|
||||||
|
return ParseString(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pattern ParseString(ParserContext context)
|
||||||
|
{
|
||||||
|
var results = new List<PatternSegment>();
|
||||||
|
while (context.Next())
|
||||||
|
{
|
||||||
|
if (context.Current == OpenBrace)
|
||||||
|
{
|
||||||
|
// This is a server parameter, parse for a condition variable
|
||||||
|
if (!context.Next())
|
||||||
|
{
|
||||||
|
throw new FormatException(context.Error());
|
||||||
|
}
|
||||||
|
ParseParameter(context, results);
|
||||||
|
}
|
||||||
|
else if (context.Current == CloseBrace)
|
||||||
|
{
|
||||||
|
// TODO we should be throwing a syntax error if we have uneven close braces
|
||||||
|
// Can fix by keeping track of the number of '{' and '}' with an int, where {
|
||||||
|
// increments and } decrements. Throw if < 0.
|
||||||
|
return new Pattern(results);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parse for literals, which will return on either the end of the test string
|
||||||
|
// or when it hits a special character
|
||||||
|
ParseLiteral(context, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Pattern(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseParameter(ParserContext context, List<PatternSegment> results)
|
||||||
|
{
|
||||||
|
context.Mark();
|
||||||
|
// Four main cases:
|
||||||
|
// 1. {NAME} - Server Variable, create lambda to get the part of the context
|
||||||
|
// 2. {R:1} - Rule parameter
|
||||||
|
// 3. {C:1} - Condition Parameter
|
||||||
|
// 4. {function:xxx} - String function
|
||||||
|
// TODO consider perf here. This is on startup and will only happen one time
|
||||||
|
// (unless we support Reload)
|
||||||
|
string parameter;
|
||||||
|
while (context.Next())
|
||||||
|
{
|
||||||
|
if (context.Current == CloseBrace)
|
||||||
|
{
|
||||||
|
// This is just a server variable, so we do a lookup and verify the server variable exists.
|
||||||
|
parameter = context.Capture();
|
||||||
|
results.Add(ServerVariables.FindServerVariable(parameter));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (context.Current == Colon)
|
||||||
|
{
|
||||||
|
parameter = context.Capture();
|
||||||
|
|
||||||
|
// Only 5 strings to expect here. Case sensitive.
|
||||||
|
switch (parameter)
|
||||||
|
{
|
||||||
|
case "ToLower":
|
||||||
|
{
|
||||||
|
var pattern = ParseString(context);
|
||||||
|
results.Add(new ToLowerSegment(pattern));
|
||||||
|
|
||||||
|
// at this point, we expect our context to be on the ending closing brace,
|
||||||
|
// because the ParseString() call will increment the context until it
|
||||||
|
// has processed the new string.
|
||||||
|
if (context.Current != CloseBrace)
|
||||||
|
{
|
||||||
|
throw new FormatException("Lacking close brace for parameter at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "UrlDecode":
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("UrlDecode is not supported.");
|
||||||
|
}
|
||||||
|
case "UrlEncode":
|
||||||
|
{
|
||||||
|
var pattern = ParseString(context);
|
||||||
|
results.Add(new UrlEncodeSegment(pattern));
|
||||||
|
|
||||||
|
if (context.Current != CloseBrace)
|
||||||
|
{
|
||||||
|
throw new FormatException("Lacking close brace for parameter at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "R":
|
||||||
|
{
|
||||||
|
var index = GetBackReferenceIndex(context);
|
||||||
|
results.Add(new RuleMatchSegment(index));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "C":
|
||||||
|
{
|
||||||
|
var index = GetBackReferenceIndex(context);
|
||||||
|
results.Add(new ConditionMatchSegment(index));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new FormatException("Unrecognized parameter type: " + parameter + ", terminated at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetBackReferenceIndex(ParserContext context)
|
||||||
|
{
|
||||||
|
if (!context.Next())
|
||||||
|
{
|
||||||
|
throw new FormatException("No index avaible for backreference at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Mark();
|
||||||
|
while (context.Current != CloseBrace)
|
||||||
|
{
|
||||||
|
if (!context.Next())
|
||||||
|
{
|
||||||
|
throw new FormatException("Lacking close brace for parameter at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = context.Capture();
|
||||||
|
int index;
|
||||||
|
if (!int.TryParse(res, out index))
|
||||||
|
{
|
||||||
|
throw new FormatException("Syntax error, invalid integer in response parameter at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 9 || index < 0)
|
||||||
|
{
|
||||||
|
throw new FormatException("Invalid integer into backreference " + index + "at index: " + context.GetIndex());
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ParseLiteral(ParserContext context, List<PatternSegment> results)
|
||||||
|
{
|
||||||
|
context.Mark();
|
||||||
|
string literal;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (context.Current == OpenBrace || context.Current == CloseBrace)
|
||||||
|
{
|
||||||
|
literal = context.Capture();
|
||||||
|
context.Back();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.Next())
|
||||||
|
{
|
||||||
|
literal = context.Capture();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.Add(new LiteralSegment(literal));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public enum LogicalGrouping
|
||||||
|
{
|
||||||
|
MatchAll,
|
||||||
|
MatchAny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public enum MatchType
|
||||||
|
{
|
||||||
|
Pattern,
|
||||||
|
IsFile,
|
||||||
|
IsDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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.UrlRewrite
|
||||||
|
{
|
||||||
|
public class Pattern
|
||||||
|
{
|
||||||
|
public IList<PatternSegment> PatternSegments { get; }
|
||||||
|
|
||||||
|
public Pattern(List<PatternSegment> patternSegments)
|
||||||
|
{
|
||||||
|
PatternSegments = patternSegments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
var strBuilder = new StringBuilder();
|
||||||
|
foreach (var pattern in PatternSegments)
|
||||||
|
{
|
||||||
|
strBuilder.Append(pattern.Evaluate(context, ruleMatch, condMatch));
|
||||||
|
}
|
||||||
|
return strBuilder.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;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public abstract class PatternSegment
|
||||||
|
{
|
||||||
|
// Match from prevRule, Match from prevCond
|
||||||
|
public abstract string Evaluate(HttpContext context, Match ruleMatch, Match 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.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class ConditionMatchSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
public ConditionMatchSegment(int index)
|
||||||
|
{
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return condMatch?.Groups[Index]?.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class HeaderSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public string Header { get; set; }
|
||||||
|
|
||||||
|
public HeaderSegment(string header)
|
||||||
|
{
|
||||||
|
Header = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Request.Headers[Header];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class IsHttpsSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Request.IsHttps ? "ON" : "OFF";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class LiteralSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public string Literal { get; set; }
|
||||||
|
|
||||||
|
public LiteralSegment(string literal)
|
||||||
|
{
|
||||||
|
Literal = literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return Literal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class LocalAddressSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Connection.LocalIpAddress?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class QueryStringSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Request.QueryString.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class RemoteAddressSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Connection.RemoteIpAddress?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class RemotePortSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Connection.RemotePort.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class RuleMatchSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
public RuleMatchSegment(int index)
|
||||||
|
{
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return ruleMatch?.Groups[Index]?.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class ToLowerSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public Pattern Pattern { get; set; }
|
||||||
|
|
||||||
|
public ToLowerSegment(Pattern pattern)
|
||||||
|
{
|
||||||
|
Pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
|
||||||
|
return pattern.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// 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.Encodings.Web;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class UrlEncodeSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public Pattern Pattern { get; set; }
|
||||||
|
|
||||||
|
public UrlEncodeSegment(Pattern pattern)
|
||||||
|
{
|
||||||
|
Pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
var pattern = Pattern.Evaluate(context, ruleMatch, condMatch);
|
||||||
|
return UrlEncoder.Default.Encode(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments
|
||||||
|
{
|
||||||
|
public class UrlSegment : PatternSegment
|
||||||
|
{
|
||||||
|
public override string Evaluate(HttpContext context, Match ruleMatch, Match condMatch)
|
||||||
|
{
|
||||||
|
return context.Request.Path.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public enum PatternSyntax
|
||||||
|
{
|
||||||
|
ECMAScript,
|
||||||
|
WildCard,
|
||||||
|
ExactMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.UrlRewrite
|
||||||
|
{
|
||||||
|
public enum RedirectType
|
||||||
|
{
|
||||||
|
Permanent = 301,
|
||||||
|
Found = 302,
|
||||||
|
SeeOther = 303,
|
||||||
|
Temporary = 307
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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.UrlRewrite
|
||||||
|
{
|
||||||
|
public static class RewriteTags
|
||||||
|
{
|
||||||
|
// TODO More strings to be added later once further implementations are added.
|
||||||
|
public const string Rewrite = "rewrite";
|
||||||
|
public const string GlobalRules = "globalRules";
|
||||||
|
public const string Rules = "rules";
|
||||||
|
public const string Rule = "rule";
|
||||||
|
public const string Action = "action";
|
||||||
|
public const string Name = "name";
|
||||||
|
public const string Enabled = "enabled";
|
||||||
|
public const string PatternSyntax = "patternSyntax";
|
||||||
|
public const string StopProcessing = "stopProcessing";
|
||||||
|
public const string Match = "match";
|
||||||
|
public const string Conditions = "conditions";
|
||||||
|
public const string IgnoreCase = "ignoreCase";
|
||||||
|
public const string Negate = "negate";
|
||||||
|
public const string Url = "url";
|
||||||
|
public const string MatchType = "matchType";
|
||||||
|
public const string Add = "add";
|
||||||
|
public const string TrackingAllCaptures = "trackingAllCaptures";
|
||||||
|
public const string MatchPattern = "matchPattern";
|
||||||
|
public const string Input = "input";
|
||||||
|
public const string Pattern = "pattern";
|
||||||
|
public const string Type = "type";
|
||||||
|
public const string AppendQuery = "appendQuery";
|
||||||
|
public const string LogRewrittenUrl = "logRewrittenUrl";
|
||||||
|
public const string RedirectType = "redirectType";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
// 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;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.UrlRewrite.PatternSegments;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public static class ServerVariables
|
||||||
|
{
|
||||||
|
public static PatternSegment FindServerVariable(string serverVariable)
|
||||||
|
{
|
||||||
|
switch(serverVariable)
|
||||||
|
{
|
||||||
|
// TODO Add all server variables here.
|
||||||
|
case "ALL_RAW":
|
||||||
|
throw new NotImplementedException();
|
||||||
|
case "APP_POOL_ID":
|
||||||
|
throw new NotImplementedException();
|
||||||
|
case "CONTENT_LENGTH":
|
||||||
|
return new HeaderSegment(HeaderNames.ContentLength);
|
||||||
|
case "CONTENT_TYPE":
|
||||||
|
return new HeaderSegment(HeaderNames.ContentType);
|
||||||
|
case "HTTP_ACCEPT":
|
||||||
|
return new HeaderSegment(HeaderNames.Accept);
|
||||||
|
case "HTTP_COOKIE":
|
||||||
|
return new HeaderSegment(HeaderNames.Cookie);
|
||||||
|
case "HTTP_HOST":
|
||||||
|
return new HeaderSegment(HeaderNames.Host);
|
||||||
|
case "HTTP_PROXY_CONNECTION":
|
||||||
|
return new HeaderSegment(HeaderNames.ProxyAuthenticate);
|
||||||
|
case "HTTP_REFERER":
|
||||||
|
return new HeaderSegment(HeaderNames.Referer);
|
||||||
|
case "HTTP_USER_AGENT":
|
||||||
|
return new HeaderSegment(HeaderNames.UserAgent);
|
||||||
|
case "HTTP_CONNECTION":
|
||||||
|
return new HeaderSegment(HeaderNames.Connection);
|
||||||
|
case "HTTP_URL":
|
||||||
|
return new UrlSegment();
|
||||||
|
case "HTTPS":
|
||||||
|
return new IsHttpsSegment();
|
||||||
|
case "LOCAL_ADDR":
|
||||||
|
return new LocalAddressSegment();
|
||||||
|
case "QUERY_STRING":
|
||||||
|
return new QueryStringSegment();
|
||||||
|
case "REMOTE_ADDR":
|
||||||
|
return new RemoteAddressSegment();
|
||||||
|
case "REMOTE_HOST":
|
||||||
|
throw new NotImplementedException();
|
||||||
|
case "REMOTE_PORT":
|
||||||
|
return new RemotePortSegment();
|
||||||
|
default:
|
||||||
|
throw new FormatException("Unrecognized server variable.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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.UrlRewrite
|
||||||
|
{
|
||||||
|
public class UrlAction
|
||||||
|
{
|
||||||
|
public ActionType Type { get; set; }
|
||||||
|
public Pattern Url { get; set; }
|
||||||
|
public bool AppendQueryString { get; set; }
|
||||||
|
public bool LogRewrittenUrl { get; set; }
|
||||||
|
public RedirectType RedirectType { get; set; } = RedirectType.Permanent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.UrlRewrite;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
|
{
|
||||||
|
public static class UrlRewriteExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Imports rules from a mod_rewrite file and adds the rules to current rules.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The UrlRewrite options.</param>
|
||||||
|
/// <param name="hostingEnv"></param>
|
||||||
|
/// <param name="filePath">The path to the file containing urlrewrite rules.</param>
|
||||||
|
public static UrlRewriteOptions ImportFromUrlRewrite(this UrlRewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("UrlRewriteOptions is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostingEnv == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("HostingEnvironment is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(filePath))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
|
||||||
|
using (var stream = File.OpenRead(path))
|
||||||
|
{
|
||||||
|
options.Rules.AddRange(UrlRewriteFileParser.Parse(new StreamReader(stream)));
|
||||||
|
};
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,228 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
// TODO rename
|
||||||
|
public static class UrlRewriteFileParser
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
|
||||||
|
public static List<UrlRewriteRule> Parse(TextReader reader)
|
||||||
|
{
|
||||||
|
var temp = XDocument.Load(reader);
|
||||||
|
var xmlRoot = temp.Descendants(RewriteTags.Rewrite);
|
||||||
|
var rules = new List<UrlRewriteRule>();
|
||||||
|
|
||||||
|
if (xmlRoot != null)
|
||||||
|
{
|
||||||
|
// there is a valid rewrite block, go through each rule and process
|
||||||
|
GetGlobalRules(xmlRoot.Descendants(RewriteTags.GlobalRules), rules);
|
||||||
|
GetRules(xmlRoot.Descendants(RewriteTags.Rules), rules);
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetGlobalRules(IEnumerable<XElement> globalRules, List<UrlRewriteRule> result)
|
||||||
|
{
|
||||||
|
foreach (var rule in globalRules.Elements(RewriteTags.Rule) ?? Enumerable.Empty<XElement>())
|
||||||
|
{
|
||||||
|
var res = new UrlRewriteRule();
|
||||||
|
SetRuleAttributes(rule, res);
|
||||||
|
// TODO handle full url with global rules - may or may not support
|
||||||
|
res.Action = CreateUrlAction(rule.Element(RewriteTags.Action));
|
||||||
|
result.Add(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetRules(IEnumerable<XElement> rules, List<UrlRewriteRule> result)
|
||||||
|
{
|
||||||
|
// TODO Better null check?
|
||||||
|
foreach (var rule in rules.Elements(RewriteTags.Rule) ?? Enumerable.Empty<XElement>())
|
||||||
|
{
|
||||||
|
var res = new UrlRewriteRule();
|
||||||
|
SetRuleAttributes(rule, res);
|
||||||
|
res.Action = CreateUrlAction(rule.Element(RewriteTags.Action));
|
||||||
|
result.Add(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetRuleAttributes(XElement rule, UrlRewriteRule res)
|
||||||
|
{
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Name = rule.Attribute(RewriteTags.Name)?.Value;
|
||||||
|
|
||||||
|
bool enabled;
|
||||||
|
if (bool.TryParse(rule.Attribute(RewriteTags.Enabled)?.Value, out enabled))
|
||||||
|
{
|
||||||
|
res.Enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternSyntax patternSyntax;
|
||||||
|
if (Enum.TryParse(rule.Attribute(RewriteTags.PatternSyntax)?.Value, out patternSyntax))
|
||||||
|
{
|
||||||
|
res.PatternSyntax = patternSyntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stopProcessing;
|
||||||
|
if (bool.TryParse(rule.Attribute(RewriteTags.StopProcessing)?.Value, out stopProcessing))
|
||||||
|
{
|
||||||
|
res.StopProcessing = stopProcessing;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Match = CreateMatch(rule.Element(RewriteTags.Match));
|
||||||
|
res.Conditions = CreateConditions(rule.Element(RewriteTags.Conditions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InitialMatch CreateMatch(XElement match)
|
||||||
|
{
|
||||||
|
if (match == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchRes = new InitialMatch();
|
||||||
|
|
||||||
|
bool parBool;
|
||||||
|
if (bool.TryParse(match.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
|
||||||
|
{
|
||||||
|
matchRes.IgnoreCase = parBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bool.TryParse(match.Attribute(RewriteTags.Negate)?.Value, out parBool))
|
||||||
|
{
|
||||||
|
matchRes.Negate = parBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedInputString = match.Attribute(RewriteTags.Url)?.Value;
|
||||||
|
|
||||||
|
if (matchRes.IgnoreCase)
|
||||||
|
{
|
||||||
|
matchRes.Url = new Regex(parsedInputString, RegexOptions.IgnoreCase | RegexOptions.Compiled, RegexTimeout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matchRes.Url = new Regex(parsedInputString, RegexOptions.Compiled, RegexTimeout);
|
||||||
|
}
|
||||||
|
return matchRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Conditions CreateConditions(XElement conditions)
|
||||||
|
{
|
||||||
|
var condRes = new Conditions();
|
||||||
|
if (conditions == null)
|
||||||
|
{
|
||||||
|
return condRes; // TODO make sure no null exception on Conditions
|
||||||
|
}
|
||||||
|
|
||||||
|
LogicalGrouping grouping;
|
||||||
|
if (Enum.TryParse(conditions.Attribute(RewriteTags.MatchType)?.Value, out grouping))
|
||||||
|
{
|
||||||
|
condRes.MatchType = grouping;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parBool;
|
||||||
|
if (bool.TryParse(conditions.Attribute(RewriteTags.TrackingAllCaptures)?.Value, out parBool))
|
||||||
|
{
|
||||||
|
condRes.TrackingAllCaptures = parBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var cond in conditions.Elements(RewriteTags.Add))
|
||||||
|
{
|
||||||
|
condRes.ConditionList.Add(CreateCondition(cond));
|
||||||
|
}
|
||||||
|
return condRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Condition CreateCondition(XElement condition)
|
||||||
|
{
|
||||||
|
if (condition == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var condRes = new Condition();
|
||||||
|
|
||||||
|
bool parBool;
|
||||||
|
if (bool.TryParse(condition.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
|
||||||
|
{
|
||||||
|
condRes.IgnoreCase = parBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bool.TryParse(condition.Attribute(RewriteTags.Negate)?.Value, out parBool))
|
||||||
|
{
|
||||||
|
condRes.Negate = parBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchType matchType;
|
||||||
|
if (Enum.TryParse(condition.Attribute(RewriteTags.MatchPattern)?.Value, out matchType))
|
||||||
|
{
|
||||||
|
condRes.MatchType = matchType;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedInputString = condition.Attribute(RewriteTags.Input)?.Value;
|
||||||
|
if (parsedInputString != null)
|
||||||
|
{
|
||||||
|
condRes.Input = InputParser.ParseInputString(parsedInputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedInputString = condition.Attribute(RewriteTags.Pattern)?.Value;
|
||||||
|
|
||||||
|
if (condRes.IgnoreCase)
|
||||||
|
{
|
||||||
|
condRes.MatchPattern = new Regex(parsedInputString, RegexOptions.IgnoreCase | RegexOptions.Compiled, RegexTimeout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
condRes.MatchPattern = new Regex(parsedInputString, RegexOptions.Compiled, RegexTimeout);
|
||||||
|
}
|
||||||
|
return condRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UrlAction CreateUrlAction(XElement urlAction)
|
||||||
|
{
|
||||||
|
if (urlAction == null)
|
||||||
|
{
|
||||||
|
throw new FormatException("Action is a required element of a rule.");
|
||||||
|
}
|
||||||
|
var actionRes = new UrlAction();
|
||||||
|
|
||||||
|
ActionType actionType;
|
||||||
|
if (Enum.TryParse(urlAction.Attribute(RewriteTags.Type)?.Value, out actionType))
|
||||||
|
{
|
||||||
|
actionRes.Type = actionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseBool;
|
||||||
|
if (bool.TryParse(urlAction.Attribute(RewriteTags.AppendQuery)?.Value, out parseBool))
|
||||||
|
{
|
||||||
|
actionRes.AppendQueryString = parseBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bool.TryParse(urlAction.Attribute(RewriteTags.LogRewrittenUrl)?.Value, out parseBool))
|
||||||
|
{
|
||||||
|
actionRes.LogRewrittenUrl = parseBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
RedirectType redirectType;
|
||||||
|
if (Enum.TryParse(urlAction.Attribute(RewriteTags.RedirectType)?.Value, out redirectType))
|
||||||
|
{
|
||||||
|
actionRes.RedirectType = redirectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionRes.Url = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
|
||||||
|
return actionRes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// 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.Rewrite.RuleAbstraction;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.UrlRewrite
|
||||||
|
{
|
||||||
|
public class UrlRewriteRule : Rule
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
public PatternSyntax PatternSyntax { get; set; }
|
||||||
|
public bool StopProcessing { get; set; }
|
||||||
|
public InitialMatch Match { get; set; }
|
||||||
|
public Conditions Conditions { get; set; }
|
||||||
|
public UrlAction Action { get; set; }
|
||||||
|
|
||||||
|
public override RuleResult ApplyRule(UrlRewriteContext context)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"net451": {
|
"net451": {
|
||||||
"frameworkAssemblies": {
|
"frameworkAssemblies": {
|
||||||
|
"System.Xml": "",
|
||||||
"System.Xml.Linq": ""
|
"System.Xml.Linq": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.UrlRewrite;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
{
|
||||||
|
public class FileParserTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void RuleParse_ParseTypicalRule()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var xml = @"<rewrite>
|
||||||
|
<rules>
|
||||||
|
<rule name=""Rewrite to article.aspx"">
|
||||||
|
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||||
|
<action type=""Rewrite"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</rewrite>";
|
||||||
|
|
||||||
|
var expected = new List<UrlRewriteRule>();
|
||||||
|
expected.Add(CreateTestRule(new List<Condition>(),
|
||||||
|
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
|
name: "Rewrite to article.aspx",
|
||||||
|
actionType: ActionType.Rewrite,
|
||||||
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
|
||||||
|
// act
|
||||||
|
var res = UrlRewriteFileParser.Parse(new StringReader(xml));
|
||||||
|
|
||||||
|
// assert
|
||||||
|
AssertUrlRewriteRuleEquality(res, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RuleParse_ParseSingleRuleWithSingleCondition()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var xml = @"<rewrite>
|
||||||
|
<rules>
|
||||||
|
<rule name=""Rewrite to article.aspx"">
|
||||||
|
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||||
|
<conditions>
|
||||||
|
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||||
|
</conditions>
|
||||||
|
<action type=""Rewrite"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</rewrite>";
|
||||||
|
|
||||||
|
var condList = new List<Condition>();
|
||||||
|
condList.Add(new Condition
|
||||||
|
{
|
||||||
|
Input = InputParser.ParseInputString("{HTTPS}"),
|
||||||
|
MatchPattern = new Regex("^OFF$")
|
||||||
|
});
|
||||||
|
|
||||||
|
var expected = new List<UrlRewriteRule>();
|
||||||
|
expected.Add(CreateTestRule(condList,
|
||||||
|
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
|
name: "Rewrite to article.aspx",
|
||||||
|
actionType: ActionType.Rewrite,
|
||||||
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
|
||||||
|
// act
|
||||||
|
var res = UrlRewriteFileParser.Parse(new StringReader(xml));
|
||||||
|
|
||||||
|
// assert
|
||||||
|
AssertUrlRewriteRuleEquality(expected, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RuleParse_ParseMultipleRules()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var xml = @"<rewrite>
|
||||||
|
<rules>
|
||||||
|
<rule name=""Rewrite to article.aspx"">
|
||||||
|
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||||
|
<conditions>
|
||||||
|
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||||
|
</conditions>
|
||||||
|
<action type=""Rewrite"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||||
|
</rule>
|
||||||
|
<rule name=""Rewrite to article.aspx"">
|
||||||
|
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||||
|
<conditions>
|
||||||
|
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||||
|
</conditions>
|
||||||
|
<action type=""Redirect"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</rewrite>";
|
||||||
|
|
||||||
|
var condList = new List<Condition>();
|
||||||
|
condList.Add(new Condition
|
||||||
|
{
|
||||||
|
Input = InputParser.ParseInputString("{HTTPS}"),
|
||||||
|
MatchPattern = new Regex("^OFF$")
|
||||||
|
});
|
||||||
|
|
||||||
|
var expected = new List<UrlRewriteRule>();
|
||||||
|
expected.Add(CreateTestRule(condList,
|
||||||
|
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
|
name: "Rewrite to article.aspx",
|
||||||
|
actionType: ActionType.Rewrite,
|
||||||
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
expected.Add(CreateTestRule(condList,
|
||||||
|
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
|
name: "Rewrite to article.aspx",
|
||||||
|
actionType: ActionType.Redirect,
|
||||||
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
|
||||||
|
// act
|
||||||
|
var res = UrlRewriteFileParser.Parse(new StringReader(xml));
|
||||||
|
|
||||||
|
// assert
|
||||||
|
AssertUrlRewriteRuleEquality(expected, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a rule with appropriate default values of the url rewrite rule.
|
||||||
|
private UrlRewriteRule CreateTestRule(List<Condition> conditions,
|
||||||
|
LogicalGrouping condGrouping = LogicalGrouping.MatchAll,
|
||||||
|
bool condTracking = false,
|
||||||
|
string name = "",
|
||||||
|
bool enabled = true,
|
||||||
|
PatternSyntax patternSyntax = PatternSyntax.ECMAScript,
|
||||||
|
bool stopProcessing = false,
|
||||||
|
string Url = "",
|
||||||
|
bool ignoreCase = true,
|
||||||
|
bool negate = false,
|
||||||
|
ActionType actionType = ActionType.None,
|
||||||
|
string pattern = "",
|
||||||
|
bool appendQueryString = false,
|
||||||
|
bool rewrittenUrl = false,
|
||||||
|
RedirectType redirectType = RedirectType.Permanent
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return new UrlRewriteRule
|
||||||
|
{
|
||||||
|
Action = new UrlAction
|
||||||
|
{
|
||||||
|
Url = InputParser.ParseInputString(pattern),
|
||||||
|
Type = actionType,
|
||||||
|
AppendQueryString = appendQueryString,
|
||||||
|
LogRewrittenUrl = rewrittenUrl,
|
||||||
|
RedirectType = redirectType
|
||||||
|
},
|
||||||
|
Name = name,
|
||||||
|
Enabled = enabled,
|
||||||
|
StopProcessing = stopProcessing,
|
||||||
|
PatternSyntax = patternSyntax,
|
||||||
|
Match = new InitialMatch
|
||||||
|
{
|
||||||
|
Url = new Regex(Url),
|
||||||
|
IgnoreCase = ignoreCase,
|
||||||
|
Negate = negate
|
||||||
|
},
|
||||||
|
Conditions = new Conditions
|
||||||
|
{
|
||||||
|
ConditionList = conditions,
|
||||||
|
MatchType = condGrouping,
|
||||||
|
TrackingAllCaptures = condTracking
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssertUrlRewriteRuleEquality(List<UrlRewriteRule> expected, List<UrlRewriteRule> actual)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected.Count, actual.Count);
|
||||||
|
for (var i = 0; i < expected.Count; i++)
|
||||||
|
{
|
||||||
|
var r1 = expected[i];
|
||||||
|
var r2 = actual[i];
|
||||||
|
|
||||||
|
Assert.Equal(r1.Name, r2.Name);
|
||||||
|
Assert.Equal(r1.Enabled, r2.Enabled);
|
||||||
|
Assert.Equal(r1.StopProcessing, r2.StopProcessing);
|
||||||
|
Assert.Equal(r1.PatternSyntax, r2.PatternSyntax);
|
||||||
|
|
||||||
|
Assert.Equal(r1.Match.IgnoreCase, r2.Match.IgnoreCase);
|
||||||
|
Assert.Equal(r1.Match.Negate, r2.Match.Negate);
|
||||||
|
|
||||||
|
Assert.Equal(r1.Action.Type, r2.Action.Type);
|
||||||
|
Assert.Equal(r1.Action.AppendQueryString, r2.Action.AppendQueryString);
|
||||||
|
Assert.Equal(r1.Action.RedirectType, r2.Action.RedirectType);
|
||||||
|
Assert.Equal(r1.Action.LogRewrittenUrl, r2.Action.LogRewrittenUrl);
|
||||||
|
|
||||||
|
// TODO conditions, url pattern, initial match regex
|
||||||
|
Assert.Equal(r1.Conditions.MatchType, r2.Conditions.MatchType);
|
||||||
|
Assert.Equal(r1.Conditions.TrackingAllCaptures, r2.Conditions.TrackingAllCaptures);
|
||||||
|
Assert.Equal(r1.Conditions.ConditionList.Count, r2.Conditions.ConditionList.Count);
|
||||||
|
|
||||||
|
for (var j = 0; j < r1.Conditions.ConditionList.Count; j++)
|
||||||
|
{
|
||||||
|
var c1 = r1.Conditions.ConditionList[j];
|
||||||
|
var c2 = r2.Conditions.ConditionList[j];
|
||||||
|
Assert.Equal(c1.IgnoreCase, c2.IgnoreCase);
|
||||||
|
Assert.Equal(c1.Negate, c2.Negate);
|
||||||
|
Assert.Equal(c1.MatchType, c2.MatchType);
|
||||||
|
Assert.Equal(c1.Input.PatternSegments.Count, c2.Input.PatternSegments.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.UrlRewrite;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
{
|
||||||
|
public class InputParserTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void InputParser_ParseLiteralString()
|
||||||
|
{
|
||||||
|
var testString = "hello/hey/what";
|
||||||
|
var result = InputParser.ParseInputString(testString);
|
||||||
|
Assert.Equal(result.PatternSegments.Count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests sizes of the pattern segments. These are all anonyomus lambdas, so cant check contents.
|
||||||
|
[Theory]
|
||||||
|
[InlineData("foo/bar/{R:1}/what", 3)]
|
||||||
|
[InlineData("foo/{R:1}", 2)]
|
||||||
|
[InlineData("foo/{R:1}/{C:2}", 4)]
|
||||||
|
[InlineData("foo/{R:1}{C:2}", 3)]
|
||||||
|
[InlineData("foo/", 1)]
|
||||||
|
public void InputParser_ParseStringWithBackReference(string testString, int expected)
|
||||||
|
{
|
||||||
|
var result = InputParser.ParseInputString(testString);
|
||||||
|
Assert.Equal(result.PatternSegments.Count, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test actual evaluation of the lambdas, verifying the correct string comes from the evalation
|
||||||
|
[Theory]
|
||||||
|
[InlineData("hey/hello/what", "hey/hello/what")]
|
||||||
|
[InlineData("hey/{R:1}/what", "hey/foo/what")]
|
||||||
|
[InlineData("hey/{R:2}/what", "hey/bar/what")]
|
||||||
|
[InlineData("hey/{R:3}/what", "hey/baz/what")]
|
||||||
|
[InlineData("hey/{C:1}/what", "hey/foo/what")]
|
||||||
|
[InlineData("hey/{C:2}/what", "hey/bar/what")]
|
||||||
|
[InlineData("hey/{C:3}/what", "hey/baz/what")]
|
||||||
|
[InlineData("hey/{R:1}/{C:1}", "hey/foo/foo")]
|
||||||
|
public void EvaluateBackReferenceRule(string testString, string expected)
|
||||||
|
{
|
||||||
|
var middle = InputParser.ParseInputString(testString);
|
||||||
|
var result = middle.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
|
||||||
|
Assert.Equal(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("hey/{ToLower:HEY}", "hey/hey")]
|
||||||
|
[InlineData("hey/{ToLower:{R:1}}", "hey/foo")]
|
||||||
|
[InlineData("hey/{ToLower:{C:1}}", "hey/foo")]
|
||||||
|
[InlineData("hey/{ToLower:{C:1}/what}", "hey/foo/what")]
|
||||||
|
[InlineData("hey/ToLower:/what", "hey/ToLower:/what")]
|
||||||
|
public void EvaluatToLowerRule(string testString, string expected)
|
||||||
|
{
|
||||||
|
var middle = InputParser.ParseInputString(testString);
|
||||||
|
var result = middle.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
|
||||||
|
Assert.Equal(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("hey/{UrlEncode:<hey>}", "hey/%3Chey%3E")]
|
||||||
|
public void EvaluatUriEncodeRule(string testString, string expected)
|
||||||
|
{
|
||||||
|
var middle = InputParser.ParseInputString(testString);
|
||||||
|
var result = middle.Evaluate(CreateTestHttpContext(), CreateTestRuleMatch(), CreateTestCondMatch());
|
||||||
|
Assert.Equal(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("{")]
|
||||||
|
[InlineData("{:}")]
|
||||||
|
[InlineData("{R:")]
|
||||||
|
[InlineData("{R:1")]
|
||||||
|
[InlineData("{R:A}")]
|
||||||
|
[InlineData("{R:10}")]
|
||||||
|
[InlineData("{R:-1}")]
|
||||||
|
[InlineData("{foo:1")]
|
||||||
|
[InlineData("{UrlEncode:{R:}}")]
|
||||||
|
[InlineData("{UrlEncode:{R:1}")]
|
||||||
|
public void FormatExceptionsOnBadSyntax(string testString)
|
||||||
|
{
|
||||||
|
Assert.Throws<FormatException>(() => InputParser.ParseInputString(testString));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpContext CreateTestHttpContext()
|
||||||
|
{
|
||||||
|
|
||||||
|
HttpContext context = new DefaultHttpContext();
|
||||||
|
// TODO add fields if necessary
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Match CreateTestRuleMatch()
|
||||||
|
{
|
||||||
|
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Match CreateTestCondMatch()
|
||||||
|
{
|
||||||
|
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue