Reacting to new IServer IHttpApplication design

This commit is contained in:
John Luo 2015-11-03 13:24:23 -08:00
parent 82c855d172
commit 8712ba4855
8 changed files with 100 additions and 39 deletions

View File

@ -21,7 +21,6 @@ using System.Diagnostics.Contracts;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
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.Server.Features; using Microsoft.AspNet.Server.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -35,9 +34,8 @@ namespace Microsoft.AspNet.Server.WebListener
private readonly Microsoft.Net.Http.Server.WebListener _listener; private readonly Microsoft.Net.Http.Server.WebListener _listener;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IHttpContextFactory _httpContextFactory;
private RequestDelegate _appFunc; private IHttpApplication<object> _application;
private int _maxAccepts; private int _maxAccepts;
private int _acceptorCounts; private int _acceptorCounts;
@ -47,17 +45,16 @@ namespace Microsoft.AspNet.Server.WebListener
private int _outstandingRequests; private int _outstandingRequests;
private ManualResetEvent _shutdownSignal; private ManualResetEvent _shutdownSignal;
internal MessagePump(Microsoft.Net.Http.Server.WebListener listener, ILoggerFactory loggerFactory, IFeatureCollection features, IHttpContextFactory httpContextFactory) internal MessagePump(Microsoft.Net.Http.Server.WebListener listener, ILoggerFactory loggerFactory, IFeatureCollection features)
{ {
if (features == null) if (features == null)
{ {
throw new ArgumentNullException(nameof(Features)); throw new ArgumentNullException(nameof(features));
} }
Contract.Assert(listener != null); Contract.Assert(listener != null);
_listener = listener; _listener = listener;
_logger = LogHelper.CreateLogger(loggerFactory, typeof(MessagePump)); _logger = LogHelper.CreateLogger(loggerFactory, typeof(MessagePump));
_httpContextFactory = httpContextFactory;
Features = features; Features = features;
_processRequest = new Action<object>(ProcessRequestAsync); _processRequest = new Action<object>(ProcessRequestAsync);
@ -90,11 +87,11 @@ namespace Microsoft.AspNet.Server.WebListener
public IFeatureCollection Features { get; } public IFeatureCollection Features { get; }
public void Start(RequestDelegate app) public void Start<TContext>(IHttpApplication<TContext> application)
{ {
if (app == null) if (application == null)
{ {
throw new ArgumentNullException(nameof(app)); throw new ArgumentNullException(nameof(application));
} }
var addressesFeature = Features.Get<IServerAddressesFeature>(); var addressesFeature = Features.Get<IServerAddressesFeature>();
@ -106,11 +103,11 @@ namespace Microsoft.AspNet.Server.WebListener
ParseAddresses(addressesFeature.Addresses, Listener); ParseAddresses(addressesFeature.Addresses, Listener);
// Can't call Start twice // Can't call Start twice
Contract.Assert(_appFunc == null); Contract.Assert(_application == null);
Contract.Assert(app != null); Contract.Assert(application != null);
_appFunc = app; _application = new ApplicationWrapper<TContext>(application);
if (_listener.UrlPrefixes.Count == 0) if (_listener.UrlPrefixes.Count == 0)
{ {
@ -183,14 +180,15 @@ namespace Microsoft.AspNet.Server.WebListener
return; return;
} }
HttpContext httpContext = null; object context = null;
try try
{ {
Interlocked.Increment(ref _outstandingRequests); Interlocked.Increment(ref _outstandingRequests);
FeatureContext featureContext = new FeatureContext(requestContext, EnableResponseCaching); FeatureContext featureContext = new FeatureContext(requestContext, EnableResponseCaching);
httpContext = _httpContextFactory.Create(featureContext.Features); context = _application.CreateContext(featureContext.Features);
await _appFunc(httpContext).SupressContext(); await _application.ProcessRequestAsync(context).SupressContext();
requestContext.Dispose(); requestContext.Dispose();
_application.DisposeContext(context, null);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -205,13 +203,10 @@ namespace Microsoft.AspNet.Server.WebListener
requestContext.Response.Reset(); requestContext.Response.Reset();
SetFatalResponse(requestContext, 500); SetFatalResponse(requestContext, 500);
} }
_application.DisposeContext(context, ex);
} }
finally finally
{ {
if (httpContext != null)
{
_httpContextFactory.Dispose(httpContext);
}
if (Interlocked.Decrement(ref _outstandingRequests) == 0 && _stopping) if (Interlocked.Decrement(ref _outstandingRequests) == 0 && _stopping)
{ {
_shutdownSignal.Set(); _shutdownSignal.Set();
@ -253,5 +248,30 @@ namespace Microsoft.AspNet.Server.WebListener
// All requests are finished // All requests are finished
_listener.Dispose(); _listener.Dispose();
} }
private class ApplicationWrapper<TContext> : IHttpApplication<object>
{
private readonly IHttpApplication<TContext> _application;
public ApplicationWrapper(IHttpApplication<TContext> application)
{
_application = application;
}
public object CreateContext(IFeatureCollection contextFeatures)
{
return _application.CreateContext(contextFeatures);
}
public void DisposeContext(object context, Exception exception)
{
_application.DisposeContext((TContext)context, exception);
}
public Task ProcessRequestAsync(object context)
{
return _application.ProcessRequestAsync((TContext)context);
}
}
} }
} }

