11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System . Collections . Frozen ;
45using Microsoft . AspNetCore . Builder ;
6+ using Microsoft . AspNetCore . Hosting ;
57using Microsoft . AspNetCore . Http ;
8+ using Microsoft . AspNetCore . Http . Features ;
69using Microsoft . AspNetCore . SystemWebAdapters . Features ;
710using Microsoft . Extensions . DependencyInjection ;
811using Microsoft . Extensions . Logging ;
1215
1316namespace Microsoft . AspNetCore . SystemWebAdapters ;
1417
15- internal sealed partial class IntegratedPipelineConfigureOptions ( IOptions < OwinAppOptions > owinOptions , IServiceProvider sp ) : IConfigureOptions < HttpApplicationOptions >
18+ internal sealed partial class OwinHttpApplicationIntegrationStartup (
19+ IOptions < OwinAppOptions > owinOptions ,
20+ ILogger < OwinHttpApplicationIntegrationStartup > logger ,
21+ IServiceProvider sp )
22+ : IStartupFilter
1623{
17- public void Configure ( HttpApplicationOptions options )
24+ [ LoggerMessage ( EventId = 0 , Level = LogLevel . Error , Message = "Integrated OWIN pipeline stage '{StageName}' added out of order before stage '{CurrentStageName}'. This stage will be ignored and middleware may run earlier than expected." ) ]
25+ private static partial void LogOutOfOrder ( ILogger logger , string stageName , string currentStageName ) ;
26+
27+ [ LoggerMessage ( EventId = 1 , Level = LogLevel . Error , Message = "Integrated OWIN pipeline stage '{StageName}' could not be mapped to an ApplicationEvent" ) ]
28+ private static partial void LogSkippedStage ( ILogger logger , string stageName ) ;
29+
30+ public Action < IApplicationBuilder > Configure ( Action < IApplicationBuilder > next )
31+ => builder =>
32+ {
33+ var stages = BuildStages ( ) . ToFrozenDictionary ( kv => kv . Item1 , kv => kv . Item2 ) ;
34+
35+ builder . Use ( async ( ctx , next ) =>
36+ {
37+ var existing = ctx . Features . GetRequiredFeature < IHttpApplicationFeature > ( ) ;
38+
39+ ctx . Features . Set < IHttpApplicationFeature > ( new OwinPipelineApplicationEventsFeatures ( existing , stages , ctx ) ) ;
40+
41+ try
42+ {
43+ await next ( ) ;
44+ }
45+ finally
46+ {
47+ ctx . Features . Set < IHttpApplicationFeature > ( existing ) ;
48+ }
49+ } ) ;
50+
51+ next ( builder ) ;
52+ } ;
53+
54+ private sealed class OwinPipelineApplicationEventsFeatures (
55+ IHttpApplicationFeature other ,
56+ FrozenDictionary < ApplicationEvent , RequestDelegate > stages ,
57+ HttpContext context )
58+ : IHttpApplicationFeature
59+ {
60+ System . Web . HttpApplication IHttpApplicationFeature . Application => other . Application ;
61+
62+ System . Web . RequestNotification IHttpApplicationFeature . CurrentNotification => other . CurrentNotification ;
63+
64+ bool IHttpApplicationFeature . IsPostNotification => other . IsPostNotification ;
65+
66+ async ValueTask IHttpApplicationFeature . RaiseEventAsync ( ApplicationEvent appEvent )
67+ {
68+ await other . RaiseEventAsync ( appEvent ) ;
69+
70+ if ( stages . TryGetValue ( appEvent , out var stage ) )
71+ {
72+ await stage ( context ) ;
73+ }
74+ }
75+ }
76+
77+ public IEnumerable < ( ApplicationEvent , RequestDelegate ) > BuildStages ( )
1878 {
1979 var stages = CreateStages ( owinOptions . Value . Configure , sp ) ;
2080
@@ -38,14 +98,18 @@ public void Configure(HttpApplicationOptions options)
3898
3999 if ( appEvent is { } value )
40100 {
41- options . RegisterEvent ( value , stage . Next ) ;
101+ yield return ( value , stage . Next ) ;
102+ }
103+ else
104+ {
105+ LogSkippedStage ( logger , stage . Name ) ;
42106 }
43107 }
44108 }
45109
46110 private sealed record OwinStage ( string Name , RequestDelegate Next ) ;
47111
48- private static IEnumerable < OwinStage > CreateStages ( Action < IAppBuilder , IServiceProvider > ? configure , IServiceProvider services )
112+ private IEnumerable < OwinStage > CreateStages ( Action < IAppBuilder , IServiceProvider > ? configure , IServiceProvider services )
49113 {
50114 if ( configure is null )
51115 {
@@ -54,7 +118,7 @@ private static IEnumerable<OwinStage> CreateStages(Action<IAppBuilder, IServiceP
54118
55119 StageBuilder ? firstStage = null ;
56120
57- Task DefaultApp ( IDictionary < string , object > env )
121+ static Task DefaultApp ( IDictionary < string , object > env )
58122 {
59123 if ( ! env . TryGetValue ( OwinConstants . IntegratedPipelineCurrentStage , out var currentStage ) )
60124 {
@@ -71,7 +135,7 @@ Task DefaultApp(IDictionary<string, object> env)
71135
72136 var appFunc = OwinBuilder . Build ( DefaultApp , ( builder , sp ) =>
73137 {
74- EnableIntegratedPipeline ( builder , stage => firstStage = stage , DefaultApp , sp . GetRequiredService < ILogger < OwinStage > > ( ) ) ;
138+ EnableIntegratedPipeline ( builder , stage => firstStage = stage , DefaultApp , logger ) ;
75139 configure ( builder , services ) ;
76140 } , services ) ;
77141
@@ -80,7 +144,7 @@ Task DefaultApp(IDictionary<string, object> env)
80144 throw new InvalidOperationException ( "Did not have a stage" ) ;
81145 }
82146
83- return [ .. GetStages ( firstStage , appFunc , services ) ] ;
147+ return GetStages ( firstStage , appFunc , services ) ;
84148 }
85149
86150 private static IEnumerable < OwinStage > GetStages ( StageBuilder ? stage , AppFunc appFunc , IServiceProvider services )
@@ -118,9 +182,6 @@ private sealed class StageBuilder
118182 public AppFunc ? EntryPoint { get ; set ; }
119183 }
120184
121- [ LoggerMessage ( EventId = 1 , Level = LogLevel . Error , Message = "Integrated Pipeline stage '{StageName}' added out of order before stage '{CurrentStageName}'. This stage will be ignored and middleware may run earlier than expected." ) ]
122- private static partial void LogOutOfOrder ( ILogger logger , string stageName , string currentStageName ) ;
123-
124185 private static void EnableIntegratedPipeline ( IAppBuilder app , Action < StageBuilder > onStageCreated , AppFunc exitPoint , ILogger logger )
125186 {
126187 var stage = new StageBuilder { Name = "PreHandlerExecute" } ;
@@ -181,6 +242,7 @@ internal static bool VerifyStageOrder(string stage1, string stage2)
181242 {
182243 return false ;
183244 }
245+
184246 return stage1Index < stage2Index ;
185247 }
186248}
0 commit comments