mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-25 22:17:51 +01:00
Skeleton web app
This commit is contained in:
@@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.Startup
|
||||
/// <summary>
|
||||
/// Represents the host
|
||||
/// </summary>
|
||||
public interface IServer : IDisposable
|
||||
public interface IServer
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to the updated assembly to run if any. Populated once <see cref="RunAsync(string[], CancellationToken)"/> returns
|
||||
@@ -17,9 +17,8 @@ namespace Tgstation.Server.Host.Startup
|
||||
/// <summary>
|
||||
/// Runs the <see cref="IServer"/>
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments for the <see cref="IServer"/></param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task RunAsync(string[] args, CancellationToken cancellationToken);
|
||||
Task RunAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
/// </summary>
|
||||
public interface IServerFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a <see cref="IServer"/>
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="IServer"/></returns>
|
||||
IServer CreateServer();
|
||||
/// <summary>
|
||||
/// Create a <see cref="IServer"/>
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments for the <see cref="IServer"/></param>
|
||||
/// <returns>A new <see cref="IServer"/></returns>
|
||||
IServer CreateServer(string[] args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
/// <summary>
|
||||
/// Loads the <see cref="Assembly"/> at <see cref="assemblyPath"/> and creates an <see cref="IServer"/> from it
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments for the <see cref="IServer"/></param>
|
||||
/// <returns>A new <see cref="IServer"/></returns>
|
||||
public IServer CreateServer()
|
||||
public IServer CreateServer(string[] args)
|
||||
{
|
||||
var assembly = LoadFromAssemblyPath(assemblyPath);
|
||||
//find the IServerFactory implementation
|
||||
@@ -35,7 +36,7 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
var serverFactoryImplementationType = assembly.GetTypes().Where(x => serverFactoryInterfaceType.IsAssignableFrom(x)).First();
|
||||
|
||||
var serverFactory = (IServerFactory)Activator.CreateInstance(serverFactoryImplementationType);
|
||||
return serverFactory.CreateServer();
|
||||
return serverFactory.CreateServer(args);
|
||||
}
|
||||
|
||||
//honestly have no idea what this is for, but the examples i see just return null and it seems to work just fine
|
||||
|
||||
@@ -43,24 +43,19 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
//first run the host we started with
|
||||
var serverFactory = initialServerFactory;
|
||||
var assemblyPath = serverFactory.GetType().Assembly.Location;
|
||||
do
|
||||
{
|
||||
string updatePath;
|
||||
using (var server = serverFactory.CreateServer())
|
||||
{
|
||||
serverFactory = null;
|
||||
await server.RunAsync(args, cancellationToken).ConfigureAwait(false);
|
||||
updatePath = server.UpdatePath;
|
||||
}
|
||||
do
|
||||
{
|
||||
var server = serverFactory.CreateServer(args);
|
||||
await server.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (updatePath == null)
|
||||
break;
|
||||
|
||||
activeAssemblyDeleter.DeleteActiveAssembly(assemblyPath);
|
||||
File.Move(updatePath, assemblyPath);
|
||||
serverFactory = isolatedAssemblyLoader.CreateIsolatedServerFactory(assemblyPath);
|
||||
}
|
||||
while (!cancellationToken.IsCancellationRequested);
|
||||
if (server.UpdatePath == null)
|
||||
break;
|
||||
|
||||
activeAssemblyDeleter.DeleteActiveAssembly(assemblyPath);
|
||||
File.Move(server.UpdatePath, assemblyPath);
|
||||
serverFactory = isolatedAssemblyLoader.CreateIsolatedServerFactory(assemblyPath);
|
||||
}
|
||||
while (!cancellationToken.IsCancellationRequested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the ASP.NET Core web application
|
||||
/// </summary>
|
||||
sealed class Application
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IConfiguration"/> for the <see cref="Application"/>
|
||||
/// </summary>
|
||||
readonly IConfiguration configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Construct an <see cref="Application"/>
|
||||
/// </summary>
|
||||
/// <param name="configuration">The value of <see cref="configuration"/></param>
|
||||
public Application(IConfiguration configuration) => this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
|
||||
/// <summary>
|
||||
/// Configure dependency injected services
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to configure</param>
|
||||
#pragma warning disable CA1822 // Mark members as static
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
#pragma warning restore CA1822 // Mark members as static
|
||||
{
|
||||
if (services == null)
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
|
||||
services.AddMvc();
|
||||
services.AddOptions();
|
||||
services.AddLocalization();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the <see cref="Application"/>
|
||||
/// </summary>
|
||||
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/> to configure</param>
|
||||
/// <param name="hostingEnvironment">The <see cref="IHostingEnvironment"/> of the <see cref="Application"/></param>
|
||||
public void Configure(IApplicationBuilder applicationBuilder, IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
if (applicationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(applicationBuilder));
|
||||
if (hostingEnvironment == null)
|
||||
throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||
|
||||
if (hostingEnvironment.IsDevelopment())
|
||||
applicationBuilder.UseDeveloperExceptionPage();
|
||||
|
||||
var defaultCulture = new CultureInfo("en");
|
||||
var supportedCultures = new List<CultureInfo>
|
||||
{
|
||||
defaultCulture
|
||||
};
|
||||
|
||||
CultureInfo.CurrentCulture = defaultCulture;
|
||||
CultureInfo.CurrentUICulture = defaultCulture;
|
||||
|
||||
applicationBuilder.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures,
|
||||
});
|
||||
|
||||
applicationBuilder.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Startup;
|
||||
|
||||
namespace Tgstation.Server.Host
|
||||
@@ -10,10 +14,23 @@ namespace Tgstation.Server.Host
|
||||
/// <inheritdoc />
|
||||
public string UpdatePath => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() { }
|
||||
/// <summary>
|
||||
/// The <see cref="IWebHostBuilder"/> for the <see cref="Server"/>
|
||||
/// </summary>
|
||||
readonly IWebHostBuilder webHostBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="Server"/>
|
||||
/// </summary>
|
||||
/// <param name="webHostBuilder">The value of <see cref="webHostBuilder"/></param>
|
||||
public Server(IWebHostBuilder webHostBuilder) => this.webHostBuilder = webHostBuilder ?? throw new ArgumentNullException(nameof(webHostBuilder));
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync(string[] args, CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
[ExcludeFromCodeCoverage]
|
||||
public async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using (var webHost = webHostBuilder.UseStartup<Application>().Build())
|
||||
await webHost.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Tgstation.Server.Host.Startup;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Tgstation.Server.Host.Startup;
|
||||
|
||||
namespace Tgstation.Server.Host
|
||||
{
|
||||
@@ -6,6 +8,6 @@ namespace Tgstation.Server.Host
|
||||
public sealed class ServerFactory : IServerFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IServer CreateServer() => new Server();
|
||||
public IServer CreateServer(string[] args) => new Server(WebHost.CreateDefaultBuilder(args));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.3" />
|
||||
<PackageReference Include="Octokit" Version="0.29.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Tgstation.Server.Host.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class TestServer
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task TestRunAndDispose()
|
||||
{
|
||||
using (var server = new Server())
|
||||
{
|
||||
await server.RunAsync(null, default).ConfigureAwait(false);
|
||||
Assert.IsNull(server.UpdatePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
|
||||
<PackageReference Include="Moq" Version="4.8.2" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.2.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
public void TestLoading()
|
||||
{
|
||||
var isf = new IsolatedServerFactory(typeof(ServerFactory).Assembly.Location);
|
||||
Assert.IsNotNull(isf.CreateServer());
|
||||
Assert.IsNotNull(isf.CreateServer(Array.Empty<string>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
{
|
||||
readonly IServer server;
|
||||
public MockServerFactory(IServer server) => this.server = server;
|
||||
public IServer CreateServer() => server;
|
||||
public IServer CreateServer(string[] args) => server;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -41,7 +41,7 @@ namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
|
||||
using (var cts = new CancellationTokenSource())
|
||||
{
|
||||
mockServer.Setup(x => x.RunAsync(It.IsNotNull<string[]>(), cts.Token)).Returns(Task.CompletedTask).Verifiable();
|
||||
mockServer.Setup(x => x.RunAsync(cts.Token)).Returns(Task.CompletedTask).Verifiable();
|
||||
await wd.RunAsync(Array.Empty<string>(), cts.Token).ConfigureAwait(false);
|
||||
mockServer.VerifyAll();
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
using (var cts = new CancellationTokenSource())
|
||||
{
|
||||
int count = 0;
|
||||
mockServer.Setup(x => x.RunAsync(It.IsNotNull<string[]>(), cts.Token)).Callback(() =>
|
||||
mockServer.Setup(x => x.RunAsync(cts.Token)).Callback(() =>
|
||||
{
|
||||
if (++count > 1)
|
||||
cts.Cancel();
|
||||
|
||||
Reference in New Issue
Block a user