From 1182a5a9ca24a4ac43327058f5bb27ad939ddc03 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Tue, 29 Mar 2016 22:40:42 -0700 Subject: [PATCH] Remove IServerLoader and add the IServerFactory to the DI container - This change removes the indirection and between the IServerLoader and the IServerFactory. We now add the IServerFactory directly to the DI container and resolve it from there. - Moved logic that resolves IServerFactory from an assembly to a static helper --- .../IWebHostBuilder.cs | 7 --- .../Internal/ServerLoader.cs | 31 ++++++++++ .../Internal/WebHost.cs | 21 +++---- .../Internal/WebHostOptions.cs | 4 +- .../Server/IServerLoader.cs | 10 ---- .../Server/ServerLoader.cs | 50 ---------------- .../WebHostBuilder.cs | 33 +++-------- .../WebHostBuilderExtensions.cs | 26 +++++++++ .../WebHostBuilderTests.cs | 24 ++++---- .../WebHostConfigurationsTests.cs | 2 +- .../WebHostTests.cs | 57 ++++++++++++------- 11 files changed, 125 insertions(+), 140 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Hosting/Internal/ServerLoader.cs delete mode 100644 src/Microsoft.AspNetCore.Hosting/Server/IServerLoader.cs delete mode 100644 src/Microsoft.AspNetCore.Hosting/Server/ServerLoader.cs diff --git a/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs b/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs index aa40a362e2..a94ca8c199 100644 --- a/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs +++ b/src/Microsoft.AspNetCore.Hosting.Abstractions/IWebHostBuilder.cs @@ -26,13 +26,6 @@ namespace Microsoft.AspNetCore.Hosting /// The . IWebHostBuilder UseLoggerFactory(ILoggerFactory loggerFactory); - /// - /// Specify the to be used by the web host. - /// - /// The to be used. - /// The . - IWebHostBuilder UseServer(IServerFactory factory); - /// /// Specify the startup type to be used by the web host. /// diff --git a/src/Microsoft.AspNetCore.Hosting/Internal/ServerLoader.cs b/src/Microsoft.AspNetCore.Hosting/Internal/ServerLoader.cs new file mode 100644 index 0000000000..20f821932f --- /dev/null +++ b/src/Microsoft.AspNetCore.Hosting/Internal/ServerLoader.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Hosting.Server; + +namespace Microsoft.AspNetCore.Hosting.Internal +{ + internal static class ServerLoader + { + internal static Type ResolveServerFactoryType(string assemblyName) + { + var assembly = Assembly.Load(new AssemblyName(assemblyName)); + if (assembly == null) + { + throw new InvalidOperationException(string.Format("The assembly {0} failed to load.", assemblyName)); + } + + var serverTypeInfo = assembly.DefinedTypes.Where( + t => t.ImplementedInterfaces.FirstOrDefault(interf => interf.Equals(typeof(IServerFactory))) != null) + .FirstOrDefault(); + + if (serverTypeInfo == null) + { + throw new InvalidOperationException($"No server type found that implements IServerFactory in assembly: {assemblyName}."); + } + + return serverTypeInfo.AsType(); + } + + } +} diff --git a/src/Microsoft.AspNetCore.Hosting/Internal/WebHost.cs b/src/Microsoft.AspNetCore.Hosting/Internal/WebHost.cs index e3d5cfd6db..302cc18a80 100644 --- a/src/Microsoft.AspNetCore.Hosting/Internal/WebHost.cs +++ b/src/Microsoft.AspNetCore.Hosting/Internal/WebHost.cs @@ -31,14 +31,16 @@ namespace Microsoft.AspNetCore.Hosting.Internal private RequestDelegate _application; private ILogger _logger; + // Used for testing only + internal WebHostOptions Options => _options; + // Only one of these should be set internal string StartupAssemblyName { get; set; } internal StartupMethods Startup { get; set; } internal Type StartupType { get; set; } - // Only one of these should be set - internal IServerFactory ServerFactory { get; set; } - internal string ServerFactoryLocation { get; set; } + private IServerFactory ServerFactory { get; set; } + private IServer Server { get; set; } public WebHost( @@ -207,18 +209,9 @@ namespace Microsoft.AspNetCore.Hosting.Internal { if (Server == null) { - if (ServerFactory == null) - { - // Blow up if we don't have a server set at this point - if (ServerFactoryLocation == null) - { - throw new InvalidOperationException("IHostingBuilder.UseServer() is required for " + nameof(Start) + "()"); - } - - ServerFactory = _applicationServices.GetRequiredService().LoadServerFactory(ServerFactoryLocation); - } - + ServerFactory = _applicationServices.GetRequiredService(); Server = ServerFactory.CreateServer(_config); + var addresses = Server.Features?.Get()?.Addresses; if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0) { diff --git a/src/Microsoft.AspNetCore.Hosting/Internal/WebHostOptions.cs b/src/Microsoft.AspNetCore.Hosting/Internal/WebHostOptions.cs index 3f7f428fa1..a57db8cbe1 100644 --- a/src/Microsoft.AspNetCore.Hosting/Internal/WebHostOptions.cs +++ b/src/Microsoft.AspNetCore.Hosting/Internal/WebHostOptions.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal DetailedErrors = ParseBool(configuration, WebHostDefaults.DetailedErrorsKey); CaptureStartupErrors = ParseBool(configuration, WebHostDefaults.CaptureStartupErrorsKey); Environment = configuration[WebHostDefaults.EnvironmentKey]; - ServerFactoryLocation = configuration[WebHostDefaults.ServerKey]; + ServerFactoryAssembly = configuration[WebHostDefaults.ServerKey]; WebRoot = configuration[WebHostDefaults.WebRootKey]; ContentRootPath = configuration[WebHostDefaults.ContentRootKey]; } @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal public string Environment { get; set; } - public string ServerFactoryLocation { get; set; } + public string ServerFactoryAssembly { get; set; } public string WebRoot { get; set; } diff --git a/src/Microsoft.AspNetCore.Hosting/Server/IServerLoader.cs b/src/Microsoft.AspNetCore.Hosting/Server/IServerLoader.cs deleted file mode 100644 index a6afea0aea..0000000000 --- a/src/Microsoft.AspNetCore.Hosting/Server/IServerLoader.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Hosting.Server -{ - public interface IServerLoader - { - IServerFactory LoadServerFactory(string assemblyName); - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Hosting/Server/ServerLoader.cs b/src/Microsoft.AspNetCore.Hosting/Server/ServerLoader.cs deleted file mode 100644 index 3868cc1373..0000000000 --- a/src/Microsoft.AspNetCore.Hosting/Server/ServerLoader.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Hosting.Server -{ - public class ServerLoader : IServerLoader - { - private readonly IServiceProvider _services; - - public ServerLoader(IServiceProvider services) - { - _services = services; - } - - public IServerFactory LoadServerFactory(string assemblyName) - { - if (assemblyName == null) - { - throw new ArgumentNullException(nameof(assemblyName)); - } - - if (string.IsNullOrEmpty(assemblyName)) - { - throw new ArgumentException(string.Empty, nameof(assemblyName)); - } - - var assembly = Assembly.Load(new AssemblyName(assemblyName)); - if (assembly == null) - { - throw new Exception(string.Format("The assembly {0} failed to load.", assemblyName)); - } - - var serverTypeInfo = assembly.DefinedTypes.Where( - t => t.ImplementedInterfaces.FirstOrDefault(interf => interf.Equals(typeof(IServerFactory))) != null) - .FirstOrDefault(); - - if (serverTypeInfo == null) - { - throw new InvalidOperationException($"No server type found that implements IServerFactory in assembly: {assemblyName}."); - } - - return (IServerFactory)ActivatorUtilities.GetServiceOrCreateInstance(_services, serverTypeInfo.AsType()); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs b/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs index af249dabcf..3ccef49f18 100644 --- a/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs +++ b/src/Microsoft.AspNetCore.Hosting/WebHostBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Builder; @@ -38,9 +39,6 @@ namespace Microsoft.AspNetCore.Hosting private StartupMethods _startup; private Type _startupType; - // Only one of these should be set - private IServerFactory _serverFactory; - /// /// Initializes a new instance of the class. /// @@ -89,22 +87,6 @@ namespace Microsoft.AspNetCore.Hosting return this; } - /// - /// Specify the to be used by the web host. - /// - /// The to be used. - /// The . - public IWebHostBuilder UseServer(IServerFactory factory) - { - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - _serverFactory = factory; - return this; - } - /// /// Specify the startup type to be used by the web host. /// @@ -189,10 +171,6 @@ namespace Microsoft.AspNetCore.Hosting var host = new WebHost(hostingServices, startupLoader, _options, _config); - // Only one of these should be set, but they are used in priority - host.ServerFactory = _serverFactory; - host.ServerFactoryLocation = _options.ServerFactoryLocation; - // Only one of these should be set, but they are used in priority host.Startup = _startup; host.StartupType = _startupType; @@ -227,8 +205,6 @@ namespace Microsoft.AspNetCore.Hosting services.AddLogging(); services.AddTransient(); - - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddOptions(); @@ -248,6 +224,13 @@ namespace Microsoft.AspNetCore.Hosting services.AddSingleton(defaultPlatformServices.Application); services.AddSingleton(defaultPlatformServices.Runtime); + if (!string.IsNullOrEmpty(_options.ServerFactoryAssembly)) + { + // Add the server factory + var serverFactoryType = ServerLoader.ResolveServerFactoryType(_options.ServerFactoryAssembly); + services.AddSingleton(typeof(IServerFactory), serverFactoryType); + } + foreach (var configureServices in _configureServicesDelegates) { configureServices(services); diff --git a/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs b/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs index 20e97ff7e3..ce32436203 100644 --- a/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Hosting/WebHostBuilderExtensions.cs @@ -2,9 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; +using System.Reflection; using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Hosting { @@ -104,6 +107,27 @@ namespace Microsoft.AspNetCore.Hosting return hostBuilder.UseSetting(WebHostDefaults.ServerKey, assemblyName); } + /// + /// Specify the to be used by the web host. + /// + /// The to configure. + /// The to be used. + /// The . + public static IWebHostBuilder UseServer(this IWebHostBuilder hostBuilder, IServerFactory factory) + { + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + return hostBuilder.ConfigureServices(services => + { + // It would be nicer if this was transient but we need to pass in the + // factory instance directly + services.AddSingleton(factory); + }); + } + /// /// Specify the server to be used by the web host. /// @@ -117,6 +141,8 @@ namespace Microsoft.AspNetCore.Hosting throw new ArgumentNullException(nameof(server)); } + // It would be nicer if this was transient but we need to pass in the + // server instance directly return hostBuilder.UseServer(new ServerFactory(server)); } diff --git a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs index 14531d07a0..7a14757fa8 100644 --- a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs +++ b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs @@ -242,14 +242,14 @@ namespace Microsoft.AspNetCore.Hosting public void CodeBasedSettingsCodeBasedOverride() { var hostBuilder = new WebHostBuilder() - .UseSetting(WebHostDefaults.ServerKey, "ServerA") - .UseSetting(WebHostDefaults.ServerKey, "ServerB") + .UseSetting(WebHostDefaults.EnvironmentKey, "EnvA") + .UseSetting(WebHostDefaults.EnvironmentKey, "EnvB") .UseServer(new TestServer()) .UseStartup(); var host = (WebHost)hostBuilder.Build(); - Assert.Equal("ServerB", host.ServerFactoryLocation); + Assert.Equal("EnvB", host.Options.Environment); } [Fact] @@ -257,7 +257,7 @@ namespace Microsoft.AspNetCore.Hosting { var settings = new Dictionary { - { WebHostDefaults.ServerKey, "ServerB" } + { WebHostDefaults.EnvironmentKey, "EnvB" } }; var config = new ConfigurationBuilder() @@ -265,14 +265,14 @@ namespace Microsoft.AspNetCore.Hosting .Build(); var hostBuilder = new WebHostBuilder() - .UseSetting(WebHostDefaults.ServerKey, "ServerA") + .UseSetting(WebHostDefaults.EnvironmentKey, "EnvA") .UseConfiguration(config) .UseServer(new TestServer()) .UseStartup(); var host = (WebHost)hostBuilder.Build(); - Assert.Equal("ServerB", host.ServerFactoryLocation); + Assert.Equal("EnvB", host.Options.Environment); } [Fact] @@ -280,7 +280,7 @@ namespace Microsoft.AspNetCore.Hosting { var settings = new Dictionary { - { WebHostDefaults.ServerKey, "ServerA" } + { WebHostDefaults.EnvironmentKey, "EnvA" } }; var config = new ConfigurationBuilder() @@ -289,13 +289,13 @@ namespace Microsoft.AspNetCore.Hosting var hostBuilder = new WebHostBuilder() .UseConfiguration(config) - .UseSetting(WebHostDefaults.ServerKey, "ServerB") + .UseSetting(WebHostDefaults.EnvironmentKey, "EnvB") .UseServer(new TestServer()) .UseStartup(); var host = (WebHost)hostBuilder.Build(); - Assert.Equal("ServerB", host.ServerFactoryLocation); + Assert.Equal("EnvB", host.Options.Environment); } [Fact] @@ -303,7 +303,7 @@ namespace Microsoft.AspNetCore.Hosting { var settings = new Dictionary { - { WebHostDefaults.ServerKey, "ServerA" } + { WebHostDefaults.EnvironmentKey, "EnvA" } }; var config = new ConfigurationBuilder() @@ -312,7 +312,7 @@ namespace Microsoft.AspNetCore.Hosting var overrideSettings = new Dictionary { - { WebHostDefaults.ServerKey, "ServerB" } + { WebHostDefaults.EnvironmentKey, "EnvB" } }; var overrideConfig = new ConfigurationBuilder() @@ -327,7 +327,7 @@ namespace Microsoft.AspNetCore.Hosting var host = (WebHost)hostBuilder.Build(); - Assert.Equal("ServerB", host.ServerFactoryLocation); + Assert.Equal("EnvB", host.Options.Environment); } [Fact] diff --git a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostConfigurationsTests.cs b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostConfigurationsTests.cs index 8cb54a852c..c7ffe999d4 100644 --- a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostConfigurationsTests.cs +++ b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostConfigurationsTests.cs @@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build()); Assert.Equal("wwwroot", config.WebRoot); - Assert.Equal("Microsoft.AspNetCore.Server.Kestrel", config.ServerFactoryLocation); + Assert.Equal("Microsoft.AspNetCore.Server.Kestrel", config.ServerFactoryAssembly); Assert.Equal("MyProjectReference", config.Application); Assert.Equal("Development", config.Environment); Assert.True(config.CaptureStartupErrors); diff --git a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostTests.cs b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostTests.cs index 0053351a0f..3b2a68911d 100644 --- a/test/Microsoft.AspNetCore.Hosting.Tests/WebHostTests.cs +++ b/test/Microsoft.AspNetCore.Hosting.Tests/WebHostTests.cs @@ -24,7 +24,7 @@ using Xunit; namespace Microsoft.AspNetCore.Hosting { - public class WebHostTests : IServerFactory, IServer + public class WebHostTests : IServerFactory { private readonly IList _startInstances = new List(); private IFeatureCollection _featuresSupportedByThisHost = NewFeatureCollection(); @@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHostThrowsWithNoServer() { var ex = Assert.Throws(() => CreateBuilder().Build().Start()); - Assert.True(ex.Message.Contains("UseServer()")); + Assert.Equal("No service for type 'Microsoft.AspNetCore.Hosting.Server.IServerFactory' has been registered.", ex.Message); } [Fact] @@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Hosting } [Fact] - public void CanDefaultAddresseIfNotConfigured() + public void CanDefaultAddressesIfNotConfigured() { var vals = new Dictionary { @@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHostCanBeStarted() { var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .UseStartup("Microsoft.AspNetCore.Hosting.Tests") .Start(); @@ -145,7 +145,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHostShutsDownWhenTokenTriggers() { var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .UseStartup("Microsoft.AspNetCore.Hosting.Tests") .Build(); @@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHostDisposesServiceProvider() { var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .ConfigureServices(s => { s.AddTransient(); @@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHostNotifiesApplicationStarted() { var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .Build(); var applicationLifetime = host.Services.GetService(); @@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHostInjectsHostingEnvironment() { var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .UseStartup("Microsoft.AspNetCore.Hosting.Tests") .UseEnvironment("WithHostingEnvironment") .Build(); @@ -237,7 +237,7 @@ namespace Microsoft.AspNetCore.Hosting { services.AddTransient(); }) - .UseServer((IServerFactory)this) + .UseServer(this) .UseStartup("Microsoft.AspNetCore.Hosting.Tests"); Assert.Throws(() => builder.Build()); @@ -246,7 +246,7 @@ namespace Microsoft.AspNetCore.Hosting [Fact] public void CanCreateApplicationServicesWithAddedServices() { - var host = CreateBuilder().UseServer((IServerFactory)this).ConfigureServices(services => services.AddOptions()).Build(); + var host = CreateBuilder().UseServer(this).ConfigureServices(services => services.AddOptions()).Build(); Assert.NotNull(host.Services.GetRequiredService>()); } @@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.Hosting // Verify ordering var configureOrder = 0; var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .ConfigureServices(services => { services.AddTransient(serviceProvider => new TestFilter( @@ -300,7 +300,7 @@ namespace Microsoft.AspNetCore.Hosting [Fact] public void EnvDefaultsToProductionIfNoConfig() { - var host = CreateBuilder().UseServer((IServerFactory)this).Build(); + var host = CreateBuilder().UseServer(this).Build(); var env = host.Services.GetService(); Assert.Equal(EnvironmentName.Production, env.EnvironmentName); } @@ -317,7 +317,7 @@ namespace Microsoft.AspNetCore.Hosting .AddInMemoryCollection(vals); var config = builder.Build(); - var host = CreateBuilder(config).UseServer((IServerFactory)this).Build(); + var host = CreateBuilder(config).UseServer(this).Build(); var env = host.Services.GetService(); Assert.Equal("Staging", env.EnvironmentName); } @@ -334,7 +334,7 @@ namespace Microsoft.AspNetCore.Hosting .AddInMemoryCollection(vals); var config = builder.Build(); - var host = CreateBuilder(config).UseServer((IServerFactory)this).Build(); + var host = CreateBuilder(config).UseServer(this).Build(); var env = host.Services.GetService(); Assert.Equal(Path.GetFullPath("testroot"), env.WebRootPath); Assert.True(env.WebRootFileProvider.GetFileInfo("TextFile.txt").Exists); @@ -343,7 +343,7 @@ namespace Microsoft.AspNetCore.Hosting [Fact] public void IsEnvironment_Extension_Is_Case_Insensitive() { - var host = CreateBuilder().UseServer((IServerFactory)this).Build(); + var host = CreateBuilder().UseServer(this).Build(); using (host) { host.Start(); @@ -401,7 +401,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHost_InvokesConfigureMethodsOnlyOnce() { var host = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .UseStartup() .Build(); using (host) @@ -434,7 +434,7 @@ namespace Microsoft.AspNetCore.Hosting public void WebHost_ThrowsForBadConfigureServiceSignature() { var builder = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .UseStartup(); var ex = Assert.Throws(() => builder.Build()); @@ -450,7 +450,7 @@ namespace Microsoft.AspNetCore.Hosting private IWebHost CreateHost(RequestDelegate requestDelegate) { var builder = CreateBuilder() - .UseServer((IServerFactory)this) + .UseServer(this) .Configure( appBuilder => { @@ -497,7 +497,7 @@ namespace Microsoft.AspNetCore.Hosting { _instanceFeaturesSupportedByThisHost = new FeatureCollection(); _instanceFeaturesSupportedByThisHost.Set(new ServerAddressesFeature()); - return this; + return new FakeServer(this); } private class StartInstance : IDisposable @@ -671,5 +671,24 @@ namespace Microsoft.AspNetCore.Hosting { public string TraceIdentifier { get; set; } } + + private class FakeServer : IServer + { + private readonly WebHostTests _webHostTests; + + public FakeServer(WebHostTests webHostTests) + { + _webHostTests = webHostTests; + } + + public IFeatureCollection Features => _webHostTests.Features; + + public void Dispose() => _webHostTests.Dispose(); + + public void Start(IHttpApplication application) + { + _webHostTests.Start(application); + } + } } }