From 0646fd58afa78a40b0d4de81d2845aef124d2c75 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 21 Apr 2020 23:55:59 -0400 Subject: [PATCH] Finish documenting DMAPI constructs. Get tests running --- src/DMAPI/tgs/v5/_defines.dm | 6 +-- .../Components/Chat/ChatFactory.cs | 1 + .../Components/Chat/ChatUser.cs | 2 +- .../Components/InstanceFactory.cs | 2 +- .../Interop/Bridge/BridgeCommandType.cs | 28 +++++++++- .../Interop/Bridge/BridgeParameters.cs | 24 +++++++-- .../Interop/Bridge/BridgeResponse.cs | 9 +++- .../Components/Interop/ChatMessage.cs | 11 +++- .../Components/Interop/DMApiParameters.cs | 8 ++- .../Components/Interop/DMApiResponse.cs | 13 +++++ .../Components/Interop/IBridgeHandler.cs | 2 +- .../Components/Interop/Topic/ChatCommand.cs | 4 +- .../Interop/Topic/EventNotification.cs | 16 +++++- .../Interop/Topic/TopicCommandType.cs | 2 +- .../Interop/Topic/TopicParameters.cs | 48 ++++++++++++++++- .../Components/Interop/Topic/TopicResponse.cs | 13 +++-- .../Components/Watchdog/SessionController.cs | 17 ++++-- .../Watchdog/SessionControllerFactory.cs | 2 +- .../Components/Watchdog/WatchdogBase.cs | 18 ++++++- src/Tgstation.Server.Host/Core/Application.cs | 8 --- .../Core/IServerPortProvider.cs | 15 ++---- .../Core/ServerPortProivder.cs | 54 +++++++------------ .../Core/TestApplication.cs | 16 +++--- tests/Tgstation.Server.Tests/TestingServer.cs | 5 +- 24 files changed, 227 insertions(+), 97 deletions(-) create mode 100644 src/Tgstation.Server.Host/Components/Interop/DMApiResponse.cs diff --git a/src/DMAPI/tgs/v5/_defines.dm b/src/DMAPI/tgs/v5/_defines.dm index 1147817406..2f060b3a76 100644 --- a/src/DMAPI/tgs/v5/_defines.dm +++ b/src/DMAPI/tgs/v5/_defines.dm @@ -1,7 +1,7 @@ #define DMAPI5_PARAM_RUNTIME_INFORMATION_FILE "tgs_json" #define DMAPI5_TOPIC_DATA "data" -#define DMAPI5_BRIDGE_COMMAND_NEW_PORT 0 +#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0 #define DMAPI5_BRIDGE_COMMAND_VALIDATE 1 #define DMAPI5_BRIDGE_COMMAND_PRIME 2 #define DMAPI5_BRIDGE_COMMAND_REBOOT 3 @@ -10,8 +10,8 @@ #define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier" -#define DMAPI5_BRIDGE_PARAMETER_COMMAND "commandType" -#define DMAPI5_BRIDGE_PARAMETER_NEW_PORT "newPort" +#define DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE "commandType" +#define DMAPI5_BRIDGE_PARAMETER_CURRENT_PORT "currentPort" #define DMAPI5_BRIDGE_PARAMETER_VERSION "version" #define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage" #define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel" diff --git a/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs b/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs index 15d353e644..83ef08518a 100644 --- a/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs +++ b/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Tgstation.Server.Host.Components.Chat.Commands; +using Tgstation.Server.Host.Components.Chat.Providers; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.IO; diff --git a/src/Tgstation.Server.Host/Components/Chat/ChatUser.cs b/src/Tgstation.Server.Host/Components/Chat/ChatUser.cs index f74f099fdd..981030528f 100644 --- a/src/Tgstation.Server.Host/Components/Chat/ChatUser.cs +++ b/src/Tgstation.Server.Host/Components/Chat/ChatUser.cs @@ -35,7 +35,7 @@ namespace Tgstation.Server.Host.Components.Chat public string Mention { get; set; } /// - /// The the user spoke from + /// The the user spoke from /// public ChatChannel Channel { get; set; } } diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index 78366142ee..b064babd4d 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -68,7 +68,7 @@ namespace Tgstation.Server.Host.Components readonly IByondInstaller byondInstaller; /// - /// The for the + /// The for the /// readonly IChatFactory chatFactory; diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs index 1736fc5b8d..c3b98605f8 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeCommandType.cs @@ -1,12 +1,38 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge { + /// + /// Represents the . + /// public enum BridgeCommandType { - NewPort, + /// + /// DreamDaemon notifying us of its current port and requesting a change if necessary. + /// + PortUpdate, + + /// + /// DreamDaemon responding to an API validation request. + /// Validate, + + /// + /// DreamDaemon notifying the server is primed + /// Prime, + + /// + /// DreamDaemon notifiying the server is calling /world/Reboot(). + /// Reboot, + + /// + /// DreamDaemon requesting the process be terminated. + /// Kill, + + /// + /// DreamDaemon requesting a be sent. + /// ChatSend } } diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs index 7d84cd8fc0..64800b15a0 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeParameters.cs @@ -3,16 +3,34 @@ using Tgstation.Server.Api.Models; namespace Tgstation.Server.Host.Components.Interop.Bridge { + /// + /// Parameters for a bridge request. + /// public sealed class BridgeParameters : DMApiParameters { + /// + /// The . + /// public BridgeCommandType? CommandType { get; set; } - public ushort? NewPort { get; set; } + /// + /// The current port for requests. + /// + public ushort? CurrentPort { get; set; } + /// + /// The DMAPI for requests. + /// public Version Version { get; set; } - public ChatMessage ChatMessage { get; set; } - + /// + /// The minimum required level for requests. + /// public DreamDaemonSecurity? MinimumSecurityLevel { get; set; } + + /// + /// The for requests. + /// + public ChatMessage ChatMessage { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeResponse.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeResponse.cs index 50c231f3e1..76801de132 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeResponse.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/BridgeResponse.cs @@ -1,8 +1,13 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge { - public sealed class BridgeResponse + /// + /// A response to a bridge request. + /// + public sealed class BridgeResponse : DMApiResponse { - public string ErrorMessage { get; set; } + /// + /// The new port for requests. + /// public ushort? NewPort { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Interop/ChatMessage.cs b/src/Tgstation.Server.Host/Components/Interop/ChatMessage.cs index ca13d25ce0..826f0e99ec 100644 --- a/src/Tgstation.Server.Host/Components/Interop/ChatMessage.cs +++ b/src/Tgstation.Server.Host/Components/Interop/ChatMessage.cs @@ -2,10 +2,19 @@ namespace Tgstation.Server.Host.Components.Interop { + /// + /// Represents a message to send to one or more s. + /// public sealed class ChatMessage { + /// + /// The message . + /// public string Text { get; set; } - public ICollection ChannelIds { get; set; } + /// + /// The of s to sent the to. Must be safe to parse as s. + /// + public ICollection ChannelIds { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Interop/DMApiParameters.cs b/src/Tgstation.Server.Host/Components/Interop/DMApiParameters.cs index 22b8785be8..db63c8393d 100644 --- a/src/Tgstation.Server.Host/Components/Interop/DMApiParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/DMApiParameters.cs @@ -1,7 +1,13 @@ namespace Tgstation.Server.Host.Components.Interop { - public class DMApiParameters + /// + /// Common base for interop parameters. + /// + public abstract class DMApiParameters { + /// + /// The for interop. + /// public string AccessIdentifier { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Interop/DMApiResponse.cs b/src/Tgstation.Server.Host/Components/Interop/DMApiResponse.cs new file mode 100644 index 0000000000..bb1adf4925 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Interop/DMApiResponse.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Host.Components.Interop +{ + /// + /// Common base for interop responses. + /// + public abstract class DMApiResponse + { + /// + /// Any errors in the client's parameters. + /// + public string ErrorMessage { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Interop/IBridgeHandler.cs b/src/Tgstation.Server.Host/Components/Interop/IBridgeHandler.cs index c7f431a504..ed828ced12 100644 --- a/src/Tgstation.Server.Host/Components/Interop/IBridgeHandler.cs +++ b/src/Tgstation.Server.Host/Components/Interop/IBridgeHandler.cs @@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.Components.Interop interface IBridgeHandler : IBridgeHandlerBase { /// - /// The for the . + /// The for the . /// string AccessIdentifier { get; } diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/ChatCommand.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/ChatCommand.cs index ec9a8ea641..63e42f04f9 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/ChatCommand.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/ChatCommand.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic public string Params { get; } /// - /// The that sent the command + /// The that sent the command /// public ChatUser User { get; } @@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic /// /// The value of . /// The value of . - /// The value of . + /// The value of . public ChatCommand(ChatUser user, string command, string parameters) { User = user ?? throw new ArgumentNullException(nameof(user)); diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/EventNotification.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/EventNotification.cs index eb6db4236c..e9183f5787 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/EventNotification.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/EventNotification.cs @@ -3,16 +3,30 @@ using System.Linq; namespace Tgstation.Server.Host.Components.Interop.Topic { + /// + /// Data structure for requests. + /// sealed class EventNotification { + /// + /// The triggered. + /// public EventType Type { get; } + /// + /// The set of parameters. + /// public IReadOnlyCollection Parameters { get; } + /// + /// Initializes a new instance of the . + /// + /// The value of . + /// The that forms the value of . public EventNotification(EventType eventType, IEnumerable parameters = null) { Type = eventType; Parameters = parameters?.ToList(); } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs index f254da4d00..627230626b 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicCommandType.cs @@ -13,7 +13,7 @@ /// /// Notification of a TGS event. /// - Event, + EventNotification, /// /// Port change request. diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs index 49628ecce6..ce4963ec63 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs @@ -3,48 +3,94 @@ using Tgstation.Server.Host.Components.Watchdog; namespace Tgstation.Server.Host.Components.Interop.Topic { + /// + /// Parameters for a topic request. + /// sealed class TopicParameters : DMApiParameters { + /// + /// The . + /// public TopicCommandType CommandType { get; } + /// + /// The for requests. + /// public ChatCommand ChatCommand { get; } + /// + /// The for requests. + /// public EventNotification EventNotification { get; } + /// + /// The new port for requests. + /// public ushort? NewPort { get; } + /// + /// The for requests. + /// public RebootState? NewRebootState { get; } + + /// + /// The new for requests. + /// public string NewInstanceName { get; } + /// + /// Initializes a new instance of the . + /// + /// The value of . private TopicParameters(TopicCommandType commandType) { CommandType = commandType; } + /// + /// Initializes a new instance of the . + /// + /// The value of . public TopicParameters(ChatCommand chatCommand) : this(TopicCommandType.ChatCommand) { ChatCommand = chatCommand ?? throw new ArgumentNullException(nameof(chatCommand)); } + /// + /// Initializes a new instance of the . + /// + /// The value of . public TopicParameters(EventNotification eventNotification) - : this(TopicCommandType.Event) + : this(TopicCommandType.EventNotification) { EventNotification = eventNotification ?? throw new ArgumentNullException(nameof(eventNotification)); } + /// + /// Initializes a new instance of the . + /// + /// The value of . public TopicParameters(ushort newPort) : this(TopicCommandType.ChangePort) { NewPort = newPort; } + /// + /// Initializes a new instance of the . + /// + /// The value of . public TopicParameters(RebootState newRebootState) : this(TopicCommandType.ChangeRebootState) { NewRebootState = newRebootState; } + /// + /// Initializes a new instance of the . + /// + /// The value of . public TopicParameters(string newInstanceName) : this(TopicCommandType.InstanceRenamed) { diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicResponse.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicResponse.cs index d181745d17..e0a397fae3 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicResponse.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicResponse.cs @@ -2,12 +2,19 @@ namespace Tgstation.Server.Host.Components.Interop.Topic { - sealed class TopicResponse + /// + /// A response to a topic request. + /// + sealed class TopicResponse : DMApiResponse { - public string ErrorMessage { get; set; } - + /// + /// The text to reply with as the result of a request, if any. + /// public string CommandResponseMessage { get; set; } + /// + /// The s to send as the result of a request, if any. + /// public ICollection ChatResponses { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 1b990cb9e5..1c4a05014f 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Globalization; +using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -316,6 +317,12 @@ namespace Tgstation.Server.Host.Components.Watchdog ErrorMessage = "Missing channelIds field in chatMessage!" }; + if(parameters.ChatMessage.ChannelIds.Any(channelIdString => !UInt64.TryParse(channelIdString, out var _))) + return new BridgeResponse + { + ErrorMessage = "Invalid channelIds in chatMessage!" + }; + if (parameters.ChatMessage.Text == null) return new BridgeResponse { @@ -324,7 +331,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await chat.SendMessage( parameters.ChatMessage.Text, - parameters.ChatMessage.ChannelIds, + parameters.ChatMessage.ChannelIds.Select(UInt64.Parse), cancellationToken).ConfigureAwait(false); break; case BridgeCommandType.Prime: @@ -334,10 +341,10 @@ namespace Tgstation.Server.Host.Components.Watchdog TerminationWasRequested = true; process.Terminate(); break; - case BridgeCommandType.NewPort: + case BridgeCommandType.PortUpdate: lock (this) { - if (!parameters.NewPort.HasValue) + if (!parameters.CurrentPort.HasValue) { /////UHHHH logger.LogWarning("DreamDaemon sent new port command without providing it's own!"); @@ -347,9 +354,9 @@ namespace Tgstation.Server.Host.Components.Watchdog }; } - var currentPort = parameters.NewPort.Value; + var currentPort = parameters.CurrentPort.Value; if (!nextPort.HasValue) - reattachInformation.Port = parameters.NewPort.Value; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to + reattachInformation.Port = parameters.CurrentPort.Value; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to else { // nextPort is ready, tell DD to switch to that diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index 3d5e444d34..159136a688 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -220,7 +220,7 @@ namespace Tgstation.Server.Host.Components.Watchdog JsonFile("chat_channels"), JsonFile("chat_commands"), securityLevelToUse, - await serverPortProvider.HttpApiPort.ConfigureAwait(false)); + serverPortProvider.HttpApiPort); var interopJsonFile = JsonFile("interop"); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 65ae20fe7c..e04ea576f5 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -447,13 +447,27 @@ namespace Tgstation.Server.Host.Components.Watchdog if (result?.ChatResponses == null) return true; - await Task.WhenAll(result.ChatResponses.Select(x => Chat.SendMessage(x.Text, x.ChannelIds, cancellationToken))).ConfigureAwait(false); + await Task.WhenAll( + result.ChatResponses.Select( + x => Chat.SendMessage( + x.Text, + x.ChannelIds + .Select(channelIdString => + { + if (UInt64.TryParse(channelIdString, out var channelId)) + return (ulong?)channelId; + + return null; + }) + .Where(nullableChannelId => nullableChannelId.HasValue) + .Select(nullableChannelId => nullableChannelId.Value), + cancellationToken))).ConfigureAwait(false); return true; } /// - public async Task HandleChatCommand(string commandName, string arguments, Chat.ChatUser sender, CancellationToken cancellationToken) + public async Task HandleChatCommand(string commandName, string arguments, ChatUser sender, CancellationToken cancellationToken) { using (await SemaphoreSlimContext.Lock(Semaphore, cancellationToken).ConfigureAwait(false)) { diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index c593584c0a..946485f1f2 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -376,7 +375,6 @@ namespace Tgstation.Server.Host.Core IApplicationBuilder applicationBuilder, IServerControl serverControl, ITokenFactory tokenFactory, - IServerPortProvider serverPortProvider, IOptions controlPanelConfigurationOptions, IOptions generalConfigurationOptions, ILogger logger) @@ -388,12 +386,6 @@ namespace Tgstation.Server.Host.Core this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory)); - if (serverPortProvider == null) - throw new ArgumentNullException(nameof(serverPortProvider)); - - var addressFeature = applicationBuilder?.ServerFeatures.Get(); - serverPortProvider.Configure(addressFeature); - var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions)); var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions)); diff --git a/src/Tgstation.Server.Host/Core/IServerPortProvider.cs b/src/Tgstation.Server.Host/Core/IServerPortProvider.cs index c1f98d4ab8..9727726367 100644 --- a/src/Tgstation.Server.Host/Core/IServerPortProvider.cs +++ b/src/Tgstation.Server.Host/Core/IServerPortProvider.cs @@ -1,7 +1,4 @@ -using Microsoft.AspNetCore.Hosting.Server.Features; -using System.Threading.Tasks; - -namespace Tgstation.Server.Host.Core +namespace Tgstation.Server.Host.Core { /// /// Provides access to the server's . @@ -9,14 +6,8 @@ namespace Tgstation.Server.Host.Core interface IServerPortProvider { /// - /// A resulting in the port the server listens on. + /// The port the server listens on. /// - Task HttpApiPort { get; } - - /// - /// Configures the . - /// - /// The to use. - void Configure(IServerAddressesFeature addressFeature); + ushort HttpApiPort { get; } } } diff --git a/src/Tgstation.Server.Host/Core/ServerPortProivder.cs b/src/Tgstation.Server.Host/Core/ServerPortProivder.cs index cbc90ebc03..244836e5af 100644 --- a/src/Tgstation.Server.Host/Core/ServerPortProivder.cs +++ b/src/Tgstation.Server.Host/Core/ServerPortProivder.cs @@ -1,7 +1,6 @@ -using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.Configuration; using System; using System.Linq; -using System.Threading.Tasks; namespace Tgstation.Server.Host.Core { @@ -9,38 +8,25 @@ namespace Tgstation.Server.Host.Core sealed class ServerPortProivder : IServerPortProvider { /// - public Task HttpApiPort => taskCompletionSource.Task; + public ushort HttpApiPort { get; } /// - /// Backing field for / + /// Initializes a new instance of the . /// - readonly TaskCompletionSource taskCompletionSource; - - /// - /// In - /// - public ServerPortProivder() + /// The to use. + public ServerPortProivder(IConfiguration configuration) { - taskCompletionSource = new TaskCompletionSource(); - } + if (configuration == null) + throw new ArgumentNullException(nameof(configuration)); - /// - public void Configure(IServerAddressesFeature addressFeature) - { - if (addressFeature == null) - throw new ArgumentNullException(nameof(addressFeature)); + var httpEndpoint = configuration + .GetSection("Kestrel") + .GetSection("Endpoints") + .GetSection("Http") + .GetSection("Url") + .Value; - var enumerator = addressFeature.Addresses.Select(GetPortFromAddress); - var newPort = enumerator.FirstOrDefault(x => x.HasValue); - - if(!newPort.HasValue) - throw new InvalidOperationException("At least one plain HTTP endpoint must be configured. Neded for BYOND -> Server communications!"); - - if (!addressFeature.Addresses.Select(GetPortFromAddress).All(x => !x.HasValue || x == newPort)) - throw new InvalidOperationException("All configured HTTP server addresses must use the same port!"); - - // Will fail if set twice - taskCompletionSource.SetResult(newPort.Value); + HttpApiPort = GetPortFromAddress(httpEndpoint); } /// @@ -48,18 +34,14 @@ namespace Tgstation.Server.Host.Core /// /// The address . /// The parsed port. - static ushort? GetPortFromAddress(string address) + static ushort GetPortFromAddress(string address) { var splits = address.Split(":", StringSplitOptions.RemoveEmptyEntries); - if (splits.First().Equals("https", StringComparison.OrdinalIgnoreCase)) - return null; - var portString = splits.Last(); portString = portString.TrimEnd('/'); - if (UInt16.TryParse(portString, out var result)) - return result; - - return null; + if (!UInt16.TryParse(portString, out var result)) + throw new InvalidOperationException("Failed to parse HTTP API port!"); + return result; } } } diff --git a/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs b/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs index a346bd8a39..7c73b9fdae 100644 --- a/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs +++ b/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -40,27 +41,24 @@ namespace Tgstation.Server.Host.Core.Tests var app = new Application(mockConfiguration.Object, mockAssemblyInfo.Object, mockHostingEnvironment.Object, Mock.Of()); Assert.ThrowsException(() => app.ConfigureServices(null)); - Assert.ThrowsException(() => app.Configure(null, null, null, null, null, null, null)); + Assert.ThrowsException(() => app.Configure(null, null, null, null, null, null)); var mockAppBuilder = new Mock(); - Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, null, null, null, null, null, null)); + Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, null, null, null, null, null)); var mockServerControl = new Mock(); - Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, null, null, null, null, null)); + Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, null, null, null, null)); var mockTokenFactory = new Mock(); - Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, null, null, null, null)); - - var mockServerPortProvider = new Mock(); - Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockServerPortProvider.Object, null, null, null)); + Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, null, null, null)); var mockControlPanelOptions = new Mock>(); mockControlPanelOptions.SetupGet(x => x.Value).Returns(new ControlPanelConfiguration()).Verifiable(); - Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockServerPortProvider.Object, mockControlPanelOptions.Object, null, null)); + Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockControlPanelOptions.Object, null, null)); var mockGeneralOptions = new Mock>(); mockGeneralOptions.SetupGet(x => x.Value).Returns(new GeneralConfiguration()).Verifiable(); - Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockServerPortProvider.Object, mockControlPanelOptions.Object, mockGeneralOptions.Object, null)); + Assert.ThrowsException(() => app.Configure(mockAppBuilder.Object, mockServerControl.Object, mockTokenFactory.Object, mockControlPanelOptions.Object, mockGeneralOptions.Object, null)); mockControlPanelOptions.VerifyAll(); mockGeneralOptions.VerifyAll(); } diff --git a/tests/Tgstation.Server.Tests/TestingServer.cs b/tests/Tgstation.Server.Tests/TestingServer.cs index 09de358599..8fc37c9f51 100644 --- a/tests/Tgstation.Server.Tests/TestingServer.cs +++ b/tests/Tgstation.Server.Tests/TestingServer.cs @@ -34,7 +34,8 @@ namespace Tgstation.Server.Tests Directory = Path.GetTempFileName(); File.Delete(Directory); System.IO.Directory.CreateDirectory(Directory); - Url = new Uri("http://localhost:5001"); + const string UrlString = "http://localhost:5001"; + Url = new Uri(UrlString); //so we need a db //we have to rely on env vars @@ -56,7 +57,7 @@ namespace Tgstation.Server.Tests var args = new List() { - String.Format(CultureInfo.InvariantCulture, "Kestrel:EndPoints:Http:Url={0}", Url), + String.Format(CultureInfo.InvariantCulture, "Kestrel:EndPoints:Http:Url={0}", UrlString), String.Format(CultureInfo.InvariantCulture, "Database:DatabaseType={0}", databaseType), String.Format(CultureInfo.InvariantCulture, "Database:ConnectionString={0}", connectionString), String.Format(CultureInfo.InvariantCulture, "Database:DropDatabase={0}", true),