From 9d86f20ca728811c1da908337a4d5e7a998e5b48 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 16 May 2020 20:01:52 -0400 Subject: migrate subdomain redirects to Azure --- .../RedirectRules/RedirectHostsToUrlsRule.cs | 54 +++++++++++++++++++ .../Framework/RedirectRules/RedirectMatchRule.cs | 58 ++++++++++++++++++++ .../RedirectRules/RedirectPathsToUrlsRule.cs | 54 +++++++++++++++++++ .../Framework/RedirectRules/RedirectToHttpsRule.cs | 47 ++++++++++++++++ .../RewriteRules/ConditionalRedirectToHttpsRule.cs | 62 ---------------------- .../Framework/RewriteRules/RedirectToUrlRule.cs | 57 -------------------- 6 files changed, 213 insertions(+), 119 deletions(-) create mode 100644 src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs create mode 100644 src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs create mode 100644 src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs create mode 100644 src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs delete mode 100644 src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs delete mode 100644 src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs new file mode 100644 index 00000000..d75ee791 --- /dev/null +++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs @@ -0,0 +1,54 @@ +using System; +using System.Net; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RedirectRules +{ + /// Redirect hostnames to a URL if they match a condition. + internal class RedirectHostsToUrlsRule : RedirectMatchRule + { + /********* + ** Fields + *********/ + /// Maps a lowercase hostname to the resulting redirect URL. + private readonly Func Map; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The status code to use for redirects. + /// Hostnames mapped to the resulting redirect URL. + public RedirectHostsToUrlsRule(HttpStatusCode statusCode, Func map) + { + this.StatusCode = statusCode; + this.Map = map ?? throw new ArgumentNullException(nameof(map)); + } + + + /********* + ** Private methods + *********/ + /// Get the new redirect URL. + /// The rewrite context. + /// Returns the redirect URL, or null if the redirect doesn't apply. + protected override string GetNewUrl(RewriteContext context) + { + // get requested host + string host = context.HttpContext.Request.Host.Host; + if (host == null) + return null; + + // get new host + host = this.Map(host); + if (host == null) + return null; + + // rewrite URL + UriBuilder uri = this.GetUrl(context.HttpContext.Request); + uri.Host = host; + return uri.ToString(); + } + } +} diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs new file mode 100644 index 00000000..6e81c4ca --- /dev/null +++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs @@ -0,0 +1,58 @@ +using System; +using System.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RedirectRules +{ + /// Redirect matching requests to a URL. + internal abstract class RedirectMatchRule : IRule + { + /********* + ** Fields + *********/ + /// The status code to use for redirects. + protected HttpStatusCode StatusCode { get; set; } = HttpStatusCode.Redirect; + + + /********* + ** Public methods + *********/ + /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). + /// The rewrite context. + public void ApplyRule(RewriteContext context) + { + string newUrl = this.GetNewUrl(context); + if (newUrl == null) + return; + + HttpResponse response = context.HttpContext.Response; + response.StatusCode = (int)HttpStatusCode.Redirect; + response.Headers["Location"] = newUrl; + context.Result = RuleResult.EndResponse; + } + + + /********* + ** Protected methods + *********/ + /// Get the new redirect URL. + /// The rewrite context. + /// Returns the redirect URL, or null if the redirect doesn't apply. + protected abstract string GetNewUrl(RewriteContext context); + + /// Get the full request URL. + /// The request. + protected UriBuilder GetUrl(HttpRequest request) + { + return new UriBuilder + { + Scheme = request.Scheme, + Host = request.Host.Host, + Port = request.Host.Port ?? -1, + Path = request.PathBase + request.Path, + Query = request.QueryString.Value + }; + } + } +} diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs new file mode 100644 index 00000000..16397c1e --- /dev/null +++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RedirectRules +{ + /// Redirect paths to URLs if they match a condition. + internal class RedirectPathsToUrlsRule : RedirectMatchRule + { + /********* + ** Fields + *********/ + /// Regex patterns matching the current URL mapped to the resulting redirect URL. + private readonly IDictionary Map; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Regex patterns matching the current URL mapped to the resulting redirect URL. + public RedirectPathsToUrlsRule(IDictionary map) + { + this.Map = map.ToDictionary( + p => new Regex(p.Key, RegexOptions.IgnoreCase | RegexOptions.Compiled), + p => p.Value + ); + } + + + /********* + ** Protected methods + *********/ + /// Get the new redirect URL. + /// The rewrite context. + /// Returns the redirect URL, or null if the redirect doesn't apply. + protected override string GetNewUrl(RewriteContext context) + { + string path = context.HttpContext.Request.Path.Value; + + if (!string.IsNullOrWhiteSpace(path)) + { + foreach ((Regex pattern, string url) in this.Map) + { + if (pattern.IsMatch(path)) + return pattern.Replace(path, url); + } + } + + return null; + } + } +} diff --git a/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs b/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs new file mode 100644 index 00000000..2a503ae3 --- /dev/null +++ b/src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs @@ -0,0 +1,47 @@ +using System; +using System.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RedirectRules +{ + /// Redirect requests to HTTPS. + internal class RedirectToHttpsRule : RedirectMatchRule + { + /********* + ** Fields + *********/ + /// Matches requests which should be ignored. + private readonly Func Except; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Matches requests which should be ignored. + public RedirectToHttpsRule(Func except = null) + { + this.Except = except ?? (req => false); + this.StatusCode = HttpStatusCode.RedirectKeepVerb; + } + + + /********* + ** Protected methods + *********/ + /// Get the new redirect URL. + /// The rewrite context. + /// Returns the redirect URL, or null if the redirect doesn't apply. + protected override string GetNewUrl(RewriteContext context) + { + HttpRequest request = context.HttpContext.Request; + if (request.IsHttps || this.Except(request)) + return null; + + UriBuilder uri = this.GetUrl(request); + uri.Scheme = "https"; + return uri.ToString(); + } + } +} diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs deleted file mode 100644 index 36effd82..00000000 --- a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Net; -using System.Text; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite; - -namespace StardewModdingAPI.Web.Framework.RewriteRules -{ - /// Redirect requests to HTTPS. - /// Derived from and . - internal class ConditionalRedirectToHttpsRule : IRule - { - /********* - ** Fields - *********/ - /// A predicate which indicates when the rule should be applied. - private readonly Func ShouldRewrite; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// A predicate which indicates when the rule should be applied. - public ConditionalRedirectToHttpsRule(Func shouldRewrite = null) - { - this.ShouldRewrite = shouldRewrite ?? (req => true); - } - - /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). - /// The rewrite context. - public void ApplyRule(RewriteContext context) - { - HttpRequest request = context.HttpContext.Request; - - // check condition - if (this.IsSecure(request) || !this.ShouldRewrite(request)) - return; - - // redirect request - HttpResponse response = context.HttpContext.Response; - response.StatusCode = (int)HttpStatusCode.RedirectKeepVerb; - response.Headers["Location"] = new StringBuilder() - .Append("https://") - .Append(request.Host.Host) - .Append(request.PathBase) - .Append(request.Path) - .Append(request.QueryString) - .ToString(); - context.Result = RuleResult.EndResponse; - } - - /// Get whether the request was received over HTTPS. - /// The request to check. - public bool IsSecure(HttpRequest request) - { - return - request.IsHttps // HTTPS to server - || string.Equals(request.Headers["x-forwarded-proto"], "HTTPS", StringComparison.OrdinalIgnoreCase); // HTTPS to AWS load balancer - } - } -} diff --git a/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs deleted file mode 100644 index ab9e019c..00000000 --- a/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Net; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite; - -namespace StardewModdingAPI.Web.Framework.RewriteRules -{ - /// Redirect requests to an external URL if they match a condition. - internal class RedirectToUrlRule : IRule - { - /********* - ** Fields - *********/ - /// Get the new URL to which to redirect (or null to skip). - private readonly Func NewUrl; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// A predicate which indicates when the rule should be applied. - /// The new URL to which to redirect. - public RedirectToUrlRule(Func shouldRewrite, string url) - { - this.NewUrl = req => shouldRewrite(req) ? url : null; - } - - /// Construct an instance. - /// A case-insensitive regex to match against the path. - /// The external URL. - public RedirectToUrlRule(string pathRegex, string url) - { - Regex regex = new Regex(pathRegex, RegexOptions.IgnoreCase | RegexOptions.Compiled); - this.NewUrl = req => req.Path.HasValue ? regex.Replace(req.Path.Value, url) : null; - } - - /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). - /// The rewrite context. - public void ApplyRule(RewriteContext context) - { - HttpRequest request = context.HttpContext.Request; - - // check rewrite - string newUrl = this.NewUrl(request); - if (newUrl == null || newUrl == request.Path.Value) - return; - - // redirect request - HttpResponse response = context.HttpContext.Response; - response.StatusCode = (int)HttpStatusCode.Redirect; - response.Headers["Location"] = newUrl; - context.Result = RuleResult.EndResponse; - } - } -} -- cgit