From 42a97778b06fff7e3007e60f16e6cc4ce5e97ca5 Mon Sep 17 00:00:00 2001 From: Paulo Morgado <470455+paulomorgado@users.noreply.github.com> Date: Tue, 29 Dec 2020 15:47:50 +0000 Subject: [PATCH] Use UriHelper.BuildAbsolute and UriHelper.BuildRelative for RedirectRule --- src/Middleware/Rewrite/src/RedirectRule.cs | 72 +++++++++++++------ .../Rewrite/test/MiddlewareTests.cs | 23 ++++-- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/Middleware/Rewrite/src/RedirectRule.cs b/src/Middleware/Rewrite/src/RedirectRule.cs index e9b502295e91..edce6c2c0a3b 100644 --- a/src/Middleware/Rewrite/src/RedirectRule.cs +++ b/src/Middleware/Rewrite/src/RedirectRule.cs @@ -4,8 +4,9 @@ using System; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; -using Microsoft.Net.Http.Headers; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Rewrite.Logging; +using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Rewrite { @@ -34,13 +35,14 @@ public RedirectRule(string regex, string replacement, int statusCode) public virtual void ApplyRule(RewriteContext context) { - var path = context.HttpContext.Request.Path; - var pathBase = context.HttpContext.Request.PathBase; + var request = context.HttpContext.Request; + var path = request.Path; + var pathBase = request.PathBase; Match initMatchResults; - if (path == PathString.Empty) + if (!path.HasValue) { - initMatchResults = InitialMatch.Match(path.ToString()); + initMatchResults = InitialMatch.Match(string.Empty); } else { @@ -56,31 +58,55 @@ public virtual void ApplyRule(RewriteContext context) response.StatusCode = StatusCode; context.Result = RuleResult.EndResponse; - if (string.IsNullOrEmpty(newPath)) - { - response.Headers[HeaderNames.Location] = pathBase.HasValue ? pathBase.Value : "/"; - return; - } - - if (newPath.IndexOf(Uri.SchemeDelimiter, StringComparison.Ordinal) == -1 && newPath[0] != '/') - { - newPath = '/' + newPath; - } + string encodedPath; - var split = newPath.IndexOf('?'); - if (split >= 0) + if (string.IsNullOrEmpty(newPath)) { - var query = context.HttpContext.Request.QueryString.Add( - QueryString.FromUriComponent( - newPath.Substring(split))); - // not using the HttpContext.Response.redirect here because status codes may be 301, 302, 307, 308 - response.Headers[HeaderNames.Location] = pathBase + newPath.Substring(0, split) + query.ToUriComponent(); + encodedPath = pathBase.HasValue ? pathBase.Value : "/"; } else { - response.Headers[HeaderNames.Location] = pathBase + newPath + context.HttpContext.Request.QueryString.ToUriComponent(); + var host = default(HostString); + var schemeSplit = newPath.IndexOf(Uri.SchemeDelimiter, StringComparison.Ordinal); + if (schemeSplit >= 0) + { + schemeSplit += Uri.SchemeDelimiter.Length; + var pathSplit = newPath.IndexOf('/', schemeSplit); + + if (pathSplit == -1) + { + host = new HostString(newPath.Substring(schemeSplit)); + newPath = "/"; + } + else + { + host = new HostString(newPath.Substring(schemeSplit, pathSplit - schemeSplit)); + newPath = newPath.Substring(pathSplit); + } + } + + if (newPath[0] != '/') + { + newPath = '/' + newPath; + } + + var resolvedQuery = request.QueryString; + var resolvedPath = newPath; + var querySplit = newPath.IndexOf('?'); + if (querySplit >= 0) + { + resolvedQuery = request.QueryString.Add(QueryString.FromUriComponent(newPath.Substring(querySplit))); + resolvedPath = newPath.Substring(0, querySplit); + } + + encodedPath = host.HasValue + ? UriHelper.BuildAbsolute(request.Scheme, host, pathBase, resolvedPath, resolvedQuery, default) + : UriHelper.BuildRelative(pathBase, resolvedPath, resolvedQuery, default); } + // not using the HttpContext.Response.redirect here because status codes may be 301, 302, 307, 308 + response.Headers[HeaderNames.Location] = encodedPath; + context.Logger.RedirectedRequest(newPath); } } diff --git a/src/Middleware/Rewrite/test/MiddlewareTests.cs b/src/Middleware/Rewrite/test/MiddlewareTests.cs index 21fb4bdef8f6..f687f6ec74a5 100644 --- a/src/Middleware/Rewrite/test/MiddlewareTests.cs +++ b/src/Middleware/Rewrite/test/MiddlewareTests.cs @@ -45,10 +45,19 @@ public async Task CheckRewritePath() Assert.Equal("http://example.com/foo", response); } - [Fact] - public async Task CheckRedirectPath() + [Theory] + [InlineData("(.*)", "http://example.com/$1", null, "path", "http://example.com/path")] + [InlineData("(.*)", "http://example.com", null, "", "http://example.com/")] + [InlineData("(z*)", "$1", null, "path", "/")] + [InlineData("(z*)", "http://example.com/$1", null, "path", "http://example.com/")] + [InlineData("(z*)", "$1", "http://example.com/pathBase", "/pathBase/path", "/pathBase")] + [InlineData("path/(.*)", "path?value=$1", null, "path/value", "/path?value=value")] + [InlineData("path/(.*)", "path?param=$1", null, "path/value?param1=OtherValue", "/path?param1=OtherValue¶m=value")] + [InlineData("path/(.*)", "http://example.com/pathBase/path?param=$1", "http://example.com/pathBase", "path/value?param1=OtherValue", "http://example.com/pathBase/path?param1=OtherValue¶m=value")] + [InlineData("path/(.*)", "http://hoψst.com/pÂthBase/path?parãm=$1", "http://example.com/pathBase", "path/value?päram1=OtherValüe", "http://xn--host-cpd.com/p%C3%82thBase/path?p%C3%A4ram1=OtherVal%C3%BCe&parãm=value")] + public async Task CheckRedirectPath(string pattern, string replacement, string baseAddress, string requestUrl, string expectedUrl) { - var options = new RewriteOptions().AddRedirect("(.*)", "http://example.com/$1", statusCode: StatusCodes.Status301MovedPermanently); + var options = new RewriteOptions().AddRedirect(pattern, replacement, statusCode: StatusCodes.Status301MovedPermanently); using var host = new HostBuilder() .ConfigureWebHost(webHostBuilder => { @@ -63,10 +72,14 @@ public async Task CheckRedirectPath() await host.StartAsync(); var server = host.GetTestServer(); + if (!string.IsNullOrEmpty(baseAddress)) + { + server.BaseAddress = new Uri(baseAddress); + } - var response = await server.CreateClient().GetAsync("foo"); + var response = await server.CreateClient().GetAsync(requestUrl); - Assert.Equal("http://example.com/foo", response.Headers.Location.OriginalString); + Assert.Equal(expectedUrl, response.Headers.Location.OriginalString); } [Fact]