From 5f532c259d5d3050bd6a053659067617db136d57 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 1 Dec 2019 21:55:20 -0500 Subject: migrate from AWS to Azure This commit migrates from subdomains to paths (due to the cost of a wildcard HTTPS certificate on Azure), adds a web project to redirect the old subdomains from AWS to Azure, and removes AWS-specific hacks. --- .../Controllers/ModsApiController.cs | 33 ++++++++ .../Framework/LambdaRewriteRule.cs | 37 +++++++++ src/SMAPI.Web.LegacyRedirects/Program.cs | 23 ++++++ .../Properties/launchSettings.json | 29 +++++++ .../SMAPI.Web.LegacyRedirects.csproj | 21 +++++ src/SMAPI.Web.LegacyRedirects/Startup.cs | 94 ++++++++++++++++++++++ 6 files changed, 237 insertions(+) create mode 100644 src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs create mode 100644 src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs create mode 100644 src/SMAPI.Web.LegacyRedirects/Program.cs create mode 100644 src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json create mode 100644 src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj create mode 100644 src/SMAPI.Web.LegacyRedirects/Startup.cs (limited to 'src/SMAPI.Web.LegacyRedirects') diff --git a/src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs b/src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs new file mode 100644 index 00000000..44ed0b6b --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Controllers/ModsApiController.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Pathoschild.Http.Client; +using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; + +namespace SMAPI.Web.LegacyRedirects.Controllers +{ + /// Provides an API to perform mod update checks. + [ApiController] + [Produces("application/json")] + [Route("api/v{version}/mods")] + public class ModsApiController : Controller + { + /********* + ** Public methods + *********/ + /// Fetch version metadata for the given mods. + /// The mod search criteria. + [HttpPost] + public async Task> PostAsync([FromBody] ModSearchModel model) + { + using IClient client = new FluentClient("https://smapi.io/api"); + + Startup.ConfigureJsonNet(client.Formatters.JsonFormatter.SerializerSettings); + + return await client + .PostAsync(this.Request.Path) + .WithBody(model) + .AsArray(); + } + } +} diff --git a/src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs b/src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs new file mode 100644 index 00000000..e5138e5c --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Framework/LambdaRewriteRule.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace SMAPI.Web.LegacyRedirects.Framework +{ + /// Rewrite requests to prepend the subdomain portion (if any) to the path. + /// Derived from . + internal class LambdaRewriteRule : IRule + { + /********* + ** Accessors + *********/ + /// Rewrite an HTTP request if needed. + private readonly Action Rewrite; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Rewrite an HTTP request if needed. + public LambdaRewriteRule(Action rewrite) + { + this.Rewrite = rewrite ?? throw new ArgumentNullException(nameof(rewrite)); + } + + /// 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; + HttpResponse response = context.HttpContext.Response; + this.Rewrite(context, request, response); + } + } +} diff --git a/src/SMAPI.Web.LegacyRedirects/Program.cs b/src/SMAPI.Web.LegacyRedirects/Program.cs new file mode 100644 index 00000000..6adee877 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Program.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace SMAPI.Web.LegacyRedirects +{ + /// The main app entry point. + public class Program + { + /********* + ** Public methods + *********/ + /// The main app entry point. + /// The command-line arguments. + public static void Main(string[] args) + { + Host + .CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(builder => builder.UseStartup()) + .Build() + .Run(); + } + } +} diff --git a/src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json b/src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json new file mode 100644 index 00000000..e9a1b210 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52756", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "SMAPI.Web.LegacyRedirects": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "/", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj b/src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj new file mode 100644 index 00000000..a3d5c2b6 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/SMAPI.Web.LegacyRedirects.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.0 + + + + + + + + + + + + + + + + + diff --git a/src/SMAPI.Web.LegacyRedirects/Startup.cs b/src/SMAPI.Web.LegacyRedirects/Startup.cs new file mode 100644 index 00000000..4af51575 --- /dev/null +++ b/src/SMAPI.Web.LegacyRedirects/Startup.cs @@ -0,0 +1,94 @@ +using System.Net; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Rewrite; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using SMAPI.Web.LegacyRedirects.Framework; +using StardewModdingAPI.Toolkit.Serialization; + +namespace SMAPI.Web.LegacyRedirects +{ + /// The web app startup configuration. + public class Startup + { + /********* + ** Public methods + *********/ + /// The method called by the runtime to add services to the container. + /// The service injection container. + public void ConfigureServices(IServiceCollection services) + { + services + .AddControllers() + .AddNewtonsoftJson(options => Startup.ConfigureJsonNet(options.SerializerSettings)); + } + + /// The method called by the runtime to configure the HTTP request pipeline. + /// The application builder. + /// The hosting environment. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + app.UseDeveloperExceptionPage(); + + app + .UseRewriter(this.GetRedirectRules()) + .UseRouting() + .UseAuthorization() + .UseEndpoints(endpoints => endpoints.MapControllers()); + } + + /// Configure a Json.NET serializer. + /// The serializer settings to edit. + internal static void ConfigureJsonNet(JsonSerializerSettings settings) + { + foreach (JsonConverter converter in new JsonHelper().JsonSettings.Converters) + settings.Converters.Add(converter); + + settings.Formatting = Formatting.Indented; + settings.NullValueHandling = NullValueHandling.Ignore; + } + + + /********* + ** Private methods + *********/ + /// Get the redirect rules to apply. + private RewriteOptions GetRedirectRules() + { + var redirects = new RewriteOptions(); + + redirects.Add( + new LambdaRewriteRule((context, request, response) => + { + string host = request.Host.Host; + + // map API requests to proxy + // This is needed because the low-level HTTP client SMAPI uses for Linux/Mac compatibility doesn't support redirects. + if (host == "api.smapi.io") + { + request.Path = $"/api{request.Path}"; + return; + } + + // redirect other requests to Azure + string newRoot = host switch + { + "api.smapi.io" => "smapi.io/api", + "json.smapi.io" => "smapi.io/json", + "log.smapi.io" => "smapi.io/log", + "mods.smapi.io" => "smapi.io/mods", + _ => "smapi.io" + }; + response.StatusCode = (int)HttpStatusCode.PermanentRedirect; + response.Headers["Location"] = $"{(request.IsHttps ? "https" : "http")}://{newRoot}{request.PathBase}{request.Path}{request.QueryString}"; + context.Result = RuleResult.EndResponse; + }) + ); + + return redirects; + } + } +} -- cgit