Skip to content

Commit dbe9ab7

Browse files
danielloczihalter73
authored andcommitted
Respect CancellationToken in HubConnection.StartAsync() (#10140)
1 parent 2cc071a commit dbe9ab7

File tree

10 files changed

+43
-38
lines changed

10 files changed

+43
-38
lines changed

src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,10 @@ private async Task StartAsyncInner(CancellationToken cancellationToken = default
253253
throw new InvalidOperationException($"The {nameof(HubConnection)} cannot be started while {nameof(StopAsync)} is running.");
254254
}
255255

256-
await StartAsyncCore(cancellationToken);
256+
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _state.StopCts.Token))
257+
{
258+
await StartAsyncCore(cancellationToken);
259+
}
257260

258261
_state.ChangeState(HubConnectionState.Connecting, HubConnectionState.Connected);
259262
}
@@ -422,7 +425,7 @@ private async Task StartAsyncCore(CancellationToken cancellationToken)
422425
Log.Starting(_logger);
423426

424427
// Start the connection
425-
var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat);
428+
var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat, cancellationToken);
426429
var startingConnectionState = new ConnectionState(connection, this);
427430

428431
// From here on, if an error occurs we need to shut down the connection because
@@ -1023,7 +1026,8 @@ private async Task HandshakeAsync(ConnectionState startingConnectionState, Cance
10231026
try
10241027
{
10251028
using (var handshakeCts = new CancellationTokenSource(HandshakeTimeout))
1026-
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, handshakeCts.Token, _state.StopCts.Token))
1029+
// cancellationToken already contains _state.StopCts.Token, so we don't have to link it again
1030+
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, handshakeCts.Token))
10271031
{
10281032
while (true)
10291033
{
@@ -1287,7 +1291,7 @@ private async Task ReconnectAsync(Exception closeException)
12871291
var reconnectStartTime = DateTime.UtcNow;
12881292
var retryReason = closeException;
12891293
var nextRetryDelay = GetNextRetryDelay(previousReconnectAttempts++, TimeSpan.Zero, retryReason);
1290-
1294+
12911295
// We still have the connection lock from the caller, HandleConnectionClose.
12921296
_state.AssertInConnectionLock();
12931297

@@ -1347,8 +1351,7 @@ private async Task ReconnectAsync(Exception closeException)
13471351
SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, null),
13481352
"Someone other than Reconnect set the connection state!");
13491353

1350-
// HandshakeAsync already checks ReconnectingConnectionState.StopCts.Token.
1351-
await StartAsyncCore(CancellationToken.None);
1354+
await StartAsyncCore(_state.StopCts.Token);
13521355

13531356
Log.Reconnected(_logger, previousReconnectAttempts, DateTime.UtcNow - reconnectStartTime);
13541357

src/SignalR/clients/csharp/Client/test/UnitTests/TestTransport.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO.Pipelines;
3+
using System.Threading;
34
using System.Threading.Tasks;
45
using Microsoft.AspNetCore.Connections;
56
using Microsoft.AspNetCore.Http.Connections.Client;
@@ -29,13 +30,13 @@ public TestTransport(Func<Task> onTransportStop = null, Func<Task> onTransportSt
2930
Format = transferFormat;
3031
}
3132

