From 0293e53e11e3a3c09a95f106d7426ab405a81fdf Mon Sep 17 00:00:00 2001 From: David Fowler <davidfowl@gmail.com> Date: Fri, 30 Mar 2018 15:51:48 -0700 Subject: [PATCH] Handle uninitialized connections in disposal (#1786) (#1794) - We made a change to not initialize pipes up front on connection creation. That change make it null ref in disposal because we didn't check if the pipes were initialized. - Added a test - Also fixed the EchoConnectionHandler in the functional ts tests. --- .../FunctionalTests/EchoConnectionHandler.cs | 38 ++++++++----------- .../HttpConnectionContext.cs | 12 +++--- .../HttpConnectionManagerTests.cs | 14 +++++++ 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/clients/ts/FunctionalTests/EchoConnectionHandler.cs b/clients/ts/FunctionalTests/EchoConnectionHandler.cs index 43ceb72e164..646d328a5b6 100644 --- a/clients/ts/FunctionalTests/EchoConnectionHandler.cs +++ b/clients/ts/FunctionalTests/EchoConnectionHandler.cs @@ -11,35 +11,27 @@ namespace FunctionalTests { public async override Task OnConnectedAsync(ConnectionContext connection) { - var result = await connection.Transport.Input.ReadAsync(); - var buffer = result.Buffer; - - try - { - if (!buffer.IsEmpty) - { - await connection.Transport.Output.WriteAsync(buffer.ToArray()); - } - } - finally + while (true) { - connection.Transport.Input.AdvanceTo(result.Buffer.End); - } + var result = await connection.Transport.Input.ReadAsync(); + var buffer = result.Buffer; - // Wait for the user to close - var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); - connection.Transport.Input.OnWriterCompleted((ex, state) => - { - if (ex != null) + try { - ((TaskCompletionSource<object>)state).TrySetException(ex); + if (!buffer.IsEmpty) + { + await connection.Transport.Output.WriteAsync(buffer.ToArray()); + } + else if (result.IsCompleted) + { + break; + } } - else + finally { - ((TaskCompletionSource<object>)state).TrySetResult(null); + connection.Transport.Input.AdvanceTo(result.Buffer.End); } - }, tcs); - await tcs.Task; + } } } } diff --git a/src/Microsoft.AspNetCore.Http.Connections/HttpConnectionContext.cs b/src/Microsoft.AspNetCore.Http.Connections/HttpConnectionContext.cs index c406dae6f79..bcbd63bf1ce 100644 --- a/src/Microsoft.AspNetCore.Http.Connections/HttpConnectionContext.cs +++ b/src/Microsoft.AspNetCore.Http.Connections/HttpConnectionContext.cs @@ -143,21 +143,21 @@ namespace Microsoft.AspNetCore.Http.Connections // If the application task is faulted, propagate the error to the transport if (ApplicationTask?.IsFaulted == true) { - Transport.Output.Complete(ApplicationTask.Exception.InnerException); + Transport?.Output.Complete(ApplicationTask.Exception.InnerException); } else { - Transport.Output.Complete(); + Transport?.Output.Complete(); } // If the transport task is faulted, propagate the error to the application if (TransportTask?.IsFaulted == true) { - Application.Output.Complete(TransportTask.Exception.InnerException); + Application?.Output.Complete(TransportTask.Exception.InnerException); } else { - Application.Output.Complete(); + Application?.Output.Complete(); } var applicationTask = ApplicationTask ?? Task.CompletedTask; @@ -180,8 +180,8 @@ namespace Microsoft.AspNetCore.Http.Connections // REVIEW: Should we move this to the read loops? // Complete the reading side of the pipes - Application.Input.Complete(); - Transport.Input.Complete(); + Application?.Input.Complete(); + Transport?.Input.Complete(); } } diff --git a/test/Microsoft.AspNetCore.Http.Connections.Tests/HttpConnectionManagerTests.cs b/test/Microsoft.AspNetCore.Http.Connections.Tests/HttpConnectionManagerTests.cs index 72ae778f848..33b6cdc0c31 100644 --- a/test/Microsoft.AspNetCore.Http.Connections.Tests/HttpConnectionManagerTests.cs +++ b/test/Microsoft.AspNetCore.Http.Connections.Tests/HttpConnectionManagerTests.cs @@ -190,6 +190,20 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests Assert.Equal(HttpConnectionContext.ConnectionStatus.Disposed, connection.Status); } + [Fact] + public async Task DisposeInactiveConnectionWithNoPipes() + { + var connectionManager = CreateConnectionManager(); + var connection = connectionManager.CreateConnection(); + + Assert.NotNull(connection.ConnectionId); + Assert.Null(connection.Transport); + Assert.Null(connection.Application); + + await connection.DisposeAsync(); + Assert.Equal(HttpConnectionContext.ConnectionStatus.Disposed, connection.Status); + } + [Fact] public void ScanAfterDisposeNoops() { -- GitLab