summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web/Framework')
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectHostsToUrlsRule.cs54
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectMatchRule.cs58
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectPathsToUrlsRule.cs54
-rw-r--r--src/SMAPI.Web/Framework/RedirectRules/RedirectToHttpsRule.cs47
-rw-r--r--src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs62
-rw-r--r--src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs57
6 files changed, 213 insertions, 119 deletions
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
+{
+ /// <summary>Redirect hostnames to a URL if they match a condition.</summary>
+ internal class RedirectHostsToUrlsRule : RedirectMatchRule
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>Maps a lowercase hostname to the resulting redirect URL.</summary>
+ private readonly Func<string, string> Map;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="statusCode">The status code to use for redirects.</param>
+ /// <param name="map">Hostnames mapped to the resulting redirect URL.</param>
+ public RedirectHostsToUrlsRule(HttpStatusCode statusCode, Func<string, string> map)
+ {
+ this.StatusCode = statusCode;
+ this.Map = map ?? throw new ArgumentNullException(nameof(map));
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Get the new redirect URL.</summary>
+ /// <param name="context">The rewrite context.</param>
+ /// <returns>Returns the redirect URL, or <c>null</c> if the redirect doesn't apply.</returns>
+ 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
+{
+ /// <summary>Redirect matching requests to a URL.</summary>
+ internal abstract class RedirectMatchRule : IRule
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The status code to use for redirects.</summary>
+ protected HttpStatusCode StatusCode { get; set; } = HttpStatusCode.Redirect;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Applies the rule. Implementations of ApplyRule should set the value for <see cref="RewriteContext.Result" /> (defaults to RuleResult.ContinueRules).</summary>
+ /// <param name="context">The rewrite context.</param>
+ 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
+ *********/
+ /// <summary>Get the new redirect URL.</summary>
+ /// <param name="context">The rewrite context.</param>
+ /// <returns>Returns the redirect URL, or <c>null</c> if the redirect doesn't apply.</returns>
+ protected abstract string GetNewUrl(RewriteContext context);
+
+ /// <summary>Get the full request URL.</summary>
+ /// <param name="request">The request.</param>
+ 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
+{
+ /// <summary>Redirect paths to URLs if they match a condition.</summary>
+ internal class RedirectPathsToUrlsRule : RedirectMatchRule
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>Regex patterns matching the current URL mapped to the resulting redirect URL.</summary>
+ private readonly IDictionary<Regex, string> Map;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="map">Regex patterns matching the current URL mapped to the resulting redirect URL.</param>
+ public RedirectPathsToUrlsRule(IDictionary<string, string> map)
+ {
+ this.Map = map.ToDictionary(
+ p => new Regex(p.Key, RegexOptions.IgnoreCase | RegexOptions.Compiled),
+ p => p.Value
+ );
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get the new redirect URL.</summary>
+ /// <param name="context">The rewrite context.</param>
+ /// <returns>Returns the redirect URL, or <c>null</c> if the redirect doesn't apply.</returns>
+ 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
+{
+ /// <summary>Redirect requests to HTTPS.</summary>
+ internal class RedirectToHttpsRule : RedirectMatchRule
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>Matches requests which should be ignored.</summary>
+ private readonly Func<HttpRequest, bool> Except;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="except">Matches requests which should be ignored.</param>
+ public RedirectToHttpsRule(Func<HttpRequest, bool> except = null)
+ {
+ this.Except = except ?? (req => false);
+ this.StatusCode = HttpStatusCode.RedirectKeepVerb;
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get the new redirect URL.</summary>
+ /// <param name="context">The rewrite context.</param>
+ /// <returns>Returns the redirect URL, or <c>null</c> if the redirect doesn't apply.</returns>
+ 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
-{
- /// <summary>Redirect requests to HTTPS.</summary>
- /// <remarks>Derived from <a href="https://stackoverflow.com/a/44526747/262123" /> and <see cref="Microsoft.AspNetCore.Rewrite.Internal.RedirectToHttpsRule"/>.</remarks>
- internal class ConditionalRedirectToHttpsRule : IRule
- {
- /*********
- ** Fields
- *********/
- /// <summary>A predicate which indicates when the rule should be applied.</summary>
- private readonly Func<HttpRequest, bool> ShouldRewrite;
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an instance.</summary>
- /// <param name="shouldRewrite">A predicate which indicates when the rule should be applied.</param>
- public ConditionalRedirectToHttpsRule(Func<HttpRequest, bool> shouldRewrite = null)
- {
- this.ShouldRewrite = shouldRewrite ?? (req => true);
- }
-
- /// <summary>Applies the rule. Implementations of ApplyRule should set the value for <see cref="RewriteContext.Result" /> (defaults to RuleResult.ContinueRules).</summary>
- /// <param name="context">The rewrite context.</param>
- 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;
- }
-
- /// <summary>Get whether the request was received over HTTPS.</summary>
- /// <param name="request">The request to check.</param>
- 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
-{
- /// <summary>Redirect requests to an external URL if they match a condition.</summary>
- internal class RedirectToUrlRule : IRule
- {
- /*********
- ** Fields
- *********/
- /// <summary>Get the new URL to which to redirect (or <c>null</c> to skip).</summary>
- private readonly Func<HttpRequest, string> NewUrl;
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an instance.</summary>
- /// <param name="shouldRewrite">A predicate which indicates when the rule should be applied.</param>
- /// <param name="url">The new URL to which to redirect.</param>
- public RedirectToUrlRule(Func<HttpRequest, bool> shouldRewrite, string url)
- {
- this.NewUrl = req => shouldRewrite(req) ? url : null;
- }
-
- /// <summary>Construct an instance.</summary>
- /// <param name="pathRegex">A case-insensitive regex to match against the path.</param>
- /// <param name="url">The external URL.</param>
- 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;
- }
-
- /// <summary>Applies the rule. Implementations of ApplyRule should set the value for <see cref="RewriteContext.Result" /> (defaults to RuleResult.ContinueRules).</summary>
- /// <param name="context">The rewrite context.</param>
- 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;
- }
- }
-}