diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index d43a1ab4ce..c5e741043a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -9,6 +9,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { public class Connection : ConnectionContext, IConnectionControl { + private const int EOF = -4095; + private const int ECONNRESET = -4077; + private static readonly Action _readCallback = ReadCallback; private static readonly Func _allocCallback = AllocCallback; @@ -54,7 +57,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http SocketInput.Unpin(status); var normalRead = error == null && status > 0; - var normalDone = status == 0 || status == -4077 || status == -4095; + var normalDone = status == 0 || status == ECONNRESET || status == EOF; var errorDone = !(normalDone || normalRead); if (normalRead) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 78d4c6f198..fd584323df 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -94,7 +94,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (input.Buffer.Count == 0 && input.RemoteIntakeFin) { _mode = Mode.Terminated; - return; + break; } if (!TakeStartLine(input)) @@ -102,6 +102,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (input.RemoteIntakeFin) { _mode = Mode.Terminated; + break; } return; } @@ -113,7 +114,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (input.Buffer.Count == 0 && input.RemoteIntakeFin) { _mode = Mode.Terminated; - return; + break; } var endOfHeaders = false; @@ -124,11 +125,19 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (input.RemoteIntakeFin) { _mode = Mode.Terminated; + break; } return; } } + if (_mode == Mode.Terminated) + { + // If we broke out of the above while loop in the Terminated + // state, we don't want to transition to the MessageBody state. + break; + } + //var resumeBody = HandleExpectContinue(callback); _mode = Mode.MessageBody; Execute(); @@ -145,6 +154,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; case Mode.Terminated: + ConnectionControl.End(ProduceEndType.SocketShutdownSend); + ConnectionControl.End(ProduceEndType.SocketDisconnect); return; } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index 99fe1b4140..771471f632 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -468,5 +468,72 @@ namespace Microsoft.AspNet.Server.KestrelTests } } } + + [Fact] + public async Task ConnectionClosesWhenFinReceived() + { + using (var server = new TestServer(AppChunked)) + { + using (var connection = new TestConnection()) + { + await connection.SendEnd( + "GET / HTTP/1.1", + "", + "Post / HTTP/1.1", + "Content-Length: 7", + "", + "Goodbye"); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + "Content-Length: 0", + "", + "HTTP/1.1 200 OK", + "Content-Length: 7", + "", + "Goodbye"); + } + } + } + + [Fact] + public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes() + { + using (var server = new TestServer(AppChunked)) + { + using (var connection = new TestConnection()) + { + await connection.SendEnd( + "GET /"); + await connection.ReceiveEnd(); + } + + using (var connection = new TestConnection()) + { + await connection.SendEnd( + "GET / HTTP/1.1", + "", + "Post / HTTP/1.1"); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + "Content-Length: 0", + "", + ""); + } + + using (var connection = new TestConnection()) + { + await connection.SendEnd( + "GET / HTTP/1.1", + "", + "Post / HTTP/1.1", + "Content-Length: 7"); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + "Content-Length: 0", + "", + ""); + } + } + } } }