32-
public async Task StartAsync(Uri url, TransferFormat transferFormat)
33+
public async Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default)
3334
{
3435
if ((Format & transferFormat) == 0)
3536
{
3637
throw new InvalidOperationException($"The '{transferFormat}' transfer format is not supported by this transport.");
3738
}
38-
39+
3940
var options = ClientPipeOptions.DefaultOptions;
4041
var pair = DuplexPipe.CreateConnectionPair(options, options);
4142

src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netcoreapp3.0.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
5151
{
5252
public partial interface ITransport : System.IO.Pipelines.IDuplexPipe
5353
{
54-
System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat);
54+
System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
5555
System.Threading.Tasks.Task StopAsync();
5656
}
5757
public partial interface ITransportFactory
@@ -65,7 +65,7 @@ public LongPollingTransport(System.Net.Http.HttpClient httpClient, Microsoft.Ext
6565
public System.IO.Pipelines.PipeReader Input { get { throw null; } }
6666
public System.IO.Pipelines.PipeWriter Output { get { throw null; } }
6767
[System.Diagnostics.DebuggerStepThroughAttribute]
68-
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) { throw null; }
68+
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
6969
[System.Diagnostics.DebuggerStepThroughAttribute]
7070
public System.Threading.Tasks.Task StopAsync() { throw null; }
7171
}
@@ -76,7 +76,7 @@ public ServerSentEventsTransport(System.Net.Http.HttpClient httpClient, Microsof
7676
public System.IO.Pipelines.PipeReader Input { get { throw null; } }
7777
public System.IO.Pipelines.PipeWriter Output { get { throw null; } }
7878
[System.Diagnostics.DebuggerStepThroughAttribute]
79-
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) { throw null; }
79+
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
8080
[System.Diagnostics.DebuggerStepThroughAttribute]
8181
public System.Threading.Tasks.Task StopAsync() { throw null; }
8282
}
@@ -86,7 +86,7 @@ public WebSocketsTransport(Microsoft.AspNetCore.Http.Connections.Client.HttpConn
8686
public System.IO.Pipelines.PipeReader Input { get { throw null; } }
8787
public System.IO.Pipelines.PipeWriter Output { get { throw null; } }
8888
[System.Diagnostics.DebuggerStepThroughAttribute]
89-
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) { throw null; }
89+
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
9090
[System.Diagnostics.DebuggerStepThroughAttribute]
9191
public System.Threading.Tasks.Task StopAsync() { throw null; }
9292
}

src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
5151
{
5252
public partial interface ITransport : System.IO.Pipelines.IDuplexPipe
5353
{
54-
System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat);
54+
System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
5555
System.Threading.Tasks.Task StopAsync();
5656
}
5757
public partial interface ITransportFactory
@@ -65,7 +65,7 @@ public LongPollingTransport(System.Net.Http.HttpClient httpClient, Microsoft.Ext
6565
public System.IO.Pipelines.PipeReader Input { get { throw null; } }
6666
public System.IO.Pipelines.PipeWriter Output { get { throw null; } }
6767
[System.Diagnostics.DebuggerStepThroughAttribute]
68-
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) { throw null; }
68+
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
6969
[System.Diagnostics.DebuggerStepThroughAttribute]
7070
public System.Threading.Tasks.Task StopAsync() { throw null; }
7171
}
@@ -76,7 +76,7 @@ public ServerSentEventsTransport(System.Net.Http.HttpClient httpClient, Microsof
7676
public System.IO.Pipelines.PipeReader Input { get { throw null; } }
7777
public System.IO.Pipelines.PipeWriter Output { get { throw null; } }
7878
[System.Diagnostics.DebuggerStepThroughAttribute]
79-
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) { throw null; }
79+
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
8080
[System.Diagnostics.DebuggerStepThroughAttribute]
8181
public System.Threading.Tasks.Task StopAsync() { throw null; }
8282
}
@@ -86,7 +86,7 @@ public WebSocketsTransport(Microsoft.AspNetCore.Http.Connections.Client.HttpConn
8686
public System.IO.Pipelines.PipeReader Input { get { throw null; } }
8787
public System.IO.Pipelines.PipeWriter Output { get { throw null; } }
8888
[System.Diagnostics.DebuggerStepThroughAttribute]
89-
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) { throw null; }
89+
public System.Threading.Tasks.Task StartAsync(System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
9090
[System.Diagnostics.DebuggerStepThroughAttribute]
9191
public System.Threading.Tasks.Task StopAsync() { throw null; }
9292
}

