Skip to content

Commit 4290bcc

Browse files
Pass serialization exceptions to Hub disconnect (#24408)
1 parent f28ef7c commit 4290bcc

File tree

4 files changed

+78
-13
lines changed

4 files changed

+78
-13
lines changed

src/SignalR/server/Core/src/HubConnectionContext.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ internal StreamTracker StreamTracker
9191

9292
internal HubCallerContext HubCallerContext { get; }
9393

94+
internal Exception CloseException { get; private set; }
95+
9496
/// <summary>
9597
/// Gets a <see cref="CancellationToken"/> that notifies when the connection is aborted.
9698
/// </summary>
@@ -212,6 +214,7 @@ private ValueTask<FlushResult> WriteCore(HubMessage message, CancellationToken c
212214
}
213215
catch (Exception ex)
214216
{
217+
CloseException = ex;
215218
Log.FailedWritingMessage(_logger, ex);
216219

217220
AbortAllowReconnect();
@@ -231,6 +234,7 @@ private ValueTask<FlushResult> WriteCore(SerializedHubMessage message, Cancellat
231234
}
232235
catch (Exception ex)
233236
{
237+
CloseException = ex;
234238
Log.FailedWritingMessage(_logger, ex);
235239

236240
AbortAllowReconnect();
@@ -247,6 +251,7 @@ private async Task CompleteWriteAsync(ValueTask<FlushResult> task)
247251
}
248252
catch (Exception ex)
249253
{
254+
CloseException = ex;
250255
Log.FailedWritingMessage(_logger, ex);
251256

252257
AbortAllowReconnect();
@@ -274,6 +279,7 @@ private async Task WriteSlowAsync(HubMessage message, CancellationToken cancella
274279
}
275280
catch (Exception ex)
276281
{
282+
CloseException = ex;
277283
Log.FailedWritingMessage(_logger, ex);
278284
AbortAllowReconnect();
279285
}
@@ -299,6 +305,7 @@ private async Task WriteSlowAsync(SerializedHubMessage message, CancellationToke
299305
}
300306
catch (Exception ex)
301307
{
308+
CloseException = ex;
302309
Log.FailedWritingMessage(_logger, ex);
303310
AbortAllowReconnect();
304311
}
@@ -336,6 +343,7 @@ private async Task TryWritePingSlowAsync()
336343
}
337344
catch (Exception ex)
338345
{
346+
CloseException = ex;
339347
Log.FailedWritingMessage(_logger, ex);
340348
AbortAllowReconnect();
341349
}

src/SignalR/server/Core/src/HubConnectionHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ private async Task RunHubAsync(HubConnectionContext connection)
182182
return;
183183
}
184184

185-
await HubOnDisconnectedAsync(connection, null);
185+
await HubOnDisconnectedAsync(connection, connection.CloseException);
186186
}
187187

188188
private async Task HubOnDisconnectedAsync(HubConnectionContext connection, Exception exception)

src/SignalR/server/SignalR/test/HubConnectionHandlerTestUtils/Hubs.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,6 @@ public void InvalidArgument(CancellationToken token)
189189
{
190190
}
191191

192-
private class SelfRef
193-
{
194-
public SelfRef()
195-
{
196-
Self = this;
197-
}
198-
199-
public SelfRef Self { get; set; }
200-
}
201-
202192
public async Task<string> StreamingConcat(ChannelReader<string> source)
203193
{
204194
var sb = new StringBuilder();
@@ -331,6 +321,16 @@ public async Task BlockingMethod()
331321
}
332322
}
333323

324+
internal class SelfRef
325+
{
326+
public SelfRef()
327+
{
328+
Self = this;
329+
}
330+
331+
public SelfRef Self { get; set; }
332+
}
333+
334334
public abstract class TestHub : Hub
335335
{
336336
public override Task OnConnectedAsync()
@@ -1123,9 +1123,20 @@ public override Task OnConnectedAsync()
11231123
return base.OnConnectedAsync();
11241124
}
11251125

