From af2c1acee392229942d63517f37aed33ab3e2052 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Mon, 29 Aug 2016 13:55:44 -0700 Subject: [PATCH] Adds XML comments and feedback from 1on1 with Nate. --- samples/RewriteSample/Rewrite.txt | 5 +-- samples/RewriteSample/Startup.cs | 25 +++++---------- samples/RewriteSample/UrlRewrite.xml | 7 ++-- ...s => ApacheModRewriteOptionsExtensions.cs} | 13 +++++--- ...s.cs => IISUrlRewriteOptionsExtensions.cs} | 11 ++++--- .../Internal/CodeRules/RewriteRule.cs | 1 - .../Internal/ModRewrite/FlagParser.cs | 2 ++ .../Internal/ModRewrite/RuleBuilder.cs | 25 ++++++--------- .../Internal/ModRewriteRule.cs | 17 ++++------ .../PatternSegments/IsHttpsModSegment.cs | 2 ++ ...IsHttpsSegment.cs => IsHttpsUrlSegment.cs} | 6 ++-- .../Internal/PreAction.cs | 12 ------- .../ChangeCookieAction.cs} | 8 ++--- .../ChangeEnvironmentAction.cs} | 8 ++--- .../Internal/UrlActions/RedirectAction.cs | 2 -- .../Internal/UrlActions/RewriteAction.cs | 1 + .../Internal/UrlRewrite/ServerVariables.cs | 2 +- .../RewriteContext.cs | 20 ++++++++++-- .../RewriteMiddleware.cs | 1 - .../RewriteOptions.cs | 8 ++++- .../RewriteOptionsExtensions.cs | 32 +++++++++++-------- src/Microsoft.AspNetCore.Rewrite/Rule.cs | 11 +++++-- .../RuleTermination.cs | 12 +++++++ .../CodeRules/MiddlewareTests.cs | 6 ++-- .../ModRewrite/ModRewriteMiddlewareTest.cs | 30 ++++++++--------- .../PatternSegments/IsHttpsSegmentTests.cs | 2 +- .../UrlRewrite/MiddleWareTests.cs | 18 +++++------ 27 files changed, 158 insertions(+), 129 deletions(-) rename src/Microsoft.AspNetCore.Rewrite/{ModRewriteOptionsExtensions.cs => ApacheModRewriteOptionsExtensions.cs} (77%) rename src/Microsoft.AspNetCore.Rewrite/{UrlRewriteOptionsExtensions.cs => IISUrlRewriteOptionsExtensions.cs} (80%) rename src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/{IsHttpsSegment.cs => IsHttpsUrlSegment.cs} (69%) delete mode 100644 src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs rename src/Microsoft.AspNetCore.Rewrite/Internal/{PreActions/ChangeCookiePreAction.cs => UrlActions/ChangeCookieAction.cs} (58%) rename src/Microsoft.AspNetCore.Rewrite/Internal/{PreActions/ChangeEnvironmentPreAction.cs => UrlActions/ChangeEnvironmentAction.cs} (60%) diff --git a/samples/RewriteSample/Rewrite.txt b/samples/RewriteSample/Rewrite.txt index 23d4948c18..a803234457 100644 --- a/samples/RewriteSample/Rewrite.txt +++ b/samples/RewriteSample/Rewrite.txt @@ -1,3 +1,4 @@ # Rewrite path with additional sub directory -RewriteCond %{QUERY_STRING} id=20 -RewriteRule ^(.*)$ - [G] +RewriteCond %{HTTP_HOST} !^www\.example\.com [NC,OR] +RewriteCond %{SERVER_PORT} !^5000$ +RewriteRule ^/(.*) http://www.example.com/$1 [L,R=302] \ No newline at end of file diff --git a/samples/RewriteSample/Startup.cs b/samples/RewriteSample/Startup.cs index 85918d2459..6a76d5df3e 100644 --- a/samples/RewriteSample/Startup.cs +++ b/samples/RewriteSample/Startup.cs @@ -11,25 +11,16 @@ namespace RewriteSample { public class Startup { - public void Configure(IApplicationBuilder app, IHostingEnvironment hostingEnv) + public void Configure(IApplicationBuilder app, IHostingEnvironment hostingEnvironment) { - // 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 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. + var options = new RewriteOptions() + .AddRedirect("(.*)/$", "$1") + .AddRewrite(@"app/(\d+)", "app?id=$1") + .AddRedirectToHttps(302) + .AddIISUrlRewrite(hostingEnvironment, "UrlRewrite.xml") + .AddApacheModRewrite(hostingEnvironment, "Rewrite.txt"); - app.UseRewriter(new RewriteOptions() - .Rewrite(@"foo/(\d+)", "foo?id=$1") - .ImportFromUrlRewrite(hostingEnv, "UrlRewrite.xml") - .ImportFromModRewrite(hostingEnv, "Rewrite.txt")); + app.UseRewriter(options); app.Run(context => context.Response.WriteAsync($"Rewritten Url: {context.Request.Path + context.Request.QueryString}")); } diff --git a/samples/RewriteSample/UrlRewrite.xml b/samples/RewriteSample/UrlRewrite.xml index 9d9e089676..fee831050c 100644 --- a/samples/RewriteSample/UrlRewrite.xml +++ b/samples/RewriteSample/UrlRewrite.xml @@ -1,8 +1,11 @@  - - + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Rewrite/ModRewriteOptionsExtensions.cs b/src/Microsoft.AspNetCore.Rewrite/ApacheModRewriteOptionsExtensions.cs similarity index 77% rename from src/Microsoft.AspNetCore.Rewrite/ModRewriteOptionsExtensions.cs rename to src/Microsoft.AspNetCore.Rewrite/ApacheModRewriteOptionsExtensions.cs index 1ad897b90e..e237c7610c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/ModRewriteOptionsExtensions.cs +++ b/src/Microsoft.AspNetCore.Rewrite/ApacheModRewriteOptionsExtensions.cs @@ -8,7 +8,10 @@ using Microsoft.AspNetCore.Rewrite.Internal.ModRewrite; namespace Microsoft.AspNetCore.Rewrite { - public static class ModRewriteOptionsExtensions + /// + /// Apache mod_rewrite extensions on top of the + /// + public static class ApacheModRewriteOptionsExtensions { /// /// Imports rules from a mod_rewrite file and adds the rules to current rules. @@ -16,7 +19,7 @@ namespace Microsoft.AspNetCore.Rewrite /// The Rewrite options. /// The Hosting Environment /// The path to the file containing mod_rewrite rules. - public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, IHostingEnvironment hostingEnvironment, string filePath) + public static RewriteOptions AddApacheModRewrite(this RewriteOptions options, IHostingEnvironment hostingEnvironment, string filePath) { if (options == null) { @@ -36,7 +39,7 @@ namespace Microsoft.AspNetCore.Rewrite var path = Path.Combine(hostingEnvironment.ContentRootPath, filePath); using (var stream = File.OpenRead(path)) { - return options.ImportFromModRewrite(new StreamReader(stream)); + return options.AddApacheModRewrite(new StreamReader(stream)); } } @@ -44,8 +47,8 @@ namespace Microsoft.AspNetCore.Rewrite /// Imports rules from a mod_rewrite file and adds the rules to current rules. /// /// The Rewrite options. - /// Text reader containing a stream of mod_rewrite rules. - public static RewriteOptions ImportFromModRewrite(this RewriteOptions options, TextReader reader) + /// A stream of mod_rewrite rules. + public static RewriteOptions AddApacheModRewrite(this RewriteOptions options, TextReader reader) { if (options == null) { diff --git a/src/Microsoft.AspNetCore.Rewrite/UrlRewriteOptionsExtensions.cs b/src/Microsoft.AspNetCore.Rewrite/IISUrlRewriteOptionsExtensions.cs similarity index 80% rename from src/Microsoft.AspNetCore.Rewrite/UrlRewriteOptionsExtensions.cs rename to src/Microsoft.AspNetCore.Rewrite/IISUrlRewriteOptionsExtensions.cs index 1ca090fdbe..21658e5649 100644 --- a/src/Microsoft.AspNetCore.Rewrite/UrlRewriteOptionsExtensions.cs +++ b/src/Microsoft.AspNetCore.Rewrite/IISUrlRewriteOptionsExtensions.cs @@ -8,7 +8,10 @@ using Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite; namespace Microsoft.AspNetCore.Rewrite { - public static class UrlRewriteOptionsExtensions + /// + /// IIS Url rewrite extensions on top of the + /// + public static class IISUrlRewriteOptionsExtensions { /// /// Imports rules from a mod_rewrite file and adds the rules to current rules. @@ -16,7 +19,7 @@ namespace Microsoft.AspNetCore.Rewrite /// The UrlRewrite options. /// /// The path to the file containing urlrewrite rules. - public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, IHostingEnvironment hostingEnvironment, string filePath) + public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, IHostingEnvironment hostingEnvironment, string filePath) { if (options == null) { @@ -36,7 +39,7 @@ namespace Microsoft.AspNetCore.Rewrite var path = Path.Combine(hostingEnvironment.ContentRootPath, filePath); using (var stream = File.OpenRead(path)) { - return ImportFromUrlRewrite(options, new StreamReader(stream)); + return AddIISUrlRewrite(options, new StreamReader(stream)); } } @@ -45,7 +48,7 @@ namespace Microsoft.AspNetCore.Rewrite /// /// The UrlRewrite options. /// The text reader stream. - public static RewriteOptions ImportFromUrlRewrite(this RewriteOptions options, TextReader reader) + public static RewriteOptions AddIISUrlRewrite(this RewriteOptions options, TextReader reader) { if (options == null) { diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs index 4892a3d467..dd82c6fcde 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/CodeRules/RewriteRule.cs @@ -39,7 +39,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.CodeRules { var result = initMatchResults.Result(Replacement); var request = context.HttpContext.Request; - if (result.IndexOf("://", StringComparison.Ordinal) >= 0) { string scheme; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs index 895dcb16d4..265c4acf22 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/FlagParser.cs @@ -35,6 +35,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite { "noescape", FlagType.NoEscape }, { "ns", FlagType.NoSubReq }, { "nosubreq", FlagType.NoSubReq }, + { "or", FlagType.Or }, + { "ornext", FlagType.Or }, { "p", FlagType.Proxy }, { "proxy", FlagType.Proxy }, { "pt", FlagType.PassThrough }, diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs index 8d578feed8..ece600a965 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewrite/RuleBuilder.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Rewrite.Internal.PreActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlActions; using Microsoft.AspNetCore.Rewrite.Internal.UrlMatches; @@ -13,19 +12,18 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite public class RuleBuilder { private IList _conditions; - private UrlAction _action; + private IList _actions = new List(); private UrlMatch _match; - private List _preActions; private readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(1); public ModRewriteRule Build() { - if (_action == null || _match == null) + if (_actions.Count == 0 || _match == null) { throw new InvalidOperationException("Cannot create ModRewriteRule without action and match"); } - return new ModRewriteRule(_match, _conditions, _action, _preActions); + return new ModRewriteRule(_match, _conditions, _actions); } public void AddRule(string rule) @@ -169,31 +167,26 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite Flags flags) { // first create pre conditions - if (_preActions == null) - { - _preActions = new List(); - } - string flag; if (flags.GetValue(FlagType.Cookie, out flag)) { // parse cookie - _preActions.Add(new ChangeCookiePreAction(flag)); + _actions.Add(new ChangeCookieAction(flag)); } if (flags.GetValue(FlagType.Env, out flag)) { // parse env - _preActions.Add(new ChangeEnvironmentPreAction(flag)); + _actions.Add(new ChangeEnvironmentAction(flag)); } if (flags.HasFlag(FlagType.Forbidden)) { - _action = new ForbiddenAction(); + _actions.Add(new ForbiddenAction()); } else if (flags.HasFlag(FlagType.Gone)) { - _action = new GoneAction(); + _actions.Add(new GoneAction()); } else { @@ -210,13 +203,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.ModRewrite { throw new FormatException(Resources.FormatError_InputParserInvalidInteger(statusCode, -1)); } - _action = new RedirectAction(res, pattern, queryStringAppend, queryStringDelete, escapeBackReference); + _actions.Add(new RedirectAction(res, pattern, queryStringAppend, queryStringDelete, escapeBackReference)); } else { var last = flags.HasFlag(FlagType.End) || flags.HasFlag(FlagType.Last); var termination = last ? RuleTermination.StopRules : RuleTermination.Continue; - _action = new RewriteAction(termination, pattern, queryStringAppend, queryStringDelete, escapeBackReference); + _actions.Add(new RewriteAction(termination, pattern, queryStringAppend, queryStringDelete, escapeBackReference)); } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs index 3a2bcb21dd..11daadce68 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/ModRewriteRule.cs @@ -10,15 +10,13 @@ namespace Microsoft.AspNetCore.Rewrite.Internal { public UrlMatch InitialMatch { get; } public IList Conditions { get; } - public UrlAction Action { get; } - public IList PreActions { get; } + public IList Actions { get; } - public ModRewriteRule(UrlMatch initialMatch, IList conditions, UrlAction urlAction, IList preActions) + public ModRewriteRule(UrlMatch initialMatch, IList conditions, IList urlActions) { Conditions = conditions; InitialMatch = initialMatch; - Action = urlAction; - PreActions = preActions; + Actions = urlActions; } public override void ApplyRule(RewriteContext context) @@ -46,12 +44,11 @@ namespace Microsoft.AspNetCore.Rewrite.Internal // 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 context.Logger?.ModRewriteMatchedRule(); - foreach (var preAction in PreActions) - { - preAction.ApplyAction(context.HttpContext, initMatchRes, condMatchRes); - } - Action.ApplyAction(context, initMatchRes, condMatchRes); + foreach (var action in Actions) + { + action.ApplyAction(context, initMatchRes, condMatchRes); + } } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs index c5a72eef55..3b3f39339d 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsModSegment.cs @@ -5,6 +5,8 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { public class IsHttpsModSegment : PatternSegment { + // Note: Mod rewrite pattern matches on lower case "on" and "off" + // while IIS looks for capitalized "ON" and "OFF" public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) { return context.HttpContext.Request.IsHttps ? "on" : "off"; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsSegment.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs similarity index 69% rename from src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsSegment.cs rename to src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs index be940dd284..21beccc746 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsSegment.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/PatternSegments/IsHttpsUrlSegment.cs @@ -3,8 +3,10 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.PatternSegments { - public class IsHttpsSegment : PatternSegment - { + public class IsHttpsUrlSegment : PatternSegment + { + // Note: Mod rewrite pattern matches on lower case "on" and "off" + // while IIS looks for capitalized "ON" and "OFF" public override string Evaluate(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) { return context.HttpContext.Request.IsHttps ? "ON" : "OFF"; diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs deleted file mode 100644 index e86bc03f3c..0000000000 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PreAction.cs +++ /dev/null @@ -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. - -using Microsoft.AspNetCore.Http; - -namespace Microsoft.AspNetCore.Rewrite.Internal -{ - public abstract class PreAction - { - public abstract void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch); - } -} diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeCookiePreAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs similarity index 58% rename from src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeCookiePreAction.cs rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs index a08506158f..3f666abb79 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeCookiePreAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeCookieAction.cs @@ -4,17 +4,17 @@ using System; using Microsoft.AspNetCore.Http; -namespace Microsoft.AspNetCore.Rewrite.Internal.PreActions +namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { - public class ChangeCookiePreAction : PreAction + public class ChangeCookieAction : UrlAction { - public ChangeCookiePreAction(string cookie) + public ChangeCookieAction(string cookie) { // TODO throw new NotImplementedException(cookie); } - public override void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) { // modify the cookies diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeEnvironmentPreAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeEnvironmentAction.cs similarity index 60% rename from src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeEnvironmentPreAction.cs rename to src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeEnvironmentAction.cs index 4e79c0f300..09710350e4 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/PreActions/ChangeEnvironmentPreAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/ChangeEnvironmentAction.cs @@ -4,17 +4,17 @@ using System; using Microsoft.AspNetCore.Http; -namespace Microsoft.AspNetCore.Rewrite.Internal.PreActions +namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { - public class ChangeEnvironmentPreAction : PreAction + public class ChangeEnvironmentAction : UrlAction { - public ChangeEnvironmentPreAction(string env) + public ChangeEnvironmentAction(string env) { // TODO throw new NotImplementedException(); } - public override void ApplyAction(HttpContext context, MatchResults ruleMatch, MatchResults condMatch) + public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) { // Do stuff to modify the env throw new NotImplementedException(); diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs index ef616c16b5..9e6b636205 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RedirectAction.cs @@ -41,7 +41,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions queryStringDelete: true, escapeBackReferences: false) { - } public override void ApplyAction(RewriteContext context, MatchResults ruleMatch, MatchResults condMatch) @@ -58,7 +57,6 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { pattern = '/' + pattern; } - response.StatusCode = StatusCode; // url can either contain the full url or the path and query diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs index c95bf909a6..efb8468ed3 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlActions/RewriteAction.cs @@ -46,6 +46,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlActions { var pattern = Url.Evaluate(context, ruleMatch, condMatch); var request = context.HttpContext.Request; + if (EscapeBackReferences) { // because escapebackreferences will be encapsulated by the pattern, just escape the pattern diff --git a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs index 8a0fad4150..0bb2764aa4 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Internal/UrlRewrite/ServerVariables.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Rewrite.Internal.UrlRewrite case "HTTP_URL": return new UrlSegment(); case "HTTPS": - return new IsHttpsSegment(); + return new IsHttpsUrlSegment(); case "LOCAL_ADDR": return new LocalAddressSegment(); case "HTTP_PROXY_CONNECTION": diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs index 76dd9c6989..d7dec645f7 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RewriteContext.cs @@ -9,15 +9,31 @@ using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Rewrite { /// - /// 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. + /// A context object for /// public class RewriteContext { + /// + /// Gets and sets the + /// public HttpContext HttpContext { get; set; } + + /// + /// Gets and sets the File Provider for file and directory checks. + /// public IFileProvider StaticFileProvider { get; set; } + + /// + /// Gets and sets the logger + /// public ILogger Logger { get; set; } + + /// + /// A shared result that is set appropriately by each rule for the next action that + /// should be take. See + /// public RuleTermination Result { get; set; } + // PERF: share the same string builder per request internal StringBuilder Builder { get; set; } = new StringBuilder(64); } diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs index 10e1a0a1a1..d02a28a6db 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RewriteMiddleware.cs @@ -5,7 +5,6 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite.Internal; using Microsoft.AspNetCore.Rewrite.Logging; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteOptions.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteOptions.cs index 270c7a9313..f4370cabb4 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RewriteOptions.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RewriteOptions.cs @@ -11,8 +11,14 @@ namespace Microsoft.AspNetCore.Rewrite /// public class RewriteOptions { - // TODO doc comments + /// + /// A list of that will be applied in order upon a request. + /// public IList Rules { get; } = new List(); + + /// + /// Gets and sets the File Provider for file and directory checks. + /// public IFileProvider StaticFileProvider { get; set; } } } diff --git a/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs b/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs index 76c4a1e791..4d89e13d3c 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RewriteOptionsExtensions.cs @@ -44,9 +44,9 @@ namespace Microsoft.AspNetCore.Rewrite /// The regex string to compare with. /// If the regex matches, what to replace HttpContext with. /// The Rewrite options. - public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string replacement) + public static RewriteOptions AddRewrite(this RewriteOptions options, string regex, string replacement) { - return Rewrite(options, regex, replacement, stopProcessing: false); + return AddRewrite(options, regex, replacement, stopProcessing: false); } /// @@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Rewrite /// If the regex matches, what to replace the uri with. /// If the regex matches, conditionally stop processing other rules. /// The Rewrite options. - public static RewriteOptions Rewrite(this RewriteOptions options, string regex, string replacement, bool stopProcessing) + public static RewriteOptions AddRewrite(this RewriteOptions options, string regex, string replacement, bool stopProcessing) { options.Rules.Add(new RewriteRule(regex, replacement, stopProcessing)); return options; @@ -70,9 +70,9 @@ namespace Microsoft.AspNetCore.Rewrite /// The regex string to compare with. /// If the regex matches, what to replace the uri with. /// The Rewrite options. - public static RewriteOptions Redirect(this RewriteOptions options, string regex, string replacement) + public static RewriteOptions AddRedirect(this RewriteOptions options, string regex, string replacement) { - return Redirect(options, regex, replacement, statusCode: 302); + return AddRedirect(options, regex, replacement, statusCode: 302); } /// @@ -83,24 +83,30 @@ namespace Microsoft.AspNetCore.Rewrite /// If the regex matches, what to replace the uri with. /// The status code to add to the response. /// The Rewrite options. - public static RewriteOptions Redirect(this RewriteOptions options, string regex, string replacement, int statusCode) + public static RewriteOptions AddRedirect(this RewriteOptions options, string regex, string replacement, int statusCode) { options.Rules.Add(new RedirectRule(regex, replacement, statusCode)); return options; } - public static RewriteOptions RedirectToHttpsPermanent(this RewriteOptions options) + /// + /// Redirect a request to https if the incoming request is http, with returning a 301 + /// status code for permanently redirected. + /// + /// + /// + public static RewriteOptions AddRedirectToHttpsPermanent(this RewriteOptions options) { - return RedirectToHttps(options, statusCode: 301, sslPort: null); + return AddRedirectToHttps(options, statusCode: 301, sslPort: null); } /// /// Redirect a request to https if the incoming request is http /// /// The Rewrite options. - public static RewriteOptions RedirectToHttps(this RewriteOptions options) + public static RewriteOptions AddRedirectToHttps(this RewriteOptions options) { - return RedirectToHttps(options, statusCode: 302, sslPort: null); + return AddRedirectToHttps(options, statusCode: 302, sslPort: null); } /// @@ -108,9 +114,9 @@ namespace Microsoft.AspNetCore.Rewrite /// /// The Rewrite options. /// The status code to add to the response. - public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode) + public static RewriteOptions AddRedirectToHttps(this RewriteOptions options, int statusCode) { - return RedirectToHttps(options, statusCode, sslPort: null); + return AddRedirectToHttps(options, statusCode, sslPort: null); } /// @@ -119,7 +125,7 @@ namespace Microsoft.AspNetCore.Rewrite /// The Rewrite options. /// The status code to add to the response. /// The SSL port to add to the response. - public static RewriteOptions RedirectToHttps(this RewriteOptions options, int statusCode, int? sslPort) + public static RewriteOptions AddRedirectToHttps(this RewriteOptions options, int statusCode, int? sslPort) { options.Rules.Add(new RedirectToHttpsRule { StatusCode = statusCode, SSLPort = sslPort }); return options; diff --git a/src/Microsoft.AspNetCore.Rewrite/Rule.cs b/src/Microsoft.AspNetCore.Rewrite/Rule.cs index 90eef4b050..22b5a253e0 100644 --- a/src/Microsoft.AspNetCore.Rewrite/Rule.cs +++ b/src/Microsoft.AspNetCore.Rewrite/Rule.cs @@ -3,10 +3,17 @@ namespace Microsoft.AspNetCore.Rewrite { - // make this public and doc comements - // caller must set the context.Results field appropriately in rule. + /// + /// Represents an abstract rule. + /// public abstract class Rule { + /// + /// Applies the rule. + /// Implementations of ApplyRule should set the value for RewriteContext.Results + /// (defaults to RuleTermination.Continue) + /// + /// public abstract void ApplyRule(RewriteContext context); } } diff --git a/src/Microsoft.AspNetCore.Rewrite/RuleTermination.cs b/src/Microsoft.AspNetCore.Rewrite/RuleTermination.cs index dff816d9cc..70f5c9f39f 100644 --- a/src/Microsoft.AspNetCore.Rewrite/RuleTermination.cs +++ b/src/Microsoft.AspNetCore.Rewrite/RuleTermination.cs @@ -3,10 +3,22 @@ namespace Microsoft.AspNetCore.Rewrite { + /// + /// An enum representing the result of a rule. + /// public enum RuleTermination { + /// + /// Default value, continue applying rules. + /// Continue, + /// + /// Redirect occured, should send back new rewritten url. + /// ResponseComplete, + /// + /// Stop applying rules and send context to the next middleware + /// StopRules } } diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs index bb284b74a5..1e4e10beb3 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/CodeRules/MiddlewareTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules [Fact] public async Task CheckRewritePath() { - var options = new RewriteOptions().Rewrite("(.*)", "http://example.com/$1"); + var options = new RewriteOptions().AddRewrite("(.*)", "http://example.com/$1"); var builder = new WebHostBuilder() .Configure(app => { @@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules [Fact] public async Task CheckRedirectPath() { - var options = new RewriteOptions().Redirect("(.*)","http://example.com/$1", statusCode: 301); + var options = new RewriteOptions().AddRedirect("(.*)","http://example.com/$1", statusCode: 301); var builder = new WebHostBuilder() .Configure(app => { @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.CodeRules [Fact] public async Task CheckRedirectToHttps() { - var options = new RewriteOptions().RedirectToHttps(statusCode: 301); + var options = new RewriteOptions().AddRedirectToHttps(statusCode: 301); var builder = new WebHostBuilder() .Configure(app => { diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs index 7b358c265f..2fd055a7cb 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/ModRewrite/ModRewriteMiddlewareTest.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_RewritePathWhenMatching() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader("RewriteRule /hey/(.*) /$1 ")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader("RewriteRule /hey/(.*) /$1 ")); var builder = new WebHostBuilder() .Configure(app => { @@ -34,8 +34,8 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_RewritePathTerminatesOnFirstSuccessOfRule() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader("RewriteRule /hey/(.*) /$1 [L]")) - .ImportFromModRewrite(new StringReader("RewriteRule /hello /what")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader("RewriteRule /hey/(.*) /$1 [L]")) + .AddApacheModRewrite(new StringReader("RewriteRule /hello /what")); var builder = new WebHostBuilder() .Configure(app => { @@ -52,8 +52,8 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_RewritePathDoesNotTerminateOnFirstSuccessOfRule() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader("RewriteRule /hey/(.*) /$1")) - .ImportFromModRewrite(new StringReader("RewriteRule /hello /what")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader("RewriteRule /hey/(.*) /$1")) + .AddApacheModRewrite(new StringReader("RewriteRule /hello /what")); var builder = new WebHostBuilder() .Configure(app => { @@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_ShouldIgnoreComments() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader("#RewriteRule ^/hey/(.*) /$1 ")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader("#RewriteRule ^/hey/(.*) /$1 ")); var builder = new WebHostBuilder() .Configure(app => { @@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_ShouldRewriteHomepage() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader(@"RewriteRule ^/$ /homepage.html")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader(@"RewriteRule ^/$ /homepage.html")); var builder = new WebHostBuilder() .Configure(app => { @@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_ShouldIgnorePorts() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader(@"RewriteRule ^/$ /homepage.html")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader(@"RewriteRule ^/$ /homepage.html")); var builder = new WebHostBuilder() .Configure(app => { @@ -121,7 +121,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_HandleNegatedRewriteRules() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader(@"RewriteRule !^/$ /homepage.html")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader(@"RewriteRule !^/$ /homepage.html")); var builder = new WebHostBuilder() .Configure(app => { @@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [Fact] public async Task Invoke_BackReferencesShouldBeApplied() { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader(@"RewriteRule (.*)\.aspx $1.php")); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader(@"RewriteRule (.*)\.aspx $1.php")); var builder = new WebHostBuilder() .Configure(app => { @@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite [InlineData("http://www.foo.org/homepage.ASPX", @"RewriteRule (.*)\.aspx $1.php [nocase]", "/homepage.php")] public async Task Invoke_ShouldHandleFlagNoCase(string url, string rule, string expected) { - var options = new RewriteOptions().ImportFromModRewrite(new StringReader(rule)); + var options = new RewriteOptions().AddApacheModRewrite(new StringReader(rule)); var builder = new WebHostBuilder() .Configure(app => { @@ -179,7 +179,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite public async Task Invoke_CheckFullUrlWithOnlyPath() { var options = new RewriteOptions() - .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/")); + .AddApacheModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/")); var builder = new WebHostBuilder() .Configure(app => { @@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite public async Task Invoke_CheckFullUrlWithUFlag() { var options = new RewriteOptions() - .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/")); + .AddApacheModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/")); var builder = new WebHostBuilder() .Configure(app => { @@ -215,7 +215,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite public async Task Invoke_CheckModFileConditions() { var options = new RewriteOptions() - .ImportFromModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/")); + .AddApacheModRewrite(new StringReader(@"RewriteRule (.+) http://www.example.com$1/")); var builder = new WebHostBuilder() .Configure(app => { @@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.ModRewrite public async Task Invoke_EnsureHttps(string input) { var options = new RewriteOptions() - .ImportFromModRewrite(new StringReader("RewriteCond %{REQUEST_URI} /foo/ \nRewriteCond %{HTTPS} !on \nRewriteRule ^(.*)$ https://www.example.com$1 [R=301,L]")); + .AddApacheModRewrite(new StringReader("RewriteCond %{REQUEST_URI} /foo/ \nRewriteCond %{HTTPS} !on \nRewriteRule ^(.*)$ https://www.example.com$1 [R=301,L]")); var builder = new WebHostBuilder() .Configure(app => { diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/IsHttpsSegmentTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/IsHttpsSegmentTests.cs index c3d3b6eccc..789f94e670 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/IsHttpsSegmentTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/PatternSegments/IsHttpsSegmentTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.PatternSegments public void IsHttps_AssertCorrectBehaviorWhenProvidedHttpContext(string input, string expected) { // Arrange - var segement = new IsHttpsSegment(); + var segement = new IsHttpsUrlSegment(); var context = new RewriteContext { HttpContext = new DefaultHttpContext() }; context.HttpContext.Request.Scheme = input; diff --git a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/MiddleWareTests.cs b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/MiddleWareTests.cs index eadddbd60d..af460e9f13 100644 --- a/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/MiddleWareTests.cs +++ b/test/Microsoft.AspNetCore.Rewrite.Tests/UrlRewrite/MiddleWareTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RedirectPathToPathAndQuery() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RewritePathToPathAndQuery() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RewriteBasedOnQueryStringParameters() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RedirectToLowerCase() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RedirectRemoveTrailingSlash() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -145,7 +145,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RedirectAddTrailingSlash() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -172,7 +172,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RedirectToHttps() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -198,7 +198,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_RewriteToHttps() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@" @@ -230,7 +230,7 @@ namespace Microsoft.AspNetCore.Rewrite.Tests.UrlRewrite [Fact] public async Task Invoke_ReverseProxyToAnotherSite() { - var options = new RewriteOptions().ImportFromUrlRewrite(new StringReader(@" + var options = new RewriteOptions().AddIISUrlRewrite(new StringReader(@"