src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@ public async Task StartAsync(TransferFormat transferFormat, CancellationToken ca
188188
{
189189
using (_logger.BeginScope(_logScope))
190190
{
191-
await StartAsyncCore(transferFormat).ForceAsync();
191+
await StartAsyncCore(transferFormat, cancellationToken).ForceAsync();
192192
}
193193
}
194194

195-
private async Task StartAsyncCore(TransferFormat transferFormat)
195+
private async Task StartAsyncCore(TransferFormat transferFormat, CancellationToken cancellationToken)
196196
{
197197
CheckDisposed();
198198

@@ -215,7 +215,7 @@ private async Task StartAsyncCore(TransferFormat transferFormat)
215215

216216
Log.Starting(_logger);
217217

218-
await SelectAndStartTransport(transferFormat);
218+
await SelectAndStartTransport(transferFormat, cancellationToken);
219219

220220
_started = true;
221221
Log.Started(_logger);
@@ -288,7 +288,7 @@ private async Task DisposeAsyncCore()
288288
}
289289
}
290290

291-
private async Task SelectAndStartTransport(TransferFormat transferFormat)
291+
private async Task SelectAndStartTransport(TransferFormat transferFormat, CancellationToken cancellationToken)
292292
{
293293
var uri = _httpConnectionOptions.Url;
294294
// Set the initial access token provider back to the original one from options
@@ -301,7 +301,7 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat)
301301
if (_httpConnectionOptions.Transports == HttpTransportType.WebSockets)
302302
{
303303
Log.StartingTransport(_logger, _httpConnectionOptions.Transports, uri);
304-
await StartTransport(uri, _httpConnectionOptions.Transports, transferFormat);
304+
await StartTransport(uri, _httpConnectionOptions.Transports, transferFormat, cancellationToken);
305305
}
306306
else
307307
{
@@ -315,7 +315,7 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat)
315315

316316
do
317317
{
318-
negotiationResponse = await GetNegotiationResponseAsync(uri);
318+
negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken);
319319

320320
if (negotiationResponse.Url != null)
321321
{
@@ -379,12 +379,12 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat)
379379
// The negotiation response gets cleared in the fallback scenario.
380380
if (negotiationResponse == null)
381381
{
382-
negotiationResponse = await GetNegotiationResponseAsync(uri);
382+
negotiationResponse = await GetNegotiationResponseAsync(uri, cancellationToken);
383383
connectUrl = CreateConnectUrl(uri, negotiationResponse.ConnectionId);
384384
}
385385

386386
Log.StartingTransport(_logger, transportType, connectUrl);
387-
await StartTransport(connectUrl, transportType, transferFormat);
387+
await StartTransport(connectUrl, transportType, transferFormat, cancellationToken);
388388
break;
389389
}
390390
}
@@ -414,7 +414,7 @@ private async Task SelectAndStartTransport(TransferFormat transferFormat)
414414
}
415415
}
416416

