Flatten exception handling (#2313)
3 nested try blocks with 3 finallies in same function O_o
This commit is contained in:
parent
c57784447e
commit
6252ffd86a
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
private CancellationToken? _manuallySetRequestAbortToken;
|
private CancellationToken? _manuallySetRequestAbortToken;
|
||||||
|
|
||||||
protected RequestProcessingStatus _requestProcessingStatus;
|
protected RequestProcessingStatus _requestProcessingStatus;
|
||||||
protected volatile bool _keepAlive = true; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
protected volatile bool _keepAlive; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
|
||||||
protected bool _upgradeAvailable;
|
protected bool _upgradeAvailable;
|
||||||
private bool _canHaveBody;
|
private bool _canHaveBody;
|
||||||
private bool _autoChunk;
|
private bool _autoChunk;
|
||||||
|
|
@ -440,139 +440,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (_keepAlive)
|
await ProcessRequests(application);
|
||||||
{
|
|
||||||
BeginRequestProcessing();
|
|
||||||
|
|
||||||
var result = default(ReadResult);
|
|
||||||
var endConnection = false;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (BeginRead(out var awaitable))
|
|
||||||
{
|
|
||||||
result = await awaitable;
|
|
||||||
}
|
|
||||||
} while (!TryParseRequest(result, out endConnection));
|
|
||||||
|
|
||||||
if (endConnection)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageBody = CreateMessageBody();
|
|
||||||
|
|
||||||
if (!messageBody.RequestKeepAlive)
|
|
||||||
{
|
|
||||||
_keepAlive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_upgradeAvailable = messageBody.RequestUpgrade;
|
|
||||||
|
|
||||||
InitializeStreams(messageBody);
|
|
||||||
|
|
||||||
var httpContext = application.CreateContext(this);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
KestrelEventSource.Log.RequestStart(this);
|
|
||||||
|
|
||||||
await application.ProcessRequestAsync(httpContext);
|
|
||||||
|
|
||||||
if (_requestAborted == 0)
|
|
||||||
{
|
|
||||||
VerifyResponseContentLength();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ReportApplicationError(ex);
|
|
||||||
|
|
||||||
if (ex is BadHttpRequestException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
KestrelEventSource.Log.RequestStop(this);
|
|
||||||
|
|
||||||
// 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 (!HasResponseStarted && _applicationException == null && _onStarting != null)
|
|
||||||
{
|
|
||||||
await FireOnStarting();
|
|
||||||
}
|
|
||||||
|
|
||||||
PauseStreams();
|
|
||||||
|
|
||||||
if (_onCompleted != null)
|
|
||||||
{
|
|
||||||
await FireOnCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If _requestAbort is set, the connection has already been closed.
|
|
||||||
if (_requestAborted == 0)
|
|
||||||
{
|
|
||||||
// Call ProduceEnd() before consuming the rest of the request body to prevent
|
|
||||||
// delaying clients waiting for the chunk terminator:
|
|
||||||
//
|
|
||||||
// https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
|
|
||||||
//
|
|
||||||
// This also prevents the 100 Continue response from being sent if the app
|
|
||||||
// never tried to read the body.
|
|
||||||
// https://github.com/aspnet/KestrelHttpServer/issues/2102
|
|
||||||
//
|
|
||||||
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
|
|
||||||
// HttpContext.Response.StatusCode is correctly set when
|
|
||||||
// IHttpContextFactory.Dispose(HttpContext) is called.
|
|
||||||
await ProduceEnd();
|
|
||||||
|
|
||||||
// ForZeroContentLength does not complete the reader nor the writer
|
|
||||||
if (!messageBody.IsEmpty)
|
|
||||||
{
|
|
||||||
// Finish reading the request body in case the app did not.
|
|
||||||
await messageBody.ConsumeAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!HasResponseStarted)
|
|
||||||
{
|
|
||||||
// If the request was aborted and no response was sent, there's no
|
|
||||||
// meaningful status code to log.
|
|
||||||
StatusCode = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (BadHttpRequestException ex)
|
|
||||||
{
|
|
||||||
// Handle BadHttpRequestException thrown during app execution or remaining message body consumption.
|
|
||||||
// This has to be caught here so StatusCode is set properly before disposing the HttpContext
|
|
||||||
// (DisposeContext logs StatusCode).
|
|
||||||
SetBadRequestState(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
application.DisposeContext(httpContext, _applicationException);
|
|
||||||
|
|
||||||
// StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block
|
|
||||||
// to ensure InitializeStreams has been called.
|
|
||||||
StopStreams();
|
|
||||||
|
|
||||||
if (HasStartedConsumingRequestBody)
|
|
||||||
{
|
|
||||||
RequestBodyPipe.Reader.Complete();
|
|
||||||
|
|
||||||
// Wait for MessageBody.PumpAsync() to call RequestBodyPipe.Writer.Complete().
|
|
||||||
await messageBody.StopAsync();
|
|
||||||
|
|
||||||
// At this point both the request body pipe reader and writer should be completed.
|
|
||||||
RequestBodyPipe.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (BadHttpRequestException ex)
|
catch (BadHttpRequestException ex)
|
||||||
{
|
{
|
||||||
|
|
@ -615,6 +483,156 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application)
|
||||||
|
{
|
||||||
|
// Keep-alive is default for HTTP/1.1 and HTTP/2; parsing and errors will change its value
|
||||||
|
_keepAlive = true;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
BeginRequestProcessing();
|
||||||
|
|
||||||
|
var result = default(ReadResult);
|
||||||
|
var endConnection = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (BeginRead(out var awaitable))
|
||||||
|
{
|
||||||
|
result = await awaitable;
|
||||||
|
}
|
||||||
|
} while (!TryParseRequest(result, out endConnection));
|
||||||
|
|
||||||
|
if (endConnection)
|
||||||
|
{
|
||||||
|
// Connection finished, stop processing requests
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageBody = CreateMessageBody();
|
||||||
|
if (!messageBody.RequestKeepAlive)
|
||||||
|
{
|
||||||
|
_keepAlive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_upgradeAvailable = messageBody.RequestUpgrade;
|
||||||
|
|
||||||
|
InitializeStreams(messageBody);
|
||||||
|
|
||||||
|
var httpContext = application.CreateContext(this);
|
||||||
|
|
||||||
|
BadHttpRequestException badRequestException = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
KestrelEventSource.Log.RequestStart(this);
|
||||||
|
|
||||||
|
// Run the application code for this request
|
||||||
|
await application.ProcessRequestAsync(httpContext);
|
||||||
|
|
||||||
|
if (_requestAborted == 0)
|
||||||
|
{
|
||||||
|
VerifyResponseContentLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ReportApplicationError(ex);
|
||||||
|
// Capture BadHttpRequestException for further processing
|
||||||
|
badRequestException = ex as BadHttpRequestException;
|
||||||
|
}
|
||||||
|
|
||||||
|
KestrelEventSource.Log.RequestStop(this);
|
||||||
|
|
||||||
|
// 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 (!HasResponseStarted && _applicationException == null && _onStarting != null)
|
||||||
|
{
|
||||||
|
await FireOnStarting();
|
||||||
|
}
|
||||||
|
|
||||||
|
PauseStreams();
|
||||||
|
|
||||||
|
if (_onCompleted != null)
|
||||||
|
{
|
||||||
|
await FireOnCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badRequestException == null)
|
||||||
|
{
|
||||||
|
// If _requestAbort is set, the connection has already been closed.
|
||||||
|
if (_requestAborted == 0)
|
||||||
|
{
|
||||||
|
// Call ProduceEnd() before consuming the rest of the request body to prevent
|
||||||
|
// delaying clients waiting for the chunk terminator:
|
||||||
|
//
|
||||||
|
// https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
|
||||||
|
//
|
||||||
|
// This also prevents the 100 Continue response from being sent if the app
|
||||||
|
// never tried to read the body.
|
||||||
|
// https://github.com/aspnet/KestrelHttpServer/issues/2102
|
||||||
|
//
|
||||||
|
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
|
||||||
|
// HttpContext.Response.StatusCode is correctly set when
|
||||||
|
// IHttpContextFactory.Dispose(HttpContext) is called.
|
||||||
|
await ProduceEnd();
|
||||||
|
|
||||||
|
// ForZeroContentLength does not complete the reader nor the writer
|
||||||
|
if (!messageBody.IsEmpty)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Finish reading the request body in case the app did not.
|
||||||
|
await messageBody.ConsumeAsync();
|
||||||
|
}
|
||||||
|
catch (BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
// Capture BadHttpRequestException for further processing
|
||||||
|
badRequestException = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!HasResponseStarted)
|
||||||
|
{
|
||||||
|
// If the request was aborted and no response was sent, there's no
|
||||||
|
// meaningful status code to log.
|
||||||
|
StatusCode = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badRequestException != null)
|
||||||
|
{
|
||||||
|
// Handle BadHttpRequestException thrown during app execution or remaining message body consumption.
|
||||||
|
// This has to be caught here so StatusCode is set properly before disposing the HttpContext
|
||||||
|
// (DisposeContext logs StatusCode).
|
||||||
|
SetBadRequestState(badRequestException);
|
||||||
|
}
|
||||||
|
|
||||||
|
application.DisposeContext(httpContext, _applicationException);
|
||||||
|
|
||||||
|
// StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block
|
||||||
|
// to ensure InitializeStreams has been called.
|
||||||
|
StopStreams();
|
||||||
|
|
||||||
|
if (HasStartedConsumingRequestBody)
|
||||||
|
{
|
||||||
|
RequestBodyPipe.Reader.Complete();
|
||||||
|
|
||||||
|
// Wait for MessageBody.PumpAsync() to call RequestBodyPipe.Writer.Complete().
|
||||||
|
await messageBody.StopAsync();
|
||||||
|
|
||||||
|
// At this point both the request body pipe reader and writer should be completed.
|
||||||
|
RequestBodyPipe.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badRequestException != null)
|
||||||
|
{
|
||||||
|
// Bad request reported, stop processing requests
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (_keepAlive);
|
||||||
|
}
|
||||||
|
|
||||||
public void OnStarting(Func<object, Task> callback, object state)
|
public void OnStarting(Func<object, Task> callback, object state)
|
||||||
{
|
{
|
||||||
lock (_onStartingSync)
|
lock (_onStartingSync)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue