Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 0267695

Browse files
Pawel Kadluczkamoozzyk
Pawel Kadluczka
authored andcommitted
Exceptions thrown during writing should close the connection
We need to close the connection if there is an exception when writing to the transport on the server side. Currently if an exception happens it leaves the connection in an unsable state - after the exception no messages from the server will be sent to the client because the writing loop is terminated. Ignoring the message could cause hangs on the client side since we can fail while writing a completion message. In this case if the client is awaiting the invocation it will hang because the task will never be completed.
1 parent 4db7868 commit 0267695

File tree

2 files changed

+47
-8
lines changed

2 files changed

+47
-8
lines changed

src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,27 @@ public async Task OnConnectedAsync(ConnectionContext connection)
7676
var protocolReaderWriter = connectionContext.ProtocolReaderWriter;
7777
async Task WriteToTransport()
7878
{
79-
while (await output.In.WaitToReadAsync())
79+
try
8080
{
81-
while (output.In.TryRead(out var hubMessage))
81+
while (await output.In.WaitToReadAsync())
8282
{
83-
var buffer = protocolReaderWriter.WriteMessage(hubMessage);
84-
while (await connection.Transport.Out.WaitToWriteAsync())
83+
while (output.In.TryRead(out var hubMessage))
8584
{
86-
if (connection.Transport.Out.TryWrite(buffer))
85+
var buffer = protocolReaderWriter.WriteMessage(hubMessage);
86+
while (await connection.Transport.Out.WaitToWriteAsync())
8787
{
88-
break;
88+
if (connection.Transport.Out.TryWrite(buffer))
89+
{
90+
break;
91+
}
8992
}
9093
}
9194
}
9295
}
96+
catch (Exception ex)
97+
{
98+
connectionContext.Abort(ex);
99+
}
93100
}
94101

95102
var writingOutputTask = WriteToTransport();
@@ -244,7 +251,7 @@ private async Task HubOnDisconnectedAsync(HubConnectionContext connection, Excep
244251

245252
private async Task DispatchMessagesAsync(HubConnectionContext connection)
246253
{
247-
// Since we dispatch multiple hub invocations in parallel, we need a way to communicate failure back to the main processing loop.
254+
// Since we dispatch multiple hub invocations in parallel, we need a way to communicate failure back to the main processing loop.
248255
// This is done by aborting the connection.
249256

250257
try

test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTests.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Runtime.Serialization;
78
using System.Security.Claims;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using System.Threading.Tasks.Channels;
1112
using Microsoft.AspNetCore.Authorization;
13+
using Microsoft.AspNetCore.Hosting;
1214
using Microsoft.AspNetCore.Http;
1315
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
1416
using Microsoft.AspNetCore.SignalR.Tests.Common;
@@ -21,7 +23,6 @@
2123
using Newtonsoft.Json;
2224
using Newtonsoft.Json.Serialization;
2325
using Xunit;
24-
using Microsoft.AspNetCore.Hosting;
2526

2627
namespace Microsoft.AspNetCore.SignalR.Tests
2728
{
@@ -1149,6 +1150,32 @@ public async Task GetHttpContextFromHubConnectionContextHandlesNull()
11491150
}
11501151
}
11511152

1153+
[Fact]
1154+
public async Task ConnectionClosedIfWritingToTransportFails()
1155+
{
1156+
// MessagePack does not support serializing objects or private types (including anonymous types)
1157+
// and throws. In this test we make sure that this exception closes the connection and bubbles up.
1158+
1159+
var serviceProvider = CreateServiceProvider();
1160+
1161+
var endPoint = serviceProvider.GetService<HubEndPoint<MethodHub>>();
1162+
1163+
using (var client = new TestClient(false, new MessagePackHubProtocol()))
1164+
{
1165+
var transportFeature = new Mock<IConnectionTransportFeature>();
1166+
transportFeature.SetupGet(f => f.TransportCapabilities).Returns(TransferMode.Binary);
1167+
client.Connection.Features.Set(transportFeature.Object);
1168+
1169+
var endPointLifetime = endPoint.OnConnectedAsync(client.Connection);
1170+
1171+
await client.Connected.OrTimeout();
1172+
1173+
await client.SendInvocationAsync(nameof(MethodHub.SendAnonymousObject)).OrTimeout();
1174+
1175+
await Assert.ThrowsAsync<SerializationException>(() => endPointLifetime.OrTimeout());
1176+
}
1177+
}
1178+
11521179
private static void AssertHubMessage(HubMessage expected, HubMessage actual)
11531180
{
11541181
// We aren't testing InvocationIds here
@@ -1567,6 +1594,11 @@ public string ConcatString(byte b, int i, char c, string s)
15671594
return $"{b}, {i}, {c}, {s}";
15681595
}
15691596

1597+
public Task SendAnonymousObject()
1598+
{
1599+
return Clients.Client(Context.ConnectionId).InvokeAsync("Send", new { });
1600+
}
1601+
15701602
public override Task OnDisconnectedAsync(Exception e)
15711603
{
15721604
return Task.CompletedTask;

0 commit comments

Comments
 (0)