diff --git a/build/Version.props b/build/Version.props index 7559b69bfb..b3b449c726 100644 --- a/build/Version.props +++ b/build/Version.props @@ -7,7 +7,7 @@ 4.5.0 9.9.0 10.3.0 - 11.3.0 + 11.3.1 6.4.2 5.6.0 1.2.2 diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index a01db4e9d8..cfb7795d98 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -20,6 +20,7 @@ using Newtonsoft.Json.Serialization; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Response; +using Tgstation.Server.Common; namespace Tgstation.Server.Client { @@ -50,7 +51,7 @@ namespace Tgstation.Server.Client } /// - /// The for the . + /// The for the . /// readonly IHttpClient httpClient; diff --git a/src/Tgstation.Server.Client/ApiClientFactory.cs b/src/Tgstation.Server.Client/ApiClientFactory.cs index b003cb39c6..0e20eb3b00 100644 --- a/src/Tgstation.Server.Client/ApiClientFactory.cs +++ b/src/Tgstation.Server.Client/ApiClientFactory.cs @@ -1,6 +1,7 @@ using System; using Tgstation.Server.Api; +using Tgstation.Server.Common; namespace Tgstation.Server.Client { @@ -13,7 +14,7 @@ namespace Tgstation.Server.Client ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders, bool authless) => new ApiClient( - new HttpClientImplementation(), + new HttpClient(), url, apiHeaders, tokenRefreshHeaders, diff --git a/src/Tgstation.Server.Client/HttpClientImplementation.cs b/src/Tgstation.Server.Client/HttpClientImplementation.cs deleted file mode 100644 index d27708df2a..0000000000 --- a/src/Tgstation.Server.Client/HttpClientImplementation.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace Tgstation.Server.Client -{ - /// - sealed class HttpClientImplementation : IHttpClient - { - /// - public TimeSpan Timeout - { - get => httpClient.Timeout; - set => httpClient.Timeout = value; - } - - /// - /// The real . - /// - readonly HttpClient httpClient; - - /// - /// Initializes a new instance of the class. - /// - public HttpClientImplementation() - { - httpClient = new HttpClient(); - } - - /// - public void Dispose() => httpClient.Dispose(); - - /// - public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => httpClient.SendAsync(request, cancellationToken); - } -} diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj index 9abd30240d..e2272b2855 100644 --- a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj +++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj @@ -41,6 +41,7 @@ + diff --git a/src/Tgstation.Server.Common/HttpClient.cs b/src/Tgstation.Server.Common/HttpClient.cs new file mode 100644 index 0000000000..41dcf6dec4 --- /dev/null +++ b/src/Tgstation.Server.Common/HttpClient.cs @@ -0,0 +1,50 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Common +{ + /// + public sealed class HttpClient : IHttpClient + { + /// + public TimeSpan Timeout + { + get => httpClient.Timeout; + set => httpClient.Timeout = value; + } + + /// + public HttpRequestHeaders DefaultRequestHeaders => httpClient.DefaultRequestHeaders; + + /// + /// The real . + /// + readonly System.Net.Http.HttpClient httpClient; + + /// + /// Initializes a new instance of the class. + /// + /// The to wrap. + public HttpClient(System.Net.Http.HttpClient implementation) + { + httpClient = implementation ?? throw new ArgumentNullException(nameof(implementation)); + } + + /// + /// Initializes a new instance of the class. + /// + public HttpClient() + : this(new System.Net.Http.HttpClient()) + { + } + + /// + public void Dispose() => httpClient.Dispose(); + + /// + public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => httpClient.SendAsync(request, cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/IHttpClient.cs b/src/Tgstation.Server.Common/IHttpClient.cs similarity index 73% rename from src/Tgstation.Server.Client/IHttpClient.cs rename to src/Tgstation.Server.Common/IHttpClient.cs index e7e5ad5325..a1ce0190e0 100644 --- a/src/Tgstation.Server.Client/IHttpClient.cs +++ b/src/Tgstation.Server.Common/IHttpClient.cs @@ -1,20 +1,26 @@ using System; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -namespace Tgstation.Server.Client +namespace Tgstation.Server.Common { /// /// For sending HTTP requests. /// - interface IHttpClient : IDisposable + public interface IHttpClient : IDisposable { /// /// The request timeout. /// TimeSpan Timeout { get; set; } + /// + /// The used on every request. + /// + HttpRequestHeaders DefaultRequestHeaders { get; } + /// /// Send an HTTP request. /// diff --git a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj new file mode 100644 index 0000000000..748bb6ebb3 --- /dev/null +++ b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj @@ -0,0 +1,31 @@ + + + + + netstandard2.0 + Full + $(TgsCoreVersion) + ../../build/analyzers.ruleset + latest + enable + bin\$(Configuration)\netstandard2.0\Tgstation.Server.Client.xml + true + + + + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs index 67e80cda1f..0b26625bc7 100644 --- a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs +++ b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs @@ -39,14 +39,21 @@ namespace Tgstation.Server.Host.Components.Byond /// protected ILogger Logger { get; } + /// + /// The for the . + /// + readonly IFileDownloader fileDownloader; + /// /// Initializes a new instance of the class. /// /// The value of . + /// The value of . /// The value of . - protected ByondInstallerBase(IIOManager ioManager, ILogger logger) + protected ByondInstallerBase(IIOManager ioManager, IFileDownloader fileDownloader, ILogger logger) { IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -88,11 +95,9 @@ namespace Tgstation.Server.Host.Components.Byond if (version == null) throw new ArgumentNullException(nameof(version)); + Logger.LogTrace("Downloading BYOND version {major}.{minor}...", version.Major, version.Minor); var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Major, version.Minor); - - Logger.LogTrace("Downloading from: {0}", url); - - return IOManager.DownloadFile(new Uri(url), cancellationToken); + return fileDownloader.DownloadFile(new Uri(url), cancellationToken); } } } diff --git a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs index 876ed37aa7..ddd012ab0c 100644 --- a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs @@ -49,9 +49,14 @@ namespace Tgstation.Server.Host.Components.Byond /// /// The value of . /// The for the . + /// The for the . /// The for the . - public PosixByondInstaller(IPostWriteHandler postWriteHandler, IIOManager ioManager, ILogger logger) - : base(ioManager, logger) + public PosixByondInstaller( + IPostWriteHandler postWriteHandler, + IIOManager ioManager, + IFileDownloader fileDownloader, + ILogger logger) + : base(ioManager, fileDownloader, logger) { this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler)); @@ -89,7 +94,7 @@ namespace Tgstation.Server.Host.Components.Byond async Task WriteAndMakeExecutable(string pathToScript, string script) { - Logger.LogTrace("Writing script {0}:{1}{2}", pathToScript, Environment.NewLine, script); + Logger.LogTrace("Writing script {path}:{newLine}{scriptContents}", pathToScript, Environment.NewLine, script); await IOManager.WriteAllBytes(pathToScript, Encoding.ASCII.GetBytes(script), cancellationToken); postWriteHandler.HandleWrite(IOManager.ResolvePath(pathToScript)); } diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs index 76d47e26f6..bb999f2e89 100644 --- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs @@ -81,9 +81,15 @@ namespace Tgstation.Server.Host.Components.Byond /// The value of . /// The containing the value of . /// The for the . + /// The for the . /// The for the . - public WindowsByondInstaller(IProcessExecutor processExecutor, IIOManager ioManager, IOptions generalConfigurationOptions, ILogger logger) - : base(ioManager, logger) + public WindowsByondInstaller( + IProcessExecutor processExecutor, + IIOManager ioManager, + IFileDownloader fileDownloader, + IOptions generalConfigurationOptions, + ILogger logger) + : base(ioManager, fileDownloader, logger) { this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor)); generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions)); diff --git a/src/Tgstation.Server.Host/Core/AbstractHttpClientFactory.cs b/src/Tgstation.Server.Host/Core/AbstractHttpClientFactory.cs new file mode 100644 index 0000000000..c471bb937a --- /dev/null +++ b/src/Tgstation.Server.Host/Core/AbstractHttpClientFactory.cs @@ -0,0 +1,63 @@ +using System; +using System.Net.Http; + +using Microsoft.Extensions.Logging; + +using Tgstation.Server.Common; +using Tgstation.Server.Host.System; + +namespace Tgstation.Server.Host.Core +{ + /// + sealed class AbstractHttpClientFactory : IAbstractHttpClientFactory + { + /// + /// The real . + /// + readonly IHttpClientFactory httpClientFactory; + + /// + /// The for the . + /// + readonly IAssemblyInformationProvider assemblyInformationProvider; + + /// + /// The for the . + /// + readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// The value of . + /// The value of . + /// The value of . + public AbstractHttpClientFactory( + IHttpClientFactory httpClientFactory, + IAssemblyInformationProvider assemblyInformationProvider, + ILogger logger) + { + this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); + this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public IHttpClient CreateClient() + { + logger.LogTrace("Creating client..."); + var innerClient = httpClientFactory.CreateClient(); + try + { + var client = new Tgstation.Server.Common.HttpClient(innerClient); + client.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue); + return client; + } + catch + { + innerClient.Dispose(); + throw; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index e83fb92f81..33a3cd3ed2 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -73,7 +73,7 @@ namespace Tgstation.Server.Host.Core public static IServerFactory CreateDefaultServerFactory() { var assemblyInformationProvider = new AssemblyInformationProvider(); - var ioManager = new DefaultIOManager(assemblyInformationProvider); + var ioManager = new DefaultIOManager(); return new ServerFactory( assemblyInformationProvider, ioManager); @@ -253,6 +253,7 @@ namespace Tgstation.Server.Host.Core // Enable managed HTTP clients services.AddHttpClient(); + services.AddSingleton(); void AddTypedContext() where TContext : DatabaseContext { @@ -350,6 +351,7 @@ namespace Tgstation.Server.Host.Core services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(x => x.GetRequiredService()); services.AddSingleton(x => x.GetRequiredService()); diff --git a/src/Tgstation.Server.Host/Core/IAbstractHttpClientFactory.cs b/src/Tgstation.Server.Host/Core/IAbstractHttpClientFactory.cs new file mode 100644 index 0000000000..d031fb350f --- /dev/null +++ b/src/Tgstation.Server.Host/Core/IAbstractHttpClientFactory.cs @@ -0,0 +1,16 @@ +using Tgstation.Server.Common; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Creates s. + /// + public interface IAbstractHttpClientFactory + { + /// + /// Create a . + /// + /// A new . + IHttpClient CreateClient(); + } +} diff --git a/src/Tgstation.Server.Host/Core/ServerUpdater.cs b/src/Tgstation.Server.Host/Core/ServerUpdater.cs index 4c1a99eb79..b8139202de 100644 --- a/src/Tgstation.Server.Host/Core/ServerUpdater.cs +++ b/src/Tgstation.Server.Host/Core/ServerUpdater.cs @@ -30,6 +30,11 @@ namespace Tgstation.Server.Host.Core /// readonly IIOManager ioManager; + /// + /// The for the . + /// + readonly IFileDownloader fileDownloader; + /// /// The for the . /// @@ -55,18 +60,21 @@ namespace Tgstation.Server.Host.Core /// /// The value of . /// The value of . + /// The value of . /// The value of . /// The value of . /// The containing the value of . public ServerUpdater( IGitHubClientFactory gitHubClientFactory, IIOManager ioManager, + IFileDownloader fileDownloader, IServerControl serverControl, ILogger logger, IOptions updatesConfigurationOptions) { this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader)); this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); updatesConfiguration = updatesConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(updatesConfigurationOptions)); @@ -154,7 +162,7 @@ namespace Tgstation.Server.Host.Core try { logger.LogTrace("Downloading zip package..."); - updateZipData = await ioManager.DownloadFile(serverUpdateOperation.UpdateZipUrl, cancellationToken); + updateZipData = await fileDownloader.DownloadFile(serverUpdateOperation.UpdateZipUrl, cancellationToken); } catch (Exception e1) { diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs index 7ad9ad7872..6edc397308 100644 --- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs +++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs @@ -3,12 +3,10 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Host.Core; -using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.IO { @@ -32,11 +30,6 @@ namespace Tgstation.Server.Host.IO /// public const TaskCreationOptions BlockingTaskCreationOptions = TaskCreationOptions.None; - /// - /// The for the . - /// - readonly IAssemblyInformationProvider assemblyInformationProvider; - /// /// Recursively empty a directory. /// @@ -67,22 +60,6 @@ namespace Tgstation.Server.Host.IO dir.Delete(true); } - /// - /// Initializes a new instance of the class. - /// - /// The value of . - public DefaultIOManager(IAssemblyInformationProvider assemblyInformationProvider) - { - this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); - } - - /// - /// Initializes a new instance of the class. - /// - protected DefaultIOManager() - { - } - /// public async Task CopyDirectory( string src, @@ -296,29 +273,6 @@ namespace Tgstation.Server.Host.IO BlockingTaskCreationOptions, TaskScheduler.Current); - /// - public async Task DownloadFile(Uri url, CancellationToken cancellationToken) - { - using var httpClient = new HttpClient(); - httpClient.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue); - var webRequestTask = httpClient.GetAsync(url, cancellationToken); - using var response = await webRequestTask; - response.EnsureSuccessStatusCode(); - using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken); - var memoryStream = new MemoryStream(); - try - { - await responseStream.CopyToAsync(memoryStream, cancellationToken); - memoryStream.Seek(0, SeekOrigin.Begin); - return memoryStream; - } - catch - { - memoryStream.Dispose(); - throw; - } - } - /// public Task ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken) => Task.Factory.StartNew( () => diff --git a/src/Tgstation.Server.Host/IO/FileDownloader.cs b/src/Tgstation.Server.Host/IO/FileDownloader.cs new file mode 100644 index 0000000000..976f4d7ec7 --- /dev/null +++ b/src/Tgstation.Server.Host/IO/FileDownloader.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Extensions.Logging; + +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.IO +{ + /// + public sealed class FileDownloader : IFileDownloader + { + /// + /// The for the . + /// + readonly IAbstractHttpClientFactory httpClientFactory; + + /// + /// The for the . + /// + readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// The value of . + /// The value of . + public FileDownloader(IAbstractHttpClientFactory httpClientFactory, ILogger logger) + { + this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public async Task DownloadFile(Uri url, CancellationToken cancellationToken) + { + logger.LogDebug("Starting download of {url}...", url); + using var httpClient = httpClientFactory.CreateClient(); + using var request = new HttpRequestMessage( + HttpMethod.Get, + url); + + var webRequestTask = httpClient.SendAsync(request, cancellationToken); + using var response = await webRequestTask; + response.EnsureSuccessStatusCode(); + using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken); + var memoryStream = new MemoryStream(); + try + { + await responseStream.CopyToAsync(memoryStream, cancellationToken); + memoryStream.Seek(0, SeekOrigin.Begin); + return memoryStream; + } + catch + { + memoryStream.Dispose(); + throw; + } + } + } +} diff --git a/src/Tgstation.Server.Host/IO/IFileDownloader.cs b/src/Tgstation.Server.Host/IO/IFileDownloader.cs new file mode 100644 index 0000000000..199d904f06 --- /dev/null +++ b/src/Tgstation.Server.Host/IO/IFileDownloader.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.IO +{ + /// + /// Downloads files. + /// + interface IFileDownloader + { + /// + /// Downloads a file from . + /// + /// The URL to download. + /// A for the operation. + /// A resulting in a of the downloaded file. + Task DownloadFile(Uri url, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/IO/IIOManager.cs b/src/Tgstation.Server.Host/IO/IIOManager.cs index a51d130720..f9275e1216 100644 --- a/src/Tgstation.Server.Host/IO/IIOManager.cs +++ b/src/Tgstation.Server.Host/IO/IIOManager.cs @@ -192,14 +192,6 @@ namespace Tgstation.Server.Host.IO /// A representing the running operation. Task MoveDirectory(string source, string destination, CancellationToken cancellationToken); - /// - /// Downloads a file from . - /// - /// The URL to download. - /// A for the operation. - /// A resulting in a of the downloaded file. - Task DownloadFile(Uri url, CancellationToken cancellationToken); - /// /// Extract a set of to a given . /// diff --git a/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs index e74a6c9d4b..f147d20696 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs @@ -1,11 +1,10 @@ using System; -using System.Net.Http; using Microsoft.Extensions.Logging; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.System; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Security.OAuth { @@ -18,29 +17,27 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.Discord; /// - protected override Uri TokenUrl => new Uri("https://discord.com/api/oauth2/token"); + protected override Uri TokenUrl => new ("https://discord.com/api/oauth2/token"); /// - protected override Uri UserInformationUrl => new Uri("https://discord.com/api/users/@me"); + protected override Uri UserInformationUrl => new ("https://discord.com/api/users/@me"); /// /// Initializes a new instance of the class. /// - /// The for the . - /// The for the . + /// The for the . /// The for the . /// The for the . public DiscordOAuthValidator( - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, + IAbstractHttpClientFactory httpClientFactory, ILogger logger, OAuthConfiguration oAuthConfiguration) - : base(httpClientFactory, assemblyInformationProvider, logger, oAuthConfiguration) + : base(httpClientFactory, logger, oAuthConfiguration) { } /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "identify"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "identify"); /// protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; diff --git a/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs index f1cccb7437..a88003bb46 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs @@ -13,8 +13,9 @@ using Newtonsoft.Json.Serialization; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Common; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.System; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Security.OAuth { @@ -49,18 +50,13 @@ namespace Tgstation.Server.Host.Security.OAuth /// /// The for the . /// - readonly IHttpClientFactory httpClientFactory; - - /// - /// The for the . - /// - readonly IAssemblyInformationProvider assemblyInformationProvider; + readonly IAbstractHttpClientFactory httpClientFactory; /// /// Gets that should be used. /// /// A new . - protected static JsonSerializerSettings SerializerSettings() => new JsonSerializerSettings + protected static JsonSerializerSettings SerializerSettings() => new () { ContractResolver = new DefaultContractResolver { @@ -72,17 +68,14 @@ namespace Tgstation.Server.Host.Security.OAuth /// Initializes a new instance of the class. /// /// The value of . - /// The value of . /// The value of . /// The value of . public GenericOAuthValidator( - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, + IAbstractHttpClientFactory httpClientFactory, ILogger logger, OAuthConfiguration oAuthConfiguration) { this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); - this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration)); } @@ -109,7 +102,7 @@ namespace Tgstation.Server.Host.Security.OAuth tokenRequest.Content = new FormUrlEncodedContent(tokenRequestDictionary); var tokenResponse = await httpClient.SendAsync(tokenRequest, cancellationToken); - tokenResponsePayload = await tokenResponse.Content.ReadAsStringAsync(); + tokenResponsePayload = await tokenResponse.Content.ReadAsStringAsync(cancellationToken); tokenResponse.EnsureSuccessStatusCode(); var tokenResponseJson = JObject.Parse(tokenResponsePayload); @@ -129,7 +122,7 @@ namespace Tgstation.Server.Host.Security.OAuth accessToken); var userInformationResponse = await httpClient.SendAsync(userInformationRequest, cancellationToken); - userInformationPayload = await userInformationResponse.Content.ReadAsStringAsync(); + userInformationPayload = await userInformationResponse.Content.ReadAsStringAsync(cancellationToken); userInformationResponse.EnsureSuccessStatusCode(); var userInformationJson = JObject.Parse(userInformationPayload); @@ -178,16 +171,15 @@ namespace Tgstation.Server.Host.Security.OAuth protected abstract OAuthTokenRequest CreateTokenRequest(string code); /// - /// Create a new configured . + /// Create a new configured . /// - /// A new configured . - HttpClient CreateHttpClient() + /// A new configured . + IHttpClient CreateHttpClient() { var httpClient = httpClientFactory.CreateClient(); try { httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); - httpClient.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue); return httpClient; } catch diff --git a/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs index e0021d0143..d310e026f4 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/InvisionCommunityOAuthValidator.cs @@ -1,11 +1,10 @@ using System; -using System.Net.Http; using Microsoft.Extensions.Logging; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.System; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Security.OAuth { @@ -18,29 +17,27 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.InvisionCommunity; /// - protected override Uri TokenUrl => new Uri($"{OAuthConfiguration.ServerUrl}/oauth/token/"); // This needs the trailing slash or it doesnt get the token. Do not remove. + protected override Uri TokenUrl => new ($"{OAuthConfiguration.ServerUrl}/oauth/token/"); // This needs the trailing slash or it doesnt get the token. Do not remove. /// - protected override Uri UserInformationUrl => new Uri($"{OAuthConfiguration.ServerUrl}/api/core/me"); + protected override Uri UserInformationUrl => new ($"{OAuthConfiguration.ServerUrl}/api/core/me"); /// /// Initializes a new instance of the class. /// - /// The for the . - /// The for the . + /// The for the . /// The for the . /// The for the . public InvisionCommunityOAuthValidator( - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, + IAbstractHttpClientFactory httpClientFactory, ILogger logger, OAuthConfiguration oAuthConfiguration) - : base(httpClientFactory, assemblyInformationProvider, logger, oAuthConfiguration) + : base(httpClientFactory, logger, oAuthConfiguration) { } /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "profile"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "profile"); /// protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; diff --git a/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs index 87e3cdbce8..6d168aa471 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs @@ -1,11 +1,10 @@ using System; -using System.Net.Http; using Microsoft.Extensions.Logging; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.System; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Security.OAuth { @@ -18,10 +17,10 @@ namespace Tgstation.Server.Host.Security.OAuth public override OAuthProvider Provider => OAuthProvider.Keycloak; /// - protected override Uri TokenUrl => new Uri($"{BaseProtocolPath}/token"); + protected override Uri TokenUrl => new ($"{BaseProtocolPath}/token"); /// - protected override Uri UserInformationUrl => new Uri($"{BaseProtocolPath}/userinfo"); + protected override Uri UserInformationUrl => new ($"{BaseProtocolPath}/userinfo"); /// /// Base path to the server's OAuth endpoint. @@ -31,21 +30,19 @@ namespace Tgstation.Server.Host.Security.OAuth /// /// Initializes a new instance of the class. /// - /// The for the . - /// The for the . + /// The for the . /// The for the . /// The for the . public KeycloakOAuthValidator( - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, + IAbstractHttpClientFactory httpClientFactory, ILogger logger, OAuthConfiguration oAuthConfiguration) - : base(httpClientFactory, assemblyInformationProvider, logger, oAuthConfiguration) + : base(httpClientFactory, logger, oAuthConfiguration) { } /// - protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "openid"); + protected override OAuthTokenRequest CreateTokenRequest(string code) => new (OAuthConfiguration, code, "openid"); /// protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; diff --git a/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs b/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs index 6f92824239..298d9b25e1 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -11,7 +10,6 @@ using Microsoft.Extensions.Options; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; -using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.Security.OAuth { @@ -27,14 +25,12 @@ namespace Tgstation.Server.Host.Security.OAuth /// Initializes a new instance of the class. /// /// The to use. - /// The to use. - /// The to use. + /// The to use. /// The to use. /// The containing the to use. public OAuthProviders( IGitHubClientFactory gitHubClientFactory, - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, + IAbstractHttpClientFactory httpClientFactory, ILoggerFactory loggerFactory, IOptions securityConfigurationOptions) { @@ -60,7 +56,6 @@ namespace Tgstation.Server.Host.Security.OAuth validatorsBuilder.Add( new DiscordOAuthValidator( httpClientFactory, - assemblyInformationProvider, loggerFactory.CreateLogger(), discordConfig)); @@ -68,7 +63,6 @@ namespace Tgstation.Server.Host.Security.OAuth validatorsBuilder.Add( new TGForumsOAuthValidator( httpClientFactory, - assemblyInformationProvider, loggerFactory.CreateLogger(), tgConfig)); @@ -76,7 +70,6 @@ namespace Tgstation.Server.Host.Security.OAuth validatorsBuilder.Add( new KeycloakOAuthValidator( httpClientFactory, - assemblyInformationProvider, loggerFactory.CreateLogger(), keyCloakConfig)); @@ -84,7 +77,6 @@ namespace Tgstation.Server.Host.Security.OAuth validatorsBuilder.Add( new InvisionCommunityOAuthValidator( httpClientFactory, - assemblyInformationProvider, loggerFactory.CreateLogger(), invisionConfig)); } diff --git a/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs index 3c0f504986..91d21a5291 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs @@ -1,11 +1,10 @@ using System; -using System.Net.Http; using Microsoft.Extensions.Logging; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.System; +using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Security.OAuth { @@ -26,18 +25,15 @@ namespace Tgstation.Server.Host.Security.OAuth /// /// Initializes a new instance of the class. /// - /// The for the . - /// The for the . + /// The for the . /// The for the . /// The for the . public TGForumsOAuthValidator( - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, + IAbstractHttpClientFactory httpClientFactory, ILogger logger, OAuthConfiguration oAuthConfiguration) : base( httpClientFactory, - assemblyInformationProvider, logger, oAuthConfiguration) { diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs index f967e85726..51ecf82811 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs @@ -71,11 +71,6 @@ namespace Tgstation.Server.Host.Swarm /// bool SwarmMode => swarmConfiguration.PrivateKey != null; - /// - /// Lazily constructed . - /// - readonly Lazy lazyRestartRegistration; - /// /// The for the . /// @@ -92,9 +87,9 @@ namespace Tgstation.Server.Host.Swarm readonly IAssemblyInformationProvider assemblyInformationProvider; /// - /// The for the . + /// The for the . /// - readonly IHttpClientFactory httpClientFactory; + readonly IAbstractHttpClientFactory httpClientFactory; /// /// The for the . @@ -106,6 +101,11 @@ namespace Tgstation.Server.Host.Swarm /// readonly IServerUpdater serverUpdater; + /// + /// The for the . + /// + readonly IRestartRegistration restartRegistration; + /// /// The for the . /// @@ -202,7 +202,7 @@ namespace Tgstation.Server.Host.Swarm IDatabaseContextFactory databaseContextFactory, IDatabaseSeeder databaseSeeder, IAssemblyInformationProvider assemblyInformationProvider, - IHttpClientFactory httpClientFactory, + IAbstractHttpClientFactory httpClientFactory, IServerControl serverControl, IServerUpdater serverUpdater, IAsyncDelayer asyncDelayer, @@ -215,6 +215,7 @@ namespace Tgstation.Server.Host.Swarm this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); if (serverControl == null) throw new ArgumentNullException(nameof(serverControl)); + restartRegistration = serverControl.RegisterForRestart(this); this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater)); this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer)); @@ -249,12 +250,14 @@ namespace Tgstation.Server.Host.Swarm updateSynchronizationLock = new object(); } - - lazyRestartRegistration = new Lazy(() => serverControl.RegisterForRestart(this)); } /// - public void Dispose() => serverHealthCheckCancellationTokenSource?.Dispose(); + public void Dispose() + { + restartRegistration.Dispose(); + serverHealthCheckCancellationTokenSource?.Dispose(); + } /// public async Task RemoteAbortUpdate(CancellationToken cancellationToken) @@ -379,7 +382,7 @@ namespace Tgstation.Server.Host.Swarm if (!commitGoAhead) { logger.LogDebug( - "Update commit failed!{0}", + "Update commit failed!{maybeTimeout}", timeoutTask.IsCompleted ? " Timed out!" : String.Empty); @@ -465,8 +468,6 @@ namespace Tgstation.Server.Host.Swarm else logger.LogTrace("Swarm mode disabled"); - _ = lazyRestartRegistration.Value; - SwarmRegistrationResult result; if (swarmController) { @@ -1164,25 +1165,22 @@ namespace Tgstation.Server.Host.Swarm var request = new HttpRequestMessage( httpMethod, swarmServer.Address + subroute[1..]); - - request.Headers.Add(SwarmConstants.ApiKeyHeader, swarmConfiguration.PrivateKey); - request.Headers.UserAgent.Clear(); - request.Headers.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue); - request.Headers.Accept.Clear(); - request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); - if (registrationIdOverride.HasValue) - request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdOverride.Value.ToString()); - else if (swarmController) - { - lock (swarmServers) - if (registrationIds.TryGetValue(swarmServer.Identifier, out var registrationId)) - request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationId.ToString()); - } - else if (controllerRegistration.HasValue) - request.Headers.Add(SwarmConstants.RegistrationIdHeader, controllerRegistration.Value.ToString()); - try { + request.Headers.Add(SwarmConstants.ApiKeyHeader, swarmConfiguration.PrivateKey); + request.Headers.Accept.Clear(); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); + if (registrationIdOverride.HasValue) + request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationIdOverride.Value.ToString()); + else if (swarmController) + { + lock (swarmServers) + if (registrationIds.TryGetValue(swarmServer.Identifier, out var registrationId)) + request.Headers.Add(SwarmConstants.RegistrationIdHeader, registrationId.ToString()); + } + else if (controllerRegistration.HasValue) + request.Headers.Add(SwarmConstants.RegistrationIdHeader, controllerRegistration.Value.ToString()); + if (body != null) request.Content = new StringContent( JsonConvert.SerializeObject(body, SerializerSettings), diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index f342b53791..0730a00c27 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -139,6 +139,7 @@ + diff --git a/tests/Tgstation.Server.Client.Tests/TestApiClient.cs b/tests/Tgstation.Server.Client.Tests/TestApiClient.cs index 17b0919ccb..49ab83e74a 100644 --- a/tests/Tgstation.Server.Client.Tests/TestApiClient.cs +++ b/tests/Tgstation.Server.Client.Tests/TestApiClient.cs @@ -11,8 +11,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; -using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Response; +using Tgstation.Server.Common; namespace Tgstation.Server.Client.Tests { diff --git a/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs b/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs index 558ccf0faf..a78b19a86e 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs @@ -15,14 +15,16 @@ namespace Tgstation.Server.Host.Components.Byond.Tests [TestMethod] public void TestConstruction() { - Assert.ThrowsException(() => new PosixByondInstaller(null, null, null)); + Assert.ThrowsException(() => new PosixByondInstaller(null, null, null, null)); var mockPostWriteHandler = new Mock(); - Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, null, null)); + Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, null, null, null)); var mockIOManager = new Mock(); - Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, null)); + Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, null, null)); + var mockFileDownloader = Mock.Of(); + Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader, null)); var mockLogger = new Mock>(); - _ = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); + _ = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader, mockLogger.Object); } [TestMethod] @@ -31,7 +33,8 @@ namespace Tgstation.Server.Host.Components.Byond.Tests var mockPostWriteHandler = new Mock(); var mockIOManager = new Mock(); var mockLogger = new Mock>(); - var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); + var mockFileDownloader = Mock.Of(); + var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader, mockLogger.Object); await installer.CleanCache(default); } @@ -42,12 +45,13 @@ namespace Tgstation.Server.Host.Components.Byond.Tests var mockIOManager = new Mock(); var mockPostWriteHandler = new Mock(); var mockLogger = new Mock>(); - var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); + var mockFileDownloader = new Mock(); + var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader.Object, mockLogger.Object); await Assert.ThrowsExceptionAsync(() => installer.DownloadVersion(null, default)); var ourArray = Array.Empty(); - mockIOManager.Setup(x => x.DownloadFile(It.Is(uri => uri == new Uri("https://secure.byond.com/download/build/511/511.1385_byond_linux.zip")), default)).Returns(Task.FromResult(new MemoryStream(ourArray))).Verifiable(); + mockFileDownloader.Setup(x => x.DownloadFile(It.Is(uri => uri == new Uri("https://secure.byond.com/download/build/511/511.1385_byond_linux.zip")), default)).Returns(Task.FromResult(new MemoryStream(ourArray))).Verifiable(); var result = await installer.DownloadVersion(new Version(511, 1385), default); @@ -61,7 +65,8 @@ namespace Tgstation.Server.Host.Components.Byond.Tests var mockIOManager = new Mock(); var mockPostWriteHandler = new Mock(); var mockLogger = new Mock>(); - var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); + var mockFileDownloader = Mock.Of(); + var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader, mockLogger.Object); const string FakePath = "fake"; await Assert.ThrowsExceptionAsync(() => installer.InstallByond(null, null, default)); diff --git a/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs b/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs index d18400d728..d4a486b584 100644 --- a/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs +++ b/tests/Tgstation.Server.Host.Tests/IO/TestIOManager.cs @@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.IO.Tests [TestClass] public sealed class TestIOManager { - readonly IIOManager ioManager = new DefaultIOManager(new AssemblyInformationProvider()); + readonly IIOManager ioManager = new DefaultIOManager(); [TestMethod] public async Task TestDeleteDirectory() diff --git a/tests/Tgstation.Server.Host.Tests/System/TestPosixSignalHandler.cs b/tests/Tgstation.Server.Host.Tests/System/TestPosixSignalHandler.cs index 382e060f72..52c6129ef9 100644 --- a/tests/Tgstation.Server.Host.Tests/System/TestPosixSignalHandler.cs +++ b/tests/Tgstation.Server.Host.Tests/System/TestPosixSignalHandler.cs @@ -57,7 +57,7 @@ namespace Tgstation.Server.Host.System.Tests processExecutor = new ProcessExecutor( new PosixProcessFeatures( new Lazy(() => processExecutor), - new DefaultIOManager(new AssemblyInformationProvider()), + new DefaultIOManager(), loggerFactory.CreateLogger()), Mock.Of(), loggerFactory.CreateLogger(), diff --git a/tests/Tgstation.Server.Host.Tests/System/TestProcessFeatures.cs b/tests/Tgstation.Server.Host.Tests/System/TestProcessFeatures.cs index ae205ffaec..f6f680c50d 100644 --- a/tests/Tgstation.Server.Host.Tests/System/TestProcessFeatures.cs +++ b/tests/Tgstation.Server.Host.Tests/System/TestProcessFeatures.cs @@ -22,7 +22,7 @@ namespace Tgstation.Server.Host.System.Tests { features = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? (IProcessFeatures)new WindowsProcessFeatures(Mock.Of>()) - : new PosixProcessFeatures(new Lazy(() => null), new DefaultIOManager(new AssemblyInformationProvider()), Mock.Of>()); + : new PosixProcessFeatures(new Lazy(() => null), new DefaultIOManager(), Mock.Of>()); } [TestMethod] diff --git a/tests/Tgstation.Server.Host.Tests/TestProgram.cs b/tests/Tgstation.Server.Host.Tests/TestProgram.cs index cb070dac63..67c3546d0c 100644 --- a/tests/Tgstation.Server.Host.Tests/TestProgram.cs +++ b/tests/Tgstation.Server.Host.Tests/TestProgram.cs @@ -84,7 +84,7 @@ namespace Tgstation.Server.Host.Tests mockServer.Setup(x => x.Run(It.IsAny())).Throws(exception); mockServer.SetupGet(x => x.RestartRequested).Returns(true); var mockServerFactory = new Mock(); - mockServerFactory.SetupGet(x => x.IOManager).Returns(new DefaultIOManager(new AssemblyInformationProvider())); + mockServerFactory.SetupGet(x => x.IOManager).Returns(new DefaultIOManager()); mockServerFactory.Setup(x => x.CreateServer(It.IsNotNull(), It.IsAny(), It.IsAny())).ReturnsAsync(mockServer.Object); var program = new Program { diff --git a/tests/Tgstation.Server.Tests/ConcreteHttpClientFactory.cs b/tests/Tgstation.Server.Tests/ConcreteHttpClientFactory.cs new file mode 100644 index 0000000000..572c860b27 --- /dev/null +++ b/tests/Tgstation.Server.Tests/ConcreteHttpClientFactory.cs @@ -0,0 +1,10 @@ +using Tgstation.Server.Common; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Tests +{ + sealed class ConcreteHttpClientFactory : IAbstractHttpClientFactory + { + public IHttpClient CreateClient() => new HttpClient(); + } +} diff --git a/tests/Tgstation.Server.Tests/Instance/ByondTest.cs b/tests/Tgstation.Server.Tests/Instance/ByondTest.cs index 8523f37884..1b19569b6f 100644 --- a/tests/Tgstation.Server.Tests/Instance/ByondTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ByondTest.cs @@ -105,12 +105,18 @@ namespace Tgstation.Server.Tests.Instance var byondInstaller = new PlatformIdentifier().IsWindows ? (IByondInstaller)new WindowsByondInstaller( Mock.Of(), - new DefaultIOManager(new AssemblyInformationProvider()), + Mock.Of(), + new FileDownloader( + new ConcreteHttpClientFactory(), + Mock.Of>()), generalConfigOptionsMock.Object, Mock.Of>()) : new PosixByondInstaller( Mock.Of(), - new DefaultIOManager(new AssemblyInformationProvider()), + Mock.Of(), + new FileDownloader( + new ConcreteHttpClientFactory(), + Mock.Of>()), Mock.Of>()); using var windowsByondInstaller = byondInstaller as WindowsByondInstaller; diff --git a/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs b/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs index f07dd4d8c7..e2bc9c7ebe 100644 --- a/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs @@ -103,7 +103,7 @@ namespace Tgstation.Server.Tests.Instance Task SetupDMApiTests(CancellationToken cancellationToken) { // just use an I/O manager here - var ioManager = new DefaultIOManager(new AssemblyInformationProvider()); + var ioManager = new DefaultIOManager(); return Task.WhenAll( ioManager.CopyDirectory( "../../../../DMAPI", diff --git a/tgstation-server.sln b/tgstation-server.sln index 063295bfa3..2a7bda3f3d 100644 --- a/tgstation-server.sln +++ b/tgstation-server.sln @@ -193,6 +193,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Migrator.C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Common", "src\Tgstation.Server.Host.Common\Tgstation.Server.Host.Common.csproj", "{CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tgstation.Server.Common", "src\Tgstation.Server.Common\Tgstation.Server.Common.csproj", "{70CD9A98-D31A-44A4-81D1-D02764CEEEFD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -341,6 +343,14 @@ Global {CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}.Release|Any CPU.Build.0 = Release|Any CPU {CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}.ReleaseNoService|Any CPU.ActiveCfg = Release|Any CPU {CF3968A0-EA81-4464-B2D4-C7D40F6B5BCB}.ReleaseNoService|Any CPU.Build.0 = Release|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.DebugNoService|Any CPU.ActiveCfg = Debug|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.DebugNoService|Any CPU.Build.0 = Debug|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.Release|Any CPU.Build.0 = Release|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.ReleaseNoService|Any CPU.ActiveCfg = Release|Any CPU + {70CD9A98-D31A-44A4-81D1-D02764CEEEFD}.ReleaseNoService|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/Tgstation.Server.Migrator/ConcreteHttpClientFactory.cs b/tools/Tgstation.Server.Migrator/ConcreteHttpClientFactory.cs new file mode 100644 index 0000000000..2a15963fed --- /dev/null +++ b/tools/Tgstation.Server.Migrator/ConcreteHttpClientFactory.cs @@ -0,0 +1,10 @@ +using Tgstation.Server.Common; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Migrator +{ + sealed class ConcreteHttpClientFactory : IAbstractHttpClientFactory + { + public IHttpClient CreateClient() => new HttpClient(); + } +} diff --git a/tools/Tgstation.Server.Migrator/Program.cs b/tools/Tgstation.Server.Migrator/Program.cs index 0a7cb5654e..298877c75a 100644 --- a/tools/Tgstation.Server.Migrator/Program.cs +++ b/tools/Tgstation.Server.Migrator/Program.cs @@ -15,11 +15,15 @@ using System.ServiceProcess; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + using Octokit; using Tgstation.Server.Api; using Tgstation.Server.Client; +using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Setup; +using Tgstation.Server.Migrator; using FileMode = System.IO.FileMode; @@ -377,10 +381,14 @@ try // TGS5 DOWNLOAD AND UNZIP Console.WriteLine("Downloading TGS5..."); - using (var tgsFiveZipMemoryStream = await SetupApplication.IOManager.DownloadFile(new Uri(serverServiceAsset.BrowserDownloadUrl), default)) + + var httpClientFactory = new ConcreteHttpClientFactory(); + using (var loggerFactory = LoggerFactory.Create(builder => { })) { + var fileDownloader = new FileDownloader(httpClientFactory, loggerFactory.CreateLogger()); + using var tgsFiveZipMemoryStream = await fileDownloader.DownloadFile(new Uri(serverServiceAsset.BrowserDownloadUrl), default); Console.WriteLine("Unzipping TGS5..."); - await SetupApplication.IOManager.ZipToDirectory(tgsInstallPath, tgsFiveZipMemoryStream, default); + await serverFactory.IOManager.ZipToDirectory(tgsInstallPath, tgsFiveZipMemoryStream, default); } // TGS5 CONFIG SETUP