@@ -607,6 +607,7 @@ public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> appl
607
607
608
608
private async Task ProcessRequests < TContext > ( IHttpApplication < TContext > application )
609
609
{
610
+ var cleanContext = ExecutionContext . Capture ( ) ;
610
611
while ( _keepAlive )
611
612
{
612
613
BeginRequestProcessing ( ) ;
@@ -637,111 +638,107 @@ private async Task ProcessRequests<TContext>(IHttpApplication<TContext> applicat
637
638
638
639
InitializeBodyControl ( messageBody ) ;
639
640
640
- // We run user controlled request processing in a seperate async method
641
- // so any changes made to ExecutionContext are undone when it returns and
642
- // each request starts with a fresh ExecutionContext state.
643
- await ProcessRequest ( application ) ;
644
641
645
- // Even for non-keep-alive requests, try to consume the entire body to avoid RSTs.
646
- if ( ! _connectionAborted && _requestRejectedException == null && ! messageBody . IsEmpty )
647
- {
648
- await messageBody . ConsumeAsync ( ) ;
649
- }
642
+ var context = application . CreateContext ( this ) ;
650
643
651
- if ( HasStartedConsumingRequestBody )
644
+ try
652
645
{
653
- await messageBody . StopAsync ( ) ;
654
- }
655
- }
656
- }
646
+ KestrelEventSource . Log . RequestStart ( this ) ;
657
647
658
- private async ValueTask ProcessRequest < TContext > ( IHttpApplication < TContext > application )
659
- {
660
- var context = application . CreateContext ( this ) ;
648
+ // Run the application code for this request
649
+ await application . ProcessRequestAsync ( context ) ;
661
650
662
- try
663
- {
664
- KestrelEventSource . Log . RequestStart ( this ) ;
651
+ // Trigger OnStarting if it hasn't been called yet and the app hasn't
652
+ // already failed. If an OnStarting callback throws we can go through
653
+ // our normal error handling in ProduceEnd.
654
+ // https://github.com/aspnet/KestrelHttpServer/issues/43
655
+ if ( ! HasResponseStarted && _applicationException == null && _onStarting ? . Count > 0 )
656
+ {
657
+ await FireOnStarting ( ) ;
658
+ }
665
659
666
- // Run the application code for this request
667
- await application . ProcessRequestAsync ( context ) ;
660
+ if ( ! _connectionAborted && ! VerifyResponseContentLength ( out var lengthException ) )
661
+ {
662
+ ReportApplicationError ( lengthException ) ;
663
+ }
664
+ }
665
+ catch ( BadHttpRequestException ex )
666
+ {
667
+ // Capture BadHttpRequestException for further processing
668
+ // This has to be caught here so StatusCode is set properly before disposing the HttpContext
669
+ // (DisposeContext logs StatusCode).
670
+ SetBadRequestState ( ex ) ;
671
+ ReportApplicationError ( ex ) ;
672
+ }
673
+ catch ( Exception ex )
674
+ {
675
+ ReportApplicationError ( ex ) ;
676
+ }
668
677
669
- // Trigger OnStarting if it hasn't been called yet and the app hasn't
670
- // already failed. If an OnStarting callback throws we can go through
671
- // our normal error handling in ProduceEnd .
672
- // https://github.com/aspnet/KestrelHttpServer/issues/43
673
- if ( ! HasResponseStarted && _applicationException == null && _onStarting ? . Count > 0 )
678
+ KestrelEventSource . Log . RequestStop ( this ) ;
679
+
680
+ // At this point all user code that needs use to the request or response streams has completed .
681
+ // Using these streams in the OnCompleted callback is not allowed.
682
+ try
674
683
{
675
- await FireOnStarting ( ) ;
684
+ await _bodyControl . StopAsync ( ) ;
685
+ }
686
+ catch ( Exception ex )
687
+ {
688
+ // BodyControl.StopAsync() can throw if the PipeWriter was completed prior to the application writing
689
+ // enough bytes to satisfy the specified Content-Length. This risks double-logging the exception,
690
+ // but this scenario generally indicates an app bug, so I don't want to risk not logging it.
691
+ ReportApplicationError ( ex ) ;
676
692
}
677
693
678
- if ( ! _connectionAborted && ! VerifyResponseContentLength ( out var lengthException ) )
694
+ // 4XX responses are written by TryProduceInvalidRequestResponse during connection tear down.
695
+ if ( _requestRejectedException == null )
679
696
{
680
- ReportApplicationError ( lengthException ) ;
697
+ if ( ! _connectionAborted )
698
+ {
699
+ // Call ProduceEnd() before consuming the rest of the request body to prevent
700
+ // delaying clients waiting for the chunk terminator:
701
+ //
702
+ // https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
703
+ //
704
+ // This also prevents the 100 Continue response from being sent if the app
705
+ // never tried to read the body.
706
+ // https://github.com/aspnet/KestrelHttpServer/issues/2102
707
+ //
708
+ // ProduceEnd() must be called before _application.DisposeContext(), to ensure
709
+ // HttpContext.Response.StatusCode is correctly set when
710
+ // IHttpContextFactory.Dispose(HttpContext) is called.
711
+ await ProduceEnd ( ) ;
712
+ }
713
+ else if ( ! HasResponseStarted )
714
+ {
715
+ // If the request was aborted and no response was sent, there's no
716
+ // meaningful status code to log.
717
+ StatusCode = 0 ;
718
+ }
681
719
}
682
- }
683
- catch ( BadHttpRequestException ex )
684
- {
685
- // Capture BadHttpRequestException for further processing
686
- // This has to be caught here so StatusCode is set properly before disposing the HttpContext
687
- // (DisposeContext logs StatusCode).
688
- SetBadRequestState ( ex ) ;
689
- ReportApplicationError ( ex ) ;
690
- }
691
- catch ( Exception ex )
692
- {
693
- ReportApplicationError ( ex ) ;
694
- }
695
720
696
- KestrelEventSource . Log . RequestStop ( this ) ;
721
+ if ( _onCompleted ? . Count > 0 )
722
+ {
723
+ await FireOnCompleted ( ) ;
724
+ }
697
725
698
- // At this point all user code that needs use to the request or response streams has completed.
699
- // Using these streams in the OnCompleted callback is not allowed.
700
- try
701
- {
702
- await _bodyControl . StopAsync ( ) ;
703
- }
704
- catch ( Exception ex )
705
- {
706
- // BodyControl.StopAsync() can throw if the PipeWriter was completed prior to the application writing
707
- // enough bytes to satisfy the specified Content-Length. This risks double-logging the exception,
708
- // but this scenario generally indicates an app bug, so I don't want to risk not logging it.
709
- ReportApplicationError ( ex ) ;
710
- }
726
+ application . DisposeContext ( context , _applicationException ) ;
711
727
712
- // 4XX responses are written by TryProduceInvalidRequestResponse during connection tear down.
713
- if ( _requestRejectedException == null )
714
- {
715
- if ( ! _connectionAborted )
728
+ // Even for non-keep-alive requests, try to consume the entire body to avoid RSTs.
729
+ if ( ! _connectionAborted && _requestRejectedException == null && ! messageBody . IsEmpty )
716
730
{
717
- // Call ProduceEnd() before consuming the rest of the request body to prevent
718
- // delaying clients waiting for the chunk terminator:
719
- //
720
- // https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
721
- //
722
- // This also prevents the 100 Continue response from being sent if the app
723
- // never tried to read the body.
724
- // https://github.com/aspnet/KestrelHttpServer/issues/2102
725
- //
726
- // ProduceEnd() must be called before _application.DisposeContext(), to ensure
727
- // HttpContext.Response.StatusCode is correctly set when
728
- // IHttpContextFactory.Dispose(HttpContext) is called.
729
- await ProduceEnd ( ) ;
731
+ await messageBody . ConsumeAsync ( ) ;
730
732
}
731
- else if ( ! HasResponseStarted )
733
+
734
+ if ( HasStartedConsumingRequestBody )
732
735
{
733
- // If the request was aborted and no response was sent, there's no
734
- // meaningful status code to log.
735
- StatusCode = 0 ;
736
+ await messageBody . StopAsync ( ) ;
736
737
}
737
- }
738
738
739
- if ( _onCompleted ? . Count > 0 )
740
- {
741
- await FireOnCompleted ( ) ;
739
+ // Clear any AsyncLocals set during the request; back to a clean state ready for next request
740
+ ExecutionContext . Restore ( cleanContext ) ;
742
741
}
743
-
744
- application . DisposeContext ( context , _applicationException ) ;
745
742
}
746
743
747
744
public void OnStarting ( Func < object , Task > callback , object state )
0 commit comments