Skip to content
This repository was archived by the owner on Dec 19, 2018. It is now read-only.

Refactoring IServerFactory #395 #445

Merged
merged 1 commit into from
Oct 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/Microsoft.AspNet.Hosting.Server.Abstractions/IServer.cs
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some doc comments

{
/// <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);
}
}
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Represents a factory for creating servers.
/// </summary>
public interface IServerFactory
{
IFeatureCollection Initialize(IConfiguration configuration);
IDisposable Start(IFeatureCollection serverFeatures, Func<IFeatureCollection, Task> application);
/// <summary>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, add some doc comments

/// 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create or CreateServer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the issue filed, CreateServer.

}
}
4 changes: 2 additions & 2 deletions src/Microsoft.AspNet.Hosting.Server.Abstractions/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {}
}
}
}
36 changes: 17 additions & 19 deletions src/Microsoft.AspNet.Hosting/Internal/HostingEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class HostingEngine : IHostingEngine
// 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,
Expand Down Expand Up @@ -87,15 +87,13 @@ public virtual IApplication Start()
var application = BuildApplication();

var logger = _applicationServices.GetRequiredService<ILogger<HostingEngine>>();
var contextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>();
var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticSource>();

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"))
Expand Down Expand Up @@ -135,11 +133,11 @@ public virtual IApplication Start()
_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();
}));
Expand Down Expand Up @@ -191,7 +189,7 @@ private RequestDelegate BuildApplication()
EnsureServer();

var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
var builder = builderFactory.CreateBuilder(_serverFeatures);
var builder = builderFactory.CreateBuilder(Server.Features);
builder.ApplicationServices = _applicationServices;

var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
Expand Down Expand Up @@ -246,21 +244,21 @@ private RequestDelegate BuildApplication()

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<IServerLoader>().LoadServerFactory(ServerFactoryLocation);
}
ServerFactory = _applicationServices.GetRequiredService<IServerLoader>().LoadServerFactory(ServerFactoryLocation);
}

if (_serverFeatures == null)
{
_serverFeatures = ServerFactory.Initialize(_config);
var addresses = _serverFeatures?.Get<IServerAddressesFeature>()?.Addresses;
Server = ServerFactory.CreateServer(_config);
var addresses = Server.Features?.Get<IServerAddressesFeature>()?.Addresses;
if (addresses != null && !addresses.IsReadOnly)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you could do if (!addresses?.IsReadOnly)

{
var port = _config[ServerPort];
Expand Down
22 changes: 20 additions & 2 deletions src/Microsoft.AspNet.Hosting/WebHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class WebHostBuilder
// Only one of these should be set
private string _serverFactoryLocation;
private IServerFactory _serverFactory;
private IServer _server;

public WebHostBuilder()
: this(config: new ConfigurationBuilder().Build())
Expand Down Expand Up @@ -133,6 +134,7 @@ public IHostingEngine Build()
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;

Expand Down Expand Up @@ -161,7 +163,18 @@ public WebHostBuilder UseEnvironment(string environment)
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)
{
Expand All @@ -172,8 +185,13 @@ public WebHostBuilder UseServer(string assemblyName)
return this;
}

public WebHostBuilder UseServer(IServerFactory factory)
public WebHostBuilder UseServerFactory(IServerFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}

_serverFactory = factory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a null check here for consistency?

return this;
}
Expand Down
30 changes: 24 additions & 6 deletions src/Microsoft.AspNet.TestHost/ClientHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,27 @@ namespace Microsoft.AspNet.TestHost
/// </summary>
public class ClientHandler : HttpMessageHandler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type shouldn't have changed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's wrong here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using the default http context factory that sets the accessor for us automatically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default http context factory is less convienet to use here, but it would make it so you don't have to think about the accessor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, so use it

{
private readonly Func<IFeatureCollection, Task> _next;
private readonly RequestDelegate _next;
private readonly PathString _pathBase;
private readonly IHttpContextFactory _factory;

/// <summary>
/// Create a new handler.
/// </summary>
/// <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)
{
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("/"))
Expand All @@ -63,7 +69,7 @@ protected override async Task<HttpResponseMessage> SendAsync(
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)
Expand All @@ -79,7 +85,7 @@ protected override async Task<HttpResponseMessage> SendAsync(
{
try
{
await _next(state.HttpContext.Features);
await _next(state.HttpContext);
state.CompleteResponse();
}
catch (Exception ex)
Expand All @@ -88,6 +94,7 @@ protected override async Task<HttpResponseMessage> SendAsync(
}
finally
{
state.ServerCleanup();
registration.Dispose();
}
});
Expand All @@ -102,14 +109,16 @@ private class RequestState
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<HttpResponseMessage>();
_requestAbortedSource = new CancellationTokenSource();
_pipelineFinished = false;
_factory = factory;

if (request.RequestUri.IsDefaultPort)
{
Expand All @@ -120,7 +129,8 @@ internal RequestState(HttpRequestMessage request, PathString pathBase)
request.Headers.Host = request.RequestUri.GetComponents(UriComponents.HostAndPort, UriFormat.UriEscaped);
}

HttpContext = new DefaultHttpContext();
HttpContext = _factory.Create(new FeatureCollection());

HttpContext.Features.Set<IHttpRequestFeature>(new RequestFeature());
_responseFeature = new ResponseFeature();
HttpContext.Features.Set<IHttpResponseFeature>(_responseFeature);
Expand Down Expand Up @@ -228,6 +238,14 @@ internal void Abort(Exception exception)
_responseStream.Abort(exception);
_responseTcs.TrySetException(exception);
}

internal void ServerCleanup()
{
if (HttpContext != null)
{
_factory.Dispose(HttpContext);
}
}
}
}
}
39 changes: 18 additions & 21 deletions src/Microsoft.AspNet.TestHost/TestServer.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,22 +14,26 @@

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<IFeatureCollection, Task> _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<IHttpContextFactory>();
_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);
Expand Down Expand Up @@ -95,7 +99,7 @@ public static WebHostBuilder CreateBuilder()
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()
Expand All @@ -106,7 +110,7 @@ public HttpClient CreateClient()
public WebSocketClient CreateWebSocketClient()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
return new WebSocketClient(Invoke, pathBase);
return new WebSocketClient(Invoke, pathBase, _httpContextFactory);
}

/// <summary>
Expand All @@ -119,31 +123,24 @@ public RequestBuilder CreateRequest(string path)
return new RequestBuilder(this, path);
}

public IFeatureCollection Initialize(IConfiguration configuration)
{
return ServerInfo;
}

public IDisposable Start(IFeatureCollection serverInformation, Func<IFeatureCollection, Task> 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()
{
_disposed = true;
_appInstance.Dispose();
}

void IServer.Start(RequestDelegate requestDelegate)
{
_appDelegate = requestDelegate;
}
}
}
Loading