View File

@ -37,7 +37,6 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
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.Server.Features; using Microsoft.AspNet.Server.Features;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -51,12 +50,10 @@ namespace Microsoft.AspNet.Server.WebListener
public class ServerFactory : IServerFactory public class ServerFactory : IServerFactory
{ {
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
private IHttpContextFactory _httpContextFactory;
public ServerFactory(ILoggerFactory loggerFactory, IHttpContextFactory httpContextFactory) public ServerFactory(ILoggerFactory loggerFactory)
{ {
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
_httpContextFactory = httpContextFactory;
} }
/// <summary> /// <summary>
@ -72,7 +69,7 @@ namespace Microsoft.AspNet.Server.WebListener
serverFeatures.Set(listener); serverFeatures.Set(listener);
serverFeatures.Set(SplitAddresses(configuration)); serverFeatures.Set(SplitAddresses(configuration));
return new MessagePump(listener, _loggerFactory, serverFeatures, _httpContextFactory); return new MessagePump(listener, _loggerFactory, serverFeatures);
} }
private IServerAddressesFeature SplitAddresses(IConfiguration config) private IServerAddressesFeature SplitAddresses(IConfiguration config)

View File

@ -20,9 +20,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Authentication; using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.AspNet.Http.Internal;
using Xunit; using Xunit;
using AuthenticationSchemes = Microsoft.Net.Http.Server.AuthenticationSchemes; using AuthenticationSchemes = Microsoft.Net.Http.Server.AuthenticationSchemes;

View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
// NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing
// permissions and limitations under the License.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting.Server;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
namespace Microsoft.AspNet.Server.WebListener
{
internal class DummyApplication : IHttpApplication<HttpContext>
{
private readonly RequestDelegate _requestDelegate;
public DummyApplication(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
}
public HttpContext CreateContext(IFeatureCollection contextFeatures)
{
return new DefaultHttpContext(contextFeatures);
}
public void DisposeContext(HttpContext httpContext, Exception exception)
{
}
public async Task ProcessRequestAsync(HttpContext httpContext)
{
await _requestDelegate(httpContext);
}
}
}

View File

@ -22,7 +22,6 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener

View File

@ -20,11 +20,9 @@ using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Hosting.Builder;
using Microsoft.AspNet.Hosting.Server; using Microsoft.AspNet.Hosting.Server;
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.Net.Http.Server; using Microsoft.Net.Http.Server;
using Xunit; using Xunit;
@ -171,7 +169,7 @@ namespace Microsoft.AspNet.Server.WebListener
var dynamicServer = Utilities.CreateHttpServerReturnRoot("/", out root, app); var dynamicServer = Utilities.CreateHttpServerReturnRoot("/", out root, app);
dynamicServer.Dispose(); dynamicServer.Dispose();
var rootUri = new Uri(root); var rootUri = new Uri(root);
var factory = new ServerFactory(loggerFactory: null, httpContextFactory: new HttpContextFactory(new HttpContextAccessor())); var factory = new ServerFactory(loggerFactory: null);
var server = factory.CreateServer(configuration: null); var server = factory.CreateServer(configuration: null);
var listener = server.Features.Get<Microsoft.Net.Http.Server.WebListener>(); var listener = server.Features.Get<Microsoft.Net.Http.Server.WebListener>();
@ -180,7 +178,7 @@ namespace Microsoft.AspNet.Server.WebListener
listener.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path)); listener.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path));
} }
server.Start(app); server.Start(new DummyApplication(app));
return server; return server;
} }

View File

@ -24,10 +24,8 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Hosting.Builder;
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.Net.Http.Server; using Microsoft.Net.Http.Server;
using Xunit; using Xunit;
@ -253,7 +251,7 @@ namespace Microsoft.AspNet.Server.WebListener
string address; string address;
using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { } using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { }
var factory = new ServerFactory(loggerFactory: null, httpContextFactory: new HttpContextFactory(new HttpContextAccessor())); var factory = new ServerFactory(loggerFactory: null);
var server = factory.CreateServer(configuration: null); var server = factory.CreateServer(configuration: null);
var listener = server.Features.Get<Microsoft.Net.Http.Server.WebListener>(); var listener = server.Features.Get<Microsoft.Net.Http.Server.WebListener>();
listener.UrlPrefixes.Add(UrlPrefix.Create(address)); listener.UrlPrefixes.Add(UrlPrefix.Create(address));
@ -261,7 +259,7 @@ namespace Microsoft.AspNet.Server.WebListener
using (server) using (server)
{ {
server.Start(httpContext => Task.FromResult(0)); server.Start(new DummyApplication(httpContext => Task.FromResult(0)));
string response = await SendRequestAsync(address); string response = await SendRequestAsync(address);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);
} }

View File

@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Server.WebListener
internal static IServer CreateDynamicHttpServer(string basePath, AuthenticationSchemes authType, out string root, out string baseAddress, RequestDelegate app) internal static IServer CreateDynamicHttpServer(string basePath, AuthenticationSchemes authType, out string root, out string baseAddress, RequestDelegate app)
{ {
var factory = new ServerFactory(loggerFactory: null, httpContextFactory: Factory); var factory = new ServerFactory(loggerFactory: null);
lock (PortLock) lock (PortLock)
{ {
while (NextPort < MaxPort) while (NextPort < MaxPort)
@ -73,7 +73,7 @@ namespace Microsoft.AspNet.Server.WebListener
listener.AuthenticationManager.AuthenticationSchemes = authType; listener.AuthenticationManager.AuthenticationSchemes = authType;
try try
{ {
server.Start(app); server.Start(new DummyApplication(app));
return server; return server;
} }
catch (WebListenerException) catch (WebListenerException)
@ -92,10 +92,10 @@ namespace Microsoft.AspNet.Server.WebListener
internal static IServer CreateServer(string scheme, string host, int port, string path, RequestDelegate app) internal static IServer CreateServer(string scheme, string host, int port, string path, RequestDelegate app)
{ {
var factory = new ServerFactory(loggerFactory: null, httpContextFactory: Factory); var factory = new ServerFactory(loggerFactory: null);
var server = factory.CreateServer(configuration: null); var server = factory.CreateServer(configuration: null);
server.Features.Get<IServerAddressesFeature>().Addresses.Add(UrlPrefix.Create(scheme, host, port, path).ToString()); server.Features.Get<IServerAddressesFeature>().Addresses.Add(UrlPrefix.Create(scheme, host, port, path).ToString());
server.Start(app); server.Start(new DummyApplication(app));
return server; return server;
} }
} }