Introduction to pipeline processing for Asp.net request processing

  • 2020-05-17 05:22:08
  • OfStack

In the process of understanding the request processing process of Asp.net, I feel it is necessary to understand how the asp.net pipeline is implemented from a source code perspective.

Before you do this, it is important to understand the basics of the asp.net request process, such as the ASP.NET request process, Asp.net pipeline, ASP.NET pipeline and application lifecycle

We all know that the main method of HttpRuntime is

public static void ProcessRequest(HttpWorkerRequest wr)

private void ProcessRequestInternal(HttpWorkerRequest wr) 
{ 
HttpContext context; 
try 
{ 
context = new HttpContext(wr, false); 
} 
catch 
{ 
wr.SendStatus(400, "Bad Request"); 
wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); 
byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); 
wr.SendResponseFromMemory(bytes, bytes.Length); 
wr.FlushResponse(true); 
wr.EndOfRequest(); 
return; 
} 
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); 
Interlocked.Increment(ref this._activeRequestCount); 
HostingEnvironment.IncrementBusyCount(); 
try 
{ 
try 
{ 
this.EnsureFirstRequestInit(context); 
} 
catch 
{ 
if (!context.Request.IsDebuggingRequest) 
{ 
throw; 
} 
} 
context.Response.InitResponseWriter(); 
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); 
if (applicationInstance == null) 
{ 
throw new HttpException(SR.GetString("Unable_create_app_object")); 
} 
if (EtwTrace.IsTraceEnabled(5, 1)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start"); 
} 
if (applicationInstance is IHttpAsyncHandler) 
{ 
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; 
context.AsyncAppHandler = handler2; 
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); 
} 
else 
{ 
applicationInstance.ProcessRequest(context); 
this.FinishRequest(context.WorkerRequest, context, null); 
} 
} 
catch (Exception exception) 
{ 
context.Response.InitResponseWriter(); 
this.FinishRequest(wr, context, exception); 
} 
}

We see that there is a sentence in it

IHttpHandler applicationInstance = HttpApplicationFactory. GetApplicationInstance (context); HttpApplication implements the IHttpAsyncHandler interface public class HttpApplication: IHttpAsyncHandler, IHttpHandler, IComponent, IDisposable, and finally calls the BeginProcessRequest method of application.
HttpApplicationFactory.GetApplicationInstance (context) is mainly a call to GetNormalApplicationInstance

internal static IHttpHandler GetApplicationInstance(HttpContext context) 
{ 
if (_customApplication != null) 
{ 
return _customApplication; 
} 
if (context.Request.IsDebuggingRequest) 
{ 
return new HttpDebugHandler(); 
} 
_theApplicationFactory.EnsureInited(); 
_theApplicationFactory.EnsureAppStartCalled(context); 
return _theApplicationFactory.GetNormalApplicationInstance(context); 
}


private HttpApplication GetNormalApplicationInstance(HttpContext context) 
{ 
HttpApplication application = null; 
lock (this._freeList) 
{ 
if (this._numFreeAppInstances > 0) 
{ 
application = (HttpApplication) this._freeList.Pop(); 
this._numFreeAppInstances--; 
if (this._numFreeAppInstances < this._minFreeAppInstances) 
{ 
this._minFreeAppInstances = this._numFreeAppInstances; 
} 
} 
} 
if (application == null) 
{ 
application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType); 
using (new ApplicationImpersonationContext()) 
{ 
application.InitInternal(context, this._state, this._eventHandlerMethods); 
} 
} 
return application; 
}

There is a key method in GetNormalApplicationInstance, application.InitInternal (context, this._state, this._eventHandlerMethods); We assume it does the initialization of Application, including the initialization of the http pipeline.

internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) 
{ 
this._state = state; 
PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); 
try 
{ 
try 
{ 
this._initContext = context; 
this._initContext.ApplicationInstance = this; 
context.ConfigurationPath = context.Request.ApplicationPathObject; 
using (new DisposableHttpContextWrapper(context)) 
{ 
if (HttpRuntime.UseIntegratedPipeline) 
{ 
try 
{ 
context.HideRequestResponse = true; 
this._hideRequestResponse = true; 
this.InitIntegratedModules(); 
goto Label_006B; 
} 
finally 
{ 
context.HideRequestResponse = false; 
this._hideRequestResponse = false; 
} 
} 
this.InitModules(); 
Label_006B: 
if (handlers != null) 
{ 
this.HookupEventHandlersForApplicationAndModules(handlers); 
} 
this._context = context; 
if (HttpRuntime.UseIntegratedPipeline && (this._context != null)) 
{ 
this._context.HideRequestResponse = true; 
} 
this._hideRequestResponse = true; 
try 
{ 
this.Init(); 
} 
catch (Exception exception) 
{ 
this.RecordError(exception); 
} 
} 
if (HttpRuntime.UseIntegratedPipeline && (this._context != null)) 
{ 
this._context.HideRequestResponse = false; 
} 
this._hideRequestResponse = false; 
this._context = null; 
this._resumeStepsWaitCallback = new WaitCallback(this.ResumeStepsWaitCallback); 
if (HttpRuntime.UseIntegratedPipeline) 
{ 
this._stepManager = new PipelineStepManager(this); 
} 
else 
{ 
this._stepManager = new ApplicationStepManager(this); 
} 
this._stepManager.BuildSteps(this._resumeStepsWaitCallback); 
} 
finally 
{ 
this._initInternalCompleted = true; 
context.ConfigurationPath = null; 
this._initContext.ApplicationInstance = null; 
this._initContext = null; 
} 
} 
catch 
{ 
throw; 
} 
}

The key code for this method is:


if (HttpRuntime.UseIntegratedPipeline) 
{ 
this._stepManager = new PipelineStepManager(this); 
} 
else 
{ 
this._stepManager = new ApplicationStepManager(this); 
} 
this._stepManager.BuildSteps(this._resumeStepsWaitCallback); 

I think you can see here why IIS7 has an integration mode and a classic mode. If you don't pay much attention to this code, let's take a look at the classic ApplicationStepManager


internal class ApplicationStepManager : HttpApplication.StepManager 
{ 
// Fields 
private int _currentStepIndex; 
private int _endRequestStepIndex; 
private HttpApplication.IExecutionStep[] _execSteps; 
private int _numStepCalls; 
private int _numSyncStepCalls; 
private WaitCallback _resumeStepsWaitCallback; 

// Methods 
internal ApplicationStepManager(HttpApplication app) : base(app) 
{ 
} 

internal override void BuildSteps(WaitCallback stepCallback) 
{ 
ArrayList steps = new ArrayList(); 
HttpApplication app = base._application; 
bool flag = false; 
UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings; 
flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0); 
steps.Add(new HttpApplication.ValidateRequestExecutionStep(app)); 
steps.Add(new HttpApplication.ValidatePathExecutionStep(app)); 
if (flag) 
{ 
steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); 
} 
app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps); 
steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); 
app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps); 
steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); 
app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); 
steps.Add(new HttpApplication.CallFilterExecutionStep(app)); 
app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); 
app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); 
this._endRequestStepIndex = steps.Count; 
app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); 
steps.Add(new HttpApplication.NoopExecutionStep()); 
this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; 
steps.CopyTo(this._execSteps); 
this._resumeStepsWaitCallback = stepCallback; 
} 

internal override void InitRequest() 
{ 
this._currentStepIndex = -1; 
this._numStepCalls = 0; 
this._numSyncStepCalls = 0; 
base._requestCompleted = false; 
} 

