From c6d8333c7a28b752397e171540306ceccf74ca12 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 28 Oct 2017 11:53:54 -0400 Subject: improve criteria for subdomain rewriting (#358) --- .../ConditionalRewriteSubdomainRule.cs | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs (limited to 'src/SMAPI.Web/Framework/RewriteRules') diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs new file mode 100644 index 00000000..83f23058 --- /dev/null +++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RewriteRules +{ + /// Rewrite requests to prepend the subdomain portion (if any) to the path. + /// Derived from . + internal class ConditionalRewriteSubdomainRule : IRule + { + /********* + ** Accessors + *********/ + /// 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 ConditionalRewriteSubdomainRule(Func shouldRewrite = null) + { + this.ShouldRewrite = shouldRewrite; + } + + /// 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.ShouldRewrite != null && !this.ShouldRewrite(request)) + return; + + // get host parts + string host = request.Host.Host; + string[] parts = host.Split('.'); + if (parts.Length < 2) + return; + + // prepend to path + request.Path = $"/{parts[0]}{request.Path}"; + } + } +} -- cgit From d545281ef3d83d4db43d5ca56eb59800c8a1b8d2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 28 Oct 2017 12:24:50 -0400 Subject: redirect web views to HTTPS (#358) --- .../RewriteRules/ConditionalRedirectToHttpsRule.cs | 62 ++++++++++++++++++++++ .../ConditionalRewriteSubdomainRule.cs | 4 +- src/SMAPI.Web/Startup.cs | 7 +++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs (limited to 'src/SMAPI.Web/Framework/RewriteRules') diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs new file mode 100644 index 00000000..d6a56bb7 --- /dev/null +++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs @@ -0,0 +1,62 @@ +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 + { + /********* + ** Properties + *********/ + /// 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/ConditionalRewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs index 83f23058..920632ab 100644 --- a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs +++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs @@ -22,7 +22,7 @@ namespace StardewModdingAPI.Web.Framework.RewriteRules /// A predicate which indicates when the rule should be applied. public ConditionalRewriteSubdomainRule(Func shouldRewrite = null) { - this.ShouldRewrite = shouldRewrite; + this.ShouldRewrite = shouldRewrite ?? (req => true); } /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). @@ -32,7 +32,7 @@ namespace StardewModdingAPI.Web.Framework.RewriteRules HttpRequest request = context.HttpContext.Request; // check condition - if (this.ShouldRewrite != null && !this.ShouldRewrite(request)) + if (!this.ShouldRewrite(request)) return; // get host parts diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 0f656e55..b27ff9a5 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -66,6 +66,13 @@ namespace StardewModdingAPI.Web loggerFactory.AddDebug(); app .UseRewriter(new RewriteOptions() + // redirect to HTTPS (except API for Linux/Mac Mono compatibility) + .Add(new ConditionalRedirectToHttpsRule( + shouldRewrite: req => + req.Host.Host != "localhost" + && !req.Path.StartsWithSegments("/api") + )) + // convert subdomain.smapi.io => smapi.io/subdomain for routing .Add(new ConditionalRewriteSubdomainRule( shouldRewrite: req => -- cgit From 13baaf8920f4a80ac3c0cd41a16b9afb1b993048 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 29 Oct 2017 22:18:08 -0400 Subject: add smapi.io shortcut URLs (#375) --- .../Framework/RewriteRules/RedirectToUrlRule.cs | 61 ++++++++++++++++++++++ src/SMAPI.Web/Startup.cs | 4 ++ 2 files changed, 65 insertions(+) create mode 100644 src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs (limited to 'src/SMAPI.Web/Framework/RewriteRules') diff --git a/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs new file mode 100644 index 00000000..0719e311 --- /dev/null +++ b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs @@ -0,0 +1,61 @@ +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 + { + /********* + ** Properties + *********/ + /// A predicate which indicates when the rule should be applied. + private readonly Func ShouldRewrite; + + /// The new URL to which to redirect. + private readonly string 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.ShouldRewrite = shouldRewrite ?? (req => true); + this.NewUrl = url; + } + + /// 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.ShouldRewrite = req => req.Path.HasValue && regex.IsMatch(req.Path.Value); + this.NewUrl = url; + } + + /// 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.ShouldRewrite(request)) + return; + + // redirect request + HttpResponse response = context.HttpContext.Response; + response.StatusCode = (int)HttpStatusCode.Redirect; + response.Headers["Location"] = this.NewUrl; + context.Result = RuleResult.EndResponse; + } + } +} diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 860354f1..bc491128 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -79,6 +79,10 @@ namespace StardewModdingAPI.Web && (req.Host.Host.StartsWith("api.") || req.Host.Host.StartsWith("log.")) && !req.Path.StartsWithSegments("/content") )) + + // shortcut redirects + .Add(new RedirectToUrlRule("^/docs$", "https://stardewvalleywiki.com/Modding:Index")) + .Add(new RedirectToUrlRule("^/install$", "https://stardewvalleywiki.com/Modding:Installing_SMAPI")) ) .UseStaticFiles() // wwwroot folder .UseMvc(); -- cgit