Address PR feedback

This commit is contained in:
Stephen Halter 2015-10-15 16:52:37 -07:00
parent 7b315d2470
commit 8e818e3549
2 changed files with 53 additions and 29 deletions

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private bool _responseStarted;
private bool _keepAlive;
private bool _autoChunk;
private bool _applicationFailed;
private Exception _applicationException;
public Frame(ConnectionContext context) : base(context)
{
@ -79,6 +79,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
_responseStarted = false;
_keepAlive = false;
_autoChunk = false;
_applicationException = null;
_requestHeaders.Reset();
ResetResponseHeaders();
@ -183,10 +184,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
}
finally
{
// Trigger FireOnStarting if ProduceStart hasn't been called yet.
// We call it here, so it can go through our normal error handling
// and respond with a 500 if an OnStarting callback throws.
if (!_responseStarted)
// 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();
}
@ -260,16 +262,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
}
if (onStarting != null)
{
foreach (var entry in onStarting)
try
{
try
foreach (var entry in onStarting)
{
await entry.Key.Invoke(entry.Value);
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
}
}
@ -419,9 +421,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
await FireOnStarting();
if (_applicationFailed)
if (_applicationException != null)
{
throw new ObjectDisposedException(typeof(Frame).FullName);
throw new ObjectDisposedException(
"The response has been aborted due to an unhandled application exception.",
_applicationException);
}
await ProduceStart(immediate, appCompleted: false);
@ -444,12 +448,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private async Task ProduceEnd()
{
if (_applicationFailed)
if (_applicationException != null)
{
if (_responseStarted)
{
// We can no longer respond with a 500, so we simply close the connection.
_keepAlive = false;
_requestProcessingStopping = true;
return;
}
else
@ -457,10 +461,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
StatusCode = 500;
ReasonPhrase = null;
// If OnStarting hasn't been triggered yet, we don't want to trigger it now that
// the app func has failed. https://github.com/aspnet/KestrelHttpServer/issues/43
_onStarting = null;
ResetResponseHeaders();
_responseHeaders.HeaderContentLength = "0";
}
@ -750,7 +750,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private void ReportApplicationError(Exception ex)
{
_applicationFailed = true;
_applicationException = ex;
Log.ApplicationError(ex);
}
}

View File

@ -550,7 +550,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
[MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingResultsIn500Response(ServiceContext testContext)
{
var onStartingCallCount = 0;
bool onStartingCalled = false;
var testLogger = new TestApplicationErrorLogger();
testContext.Log = new KestrelTrace(testLogger);
@ -560,7 +560,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
var response = frame.Get<IHttpResponseFeature>();
response.OnStarting(_ =>
{
onStartingCallCount++;
onStartingCalled = true;
return Task.FromResult<object>(null);
}, null);
@ -597,7 +597,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
"",
"");
Assert.Equal(2, onStartingCallCount);
Assert.False(onStartingCalled);
Assert.Equal(2, testLogger.ApplicationErrorsLogged);
}
}
@ -760,7 +760,8 @@ namespace Microsoft.AspNet.Server.KestrelTests
[MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext)
{
var onStartingCallCount = 0;
var onStartingCallCount1 = 0;
var onStartingCallCount2 = 0;
var failedWriteCount = 0;
var testLogger = new TestApplicationErrorLogger();
@ -768,19 +769,28 @@ namespace Microsoft.AspNet.Server.KestrelTests
using (var server = new TestServer(async frame =>
{
var onStartingException = new Exception();
var response = frame.Get<IHttpResponseFeature>();
response.OnStarting(_ =>
{
onStartingCallCount++;
throw new Exception();
onStartingCallCount1++;
throw onStartingException;
}, null);
response.OnStarting(_ =>
{
onStartingCallCount2++;
throw onStartingException;
}, null);
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" };
await Assert.ThrowsAsync<ObjectDisposedException>(async () =>
var writeException = await Assert.ThrowsAsync<ObjectDisposedException>(async () =>
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11));
Assert.Same(onStartingException, writeException.InnerException);
failedWriteCount++;
}, testContext))
{
@ -811,7 +821,9 @@ namespace Microsoft.AspNet.Server.KestrelTests
"",
"");
Assert.Equal(2, onStartingCallCount);
Assert.Equal(2, onStartingCallCount1);
// The second OnStarting callback should not be called since the first failed.
Assert.Equal(0, onStartingCallCount2);
Assert.Equal(2, testLogger.ApplicationErrorsLogged);
}
}
@ -820,6 +832,9 @@ namespace Microsoft.AspNet.Server.KestrelTests
[MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ServiceContext testContext)
{
var onCompletedCalled1 = false;
var onCompletedCalled2 = false;
var testLogger = new TestApplicationErrorLogger();
testContext.Log = new KestrelTrace(testLogger);
@ -828,6 +843,12 @@ namespace Microsoft.AspNet.Server.KestrelTests
var response = frame.Get<IHttpResponseFeature>();
response.OnCompleted(_ =>
{
onCompletedCalled1 = true;
throw new Exception();
}, null);
response.OnCompleted(_ =>
{
onCompletedCalled2 = true;
throw new Exception();
}, null);
@ -850,7 +871,10 @@ namespace Microsoft.AspNet.Server.KestrelTests
"Hello World");
}
Assert.Equal(1, testLogger.ApplicationErrorsLogged);
// All OnCompleted callbacks should be called even if they throw.
Assert.Equal(2, testLogger.ApplicationErrorsLogged);
Assert.True(onCompletedCalled1);
Assert.True(onCompletedCalled2);
}
}