Reacting to new IServer and IHttpApplication design
This commit is contained in:
parent
0c33cad343
commit
168f4770f4
|
|
@ -185,7 +185,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
private Frame CreateFrame()
|
||||
{
|
||||
return new Frame(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest);
|
||||
return FrameFactory(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest);
|
||||
}
|
||||
|
||||
void IConnectionControl.Pause()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ using Microsoft.Extensions.Primitives;
|
|||
|
||||
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||
{
|
||||
public partial class Frame : FrameContext, IFrameControl
|
||||
public abstract partial class Frame : FrameContext, IFrameControl
|
||||
{
|
||||
private static readonly Encoding _ascii = Encoding.ASCII;
|
||||
private static readonly ArraySegment<byte> _endChunkBytes = CreateAsciiByteArraySegment("\r\n");
|
||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
private readonly object _onStartingSync = new Object();
|
||||
private readonly object _onCompletedSync = new Object();
|
||||
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
|
||||
protected readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
|
||||
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
|
||||
|
||||
private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||
|
|
@ -50,18 +50,18 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
private bool _requestProcessingStarted;
|
||||
private Task _requestProcessingTask;
|
||||
private volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||
private volatile bool _requestAborted;
|
||||
private CancellationTokenSource _abortedCts;
|
||||
private CancellationToken? _manuallySetRequestAbortToken;
|
||||
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||
protected volatile bool _requestAborted;
|
||||
protected CancellationTokenSource _abortedCts;
|
||||
protected CancellationToken? _manuallySetRequestAbortToken;
|
||||
|
||||
private FrameRequestStream _requestBody;
|
||||
private FrameResponseStream _responseBody;
|
||||
internal FrameRequestStream _requestBody;
|
||||
internal FrameResponseStream _responseBody;
|
||||
|
||||
private bool _responseStarted;
|
||||
private bool _keepAlive;
|
||||
protected bool _responseStarted;
|
||||
protected bool _keepAlive;
|
||||
private bool _autoChunk;
|
||||
private Exception _applicationException;
|
||||
protected Exception _applicationException;
|
||||
|
||||
private HttpVersionType _httpVersion;
|
||||
|
||||
|
|
@ -306,119 +306,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
/// The resulting Task from this loop is preserved in a field which is used when the server needs
|
||||
/// to drain and close all currently active connections.
|
||||
/// </summary>
|
||||
public async Task RequestProcessingAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var terminated = false;
|
||||
while (!terminated && !_requestProcessingStopping)
|
||||
{
|
||||
while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput))
|
||||
{
|
||||
terminated = SocketInput.RemoteIntakeFin;
|
||||
if (!terminated)
|
||||
{
|
||||
await SocketInput;
|
||||
}
|
||||
}
|
||||
|
||||
while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders))
|
||||
{
|
||||
terminated = SocketInput.RemoteIntakeFin;
|
||||
if (!terminated)
|
||||
{
|
||||
await SocketInput;
|
||||
}
|
||||
}
|
||||
|
||||
if (!terminated && !_requestProcessingStopping)
|
||||
{
|
||||
var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this);
|
||||
_keepAlive = messageBody.RequestKeepAlive;
|
||||
_requestBody = new FrameRequestStream(messageBody);
|
||||
RequestBody = _requestBody;
|
||||
_responseBody = new FrameResponseStream(this);
|
||||
ResponseBody = _responseBody;
|
||||
DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
|
||||
|
||||
_abortedCts = null;
|
||||
_manuallySetRequestAbortToken = null;
|
||||
|
||||
var httpContext = HttpContextFactory.Create(this);
|
||||
try
|
||||
{
|
||||
await Application.Invoke(httpContext).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReportApplicationError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Trigger OnStarting if it hasn't been called yet and the app hasn't
|
||||
// already failed. If an OnStarting callback throws we can go through
|
||||
// our normal error handling in ProduceEnd.
|
||||
// https://github.com/aspnet/KestrelHttpServer/issues/43
|
||||
if (!_responseStarted && _applicationException == null)
|
||||
{
|
||||
await FireOnStarting();
|
||||
}
|
||||
|
||||
await FireOnCompleted();
|
||||
|
||||
HttpContextFactory.Dispose(httpContext);
|
||||
|
||||
// If _requestAbort is set, the connection has already been closed.
|
||||
if (!_requestAborted)
|
||||
{
|
||||
await ProduceEnd();
|
||||
|
||||
if (_keepAlive)
|
||||
{
|
||||
// Finish reading the request body in case the app did not.
|
||||
await messageBody.Consume();
|
||||
}
|
||||
}
|
||||
|
||||
_requestBody.StopAcceptingReads();
|
||||
_responseBody.StopAcceptingWrites();
|
||||
}
|
||||
|
||||
terminated = !_keepAlive;
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogWarning("Connection processing ended abnormally", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
_abortedCts = null;
|
||||
|
||||
// If _requestAborted is set, the connection has already been closed.
|
||||
if (!_requestAborted)
|
||||
{
|
||||
// Inform client no more data will ever arrive
|
||||
ConnectionControl.End(ProduceEndType.SocketShutdownSend);
|
||||
|
||||
// Wait for client to either disconnect or send unexpected data
|
||||
await SocketInput;
|
||||
|
||||
// Dispose socket
|
||||
ConnectionControl.End(ProduceEndType.SocketDisconnect);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogWarning("Connection shutdown abnormally", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
public abstract Task RequestProcessingAsync();
|
||||
|
||||
public void OnStarting(Func<object, Task> callback, object state)
|
||||
{
|
||||
|
|
@ -444,7 +332,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
}
|
||||
}
|
||||
|
||||
private async Task FireOnStarting()
|
||||
protected async Task FireOnStarting()
|
||||
{
|
||||
List<KeyValuePair<Func<object, Task>, object>> onStarting = null;
|
||||
lock (_onStartingSync)
|
||||
|
|
@ -468,7 +356,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
}
|
||||
}
|
||||
|
||||
private async Task FireOnCompleted()
|
||||
protected async Task FireOnCompleted()
|
||||
{
|
||||
List<KeyValuePair<Func<object, Task>, object>> onCompleted = null;
|
||||
lock (_onCompletedSync)
|
||||
|
|
@ -633,7 +521,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
return CreateResponseHeader(statusBytes, appCompleted, immediate);
|
||||
}
|
||||
|
||||
private async Task ProduceEnd()
|
||||
protected async Task ProduceEnd()
|
||||
{
|
||||
if (_applicationException != null)
|
||||
{
|
||||
|
|
@ -740,7 +628,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
}
|
||||
}
|
||||
|
||||
private bool TakeStartLine(SocketInput input)
|
||||
protected bool TakeStartLine(SocketInput input)
|
||||
{
|
||||
var scan = input.ConsumingStart();
|
||||
var consumed = scan;
|
||||
|
|
@ -977,9 +865,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
statusCode != 304;
|
||||
}
|
||||
|
||||
private void ReportApplicationError(Exception ex)
|
||||
protected void ReportApplicationError(Exception ex)
|
||||
{
|
||||
_applicationException = ex;
|
||||
if (_applicationException == null)
|
||||
{
|
||||
_applicationException = ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
_applicationException = new AggregateException(_applicationException, ex);
|
||||
}
|
||||
Log.ApplicationError(ex);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
// 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.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Hosting.Server;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||
{
|
||||
public class Frame<TContext> : Frame
|
||||
{
|
||||
private readonly IHttpApplication<TContext> _application;
|
||||
|
||||
public Frame(IHttpApplication<TContext> application,
|
||||
ConnectionContext context)
|
||||
: this(application, context, remoteEndPoint: null, localEndPoint: null, prepareRequest: null)
|
||||
{
|
||||
}
|
||||
|
||||
public Frame(IHttpApplication<TContext> application,
|
||||
ConnectionContext context,
|
||||
IPEndPoint remoteEndPoint,
|
||||
IPEndPoint localEndPoint,
|
||||
Action<IFeatureCollection> prepareRequest)
|
||||
: base(context, remoteEndPoint, localEndPoint, prepareRequest)
|
||||
{
|
||||
_application = application;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary loop which consumes socket input, parses it for protocol framing, and invokes the
|
||||
/// application delegate for as long as the socket is intended to remain open.
|
||||
/// The resulting Task from this loop is preserved in a field which is used when the server needs
|
||||
/// to drain and close all currently active connections.
|
||||
/// </summary>
|
||||
public override async Task RequestProcessingAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var terminated = false;
|
||||
while (!terminated && !_requestProcessingStopping)
|
||||
{
|
||||
while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput))
|
||||
{
|
||||
terminated = SocketInput.RemoteIntakeFin;
|
||||
if (!terminated)
|
||||
{
|
||||
await SocketInput;
|
||||
}
|
||||
}
|
||||
|
||||
while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders))
|
||||
{
|
||||
terminated = SocketInput.RemoteIntakeFin;
|
||||
if (!terminated)
|
||||
{
|
||||
await SocketInput;
|
||||
}
|
||||
}
|
||||
|
||||
if (!terminated && !_requestProcessingStopping)
|
||||
{
|
||||
var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this);
|
||||
_keepAlive = messageBody.RequestKeepAlive;
|
||||
_requestBody = new FrameRequestStream(messageBody);
|
||||
RequestBody = _requestBody;
|
||||
_responseBody = new FrameResponseStream(this);
|
||||
ResponseBody = _responseBody;
|
||||
DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
|
||||
|
||||
_abortedCts = null;
|
||||
_manuallySetRequestAbortToken = null;
|
||||
|
||||
var context = _application.CreateContext(this);
|
||||
try
|
||||
{
|
||||
await _application.ProcessRequestAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReportApplicationError(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Trigger OnStarting if it hasn't been called yet and the app hasn't
|
||||
// already failed. If an OnStarting callback throws we can go through
|
||||
// our normal error handling in ProduceEnd.
|
||||
// https://github.com/aspnet/KestrelHttpServer/issues/43
|
||||
if (!_responseStarted && _applicationException == null)
|
||||
{
|
||||
await FireOnStarting();
|
||||
}
|
||||
|
||||
await FireOnCompleted();
|
||||
|
||||
_application.DisposeContext(context, _applicationException);
|
||||
|
||||
// If _requestAbort is set, the connection has already been closed.
|
||||
if (!_requestAborted)
|
||||
{
|
||||
await ProduceEnd();
|
||||
|
||||
if (_keepAlive)
|
||||
{
|
||||
// Finish reading the request body in case the app did not.
|
||||
await messageBody.Consume();
|
||||
}
|
||||
}
|
||||
|
||||
_requestBody.StopAcceptingReads();
|
||||
_responseBody.StopAcceptingWrites();
|
||||
}
|
||||
|
||||
terminated = !_keepAlive;
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogWarning("Connection processing ended abnormally", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
_abortedCts = null;
|
||||
|
||||
// If _requestAborted is set, the connection has already been closed.
|
||||
if (!_requestAborted)
|
||||
{
|
||||
// Inform client no more data will ever arrive
|
||||
ConnectionControl.End(ProduceEndType.SocketShutdownSend);
|
||||
|
||||
// Wait for client to either disconnect or send unexpected data
|
||||
await SocketInput;
|
||||
|
||||
// Dispose socket
|
||||
ConnectionControl.End(ProduceEndType.SocketDisconnect);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogWarning("Connection shutdown abnormally", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,12 +23,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
public Task StartAsync(
|
||||
ServerAddress address,
|
||||
KestrelThread thread,
|
||||
RequestDelegate application)
|
||||
KestrelThread thread)
|
||||
{
|
||||
ServerAddress = address;
|
||||
Thread = thread;
|
||||
Application = application;
|
||||
|
||||
var tcs = new TaskCompletionSource<int>(this);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
{
|
||||
ServerAddress = listenerContext.ServerAddress;
|
||||
Thread = listenerContext.Thread;
|
||||
Application = listenerContext.Application;
|
||||
Memory2 = listenerContext.Memory2;
|
||||
Log = listenerContext.Log;
|
||||
}
|
||||
|
|
@ -33,8 +32,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
public KestrelThread Thread { get; set; }
|
||||
|
||||
public RequestDelegate Application { get; set; }
|
||||
|
||||
public MemoryPool2 Memory2 { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
||||
using Microsoft.AspNet.Server.Kestrel.Networking;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -34,12 +33,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
public async Task StartAsync(
|
||||
string pipeName,
|
||||
ServerAddress address,
|
||||
KestrelThread thread,
|
||||
RequestDelegate application)
|
||||
KestrelThread thread)
|
||||
{
|
||||
_pipeName = pipeName;
|
||||
|
||||
await StartAsync(address, thread, application).ConfigureAwait(false);
|
||||
await StartAsync(address, thread).ConfigureAwait(false);
|
||||
|
||||
await Thread.PostAsync(_this => _this.PostCallback(),
|
||||
this).ConfigureAwait(false);
|
||||
|
|
|
|||
|
|
@ -32,15 +32,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
public Task StartAsync(
|
||||
string pipeName,
|
||||
ServerAddress address,
|
||||
KestrelThread thread,
|
||||
RequestDelegate application)
|
||||
KestrelThread thread)
|
||||
{
|
||||
_pipeName = pipeName;
|
||||
_buf = thread.Loop.Libuv.buf_init(_ptr, 4);
|
||||
|
||||
ServerAddress = address;
|
||||
Thread = thread;
|
||||
Application = application;
|
||||
|
||||
DispatchPipe = new UvPipeHandle(Log);
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
Threads.Clear();
|
||||
}
|
||||
|
||||
public IDisposable CreateServer(ServerAddress address, RequestDelegate application)
|
||||
public IDisposable CreateServer(ServerAddress address)
|
||||
{
|
||||
var listeners = new List<IDisposable>();
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
(Listener) new PipeListener(this) :
|
||||
new TcpListener(this);
|
||||
listeners.Add(listener);
|
||||
listener.StartAsync(address, thread, application).Wait();
|
||||
listener.StartAsync(address, thread).Wait();
|
||||
}
|
||||
else if (first)
|
||||
{
|
||||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
: new TcpListenerPrimary(this);
|
||||
|
||||
listeners.Add(listener);
|
||||
listener.StartAsync(pipeName, address, thread, application).Wait();
|
||||
listener.StartAsync(pipeName, address, thread).Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
? (ListenerSecondary) new PipeListenerSecondary(this)
|
||||
: new TcpListenerSecondary(this);
|
||||
listeners.Add(listener);
|
||||
listener.StartAsync(pipeName, address, thread, application).Wait();
|
||||
listener.StartAsync(pipeName, address, thread).Wait();
|
||||
}
|
||||
|
||||
first = false;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Hosting;
|
||||
using Microsoft.AspNet.Hosting.Server;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -17,9 +16,8 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
private Stack<IDisposable> _disposables;
|
||||
private readonly IApplicationLifetime _applicationLifetime;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IHttpContextFactory _httpContextFactory;
|
||||
|
||||
public KestrelServer(IFeatureCollection features, IApplicationLifetime applicationLifetime, ILogger logger, IHttpContextFactory httpContextFactory)
|
||||
public KestrelServer(IFeatureCollection features, IApplicationLifetime applicationLifetime, ILogger logger)
|
||||
{
|
||||
if (features == null)
|
||||
{
|
||||
|
|
@ -36,20 +34,14 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
if (httpContextFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContextFactory));
|
||||
}
|
||||
|
||||
_applicationLifetime = applicationLifetime;
|
||||
_logger = logger;
|
||||
Features = features;
|
||||
_httpContextFactory = httpContextFactory;
|
||||
}
|
||||
|
||||
public IFeatureCollection Features { get; }
|
||||
|
||||
public void Start(RequestDelegate requestDelegate)
|
||||
public void Start<TContext>(IHttpApplication<TContext> application)
|
||||
{
|
||||
if (_disposables != null)
|
||||
{
|
||||
|
|
@ -64,9 +56,12 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
var dateHeaderValueManager = new DateHeaderValueManager();
|
||||
var engine = new KestrelEngine(new ServiceContext
|
||||
{
|
||||
FrameFactory = (context, remoteEP, localEP, prepareRequest) =>
|
||||
{
|
||||
return new Frame<TContext>(application, context, remoteEP, localEP, prepareRequest);
|
||||
},
|
||||
AppLifetime = _applicationLifetime,
|
||||
Log = new KestrelTrace(_logger),
|
||||
HttpContextFactory = _httpContextFactory,
|
||||
DateHeaderValueManager = dateHeaderValueManager,
|
||||
ConnectionFilter = information.ConnectionFilter,
|
||||
NoDelay = information.NoDelay
|
||||
|
|
@ -119,8 +114,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
{
|
||||
atLeastOneListener = true;
|
||||
_disposables.Push(engine.CreateServer(
|
||||
parsedAddress,
|
||||
requestDelegate));
|
||||
parsedAddress));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using Microsoft.AspNet.Hosting;
|
||||
using Microsoft.AspNet.Hosting.Server;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Server.Features;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
|
@ -18,13 +17,11 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
{
|
||||
private readonly IApplicationLifetime _appLifetime;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IHttpContextFactory _httpContextFactory;
|
||||
|
||||
public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory, IHttpContextFactory httpContextFactory)
|
||||
public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_appLifetime = appLifetime;
|
||||
_loggerFactory = loggerFactory;
|
||||
_httpContextFactory = httpContextFactory;
|
||||
}
|
||||
|
||||
public IServer CreateServer(IConfiguration configuration)
|
||||
|
|
@ -34,7 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
var serverFeatures = new FeatureCollection();
|
||||
serverFeatures.Set<IKestrelServerInformation>(information);
|
||||
serverFeatures.Set<IServerAddressesFeature>(information);
|
||||
return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNet.Server.Kestrel"), _httpContextFactory);
|
||||
return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNet.Server.Kestrel"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// 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.Net;
|
||||
using Microsoft.AspNet.Hosting;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Server.Kestrel.Filter;
|
||||
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
||||
|
|
@ -19,7 +21,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
{
|
||||
AppLifetime = context.AppLifetime;
|
||||
Log = context.Log;
|
||||
HttpContextFactory = context.HttpContextFactory;
|
||||
FrameFactory = context.FrameFactory;
|
||||
DateHeaderValueManager = context.DateHeaderValueManager;
|
||||
ConnectionFilter = context.ConnectionFilter;
|
||||
NoDelay = context.NoDelay;
|
||||
|
|
@ -29,7 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
|||
|
||||
public IKestrelTrace Log { get; set; }
|
||||
|
||||
public IHttpContextFactory HttpContextFactory { get; set; }
|
||||
public Func<ConnectionContext, IPEndPoint, IPEndPoint, Action<IFeatureCollection>, Frame> FrameFactory { get; set; }
|
||||
|
||||
public DateHeaderValueManager DateHeaderValueManager { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// 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.Hosting.Server;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNet.Server.KestrelTests
|
||||
{
|
||||
public 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 context, Exception exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task ProcessRequestAsync(HttpContext context)
|
||||
{
|
||||
await _requestDelegate(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ using Microsoft.AspNet.Http;
|
|||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Server.Kestrel;
|
||||
using Microsoft.AspNet.Server.Kestrel.Filter;
|
||||
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||
using Microsoft.AspNet.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
|
@ -91,12 +92,13 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
[ConditionalTheory]
|
||||
[MemberData(nameof(ConnectionFilterData))]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
||||
public void ListenerCanCreateAndDispose(ServiceContext testContext)
|
||||
public void ListenerCanCreateAndDispose(TestServiceContext testContext)
|
||||
{
|
||||
testContext.App = App;
|
||||
var engine = new KestrelEngine(testContext);
|
||||
engine.Start(1);
|
||||
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
||||
var started = engine.CreateServer(address, App);
|
||||
var started = engine.CreateServer(address);
|
||||
started.Dispose();
|
||||
engine.Dispose();
|
||||
}
|
||||
|
|
@ -104,12 +106,13 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
[ConditionalTheory]
|
||||
[MemberData(nameof(ConnectionFilterData))]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
||||
public void ConnectionCanReadAndWrite(ServiceContext testContext)
|
||||
public void ConnectionCanReadAndWrite(TestServiceContext testContext)
|
||||
{
|
||||
testContext.App = App;
|
||||
var engine = new KestrelEngine(testContext);
|
||||
engine.Start(1);
|
||||
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
||||
var started = engine.CreateServer(address, App);
|
||||
var started = engine.CreateServer(address);
|
||||
|
||||
Console.WriteLine("Started");
|
||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
||||
};
|
||||
var frame = new Frame(connectionContext);
|
||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||
frame.Scheme = "https";
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
||||
};
|
||||
var frame = new Frame(connectionContext);
|
||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||
IDictionary<string, StringValues> headers = frame.ResponseHeaders;
|
||||
|
||||
Assert.Equal(2, headers.Count);
|
||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
||||
};
|
||||
var frame = new Frame(connectionContext);
|
||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||
|
||||
Assert.True(frame.ResponseHeaders.Count > 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Server.Kestrel;
|
||||
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Server.KestrelTests
|
||||
{
|
||||
|
|
@ -32,11 +33,14 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
|
||||
public void Create(RequestDelegate app, ServiceContext context, string serverAddress)
|
||||
{
|
||||
context.FrameFactory = (connectionContext, remoteEP, localEP, prepareRequest) =>
|
||||
{
|
||||
return new Frame<HttpContext>(new DummyApplication(app), connectionContext, remoteEP, localEP, prepareRequest);
|
||||
};
|
||||
_engine = new KestrelEngine(context);
|
||||
_engine.Start(1);
|
||||
_server = _engine.CreateServer(
|
||||
ServerAddress.FromUrl(serverAddress),
|
||||
app);
|
||||
ServerAddress.FromUrl(serverAddress));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -1,19 +1,37 @@
|
|||
// 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 Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Server.Kestrel;
|
||||
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Server.KestrelTests
|
||||
{
|
||||
public class TestServiceContext : ServiceContext
|
||||
{
|
||||
private RequestDelegate _app;
|
||||
|
||||
public TestServiceContext()
|
||||
{
|
||||
AppLifetime = new LifetimeNotImplemented();
|
||||
Log = new TestKestrelTrace();
|
||||
HttpContextFactory = new HttpContextFactory(new HttpContextAccessor());
|
||||
DateHeaderValueManager = new TestDateHeaderValueManager();
|
||||
}
|
||||
|
||||
public RequestDelegate App
|
||||
{
|
||||
get
|
||||
{
|
||||
return _app;
|
||||
}
|
||||
set
|
||||
{
|
||||
_app = value;
|
||||
FrameFactory = (connectionContext, remoteEP, localEP, prepareRequest) =>
|
||||
{
|
||||
return new Frame<HttpContext>(new DummyApplication(_app), connectionContext, remoteEP, localEP, prepareRequest);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue