@@ -108,6 +108,11 @@ static async ValueTask<QuicConnection> StartConnectAsync(QuicClientConnectionOpt
108108 /// </summary>
109109 private int _disposed ;
110110
111+ /// <summary>
112+ /// Completed when connection shutdown is initiated.
113+ /// </summary>
114+ private TaskCompletionSource _connectionCloseTcs = new TaskCompletionSource ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
115+
111116 private readonly ValueTaskSource _connectedTcs = new ValueTaskSource ( ) ;
112117 private readonly ResettableValueTaskSource _shutdownTcs = new ResettableValueTaskSource ( )
113118 {
@@ -424,7 +429,7 @@ public async ValueTask<QuicStream> OpenOutboundStreamAsync(QuicStreamType type,
424429
425430 await stream . StartAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
426431 }
427- catch
432+ catch ( Exception ex )
428433 {
429434 if ( stream is not null )
430435 {
@@ -433,10 +438,16 @@ public async ValueTask<QuicStream> OpenOutboundStreamAsync(QuicStreamType type,
433438
434439 // Propagate ODE if disposed in the meantime.
435440 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+
436447 // Propagate connection error if present.
437- if ( _acceptQueue . Reader . Completion . IsFaulted )
448+ if ( _connectionCloseTcs . Task . IsFaulted || connectionAbortedByPeer )
438449 {
439- await _acceptQueue . Reader . Completion . ConfigureAwait ( false ) ;
450+ await _connectionCloseTcs . Task . ConfigureAwait ( false ) ;
440451 }
441452 throw ;
442453 }
@@ -534,12 +545,15 @@ private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATE
534545 {
535546 Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( ThrowHelper . GetExceptionForMsQuicStatus ( data . Status , ( long ) data . ErrorCode ) ) ;
536547 _connectedTcs . TrySetException ( exception ) ;
548+ _connectionCloseTcs . TrySetException ( exception ) ;
537549 _acceptQueue . Writer . TryComplete ( exception ) ;
538550 return QUIC_STATUS_SUCCESS ;
539551 }
540552 private unsafe int HandleEventShutdownInitiatedByPeer ( ref SHUTDOWN_INITIATED_BY_PEER_DATA data )
541553 {
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 ) ;
543557 return QUIC_STATUS_SUCCESS ;
544558 }
545559 private unsafe int HandleEventShutdownComplete ( )
@@ -548,6 +562,7 @@ private unsafe int HandleEventShutdownComplete()
548562 _tlsSecret ? . WriteSecret ( ) ;
549563
550564 Exception exception = ExceptionDispatchInfo . SetCurrentStackTrace ( _disposed == 1 ? new ObjectDisposedException ( GetType ( ) . FullName ) : ThrowHelper . GetOperationAbortedException ( ) ) ;
565+ _connectionCloseTcs . TrySetException ( exception ) ;
551566 _acceptQueue . Writer . TryComplete ( exception ) ;
552567 _connectedTcs . TrySetException ( exception ) ;
553568 _shutdownTokenSource . Cancel ( ) ;
0 commit comments