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()
|
private Frame CreateFrame()
|
||||||
{
|
{
|
||||||
return new Frame(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest);
|
return FrameFactory(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IConnectionControl.Pause()
|
void IConnectionControl.Pause()
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel.Http
|
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 Encoding _ascii = Encoding.ASCII;
|
||||||
private static readonly ArraySegment<byte> _endChunkBytes = CreateAsciiByteArraySegment("\r\n");
|
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 _onStartingSync = new Object();
|
||||||
private readonly object _onCompletedSync = 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 readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
|
||||||
|
|
||||||
private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||||
|
|
@ -50,18 +50,18 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
private bool _requestProcessingStarted;
|
private bool _requestProcessingStarted;
|
||||||
private Task _requestProcessingTask;
|
private Task _requestProcessingTask;
|
||||||
private volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||||
private volatile bool _requestAborted;
|
protected volatile bool _requestAborted;
|
||||||
private CancellationTokenSource _abortedCts;
|
protected CancellationTokenSource _abortedCts;
|
||||||
private CancellationToken? _manuallySetRequestAbortToken;
|
protected CancellationToken? _manuallySetRequestAbortToken;
|
||||||
|
|
||||||
private FrameRequestStream _requestBody;
|
internal FrameRequestStream _requestBody;
|
||||||
private FrameResponseStream _responseBody;
|
internal FrameResponseStream _responseBody;
|
||||||
|
|
||||||
private bool _responseStarted;
|
protected bool _responseStarted;
|
||||||
private bool _keepAlive;
|
protected bool _keepAlive;
|
||||||
private bool _autoChunk;
|
private bool _autoChunk;
|
||||||
private Exception _applicationException;
|
protected Exception _applicationException;
|
||||||
|
|
||||||
private HttpVersionType _httpVersion;
|
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
|
/// 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.
|
/// to drain and close all currently active connections.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task RequestProcessingAsync()
|
public abstract 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 void OnStarting(Func<object, Task> callback, object state)
|
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;
|
List<KeyValuePair<Func<object, Task>, object>> onStarting = null;
|
||||||
lock (_onStartingSync)
|
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;
|
List<KeyValuePair<Func<object, Task>, object>> onCompleted = null;
|
||||||
lock (_onCompletedSync)
|
lock (_onCompletedSync)
|
||||||
|
|
@ -633,7 +521,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return CreateResponseHeader(statusBytes, appCompleted, immediate);
|
return CreateResponseHeader(statusBytes, appCompleted, immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProduceEnd()
|
protected async Task ProduceEnd()
|
||||||
{
|
{
|
||||||
if (_applicationException != null)
|
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 scan = input.ConsumingStart();
|
||||||
var consumed = scan;
|
var consumed = scan;
|
||||||
|
|
@ -977,9 +865,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
statusCode != 304;
|
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);
|
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(
|
public Task StartAsync(
|
||||||
ServerAddress address,
|
ServerAddress address,
|
||||||
KestrelThread thread,
|
KestrelThread thread)
|
||||||
RequestDelegate application)
|
|
||||||
{
|
{
|
||||||
ServerAddress = address;
|
ServerAddress = address;
|
||||||
Thread = thread;
|
Thread = thread;
|
||||||
Application = application;
|
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<int>(this);
|
var tcs = new TaskCompletionSource<int>(this);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
ServerAddress = listenerContext.ServerAddress;
|
ServerAddress = listenerContext.ServerAddress;
|
||||||
Thread = listenerContext.Thread;
|
Thread = listenerContext.Thread;
|
||||||
Application = listenerContext.Application;
|
|
||||||
Memory2 = listenerContext.Memory2;
|
Memory2 = listenerContext.Memory2;
|
||||||
Log = listenerContext.Log;
|
Log = listenerContext.Log;
|
||||||
}
|
}
|
||||||
|
|
@ -33,8 +32,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public KestrelThread Thread { get; set; }
|
public KestrelThread Thread { get; set; }
|
||||||
|
|
||||||
public RequestDelegate Application { get; set; }
|
|
||||||
|
|
||||||
public MemoryPool2 Memory2 { get; set; }
|
public MemoryPool2 Memory2 { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Networking;
|
using Microsoft.AspNet.Server.Kestrel.Networking;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -34,12 +33,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
public async Task StartAsync(
|
public async Task StartAsync(
|
||||||
string pipeName,
|
string pipeName,
|
||||||
ServerAddress address,
|
ServerAddress address,
|
||||||
KestrelThread thread,
|
KestrelThread thread)
|
||||||
RequestDelegate application)
|
|
||||||
{
|
{
|
||||||
_pipeName = pipeName;
|
_pipeName = pipeName;
|
||||||
|
|
||||||
await StartAsync(address, thread, application).ConfigureAwait(false);
|
await StartAsync(address, thread).ConfigureAwait(false);
|
||||||
|
|
||||||
await Thread.PostAsync(_this => _this.PostCallback(),
|
await Thread.PostAsync(_this => _this.PostCallback(),
|
||||||
this).ConfigureAwait(false);
|
this).ConfigureAwait(false);
|
||||||
|
|
|
||||||
|
|
@ -32,15 +32,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
public Task StartAsync(
|
public Task StartAsync(
|
||||||
string pipeName,
|
string pipeName,
|
||||||
ServerAddress address,
|
ServerAddress address,
|
||||||
KestrelThread thread,
|
KestrelThread thread)
|
||||||
RequestDelegate application)
|
|
||||||
{
|
{
|
||||||
_pipeName = pipeName;
|
_pipeName = pipeName;
|
||||||
_buf = thread.Loop.Libuv.buf_init(_ptr, 4);
|
_buf = thread.Loop.Libuv.buf_init(_ptr, 4);
|
||||||
|
|
||||||
ServerAddress = address;
|
ServerAddress = address;
|
||||||
Thread = thread;
|
Thread = thread;
|
||||||
Application = application;
|
|
||||||
|
|
||||||
DispatchPipe = new UvPipeHandle(Log);
|
DispatchPipe = new UvPipeHandle(Log);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
Threads.Clear();
|
Threads.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable CreateServer(ServerAddress address, RequestDelegate application)
|
public IDisposable CreateServer(ServerAddress address)
|
||||||
{
|
{
|
||||||
var listeners = new List<IDisposable>();
|
var listeners = new List<IDisposable>();
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
(Listener) new PipeListener(this) :
|
(Listener) new PipeListener(this) :
|
||||||
new TcpListener(this);
|
new TcpListener(this);
|
||||||
listeners.Add(listener);
|
listeners.Add(listener);
|
||||||
listener.StartAsync(address, thread, application).Wait();
|
listener.StartAsync(address, thread).Wait();
|
||||||
}
|
}
|
||||||
else if (first)
|
else if (first)
|
||||||
{
|
{
|
||||||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
: new TcpListenerPrimary(this);
|
: new TcpListenerPrimary(this);
|
||||||
|
|
||||||
listeners.Add(listener);
|
listeners.Add(listener);
|
||||||
listener.StartAsync(pipeName, address, thread, application).Wait();
|
listener.StartAsync(pipeName, address, thread).Wait();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
? (ListenerSecondary) new PipeListenerSecondary(this)
|
? (ListenerSecondary) new PipeListenerSecondary(this)
|
||||||
: new TcpListenerSecondary(this);
|
: new TcpListenerSecondary(this);
|
||||||
listeners.Add(listener);
|
listeners.Add(listener);
|
||||||
listener.StartAsync(pipeName, address, thread, application).Wait();
|
listener.StartAsync(pipeName, address, thread).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNet.Hosting;
|
using Microsoft.AspNet.Hosting;
|
||||||
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.Kestrel.Http;
|
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -17,9 +16,8 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
private Stack<IDisposable> _disposables;
|
private Stack<IDisposable> _disposables;
|
||||||
private readonly IApplicationLifetime _applicationLifetime;
|
private readonly IApplicationLifetime _applicationLifetime;
|
||||||
private readonly ILogger _logger;
|
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)
|
if (features == null)
|
||||||
{
|
{
|
||||||
|
|
@ -36,20 +34,14 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
throw new ArgumentNullException(nameof(logger));
|
throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (httpContextFactory == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(httpContextFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
_applicationLifetime = applicationLifetime;
|
_applicationLifetime = applicationLifetime;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
Features = features;
|
Features = features;
|
||||||
_httpContextFactory = httpContextFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFeatureCollection Features { get; }
|
public IFeatureCollection Features { get; }
|
||||||
|
|
||||||
public void Start(RequestDelegate requestDelegate)
|
public void Start<TContext>(IHttpApplication<TContext> application)
|
||||||
{
|
{
|
||||||
if (_disposables != null)
|
if (_disposables != null)
|
||||||
{
|
{
|
||||||
|
|
@ -64,9 +56,12 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
var dateHeaderValueManager = new DateHeaderValueManager();
|
var dateHeaderValueManager = new DateHeaderValueManager();
|
||||||
var engine = new KestrelEngine(new ServiceContext
|
var engine = new KestrelEngine(new ServiceContext
|
||||||
{
|
{
|
||||||
|
FrameFactory = (context, remoteEP, localEP, prepareRequest) =>
|
||||||
|
{
|
||||||
|
return new Frame<TContext>(application, context, remoteEP, localEP, prepareRequest);
|
||||||
|
},
|
||||||
AppLifetime = _applicationLifetime,
|
AppLifetime = _applicationLifetime,
|
||||||
Log = new KestrelTrace(_logger),
|
Log = new KestrelTrace(_logger),
|
||||||
HttpContextFactory = _httpContextFactory,
|
|
||||||
DateHeaderValueManager = dateHeaderValueManager,
|
DateHeaderValueManager = dateHeaderValueManager,
|
||||||
ConnectionFilter = information.ConnectionFilter,
|
ConnectionFilter = information.ConnectionFilter,
|
||||||
NoDelay = information.NoDelay
|
NoDelay = information.NoDelay
|
||||||
|
|
@ -119,8 +114,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
{
|
{
|
||||||
atLeastOneListener = true;
|
atLeastOneListener = true;
|
||||||
_disposables.Push(engine.CreateServer(
|
_disposables.Push(engine.CreateServer(
|
||||||
parsedAddress,
|
parsedAddress));
|
||||||
requestDelegate));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using Microsoft.AspNet.Hosting;
|
using Microsoft.AspNet.Hosting;
|
||||||
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;
|
||||||
|
|
@ -18,13 +17,11 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
{
|
{
|
||||||
private readonly IApplicationLifetime _appLifetime;
|
private readonly IApplicationLifetime _appLifetime;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly IHttpContextFactory _httpContextFactory;
|
|
||||||
|
|
||||||
public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory, IHttpContextFactory httpContextFactory)
|
public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_appLifetime = appLifetime;
|
_appLifetime = appLifetime;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_httpContextFactory = httpContextFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IServer CreateServer(IConfiguration configuration)
|
public IServer CreateServer(IConfiguration configuration)
|
||||||
|
|
@ -34,7 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
var serverFeatures = new FeatureCollection();
|
var serverFeatures = new FeatureCollection();
|
||||||
serverFeatures.Set<IKestrelServerInformation>(information);
|
serverFeatures.Set<IKestrelServerInformation>(information);
|
||||||
serverFeatures.Set<IServerAddressesFeature>(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.
|
// 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.Net;
|
||||||
using Microsoft.AspNet.Hosting;
|
using Microsoft.AspNet.Hosting;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http.Features;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Filter;
|
using Microsoft.AspNet.Server.Kestrel.Filter;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Http;
|
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
||||||
|
|
@ -19,7 +21,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
{
|
{
|
||||||
AppLifetime = context.AppLifetime;
|
AppLifetime = context.AppLifetime;
|
||||||
Log = context.Log;
|
Log = context.Log;
|
||||||
HttpContextFactory = context.HttpContextFactory;
|
FrameFactory = context.FrameFactory;
|
||||||
DateHeaderValueManager = context.DateHeaderValueManager;
|
DateHeaderValueManager = context.DateHeaderValueManager;
|
||||||
ConnectionFilter = context.ConnectionFilter;
|
ConnectionFilter = context.ConnectionFilter;
|
||||||
NoDelay = context.NoDelay;
|
NoDelay = context.NoDelay;
|
||||||
|
|
@ -29,7 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
|
|
||||||
public IKestrelTrace Log { get; set; }
|
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; }
|
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.Http.Features;
|
||||||
using Microsoft.AspNet.Server.Kestrel;
|
using Microsoft.AspNet.Server.Kestrel;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Filter;
|
using Microsoft.AspNet.Server.Kestrel.Filter;
|
||||||
|
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||||
using Microsoft.AspNet.Testing.xunit;
|
using Microsoft.AspNet.Testing.xunit;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -91,12 +92,13 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
[ConditionalTheory]
|
[ConditionalTheory]
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
[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);
|
var engine = new KestrelEngine(testContext);
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
||||||
var started = engine.CreateServer(address, App);
|
var started = engine.CreateServer(address);
|
||||||
started.Dispose();
|
started.Dispose();
|
||||||
engine.Dispose();
|
engine.Dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -104,12 +106,13 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
[ConditionalTheory]
|
[ConditionalTheory]
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
[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);
|
var engine = new KestrelEngine(testContext);
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
||||||
var started = engine.CreateServer(address, App);
|
var started = engine.CreateServer(address);
|
||||||
|
|
||||||
Console.WriteLine("Started");
|
Console.WriteLine("Started");
|
||||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
||||||
};
|
};
|
||||||
var frame = new Frame(connectionContext);
|
var frame = new Frame<object>(application: null, context: connectionContext);
|
||||||
frame.Scheme = "https";
|
frame.Scheme = "https";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
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;
|
IDictionary<string, StringValues> headers = frame.ResponseHeaders;
|
||||||
|
|
||||||
Assert.Equal(2, headers.Count);
|
Assert.Equal(2, headers.Count);
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
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);
|
Assert.True(frame.ResponseHeaders.Count > 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Server.Kestrel;
|
using Microsoft.AspNet.Server.Kestrel;
|
||||||
|
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.KestrelTests
|
namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
|
|
@ -32,11 +33,14 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
|
||||||
public void Create(RequestDelegate app, ServiceContext context, string serverAddress)
|
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 = new KestrelEngine(context);
|
||||||
_engine.Start(1);
|
_engine.Start(1);
|
||||||
_server = _engine.CreateServer(
|
_server = _engine.CreateServer(
|
||||||
ServerAddress.FromUrl(serverAddress),
|
ServerAddress.FromUrl(serverAddress));
|
||||||
app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,37 @@
|
||||||
// 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 Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Server.Kestrel;
|
using Microsoft.AspNet.Server.Kestrel;
|
||||||
|
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.KestrelTests
|
namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
public class TestServiceContext : ServiceContext
|
public class TestServiceContext : ServiceContext
|
||||||
{
|
{
|
||||||
|
private RequestDelegate _app;
|
||||||
|
|
||||||
public TestServiceContext()
|
public TestServiceContext()
|
||||||
{
|
{
|
||||||
AppLifetime = new LifetimeNotImplemented();
|
AppLifetime = new LifetimeNotImplemented();
|
||||||
Log = new TestKestrelTrace();
|
Log = new TestKestrelTrace();
|
||||||
HttpContextFactory = new HttpContextFactory(new HttpContextAccessor());
|
|
||||||
DateHeaderValueManager = new TestDateHeaderValueManager();
|
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