diff --git a/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs b/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs index 7c34e025c6e..ad023ebbd08 100644 --- a/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs +++ b/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs @@ -234,8 +234,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal // Disposing the stream will dispose the sslDuplexPipe await using (sslStream) + await using (sslDuplexPipe) { await _next(context); + // Dispose the inner stream (SslDuplexPipe) before disposing the SslStream + // as the duplex pipe can hit an ODE as it still may be writing. } } finally diff --git a/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStreamAdapter.cs b/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStreamAdapter.cs index 0a0d09384c2..77f4bfd24e8 100644 --- a/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStreamAdapter.cs +++ b/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStreamAdapter.cs @@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal { private readonly Pipe _output; private Task _outputTask; + private bool _disposed; + private readonly object _disposeLock = new object(); public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, Func createStream) : this(duplexPipe, new StreamPipeReaderOptions(leaveOpen: true), new StreamPipeWriterOptions(leaveOpen: true), createStream) @@ -63,17 +65,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal } } - public override async ValueTask DisposeAsync() + public override ValueTask DisposeAsync() { + lock (_disposeLock) + { + if (_disposed) + { + return default; + } + _disposed = true; + } + Input.Complete(); _output.Writer.Complete(); - if (_outputTask != null) + if (_outputTask == null || _outputTask.IsCompletedSuccessfully) { // Wait for the output task to complete, this ensures that we've copied // the application data to the underlying stream - await _outputTask; + return default; } + + return new ValueTask(_outputTask); } private async Task WriteOutputAsync()