From 79ea2bb9b3788bc51a85fd0dac4a4100cf16e9e5 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 27 Apr 2017 16:56:07 -0700 Subject: [PATCH] Quick fix for SslStream ODEs in HttpsAdaptedConnection.PrepareRequest (#1786) --- .../Internal/FrameConnection.cs | 13 +++++--- .../HttpsTests.cs | 33 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/FrameConnection.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/FrameConnection.cs index 99bf9346d9..81093896ac 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/FrameConnection.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/FrameConnection.cs @@ -164,7 +164,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal { Log.LogError(0, ex, $"Uncaught exception from the {nameof(IConnectionAdapter.OnConnectionAsync)} method of an {nameof(IConnectionAdapter)}."); _frameStartedTcs.SetResult(false); - CloseRawPipes(); + _ = CloseRawPipesAsync(); } } @@ -181,15 +181,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal } finally { - CloseRawPipes(); + // Don't allow _adaptedPipelineTask to be held up by StopAsync() or else StopAsync() will never complete. + _ = CloseRawPipesAsync(); } } - private void CloseRawPipes() + private async Task CloseRawPipesAsync() { - _filteredStream?.Dispose(); _context.OutputProducer.Dispose(); _context.Input.Reader.Complete(); + + // Wait until request processing is complete before disposing the Stream. + await StopAsync(); + + _filteredStream?.Dispose(); } private void StartFrame() diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsTests.cs index 391441ed5b..7890d6316c 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsTests.cs @@ -196,6 +196,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); } + // Regression test for https://github.com/aspnet/KestrelHttpServer/issues/1693 + [Fact] + public async Task DoesNotThrowObjectDisposedExceptionOnEmptyConnection() + { + var loggerFactory = new HandshakeErrorLoggerFactory(); + var hostBuilder = new WebHostBuilder() + .UseKestrel(options => + { + options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => + { + listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword"); + }); + }) + .UseLoggerFactory(loggerFactory) + .Configure(app => app.Run(httpContext => Task.CompletedTask)); + + using (var host = hostBuilder.Build()) + { + host.Start(); + + using (var socket = await HttpClientSlim.GetSocket(new Uri($"https://127.0.0.1:{host.GetPort()}/"))) + using (var stream = new NetworkStream(socket, ownsSocket: false)) + using (var sslStream = new SslStream(stream, true, (sender, certificate, chain, errors) => true)) + { + await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null, + enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, + checkCertificateRevocation: false); + } + } + + Assert.False(loggerFactory.ErrorLogger.ObjectDisposedExceptionLogged); + } + // Regression test for https://github.com/aspnet/KestrelHttpServer/pull/1197 [Fact] public void ConnectionFilterDoesNotLeakBlock()