Feedback from David and API Review/ Tests
This commit is contained in:
parent
0cb7445921
commit
f3b4658c2c
|
|
@ -1,10 +1,3 @@
|
||||||
# Ensure Https
|
# Rewrite path with additional sub directory
|
||||||
RewriteCond %{HTTPS} off
|
RewriteCond %{QUERY_STRING} id=20
|
||||||
RewriteRule ^(.*)$ https://www.example.com$1 [L]
|
RewriteRule ^(.*)$ - [G]
|
||||||
|
|
||||||
# Rewrite path with additional sub directory
|
|
||||||
RewriteRule ^(.*)$ /foo$1
|
|
||||||
|
|
||||||
# Forbid a certain url from being accessed
|
|
||||||
RewriteRule /bar - [F]
|
|
||||||
RewriteRule /bar/ - [F]
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite;
|
using Microsoft.AspNetCore.Rewrite;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
|
|
||||||
namespace RewriteSample
|
namespace RewriteSample
|
||||||
{
|
{
|
||||||
|
|
@ -26,16 +25,11 @@ namespace RewriteSample
|
||||||
// (ex StringReplace) that are easy to implement in code, they can do so by calling
|
// (ex StringReplace) that are easy to implement in code, they can do so by calling
|
||||||
// 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()
|
||||||
|
.Rewrite(@"foo/(\d+)", "foo?id={R:1}")
|
||||||
.ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
|
.ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml")
|
||||||
.ImportFromModRewrite(hostingEnv, "Rewrite.txt")
|
.ImportFromModRewrite(hostingEnv, "Rewrite.txt"));
|
||||||
.RedirectToHttps(StatusCodes.Status307TemporaryRedirect)
|
|
||||||
.RewriteRule("/foo/(.*)/bar", "{R:1}/bar")
|
|
||||||
.AddRule(ctx =>
|
|
||||||
{
|
|
||||||
ctx.HttpContext.Request.Path = "/index";
|
|
||||||
return RuleResult.Continue;
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}"));
|
app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<rewrite>
|
<rewrite>
|
||||||
<rules>
|
<rules>
|
||||||
<rule name="Example" stopProcessing="true">
|
<rule name="" stopProcessing="true">
|
||||||
<match url="(.*)" />
|
<match url="foo/bar" />
|
||||||
<action type="Rewrite" url="http://example.com/{R:1}" />
|
<action type="Redirect" redirectType="Found" url="foo/" />
|
||||||
</rule>
|
</rule>
|
||||||
</rules>
|
</rules>
|
||||||
</rewrite>
|
</rewrite>
|
||||||
|
|
@ -1,104 +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;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.CodeRules;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The builder to a list of rules for <see cref="RewriteOptions"/> and <see cref="RewriteMiddleware"/>
|
|
||||||
/// </summary>
|
|
||||||
public static class CodeRewriteExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a rule to the current rules.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The UrlRewrite options.</param>
|
|
||||||
/// <param name="rule">A rule to be added to the current rules.</param>
|
|
||||||
public static RewriteOptions AddRule(this RewriteOptions options, Rule rule)
|
|
||||||
{
|
|
||||||
options.Rules.Add(rule);
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a list of rules to the current rules.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The UrlRewrite options.</param>
|
|
||||||
/// <param name="rules">A list of rules.</param>
|
|
||||||
public static RewriteOptions AddRules(this RewriteOptions options, List<Rule> rules)
|
|
||||||
{
|
|
||||||
options.Rules.AddRange(rules);
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RewriteRule(this RewriteOptions options, string regex, string onMatch)
|
|
||||||
{
|
|
||||||
return RewriteRule(options, regex, onMatch, stopProcessing: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RewriteRule(this RewriteOptions options, string regex, string onMatch, bool stopProcessing)
|
|
||||||
{
|
|
||||||
var builder = new UrlRewriteRuleBuilder();
|
|
||||||
var pattern = new InputParser().ParseInputString(onMatch);
|
|
||||||
|
|
||||||
builder.AddUrlMatch(regex);
|
|
||||||
builder.AddUrlAction(pattern, actionType: ActionType.Rewrite, stopProcessing: stopProcessing);
|
|
||||||
options.Rules.Add(builder.Build());
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RedirectRule(this RewriteOptions options, string regex, string onMatch, int statusCode)
|
|
||||||
{
|
|
||||||
return RedirectRule(options, regex, onMatch, statusCode, stopProcessing: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RedirectRule(this RewriteOptions options, string regex, string onMatch, int statusCode, bool stopProcessing)
|
|
||||||
{
|
|
||||||
var builder = new UrlRewriteRuleBuilder();
|
|
||||||
var pattern = new InputParser().ParseInputString(onMatch);
|
|
||||||
|
|
||||||
builder.AddUrlMatch(regex);
|
|
||||||
builder.AddUrlAction(pattern, actionType: ActionType.Redirect, stopProcessing: stopProcessing);
|
|
||||||
options.Rules.Add(builder.Build());
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode)
|
|
||||||
{
|
|
||||||
return RedirectToHttps(options, statusCode, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode, int? sslPort)
|
|
||||||
{
|
|
||||||
options.Rules.Add(new RedirectToHttpsRule { StatusCode = statusCode, SSLPort = sslPort });
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RewriteToHttps(this RewriteOptions options)
|
|
||||||
{
|
|
||||||
return RewriteToHttps(options, sslPort: null, stopProcessing: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RewriteToHttps(this RewriteOptions options, int? sslPort)
|
|
||||||
{
|
|
||||||
return RewriteToHttps(options, sslPort, stopProcessing: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions RewriteToHttps(this RewriteOptions options, int? sslPort, bool stopProcessing)
|
|
||||||
{
|
|
||||||
options.Rules.Add(new RewriteToHttpsRule {SSLPort = sslPort, StopProcessing = stopProcessing });
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RewriteOptions AddRule(this RewriteOptions options, Func<RewriteContext, RuleResult> rule)
|
|
||||||
{
|
|
||||||
options.Rules.Add(new FunctionalRule { OnApplyRule = rule});
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,9 +5,14 @@ using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||||
{
|
{
|
||||||
public class FunctionalRule : Rule
|
public class DelegateRule : Rule
|
||||||
{
|
{
|
||||||
public Func<RewriteContext, RuleResult> OnApplyRule { get; set; }
|
private readonly Func<RewriteContext, RuleResult> _onApplyRule;
|
||||||
public override RuleResult ApplyRule(RewriteContext context) => OnApplyRule(context);
|
|
||||||
|
public DelegateRule(Func<RewriteContext, RuleResult> onApplyRule)
|
||||||
|
{
|
||||||
|
_onApplyRule = onApplyRule;
|
||||||
|
}
|
||||||
|
public override RuleResult ApplyRule(RewriteContext context) => _onApplyRule(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,36 +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.CodeRules
|
|
||||||
{
|
|
||||||
public class RewriteToHttpsRule : Rule
|
|
||||||
{
|
|
||||||
public bool StopProcessing { get; set; }
|
|
||||||
public int? SSLPort { get; set; }
|
|
||||||
|
|
||||||
public override RuleResult ApplyRule(RewriteContext context)
|
|
||||||
{
|
|
||||||
if (!context.HttpContext.Request.IsHttps)
|
|
||||||
{
|
|
||||||
var host = context.HttpContext.Request.Host;
|
|
||||||
if (SSLPort.HasValue && SSLPort.Value > 0)
|
|
||||||
{
|
|
||||||
// a specific SSL port is specified
|
|
||||||
host = new HostString(host.Host, SSLPort.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// clear the port
|
|
||||||
host = new HostString(host.Host);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.HttpContext.Request.Scheme = "https";
|
|
||||||
context.HttpContext.Request.Host = host;
|
|
||||||
return StopProcessing ? RuleResult.StopRules: RuleResult.Continue;
|
|
||||||
}
|
|
||||||
return RuleResult.Continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,15 +5,14 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class Conditions
|
public static class ConditionHelper
|
||||||
{
|
{
|
||||||
public List<Condition> ConditionList { get; set; } = new List<Condition>();
|
|
||||||
|
|
||||||
public MatchResults Evaluate(RewriteContext context, MatchResults ruleMatch)
|
public static MatchResults Evaluate(IEnumerable<Condition> conditions, RewriteContext context, MatchResults ruleMatch)
|
||||||
{
|
{
|
||||||
MatchResults prevCond = null;
|
MatchResults prevCond = null;
|
||||||
var orSucceeded = false;
|
var orSucceeded = false;
|
||||||
foreach (var condition in ConditionList)
|
foreach (var condition in conditions)
|
||||||
{
|
{
|
||||||
if (orSucceeded && condition.OrNext)
|
if (orSucceeded && condition.OrNext)
|
||||||
{
|
{
|
||||||
|
|
@ -30,7 +29,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
if (condition.OrNext)
|
if (condition.OrNext)
|
||||||
{
|
{
|
||||||
orSucceeded = prevCond.Success;
|
orSucceeded = prevCond.Success;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else if (!prevCond.Success)
|
else if (!prevCond.Success)
|
||||||
{
|
{
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
/// <returns>A new parsed condition.</returns>
|
/// <returns>A new parsed condition.</returns>
|
||||||
public ParsedModRewriteInput ParseActionCondition(string condition)
|
public ParsedModRewriteInput ParseActionCondition(string condition)
|
||||||
{
|
{
|
||||||
if (condition == null)
|
if (condition == null)
|
||||||
{
|
{
|
||||||
condition = string.Empty;
|
condition = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
public class FileParser
|
public class FileParser
|
||||||
{
|
{
|
||||||
public List<Rule> Parse(TextReader input)
|
public IList<Rule> Parse(TextReader input)
|
||||||
{
|
{
|
||||||
string line;
|
string line;
|
||||||
var rules = new List<Rule>();
|
var rules = new List<Rule>();
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
builder.AddAction(pattern, flags);
|
builder.AddAction(pattern, flags);
|
||||||
rules.Add(builder.Build());
|
rules.Add(builder.Build());
|
||||||
builder = new RuleBuilder();
|
builder = new RuleBuilder();
|
||||||
}
|
}
|
||||||
catch (FormatException formatException)
|
catch (FormatException formatException)
|
||||||
{
|
{
|
||||||
throw new FormatException(Resources.FormatError_ModRewriteGeneralParseError(lineNum), formatException);
|
throw new FormatException(Resources.FormatError_ModRewriteGeneralParseError(lineNum), formatException);
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
public bool EscapeBackReferences { get; }
|
public bool EscapeBackReferences { get; }
|
||||||
|
|
||||||
public ModRewriteRedirectAction(
|
public ModRewriteRedirectAction(
|
||||||
int statusCode,
|
int statusCode,
|
||||||
Pattern pattern,
|
Pattern pattern,
|
||||||
bool queryStringAppend,
|
bool queryStringAppend,
|
||||||
bool queryStringDelete,
|
bool queryStringDelete,
|
||||||
bool escapeBackReferences)
|
bool escapeBackReferences)
|
||||||
{
|
{
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
|
|
@ -42,19 +42,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
// always add to location header.
|
// always add to location header.
|
||||||
// TODO check for false positives
|
// TODO check for false positives
|
||||||
var split = pattern.IndexOf('?');
|
var split = pattern.IndexOf('?');
|
||||||
if (split >= 0)
|
if (split >= 0 && QueryStringAppend)
|
||||||
{
|
{
|
||||||
QueryString query;
|
var query = context.HttpContext.Request.QueryString.Add(
|
||||||
if (QueryStringAppend)
|
QueryString.FromUriComponent(
|
||||||
{
|
pattern.Substring(split)));
|
||||||
query = context.HttpContext.Request.QueryString.Add(
|
|
||||||
QueryString.FromUriComponent(
|
|
||||||
pattern.Substring(split)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
query = QueryString.FromUriComponent(pattern.Substring(split));
|
|
||||||
}
|
|
||||||
|
|
||||||
// not using the response.redirect here because status codes may be 301, 302, 307, 308
|
// 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;
|
context.HttpContext.Response.Headers[HeaderNames.Location] = pattern.Substring(0, split) + query;
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
public bool EscapeBackReferences { get; }
|
public bool EscapeBackReferences { get; }
|
||||||
|
|
||||||
public ModRewriteRewriteAction(
|
public ModRewriteRewriteAction(
|
||||||
RuleResult result,
|
RuleResult result,
|
||||||
Pattern pattern,
|
Pattern pattern,
|
||||||
bool queryStringAppend,
|
bool queryStringAppend,
|
||||||
bool queryStringDelete,
|
bool queryStringDelete,
|
||||||
bool escapeBackReferences)
|
bool escapeBackReferences)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
public class RuleBuilder
|
public class RuleBuilder
|
||||||
{
|
{
|
||||||
private Conditions _conditions;
|
private IList<Condition> _conditions;
|
||||||
private UrlAction _action;
|
private UrlAction _action;
|
||||||
private UrlMatch _match;
|
private UrlMatch _match;
|
||||||
private List<PreAction> _preActions;
|
private List<PreAction> _preActions;
|
||||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
{
|
{
|
||||||
if (_conditions == null)
|
if (_conditions == null)
|
||||||
{
|
{
|
||||||
_conditions = new Conditions();
|
_conditions = new List<Condition>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var condition = new Condition();
|
var condition = new Condition();
|
||||||
|
|
@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_conditions.ConditionList.Add(condition);
|
_conditions.Add(condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddMatch(
|
public void AddMatch(
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new ParsedModRewriteInput { Invert = false, Operand = regex};
|
return new ParsedModRewriteInput { Invert = false, Operand = regex };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
case "HTTP2":
|
case "HTTP2":
|
||||||
throw new NotImplementedException("Http2 server variable is not supported");
|
throw new NotImplementedException("Http2 server variable is not supported");
|
||||||
case "IS_SUBREQ":
|
case "IS_SUBREQ":
|
||||||
// TODO maybe can do this? context.Request.HttpContext ?
|
|
||||||
throw new NotImplementedException("Is-Subrequest server variable is not supported");
|
throw new NotImplementedException("Is-Subrequest server variable is not supported");
|
||||||
case "REQUEST_FILENAME":
|
case "REQUEST_FILENAME":
|
||||||
return new RequestFileNameSegment();
|
return new RequestFileNameSegment();
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
var ruleVariable = context.Capture();
|
var ruleVariable = context.Capture();
|
||||||
context.Back();
|
context.Back();
|
||||||
var parsedIndex = int.Parse(ruleVariable);
|
var parsedIndex = int.Parse(ruleVariable);
|
||||||
|
|
||||||
results.Add(new RuleMatchSegment(parsedIndex));
|
results.Add(new RuleMatchSegment(parsedIndex));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -89,7 +89,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 void ParseConditionParameter(ParserContext context, List<PatternSegment> results)
|
private static void ParseConditionParameter(ParserContext context, IList<PatternSegment> results)
|
||||||
{
|
{
|
||||||
// Parse { }
|
// Parse { }
|
||||||
if (context.Current == OpenBrace)
|
if (context.Current == OpenBrace)
|
||||||
|
|
@ -146,7 +146,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 void ParseLiteral(ParserContext context, List<PatternSegment> results)
|
private static void ParseLiteral(ParserContext context, IList<PatternSegment> results)
|
||||||
{
|
{
|
||||||
context.Mark();
|
context.Mark();
|
||||||
string literal;
|
string literal;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rule">The rule to tokenize.</param>
|
/// <param name="rule">The rule to tokenize.</param>
|
||||||
/// <returns>A list of tokens.</returns>
|
/// <returns>A list of tokens.</returns>
|
||||||
public List<string> Tokenize(string rule)
|
public IList<string> Tokenize(string rule)
|
||||||
{
|
{
|
||||||
// TODO make list of strings a reference to the original rule? (run into problems with escaped spaces).
|
// TODO make list of strings a reference to the original rule? (run into problems with escaped spaces).
|
||||||
// TODO handle "s and probably replace \ character with no slash.
|
// TODO handle "s and probably replace \ character with no slash.
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
// 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 Microsoft.AspNetCore.Rewrite.Internal;
|
using Microsoft.AspNetCore.Rewrite.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class ModRewriteRule : Rule
|
public class ModRewriteRule : Rule
|
||||||
{
|
{
|
||||||
public UrlMatch InitialMatch { get; set; }
|
public UrlMatch InitialMatch { get; }
|
||||||
public Conditions Conditions { get; set; }
|
public IList<Condition> Conditions { get; }
|
||||||
public UrlAction Action { get; set; }
|
public UrlAction Action { get; }
|
||||||
public List<PreAction> PreActions { get; set; }
|
public IList<PreAction> PreActions { get; }
|
||||||
|
|
||||||
public ModRewriteRule(UrlMatch initialMatch, Conditions conditions, UrlAction urlAction, List<PreAction> preActions)
|
public ModRewriteRule(UrlMatch initialMatch, IList<Condition> conditions, UrlAction urlAction, IList<PreAction> preActions)
|
||||||
{
|
{
|
||||||
Conditions = conditions;
|
Conditions = conditions;
|
||||||
InitialMatch = initialMatch;
|
InitialMatch = initialMatch;
|
||||||
|
|
@ -28,21 +28,24 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
|
|
||||||
if (!initMatchRes.Success)
|
if (!initMatchRes.Success)
|
||||||
{
|
{
|
||||||
|
context.Logger?.ModRewriteDidNotMatchRule();
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResults condMatchRes = null;
|
MatchResults condMatchRes = null;
|
||||||
if (Conditions != null)
|
if (Conditions != null)
|
||||||
{
|
{
|
||||||
condMatchRes = Conditions.Evaluate(context, initMatchRes);
|
condMatchRes = ConditionHelper.Evaluate(Conditions, context, initMatchRes);
|
||||||
if (!condMatchRes.Success)
|
if (!condMatchRes.Success)
|
||||||
{
|
{
|
||||||
|
context.Logger?.ModRewriteDidNotMatchRule();
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we know our rule passed, first apply pre conditions,
|
// 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
|
// which can modify things like the cookie or env, and then apply the action
|
||||||
|
context.Logger?.ModRewriteMatchedRule();
|
||||||
foreach (var preAction in PreActions)
|
foreach (var preAction in PreActions)
|
||||||
{
|
{
|
||||||
preAction.ApplyAction(context.HttpContext, initMatchRes, condMatchRes);
|
preAction.ApplyAction(context.HttpContext, initMatchRes, condMatchRes);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +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 Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public abstract class PatternSegment
|
public abstract class PatternSegment
|
||||||
{
|
{
|
||||||
// Match from prevRule, Match from prevCond
|
|
||||||
public abstract string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch);
|
public abstract string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
public DateTimeSegment(string segment)
|
public DateTimeSegment(string segment)
|
||||||
{
|
{
|
||||||
switch(segment)
|
switch (segment)
|
||||||
{
|
{
|
||||||
case "TIME_YEAR":
|
case "TIME_YEAR":
|
||||||
_portion = DateTimePortion.Year;
|
_portion = DateTimePortion.Year;
|
||||||
|
|
@ -24,28 +24,29 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
_portion = DateTimePortion.Day;
|
_portion = DateTimePortion.Day;
|
||||||
break;
|
break;
|
||||||
case "TIME_HOUR":
|
case "TIME_HOUR":
|
||||||
_portion = DateTimePortion.Day;
|
_portion = DateTimePortion.Hour;
|
||||||
break;
|
break;
|
||||||
case "TIME_MIN":
|
case "TIME_MIN":
|
||||||
_portion = DateTimePortion.Day;
|
_portion = DateTimePortion.Minute;
|
||||||
break;
|
break;
|
||||||
case "TIME_SEC":
|
case "TIME_SEC":
|
||||||
_portion = DateTimePortion.Day;
|
_portion = DateTimePortion.Second;
|
||||||
break;
|
break;
|
||||||
case "TIME_WDAY":
|
case "TIME_WDAY":
|
||||||
_portion = DateTimePortion.Day;
|
_portion = DateTimePortion.DayOfWeek;
|
||||||
break;
|
break;
|
||||||
case "TIME":
|
case "TIME":
|
||||||
_portion = DateTimePortion.Day;
|
_portion = DateTimePortion.Time;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new FormatException("Unsupported segment: " + segment);
|
throw new FormatException($"Unsupported segment: '{segment}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
||||||
{
|
{
|
||||||
switch (_portion) {
|
switch (_portion)
|
||||||
|
{
|
||||||
case DateTimePortion.Year:
|
case DateTimePortion.Year:
|
||||||
return DateTimeOffset.UtcNow.Year.ToString(CultureInfo.InvariantCulture);
|
return DateTimeOffset.UtcNow.Year.ToString(CultureInfo.InvariantCulture);
|
||||||
case DateTimePortion.Month:
|
case DateTimePortion.Month:
|
||||||
|
|
@ -67,7 +68,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum DateTimePortion {
|
private enum DateTimePortion
|
||||||
|
{
|
||||||
Year,
|
Year,
|
||||||
Month,
|
Month,
|
||||||
Day,
|
Day,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
public class HeaderSegment : PatternSegment
|
public class HeaderSegment : PatternSegment
|
||||||
{
|
{
|
||||||
private readonly string _header;
|
private readonly string _header;
|
||||||
|
|
||||||
public HeaderSegment(string header)
|
public HeaderSegment(string header)
|
||||||
{
|
{
|
||||||
_header = header;
|
_header = header;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
public class UrlEncodeSegment : PatternSegment
|
public class UrlEncodeSegment : PatternSegment
|
||||||
{
|
{
|
||||||
private readonly Pattern _pattern;
|
private readonly Pattern _pattern;
|
||||||
|
|
||||||
public UrlEncodeSegment(Pattern pattern)
|
public UrlEncodeSegment(Pattern pattern)
|
||||||
{
|
{
|
||||||
_pattern = pattern;
|
_pattern = pattern;
|
||||||
|
|
@ -17,10 +17,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments
|
||||||
|
|
||||||
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
||||||
{
|
{
|
||||||
var tempBuilder = context.Builder;
|
var oldBuilder = context.Builder;
|
||||||
|
// PERF
|
||||||
|
// Because we need to be able to evaluate multiple nested patterns,
|
||||||
|
// we provided a new string builder and evaluate the new pattern,
|
||||||
|
// and restore it after evaluation.
|
||||||
context.Builder = new StringBuilder(64);
|
context.Builder = new StringBuilder(64);
|
||||||
var pattern = _pattern.Evaluate(context, ruleMatch, condMatch);
|
var pattern = _pattern.Evaluate(context, ruleMatch, condMatch);
|
||||||
context.Builder = tempBuilder;
|
context.Builder = oldBuilder;
|
||||||
return UrlEncoder.Default.Encode(pattern);
|
return UrlEncoder.Default.Encode(pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PreActions
|
||||||
public override void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
|
public override void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch)
|
||||||
{
|
{
|
||||||
// modify the cookies
|
// modify the cookies
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public abstract class UrlAction
|
public abstract class UrlAction
|
||||||
{
|
{
|
||||||
public Pattern Url { get; set; }
|
protected Pattern Url { get; set; }
|
||||||
public abstract RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch);
|
public abstract RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -9,25 +10,33 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
public class RedirectAction : UrlAction
|
public class RedirectAction : UrlAction
|
||||||
{
|
{
|
||||||
public int StatusCode { get; }
|
public int StatusCode { get; }
|
||||||
public RedirectAction(int statusCode, Pattern pattern)
|
public bool AppendQueryString { get; }
|
||||||
|
|
||||||
|
public RedirectAction(int statusCode, Pattern pattern, bool appendQueryString)
|
||||||
{
|
{
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
Url = pattern;
|
Url = pattern;
|
||||||
|
AppendQueryString = appendQueryString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override RuleResult ApplyAction(RewriteContext 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.HttpContext.Response.StatusCode = StatusCode;
|
context.HttpContext.Response.StatusCode = StatusCode;
|
||||||
|
|
||||||
|
// TODO IIS guarantees that there will be a leading slash
|
||||||
|
if (pattern.IndexOf("://", StringComparison.Ordinal) == -1 && !pattern.StartsWith("/"))
|
||||||
|
{
|
||||||
|
pattern = '/' + pattern;
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
// TODO check for false positives
|
// TODO check for false positives
|
||||||
var split = pattern.IndexOf('?');
|
var split = pattern.IndexOf('?');
|
||||||
if (split >= 0)
|
if (split >= 0 && AppendQueryString)
|
||||||
{
|
{
|
||||||
var query = context.HttpContext.Request.QueryString.Add(
|
var query = context.HttpContext.Request.QueryString.Add(
|
||||||
QueryString.FromUriComponent(
|
QueryString.FromUriComponent(
|
||||||
pattern.Substring(split)));
|
pattern.Substring(split)));
|
||||||
// not using the HttpContext.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
|
||||||
|
|
|
||||||
|
|
@ -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 Microsoft.Net.Http.Headers;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
|
||||||
{
|
|
||||||
public class RedirectClearQueryAction : UrlAction
|
|
||||||
{
|
|
||||||
public int StatusCode { get; }
|
|
||||||
public RedirectClearQueryAction(int statusCode, Pattern pattern)
|
|
||||||
{
|
|
||||||
StatusCode = statusCode;
|
|
||||||
Url = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override RuleResult ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch)
|
|
||||||
{
|
|
||||||
var pattern = Url.Evaluate(context, ruleMatch, condMatch);
|
|
||||||
context.HttpContext.Response.StatusCode = StatusCode;
|
|
||||||
|
|
||||||
// we are clearing the query, so just put the pattern in the location header
|
|
||||||
context.HttpContext.Response.Headers[HeaderNames.Location] = pattern;
|
|
||||||
return RuleResult.ResponseComplete;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -10,10 +10,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
public class RewriteAction : UrlAction
|
public class RewriteAction : UrlAction
|
||||||
{
|
{
|
||||||
private readonly string ForwardSlash = "/";
|
private readonly string ForwardSlash = "/";
|
||||||
public RuleTerminiation Result { get; }
|
public RuleTermination Result { get; }
|
||||||
public bool ClearQuery { get; }
|
public bool ClearQuery { get; }
|
||||||
|
|
||||||
public RewriteAction(RuleTerminiation result, Pattern pattern, bool clearQuery)
|
public RewriteAction(RuleTermination result, Pattern pattern, bool clearQuery)
|
||||||
{
|
{
|
||||||
Result = result;
|
Result = result;
|
||||||
Url = pattern;
|
Url = pattern;
|
||||||
|
|
@ -28,8 +28,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions
|
||||||
{
|
{
|
||||||
context.HttpContext.Request.QueryString = QueryString.Empty;
|
context.HttpContext.Request.QueryString = QueryString.Empty;
|
||||||
}
|
}
|
||||||
// TODO PERF, substrings, object creation, etc.
|
|
||||||
if (pattern.IndexOf("://") >= 0)
|
if (pattern.IndexOf("://", StringComparison.Ordinal) >= 0)
|
||||||
{
|
{
|
||||||
string scheme;
|
string scheme;
|
||||||
HostString host;
|
HostString host;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
|
|
||||||
public override MatchResults Evaluate(string input, RewriteContext context)
|
public override MatchResults Evaluate(string input, RewriteContext context)
|
||||||
{
|
{
|
||||||
var fileInfo = context.FileProvider.GetFileInfo(input);
|
var fileInfo = context.StaticFileProvider.GetFileInfo(input);
|
||||||
return fileInfo.Exists && fileInfo.Length > 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
return fileInfo.Exists && fileInfo.Length > 0 ? MatchResults.EmptySuccess : MatchResults.EmptyFailure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
public class IsDirectoryMatch : UrlMatch
|
public class IsDirectoryMatch : UrlMatch
|
||||||
{
|
{
|
||||||
public IsDirectoryMatch(bool negate)
|
public IsDirectoryMatch(bool negate)
|
||||||
{
|
{
|
||||||
Negate = negate;
|
Negate = negate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override MatchResults Evaluate(string pattern, RewriteContext context)
|
public override MatchResults Evaluate(string pattern, RewriteContext context)
|
||||||
{
|
{
|
||||||
var res = context.FileProvider.GetFileInfo(pattern).IsDirectory;
|
var res = context.StaticFileProvider.GetFileInfo(pattern).IsDirectory;
|
||||||
return new MatchResults { Success = (res != Negate) };
|
return new MatchResults { Success = (res != Negate) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
{
|
{
|
||||||
public class IsFileMatch : UrlMatch
|
public class IsFileMatch : UrlMatch
|
||||||
{
|
{
|
||||||
public IsFileMatch(bool negate)
|
public IsFileMatch(bool negate)
|
||||||
{
|
{
|
||||||
Negate = negate;
|
Negate = negate;
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
|
|
||||||
public override MatchResults Evaluate(string pattern, RewriteContext context)
|
public override MatchResults Evaluate(string pattern, RewriteContext context)
|
||||||
{
|
{
|
||||||
var res = context.FileProvider.GetFileInfo(pattern).Exists;
|
var res = context.StaticFileProvider.GetFileInfo(pattern).Exists;
|
||||||
return new MatchResults { Success = (res != Negate) };
|
return new MatchResults { Success = (res != Negate) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlMatches
|
||||||
public override MatchResults Evaluate(string pattern, RewriteContext context)
|
public override MatchResults Evaluate(string pattern, RewriteContext context)
|
||||||
{
|
{
|
||||||
var res = _match.Match(pattern);
|
var res = _match.Match(pattern);
|
||||||
return new MatchResults { BackReference = res.Groups, Success = (res.Success != Negate)};
|
return new MatchResults { BackReference = res.Groups, Success = (res.Success != Negate) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
|
|
@ -60,7 +59,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
return new Pattern(results);
|
return new Pattern(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ParseParameter(ParserContext context, List<PatternSegment> results)
|
private static void ParseParameter(ParserContext context, IList<PatternSegment> results)
|
||||||
{
|
{
|
||||||
context.Mark();
|
context.Mark();
|
||||||
// Four main cases:
|
// Four main cases:
|
||||||
|
|
@ -82,7 +81,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
else if (context.Current == Colon)
|
else if (context.Current == Colon)
|
||||||
{
|
{
|
||||||
parameter = context.Capture();
|
parameter = context.Capture();
|
||||||
|
|
||||||
// Only 5 strings to expect here. Case sensitive.
|
// Only 5 strings to expect here. Case sensitive.
|
||||||
switch (parameter)
|
switch (parameter)
|
||||||
{
|
{
|
||||||
|
|
@ -165,7 +164,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ParseLiteral(ParserContext context, List<PatternSegment> results)
|
private static void ParseLiteral(ParserContext context, IList<PatternSegment> results)
|
||||||
{
|
{
|
||||||
context.Mark();
|
context.Mark();
|
||||||
string literal;
|
string literal;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// 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;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -12,7 +11,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
public static PatternSegment FindServerVariable(string serverVariable)
|
public static PatternSegment FindServerVariable(string serverVariable)
|
||||||
{
|
{
|
||||||
switch(serverVariable)
|
switch (serverVariable)
|
||||||
{
|
{
|
||||||
// TODO Add all server variables here.
|
// TODO Add all server variables here.
|
||||||
case "ALL_RAW":
|
case "ALL_RAW":
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
public class FileParser
|
public class UrlRewriteFileParser
|
||||||
{
|
{
|
||||||
private readonly InputParser _inputParser = new InputParser();
|
private readonly InputParser _inputParser = new InputParser();
|
||||||
|
|
||||||
public List<UrlRewriteRule> Parse(TextReader reader)
|
public IList<UrlRewriteRule> Parse(TextReader reader)
|
||||||
{
|
{
|
||||||
var xmlDoc = XDocument.Load(reader, LoadOptions.SetLineInfo);
|
var xmlDoc = XDocument.Load(reader, LoadOptions.SetLineInfo);
|
||||||
var xmlRoot = xmlDoc.Descendants(RewriteTags.Rewrite).FirstOrDefault();
|
var xmlRoot = xmlDoc.Descendants(RewriteTags.Rewrite).FirstOrDefault();
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseRules(XElement rules, List<UrlRewriteRule> result)
|
private void ParseRules(XElement rules, IList<UrlRewriteRule> result)
|
||||||
{
|
{
|
||||||
if (rules == null)
|
if (rules == null)
|
||||||
{
|
{
|
||||||
|
|
@ -13,12 +13,12 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
public class UrlRewriteRuleBuilder
|
public class UrlRewriteRuleBuilder
|
||||||
{
|
{
|
||||||
private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
|
private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
private UrlMatch _initialMatch;
|
private UrlMatch _initialMatch;
|
||||||
private Conditions _conditions;
|
private IList<Condition> _conditions;
|
||||||
private UrlAction _action;
|
private UrlAction _action;
|
||||||
private bool _matchAny;
|
private bool _matchAny;
|
||||||
|
|
||||||
|
|
@ -28,40 +28,28 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match");
|
throw new InvalidOperationException("Cannot create UrlRewriteRule without action and match");
|
||||||
}
|
}
|
||||||
var rule = new UrlRewriteRule();
|
|
||||||
rule.Action = _action;
|
return new UrlRewriteRule(Name, _initialMatch, _conditions, _action);
|
||||||
rule.Conditions = _conditions;
|
|
||||||
rule.InitialMatch = _initialMatch;
|
|
||||||
rule.Name = Name;
|
|
||||||
return rule;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddUrlAction(Pattern url, ActionType actionType = ActionType.None, bool appendQueryString = true, bool stopProcessing = false, int statusCode = StatusCodes.Status301MovedPermanently)
|
public void AddUrlAction(
|
||||||
|
Pattern url,
|
||||||
|
ActionType actionType = ActionType.None,
|
||||||
|
bool appendQueryString = true,
|
||||||
|
bool stopProcessing = false,
|
||||||
|
int statusCode = StatusCodes.Status301MovedPermanently)
|
||||||
{
|
{
|
||||||
switch (actionType)
|
switch (actionType)
|
||||||
{
|
{
|
||||||
case ActionType.None:
|
case ActionType.None:
|
||||||
_action = new VoidAction(stopProcessing ? RuleResult.StopRules : RuleResult.Continue);
|
_action = new VoidAction(stopProcessing ? RuleResult.StopRules : RuleResult.Continue);
|
||||||
break;
|
break;
|
||||||
case ActionType.Rewrite:
|
case ActionType.Rewrite:
|
||||||
if (appendQueryString)
|
_action = new RewriteAction(stopProcessing ? RuleTermination.StopRules : RuleTermination.Continue,
|
||||||
{
|
url, clearQuery: !appendQueryString);
|
||||||
_action = new RewriteAction(stopProcessing ? RuleTerminiation.StopRules : RuleTerminiation.Continue, url, clearQuery: false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_action = new RewriteAction(stopProcessing ? RuleTerminiation.StopRules : RuleTerminiation.Continue, url, clearQuery: true);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ActionType.Redirect:
|
case ActionType.Redirect:
|
||||||
if (appendQueryString)
|
_action = new RedirectAction(statusCode, url, appendQueryString);
|
||||||
{
|
|
||||||
_action = new RedirectAction(statusCode, url);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_action = new RedirectClearQueryAction(statusCode, url);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ActionType.AbortRequest:
|
case ActionType.AbortRequest:
|
||||||
throw new NotImplementedException("Abort Requests are not supported");
|
throw new NotImplementedException("Abort Requests are not supported");
|
||||||
|
|
@ -85,14 +73,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var regex = new Regex(input, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout);
|
var regex = new Regex(input, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout);
|
||||||
_initialMatch = new RegexMatch(regex, negate);
|
_initialMatch = new RegexMatch(regex, negate);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatternSyntax.WildCard:
|
case PatternSyntax.WildCard:
|
||||||
throw new NotImplementedException("Wildcard syntax is not supported");
|
throw new NotImplementedException("Wildcard syntax is not supported");
|
||||||
case PatternSyntax.ExactMatch:
|
case PatternSyntax.ExactMatch:
|
||||||
_initialMatch = new ExactMatch(ignoreCase, input, negate);
|
_initialMatch = new ExactMatch(ignoreCase, input, negate);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,27 +106,23 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
Regex regex = null;
|
var regex = new Regex(
|
||||||
if (ignoreCase)
|
pattern,
|
||||||
{
|
ignoreCase ? RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase :
|
||||||
regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
|
RegexOptions.CultureInvariant | RegexOptions.Compiled,
|
||||||
}
|
RegexTimeout);
|
||||||
else
|
|
||||||
{
|
_conditions.Add(new Condition { Input = input, Match = new RegexMatch(regex, negate), OrNext = _matchAny });
|
||||||
regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
_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), OrNext = _matchAny });
|
_conditions.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), OrNext = _matchAny });
|
_conditions.Add(new Condition { Input = input, Match = new IsFileMatch(negate), OrNext = _matchAny });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -153,7 +137,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), OrNext = _matchAny });
|
_conditions.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");
|
||||||
|
|
@ -162,10 +146,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||||
|
|
||||||
public void AddUrlConditions(LogicalGrouping logicalGrouping, bool trackingAllCaptures)
|
public void AddUrlConditions(LogicalGrouping logicalGrouping, bool trackingAllCaptures)
|
||||||
{
|
{
|
||||||
var conditions = new Conditions();
|
_conditions = new List<Condition>();
|
||||||
conditions.ConditionList = new List<Condition>();
|
|
||||||
_matchAny = logicalGrouping == LogicalGrouping.MatchAny;
|
_matchAny = logicalGrouping == LogicalGrouping.MatchAny;
|
||||||
_conditions = conditions;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,34 @@
|
||||||
// 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.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
{
|
{
|
||||||
public class UrlRewriteRule : Rule
|
public class UrlRewriteRule : Rule
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; }
|
||||||
public bool Enabled { get; set; } = true;
|
public UrlMatch InitialMatch { get; }
|
||||||
public UrlMatch InitialMatch { get; set; }
|
public IList<Condition> Conditions { get; }
|
||||||
public Conditions Conditions { get; set; }
|
public UrlAction Action { get; }
|
||||||
public UrlAction Action { get; set; }
|
|
||||||
|
public UrlRewriteRule(string name,
|
||||||
|
UrlMatch initialMatch,
|
||||||
|
IList<Condition> conditions,
|
||||||
|
UrlAction action)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
InitialMatch = initialMatch;
|
||||||
|
Conditions = conditions;
|
||||||
|
Action = action;
|
||||||
|
}
|
||||||
|
|
||||||
public override RuleResult ApplyRule(RewriteContext context)
|
public override RuleResult ApplyRule(RewriteContext context)
|
||||||
{
|
{
|
||||||
if (!Enabled)
|
|
||||||
{
|
|
||||||
return RuleResult.Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Due to the path string always having a leading slash,
|
// Due to the path string always having a leading slash,
|
||||||
// remove it from the path before regex comparison
|
// remove it from the path before regex comparison
|
||||||
// TODO may need to check if there is a leading slash and remove conditionally
|
|
||||||
var path = context.HttpContext.Request.Path;
|
var path = context.HttpContext.Request.Path;
|
||||||
MatchResults initMatchResults;
|
MatchResults initMatchResults;
|
||||||
if (path == PathString.Empty)
|
if (path == PathString.Empty)
|
||||||
|
|
@ -36,19 +42,22 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||||
|
|
||||||
if (!initMatchResults.Success)
|
if (!initMatchResults.Success)
|
||||||
{
|
{
|
||||||
|
context.Logger?.UrlRewriteDidNotMatchRule(Name);
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResults condMatchRes = null;
|
MatchResults condMatchRes = null;
|
||||||
if (Conditions != null)
|
if (Conditions != null)
|
||||||
{
|
{
|
||||||
condMatchRes = Conditions.Evaluate(context, initMatchResults);
|
condMatchRes = ConditionHelper.Evaluate(Conditions, context, initMatchResults);
|
||||||
if (!condMatchRes.Success)
|
if (!condMatchRes.Success)
|
||||||
{
|
{
|
||||||
|
context.Logger?.UrlRewriteDidNotMatchRule(Name);
|
||||||
return RuleResult.Continue;
|
return RuleResult.Continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.Logger?.UrlRewriteMatchedRule(Name);
|
||||||
// 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, initMatchResults, condMatchRes);
|
return Action.ApplyAction(context, initMatchResults, condMatchRes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Logging
|
||||||
|
{
|
||||||
|
internal static class RewriteMiddlewareLoggingExtensions
|
||||||
|
{
|
||||||
|
private static readonly Action<ILogger, Exception> _requestContinueResults;
|
||||||
|
private static readonly Action<ILogger, string, int, Exception> _requestResponseComplete;
|
||||||
|
private static readonly Action<ILogger, Exception> _requestStopRules;
|
||||||
|
private static readonly Action<ILogger, string, Exception> _urlRewriteDidNotMatchRule;
|
||||||
|
private static readonly Action<ILogger, string, Exception> _urlRewriteMatchedRule;
|
||||||
|
private static readonly Action<ILogger, Exception> _modRewriteDidNotMatchRule;
|
||||||
|
private static readonly Action<ILogger, Exception> _modRewriteMatchedRule;
|
||||||
|
|
||||||
|
static RewriteMiddlewareLoggingExtensions()
|
||||||
|
{
|
||||||
|
_requestContinueResults = LoggerMessage.Define(
|
||||||
|
LogLevel.Debug,
|
||||||
|
1,
|
||||||
|
"Request is continuing in applying rules.");
|
||||||
|
_requestResponseComplete = LoggerMessage.Define<string, int>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
2,
|
||||||
|
"Request is done processing, Location header '{Location}' with status code '{StatusCode}'.");
|
||||||
|
_requestStopRules = LoggerMessage.Define(
|
||||||
|
LogLevel.Debug,
|
||||||
|
3,
|
||||||
|
"Request is done applying rules.");
|
||||||
|
_urlRewriteDidNotMatchRule = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
4,
|
||||||
|
"Request did not match current rule '{Name}'.");
|
||||||
|
|
||||||
|
_urlRewriteMatchedRule = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
5,
|
||||||
|
"Request matched current UrlRewriteRule '{Name}'.");
|
||||||
|
|
||||||
|
_modRewriteDidNotMatchRule = LoggerMessage.Define(
|
||||||
|
LogLevel.Debug,
|
||||||
|
6,
|
||||||
|
"Request matched current ModRewriteRule.");
|
||||||
|
|
||||||
|
_modRewriteMatchedRule = LoggerMessage.Define(
|
||||||
|
LogLevel.Debug,
|
||||||
|
7,
|
||||||
|
"Request matched current ModRewriteRule.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RewriteMiddlewareRequestContinueResults(this ILogger logger)
|
||||||
|
{
|
||||||
|
_requestContinueResults(logger, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RewriteMiddlewareRequestResponseComplete(this ILogger logger, string location, int statusCode)
|
||||||
|
{
|
||||||
|
_requestResponseComplete(logger, location, statusCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RewriteMiddlewareRequestStopRules(this ILogger logger)
|
||||||
|
{
|
||||||
|
_requestStopRules(logger, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UrlRewriteDidNotMatchRule(this ILogger logger, string name)
|
||||||
|
{
|
||||||
|
_urlRewriteDidNotMatchRule(logger, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UrlRewriteMatchedRule(this ILogger logger, string name)
|
||||||
|
{
|
||||||
|
_urlRewriteMatchedRule(logger, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ModRewriteDidNotMatchRule(this ILogger logger)
|
||||||
|
{
|
||||||
|
_modRewriteDidNotMatchRule(logger, null);
|
||||||
|
}
|
||||||
|
public static void ModRewriteMatchedRule(this ILogger logger)
|
||||||
|
{
|
||||||
|
_modRewriteMatchedRule(logger, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,24 +8,24 @@ using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
public static class ModRewriteExtensions
|
public static class ModRewriteOptionsExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 Rewrite options.</param>
|
||||||
/// <param name="hostingEnv"></param>
|
/// <param name="hostingEnvironment">The Hosting Environment</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 RewriteOptions ImportFromModRewrite(this RewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
|
public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, IHostingEnvironment hostingEnvironment, string filePath)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hostingEnv == null)
|
if (hostingEnvironment == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(hostingEnv));
|
throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(filePath))
|
if (string.IsNullOrEmpty(filePath))
|
||||||
|
|
@ -33,18 +33,17 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
throw new ArgumentException(nameof(filePath));
|
throw new ArgumentException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
|
var path = Path.Combine(hostingEnvironment.ContentRootPath, filePath);
|
||||||
using (var stream = File.OpenRead(path))
|
using (var stream = File.OpenRead(path))
|
||||||
{
|
{
|
||||||
options.Rules.AddRange(new FileParser().Parse(new StreamReader(stream)));
|
return options.ImportFromModRewrite(new StreamReader(stream));
|
||||||
};
|
}
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 Rewrite options.</param>
|
||||||
/// <param name="reader">Text reader containing a stream of mod_rewrite rules.</param>
|
/// <param name="reader">Text reader containing a stream of mod_rewrite rules.</param>
|
||||||
public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, TextReader reader)
|
public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, TextReader reader)
|
||||||
{
|
{
|
||||||
|
|
@ -52,37 +51,19 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader == null)
|
if (reader == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(reader));
|
throw new ArgumentNullException(nameof(reader));
|
||||||
}
|
}
|
||||||
options.Rules.AddRange(new FileParser().Parse(reader));
|
var rules = new FileParser().Parse(reader);
|
||||||
|
|
||||||
|
foreach (var rule in rules)
|
||||||
|
{
|
||||||
|
options.Rules.Add(rule);
|
||||||
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a mod_rewrite rule to the current rules.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The UrlRewrite options.</param>
|
|
||||||
/// <param name="rule">The literal string of a mod_rewrite rule:
|
|
||||||
/// "RewriteRule Pattern Substitution [Flags]"</param>
|
|
||||||
public static RewriteOptions AddModRewriteRule(this RewriteOptions options, string rule)
|
|
||||||
{
|
|
||||||
if (options == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(rule));
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new RuleBuilder();
|
|
||||||
builder.AddRule(rule);
|
|
||||||
options.Rules.Add(builder.Build());
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,13 +9,13 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension methods for the <see cref="RewriteMiddleware"/>
|
/// Extension methods for the <see cref="RewriteMiddleware"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RewriteExtensions
|
public static class RewriteBuilderExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a given Url matches rules and conditions, and modifies the HttpContext on match.
|
/// Checks if a given Url matches rules and conditions, and modifies the HttpContext on match.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="app"></param>
|
/// <param name="app"></param>
|
||||||
/// <param name="options">Options for urlrewrite.</param>
|
/// <param name="options">Options for rewrite.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IApplicationBuilder UseRewriter(this IApplicationBuilder app, RewriteOptions options)
|
public static IApplicationBuilder UseRewriter(this IApplicationBuilder app, RewriteOptions options)
|
||||||
{
|
{
|
||||||
|
|
@ -4,16 +4,19 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The UrlRewrite Context contains the HttpContext of the request and the file provider to check conditions.
|
/// The UrlRewrite Context contains the HttpContext of the request, the file provider, and the logger.
|
||||||
|
/// There is also a shared string builder across the application of rules.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RewriteContext
|
public class RewriteContext
|
||||||
{
|
{
|
||||||
public HttpContext HttpContext { get; set; }
|
public HttpContext HttpContext { get; set; }
|
||||||
public IFileProvider FileProvider { get; set; }
|
public IFileProvider StaticFileProvider { get; set; }
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
// PERF: share the same string builder per request
|
// PERF: share the same string builder per request
|
||||||
internal StringBuilder Builder { get; set; } = new StringBuilder(64);
|
internal StringBuilder Builder { get; set; } = new StringBuilder(64);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Logging;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
|
|
@ -20,14 +23,20 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
private readonly RewriteOptions _options;
|
private readonly RewriteOptions _options;
|
||||||
private readonly IFileProvider _fileProvider;
|
private readonly IFileProvider _fileProvider;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of <see cref="RewriteMiddleware"/>
|
/// Creates a new instance of <see cref="RewriteMiddleware"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="next">The delegate representing the next middleware in the request pipeline.</param>
|
/// <param name="next">The delegate representing the next middleware in the request pipeline.</param>
|
||||||
/// <param name="hostingEnv">The Hosting Environment.</param>
|
/// <param name="hostingEnvironment">The Hosting Environment.</param>
|
||||||
|
/// <param name="loggerFactory">The Logger Factory.</param>
|
||||||
/// <param name="options">The middleware options, containing the rules to apply.</param>
|
/// <param name="options">The middleware options, containing the rules to apply.</param>
|
||||||
public RewriteMiddleware(RequestDelegate next, IHostingEnvironment hostingEnv, RewriteOptions options)
|
public RewriteMiddleware(
|
||||||
|
RequestDelegate next,
|
||||||
|
IHostingEnvironment hostingEnvironment,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
RewriteOptions options)
|
||||||
{
|
{
|
||||||
if (next == null)
|
if (next == null)
|
||||||
{
|
{
|
||||||
|
|
@ -41,7 +50,8 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
|
|
||||||
_next = next;
|
_next = next;
|
||||||
_options = options;
|
_options = options;
|
||||||
_fileProvider = _options.FileProvider ?? hostingEnv.WebRootFileProvider;
|
_fileProvider = _options.StaticFileProvider ?? hostingEnvironment.WebRootFileProvider;
|
||||||
|
_logger = loggerFactory.CreateLogger<RewriteMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -55,19 +65,27 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
var urlContext = new RewriteContext { HttpContext = context, FileProvider = _fileProvider };
|
var urlContext = new RewriteContext {
|
||||||
|
HttpContext = context,
|
||||||
|
StaticFileProvider = _fileProvider,
|
||||||
|
Logger = _logger
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var rule in _options.Rules)
|
foreach (var rule in _options.Rules)
|
||||||
{
|
{
|
||||||
// Apply the rule
|
|
||||||
var result = rule.ApplyRule(urlContext);
|
var result = rule.ApplyRule(urlContext);
|
||||||
switch (result.Result)
|
switch (result.Result)
|
||||||
{
|
{
|
||||||
case RuleTerminiation.Continue:
|
case RuleTermination.Continue:
|
||||||
// Explicitly show that we continue executing rules
|
_logger.RewriteMiddlewareRequestContinueResults();
|
||||||
break;
|
break;
|
||||||
case RuleTerminiation.ResponseComplete:
|
case RuleTermination.ResponseComplete:
|
||||||
|
_logger.RewriteMiddlewareRequestResponseComplete(
|
||||||
|
urlContext.HttpContext.Response.Headers[HeaderNames.Location],
|
||||||
|
urlContext.HttpContext.Response.StatusCode);
|
||||||
return CompletedTask;
|
return CompletedTask;
|
||||||
case RuleTerminiation.StopRules:
|
case RuleTermination.StopRules:
|
||||||
|
_logger.RewriteMiddlewareRequestStopRules();
|
||||||
return _next(context);
|
return _next(context);
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException($"Invalid rule termination {result}");
|
throw new ArgumentOutOfRangeException($"Invalid rule termination {result}");
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// 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 Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
|
|
@ -12,10 +11,8 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RewriteOptions
|
public class RewriteOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
// TODO doc comments
|
||||||
/// The ordered list of rules to apply to the context.
|
public IList<Rule> Rules { get; } = new List<Rule>();
|
||||||
/// </summary>
|
public IFileProvider StaticFileProvider { get; set; }
|
||||||
public List<Rule> Rules { get; set; } = new List<Rule>();
|
|
||||||
public IFileProvider FileProvider { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
// 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.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.CodeRules;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The builder to a list of rules for <see cref="RewriteOptions"/> and <see cref="RewriteMiddleware"/>
|
||||||
|
/// </summary>
|
||||||
|
public static class RewriteOptionsExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a rule to the current rules.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The UrlRewrite options.</param>
|
||||||
|
/// <param name="rule">A rule to be added to the current rules.</param>
|
||||||
|
/// <returns>The Rewrite options.</returns>
|
||||||
|
public static RewriteOptions Add(this RewriteOptions options, Rule rule)
|
||||||
|
{
|
||||||
|
options.Rules.Add(rule);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a rule to the current rules.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="applyRule">A Func that checks and applies the rule.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static RewriteOptions Add(this RewriteOptions options, Func<RewriteContext, RuleResult> applyRule)
|
||||||
|
{
|
||||||
|
options.Rules.Add(new DelegateRule(applyRule));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rewrites the path if the regex matches the HttpContext's PathString
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="regex">The regex string to compare with.</param>
|
||||||
|
/// <param name="urlPattern">If the regex matches, what to replace HttpContext with.</param>
|
||||||
|
/// <returns>The Rewrite options.</returns>
|
||||||
|
public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string urlPattern)
|
||||||
|
{
|
||||||
|
return Rewrite(options, regex, urlPattern, stopProcessing: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rewrites the path if the regex matches the HttpContext's PathString
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="regex">The regex string to compare with.</param>
|
||||||
|
/// <param name="urlPattern">If the regex matches, what to replace the uri with.</param>
|
||||||
|
/// <param name="stopProcessing">If the regex matches, conditionally stop processing other rules.</param>
|
||||||
|
/// <returns>The Rewrite options.</returns>
|
||||||
|
public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string urlPattern, bool stopProcessing)
|
||||||
|
{
|
||||||
|
var builder = new UrlRewriteRuleBuilder();
|
||||||
|
var pattern = new InputParser().ParseInputString(urlPattern);
|
||||||
|
|
||||||
|
builder.AddUrlMatch(regex);
|
||||||
|
builder.AddUrlAction(pattern, actionType: ActionType.Rewrite, stopProcessing: stopProcessing);
|
||||||
|
options.Rules.Add(builder.Build());
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirect the request if the regex matches the HttpContext's PathString
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="regex">The regex string to compare with.</param>
|
||||||
|
/// <param name="urlPattern">If the regex matches, what to replace the uri with.</param>
|
||||||
|
/// <returns>The Rewrite options.</returns>
|
||||||
|
public static RewriteOptions Redirect(this RewriteOptions options, string regex, string urlPattern)
|
||||||
|
{
|
||||||
|
return Redirect(options, regex, urlPattern, statusCode: 302);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirect the request if the regex matches the HttpContext's PathString
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="regex">The regex string to compare with.</param>
|
||||||
|
/// <param name="urlPattern">If the regex matches, what to replace the uri with.</param>
|
||||||
|
/// <param name="statusCode">The status code to add to the response.</param>
|
||||||
|
/// <returns>The Rewrite options.</returns>
|
||||||
|
public static RewriteOptions Redirect(this RewriteOptions options, string regex, string urlPattern, int statusCode)
|
||||||
|
{
|
||||||
|
var builder = new UrlRewriteRuleBuilder();
|
||||||
|
var pattern = new InputParser().ParseInputString(urlPattern);
|
||||||
|
|
||||||
|
builder.AddUrlMatch(regex);
|
||||||
|
builder.AddUrlAction(pattern, actionType: ActionType.Redirect, stopProcessing: false);
|
||||||
|
options.Rules.Add(builder.Build());
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 301 overload
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirect a request to https if the incoming request is http
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
public static RewriteOptions RedirectToHttps(this RewriteOptions options)
|
||||||
|
{
|
||||||
|
return RedirectToHttps(options, statusCode: 302, sslPort: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirect a request to https if the incoming request is http
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="statusCode">The status code to add to the response.</param>
|
||||||
|
public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode)
|
||||||
|
{
|
||||||
|
return RedirectToHttps(options, statusCode, sslPort: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirect a request to https if the incoming request is http
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The Rewrite options.</param>
|
||||||
|
/// <param name="statusCode">The status code to add to the response.</param>
|
||||||
|
/// <param name="sslPort">The SSL port to add to the response.</param>
|
||||||
|
public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode, int? sslPort)
|
||||||
|
{
|
||||||
|
options.Rules.Add(new RedirectToHttpsRule { StatusCode = statusCode, SSLPort = sslPort });
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +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.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
|
// make this public and doc comements
|
||||||
public abstract class Rule
|
public abstract class Rule
|
||||||
{
|
{
|
||||||
public abstract RuleResult ApplyRule(RewriteContext context);
|
public abstract RuleResult ApplyRule(RewriteContext context);
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
// 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
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
public class RuleResult
|
public class RuleResult
|
||||||
{
|
{
|
||||||
public static RuleResult Continue = new RuleResult { Result = RuleTerminiation.Continue };
|
public static RuleResult Continue = new RuleResult { Result = RuleTermination.Continue };
|
||||||
public static RuleResult ResponseComplete = new RuleResult { Result = RuleTerminiation.ResponseComplete };
|
public static RuleResult ResponseComplete = new RuleResult { Result = RuleTermination.ResponseComplete };
|
||||||
public static RuleResult StopRules = new RuleResult { Result = RuleTerminiation.StopRules };
|
public static RuleResult StopRules = new RuleResult { Result = RuleTermination.StopRules };
|
||||||
|
|
||||||
public RuleTerminiation Result { get; set; }
|
public RuleTermination Result { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
public enum RuleTerminiation
|
public enum RuleTermination
|
||||||
{
|
{
|
||||||
Continue,
|
Continue,
|
||||||
ResponseComplete,
|
ResponseComplete,
|
||||||
|
|
@ -8,24 +8,24 @@ using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite
|
||||||
{
|
{
|
||||||
public static class UrlRewriteExtensions
|
public static class UrlRewriteOptionsExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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="hostingEnvironment"></param>
|
||||||
/// <param name="filePath">The path to the file containing urlrewrite rules.</param>
|
/// <param name="filePath">The path to the file containing urlrewrite rules.</param>
|
||||||
public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, IHostingEnvironment hostingEnv, string filePath)
|
public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, IHostingEnvironment hostingEnvironment, string filePath)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hostingEnv == null)
|
if (hostingEnvironment == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(hostingEnv));
|
throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(filePath))
|
if (string.IsNullOrEmpty(filePath))
|
||||||
|
|
@ -33,35 +33,36 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
throw new ArgumentException(nameof(filePath));
|
throw new ArgumentException(nameof(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = Path.Combine(hostingEnv.ContentRootPath, filePath);
|
var path = Path.Combine(hostingEnvironment.ContentRootPath, filePath);
|
||||||
using (var stream = File.OpenRead(path))
|
using (var stream = File.OpenRead(path))
|
||||||
{
|
{
|
||||||
options.Rules.AddRange(new FileParser().Parse(new StreamReader(stream)));
|
return ImportFromUrlRewrite(options, new StreamReader(stream));
|
||||||
};
|
}
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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="stream">The text reader stream.</param>
|
/// <param name="reader">The text reader stream.</param>
|
||||||
public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, TextReader stream)
|
public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, TextReader reader)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream == null)
|
if (reader == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(nameof(stream));
|
throw new ArgumentException(nameof(reader));
|
||||||
}
|
}
|
||||||
|
|
||||||
using (stream)
|
var rules = new UrlRewriteFileParser().Parse(reader);
|
||||||
|
|
||||||
|
foreach (var rule in rules)
|
||||||
{
|
{
|
||||||
options.Rules.AddRange(new FileParser().Parse(stream));
|
options.Rules.Add(rule);
|
||||||
};
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CheckRewritePath()
|
public async Task CheckRewritePath()
|
||||||
{
|
{
|
||||||
var options = new RewriteOptions().RewriteRule("(.*)", "http://example.com/{R:1}");
|
var options = new RewriteOptions().Rewrite("(.*)", "http://example.com/{R:1}");
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CheckRedirectPath()
|
public async Task CheckRedirectPath()
|
||||||
{
|
{
|
||||||
var options = new RewriteOptions().RedirectRule("(.*)","http://example.com/{R:1}", statusCode: 301);
|
var options = new RewriteOptions().Redirect("(.*)","http://example.com/{R:1}", statusCode: 301);
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
@ -52,25 +52,6 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules
|
||||||
Assert.Equal(response.Headers.Location.OriginalString, "http://example.com/foo");
|
Assert.Equal(response.Headers.Location.OriginalString, "http://example.com/foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task CheckRewriteToHttps()
|
|
||||||
{
|
|
||||||
var options = new RewriteOptions().RewriteToHttps();
|
|
||||||
var builder = new WebHostBuilder()
|
|
||||||
.Configure(app =>
|
|
||||||
{
|
|
||||||
app.UseRewriter(options);
|
|
||||||
app.UseRewriter(options);
|
|
||||||
app.Run(context => context.Response.WriteAsync(
|
|
||||||
context.Request.Scheme));
|
|
||||||
});
|
|
||||||
var server = new TestServer(builder);
|
|
||||||
|
|
||||||
var response = await server.CreateClient().GetStringAsync(new Uri("http://example.com"));
|
|
||||||
|
|
||||||
Assert.Equal(response, "https");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CheckRedirectToHttps()
|
public async Task CheckRedirectToHttps()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Invoke_RewritePathWhenMatching()
|
public async Task Invoke_RewritePathWhenMatching()
|
||||||
{
|
{
|
||||||
var options = new RewriteOptions().AddModRewriteRule("RewriteRule /hey/(.*) /$1 ");
|
var options = new RewriteOptions().ImportFromModRewrite(new StringReader("RewriteRule /hey/(.*) /$1 "));
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
@ -34,8 +34,8 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Invoke_RewritePathTerminatesOnFirstSuccessOfRule()
|
public async Task Invoke_RewritePathTerminatesOnFirstSuccessOfRule()
|
||||||
{
|
{
|
||||||
var options = new RewriteOptions().AddModRewriteRule("RewriteRule /hey/(.*) /$1 [L]")
|
var options = new RewriteOptions().ImportFromModRewrite(new StringReader("RewriteRule /hey/(.*) /$1 [L]"))
|
||||||
.AddModRewriteRule("RewriteRule /hello /what");
|
.ImportFromModRewrite(new StringReader("RewriteRule /hello /what"));
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
@ -52,8 +52,8 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Invoke_RewritePathDoesNotTerminateOnFirstSuccessOfRule()
|
public async Task Invoke_RewritePathDoesNotTerminateOnFirstSuccessOfRule()
|
||||||
{
|
{
|
||||||
var options = new RewriteOptions().AddModRewriteRule("RewriteRule /hey/(.*) /$1")
|
var options = new RewriteOptions().ImportFromModRewrite(new StringReader("RewriteRule /hey/(.*) /$1"))
|
||||||
.AddModRewriteRule("RewriteRule /hello /what");
|
.ImportFromModRewrite(new StringReader("RewriteRule /hello /what"));
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite
|
namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite
|
||||||
{
|
{
|
||||||
public class TestStringParserTests
|
public class TestStringParserTests
|
||||||
{
|
{
|
||||||
|
|
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Rewrite
|
||||||
[InlineData(@"%a", "Cannot parse '%a' to integer at string index: '1'")] // invalid character after %
|
[InlineData(@"%a", "Cannot parse '%a' to integer at string index: '1'")] // invalid character after %
|
||||||
[InlineData(@"$a", "Cannot parse '$a' to integer at string index: '1'")] // invalid character after $
|
[InlineData(@"$a", "Cannot parse '$a' to integer at string index: '1'")] // invalid character after $
|
||||||
[InlineData(@"%{asdf", "Missing close brace for parameter at string index: '6'")] // no closing } with characters
|
[InlineData(@"%{asdf", "Missing close brace for parameter at string index: '6'")] // no closing } with characters
|
||||||
public void ConditionParser_Bad(string testString, string expected)
|
public void ConditionParser_InvalidInput(string testString, string expected)
|
||||||
{
|
{
|
||||||
var ex = Assert.Throws<FormatException>(() => new TestStringParser().Parse(testString));
|
var ex = Assert.Throws<FormatException>(() => new TestStringParser().Parse(testString));
|
||||||
Assert.Equal(ex.Message, expected);
|
Assert.Equal(ex.Message, expected);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class ConditionMatchSegmentTests
|
||||||
|
{
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1, "foo")]
|
||||||
|
[InlineData(2, "bar")]
|
||||||
|
[InlineData(3, "baz")]
|
||||||
|
public void ConditionMatch_AssertBackreferencesObtainsCorrectValue(int index, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var condMatch = CreateTestMatch();
|
||||||
|
var segment = new ConditionMatchSegment(index);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segment.Evaluate(null, null, condMatch);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MatchResults CreateTestMatch()
|
||||||
|
{
|
||||||
|
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
|
||||||
|
return new MatchResults {BackReference = match.Groups, Success = match.Success};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class DateTimeSegmentTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("TIME_YEAR")]
|
||||||
|
[InlineData("TIME_MON")]
|
||||||
|
[InlineData("TIME_DAY")]
|
||||||
|
[InlineData("TIME_HOUR")]
|
||||||
|
[InlineData("TIME_MIN")]
|
||||||
|
[InlineData("TIME_SEC")]
|
||||||
|
[InlineData("TIME_WDAY")]
|
||||||
|
[InlineData("TIME")]
|
||||||
|
public void DateTime_AssertDoesntThrowOnCheckOfSegment(string input)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segment = new DateTimeSegment(input);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segment.Evaluate(null, null, null);
|
||||||
|
|
||||||
|
// TODO testing dates is hard, could use moq
|
||||||
|
// currently just assert that the segment doesn't throw.
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("foo", "Unsupported segment: 'foo'")]
|
||||||
|
[InlineData("wow", "Unsupported segment: 'wow'")]
|
||||||
|
public void DateTime_AssertThrowsOnInvalidInput(string input, string expected)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Act And Assert
|
||||||
|
var ex = Assert.Throws<FormatException>(() => new DateTimeSegment(input));
|
||||||
|
Assert.Equal(ex.Message, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class HeaderSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void HeaderSegment_AssertGettingWithHeaderReturnsCorrectValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
|
||||||
|
context.HttpContext.Request.Headers[HeaderNames.Location] = "foo";
|
||||||
|
var segment = new HeaderSegment(HeaderNames.Location);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segment.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("foo", results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void HeaderSegment_AssertGettingANonExistantHeaderReturnsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
var segment = new HeaderSegment(HeaderNames.Location);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segment.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class IsHttpsModSegmentTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http", "off")]
|
||||||
|
[InlineData("https", "on")]
|
||||||
|
public void IsHttps_AssertCorrectBehaviorWhenProvidedHttpContext(string input, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new IsHttpsModSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.Scheme = input;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class IsHttpsSegmentTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http", "OFF")]
|
||||||
|
[InlineData("https", "ON")]
|
||||||
|
public void IsHttps_AssertCorrectBehaviorWhenProvidedHttpContext(string input, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new IsHttpsSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.Scheme = input;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class IsIPV6SegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IsIPv6_AssertNullRemoteIpAddressReportsCorrectValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new IsIPV6Segment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.RemoteIpAddress = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("off", results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsIPv6_AssertCorrectBehaviorWhenIPv6IsUsed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new IsIPV6Segment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.RemoteIpAddress = IPAddress.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("on", results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsIPv6_AssertCorrectBehaviorWhenIPv4IsUsed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new IsIPV6Segment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.RemoteIpAddress = IPAddress.Parse("20.30.40.50");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("off", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class LiteralSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LiteralSegment_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new LiteralSegment("foo");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(null, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("foo", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class LocalAddressSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LocalAddress_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new LocalAddressSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.LocalIpAddress = IPAddress.Parse("20.30.40.50");
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("20.30.40.50", results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LocalAddress_AssertNullLocalIpAddressReturnsNull()
|
||||||
|
{
|
||||||
|
var segement = new LocalAddressSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.LocalIpAddress = null;
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null( results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class LocalPortSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LocalPortSegment_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new LocalPortSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.LocalPort = 800;
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("800", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class QueryStringSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void QueryString_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new QueryStringSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.QueryString = new QueryString("?hey=1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("?hey=1", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class RemoteAddressSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void RemoteAddress_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new RemoteAddressSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.RemoteIpAddress = IPAddress.Parse("20.30.40.50");
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("20.30.40.50", results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoteAddress_AssertNullLocalIpAddressReturnsNull()
|
||||||
|
{
|
||||||
|
var segement = new RemoteAddressSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.RemoteIpAddress = null;
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class RemotePortSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void RemotePort_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new RemotePortSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Connection.RemotePort = 800;
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("800", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class RequestFilenameSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void RequestFilename_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new RequestFileNameSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.Path = new PathString("/foo/bar");
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("/foo/bar", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class RequestMethodSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void RequestMethod_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new RequestMethodSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.Method = "GET";
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("GET", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class RuleMatchSegmentTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1, "foo")]
|
||||||
|
[InlineData(2, "bar")]
|
||||||
|
[InlineData(3, "baz")]
|
||||||
|
public void RuleMatch_AssertBackreferencesObtainsCorrectValue(int index, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var ruleMatch = CreateTestMatch();
|
||||||
|
var segment = new RuleMatchSegment(index);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segment.Evaluate(null, ruleMatch, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MatchResults CreateTestMatch()
|
||||||
|
{
|
||||||
|
var match = Regex.Match("foo/bar/baz", "(.*)/(.*)/(.*)");
|
||||||
|
return new MatchResults { BackReference = match.Groups, Success = match.Success };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class SchemeSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void SchemeSegment_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new SchemeSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.Scheme = "http";
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("http", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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.Features;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class ServerProtocolSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ServerProtocol_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new ServerProtocolSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Features.Set<IHttpRequestFeature>(new HttpRequestFeature { Protocol = "http" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("http", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class ToLowerSegmentTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Hello", "hello")]
|
||||||
|
[InlineData("WHAT", "what")]
|
||||||
|
[InlineData("hey", "hey")]
|
||||||
|
public void ToLower_AssertLowerCaseWorksAppropriately(string input, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var pattern = new Pattern(new List<PatternSegment>());
|
||||||
|
pattern.PatternSegments.Add(new LiteralSegment(input));
|
||||||
|
var segement = new ToLowerSegment(pattern);
|
||||||
|
var context = new RewriteContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class UrlEncodeSegmentTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(" ", "%20")]
|
||||||
|
[InlineData("x&y", "x%26y")]
|
||||||
|
[InlineData("hey", "hey")]
|
||||||
|
public void ToLower_AssertLowerCaseWorksAppropriately(string input, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var pattern = new Pattern(new List<PatternSegment>());
|
||||||
|
pattern.PatternSegments.Add(new LiteralSegment(input));
|
||||||
|
var segement = new UrlEncodeSegment(pattern);
|
||||||
|
var context = new RewriteContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.PatternSegments;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments
|
||||||
|
{
|
||||||
|
public class UrlSegmentTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void LocalPortSegment_AssertSegmentIsCorrect()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var segement = new UrlSegment();
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
context.HttpContext.Request.Path = new PathString("/foo/bar");
|
||||||
|
// Act
|
||||||
|
var results = segement.Evaluate(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("/foo/bar", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlActions
|
||||||
|
{
|
||||||
|
public class ForbiddenActionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Forbidden_Verify403IsInStatusCode()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new RewriteContext {HttpContext = new DefaultHttpContext()};
|
||||||
|
var action = new ForbiddenAction();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = action.ApplyAction(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(results.Result, RuleTermination.ResponseComplete);
|
||||||
|
Assert.Equal(context.HttpContext.Response.StatusCode, StatusCodes.Status403Forbidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||||
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlActions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlActions
|
||||||
|
{
|
||||||
|
public class GoneActionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Gone_Verify410IsInStatusCode()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new RewriteContext { HttpContext = new DefaultHttpContext() };
|
||||||
|
var action = new GoneAction();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = action.ApplyAction(context, null, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(results.Result, RuleTermination.ResponseComplete);
|
||||||
|
Assert.Equal(context.HttpContext.Response.StatusCode, StatusCodes.Status410Gone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,13 +29,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var expected = new List<UrlRewriteRule>();
|
var expected = new List<UrlRewriteRule>();
|
||||||
expected.Add(CreateTestRule(new List<Condition>(),
|
expected.Add(CreateTestRule(new List<Condition>(),
|
||||||
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
name: "Rewrite to article.aspx",
|
name: "Rewrite to article.aspx",
|
||||||
actionType: ActionType.Rewrite,
|
actionType: ActionType.Rewrite,
|
||||||
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var res = new FileParser().Parse(new StringReader(xml));
|
var res = new UrlRewriteFileParser().Parse(new StringReader(xml));
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
AssertUrlRewriteRuleEquality(res, expected);
|
AssertUrlRewriteRuleEquality(res, expected);
|
||||||
|
|
@ -66,13 +66,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var expected = new List<UrlRewriteRule>();
|
var expected = new List<UrlRewriteRule>();
|
||||||
expected.Add(CreateTestRule(condList,
|
expected.Add(CreateTestRule(condList,
|
||||||
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
name: "Rewrite to article.aspx",
|
name: "Rewrite to article.aspx",
|
||||||
actionType: ActionType.Rewrite,
|
actionType: ActionType.Rewrite,
|
||||||
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var res = new FileParser().Parse(new StringReader(xml));
|
var res = new UrlRewriteFileParser().Parse(new StringReader(xml));
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
AssertUrlRewriteRuleEquality(res, expected);
|
AssertUrlRewriteRuleEquality(res, expected);
|
||||||
|
|
@ -110,18 +110,18 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var expected = new List<UrlRewriteRule>();
|
var expected = new List<UrlRewriteRule>();
|
||||||
expected.Add(CreateTestRule(condList,
|
expected.Add(CreateTestRule(condList,
|
||||||
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
name: "Rewrite to article.aspx",
|
name: "Rewrite to article.aspx",
|
||||||
actionType: ActionType.Rewrite,
|
actionType: ActionType.Rewrite,
|
||||||
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
expected.Add(CreateTestRule(condList,
|
expected.Add(CreateTestRule(condList,
|
||||||
Url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
url: "^article/([0-9]+)/([_0-9a-z-]+)",
|
||||||
name: "Rewrite to another article.aspx",
|
name: "Rewrite to another article.aspx",
|
||||||
actionType: ActionType.Rewrite,
|
actionType: ActionType.Rewrite,
|
||||||
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
pattern: "article.aspx?id={R:1}&title={R:2}"));
|
||||||
|
|
||||||
// act
|
// act
|
||||||
var res = new FileParser().Parse(new StringReader(xml));
|
var res = new UrlRewriteFileParser().Parse(new StringReader(xml));
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
AssertUrlRewriteRuleEquality(res, expected);
|
AssertUrlRewriteRuleEquality(res, expected);
|
||||||
|
|
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
bool enabled = true,
|
bool enabled = true,
|
||||||
PatternSyntax patternSyntax = PatternSyntax.ECMAScript,
|
PatternSyntax patternSyntax = PatternSyntax.ECMAScript,
|
||||||
bool stopProcessing = false,
|
bool stopProcessing = false,
|
||||||
string Url = "",
|
string url = "",
|
||||||
bool ignoreCase = true,
|
bool ignoreCase = true,
|
||||||
bool negate = false,
|
bool negate = false,
|
||||||
ActionType actionType = ActionType.None,
|
ActionType actionType = ActionType.None,
|
||||||
|
|
@ -145,22 +145,12 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
RedirectType redirectType = RedirectType.Permanent
|
RedirectType redirectType = RedirectType.Permanent
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return new UrlRewriteRule
|
return new UrlRewriteRule(name, new RegexMatch(new Regex("^OFF$"), false), conditions,
|
||||||
{
|
new RewriteAction(RuleTermination.Continue, new InputParser().ParseInputString(url), clearQuery: false));
|
||||||
Action = new RewriteAction(RuleTerminiation.Continue, new InputParser().ParseInputString(Url), clearQuery: false),
|
|
||||||
Name = name,
|
|
||||||
Enabled = enabled,
|
|
||||||
InitialMatch = new RegexMatch(new Regex("^OFF$"), false)
|
|
||||||
{
|
|
||||||
},
|
|
||||||
Conditions = new Conditions
|
|
||||||
{
|
|
||||||
ConditionList = conditions
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AssertUrlRewriteRuleEquality(List<UrlRewriteRule> actual, List<UrlRewriteRule> expected)
|
// TODO make rules comparable?
|
||||||
|
private void AssertUrlRewriteRuleEquality(IList<UrlRewriteRule> actual, IList<UrlRewriteRule> expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(actual.Count, expected.Count);
|
Assert.Equal(actual.Count, expected.Count);
|
||||||
for (var i = 0; i < actual.Count; i++)
|
for (var i = 0; i < actual.Count; i++)
|
||||||
|
|
@ -169,23 +159,22 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
var r2 = expected[i];
|
var r2 = expected[i];
|
||||||
|
|
||||||
Assert.Equal(r1.Name, r2.Name);
|
Assert.Equal(r1.Name, r2.Name);
|
||||||
Assert.Equal(r1.Enabled, r2.Enabled);
|
|
||||||
|
|
||||||
if (r1.Conditions == null)
|
if (r1.Conditions == null)
|
||||||
{
|
{
|
||||||
Assert.Equal(r2.Conditions.ConditionList.Count, 0);
|
Assert.Equal(r2.Conditions.Count, 0);
|
||||||
}
|
}
|
||||||
else if (r2.Conditions == null)
|
else if (r2.Conditions == null)
|
||||||
{
|
{
|
||||||
Assert.Equal(r1.Conditions.ConditionList.Count, 0);
|
Assert.Equal(r1.Conditions.Count, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Assert.Equal(r1.Conditions.ConditionList.Count, r2.Conditions.ConditionList.Count);
|
Assert.Equal(r1.Conditions.Count, r2.Conditions.Count);
|
||||||
for (var j = 0; j < r1.Conditions.ConditionList.Count; j++)
|
for (var j = 0; j < r1.Conditions.Count; j++)
|
||||||
{
|
{
|
||||||
var c1 = r1.Conditions.ConditionList[j];
|
var c1 = r1.Conditions[j];
|
||||||
var c2 = r2.Conditions.ConditionList[j];
|
var c2 = r2.Conditions[j];
|
||||||
Assert.Equal(c1.Input.PatternSegments.Count, c2.Input.PatternSegments.Count);
|
Assert.Equal(c1.Input.PatternSegments.Count, c2.Input.PatternSegments.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
public void ThrowFormatExceptionWithCorrectMessage(string input, string expected)
|
public void ThrowFormatExceptionWithCorrectMessage(string input, string expected)
|
||||||
{
|
{
|
||||||
// Arrange, Act, Assert
|
// Arrange, Act, Assert
|
||||||
var ex = Assert.Throws<FormatException>(() => new FileParser().Parse(new StringReader(input)));
|
var ex = Assert.Throws<FormatException>(() => new UrlRewriteFileParser().Parse(new StringReader(input)));
|
||||||
Assert.Equal(ex.Message, expected);
|
Assert.Equal(ex.Message, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
{
|
{
|
||||||
|
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
return new RewriteContext { HttpContext = context, FileProvider = null };
|
return new RewriteContext { HttpContext = context, StaticFileProvider = null };
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchResults CreateTestRuleMatch()
|
private MatchResults CreateTestRuleMatch()
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var response = await server.CreateClient().GetAsync("article/10/hey");
|
var response = await server.CreateClient().GetAsync("article/10/hey");
|
||||||
|
|
||||||
Assert.Equal(response.Headers.Location.OriginalString, "article.aspx?id=10&title=hey");
|
Assert.Equal(response.Headers.Location.OriginalString, "/article.aspx?id=10&title=hey");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var response = await server.CreateClient().GetAsync("HElLo");
|
var response = await server.CreateClient().GetAsync("HElLo");
|
||||||
|
|
||||||
Assert.Equal(response.Headers.Location.OriginalString, "hello");
|
Assert.Equal(response.Headers.Location.OriginalString, "/hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var response = await server.CreateClient().GetAsync("hey/hello/");
|
var response = await server.CreateClient().GetAsync("hey/hello/");
|
||||||
|
|
||||||
Assert.Equal(response.Headers.Location.OriginalString, "hey/hello");
|
Assert.Equal(response.Headers.Location.OriginalString, "/hey/hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -166,7 +166,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
||||||
var response = await server.CreateClient().GetAsync("hey/hello");
|
var response = await server.CreateClient().GetAsync("hey/hello");
|
||||||
|
|
||||||
Assert.Equal(response.Headers.Location.OriginalString, "hey/hello/");
|
Assert.Equal(response.Headers.Location.OriginalString, "/hey/hello/");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -1,15 +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;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
|
||||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
|
|
@ -28,11 +23,11 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
</rule>
|
</rule>
|
||||||
</rules>
|
</rules>
|
||||||
</rewrite>");
|
</rewrite>");
|
||||||
var rules = new FileParser().Parse(xml);
|
var rules = new UrlRewriteFileParser().Parse(xml);
|
||||||
|
|
||||||
Assert.Equal(rules.Count, 1);
|
Assert.Equal(rules.Count, 1);
|
||||||
var ruleResults = rules.FirstOrDefault().ApplyRule(new RewriteContext {HttpContext = new DefaultHttpContext()});
|
var ruleResults = rules.FirstOrDefault().ApplyRule(new RewriteContext {HttpContext = new DefaultHttpContext()});
|
||||||
Assert.Equal(ruleResults.Result, RuleTerminiation.StopRules);
|
Assert.Equal(ruleResults.Result, RuleTermination.StopRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -46,11 +41,11 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
||||||
</rule>
|
</rule>
|
||||||
</rules>
|
</rules>
|
||||||
</rewrite>");
|
</rewrite>");
|
||||||
var rules = new FileParser().Parse(xml);
|
var rules = new UrlRewriteFileParser().Parse(xml);
|
||||||
|
|
||||||
Assert.Equal(rules.Count, 1);
|
Assert.Equal(rules.Count, 1);
|
||||||
var ruleResults = rules.FirstOrDefault().ApplyRule(new RewriteContext { HttpContext = new DefaultHttpContext() });
|
var ruleResults = rules.FirstOrDefault().ApplyRule(new RewriteContext { HttpContext = new DefaultHttpContext() });
|
||||||
Assert.Equal(ruleResults.Result, RuleTerminiation.Continue);
|
Assert.Equal(ruleResults.Result, RuleTermination.Continue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue