Update structure of code rules, refactor to IISRewrite
This commit is contained in:
parent
52c7a94db3
commit
903949cc74
|
|
@ -5,6 +5,9 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
|||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.HttpOverrides", "src\Microsoft.AspNetCore.HttpOverrides\Microsoft.AspNetCore.HttpOverrides.xproj", "{517308C3-B477-4B01-B461-CAB9C10B6928}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5076D28-FA7E-4606-9410-FEDD0D603527}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
global.json = global.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8437B0F3-3894-4828-A945-A9187F37631D}"
|
||||
EndProject
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Rewrite;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||
|
||||
namespace RewriteSample
|
||||
{
|
||||
|
|
@ -13,9 +14,29 @@ namespace RewriteSample
|
|||
{
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment hostingEnv)
|
||||
{
|
||||
// Four main use cases for Rewrite Options.
|
||||
// 1. Importing from a UrlRewrite file, which are IIS Rewrite rules.
|
||||
// This file is in xml format, starting with the <rewrite> tag.
|
||||
// 2. Importing from a mod_rewrite file, which are mod_rewrite rules.
|
||||
// This file is in standard mod_rewrite format which only contains rewrite information.
|
||||
// 3. Inline rules in code, where you can specify rules such as rewrites and redirects
|
||||
// based on certain conditions. Ex: RedirectToHttps will check if the request is https,
|
||||
// else it will redirect the request with https.
|
||||
// 4. Functional rules. If a user has a very specific function they would like to implement
|
||||
// (ex StringReplace) that are easy to implement in code, they can do so by calling
|
||||
// AddFunctionalRule(Func);
|
||||
// TODO make this startup do something useful.
|
||||
app.UseRewriter(new RewriteOptions()
|
||||
.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(context.Request.Path));
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.CodeRules;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite
|
||||
{
|
||||
|
|
@ -35,69 +36,69 @@ namespace Microsoft.AspNetCore.Rewrite
|
|||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a rewrite path rule.
|
||||
/// </summary>
|
||||
/// <param name="options">The Url rewrite options.</param>
|
||||
/// <param name="regex">The string regex pattern to compare against the http context.</param>
|
||||
/// <param name="newPath">The string to replace the path with (with capture parameters).</param>
|
||||
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
|
||||
/// <returns></returns>
|
||||
public static RewriteOptions RewritePath(this RewriteOptions options, string regex, string newPath, bool stopRewriteOnSuccess = false)
|
||||
public static RewriteOptions RewriteRule(this RewriteOptions options, string regex, string onMatch)
|
||||
{
|
||||
options.Rules.Add(new PathRule { MatchPattern = new Regex(regex, RegexOptions.Compiled, TimeSpan.FromMilliseconds(1)), OnMatch = newPath, OnCompletion = stopRewriteOnSuccess ? Transformation.TerminatingRewrite : Transformation.Rewrite });
|
||||
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 = InputParser.ParseInputString(onMatch);
|
||||
|
||||
builder.AddUrlMatch(regex);
|
||||
builder.AddUrlAction(pattern, actionType: ActionType.Rewrite, stopProcessing: stopProcessing);
|
||||
options.Rules.Add(builder.Build());
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewrite http to https.
|
||||
/// </summary>
|
||||
/// <param name="options">The Url rewrite options.</param>
|
||||
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
|
||||
/// <returns></returns>
|
||||
public static RewriteOptions RewriteScheme(this RewriteOptions options, bool stopRewriteOnSuccess = false)
|
||||
public static RewriteOptions RedirectRule(this RewriteOptions options, string regex, string onMatch, int statusCode)
|
||||
{
|
||||
options.Rules.Add(new SchemeRule {OnCompletion = stopRewriteOnSuccess ? Transformation.TerminatingRewrite : Transformation.Rewrite });
|
||||
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 = InputParser.ParseInputString(onMatch);
|
||||
|
||||
builder.AddUrlMatch(regex);
|
||||
builder.AddUrlAction(pattern, actionType: ActionType.Redirect, stopProcessing: stopProcessing);
|
||||
options.Rules.Add(builder.Build());
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirect a path to another path.
|
||||
/// </summary>
|
||||
/// <param name="options">The Url rewrite options.</param>
|
||||
/// <param name="regex">The string regex pattern to compare against the http context.</param>
|
||||
/// <param name="newPath">The string to replace the path with (with capture parameters).</param>
|
||||
/// <param name="stopRewriteOnSuccess">Whether or not to stop rewriting on success of rule.</param>
|
||||
/// <returns></returns>
|
||||
public static RewriteOptions RedirectPath(this RewriteOptions options, string regex, string newPath, bool stopRewriteOnSuccess = false)
|
||||
public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode)
|
||||
{
|
||||
options.Rules.Add(new PathRule { MatchPattern = new Regex(regex, RegexOptions.Compiled, TimeSpan.FromMilliseconds(1)), OnMatch = newPath, OnCompletion = Transformation.Redirect });
|
||||
return RedirectToHttps(options, statusCode, null);
|
||||
}
|
||||
|
||||
// TODO Don't do this, it doesn't work in all cases. Will refactor tonight/ tomorrow.
|
||||
public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode, int? sslPort)
|
||||
{
|
||||
options.Rules.Add(new RedirectToHttpsRule { StatusCode = statusCode, SSLPort = sslPort });
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirect http to https.
|
||||
/// </summary>
|
||||
/// <param name="options">The Url rewrite options.</param>
|
||||
/// <param name="sslPort">The port to redirect the scheme to.</param>
|
||||
/// <returns></returns>
|
||||
public static RewriteOptions RedirectScheme(this RewriteOptions options, int? sslPort)
|
||||
public static RewriteOptions RewriteToHttps(this RewriteOptions options)
|
||||
{
|
||||
return RewriteToHttps(options, sslPort: null, stopProcessing: false);
|
||||
}
|
||||
|
||||
public static RewriteOptions RewriteToHttps(this RewriteOptions options, int? sslPort)
|
||||
{
|
||||
options.Rules.Add(new SchemeRule { SSLPort = sslPort, OnCompletion = Transformation.Redirect });
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User generated rule to do a specific match on a path and what to do on success of the match.
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="onApplyRule"></param>
|
||||
/// <param name="transform"></param>
|
||||
/// <param name="description"></param>
|
||||
/// <returns></returns>
|
||||
public static RewriteOptions CustomRule(this RewriteOptions options, Func<RewriteContext, RuleResult> onApplyRule, Transformation transform, string description = null)
|
||||
public static RewriteOptions AddRule(this RewriteOptions options, Func<RewriteContext, RuleResult> rule)
|
||||
{
|
||||
options.Rules.Add(new FunctionalRule { OnApplyRule = onApplyRule, OnCompletion = transform});
|
||||
options.Rules.Add(new FunctionalRule { OnApplyRule = rule});
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||
{
|
||||
public class FunctionalRule : Rule
|
||||
{
|
||||
public Func<RewriteContext, RuleResult> OnApplyRule { get; set; }
|
||||
public Transformation OnCompletion { get; set; } = Transformation.Rewrite;
|
||||
public override RuleResult ApplyRule(RewriteContext context) => OnApplyRule(context);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,12 @@
|
|||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules
|
||||
{
|
||||
public class SchemeRule : Rule
|
||||
public class RedirectToHttpsRule : Rule
|
||||
{
|
||||
public int? SSLPort { get; set; }
|
||||
public Transformation OnCompletion { get; set; } = Transformation.Rewrite;
|
||||
public int StatusCode { get; set; }
|
||||
public override RuleResult ApplyRule(RewriteContext context)
|
||||
{
|
||||
|
||||
|
|
@ -28,20 +28,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
host = new HostString(host.Host);
|
||||
}
|
||||
|
||||
if ((OnCompletion != Transformation.Redirect))
|
||||
{
|
||||
context.HttpContext.Request.Scheme = "https";
|
||||
context.HttpContext.Request.Host = host;
|
||||
if (OnCompletion == Transformation.TerminatingRewrite)
|
||||
{
|
||||
return RuleResult.StopRules;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
|
||||
var req = context.HttpContext.Request;
|
||||
|
||||
var newUrl = new StringBuilder().Append("https://").Append(host).Append(req.PathBase).Append(req.Path).Append(req.QueryString);
|
||||
|
|
@ -51,4 +37,4 @@ namespace Microsoft.AspNetCore.Rewrite.Internal
|
|||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
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)
|
||||
{
|
||||
// TODO this only does http to https, add more features in the future.
|
||||
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;
|
||||
if (stopProcessing)
|
||||
{
|
||||
return RuleResult.StopRules;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite.Operands;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite
|
||||
{
|
||||
public class RuleExpression
|
||||
{
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||
{
|
||||
public class PathRule : Rule
|
||||
{
|
||||
public Regex MatchPattern { get; set; }
|
||||
public string OnMatch { get; set; }
|
||||
public Transformation OnCompletion { get; set; } = Transformation.Rewrite;
|
||||
public override RuleResult ApplyRule(RewriteContext context)
|
||||
{
|
||||
var matches = MatchPattern.Match(context.HttpContext.Request.Path);
|
||||
if (matches.Success)
|
||||
{
|
||||
// New method here to translate the outgoing format string to the correct value.
|
||||
var path = matches.Result(OnMatch);
|
||||
if (OnCompletion == Transformation.Redirect)
|
||||
{
|
||||
var req = context.HttpContext.Request;
|
||||
var newUrl = string.Concat(
|
||||
req.Scheme,
|
||||
"://",
|
||||
req.PathBase,
|
||||
path,
|
||||
req.QueryString);
|
||||
context.HttpContext.Response.Redirect(newUrl);
|
||||
return RuleResult.ResponseComplete;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.HttpContext.Request.Path = path;
|
||||
}
|
||||
if (OnCompletion == Transformation.TerminatingRewrite)
|
||||
{
|
||||
return RuleResult.StopRules;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal
|
||||
{
|
||||
public enum Transformation
|
||||
{
|
||||
Rewrite,
|
||||
Redirect,
|
||||
TerminatingRewrite
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||
{
|
||||
public class ParsedCondition
|
||||
{
|
||||
public bool Negate { get; set; }
|
||||
public bool IgnoreCase { get; set; } = true;
|
||||
public MatchType MatchType { get; set; } = MatchType.Pattern;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||
{
|
||||
public class ParsedUrlAction
|
||||
{
|
||||
public ActionType Type { get; set; }
|
||||
public Pattern Url { get; set; }
|
||||
public bool AppendQueryString { get; set; } = true;
|
||||
public bool LogRewrittenUrl { get; set; } // Ignoring this flag.
|
||||
public RedirectType RedirectType { get; set; } = RedirectType.Permanent;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||
{
|
||||
public class ParsedUrlMatch
|
||||
{
|
||||
public bool IgnoreCase { get; set; }
|
||||
public bool Negate { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
public const string Input = "input";
|
||||
public const string Pattern = "pattern";
|
||||
public const string Type = "type";
|
||||
public const string AppendQuery = "appendQueryString";
|
||||
public const string AppendQueryString = "appendQueryString";
|
||||
public const string LogRewrittenUrl = "logRewrittenUrl";
|
||||
public const string RedirectType = "redirectType";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
public abstract class UrlAction
|
||||
{
|
||||
public Pattern Url { get; set; }
|
||||
|
||||
public abstract RuleResult ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||
{
|
||||
|
|
@ -27,14 +24,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
var result = new List<UrlRewriteRule>();
|
||||
// TODO Global rules are currently not treated differently than normal rules, fix.
|
||||
// See: https://github.com/aspnet/BasicMiddleware/issues/59
|
||||
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result, isGlobalRule: true);
|
||||
ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result, isGlobalRule: false);
|
||||
ParseRules(xmlRoot.Descendants(RewriteTags.GlobalRules).FirstOrDefault(), result);
|
||||
ParseRules(xmlRoot.Descendants(RewriteTags.Rules).FirstOrDefault(), result);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void ParseRules(XElement rules, List<UrlRewriteRule> result, bool isGlobalRule)
|
||||
private static void ParseRules(XElement rules, List<UrlRewriteRule> result)
|
||||
{
|
||||
if (rules == null)
|
||||
{
|
||||
|
|
@ -43,297 +40,176 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
|
||||
foreach (var rule in rules.Elements(RewriteTags.Rule))
|
||||
{
|
||||
var res = new UrlRewriteRule();
|
||||
SetRuleAttributes(rule, res);
|
||||
var action = rule.Element(RewriteTags.Action);
|
||||
if (action == null)
|
||||
var builder = new UrlRewriteRuleBuilder();
|
||||
ParseRuleAttributes(rule, builder);
|
||||
|
||||
if (builder.Enabled)
|
||||
{
|
||||
ThrowUrlFormatException(rule, "Rule does not have an associated action attribute");
|
||||
}
|
||||
CreateUrlAction(action, res, isGlobalRule);
|
||||
if (res.Enabled)
|
||||
{
|
||||
result.Add(res);
|
||||
result.Add(builder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetRuleAttributes(XElement rule, UrlRewriteRule res)
|
||||
private static void ParseRuleAttributes(XElement rule, UrlRewriteRuleBuilder builder)
|
||||
{
|
||||
|
||||
res.Name = rule.Attribute(RewriteTags.Name)?.Value;
|
||||
builder.Name = rule.Attribute(RewriteTags.Name)?.Value;
|
||||
|
||||
bool enabled;
|
||||
if (bool.TryParse(rule.Attribute(RewriteTags.Enabled)?.Value, out enabled))
|
||||
if (!bool.TryParse(rule.Attribute(RewriteTags.Enabled)?.Value, out enabled))
|
||||
{
|
||||
res.Enabled = enabled;
|
||||
builder.Enabled = true;
|
||||
}
|
||||
|
||||
PatternSyntax patternSyntax;
|
||||
if (Enum.TryParse(rule.Attribute(RewriteTags.PatternSyntax)?.Value, out patternSyntax))
|
||||
if (!Enum.TryParse(rule.Attribute(RewriteTags.PatternSyntax)?.Value, out patternSyntax))
|
||||
{
|
||||
res.PatternSyntax = patternSyntax;
|
||||
patternSyntax = PatternSyntax.ECMAScript;
|
||||
}
|
||||
|
||||
bool stopProcessing;
|
||||
if (bool.TryParse(rule.Attribute(RewriteTags.StopProcessing)?.Value, out stopProcessing))
|
||||
if (!bool.TryParse(rule.Attribute(RewriteTags.StopProcessing)?.Value, out stopProcessing))
|
||||
{
|
||||
res.StopProcessing = stopProcessing;
|
||||
stopProcessing = false;
|
||||
}
|
||||
|
||||
var match = rule.Element(RewriteTags.Match);
|
||||
if (match == null)
|
||||
{
|
||||
ThrowUrlFormatException(rule, "Cannot have rule without match");
|
||||
}
|
||||
CreateMatch(match, res);
|
||||
CreateConditions(rule.Element(RewriteTags.Conditions), res);
|
||||
|
||||
var action = rule.Element(RewriteTags.Action);
|
||||
if (action == null)
|
||||
{
|
||||
ThrowUrlFormatException(rule, "Rule does not have an associated action attribute");
|
||||
}
|
||||
|
||||
ParseMatch(match, builder, patternSyntax);
|
||||
ParseConditions(rule.Element(RewriteTags.Conditions), builder, patternSyntax);
|
||||
ParseUrlAction(action, builder, stopProcessing);
|
||||
}
|
||||
|
||||
private static void CreateMatch(XElement match, UrlRewriteRule res)
|
||||
private static void ParseMatch(XElement match, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
|
||||
{
|
||||
var matchRes = new ParsedUrlMatch();
|
||||
|
||||
bool parBool;
|
||||
if (bool.TryParse(match.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
|
||||
{
|
||||
matchRes.IgnoreCase = parBool;
|
||||
}
|
||||
|
||||
if (bool.TryParse(match.Attribute(RewriteTags.Negate)?.Value, out parBool))
|
||||
{
|
||||
matchRes.Negate = parBool;
|
||||
}
|
||||
|
||||
var parsedInputString = match.Attribute(RewriteTags.Url)?.Value;
|
||||
if (parsedInputString == null)
|
||||
{
|
||||
ThrowUrlFormatException(match, "Match must have Url Attribute");
|
||||
}
|
||||
|
||||
switch (res.PatternSyntax)
|
||||
bool ignoreCase;
|
||||
if (!bool.TryParse(match.Attribute(RewriteTags.IgnoreCase)?.Value, out ignoreCase))
|
||||
{
|
||||
case PatternSyntax.ECMAScript:
|
||||
{
|
||||
if (matchRes.IgnoreCase)
|
||||
{
|
||||
var regex = new Regex(parsedInputString, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
|
||||
res.InitialMatch = new RegexMatch(regex, matchRes.Negate);
|
||||
}
|
||||
else
|
||||
{
|
||||
var regex = new Regex(parsedInputString, RegexOptions.Compiled, RegexTimeout);
|
||||
res.InitialMatch = new RegexMatch(regex, matchRes.Negate);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PatternSyntax.WildCard:
|
||||
throw new NotImplementedException("Wildcard syntax is not supported.");
|
||||
case PatternSyntax.ExactMatch:
|
||||
res.InitialMatch = new ExactMatch(matchRes.IgnoreCase, parsedInputString, matchRes.Negate);
|
||||
break;
|
||||
ignoreCase = true; // default
|
||||
}
|
||||
|
||||
bool negate;
|
||||
if (!bool.TryParse(match.Attribute(RewriteTags.Negate)?.Value, out negate))
|
||||
{
|
||||
negate = false;
|
||||
}
|
||||
builder.AddUrlMatch(parsedInputString, ignoreCase, negate, patternSyntax);
|
||||
}
|
||||
|
||||
private static void CreateConditions(XElement conditions, UrlRewriteRule res)
|
||||
|
||||
|
||||
private static void ParseConditions(XElement conditions, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
|
||||
{
|
||||
// This is to avoid nullptr exception on referencing conditions.
|
||||
res.Conditions = new Conditions();
|
||||
if (conditions == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LogicalGrouping grouping;
|
||||
if (Enum.TryParse(conditions.Attribute(RewriteTags.MatchType)?.Value, out grouping))
|
||||
if (!Enum.TryParse(conditions.Attribute(RewriteTags.MatchType)?.Value, out grouping))
|
||||
{
|
||||
res.Conditions.MatchType = grouping;
|
||||
grouping = LogicalGrouping.MatchAll;
|
||||
}
|
||||
|
||||
bool parBool;
|
||||
if (bool.TryParse(conditions.Attribute(RewriteTags.TrackingAllCaptures)?.Value, out parBool))
|
||||
bool trackingAllCaptures;
|
||||
if (!bool.TryParse(conditions.Attribute(RewriteTags.TrackingAllCaptures)?.Value, out trackingAllCaptures))
|
||||
{
|
||||
res.Conditions.TrackingAllCaptures = parBool;
|
||||
trackingAllCaptures = false;
|
||||
}
|
||||
|
||||
builder.AddUrlConditions(grouping, trackingAllCaptures);
|
||||
|
||||
foreach (var cond in conditions.Elements(RewriteTags.Add))
|
||||
{
|
||||
CreateCondition(cond, res);
|
||||
ParseCondition(cond, builder, patternSyntax);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateCondition(XElement condition, UrlRewriteRule res)
|
||||
private static void ParseCondition(XElement condition, UrlRewriteRuleBuilder builder, PatternSyntax patternSyntax)
|
||||
{
|
||||
var parsedCondRes = new ParsedCondition();
|
||||
|
||||
bool parBool;
|
||||
if (bool.TryParse(condition.Attribute(RewriteTags.IgnoreCase)?.Value, out parBool))
|
||||
bool ignoreCase;
|
||||
if (!bool.TryParse(condition.Attribute(RewriteTags.IgnoreCase)?.Value, out ignoreCase))
|
||||
{
|
||||
parsedCondRes.IgnoreCase = parBool;
|
||||
ignoreCase = true;
|
||||
}
|
||||
|
||||
if (bool.TryParse(condition.Attribute(RewriteTags.Negate)?.Value, out parBool))
|
||||
bool negate;
|
||||
if (!bool.TryParse(condition.Attribute(RewriteTags.Negate)?.Value, out negate))
|
||||
{
|
||||
parsedCondRes.Negate = parBool;
|
||||
ignoreCase = false;
|
||||
}
|
||||
|
||||
MatchType matchType;
|
||||
if (Enum.TryParse(condition.Attribute(RewriteTags.MatchType)?.Value, out matchType))
|
||||
if (!Enum.TryParse(condition.Attribute(RewriteTags.MatchType)?.Value, out matchType))
|
||||
{
|
||||
parsedCondRes.MatchType = matchType;
|
||||
matchType = MatchType.Pattern;
|
||||
}
|
||||
|
||||
var parsedString = condition.Attribute(RewriteTags.Input)?.Value;
|
||||
if (parsedString == null)
|
||||
var parsedInputString = condition.Attribute(RewriteTags.Input)?.Value;
|
||||
if (parsedInputString == null)
|
||||
{
|
||||
ThrowUrlFormatException(condition, "Conditions must have an input attribute");
|
||||
}
|
||||
|
||||
var parsedPatternString = condition.Attribute(RewriteTags.Pattern)?.Value;
|
||||
|
||||
Pattern input = null;
|
||||
try
|
||||
{
|
||||
input = InputParser.ParseInputString(parsedString);
|
||||
input = InputParser.ParseInputString(parsedInputString);
|
||||
builder.AddUrlCondition(input, parsedPatternString, patternSyntax, matchType, ignoreCase, negate);
|
||||
|
||||
}
|
||||
catch (FormatException formatException)
|
||||
{
|
||||
ThrowUrlFormatException(condition, formatException.Message, formatException);
|
||||
}
|
||||
|
||||
switch (res.PatternSyntax)
|
||||
{
|
||||
case PatternSyntax.ECMAScript:
|
||||
{
|
||||
switch (parsedCondRes.MatchType)
|
||||
{
|
||||
case MatchType.Pattern:
|
||||
{
|
||||
parsedString = condition.Attribute(RewriteTags.Pattern)?.Value;
|
||||
if (parsedString == null)
|
||||
{
|
||||
ThrowUrlFormatException(condition, "Match does not have an associated pattern attribute in condition");
|
||||
|
||||
}
|
||||
Regex regex = null;
|
||||
|
||||
if (parsedCondRes.IgnoreCase)
|
||||
{
|
||||
regex = new Regex(parsedString, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
regex = new Regex(parsedString, RegexOptions.Compiled, RegexTimeout);
|
||||
}
|
||||
|
||||
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new RegexMatch(regex, parsedCondRes.Negate) });
|
||||
}
|
||||
break;
|
||||
case MatchType.IsDirectory:
|
||||
{
|
||||
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new IsDirectoryMatch(parsedCondRes.Negate) });
|
||||
}
|
||||
break;
|
||||
case MatchType.IsFile:
|
||||
{
|
||||
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new IsFileMatch(parsedCondRes.Negate) });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// TODO don't this this can ever be thrown
|
||||
ThrowUrlFormatException(condition, "Unrecognized matchType");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PatternSyntax.WildCard:
|
||||
throw new NotImplementedException("Wildcard syntax is not supported");
|
||||
case PatternSyntax.ExactMatch:
|
||||
parsedString = condition.Attribute(RewriteTags.Pattern)?.Value;
|
||||
if (parsedString == null)
|
||||
{
|
||||
ThrowUrlFormatException(condition, "Pattern match does not have an associated pattern attribute in condition");
|
||||
}
|
||||
res.Conditions.ConditionList.Add(new Condition { Input = input, Match = new ExactMatch(parsedCondRes.IgnoreCase, parsedString, parsedCondRes.Negate) });
|
||||
break;
|
||||
default:
|
||||
ThrowUrlFormatException(condition, "Unrecognized pattern syntax");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateUrlAction(XElement urlAction, UrlRewriteRule res, bool globalRule)
|
||||
private static void ParseUrlAction(XElement urlAction, UrlRewriteRuleBuilder builder, bool stopProcessing)
|
||||
{
|
||||
|
||||
var actionRes = new ParsedUrlAction();
|
||||
|
||||
ActionType actionType;
|
||||
if (Enum.TryParse(urlAction.Attribute(RewriteTags.Type)?.Value, out actionType))
|
||||
if (!Enum.TryParse(urlAction.Attribute(RewriteTags.Type)?.Value, out actionType))
|
||||
{
|
||||
actionRes.Type = actionType;
|
||||
actionType = ActionType.None;
|
||||
}
|
||||
|
||||
bool parseBool;
|
||||
if (bool.TryParse(urlAction.Attribute(RewriteTags.AppendQuery)?.Value, out parseBool))
|
||||
bool appendQuery;
|
||||
if (!bool.TryParse(urlAction.Attribute(RewriteTags.AppendQueryString)?.Value, out appendQuery))
|
||||
{
|
||||
actionRes.AppendQueryString = parseBool;
|
||||
}
|
||||
|
||||
if (bool.TryParse(urlAction.Attribute(RewriteTags.LogRewrittenUrl)?.Value, out parseBool))
|
||||
{
|
||||
actionRes.LogRewrittenUrl = parseBool;
|
||||
appendQuery = true;
|
||||
}
|
||||
|
||||
RedirectType redirectType;
|
||||
if (Enum.TryParse(urlAction.Attribute(RewriteTags.RedirectType)?.Value, out redirectType))
|
||||
if (!Enum.TryParse(urlAction.Attribute(RewriteTags.RedirectType)?.Value, out redirectType))
|
||||
{
|
||||
actionRes.RedirectType = redirectType;
|
||||
redirectType = RedirectType.Permanent;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
actionRes.Url = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
|
||||
var input = InputParser.ParseInputString(urlAction.Attribute(RewriteTags.Url)?.Value);
|
||||
builder.AddUrlAction(input, actionType, appendQuery, stopProcessing, (int)redirectType);
|
||||
}
|
||||
catch (FormatException formatException)
|
||||
{
|
||||
ThrowUrlFormatException(urlAction, formatException.Message, formatException);
|
||||
}
|
||||
|
||||
CreateUrlActionFromParsedAction(urlAction, actionRes, globalRule, res);
|
||||
}
|
||||
|
||||
private static void CreateUrlActionFromParsedAction(XElement urlAction, ParsedUrlAction actionRes, bool globalRule, UrlRewriteRule res)
|
||||
{
|
||||
switch (actionRes.Type)
|
||||
{
|
||||
case ActionType.None:
|
||||
res.Action = new VoidAction();
|
||||
break;
|
||||
case ActionType.Rewrite:
|
||||
if (actionRes.AppendQueryString)
|
||||
{
|
||||
res.Action = new RewriteAction(res.StopProcessing ? RuleTerminiation.StopRules : RuleTerminiation.Continue, actionRes.Url, clearQuery: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.Action = new RewriteAction(res.StopProcessing ? RuleTerminiation.StopRules : RuleTerminiation.Continue, actionRes.Url, clearQuery: true);
|
||||
}
|
||||
break;
|
||||
case ActionType.Redirect:
|
||||
if (actionRes.AppendQueryString)
|
||||
{
|
||||
res.Action = new RedirectAction((int)actionRes.RedirectType, actionRes.Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.Action = new RedirectClearQueryAction((int)actionRes.RedirectType, actionRes.Url);
|
||||
}
|
||||
break;
|
||||
case ActionType.AbortRequest:
|
||||
ThrowUrlFormatException(urlAction, "Abort Requests are not supported.");
|
||||
break;
|
||||
case ActionType.CustomResponse:
|
||||
// TODO
|
||||
ThrowUrlFormatException(urlAction, "Custom Responses are not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowUrlFormatException(XElement element, string message)
|
||||
|
|
@ -342,6 +218,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
var col = ((IXmlLineInfo)element).LinePosition;
|
||||
throw new FormatException(Resources.FormatError_UrlRewriteParseError(message, line, col));
|
||||
}
|
||||
|
||||
private static void ThrowUrlFormatException(XElement element, string message, Exception ex)
|
||||
{
|
||||
var line = ((IXmlLineInfo)element).LineNumber;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlActions;
|
||||
using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite.UrlMatches;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
||||
{
|
||||
public class UrlRewriteRuleBuilder
|
||||
{
|
||||
private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1);
|
||||
|
||||
public string Name { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
private UrlMatch _initialMatch;
|
||||
private Conditions _conditions;
|
||||
private UrlAction _action;
|
||||
|
||||
public UrlRewriteRule Build()
|
||||
{
|
||||
// TODO some of these are required fields, throw if null?
|
||||
var rule = new UrlRewriteRule();
|
||||
rule.Action = _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)
|
||||
{
|
||||
switch (actionType)
|
||||
{
|
||||
case ActionType.None:
|
||||
_action = new VoidAction();
|
||||
break;
|
||||
case ActionType.Rewrite:
|
||||
if (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;
|
||||
case ActionType.Redirect:
|
||||
if (appendQueryString)
|
||||
{
|
||||
_action = new RedirectAction(statusCode, url);
|
||||
}
|
||||
else
|
||||
{
|
||||
_action = new RedirectClearQueryAction(statusCode, url);
|
||||
}
|
||||
break;
|
||||
case ActionType.AbortRequest:
|
||||
throw new FormatException("Abort Requests are not supported");
|
||||
case ActionType.CustomResponse:
|
||||
// TODO
|
||||
throw new FormatException("Custom Responses are not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUrlMatch(string input, bool ignoreCase = true, bool negate = false, PatternSyntax patternSyntax = PatternSyntax.ECMAScript)
|
||||
{
|
||||
switch (patternSyntax)
|
||||
{
|
||||
case PatternSyntax.ECMAScript:
|
||||
{
|
||||
if (ignoreCase)
|
||||
{
|
||||
var regex = new Regex(input, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
|
||||
_initialMatch = new RegexMatch(regex, negate);
|
||||
}
|
||||
else
|
||||
{
|
||||
var regex = new Regex(input, RegexOptions.Compiled, RegexTimeout);
|
||||
_initialMatch = new RegexMatch(regex, negate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PatternSyntax.WildCard:
|
||||
throw new NotImplementedException("Wildcard syntax is not supported");
|
||||
case PatternSyntax.ExactMatch:
|
||||
_initialMatch = new ExactMatch(ignoreCase, input, negate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO make this take two overloads and handle regex vs non regex case.
|
||||
public void AddUrlCondition(Pattern input, string pattern, PatternSyntax patternSyntax, MatchType matchType, bool ignoreCase, bool negate)
|
||||
{
|
||||
if (_conditions == null)
|
||||
{
|
||||
AddUrlConditions(LogicalGrouping.MatchAll, trackingAllCaptures: false);
|
||||
}
|
||||
switch (patternSyntax)
|
||||
{
|
||||
case PatternSyntax.ECMAScript:
|
||||
{
|
||||
switch (matchType)
|
||||
{
|
||||
case MatchType.Pattern:
|
||||
{
|
||||
if (pattern == null)
|
||||
{
|
||||
throw new FormatException("Match does not have an associated pattern attribute in condition");
|
||||
}
|
||||
|
||||
Regex regex = null;
|
||||
if (ignoreCase)
|
||||
{
|
||||
regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase, RegexTimeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
regex = new Regex(pattern, RegexOptions.Compiled, RegexTimeout);
|
||||
}
|
||||
|
||||
_conditions.ConditionList.Add(new Condition { Input = input, Match = new RegexMatch(regex, negate) });
|
||||
break;
|
||||
}
|
||||
case MatchType.IsDirectory:
|
||||
{
|
||||
_conditions.ConditionList.Add(new Condition { Input = input, Match = new IsDirectoryMatch(negate) });
|
||||
break;
|
||||
}
|
||||
case MatchType.IsFile:
|
||||
{
|
||||
_conditions.ConditionList.Add(new Condition { Input = input, Match = new IsFileMatch(negate) });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO new exception handling
|
||||
throw new FormatException("Unrecognized matchType");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PatternSyntax.WildCard:
|
||||
throw new NotImplementedException("Wildcard syntax is not supported");
|
||||
case PatternSyntax.ExactMatch:
|
||||
if (pattern == null)
|
||||
{
|
||||
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) });
|
||||
break;
|
||||
default:
|
||||
throw new FormatException("Unrecognized pattern syntax");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUrlConditions(LogicalGrouping logicalGrouping, bool trackingAllCaptures)
|
||||
{
|
||||
var conditions = new Conditions();
|
||||
conditions.ConditionList = new List<Condition>();
|
||||
conditions.MatchType = logicalGrouping;
|
||||
conditions.TrackingAllCaptures = trackingAllCaptures;
|
||||
_conditions = conditions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
{
|
||||
public string Name { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public PatternSyntax PatternSyntax { get; set; }
|
||||
public bool StopProcessing { get; set; }
|
||||
public UrlMatch InitialMatch { get; set; }
|
||||
public Conditions Conditions { get; set; }
|
||||
public UrlAction Action { get; set; }
|
||||
|
|
@ -19,8 +17,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
{
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
|
||||
// Due to the path string always having a leading slash,
|
||||
// remove it from the path before regex comparison
|
||||
// TODO may need to check if there is a leading slash and remove conditionally
|
||||
var initMatchRes = InitialMatch.Evaluate(context.HttpContext.Request.Path.ToString().Substring(1), context);
|
||||
|
||||
if (!initMatchRes.Success)
|
||||
|
|
@ -28,10 +28,14 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite
|
|||
return RuleResult.Continue;
|
||||
}
|
||||
|
||||
var condMatchRes = Conditions.Evaluate(context, initMatchRes);
|
||||
if (!condMatchRes.Success)
|
||||
MatchResults condMatchRes = null;
|
||||
if (Conditions != null)
|
||||
{
|
||||
return RuleResult.Continue;
|
||||
condMatchRes = Conditions.Evaluate(context, initMatchRes);
|
||||
if (!condMatchRes.Success)
|
||||
{
|
||||
return RuleResult.Continue;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we know the rule passed, evaluate the replacement.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules
|
||||
{
|
||||
public class MiddlewareTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CheckRewritePath()
|
||||
{
|
||||
var options = new RewriteOptions().RewriteRule("(.*)", "http://example.com/{R:1}");
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseRewriter(options);
|
||||
app.UseRewriter(options);
|
||||
app.Run(context => context.Response.WriteAsync(
|
||||
context.Request.Scheme +
|
||||
"://" +
|
||||
context.Request.Host +
|
||||
context.Request.Path +
|
||||
context.Request.QueryString));
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var response = await server.CreateClient().GetStringAsync("foo");
|
||||
|
||||
Assert.Equal(response, "http://example.com/foo");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckRedirectPath()
|
||||
{
|
||||
var options = new RewriteOptions().RedirectRule("(.*)","http://example.com/{R:1}", statusCode: 301);
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseRewriter(options);
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var response = await server.CreateClient().GetAsync("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]
|
||||
public async Task CheckRedirectToHttps()
|
||||
{
|
||||
var options = new RewriteOptions().RedirectToHttps(statusCode: 301);
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseRewriter(options);
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
||||
var response = await server.CreateClient().GetAsync(new Uri("http://example.com"));
|
||||
|
||||
Assert.Equal(response.Headers.Location.OriginalString, "https://example.com/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,8 +150,6 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
Action = new RewriteAction(RuleTerminiation.Continue, InputParser.ParseInputString(Url), clearQuery: false),
|
||||
Name = name,
|
||||
Enabled = enabled,
|
||||
StopProcessing = stopProcessing,
|
||||
PatternSyntax = patternSyntax,
|
||||
InitialMatch = new RegexMatch(new Regex("^OFF$"), false)
|
||||
{
|
||||
},
|
||||
|
|
@ -164,29 +162,37 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
};
|
||||
}
|
||||
|
||||
private void AssertUrlRewriteRuleEquality(List<UrlRewriteRule> expected, List<UrlRewriteRule> actual)
|
||||
private void AssertUrlRewriteRuleEquality(List<UrlRewriteRule> actual, List<UrlRewriteRule> expected)
|
||||
{
|
||||
Assert.Equal(expected.Count, actual.Count);
|
||||
for (var i = 0; i < expected.Count; i++)
|
||||
Assert.Equal(actual.Count, expected.Count);
|
||||
for (var i = 0; i < actual.Count; i++)
|
||||
{
|
||||
var r1 = expected[i];
|
||||
var r2 = actual[i];
|
||||
var r1 = actual[i];
|
||||
var r2 = expected[i];
|
||||
|
||||
Assert.Equal(r1.Name, r2.Name);
|
||||
Assert.Equal(r1.Enabled, r2.Enabled);
|
||||
Assert.Equal(r1.StopProcessing, r2.StopProcessing);
|
||||
Assert.Equal(r1.PatternSyntax, r2.PatternSyntax);
|
||||
|
||||
// TODO conditions, url pattern, initial match regex
|
||||
Assert.Equal(r1.Conditions.MatchType, r2.Conditions.MatchType);
|
||||
Assert.Equal(r1.Conditions.TrackingAllCaptures, r2.Conditions.TrackingAllCaptures);
|
||||
Assert.Equal(r1.Conditions.ConditionList.Count, r2.Conditions.ConditionList.Count);
|
||||
|
||||
for (var j = 0; j < r1.Conditions.ConditionList.Count; j++)
|
||||
if (r1.Conditions == null)
|
||||
{
|
||||
var c1 = r1.Conditions.ConditionList[j];
|
||||
var c2 = r2.Conditions.ConditionList[j];
|
||||
Assert.Equal(c1.Input.PatternSegments.Count, c2.Input.PatternSegments.Count);
|
||||
Assert.Equal(r2.Conditions.ConditionList.Count, 0);
|
||||
}
|
||||
else if (r2.Conditions == null)
|
||||
{
|
||||
Assert.Equal(r1.Conditions.ConditionList.Count, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(r1.Conditions.MatchType, r2.Conditions.MatchType);
|
||||
Assert.Equal(r1.Conditions.TrackingAllCaptures, r2.Conditions.TrackingAllCaptures);
|
||||
Assert.Equal(r1.Conditions.ConditionList.Count, r2.Conditions.ConditionList.Count);
|
||||
for (var j = 0; j < r1.Conditions.ConditionList.Count; j++)
|
||||
{
|
||||
var c1 = r1.Conditions.ConditionList[j];
|
||||
var c2 = r2.Conditions.ConditionList[j];
|
||||
Assert.Equal(c1.Input.PatternSegments.Count, c2.Input.PatternSegments.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
"Could not parse the UrlRewrite file. Message: 'Abort Requests are not supported.'. Line number '5': '14'.")]
|
||||
"Could not parse the UrlRewrite file. Message: 'Abort Requests are not supported'. Line number '5': '14'.")]
|
||||
[InlineData(
|
||||
@"<rewrite>
|
||||
<rules>
|
||||
|
|
@ -54,6 +54,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match />
|
||||
<action type=""Rewrite"" url=""foo"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>",
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
public async Task Invoke_RedirectPathToPathAndQuery()
|
||||
{
|
||||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||
<action type=""Redirect"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||
<action type=""Redirect"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
|
|
@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
public async Task Invoke_RewritePathToPathAndQuery()
|
||||
{
|
||||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||
<action type=""Rewrite"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
<rules>
|
||||
<rule name=""Rewrite to article.aspx"">
|
||||
<match url = ""^article/([0-9]+)/([_0-9a-z-]+)"" />
|
||||
<action type=""Rewrite"" url =""article.aspx?id={R:1}&title={R:2}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
|
|
@ -121,13 +121,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Remove trailing slash"" stopProcessing=""true"">
|
||||
<match url=""(.*)/$"" />
|
||||
<conditions>
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsFile"" negate=""true"" />
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsDirectory"" negate=""true"" />
|
||||
</conditions>
|
||||
<action type=""Redirect"" redirectType=""Permanent"" url=""{R:1}"" />
|
||||
</rule>
|
||||
<match url=""(.*)/$"" />
|
||||
<conditions>
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsFile"" negate=""true"" />
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsDirectory"" negate=""true"" />
|
||||
</conditions>
|
||||
<action type=""Redirect"" redirectType=""Permanent"" url=""{R:1}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
|
|
@ -148,13 +148,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Add trailing slash"" stopProcessing=""true"">
|
||||
<match url=""(.*[^/])$"" />
|
||||
<conditions>
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsFile"" negate=""true"" />
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsDirectory"" negate=""true"" />
|
||||
</conditions>
|
||||
<action type=""Redirect"" redirectType=""Permanent"" url=""{R:1}/"" />
|
||||
</rule>
|
||||
<match url=""(.*[^/])$"" />
|
||||
<conditions>
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsFile"" negate=""true"" />
|
||||
<add input=""{REQUEST_FILENAME}"" matchType=""IsDirectory"" negate=""true"" />
|
||||
</conditions>
|
||||
<action type=""Redirect"" redirectType=""Permanent"" url=""{R:1}/"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
|
|
@ -174,13 +174,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
{
|
||||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Redirect to HTTPS"" stopProcessing=""true"">
|
||||
<match url=""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||
</conditions>
|
||||
<action type=""Redirect"" url=""https://{HTTP_HOST}/{R:1}"" redirectType=""Permanent"" />
|
||||
</rule>
|
||||
<rule name=""Redirect to HTTPS"" stopProcessing=""true"">
|
||||
<match url=""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||
</conditions>
|
||||
<action type=""Redirect"" url=""https://{HTTP_HOST}/{R:1}"" redirectType=""Permanent"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
|
|
@ -200,13 +200,13 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
{
|
||||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Rewrite to HTTPS"" stopProcessing=""true"">
|
||||
<match url=""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||
</conditions>
|
||||
<action type=""Rewrite"" url=""https://{HTTP_HOST}/{R:1}"" />
|
||||
</rule>
|
||||
<rule name=""Rewrite to HTTPS"" stopProcessing=""true"">
|
||||
<match url=""(.*)"" />
|
||||
<conditions>
|
||||
<add input=""{HTTPS}"" pattern=""^OFF$"" />
|
||||
</conditions>
|
||||
<action type=""Rewrite"" url=""https://{HTTP_HOST}/{R:1}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
|
|
@ -214,10 +214,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
{
|
||||
app.UseRewriter(options);
|
||||
app.Run(context => context.Response.WriteAsync(
|
||||
context.Request.Scheme +
|
||||
context.Request.Scheme +
|
||||
"://" +
|
||||
context.Request.Host +
|
||||
context.Request.Path +
|
||||
context.Request.Path +
|
||||
context.Request.QueryString));
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
|
|
@ -232,10 +232,10 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite
|
|||
{
|
||||
var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@"<rewrite>
|
||||
<rules>
|
||||
<rule name=""Proxy"">
|
||||
<match url=""(.*)"" />
|
||||
<action type=""Rewrite"" url=""http://internalserver/{R:1}"" />
|
||||
</rule>
|
||||
<rule name=""Proxy"">
|
||||
<match url=""(.*)"" />
|
||||
<action type=""Rewrite"" url=""http://internalserver/{R:1}"" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>"));
|
||||
var builder = new WebHostBuilder()
|
||||
|
|
|
|||
Loading…
Reference in New Issue