@@ -46,6 +46,7 @@ internal class Http3Connection : IHttp3StreamLifetimeHandler, IRequestProcessor
46
46
private int _stoppedAcceptingStreams ;
47
47
private bool _gracefulCloseStarted ;
48
48
private int _activeRequestCount ;
49
+ private CancellationTokenSource _acceptStreamsCts = new CancellationTokenSource ( ) ;
49
50
private readonly Http3PeerSettings _serverSettings = new Http3PeerSettings ( ) ;
50
51
private readonly Http3PeerSettings _clientSettings = new Http3PeerSettings ( ) ;
51
52
private readonly StreamCloseAwaitable _streamCompletionAwaitable = new StreamCloseAwaitable ( ) ;
@@ -107,7 +108,8 @@ public void StopProcessingNextRequest(bool serverInitiated)
107
108
108
109
if ( Interlocked . CompareExchange ( ref _gracefulCloseInitiator , initiator , GracefulCloseInitiator . None ) == GracefulCloseInitiator . None )
109
110
{
110
- UpdateConnectionState ( ) ;
111
+ // Break out of AcceptStreams so connection state can be updated.
112
+ _acceptStreamsCts . Cancel ( ) ;
111
113
}
112
114
}
113
115
}
@@ -240,15 +242,20 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
240
242
// Don't delay on waiting to send outbound control stream settings.
241
243
outboundControlStreamTask = ProcessOutboundControlStreamAsync ( outboundControlStream ) ;
242
244
243
- while ( true )
245
+ while ( _stoppedAcceptingStreams == 0 )
244
246
{
245
- var streamContext = await _multiplexedContext . AcceptAsync ( ) ;
247
+ var streamContext = await _multiplexedContext . AcceptAsync ( _acceptStreamsCts . Token ) ;
246
248
247
249
try
248
250
{
249
251
if ( streamContext == null )
250
252
{
251
- break ;
253
+ if ( _acceptStreamsCts . Token . IsCancellationRequested )
254
+ {
255
+ _acceptStreamsCts = new CancellationTokenSource ( ) ;
256
+ }
257
+
258
+ continue ;
252
259
}
253
260
254
261
var streamDirectionFeature = streamContext . Features . Get < IStreamDirectionFeature > ( ) ;
@@ -267,9 +274,10 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
267
274
}
268
275
else
269
276
{
270
- // TODO race condition between checking this and updating highest stream ID
277
+ // Request stream
278
+
271
279
// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-5.2-2
272
- if ( _stoppedAcceptingStreams == 1 )
280
+ if ( _gracefulCloseStarted )
273
281
{
274
282
// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.2-3
275
283
streamContext . Features . Get < IProtocolErrorCodeFeature > ( ) ! . Error = ( long ) Http3ErrorCode . RequestRejected ;
@@ -280,7 +288,6 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
280
288
// Request stream IDs are tracked.
281
289
UpdateHighestOpenedRequestStreamId ( streamIdFeature . StreamId ) ;
282
290
283
- // Request stream
284
291
var persistentStateFeature = streamContext . Features . Get < IPersistentStateFeature > ( ) ;
285
292
Debug . Assert ( persistentStateFeature != null , $ "Required { nameof ( IPersistentStateFeature ) } not on stream context.") ;
286
293
@@ -353,7 +360,10 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
353
360
// Only send goaway if the connection close was initiated on the server.
354
361
if ( ! clientAbort )
355
362
{
356
- await SendGoAwayAsync ( GetCurrentGoAwayStreamId ( ) ) ;
363
+ if ( TryStopAcceptingStreams ( ) || _gracefulCloseStarted )
364
+ {
365
+ await SendGoAwayAsync ( GetCurrentGoAwayStreamId ( ) ) ;
366
+ }
357
367
}
358
368
359
369
// Abort active request streams.
@@ -430,37 +440,31 @@ private void UpdateConnectionState()
430
440
return ;
431
441
}
432
442
433
- int activeRequestCount ;
434
- lock ( _streams )
443
+ if ( _gracefulCloseInitiator != GracefulCloseInitiator . None )
435
444
{
436
- activeRequestCount = _activeRequestCount ;
437
- }
445
+ int activeRequestCount ;
446
+ lock ( _streams )
447
+ {
448
+ activeRequestCount = _activeRequestCount ;
449
+ }
438
450
439
- if ( _gracefulCloseInitiator != GracefulCloseInitiator . None && ! _gracefulCloseStarted )
440
- {
441
- _gracefulCloseStarted = true ;
451
+ if ( ! _gracefulCloseStarted )
452
+ {
453
+ _gracefulCloseStarted = true ;
442
454
443
- _errorCodeFeature . Error = ( long ) Http3ErrorCode . NoError ;
444
- Log . Http3ConnectionClosing ( _context . ConnectionId ) ;
455
+ _errorCodeFeature . Error = ( long ) Http3ErrorCode . NoError ;
456
+ Log . Http3ConnectionClosing ( _context . ConnectionId ) ;
445
457
446
- if ( _gracefulCloseInitiator == GracefulCloseInitiator . Server && activeRequestCount > 0 )
447
- {
448
- if ( TryStopAcceptingStreams ( ) )
458
+ if ( _gracefulCloseInitiator == GracefulCloseInitiator . Server && activeRequestCount > 0 )
449
459
{
450
460
// Go away with largest streamid to initiate graceful shutdown.
451
461
SendGoAwayAsync ( VariableLengthIntegerHelper . EightByteLimit ) . Preserve ( ) ;
452
462
}
453
463
}
454
- }
455
464
456
- if ( _activeRequestCount == 0 )
457
- {
458
- if ( _gracefulCloseStarted )
465
+ if ( activeRequestCount == 0 )
459
466
{
460
- if ( TryStopAcceptingStreams ( ) )
461
- {
462
- SendGoAwayAsync ( GetCurrentGoAwayStreamId ( ) ) . Preserve ( ) ;
463
- }
467
+ TryStopAcceptingStreams ( ) ;
464
468
}
465
469
}
466
470
}
0 commit comments