@@ -108,6 +108,11 @@ static async ValueTask<QuicConnection> StartConnectAsync(QuicClientConnectionOpt
108
108
/// </summary>
109
109
private int _disposed ;
110
110
111
+ /// <summary>
112
+ /// Completed when connection shutdown is initiated.
113
+ /// </summary>
114
+ private TaskCompletionSource _connectionCloseTcs = new TaskCompletionSource ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
115
+
111
116
private readonly ValueTaskSource _connectedTcs = new ValueTaskSource ( ) ;
112
117
private readonly ResettableValueTaskSource _shutdownTcs = new ResettableValueTaskSource ( )
113
118
{
@@ -424,7 +429,7 @@ public async ValueTask<QuicStream> OpenOutboundStreamAsync(QuicStreamType type,
424
429
425
430
await stream . StartAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
426
431
}
427
- catch
432
+ catch ( Exception ex )
428
433
{
429
434
if ( stream is not null )
430
435
{
@@ -433,10 +438,16 @@ public async ValueTask<QuicStream> OpenOutboundStreamAsync(QuicStreamType type,
433
438
434
439
// Propagate ODE if disposed in the meantime.
435
440
ObjectDisposedException . ThrowIf ( _disposed == 1 , this ) ;
441
+
442
+ // In case of an incoming race when the connection is closed by the peer just before we open the stream,
443
+ // we receive QUIC_STATUS_ABORTED from MsQuic, but we don't know how the connection was closed. We throw
444
+ // special exception and handle it here where we can determine the shutdown reason.
445
+ bool connectionAbortedByPeer = ThrowHelper . IsConnectionAbortedWhenStartingStreamException ( ex ) ;
446
+
436
447
// Propagate connection error if present.
437
- if ( _acceptQueue . Reader . Completion . IsFaulted )
448
+ if ( _connectionCloseTcs . Task . IsFaulted || connectionAbortedByPeer )
438
449
{
439
- await _acceptQueue . Reader . Completion . ConfigureAwait ( false ) ;
450
+ await _connectionCloseTcs . Task . ConfigureAwait ( false ) ;
440
451
}
441
452
throw ;
442
453
}
@@ -534,12 +545,15 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE
534
545
{
535
546
Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetExceptionForMsQuicStatus ( data . Status , ( long ) data . ErrorCode ) ) ;
536
547
_connectedTcs . TrySetException ( exception ) ;
548
+ _connectionCloseTcs . TrySetException ( exception ) ;
537
549
_acceptQueue . Writer . TryComplete ( exception ) ;
538
550
return QUIC_STATUS_SUCCESS ;
539
551
}
540
552
private unsafe int HandleEventShutdownInitiatedByPeer ( ref SHUTDOWN_INITIATED_BY_PEER_DATA data )
541
553
{
542
- _acceptQueue . Writer . TryComplete ( ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) ) ;
554
+ Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetConnectionAbortedException ( ( long ) data . ErrorCode ) ) ;
555
+ _connectionCloseTcs . TrySetException ( exception ) ;
556
+ _acceptQueue . Writer . TryComplete ( exception ) ;
543
557
return QUIC_STATUS_SUCCESS ;
544
558
}
545
559
private unsafe int HandleEventShutdownComplete ( )
@@ -548,6 +562,7 @@ private unsafe int HandleEventShutdownComplete()
548
562
_tlsSecret ? . WriteSecret ( ) ;
549
563
550
564
Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( _disposed == 1 ? new ObjectDisposedException ( GetType ( ) . FullName ) : ThrowHelper . GetOperationAbortedException ( ) ) ;
565
+ _connectionCloseTcs . TrySetException ( exception ) ;
551
566
_acceptQueue . Writer . TryComplete ( exception ) ;
552
567
_connectedTcs . TrySetException ( exception ) ;
553
568
_shutdownTokenSource . Cancel ( ) ;
0 commit comments