417-
private async Task<NegotiationResponse> NegotiateAsync(Uri url, HttpClient httpClient, ILogger logger)
417+
private async Task<NegotiationResponse> NegotiateAsync(Uri url, HttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
418418
{
419419
try
420420
{
@@ -436,7 +436,7 @@ private async Task<NegotiationResponse> NegotiateAsync(Uri url, HttpClient httpC
436436
// rather than buffer the entire response. This gives a small perf boost.
437437
// Note that it is important to dispose of the response when doing this to
438438
// avoid leaving the connection open.
439-
using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
439+
using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
440440
{
441441
response.EnsureSuccessStatusCode();
442442
var responseBuffer = await response.Content.ReadAsByteArrayAsync();
@@ -467,15 +467,15 @@ private static Uri CreateConnectUrl(Uri url, string connectionId)
467467
return Utils.AppendQueryString(url, "id=" + connectionId);
468468
}
469469

470-
private async Task StartTransport(Uri connectUrl, HttpTransportType transportType, TransferFormat transferFormat)
470+
private async Task StartTransport(Uri connectUrl, HttpTransportType transportType, TransferFormat transferFormat, CancellationToken cancellationToken)
471471
{
472472
// Construct the transport
473473
var transport = _transportFactory.CreateTransport(transportType);
474474

475475
// Start the transport, giving it one end of the pipe
476476
try
477477
{
478-
await transport.StartAsync(connectUrl, transferFormat);
478+
await transport.StartAsync(connectUrl, transferFormat, cancellationToken);
479479
}
480480
catch (Exception ex)
481481
{
@@ -604,9 +604,9 @@ private static bool IsWebSocketsSupported()
604604
#endif
605605
}
606606

607-
private async Task<NegotiationResponse> GetNegotiationResponseAsync(Uri uri)
607+
private async Task<NegotiationResponse> GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken)
608608
{
609-
var negotiationResponse = await NegotiateAsync(uri, _httpClient, _logger);
609+
var negotiationResponse = await NegotiateAsync(uri, _httpClient, _logger, cancellationToken);
610610
_connectionId = negotiationResponse.ConnectionId;
611611
_logScope.ConnectionId = _connectionId;
612612
return negotiationResponse;

src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ITransport.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33

44
using System;
55
using System.IO.Pipelines;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using Microsoft.AspNetCore.Connections;
89

910
namespace Microsoft.AspNetCore.Http.Connections.Client.Internal
1011
{
1112
public interface ITransport : IDuplexPipe
1213
{
13-
Task StartAsync(Uri url, TransferFormat transferFormat);
14+
Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default);
1415
Task StopAsync();
1516
}
1617
}

src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/LongPollingTransport.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public LongPollingTransport(HttpClient httpClient, ILoggerFactory loggerFactory)
4040
_logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<LongPollingTransport>();
4141
}
4242

43-
public async Task StartAsync(Uri url, TransferFormat transferFormat)
43+
public async Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default)
4444
{
4545
if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text)
4646
{
@@ -52,7 +52,7 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat)
5252
// Make initial long polling request
5353
// Server uses first long polling request to finish initializing connection and it returns without data
5454
var request = new HttpRequestMessage(HttpMethod.Get, url);
55-
using (var response = await _httpClient.SendAsync(request))
55+
using (var response = await _httpClient.SendAsync(request, cancellationToken))
5656
{
5757
response.EnsureSuccessStatusCode();
5858
}

src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/ServerSentEventsTransport.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public ServerSentEventsTransport(HttpClient httpClient, ILoggerFactory loggerFac
4646
_logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<ServerSentEventsTransport>();
4747
}
4848

49-
public async Task StartAsync(Uri url, TransferFormat transferFormat)
49+
public async Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default)
5050
{
5151
if (transferFormat != TransferFormat.Text)
5252
{
@@ -62,7 +62,7 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat)
6262

6363
try
6464
{
65-
response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
65+
response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
6666
response.EnsureSuccessStatusCode();
6767
}
6868
catch

src/SignalR/clients/csharp/Http.Connections.Client/src/Internal/WebSocketsTransport.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public WebSocketsTransport(HttpConnectionOptions httpConnectionOptions, ILoggerF
8989
_accessTokenProvider = accessTokenProvider;
9090
}
9191

92-
public async Task StartAsync(Uri url, TransferFormat transferFormat)
92+
public async Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default)
9393
{
9494
if (url == null)
9595
{
@@ -121,7 +121,7 @@ public async Task StartAsync(Uri url, TransferFormat transferFormat)
121121

122122
try
123123
{
124-
await _webSocket.ConnectAsync(resolvedUrl, CancellationToken.None);
124+
await _webSocket.ConnectAsync(resolvedUrl, cancellationToken);
125125
}
126126
catch
127127
{

src/SignalR/server/SignalR/test/EndToEndTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ public FakeTransport()
568568
}
569569
}
570570

571-
public Task StartAsync(Uri url, TransferFormat transferFormat)
571+
public Task StartAsync(Uri url, TransferFormat transferFormat, CancellationToken cancellationToken = default)
572572
{
573573
var options = ClientPipeOptions.DefaultOptions;
574574
var pair = DuplexPipe.CreateConnectionPair(options, options);

0 commit comments

Comments
 (0)