1126+
public Task ProtocolErrorSelf()
1127+
{
1128+
return Clients.Caller.SendAsync("Send", new SelfRef());
1129+
}
1130+
1131+
public Task ProtocolErrorAll()
1132+
{
1133+
return Clients.All.SendAsync("Send", new SelfRef());
1134+
}
1135+
11261136
public override Task OnDisconnectedAsync(Exception exception)
11271137
{
11281138
_state.TokenStateInDisconnected = Context.ConnectionAborted.IsCancellationRequested;
1139+
_state.DisconnectedException = exception;
11291140

11301141
return base.OnDisconnectedAsync(exception);
11311142
}
@@ -1138,6 +1149,8 @@ public class ConnectionLifetimeState
11381149
public bool TokenStateInConnected { get; set; }
11391150

11401151
public bool TokenStateInDisconnected { get; set; }
1152+
1153+
public Exception DisconnectedException { get; set; }
11411154
}
11421155

11431156
public class CallerServiceHub : Hub

src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@
1919
using Microsoft.AspNetCore.Connections;
2020
using Microsoft.AspNetCore.Http;
2121
using Microsoft.AspNetCore.Http.Connections.Features;
22-
using Microsoft.AspNetCore.Internal;
2322
using Microsoft.AspNetCore.SignalR.Internal;
2423
using Microsoft.AspNetCore.SignalR.Protocol;
25-
using Microsoft.AspNetCore.Testing;
2624
using Microsoft.Extensions.DependencyInjection;
2725
using Microsoft.Extensions.Logging;
2826
using Microsoft.Extensions.Logging.Testing;
@@ -3342,6 +3340,52 @@ public async Task ConnectionAbortedIfSendFailsWithProtocolError()
33423340
}
33433341
}
33443342

3343+
[Fact]
3344+
public async Task SerializationExceptionsSendSelfArePassedToOnDisconnectedAsync()
3345+
{
3346+
using (StartVerifiableLog(write => write.EventId.Name == "FailedWritingMessage"))
3347+
{
3348+
var state = new ConnectionLifetimeState();
3349+
var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(s => s.AddSingleton(state), LoggerFactory);
3350+
var connectionHandler = serviceProvider.GetService<HubConnectionHandler<ConnectionLifetimeHub>>();
3351+
3352+
using (var client = new TestClient())
3353+
{
3354+
var connectionHandlerTask = await client.ConnectAsync(connectionHandler);
3355+
3356+
// Test HubConnectionContext.WriteCore(HubMessage) codepath
3357+
await client.SendInvocationAsync(nameof(ConnectionLifetimeHub.ProtocolErrorSelf)).OrTimeout();
3358+
3359+
await connectionHandlerTask.OrTimeout();
3360+
3361+
Assert.IsType<System.Text.Json.JsonException>(state.DisconnectedException);
3362+
}
3363+
}
3364+
}
3365+
3366+
[Fact]
3367+
public async Task SerializationExceptionsSendAllArePassedToOnDisconnectedAsync()
3368+
{
3369+
using (StartVerifiableLog(write => write.EventId.Name == "FailedWritingMessage"))
3370+
{
3371+
var state = new ConnectionLifetimeState();
3372+
var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider(s => s.AddSingleton(state), LoggerFactory);
3373+
var connectionHandler = serviceProvider.GetService<HubConnectionHandler<ConnectionLifetimeHub>>();
3374+
3375+
using (var client = new TestClient())
3376+
{
3377+
var connectionHandlerTask = await client.ConnectAsync(connectionHandler);
3378+
3379+
// Test HubConnectionContext.WriteCore(SerializedHubMessage) codepath
3380+
await client.SendInvocationAsync(nameof(ConnectionLifetimeHub.ProtocolErrorAll)).OrTimeout();
3381+
3382+
await connectionHandlerTask.OrTimeout();
3383+
3384+
Assert.IsType<System.Text.Json.JsonException>(state.DisconnectedException);
3385+
}
3386+
}
3387+
}
3388+
33453389
[Fact(Skip = "Magic auto cast not supported")]
33463390
public async Task UploadStreamItemInvalidTypeAutoCasts()
33473391
{

0 commit comments

Comments
 (0)