diff --git a/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServer.cs b/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServer.cs
new file mode 100644
index 0000000000..c6909990d0
--- /dev/null
+++ b/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServer.cs
@@ -0,0 +1,26 @@
+// 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 Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Features;
+
+namespace Microsoft.AspNet.Hosting.Server
+{
+ ///
+ /// Represents a server.
+ ///
+ public interface IServer : IDisposable
+ {
+ ///
+ /// A collection of HTTP features of the server.
+ ///
+ IFeatureCollection Features { get; }
+
+ ///
+ /// Start the server with the given function that processes an HTTP request.
+ ///
+ /// A function that processes an HTTP request.
+ void Start(RequestDelegate requestDelegate);
+ }
+}
diff --git a/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServerFactory.cs b/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServerFactory.cs
index c685ff8570..163d39f1c0 100644
--- a/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServerFactory.cs
+++ b/src/Microsoft.AspNet.Hosting.Server.Abstractions/IServerFactory.cs
@@ -1,16 +1,20 @@
// 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.Threading.Tasks;
-using Microsoft.AspNet.Http.Features;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNet.Hosting.Server
{
+ ///
+ /// Represents a factory for creating servers.
+ ///
public interface IServerFactory
{
- IFeatureCollection Initialize(IConfiguration configuration);
- IDisposable Start(IFeatureCollection serverFeatures, Func application);
+ ///
+ /// Creates based on the given configuration.
+ ///
+ /// An instance of .
+ /// The created server.
+ IServer CreateServer(IConfiguration configuration);
}
}
diff --git a/src/Microsoft.AspNet.Hosting.Server.Abstractions/project.json b/src/Microsoft.AspNet.Hosting.Server.Abstractions/project.json
index a3ccdf94bb..315af28c3f 100644
--- a/src/Microsoft.AspNet.Hosting.Server.Abstractions/project.json
+++ b/src/Microsoft.AspNet.Hosting.Server.Abstractions/project.json
@@ -6,11 +6,11 @@
"url": "git://github.com/aspnet/hosting"
},
"dependencies": {
- "Microsoft.AspNet.Http.Features": "1.0.0-*",
+ "Microsoft.AspNet.Http.Abstractions": "1.0.0-*",
"Microsoft.Extensions.Configuration.Abstractions": "1.0.0-*"
},
"frameworks": {
"net451": {},
"dotnet5.4": {}
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs b/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs
index 55e2cd589c..c504e6d0e3 100644
--- a/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs
+++ b/src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs
@@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Hosting.Internal
// Only one of these should be set
internal IServerFactory ServerFactory { get; set; }
internal string ServerFactoryLocation { get; set; }
- private IFeatureCollection _serverFeatures;
+ internal IServer Server { get; set; }
public HostingEngine(
IServiceCollection appServices,
@@ -87,15 +87,13 @@ namespace Microsoft.AspNet.Hosting.Internal
var application = BuildApplication();
var logger = _applicationServices.GetRequiredService>();
- var contextFactory = _applicationServices.GetRequiredService();
var diagnosticSource = _applicationServices.GetRequiredService();
logger.Starting();
- var server = ServerFactory.Start(_serverFeatures,
- async features =>
+ Server.Start(
+ async httpContext =>
{
- var httpContext = contextFactory.Create(features);
httpContext.ApplicationServices = _applicationServices;
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Hosting.BeginRequest"))
@@ -135,11 +133,11 @@ namespace Microsoft.AspNet.Hosting.Internal
_applicationLifetime.NotifyStarted();
logger.Started();
- return new Application(ApplicationServices, _serverFeatures, new Disposable(() =>
+ return new Application(ApplicationServices, Server.Features, new Disposable(() =>
{
logger.Shutdown();
_applicationLifetime.StopApplication();
- server.Dispose();
+ Server.Dispose();
_applicationLifetime.NotifyStopped();
(_applicationServices as IDisposable)?.Dispose();
}));
@@ -191,7 +189,7 @@ namespace Microsoft.AspNet.Hosting.Internal
EnsureServer();
var builderFactory = _applicationServices.GetRequiredService();
- var builder = builderFactory.CreateBuilder(_serverFeatures);
+ var builder = builderFactory.CreateBuilder(Server.Features);
builder.ApplicationServices = _applicationServices;
var startupFilters = _applicationServices.GetService>();
@@ -246,21 +244,21 @@ namespace Microsoft.AspNet.Hosting.Internal
private void EnsureServer()
{
- if (ServerFactory == null)
+ if (Server == null)
{
- // Blow up if we don't have a server set at this point
- if (ServerFactoryLocation == null)
+ if (ServerFactory == null)
{
- throw new InvalidOperationException("IHostingBuilder.UseServer() is required for " + nameof(Start) + "()");
+ // 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().LoadServerFactory(ServerFactoryLocation);
- }
-
- if (_serverFeatures == null)
- {
- _serverFeatures = ServerFactory.Initialize(_config);
- var addresses = _serverFeatures?.Get()?.Addresses;
+ Server = ServerFactory.CreateServer(_config);
+ var addresses = Server.Features?.Get()?.Addresses;
if (addresses != null && !addresses.IsReadOnly)
{
var port = _config[ServerPort];
diff --git a/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs b/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs
index 8d5c5648e6..ddff824f21 100644
--- a/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs
+++ b/src/Microsoft.AspNet.Hosting/WebHostBuilder.cs
@@ -42,6 +42,7 @@ namespace Microsoft.AspNet.Hosting
// Only one of these should be set
private string _serverFactoryLocation;
private IServerFactory _serverFactory;
+ private IServer _server;
public WebHostBuilder()
: this(config: new ConfigurationBuilder().Build())
@@ -133,6 +134,7 @@ namespace Microsoft.AspNet.Hosting
var engine = new HostingEngine(hostingServices, startupLoader, _config, _captureStartupErrors);
// Only one of these should be set, but they are used in priority
+ engine.Server = _server;
engine.ServerFactory = _serverFactory;
engine.ServerFactoryLocation = _config[ServerKey] ?? _config[OldServerKey] ?? _serverFactoryLocation;
@@ -161,7 +163,18 @@ namespace Microsoft.AspNet.Hosting
return this;
}
- public WebHostBuilder UseServer(string assemblyName)
+ public WebHostBuilder UseServer(IServer server)
+ {
+ if (server == null)
+ {
+ throw new ArgumentNullException(nameof(server));
+ }
+
+ _server = server;
+ return this;
+ }
+
+ public WebHostBuilder UseServerFactory(string assemblyName)
{
if (assemblyName == null)
{
@@ -172,8 +185,13 @@ namespace Microsoft.AspNet.Hosting
return this;
}
- public WebHostBuilder UseServer(IServerFactory factory)
+ public WebHostBuilder UseServerFactory(IServerFactory factory)
{
+ if (factory == null)
+ {
+ throw new ArgumentNullException(nameof(factory));
+ }
+
_serverFactory = factory;
return this;
}
diff --git a/src/Microsoft.AspNet.TestHost/ClientHandler.cs b/src/Microsoft.AspNet.TestHost/ClientHandler.cs
index efcb5dc06e..78e3463bc6 100644
--- a/src/Microsoft.AspNet.TestHost/ClientHandler.cs
+++ b/src/Microsoft.AspNet.TestHost/ClientHandler.cs
@@ -23,21 +23,27 @@ namespace Microsoft.AspNet.TestHost
///
public class ClientHandler : HttpMessageHandler
{
- private readonly Func _next;
+ private readonly RequestDelegate _next;
private readonly PathString _pathBase;
+ private readonly IHttpContextFactory _factory;
///
/// Create a new handler.
///
/// The pipeline entry point.
- public ClientHandler(Func next, PathString pathBase)
+ public ClientHandler(RequestDelegate next, PathString pathBase, IHttpContextFactory httpContextFactory)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
+ if (httpContextFactory == null)
+ {
+ throw new ArgumentNullException(nameof(httpContextFactory));
+ }
_next = next;
+ _factory = httpContextFactory;
// PathString.StartsWithSegments that we use below requires the base path to not end in a slash.
if (pathBase.HasValue && pathBase.Value.EndsWith("/"))
@@ -63,7 +69,7 @@ namespace Microsoft.AspNet.TestHost
throw new ArgumentNullException(nameof(request));
}
- var state = new RequestState(request, _pathBase);
+ var state = new RequestState(request, _pathBase, _factory);
var requestContent = request.Content ?? new StreamContent(Stream.Null);
var body = await requestContent.ReadAsStreamAsync();
if (body.CanSeek)
@@ -79,7 +85,7 @@ namespace Microsoft.AspNet.TestHost
{
try
{
- await _next(state.HttpContext.Features);
+ await _next(state.HttpContext);
state.CompleteResponse();
}
catch (Exception ex)
@@ -88,6 +94,7 @@ namespace Microsoft.AspNet.TestHost
}
finally
{
+ state.ServerCleanup();
registration.Dispose();
}
});
@@ -102,14 +109,16 @@ namespace Microsoft.AspNet.TestHost
private ResponseStream _responseStream;
private ResponseFeature _responseFeature;
private CancellationTokenSource _requestAbortedSource;
+ private IHttpContextFactory _factory;
private bool _pipelineFinished;
- internal RequestState(HttpRequestMessage request, PathString pathBase)
+ internal RequestState(HttpRequestMessage request, PathString pathBase, IHttpContextFactory factory)
{
_request = request;
_responseTcs = new TaskCompletionSource();
_requestAbortedSource = new CancellationTokenSource();
_pipelineFinished = false;
+ _factory = factory;
if (request.RequestUri.IsDefaultPort)
{
@@ -120,7 +129,8 @@ namespace Microsoft.AspNet.TestHost
request.Headers.Host = request.RequestUri.GetComponents(UriComponents.HostAndPort, UriFormat.UriEscaped);
}
- HttpContext = new DefaultHttpContext();
+ HttpContext = _factory.Create(new FeatureCollection());
+
HttpContext.Features.Set(new RequestFeature());
_responseFeature = new ResponseFeature();
HttpContext.Features.Set(_responseFeature);
@@ -228,6 +238,14 @@ namespace Microsoft.AspNet.TestHost
_responseStream.Abort(exception);
_responseTcs.TrySetException(exception);
}
+
+ internal void ServerCleanup()
+ {
+ if (HttpContext != null)
+ {
+ _factory.Dispose(HttpContext);
+ }
+ }
}
}
}
diff --git a/src/Microsoft.AspNet.TestHost/TestServer.cs b/src/Microsoft.AspNet.TestHost/TestServer.cs
index 176431060c..cd354c3eba 100644
--- a/src/Microsoft.AspNet.TestHost/TestServer.cs
+++ b/src/Microsoft.AspNet.TestHost/TestServer.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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;
@@ -14,22 +14,26 @@ using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNet.TestHost
{
- public class TestServer : IServerFactory, IDisposable
+ public class TestServer : IServer
{
private const string DefaultEnvironmentName = "Development";
private const string ServerName = nameof(TestServer);
- private static readonly IFeatureCollection ServerInfo = new FeatureCollection();
- private Func _appDelegate;
+ private RequestDelegate _appDelegate;
private IDisposable _appInstance;
private bool _disposed = false;
+ private IHttpContextFactory _httpContextFactory;
public TestServer(WebHostBuilder builder)
{
- _appInstance = builder.UseServer(this).Build().Start();
+ var hostingEngine = builder.UseServer(this).Build();
+ _httpContextFactory = hostingEngine.ApplicationServices.GetService();
+ _appInstance = hostingEngine.Start();
}
public Uri BaseAddress { get; set; } = new Uri("http://localhost/");
+ IFeatureCollection IServer.Features { get; }
+
public static TestServer Create()
{
return Create(config: null, configureApp: null, configureServices: null);
@@ -95,7 +99,7 @@ namespace Microsoft.AspNet.TestHost
public HttpMessageHandler CreateHandler()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
- return new ClientHandler(Invoke, pathBase);
+ return new ClientHandler(Invoke, pathBase, _httpContextFactory);
}
public HttpClient CreateClient()
@@ -106,7 +110,7 @@ namespace Microsoft.AspNet.TestHost
public WebSocketClient CreateWebSocketClient()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
- return new WebSocketClient(Invoke, pathBase);
+ return new WebSocketClient(Invoke, pathBase, _httpContextFactory);
}
///
@@ -119,25 +123,13 @@ namespace Microsoft.AspNet.TestHost
return new RequestBuilder(this, path);
}
- public IFeatureCollection Initialize(IConfiguration configuration)
- {
- return ServerInfo;
- }
-
- public IDisposable Start(IFeatureCollection serverInformation, Func application)
- {
- _appDelegate = application;
-
- return this;
- }
-
- public Task Invoke(IFeatureCollection featureCollection)
+ public Task Invoke(HttpContext context)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
- return _appDelegate(featureCollection);
+ return _appDelegate(context);
}
public void Dispose()
@@ -145,5 +137,10 @@ namespace Microsoft.AspNet.TestHost
_disposed = true;
_appInstance.Dispose();
}
+
+ void IServer.Start(RequestDelegate requestDelegate)
+ {
+ _appDelegate = requestDelegate;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.TestHost/WebSocketClient.cs b/src/Microsoft.AspNet.TestHost/WebSocketClient.cs
index 9946d5e4bd..320f43afe8 100644
--- a/src/Microsoft.AspNet.TestHost/WebSocketClient.cs
+++ b/src/Microsoft.AspNet.TestHost/WebSocketClient.cs
@@ -8,25 +8,31 @@ using System.Net.WebSockets;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
namespace Microsoft.AspNet.TestHost
{
public class WebSocketClient
{
- private readonly Func _next;
+ private readonly RequestDelegate _next;
private readonly PathString _pathBase;
+ private readonly IHttpContextFactory _httpContextFactory;
- internal WebSocketClient(Func next, PathString pathBase)
+ internal WebSocketClient(RequestDelegate next, PathString pathBase, IHttpContextFactory httpContextFactory)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
+ if (httpContextFactory == null)
+ {
+ throw new ArgumentNullException(nameof(httpContextFactory));
+ }
_next = next;
+ _httpContextFactory = httpContextFactory;
// PathString.StartsWithSegments that we use below requires the base path to not end in a slash.
if (pathBase.HasValue && pathBase.Value.EndsWith("/"))
@@ -52,7 +58,7 @@ namespace Microsoft.AspNet.TestHost
public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken)
{
- var state = new RequestState(uri, _pathBase, cancellationToken);
+ var state = new RequestState(uri, _pathBase, cancellationToken, _httpContextFactory);
if (ConfigureRequest != null)
{
@@ -64,7 +70,7 @@ namespace Microsoft.AspNet.TestHost
{
try
{
- await _next(state.FeatureCollection);
+ await _next(state.HttpContext);
state.PipelineComplete();
}
catch (Exception ex)
@@ -84,18 +90,18 @@ namespace Microsoft.AspNet.TestHost
{
private TaskCompletionSource _clientWebSocketTcs;
private WebSocket _serverWebSocket;
+ private IHttpContextFactory _factory;
- public IFeatureCollection FeatureCollection { get; private set; }
public HttpContext HttpContext { get; private set; }
public Task WebSocketTask { get { return _clientWebSocketTcs.Task; } }
- public RequestState(Uri uri, PathString pathBase, CancellationToken cancellationToken)
+ public RequestState(Uri uri, PathString pathBase, CancellationToken cancellationToken, IHttpContextFactory factory)
{
+ _factory = factory;
_clientWebSocketTcs = new TaskCompletionSource();
// HttpContext
- FeatureCollection = new FeatureCollection();
- HttpContext = new DefaultHttpContext(FeatureCollection);
+ HttpContext = _factory.Create(new FeatureCollection());
// Request
HttpContext.Features.Set(new RequestFeature());
@@ -147,6 +153,10 @@ namespace Microsoft.AspNet.TestHost
public void Dispose()
{
+ if (HttpContext != null)
+ {
+ _factory.Dispose(HttpContext);
+ }
if (_serverWebSocket != null)
{
_serverWebSocket.Dispose();
diff --git a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs
index 928cd5bb35..0e0655ecac 100644
--- a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs
+++ b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs
@@ -14,6 +14,7 @@ using Microsoft.AspNet.Hosting.Server;
using Microsoft.AspNet.Hosting.Startup;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
+using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Server.Features;
using Microsoft.AspNet.Testing.xunit;
using Microsoft.Extensions.Configuration;
@@ -26,10 +27,33 @@ using Xunit;
namespace Microsoft.AspNet.Hosting
{
- public class HostingEngineTests : IServerFactory
+ public class HostingEngineTests : IServerFactory, IServer
{
private readonly IList _startInstances = new List();
private IFeatureCollection _featuresSupportedByThisHost = NewFeatureCollection();
+ private IFeatureCollection _instanceFeaturesSupportedByThisHost;
+
+ public IFeatureCollection Features {
+ get
+ {
+ var features = new FeatureCollection();
+
+ foreach (var feature in _featuresSupportedByThisHost)
+ {
+ features[feature.Key] = feature.Value;
+ }
+
+ if (_instanceFeaturesSupportedByThisHost != null)
+ {
+ foreach (var feature in _instanceFeaturesSupportedByThisHost)
+ {
+ features[feature.Key] = feature.Value;
+ }
+ }
+
+ return features;
+ }
+ }
static IFeatureCollection NewFeatureCollection()
{
@@ -431,26 +455,36 @@ namespace Microsoft.AspNet.Hosting
return new WebHostBuilder(config ?? new ConfigurationBuilder().Build());
}
- public IFeatureCollection Initialize(IConfiguration configuration)
+ public void Start(RequestDelegate requestDelegate)
{
- var features = new FeatureCollection();
- features.Set(new ServerAddressesFeature());
- return features;
- }
-
- public IDisposable Start(IFeatureCollection serverFeatures, Func application)
- {
- var startInstance = new StartInstance(application);
+ var startInstance = new StartInstance(requestDelegate);
_startInstances.Add(startInstance);
- application(_featuresSupportedByThisHost);
- return startInstance;
+ requestDelegate(new DefaultHttpContext(Features));
}
- public class StartInstance : IDisposable
+ public void Dispose()
{
- private readonly Func _application;
+ if (_startInstances != null)
+ {
+ foreach (var startInstance in _startInstances)
+ {
+ startInstance.Dispose();
+ }
+ }
+ }
- public StartInstance(Func application)
+ public IServer CreateServer(IConfiguration configuration)
+ {
+ _instanceFeaturesSupportedByThisHost = new FeatureCollection();
+ _instanceFeaturesSupportedByThisHost.Set(new ServerAddressesFeature());
+ return this;
+ }
+
+ private class StartInstance : IDisposable
+ {
+ private readonly RequestDelegate _application;
+
+ public StartInstance(RequestDelegate application)
{
_application = application;
}
diff --git a/test/Microsoft.AspNet.Hosting.Tests/WebHostBuilderTests.cs b/test/Microsoft.AspNet.Hosting.Tests/WebHostBuilderTests.cs
index 988194f0a3..3ab61daf96 100644
--- a/test/Microsoft.AspNet.Hosting.Tests/WebHostBuilderTests.cs
+++ b/test/Microsoft.AspNet.Hosting.Tests/WebHostBuilderTests.cs
@@ -1,13 +1,13 @@
// 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.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting.Fakes;
using Microsoft.AspNet.Hosting.Internal;
using Microsoft.AspNet.Hosting.Server;
+using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Extensions.Configuration;
@@ -42,11 +42,11 @@ namespace Microsoft.AspNet.Hosting
public async Task StartupMissing_Fallback()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup("MissingStartupAssembly").Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup("MissingStartupAssembly").Build();
using (engine.Start())
{
- await AssertResponseContains(serverFactory.Application, "MissingStartupAssembly");
+ await AssertResponseContains(server.RequestDelegate, "MissingStartupAssembly");
}
}
@@ -54,11 +54,11 @@ namespace Microsoft.AspNet.Hosting
public async Task StartupStaticCtorThrows_Fallback()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup().Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup().Build();
using (engine.Start())
{
- await AssertResponseContains(serverFactory.Application, "Exception from static constructor");
+ await AssertResponseContains(server.RequestDelegate, "Exception from static constructor");
}
}
@@ -66,11 +66,11 @@ namespace Microsoft.AspNet.Hosting
public async Task StartupCtorThrows_Fallback()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup().Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup().Build();
using (engine.Start())
{
- await AssertResponseContains(serverFactory.Application, "Exception from constructor");
+ await AssertResponseContains(server.RequestDelegate, "Exception from constructor");
}
}
@@ -78,11 +78,11 @@ namespace Microsoft.AspNet.Hosting
public async Task StartupCtorThrows_TypeLoadException()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup().Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup().Build();
using (engine.Start())
{
- await AssertResponseContains(serverFactory.Application, "Message from the LoaderException");
+ await AssertResponseContains(server.RequestDelegate, "Message from the LoaderException");
}
}
@@ -90,13 +90,13 @@ namespace Microsoft.AspNet.Hosting
public async Task IApplicationLifetimeRegisteredEvenWhenStartupCtorThrows_Fallback()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup().Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup().Build();
using (engine.Start())
{
- var service = engine.ApplicationServices.GetService();
+ var service = engine.ApplicationServices.GetServices();
Assert.NotNull(service);
- await AssertResponseContains(serverFactory.Application, "Exception from constructor");
+ await AssertResponseContains(server.RequestDelegate, "Exception from constructor");
}
}
@@ -104,11 +104,11 @@ namespace Microsoft.AspNet.Hosting
public async Task StartupConfigureServicesThrows_Fallback()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup().Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup().Build();
using (engine.Start())
{
- await AssertResponseContains(serverFactory.Application, "Exception from ConfigureServices");
+ await AssertResponseContains(server.RequestDelegate, "Exception from ConfigureServices");
}
}
@@ -116,11 +116,11 @@ namespace Microsoft.AspNet.Hosting
public async Task StartupConfigureThrows_Fallback()
{
var builder = CreateWebHostBuilder();
- var serverFactory = new TestServerFactory();
- var engine = builder.UseServer(serverFactory).UseStartup().Build();
+ var server = new TestServer();
+ var engine = builder.UseServer(server).UseStartup().Build();
using (engine.Start())
{
- await AssertResponseContains(serverFactory.Application, "Exception from Configure");
+ await AssertResponseContains(server.RequestDelegate, "Exception from Configure");
}
}
@@ -137,36 +137,29 @@ namespace Microsoft.AspNet.Hosting
return new WebHostBuilder(config, captureStartupErrors: true);
}
- private async Task AssertResponseContains(Func app, string expectedText)
+ private async Task AssertResponseContains(RequestDelegate app, string expectedText)
{
var httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
- await app(httpContext.Features);
+ await app(httpContext);
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var bodyText = new StreamReader(httpContext.Response.Body).ReadToEnd();
Assert.Contains(expectedText, bodyText);
}
- private class TestServerFactory : IServerFactory
+ private class TestServer : IServer
{
- public Func Application { get; set; }
+ IFeatureCollection IServer.Features { get; }
+ public RequestDelegate RequestDelegate { get; private set; }
- public IFeatureCollection Initialize(IConfiguration configuration)
+ public void Dispose()
{
- return new FeatureCollection();
+
}
- public IDisposable Start(IFeatureCollection serverFeatures, Func application)
+ public void Start(RequestDelegate requestDelegate)
{
- Application = application;
- return new Disposable();
- }
-
- private class Disposable : IDisposable
- {
- public void Dispose()
- {
- }
+ RequestDelegate = requestDelegate;
}
}
}
diff --git a/test/Microsoft.AspNet.TestHost.Tests/ClientHandlerTests.cs b/test/Microsoft.AspNet.TestHost.Tests/ClientHandlerTests.cs
index ec7976d685..adacdd9d8e 100644
--- a/test/Microsoft.AspNet.TestHost.Tests/ClientHandlerTests.cs
+++ b/test/Microsoft.AspNet.TestHost.Tests/ClientHandlerTests.cs
@@ -17,13 +17,13 @@ namespace Microsoft.AspNet.TestHost
{
public class ClientHandlerTests
{
+ private IHttpContextFactory _httpContextFactory = new HttpContextFactory(new HttpContextAccessor());
+
[Fact]
public Task ExpectedKeysAreAvailable()
{
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
-
// TODO: Assert.True(context.RequestAborted.CanBeCanceled);
Assert.Equal("HTTP/1.1", context.Request.Protocol);
Assert.Equal("GET", context.Request.Method);
@@ -40,7 +40,7 @@ namespace Microsoft.AspNet.TestHost
Assert.Equal("example.com", context.Request.Host.Value);
return Task.FromResult(0);
- }, new PathString("/A/Path/"));
+ }, new PathString("/A/Path/"), _httpContextFactory);
var httpClient = new HttpClient(handler);
return httpClient.GetAsync("https://example.com/A/Path/and/file.txt?and=query");
}
@@ -48,14 +48,13 @@ namespace Microsoft.AspNet.TestHost
[Fact]
public Task SingleSlashNotMovedToPathBase()
{
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
Assert.Equal("", context.Request.PathBase.Value);
Assert.Equal("/", context.Request.Path.Value);
return Task.FromResult(0);
- }, new PathString(""));
+ }, new PathString(""), _httpContextFactory);
var httpClient = new HttpClient(handler);
return httpClient.GetAsync("https://example.com/");
}
@@ -64,15 +63,14 @@ namespace Microsoft.AspNet.TestHost
public async Task ResubmitRequestWorks()
{
int requestCount = 1;
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
int read = context.Request.Body.Read(new byte[100], 0, 100);
Assert.Equal(11, read);
context.Response.Headers["TestHeader"] = "TestValue:" + requestCount++;
return Task.FromResult(0);
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
HttpMessageInvoker invoker = new HttpMessageInvoker(handler);
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, "https://example.com/");
@@ -88,13 +86,11 @@ namespace Microsoft.AspNet.TestHost
[Fact]
public async Task MiddlewareOnlySetsHeaders()
{
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
-
context.Response.Headers["TestHeader"] = "TestValue";
return Task.FromResult(0);
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/");
Assert.Equal("TestValue", response.Headers.GetValues("TestHeader").First());
@@ -104,11 +100,11 @@ namespace Microsoft.AspNet.TestHost
public async Task BlockingMiddlewareShouldNotBlockClient()
{
ManualResetEvent block = new ManualResetEvent(false);
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
block.WaitOne();
return Task.FromResult(0);
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
Task task = httpClient.GetAsync("https://example.com/");
Assert.False(task.IsCompleted);
@@ -121,14 +117,13 @@ namespace Microsoft.AspNet.TestHost
public async Task HeadersAvailableBeforeBodyFinished()
{
ManualResetEvent block = new ManualResetEvent(false);
- var handler = new ClientHandler(async env =>
+ var handler = new ClientHandler(async context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
context.Response.Headers["TestHeader"] = "TestValue";
await context.Response.WriteAsync("BodyStarted,");
block.WaitOne();
await context.Response.WriteAsync("BodyFinished");
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
HttpCompletionOption.ResponseHeadersRead);
@@ -141,14 +136,13 @@ namespace Microsoft.AspNet.TestHost
public async Task FlushSendsHeaders()
{
ManualResetEvent block = new ManualResetEvent(false);
- var handler = new ClientHandler(async env =>
+ var handler = new ClientHandler(async context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
context.Response.Headers["TestHeader"] = "TestValue";
context.Response.Body.Flush();
block.WaitOne();
await context.Response.WriteAsync("BodyFinished");
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
HttpCompletionOption.ResponseHeadersRead);
@@ -161,14 +155,13 @@ namespace Microsoft.AspNet.TestHost
public async Task ClientDisposalCloses()
{
ManualResetEvent block = new ManualResetEvent(false);
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
context.Response.Headers["TestHeader"] = "TestValue";
context.Response.Body.Flush();
block.WaitOne();
return Task.FromResult(0);
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
HttpCompletionOption.ResponseHeadersRead);
@@ -187,14 +180,13 @@ namespace Microsoft.AspNet.TestHost
public async Task ClientCancellationAborts()
{
ManualResetEvent block = new ManualResetEvent(false);
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
context.Response.Headers["TestHeader"] = "TestValue";
context.Response.Body.Flush();
block.WaitOne();
return Task.FromResult(0);
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
HttpCompletionOption.ResponseHeadersRead);
@@ -213,10 +205,10 @@ namespace Microsoft.AspNet.TestHost
[Fact]
public Task ExceptionBeforeFirstWriteIsReported()
{
- var handler = new ClientHandler(env =>
+ var handler = new ClientHandler(context =>
{
throw new InvalidOperationException("Test Exception");
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
return Assert.ThrowsAsync(() => httpClient.GetAsync("https://example.com/",
HttpCompletionOption.ResponseHeadersRead));
@@ -227,14 +219,13 @@ namespace Microsoft.AspNet.TestHost
public async Task ExceptionAfterFirstWriteIsReported()
{
ManualResetEvent block = new ManualResetEvent(false);
- var handler = new ClientHandler(async env =>
+ var handler = new ClientHandler(async context =>
{
- var context = new DefaultHttpContext((IFeatureCollection)env);
context.Response.Headers["TestHeader"] = "TestValue";
await context.Response.WriteAsync("BodyStarted");
block.WaitOne();
throw new InvalidOperationException("Test Exception");
- }, PathString.Empty);
+ }, PathString.Empty, _httpContextFactory);
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
HttpCompletionOption.ResponseHeadersRead);