diff --git a/build/Version.props b/build/Version.props index 911e6616e3..a5d010ffb8 100644 --- a/build/Version.props +++ b/build/Version.props @@ -2,7 +2,7 @@ - 4.2.7 + 4.3.0 6.5.0 7.1.0 5.2.1 diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index 539b3ce319..b9443b8897 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -162,22 +162,9 @@ namespace Tgstation.Server.Client var headersToUse = tokenRefresh ? tokenRefreshHeaders! : headers; headersToUse.SetRequestHeaders(request.Headers, instanceId); - // This is meant to be a gate against token refresh operations - await semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); - if(!tokenRefresh) - semaphoreSlim.Release(); + await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(request, cancellationToken))).ConfigureAwait(false); - try - { - await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(request, cancellationToken))).ConfigureAwait(false); - - response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); - } - finally - { - if (tokenRefresh) - semaphoreSlim.Release(); - } + response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); } using (response) @@ -214,8 +201,13 @@ namespace Tgstation.Server.Client if (tokenRefreshHeaders == null) return false; + var startingToken = headers.Token; + await semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); try { + if (startingToken != headers.Token) + return true; + var token = await RunRequest(Routes.Root, new object(), HttpMethod.Post, null, true, cancellationToken); headers = new ApiHeaders(headers.UserAgent!, token.Bearer!); } @@ -223,6 +215,10 @@ namespace Tgstation.Server.Client { return false; } + finally + { + semaphoreSlim.Release(); + } return true; } diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs index 9b5f34f5f6..e20d5cb3df 100644 --- a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs @@ -14,6 +14,11 @@ namespace Tgstation.Server.Host.Configuration /// public const string Section = "General"; + /// + /// The default value of . + /// + public const ushort DefaultApiPort = 5000; + /// /// The default value for . /// @@ -39,6 +44,11 @@ namespace Tgstation.Server.Host.Configuration /// const int DefaultRestartTimeout = 10000; + /// + /// The port the TGS API listens on. + /// + public ushort ApiPort { get; set; } + /// /// A GitHub personal access token to use for bypassing rate limits on requests. Requires no scopes /// diff --git a/src/Tgstation.Server.Host/Core/ServerPortProivder.cs b/src/Tgstation.Server.Host/Core/ServerPortProivder.cs index a71dc72a29..04c009949d 100644 --- a/src/Tgstation.Server.Host/Core/ServerPortProivder.cs +++ b/src/Tgstation.Server.Host/Core/ServerPortProivder.cs @@ -1,6 +1,9 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Linq; +using Tgstation.Server.Host.Configuration; namespace Tgstation.Server.Host.Core { @@ -8,14 +11,25 @@ namespace Tgstation.Server.Host.Core sealed class ServerPortProivder : IServerPortProvider { /// - public ushort HttpApiPort { get; } + public ushort HttpApiPort => generalConfiguration.ApiPort; + + /// + /// The for the . + /// + readonly GeneralConfiguration generalConfiguration; /// /// Initializes a new instance of the . /// + /// The containing the value of . /// The to use. - public ServerPortProivder(IConfiguration configuration) + /// The to use. + public ServerPortProivder( + IOptions generalConfigurationOptions, + IConfiguration configuration, + ILogger logger) { + generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions)); if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -26,8 +40,13 @@ namespace Tgstation.Server.Host.Core .GetSection("Url") .Value; - if (httpEndpoint == null) - throw new InvalidOperationException("Missing required configuration option Kestrel:EndPoints:Http:Url!"); + if (generalConfiguration.ApiPort == default && httpEndpoint == null) + throw new InvalidOperationException("Missing required configuration option General:ApiPort!"); + + if (generalConfiguration.ApiPort != default) + return; + + logger.LogWarning("The \"Kestrel\" configuration section is deprecated! Please set your API port using the \"General:ApiPort\" configuration option!"); var splits = httpEndpoint.Split(":", StringSplitOptions.RemoveEmptyEntries); var portString = splits.Last(); @@ -36,7 +55,7 @@ namespace Tgstation.Server.Host.Core if (!UInt16.TryParse(portString, out var result)) throw new InvalidOperationException($"Failed to parse HTTP EndPoint port: {httpEndpoint}"); - HttpApiPort = result; + generalConfiguration.ApiPort = result; } } } diff --git a/src/Tgstation.Server.Host/ServerFactory.cs b/src/Tgstation.Server.Host/ServerFactory.cs index 32289426bc..6bb31a8948 100644 --- a/src/Tgstation.Server.Host/ServerFactory.cs +++ b/src/Tgstation.Server.Host/ServerFactory.cs @@ -1,10 +1,12 @@ using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Setup; @@ -58,8 +60,17 @@ namespace Tgstation.Server.Host } var hostBuilder = CreateDefaultBuilder() - .ConfigureWebHostDefaults(webHostBuilder => + .ConfigureWebHost(webHostBuilder => webHostBuilder + .UseKestrel(kestrelOptions => + { + var serverPortProvider = kestrelOptions.ApplicationServices.GetRequiredService(); + kestrelOptions.ListenAnyIP( + serverPortProvider.HttpApiPort, + listenOptions => listenOptions.Protocols = HttpProtocols.Http1AndHttp2); + }) + .UseIIS() + .UseIISIntegration() .UseApplication(postSetupServices) .SuppressStatusMessages(true) .UseShutdownTimeout(TimeSpan.FromMinutes(1))); diff --git a/src/Tgstation.Server.Host/Setup/SetupWizard.cs b/src/Tgstation.Server.Host/Setup/SetupWizard.cs index 72eb7e3509..9e54ac8f00 100644 --- a/src/Tgstation.Server.Host/Setup/SetupWizard.cs +++ b/src/Tgstation.Server.Host/Setup/SetupWizard.cs @@ -141,7 +141,11 @@ namespace Tgstation.Server.Host.Setup do { - await console.WriteAsync("API Port (leave blank for default): ", false, cancellationToken).ConfigureAwait(false); + await console.WriteAsync( + $"API Port (leave blank for default of {GeneralConfiguration.DefaultApiPort}): ", + false, + cancellationToken) + .ConfigureAwait(false); var portString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false); if (String.IsNullOrWhiteSpace(portString)) return null; @@ -732,6 +736,7 @@ namespace Tgstation.Server.Host.Setup { await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Configuration complete! Saving to {0}", userConfigFileName), true, cancellationToken).ConfigureAwait(false); + newGeneralConfiguration.ApiPort = hostingPort ?? GeneralConfiguration.DefaultApiPort; var map = new Dictionary() { { DatabaseConfiguration.Section, databaseConfiguration }, @@ -740,18 +745,6 @@ namespace Tgstation.Server.Host.Setup { ControlPanelConfiguration.Section, controlPanelConfiguration } }; - if (hostingPort.HasValue) - map.Add("Kestrel", new - { - EndPoints = new - { - Http = new - { - Url = String.Format(CultureInfo.InvariantCulture, "http://0.0.0.0:{0}", hostingPort) - } - } - }); - var json = JsonConvert.SerializeObject(map, Formatting.Indented); var configBytes = Encoding.UTF8.GetBytes(json); diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 09c0c09f16..4382558c26 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -83,7 +83,7 @@ - + diff --git a/src/Tgstation.Server.Host/appsettings.json b/src/Tgstation.Server.Host/appsettings.json index 2457e64d56..e7f47e59b1 100644 --- a/src/Tgstation.Server.Host/appsettings.json +++ b/src/Tgstation.Server.Host/appsettings.json @@ -1,5 +1,6 @@ { "General": { + "ApiPort": 5000, "MinimumPasswordLength": 15, "GitHubAccessToken": null, "SetupWizardMode": "AutoDetect", @@ -17,13 +18,6 @@ "LogLevel": "Debug", "MicrosoftLogLevel": "Warning" }, - "Kestrel": { - "EndPoints": { - "Http": { - "Url": "http://0.0.0.0:5000" - } - } - }, "Logging": { "IncludeScopes": false, "Debug": { diff --git a/tests/Tgstation.Server.Tests/TestingServer.cs b/tests/Tgstation.Server.Tests/TestingServer.cs index 7b7df505cc..c420bebd7f 100644 --- a/tests/Tgstation.Server.Tests/TestingServer.cs +++ b/tests/Tgstation.Server.Tests/TestingServer.cs @@ -72,7 +72,7 @@ namespace Tgstation.Server.Tests var args = new List() { String.Format(CultureInfo.InvariantCulture, "Database:DropDatabase={0}", true), - String.Format(CultureInfo.InvariantCulture, "Kestrel:EndPoints:Http:Url={0}", UrlString), + String.Format(CultureInfo.InvariantCulture, "General:ApiPort={0}", 5010), String.Format(CultureInfo.InvariantCulture, "Database:DatabaseType={0}", DatabaseType), String.Format(CultureInfo.InvariantCulture, "Database:ConnectionString={0}", connectionString), String.Format(CultureInfo.InvariantCulture, "General:SetupWizardMode={0}", SetupWizardMode.Never),