[DebuggerStepperBoundary] 
internal override void ResumeSteps(Exception error) 
{ 
bool flag = false; 
bool completedSynchronously = true; 
HttpApplication application = base._application; 
HttpContext context = application.Context; 
HttpApplication.ThreadContext context2 = null; 
AspNetSynchronizationContext syncContext = context.SyncContext; 
lock (base._application) 
{ 
try 
{ 
context2 = application.OnThreadEnter(); 
} 
catch (Exception exception) 
{ 
if (error == null) 
{ 
error = exception; 
} 
} 
try 
{ 
try 
{ 
Label_0045: 
if (syncContext.Error != null) 
{ 
error = syncContext.Error; 
syncContext.ClearError(); 
} 
if (error != null) 
{ 
application.RecordError(error); 
error = null; 
} 
if (syncContext.PendingOperationsCount > 0) 
{ 
syncContext.SetLastCompletionWorkItem(this._resumeStepsWaitCallback); 
} 
else 
{ 
if ((this._currentStepIndex < this._endRequestStepIndex) && ((context.Error != null) || base._requestCompleted)) 
{ 
context.Response.FilterOutput(); 
this._currentStepIndex = this._endRequestStepIndex; 
} 
else 
{ 
this._currentStepIndex++; 
} 
if (this._currentStepIndex >= this._execSteps.Length) 
{ 
flag = true; 
} 
else 
{ 
this._numStepCalls++; 
context.SyncContext.Enable(); 
error = application.ExecuteStep(this._execSteps[this._currentStepIndex], ref completedSynchronously); 
if (completedSynchronously) 
{ 
this._numSyncStepCalls++; 
goto Label_0045; 
} 
} 
} 
} 
finally 
{ 
if (context2 != null) 
{ 
try 
{ 
context2.Leave(); 
} 
catch 
{ 
} 
} 
} 
} 
catch 
{ 
throw; 
} 
} 
if (flag) 
{ 
context.Unroot(); 
application.AsyncResult.Complete(this._numStepCalls == this._numSyncStepCalls, null, null); 
application.ReleaseAppInstance(); 
} 
} 
}

The internal override void BuildSteps(WaitCallback stepCallback) method in this class is to register the 19 pipeline events for us, internal override void ResumeSteps(Exception error) is to execute the pipeline events in sequence, steps.MapHandlerExecutionStep (new HttpApplication.MapHandlerExecutionStep (app)); It's mapping our handler

internal class MapHandlerExecutionStep : HttpApplication.IExecutionStep 
{ 
// Fields 
private HttpApplication _application; 

// Methods 
internal MapHandlerExecutionStep(HttpApplication app) 
{ 
this._application = app; 
} 

void HttpApplication.IExecutionStep.Execute() 
{ 
HttpContext context = this._application.Context; 
HttpRequest request = context.Request; 
if (EtwTrace.IsTraceEnabled(5, 1)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, context.WorkerRequest); 
} 
context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false); 
if (EtwTrace.IsTraceEnabled(5, 1)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_LEAVE, context.WorkerRequest); 
} 
} 

// Properties 
bool HttpApplication.IExecutionStep.CompletedSynchronously 
{ 
get 
{ 
return true; 
} 
} 

bool HttpApplication.IExecutionStep.IsCancellable 
{ 
get 
{ 
return false; 
} 
} 
}

The calls inside are mainly

context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);

The MapHttpHandler of HttpApplication is as follows:

internal IHttpHandler MapHttpHandler(HttpContext context, string requestType, VirtualPath path, string pathTranslated, bool useAppConfig) 
{ 
IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null; 
using (new ApplicationImpersonationContext()) 
{ 
if (handler != null) 
{ 
return handler; 
} 
HttpHandlerAction mapping = this.GetHandlerMapping(context, requestType, path, useAppConfig); 
if (mapping == null) 
{ 
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND); 
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED); 
throw new HttpException(SR.GetString("Http_handler_not_found_for_request_type", new object[] { requestType })); 
} 
IHttpHandlerFactory factory = this.GetFactory(mapping); 
try 
{ 
IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2; 
if (factory2 != null) 
{ 
handler = factory2.GetHandler(context, requestType, path, pathTranslated); 
} 
else 
{ 
handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated); 
} 
} 
catch (FileNotFoundException exception) 
{ 
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) 
{ 
throw new HttpException(0x194, null, exception); 
} 
throw new HttpException(0x194, null); 
} 
catch (DirectoryNotFoundException exception2) 
{ 
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) 
{ 
throw new HttpException(0x194, null, exception2); 
} 
throw new HttpException(0x194, null); 
} 
catch (PathTooLongException exception3) 
{ 
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) 
{ 
throw new HttpException(0x19e, null, exception3); 
} 
throw new HttpException(0x19e, null); 
} 
if (this._handlerRecycleList == null) 
{ 
this._handlerRecycleList = new ArrayList(); 
} 
this._handlerRecycleList.Add(new HandlerWithFactory(handler, factory)); 
} 
return handler; 
}

