@@ -224,14 +224,21 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
224224 // the maximum capacity of the dynamic table to zero.
225225
226226 // Don't create Encoder and Decoder as they aren't used now.
227- Exception ? error = null ;
228227
229- // Don't delay setting up the connection on the control stream being ready.
230- // Task is awaited when connection finishes.
231- var controlStreamTask = CreateControlStreamAsync ( application ) ;
228+ Exception ? error = null ;
229+ ValueTask outboundControlStreamTask = default ;
232230
233231 try
234232 {
233+ var outboundControlStream = await CreateNewUnidirectionalStreamAsync ( application ) ;
234+ lock ( _sync )
235+ {
236+ OutboundControlStream = outboundControlStream ;
237+ }
238+
239+ // Don't delay on waiting to send outbound control stream settings.
240+ outboundControlStreamTask = ProcessOutboundControlStreamAsync ( outboundControlStream ) ;
241+
235242 while ( _isClosed == 0 )
236243 {
237244 // Don't pass a cancellation token to AcceptAsync.
@@ -341,23 +348,26 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
341348 stream . Abort ( connectionError , ( Http3ErrorCode ) _errorCodeFeature . Error ) ;
342349 }
343350
351+ lock ( _sync )
352+ {
353+ OutboundControlStream ? . Abort ( connectionError , ( Http3ErrorCode ) _errorCodeFeature . Error ) ;
354+ }
355+
344356 while ( _activeRequestCount > 0 )
345357 {
346358 await _streamCompletionAwaitable ;
347359 }
348360
361+ await outboundControlStreamTask ;
362+
349363 _context . TimeoutControl . CancelTimeout ( ) ;
350364 _context . TimeoutControl . StartDrainTimeout ( Limits . MinResponseDataRate , Limits . MaxResponseBufferSize ) ;
351365 }
352366 catch
353367 {
354- Abort ( connectionError , Http3ErrorCode . NoError ) ;
368+ Abort ( connectionError , Http3ErrorCode . InternalError ) ;
355369 throw ;
356370 }
357-
358- // Ensure control stream creation task finished. At this point the connection, including the control
359- // stream should be closed/aborted. Error handling inside method ensures await won't throw.
360- await controlStreamTask ;
361371 }
362372 }
363373
@@ -400,18 +410,12 @@ private void UpdateConnectionState()
400410 }
401411 }
402412
403- private async ValueTask CreateControlStreamAsync < TContext > ( IHttpApplication < TContext > application ) where TContext : notnull
413+ private async ValueTask ProcessOutboundControlStreamAsync ( Http3ControlStream controlStream )
404414 {
405415 try
406416 {
407- var stream = await CreateNewUnidirectionalStreamAsync ( application ) ;
408- lock ( _sync )
409- {
410- OutboundControlStream = stream ;
411- }
412-
413- await stream . SendStreamIdAsync ( id : 0 ) ;
414- await stream . SendSettingsFrameAsync ( ) ;
417+ await controlStream . SendStreamIdAsync ( id : 0 ) ;
418+ await controlStream . SendSettingsFrameAsync ( ) ;
415419 }
416420 catch ( Exception ex )
417421 {
@@ -449,15 +453,27 @@ private async ValueTask<Http3ControlStream> CreateNewUnidirectionalStreamAsync<T
449453 return new Http3ControlStream < TContext > ( application , httpConnectionContext ) ;
450454 }
451455
452- private ValueTask < FlushResult > SendGoAway ( long id )
456+ private async ValueTask < FlushResult > SendGoAway ( long id )
453457 {
458+ Http3ControlStream ? stream ;
454459 lock ( _sync )
455460 {
456- if ( OutboundControlStream != null )
461+ stream = OutboundControlStream ;
462+ }
463+
464+ if ( stream != null )
465+ {
466+ try
467+ {
468+ return await stream . SendGoAway ( id ) ;
469+ }
470+ catch
457471 {
458- return OutboundControlStream . SendGoAway ( id ) ;
472+ // The control stream may not be healthy.
473+ // Ignore error sending go away.
459474 }
460475 }
476+
461477 return default ;
462478 }
463479
0 commit comments