diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs index 8f6b72af56..cbdaae61f4 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Channels; @@ -640,7 +641,8 @@ namespace Microsoft.AspNetCore.SignalR.Client break; } } - else if (result.IsCompleted) + + if (result.IsCompleted) { // Not enough data, and we won't be getting any more data. throw new InvalidOperationException( @@ -717,8 +719,13 @@ namespace Microsoft.AspNetCore.SignalR.Client break; } } - else if (result.IsCompleted) + + if (result.IsCompleted) { + if (!buffer.IsEmpty) + { + throw new InvalidDataException("Connection terminated while reading a message."); + } break; } } diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs index d9a5d7b883..c19e24b4ff 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs @@ -387,12 +387,9 @@ namespace Microsoft.AspNetCore.SignalR await WriteHandshakeResponseAsync(HandshakeResponseMessage.Empty); return true; } - else - { - _logger.LogInformation("Didn't parse the handshake"); - } } - else if (result.IsCompleted) + + if (result.IsCompleted) { // connection was closed before we ever received a response // can't send a handshake response because there is no longer a connection diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionHandler.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionHandler.cs index 511ee6f204..f159484887 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionHandler.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionHandler.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; @@ -198,8 +199,13 @@ namespace Microsoft.AspNetCore.SignalR await _dispatcher.DispatchMessageAsync(connection, message); } } - else if (result.IsCompleted) + + if (result.IsCompleted) { + if (!buffer.IsEmpty) + { + throw new InvalidDataException("Connection terminated while reading a message."); + } break; } } diff --git a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.ConnectionLifecycle.cs b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.ConnectionLifecycle.cs index ebdfabac44..4bb97308af 100644 --- a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.ConnectionLifecycle.cs +++ b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.ConnectionLifecycle.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; @@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests testConnection.StartAsync, connection => ((TestConnection)connection).DisposeAsync()); builder.Services.AddSingleton(delegateConnectionFactory); - + return builder.Build(); } @@ -450,6 +451,34 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests } } + [Fact] + public async Task HubConnectionClosesWithErrorIfTerminatedWithPartialMessage() + { + var builder = new HubConnectionBuilder(); + var innerConnection = new TestConnection(); + + var delegateConnectionFactory = new DelegateConnectionFactory( + format => innerConnection.StartAsync(format), + connection => ((TestConnection)connection).DisposeAsync()); + builder.Services.AddSingleton(delegateConnectionFactory); + + var hubConnection = builder.Build(); + var closedEventTcs = new TaskCompletionSource(); + hubConnection.Closed += e => + { + closedEventTcs.SetResult(e); + return Task.CompletedTask; + }; + + await hubConnection.StartAsync().OrTimeout(); + + await innerConnection.Application.Output.WriteAsync(Encoding.UTF8.GetBytes(new[] { '{' })).OrTimeout(); + innerConnection.Application.Output.Complete(); + + var exception = await closedEventTcs.Task.OrTimeout(); + Assert.Equal("Connection terminated while reading a message.", exception.Message); + } + private static async Task ForceLastInvocationToComplete(TestConnection testConnection) { // We need to "complete" the invocation diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs index ed7ba5dd7e..04646faa3a 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests/HubConnectionHandlerTests.cs @@ -2207,6 +2207,38 @@ namespace Microsoft.AspNetCore.SignalR.Tests } } + [Fact] + public async Task ServerSendsCloseWithErrorWhenConnectionClosedWithPartialMessage() + { + var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(services => + { + services.AddSignalR(options => options.EnableDetailedErrors = true); + }); + var connectionHandler = serviceProvider.GetService>(); + + using (var client = new TestClient()) + { + var connectionHandlerTask = await client.ConnectAsync(connectionHandler).OrTimeout(); + + await client.Connection.Application.Output.WriteAsync(Encoding.UTF8.GetBytes(new[] { '{' })).OrTimeout(); + + // Close connection + client.Connection.Application.Output.Complete(); + + // Ignore message from OnConnectedAsync + await client.ReadAsync().OrTimeout(); + + var closeMessage = Assert.IsType(await client.ReadAsync().OrTimeout()); + + Assert.Equal("Connection closed with an error. InvalidDataException: Connection terminated while reading a message.", closeMessage.Error); + + // Shut down + client.Dispose(); + + await connectionHandlerTask.OrTimeout(); + } + } + private class CustomHubActivator : IHubActivator where THub : Hub { public int ReleaseCount;