diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/FilteredStreamAdapter.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/FilteredStreamAdapter.cs index 7174f10171..519d4440af 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/FilteredStreamAdapter.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/FilteredStreamAdapter.cs @@ -48,10 +48,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Filter { if (copyAsyncTask.IsFaulted) { + SocketInput.AbortAwaiting(); _log.LogError(0, copyAsyncTask.Exception, "FilteredStreamAdapter.CopyToAsync"); } else if (copyAsyncTask.IsCanceled) { + SocketInput.AbortAwaiting(); _log.LogError("FilteredStreamAdapter.CopyToAsync canceled."); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs index 3d335971e5..76282b9239 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs @@ -123,6 +123,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } } + public void OnSocketClosed() + { + _rawSocketInput.Dispose(); + + // If a connection filter was applied there will be two SocketInputs. + // If a connection filter failed, SocketInput will be null. + if (SocketInput != null && SocketInput != _rawSocketInput) + { + SocketInput.Dispose(); + } + } + private void ApplyConnectionFilter() { if (_filterContext.Connection != _libuvStream) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs index e00f0579d2..6b99b9898f 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs @@ -334,7 +334,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http try { ConnectionControl.End(ProduceEndType.SocketDisconnect); - SocketInput.AbortAwaiting(); } catch (Exception ex) { diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs index affb4d5867..68fd28bfeb 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs @@ -145,14 +145,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http // If _requestAborted is set, the connection has already been closed. if (Volatile.Read(ref _requestAborted) == 0) { - // Inform client no more data will ever arrive - ConnectionControl.End(ProduceEndType.SocketShutdownSend); + try + { + // 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); + // Wait for client to either disconnect or send unexpected data + await SocketInput; + } + finally + { + // Ensure we *always* disconnect the socket. + // Dispose socket + ConnectionControl.End(ProduceEndType.SocketDisconnect); + } } } catch (Exception ex) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketInput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketInput.cs index 4191f6736d..2a3ac3d698 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketInput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketInput.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; namespace Microsoft.AspNetCore.Server.Kestrel.Http { - public class SocketInput : ICriticalNotifyCompletion + public class SocketInput : ICriticalNotifyCompletion, IDisposable { private static readonly Action _awaitableIsCompleted = () => { }; private static readonly Action _awaitableIsNotCompleted = () => { }; @@ -246,5 +246,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http throw new IOException(error.Message, error); } } + + public void Dispose() + { + AbortAwaiting(); + + // Return all blocks + var block = _head; + while (block != null) + { + var returnBlock = block; + block = block.Next; + + returnBlock.Pool.Return(returnBlock); + } + + _head = null; + _tail = null; + } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs index bb39a268ae..406d0b756a 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/SocketOutput.cs @@ -614,17 +614,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { if (SocketDisconnect == false || Self._socket.IsClosed) { - CompleteOnUvThread(); + CompleteWithContextLock(); return; } Self._socket.Dispose(); + Self._connection.OnSocketClosed(); Self.ReturnAllBlocks(); Self._log.ConnectionStop(Self._connectionId); - CompleteOnUvThread(); + CompleteWithContextLock(); } - public void CompleteOnUvThread() + public void CompleteWithContextLock() { if (Monitor.TryEnter(Self._contextLock)) {