diff options
author | BrennanConroy <brecon@microsoft.com> | 2021-06-24 23:35:22 +0300 |
---|---|---|
committer | BrennanConroy <brecon@microsoft.com> | 2021-06-24 23:35:22 +0300 |
commit | 6b2c37cd024296e9f7d405a31f5216dc0dcc201c (patch) | |
tree | 09ceae4df1dfdc81a487dc13f7b85c00c43aa986 | |
parent | 7b28c2b572cf32cf3c874d637ca8337a3b459830 (diff) |
Surface more connection closure issues to OnDisconnectedAsyncbrecon/surfaceex
5 files changed, 50 insertions, 5 deletions
diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs index 9e3c61e060..c0302ec581 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs @@ -156,8 +156,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal HttpConnectionsEventSource.Log.ConnectionTimedOut(connection.ConnectionId); // This is most likely a long polling connection. The transport here ends because - // a poll completed and has been inactive for > 5 seconds so we wait for the + // a poll completed and has been inactive for > 15 seconds so we wait for the // application to finish gracefully + Debug.Assert(connection.TransportTask?.IsCompleted ?? true); + connection.TransportTask = Task.FromException(new TimeoutException("Connection timed out.")); _ = DisposeAndRemoveAsync(connection, closeGracefully: true); } else diff --git a/src/SignalR/common/Http.Connections/src/Internal/Transports/WebSocketsServerTransport.cs b/src/SignalR/common/Http.Connections/src/Internal/Transports/WebSocketsServerTransport.cs index ef0be3ae85..ad770d4996 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/Transports/WebSocketsServerTransport.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/Transports/WebSocketsServerTransport.cs @@ -180,6 +180,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal.Transports { // Client has closed the WebSocket connection without completing the close handshake Log.ClosedPrematurely(_logger, ex); + _application.Output.Complete(ex); } catch (OperationCanceledException) { diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs index 08ecfd8e39..776357a8b3 100644 --- a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs +++ b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs @@ -520,9 +520,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests return async connectionContext => { // Ensure both sides of the pipe are ok - var result = await connectionContext.Transport.Input.ReadAsync(); - Assert.True(result.IsCompleted); - await connectionContext.Transport.Output.WriteAsync(result.Buffer.First); + var ex = await Assert.ThrowsAsync<TimeoutException>(async () => await connectionContext.Transport.Input.ReadAsync()); + Assert.Equal("Connection timed out.", ex.Message); + await connectionContext.Transport.Output.WriteAsync(new ReadOnlyMemory<byte>()); }; }); diff --git a/src/SignalR/server/Core/src/HubConnectionContext.cs b/src/SignalR/server/Core/src/HubConnectionContext.cs index dcb55cab3d..916db19fb2 100644 --- a/src/SignalR/server/Core/src/HubConnectionContext.cs +++ b/src/SignalR/server/Core/src/HubConnectionContext.cs @@ -664,6 +664,7 @@ namespace Microsoft.AspNetCore.SignalR if (_receivedMessageElapsedTicks >= _clientTimeoutInterval) { Log.ClientTimeout(_logger, TimeSpan.FromTicks(_clientTimeoutInterval)); + CloseException = new TimeoutException("Client timeout elapsed without receiving a message from the client."); AbortAllowReconnect(); } } diff --git a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs index 63429b83dc..b849b5f27f 100644 --- a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs +++ b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs @@ -2788,7 +2788,10 @@ namespace Microsoft.AspNetCore.SignalR.Tests var clock = new MockSystemClock(); var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(services => services.Configure<HubOptions>(options => - options.ClientTimeoutInterval = TimeSpan.FromMilliseconds(timeout)), LoggerFactory); + { + options.ClientTimeoutInterval = TimeSpan.FromMilliseconds(timeout); + options.EnableDetailedErrors = true; + }), LoggerFactory); var connectionHandler = serviceProvider.GetService<HubConnectionHandler<MethodHub>>(); connectionHandler.SystemClock = clock; @@ -2801,12 +2804,50 @@ namespace Microsoft.AspNetCore.SignalR.Tests clock.UtcNow = clock.UtcNow.AddMilliseconds(timeout + 1); client.TickHeartbeat(); + var closeMessage = Assert.IsType<CloseMessage>(await client.ReadAsync().DefaultTimeout()); + Assert.True(closeMessage.AllowReconnect); + Assert.Equal("Connection closed with an error. TimeoutException: Client timeout elapsed without receiving a message from the client.", closeMessage.Error); await connectionHandlerTask.DefaultTimeout(); } } } [Fact] + public async Task TimeoutPassesExceptionToOnDisconnectedAsync() + { + using (StartVerifiableLog()) + { + var timeout = 100; + var clock = new MockSystemClock(); + var state = new ConnectionLifetimeState(); + var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(services => + { + services.Configure<HubOptions>(options => + { + options.ClientTimeoutInterval = TimeSpan.FromMilliseconds(timeout); + options.EnableDetailedErrors = true; + }); + services.AddSingleton(state); + }, LoggerFactory); + var connectionHandler = serviceProvider.GetService<HubConnectionHandler<ConnectionLifetimeHub>>(); + connectionHandler.SystemClock = clock; + + using (var client = new TestClient()) + { + var connectionHandlerTask = await client.ConnectAsync(connectionHandler); + await client.SendHubMessageAsync(PingMessage.Instance); + + clock.UtcNow = clock.UtcNow.AddMilliseconds(timeout + 1); + client.TickHeartbeat(); + + await connectionHandlerTask.DefaultTimeout(); + + Assert.IsType<TimeoutException>(state.DisconnectedException); + } + } + } + + [Fact] public async Task ReceivingMessagesPreventsConnectionTimeoutFromOccuring() { using (StartVerifiableLog()) |