Skip to content

Commit 55eee2e

Browse files
Fix closing SignalR Websocket connection on server close (#53288)
1 parent 429bd8b commit 55eee2e

File tree

2 files changed

+39
-0
lines changed

2 files changed

+39
-0
lines changed

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,14 @@ private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connecti
168168
{
169169
transport = HttpTransportType.WebSockets;
170170
connection = await GetOrCreateConnectionAsync(context, options);
171+
172+
if (connection is not null)
173+
{
174+
Log.EstablishedConnection(_logger);
175+
176+
// Allow the reads to be canceled
177+
connection.Cancellation ??= new CancellationTokenSource();
178+
}
171179
}
172180
else
173181
{

src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2842,6 +2842,37 @@ public async Task WebSocketConnectionClosingTriggersConnectionClosedToken()
28422842
}
28432843
}
28442844

2845+
[Fact]
2846+
public async Task ServerClosingClosesWebSocketConnection()
2847+
{
2848+
using (StartVerifiableLog())
2849+
{
2850+
var manager = CreateConnectionManager(LoggerFactory);
2851+
var connection = manager.CreateConnection();
2852+
2853+
var dispatcher = CreateDispatcher(manager, LoggerFactory);
2854+
var services = new ServiceCollection();
2855+
services.AddSingleton<TestConnectionHandler>();
2856+
var context = MakeRequest("/foo", connection, services);
2857+
SetTransport(context, HttpTransportType.WebSockets);
2858+
2859+
var builder = new ConnectionBuilder(services.BuildServiceProvider());
2860+
builder.UseConnectionHandler<TestConnectionHandler>();
2861+
var app = builder.Build();
2862+
var options = new HttpConnectionDispatcherOptions();
2863+
options.WebSockets.CloseTimeout = TimeSpan.FromSeconds(1);
2864+
2865+
var executeTask = dispatcher.ExecuteAsync(context, options, app);
2866+
2867+
// "close" server, since we're not using a server in these tests we just simulate what would be called when the server closes
2868+
await connection.DisposeAsync().DefaultTimeout();
2869+
2870+
await connection.ConnectionClosed.WaitForCancellationAsync().DefaultTimeout();
2871+
2872+
await executeTask.DefaultTimeout();
2873+
}
2874+
}
2875+
28452876
public class CustomHttpRequestLifetimeFeature : IHttpRequestLifetimeFeature
28462877
{
28472878
public CancellationToken RequestAborted { get; set; }

0 commit comments

Comments
 (0)