@@ -492,8 +492,7 @@ private async Task AddHttp11ConnectionAsync(RequestQueue<HttpConnection>.QueueIt
492
492
HttpConnection ? connection = null ;
493
493
Exception ? connectionException = null ;
494
494
495
- CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( ) ;
496
- waiter . ConnectionCancellationTokenSource = cts ;
495
+ CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( waiter ) ;
497
496
try
498
497
{
499
498
connection = await CreateHttp11ConnectionAsync ( queueItem . Request , true , cts . Token ) . ConfigureAwait ( false ) ;
@@ -691,8 +690,7 @@ private async Task AddHttp2ConnectionAsync(RequestQueue<Http2Connection?>.QueueI
691
690
Exception ? connectionException = null ;
692
691
HttpConnectionWaiter < Http2Connection ? > waiter = queueItem . Waiter ;
693
692
694
- CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( ) ;
695
- waiter . ConnectionCancellationTokenSource = cts ;
693
+ CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( waiter ) ;
696
694
try
697
695
{
698
696
( Stream stream , TransportContext ? transportContext , IPEndPoint ? remoteEndPoint ) = await ConnectAsync ( queueItem . Request , true , cts . Token ) . ConfigureAwait ( false ) ;
@@ -1520,7 +1518,27 @@ public ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool
1520
1518
return SendWithProxyAuthAsync ( request , async , doRequestAuth , cancellationToken ) ;
1521
1519
}
1522
1520
1523
- private CancellationTokenSource GetConnectTimeoutCancellationTokenSource ( ) => new CancellationTokenSource ( Settings . _connectTimeout ) ;
1521
+ private CancellationTokenSource GetConnectTimeoutCancellationTokenSource < T > ( HttpConnectionWaiter < T > waiter )
1522
+ where T : HttpConnectionBase ?
1523
+ {
1524
+ var cts = new CancellationTokenSource ( Settings . _connectTimeout ) ;
1525
+
1526
+ lock ( waiter )
1527
+ {
1528
+ waiter . ConnectionCancellationTokenSource = cts ;
1529
+
1530
+ // The initiating request for this connection attempt may complete concurrently at any time.
1531
+ // If it completed before we've set the CTS, CancelIfNecessary would no-op.
1532
+ // Check it again now that we're holding the lock and ensure we always set a timeout.
1533
+ if ( waiter . Task . IsCompleted )
1534
+ {
1535
+ CancelIfNecessary ( waiter , requestCancelled : waiter . Task . IsCanceled ) ;
1536
+ waiter . ConnectionCancellationTokenSource = null ;
1537
+ }
1538
+ }
1539
+
1540
+ return cts ;
1541
+ }
1524
1542
1525
1543
private async ValueTask < ( Stream , TransportContext ? , IPEndPoint ? ) > ConnectAsync ( HttpRequestMessage request , bool async , CancellationToken cancellationToken )
1526
1544
{
0 commit comments