@@ -162,13 +162,24 @@ internal unsafe QuicStream(MsQuicContextSafeHandle connectionHandle, QuicStreamT
162
162
try
163
163
{
164
164
QUIC_HANDLE * handle ;
165
- ThrowHelper . ThrowIfMsQuicError ( MsQuicApi . Api . StreamOpen (
165
+ int status = MsQuicApi . Api . StreamOpen (
166
166
connectionHandle ,
167
167
type == QuicStreamType . Unidirectional ? QUIC_STREAM_OPEN_FLAGS . UNIDIRECTIONAL : QUIC_STREAM_OPEN_FLAGS . NONE ,
168
168
& NativeCallback ,
169
169
( void * ) GCHandle . ToIntPtr ( context ) ,
170
- & handle ) ,
171
- "StreamOpen failed" ) ;
170
+ & handle ) ;
171
+
172
+ if ( status == QUIC_STATUS_ABORTED )
173
+ {
174
+ // Connection has been closed by the peer (either at transport or application level),
175
+ // we won't be receiving any event callback for shutdown on this stream, so we don't
176
+ // necessarily know which error to report. So we throw an exception which we can distinguish
177
+ // at the caller (ConnectionAborted normally has App error code) and throw the correct
178
+ // exception from there.
179
+ throw new QuicException ( QuicError . ConnectionAborted , null , "" ) ;
180
+ }
181
+
182
+ ThrowHelper . ThrowIfMsQuicError ( status , "StreamOpen failed" ) ;
172
183
_handle = new MsQuicContextSafeHandle ( handle , context , SafeHandleType . Stream , connectionHandle ) ;
173
184
_handle . Disposable = _sendBuffers ;
174
185
}
@@ -244,6 +255,20 @@ internal ValueTask StartAsync(CancellationToken cancellationToken = default)
244
255
int status = MsQuicApi . Api . StreamStart (
245
256
_handle ,
246
257
QUIC_STREAM_START_FLAGS . SHUTDOWN_ON_FAIL | QUIC_STREAM_START_FLAGS . INDICATE_PEER_ACCEPT ) ;
258
+
259
+ if ( status == QUIC_STATUS_ABORTED )
260
+ {
261
+ // Connection has been closed by the peer (either at transport or application level),
262
+ // we won't be receiving any event callback for shutdown on this stream, so we don't
263
+ // necessarily know which error to report. So we throw an exception which we can distinguish
264
+ // at the caller (ConnectionAborted normally has App error code) and throw the correct
265
+ // exception from there.
266
+ //
267
+ // Also, avoid setting _startedTcs so that we don't try to wait for SHUTDOWN_COMPLETE
268
+ // in DisposeAsync().
269
+ throw new QuicException ( QuicError . ConnectionAborted , null , "" ) ;
270
+ }
271
+
247
272
if ( ThrowHelper . TryGetStreamExceptionForMsQuicStatus ( status , out Exception ? exception ) )
248
273
{
249
274
_startedTcs . TrySetException ( exception ) ;
0 commit comments