IHttpHandlerFactory is created in MapHttpHandler, which in turn creates httphandler.

In ApplicationStepManager, the BuildSteps method is steps.Add (new HttpApplication.CallHandlerExecutionStep (app)); So this is where registration calls us hanndler.

internal class CallHandlerExecutionStep : HttpApplication.IExecutionStep 
{ 
// Fields 
private HttpApplication _application; 
private AsyncCallback _completionCallback; 
private IHttpAsyncHandler _handler; 
private bool _sync; 

// Methods 
internal CallHandlerExecutionStep(HttpApplication app) 
{ 
this._application = app; 
this._completionCallback = new AsyncCallback(this.OnAsyncHandlerCompletion); 
} 

private void OnAsyncHandlerCompletion(IAsyncResult ar) 
{ 
if (!ar.CompletedSynchronously) 
{ 
HttpContext context = this._application.Context; 
Exception error = null; 
try 
{ 
try 
{ 
this._handler.EndProcessRequest(ar); 
} 
finally 
{ 
context.Response.GenerateResponseHeadersForHandler(); 
} 
} 
catch (Exception exception2) 
{ 
if ((exception2 is ThreadAbortException) || ((exception2.InnerException != null) && (exception2.InnerException is ThreadAbortException))) 
{ 
this._application.CompleteRequest(); 
} 
else 
{ 
error = exception2; 
} 
} 
if (EtwTrace.IsTraceEnabled(4, 4)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_LEAVE, context.WorkerRequest); 
} 
this._handler = null; 
context.SetStartTime(); 
if (HttpRuntime.IsLegacyCas) 
{ 
this.ResumeStepsWithAssert(error); 
} 
else 
{ 
this.ResumeSteps(error); 
} 
} 
} 

private void ResumeSteps(Exception error) 
{ 
this._application.ResumeStepsFromThreadPoolThread(error); 
} 

[PermissionSet(SecurityAction.Assert, Unrestricted=true)] 
private void ResumeStepsWithAssert(Exception error) 
{ 
this.ResumeSteps(error); 
} 

void HttpApplication.IExecutionStep.Execute() 
{ 
HttpContext context = this._application.Context; 
IHttpHandler handler = context.Handler; 
if (EtwTrace.IsTraceEnabled(4, 4)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_ENTER, context.WorkerRequest); 
} 
if ((handler != null) && HttpRuntime.UseIntegratedPipeline) 
{ 
IIS7WorkerRequest workerRequest = context.WorkerRequest as IIS7WorkerRequest; 
if ((workerRequest != null) && workerRequest.IsHandlerExecutionDenied()) 
{ 
this._sync = true; 
HttpException exception = new HttpException(0x193, SR.GetString("Handler_access_denied")); 
exception.SetFormatter(new PageForbiddenErrorFormatter(context.Request.Path, SR.GetString("Handler_access_denied"))); 
throw exception; 
} 
} 
if (handler == null) 
{ 
this._sync = true; 
} 
else if (handler is IHttpAsyncHandler) 
{ 
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) handler; 
this._sync = false; 
this._handler = handler2; 
IAsyncResult result = handler2.BeginProcessRequest(context, this._completionCallback, null); 
if (result.CompletedSynchronously) 
{ 
this._sync = true; 
this._handler = null; 
try 
{ 
handler2.EndProcessRequest(result); 
} 
finally 
{ 
context.Response.GenerateResponseHeadersForHandler(); 
} 
if (EtwTrace.IsTraceEnabled(4, 4)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_LEAVE, context.WorkerRequest); 
} 
} 
} 
else 
{ 
this._sync = true; 
context.SyncContext.SetSyncCaller(); 
try 
{ 
handler.ProcessRequest(context); 
} 
finally 
{ 
context.SyncContext.ResetSyncCaller(); 
if (EtwTrace.IsTraceEnabled(4, 4)) 
{ 
EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_LEAVE, context.WorkerRequest); 
} 
context.Response.GenerateResponseHeadersForHandler(); 
} 
} 
} 

