diff --git a/clients/ts/FunctionalTests/EchoConnectionHandler.cs b/clients/ts/FunctionalTests/EchoConnectionHandler.cs index 43ceb72e164f924e3ae6a91e61c89b91f5a7e5f0..646d328a5b6f9f67e3c9dee01a7c98a1add7b7cf 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 c406dae6f790389acbc29a9106c92721c31561fb..bcbd63bf1ce88a4c8f7a581513876a91bfb3bd76 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 72ae778f848a5c2c6649b311fa60a01a6d4677cf..33b6cdc0c31d1bc0b03cdf53eb773e69c2b78ba7 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() {