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