@@ -492,8 +492,7 @@ private async Task AddHttp11ConnectionAsync(RequestQueue<HttpConnection>.QueueIt
492492 HttpConnection ? connection = null ;
493493 Exception ? connectionException = null ;
494494
495- CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( ) ;
496- waiter . ConnectionCancellationTokenSource = cts ;
495+ CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( waiter ) ;
497496 try
498497 {
499498 connection = await CreateHttp11ConnectionAsync ( queueItem . Request , true , cts . Token ) . ConfigureAwait ( false ) ;
@@ -691,8 +690,7 @@ private async Task AddHttp2ConnectionAsync(RequestQueue<Http2Connection?>.QueueI
691690 Exception ? connectionException = null ;
692691 HttpConnectionWaiter < Http2Connection ? > waiter = queueItem . Waiter ;
693692
694- CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( ) ;
695- waiter . ConnectionCancellationTokenSource = cts ;
693+ CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource ( waiter ) ;
696694 try
697695 {
698696 ( 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
15201518 return SendWithProxyAuthAsync ( request , async , doRequestAuth , cancellationToken ) ;
15211519 }
15221520
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+ }
15241542
15251543 private async ValueTask < ( Stream , TransportContext ? , IPEndPoint ? ) > ConnectAsync ( HttpRequestMessage request , bool async , CancellationToken cancellationToken )
15261544 {
0 commit comments