77using System . Net . Http ;
88using System . Net . Http . QPack ;
99using System . Runtime . CompilerServices ;
10+ using System . Threading . Tasks . Sources ;
1011using Microsoft . AspNetCore . Connections ;
1112using Microsoft . AspNetCore . Connections . Features ;
1213using Microsoft . AspNetCore . Hosting . Server ;
@@ -47,8 +48,7 @@ internal abstract partial class Http3Stream : HttpProtocol, IHttp3Stream, IHttpH
4748 private int _totalParsedHeaderSize ;
4849 private bool _isMethodConnect ;
4950
50- // TODO: Change to resetable ValueTask source
51- private TaskCompletionSource ? _appCompleted ;
51+ private readonly ManualResetValueTaskSource < object ? > _appCompletedTaskSource = new ManualResetValueTaskSource < object ? > ( ) ;
5252
5353 private StreamCompletionFlags _completionState ;
5454 private readonly object _completionLock = new object ( ) ;
@@ -70,8 +70,8 @@ internal abstract partial class Http3Stream : HttpProtocol, IHttp3Stream, IHttpH
7070 public long StreamId => _streamIdFeature . StreamId ;
7171
7272 public long StreamTimeoutTicks { get ; set ; }
73- public bool IsReceivingHeader => _appCompleted == null ; // TCS is assigned once headers are received
74- public bool IsDraining => _appCompleted ? . Task . IsCompleted ?? false ; // Draining starts once app is complete
73+ public bool IsReceivingHeader => _requestHeaderParsingState <= RequestHeaderParsingState . Headers ; // Assigned once headers are received
74+ public bool IsDraining => _appCompletedTaskSource . GetStatus ( ) != ValueTaskSourceStatus . Pending ; // Draining starts once app is complete
7575
7676 public bool IsRequestStream => true ;
7777
@@ -87,7 +87,7 @@ public void Initialize(Http3StreamContext context)
8787 _streamIdFeature = _context . ConnectionFeatures . Get < IStreamIdFeature > ( ) ! ;
8888 _streamAbortFeature = _context . ConnectionFeatures . Get < IStreamAbortFeature > ( ) ! ;
8989
90- _appCompleted = null ;
90+ _appCompletedTaskSource . Reset ( ) ;
9191 _isClosed = 0 ;
9292 _requestHeaderParsingState = default ;
9393 _parsedPseudoHeaderFields = default ;
@@ -403,8 +403,7 @@ private void CompleteStream(bool errored)
403403
404404 // Stream will be pooled after app completed.
405405 // Wait to signal app completed after any potential aborts on the stream.
406- Debug . Assert ( _appCompleted != null ) ;
407- _appCompleted . SetResult ( ) ;
406+ _appCompletedTaskSource . SetResult ( null ) ;
408407 }
409408
410409 private bool TryClose ( )
@@ -492,8 +491,12 @@ public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> appli
492491
493492 await Input . CompleteAsync ( ) ;
494493
495- var appCompleted = _appCompleted ? . Task ?? Task . CompletedTask ;
496- if ( ! appCompleted . IsCompletedSuccessfully )
494+ // Once the header is finished being received then the app has started.
495+ var appCompletedTask = ! IsReceivingHeader
496+ ? new ValueTask ( _appCompletedTaskSource , _appCompletedTaskSource . Version )
497+ : ValueTask . CompletedTask ;
498+
499+ if ( ! appCompletedTask . IsCompletedSuccessfully )
497500 {
498501 // At this point in the stream's read-side is complete. However, with HTTP/3
499502 // the write-side of the stream can still be aborted by the client on request
@@ -529,7 +532,7 @@ public async Task ProcessRequestAsync<TContext>(IHttpApplication<TContext> appli
529532 } , this ) ;
530533
531534 // Make sure application func is completed before completing writer.
532- await appCompleted ;
535+ await appCompletedTask ;
533536 }
534537
535538 try
@@ -630,7 +633,7 @@ private async Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext>
630633 throw new Http3ConnectionErrorException ( CoreStrings . FormatHttp3StreamErrorFrameReceivedAfterTrailers ( Http3Formatting . ToFormattedType ( Http3FrameType . Headers ) ) , Http3ErrorCode . UnexpectedFrame ) ;
631634 }
632635
633- if ( _requestHeaderParsingState == RequestHeaderParsingState . Headers )
636+ if ( _requestHeaderParsingState == RequestHeaderParsingState . Body )
634637 {
635638 _requestHeaderParsingState = RequestHeaderParsingState . Trailers ;
636639 }
@@ -679,7 +682,7 @@ private async Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext>
679682 throw new Http3StreamErrorException ( CoreStrings . HttpErrorMissingMandatoryPseudoHeaderFields , Http3ErrorCode . MessageError ) ;
680683 }
681684
682- _appCompleted = new TaskCompletionSource ( ) ;
685+ _requestHeaderParsingState = RequestHeaderParsingState . Body ;
683686 StreamTimeoutTicks = default ;
684687 _context . StreamLifetimeHandler . OnStreamHeaderReceived ( this ) ;
685688
@@ -990,6 +993,7 @@ protected enum RequestHeaderParsingState
990993 Ready ,
991994 PseudoHeaderFields ,
992995 Headers ,
996+ Body ,
993997 Trailers
994998 }
995999
0 commit comments