Merge branch 'benaadams/statemachines' into dev

This commit is contained in:
Stephen Halter 2015-12-07 00:07:14 -08:00
commit 706ff04160
2 changed files with 84 additions and 22 deletions

View File

@ -44,9 +44,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
protected 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; protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
private List<KeyValuePair<Func<object, Task>, object>> _onCompleted; protected List<KeyValuePair<Func<object, Task>, object>> _onCompleted;
private bool _requestProcessingStarted; private bool _requestProcessingStarted;
private Task _requestProcessingTask; private Task _requestProcessingTask;
@ -144,8 +144,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{ {
// If a request abort token was previously explicitly set, return it. // If a request abort token was previously explicitly set, return it.
if (_manuallySetRequestAbortToken.HasValue) if (_manuallySetRequestAbortToken.HasValue)
{
return _manuallySetRequestAbortToken.Value; return _manuallySetRequestAbortToken.Value;
}
// Otherwise, get the abort CTS. If we have one, which would mean that someone previously // Otherwise, get the abort CTS. If we have one, which would mean that someone previously
// asked for the RequestAborted token, simply return its token. If we don't, // asked for the RequestAborted token, simply return its token. If we don't,
// check to see whether we've already aborted, in which case just return an // check to see whether we've already aborted, in which case just return an
@ -416,7 +417,28 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
} }
} }
public async Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken) public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{
if (!_responseStarted)
{
return WriteAsyncAwaited(data, cancellationToken);
}
if (_autoChunk)
{
if (data.Count == 0)
{
return TaskUtilities.CompletedTask;
}
return WriteChunkedAsync(data, cancellationToken);
}
else
{
return SocketOutput.WriteAsync(data, immediate: true, cancellationToken: cancellationToken);
}
}
public async Task WriteAsyncAwaited(ArraySegment<byte> data, CancellationToken cancellationToken)
{ {
await ProduceStartAndFireOnStarting(immediate: false); await ProduceStartAndFireOnStarting(immediate: false);
@ -501,10 +523,27 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
} }
} }
public async Task ProduceStartAndFireOnStarting(bool immediate = true) public Task ProduceStartAndFireOnStarting(bool immediate = true)
{ {
if (_responseStarted) return; if (_responseStarted) return TaskUtilities.CompletedTask;
if (_onStarting != null)
{
return FireOnStartingProduceStart(immediate: immediate);
}
if (_applicationException != null)
{
throw new ObjectDisposedException(
"The response has been aborted due to an unhandled application exception.",
_applicationException);
}
return ProduceStart(immediate, appCompleted: false);
}
private async Task FireOnStartingProduceStart(bool immediate)
{
await FireOnStarting(); await FireOnStarting();
if (_applicationException != null) if (_applicationException != null)
@ -527,7 +566,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
return CreateResponseHeader(statusBytes, appCompleted, immediate); return CreateResponseHeader(statusBytes, appCompleted, immediate);
} }
protected async Task ProduceEnd() protected Task ProduceEnd()
{ {
if (_applicationException != null) if (_applicationException != null)
{ {
@ -535,7 +574,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{ {
// We can no longer respond with a 500, so we simply close the connection. // We can no longer respond with a 500, so we simply close the connection.
_requestProcessingStopping = true; _requestProcessingStopping = true;
return; return TaskUtilities.CompletedTask;
} }
else else
{ {
@ -547,8 +586,26 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
} }
} }
if (!_responseStarted)
{
return ProduceEndAwaited();
}
WriteSuffix();
return TaskUtilities.CompletedTask;
}
private async Task ProduceEndAwaited()
{
await ProduceStart(immediate: true, appCompleted: true); await ProduceStart(immediate: true, appCompleted: true);
WriteSuffix();
}
private void WriteSuffix()
{
// _autoChunk should be checked after we are sure ProduceStart() has been called // _autoChunk should be checked after we are sure ProduceStart() has been called
// since ProduceStart() may set _autoChunk to true. // since ProduceStart() may set _autoChunk to true.
if (_autoChunk) if (_autoChunk)

View File

@ -40,28 +40,27 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{ {
try try
{ {
var terminated = false; while (!_requestProcessingStopping)
while (!terminated && !_requestProcessingStopping)
{ {
while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) while (!_requestProcessingStopping && !TakeStartLine(SocketInput))
{ {
terminated = SocketInput.RemoteIntakeFin; if (SocketInput.RemoteIntakeFin)
if (!terminated)
{ {
await SocketInput; return;
} }
await SocketInput;
} }
while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders))
{ {
terminated = SocketInput.RemoteIntakeFin; if (SocketInput.RemoteIntakeFin)
if (!terminated)
{ {
await SocketInput; return;
} }
await SocketInput;
} }
if (!terminated && !_requestProcessingStopping) if (!_requestProcessingStopping)
{ {
var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this); var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this);
_keepAlive = messageBody.RequestKeepAlive; _keepAlive = messageBody.RequestKeepAlive;
@ -89,12 +88,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
// already failed. If an OnStarting callback throws we can go through // already failed. If an OnStarting callback throws we can go through
// our normal error handling in ProduceEnd. // our normal error handling in ProduceEnd.
// https://github.com/aspnet/KestrelHttpServer/issues/43 // https://github.com/aspnet/KestrelHttpServer/issues/43
if (!_responseStarted && _applicationException == null) if (!_responseStarted && _applicationException == null && _onStarting != null)
{ {
await FireOnStarting(); await FireOnStarting();
} }
await FireOnCompleted(); if (_onCompleted != null)
{
await FireOnCompleted();
}
_application.DisposeContext(context, _applicationException); _application.DisposeContext(context, _applicationException);
@ -114,7 +116,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
_responseBody.StopAcceptingWrites(); _responseBody.StopAcceptingWrites();
} }
terminated = !_keepAlive; if (!_keepAlive)
{
return;
}
} }
Reset(); Reset();