// Properties 
bool HttpApplication.IExecutionStep.CompletedSynchronously 
{ 
get 
{ 
return this._sync; 
} 
} 

bool HttpApplication.IExecutionStep.IsCancellable 
{ 
get 
{ 
return !(this._application.Context.Handler is IHttpAsyncHandler); 
} 
} 
}

In the code we see handler2.BeginProcessRequest (context, this._completionCallback, null); . handler. ProcessRequest (context); Are these two lines of code familiar?

Let's go back to HttpApplication's BeginProcessRequest method

IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) 
{ 
this._context = context; 
this._context.ApplicationInstance = this; 
this._stepManager.InitRequest(); 
this._context.Root(); 
HttpAsyncResult result = new HttpAsyncResult(cb, extraData); 
this.AsyncResult = result; 
if (this._context.TraceIsEnabled) 
{ 
HttpRuntime.Profile.StartRequest(this._context); 
} 
this.ResumeSteps(null); 
return result; 
}

It calls the ResumeSteps method

internal static IHttpHandler GetApplicationInstance(HttpContext context) 
{ 
if (_customApplication != null) 
{ 
return _customApplication; 
} 
if (context.Request.IsDebuggingRequest) 
{ 
return new HttpDebugHandler(); 
} 
_theApplicationFactory.EnsureInited(); 
_theApplicationFactory.EnsureAppStartCalled(context); 
return _theApplicationFactory.GetNormalApplicationInstance(context); 
}
0
Going back to our previous ApplicationStepManager ResumeSteps method, there is a sentence

error = application.ExecuteStep(this._execSteps[this._currentStepIndex], ref completedSynchronously);

Ahhpaplication's ExecuteStep method

internal Exception ExecuteStep(IExecutionStep step, ref bool completedSynchronously) 
{ 
Exception exception = null; 
try 
{ 
try 
{ 
if (step.IsCancellable) 
{ 
this._context.BeginCancellablePeriod(); 
try 
{ 
step.Execute(); 
} 
finally 
{ 
this._context.EndCancellablePeriod(); 
} 
this._context.WaitForExceptionIfCancelled(); 
} 
else 
{ 
step.Execute(); 
} 
if (!step.CompletedSynchronously) 
{ 
completedSynchronously = false; 
return null; 
} 
} 
catch (Exception exception2) 
{ 
exception = exception2; 
if (ImpersonationContext.CurrentThreadTokenExists) 
{ 
exception2.Data["ASPIMPERSONATING"] = string.Empty; 
} 
if ((exception2 is ThreadAbortException) && ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) == ThreadState.Running)) 
{ 
exception = null; 
this._stepManager.CompleteRequest(); 
} 
} 
catch 
{ 
} 
} 
catch (ThreadAbortException exception3) 
{ 
if ((exception3.ExceptionState != null) && (exception3.ExceptionState is CancelModuleException)) 
{ 
CancelModuleException exceptionState = (CancelModuleException) exception3.ExceptionState; 
if (exceptionState.Timeout) 
{ 
exception = new HttpException(SR.GetString("Request_timed_out"), null, 0xbb9); 
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TIMED_OUT); 
} 
else 
{ 
exception = null; 
this._stepManager.CompleteRequest(); 
} 
Thread.ResetAbort(); 
} 
} 
completedSynchronously = true; 
return exception; 
}

Is the Execute method that actually implements IExecutionStep.

Through the above analysis, we can simply understand that asp.net is mainly registered and invoked by ApplicationStepManager in the pipeline mode. The PipelineStepManager and ApplicationStepManager structures in integration mode are similar.

Individual here just throw brick to attract jade, hope everyone clap brick.

Related articles: