The previous MVC series (5): detailed analysis of Http Pipeline (below)

Source: Internet
Author: User

Article content

Next, we will explain in this article that Pipeline is a variety of events executed. We know that in the Init method of the custom HttpModule, we can add our own events, for example, the following code:

public class Test : IHttpModule{    public void Init(HttpApplication context)    {        context.BeginRequest += new EventHandler(context_BeginRequest);        context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);    }    void context_AuthenticateRequest(object sender, EventArgs e)    {        throw new NotImplementedException();    }    void context_BeginRequest(object sender, EventArgs e)    {        throw new NotImplementedException();    }}

The added code will execute these events when executed in the Pipeline. How can we execute these events and in what order? Before learning about this, let's take a look at how these events are exposed in HttpApplication and where they are stored? After reading the source code of HttpApplication, we can see that all events are exposed in the following form. Select two of them for a look:

/// <devdoc><para>[To be supplied.]</para></devdoc> public event EventHandler BeginRequest {    add { AddSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }    remove { RemoveSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }}  /// <devdoc><para>[To be supplied.]</para></devdoc> public event EventHandler AuthenticateRequest {    add { AddSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest); }     remove { RemoveSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest); }}

We can find that all events are added by calling the AddSyncEventHookup method. The first parameter is the value of Event + Event name. How can this value be obtained? We can find the declared code:

private static readonly object EventDisposed = new object();private static readonly object EventErrorRecorded = new object();private static readonly object EventPreSendRequestHeaders = new object(); private static readonly object EventPreSendRequestContent = new object(); private static readonly object EventBeginRequest = new object(); private static readonly object EventAuthenticateRequest = new object();private static readonly object EventDefaultAuthentication = new object(); private static readonly object EventPostAuthenticateRequest = new object();private static readonly object EventAuthorizeRequest = new object();private static readonly object EventPostAuthorizeRequest = new object();private static readonly object EventResolveRequestCache = new object(); private static readonly object EventPostResolveRequestCache = new object();private static readonly object EventMapRequestHandler = new object(); private static readonly object EventPostMapRequestHandler = new object(); private static readonly object EventAcquireRequestState = new object();private static readonly object EventPostAcquireRequestState = new object(); private static readonly object EventPreRequestHandlerExecute = new object();private static readonly object EventPostRequestHandlerExecute = new object();private static readonly object EventReleaseRequestState = new object();private static readonly object EventPostReleaseRequestState = new object(); private static readonly object EventUpdateRequestCache = new object();private static readonly object EventPostUpdateRequestCache = new object(); private static readonly object EventLogRequest = new object(); private static readonly object EventPostLogRequest = new object();private static readonly object EventEndRequest = new object(); 

Combined with the add and remove methods, we can boldly guess that these values should be used as key values. We will first read the 2nd parameters and then verify our conjecture, the 2nd parameters are RequestNotification of Enumeration type. Here we guess that all events should be placed in a unified place and then distinguished by this enumeration. Let's first look at the code of this enumeration class:

[Flags]public enum RequestNotification{    BeginRequest = 1,    AuthenticateRequest = 2,    AuthorizeRequest = 4,    ResolveRequestCache = 8,    MapRequestHandler = 16,    AcquireRequestState = 32,    PreExecuteRequestHandler = 64,    ExecuteRequestHandler = 128,    ReleaseRequestState = 256,    UpdateRequestCache = 512,    LogRequest = 1024,    EndRequest = 2048,    SendResponse = 536870912,}

Did you find anything? Although the Flags mark is used to record for different or query, the enumeration type here seems to be less, I checked the code carefully and found that all the events starting with Post did not appear in this enumeration class. Why? How are these events declared? Return to the HttpApplication class to view the code,

/// <devdoc><para>[To be supplied.]</para></devdoc>public event EventHandler PostAuthenticateRequest {    add { AddSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true); }     remove { RemoveSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true); }} 

Suddenly I found that the AddSyncEventHookup method has an additional parameter "true". What does this mean? Let's check this to see whether it is.

internal void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification) {     AddSyncEventHookup(key, handler, notification, false); }private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification) {     ThrowIfEventBindingDisallowed();     // add the event to the delegate invocation list     // this keeps non-pipeline ASP.NET hosts working    Events.AddHandler(key, handler);    // For integrated pipeline mode, add events to the IExecutionStep containers only if     // InitSpecial has completed and InitInternal has not completed.    if (IsContainerInitalizationAllowed) {         // lookup the module index and add this notification         PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey);        //WOS 1985878: HttpModule unsubscribing an event handler causes AV in Integrated Mode         if (container != null) {#if DBG            container.DebugModuleName = CurrentModuleCollectionKey;#endif             SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler)handler);            container.AddEvent(notification, isPostNotification, step);         }     }} 

In the past, this method had two retries, and 2nd were added with a Boolean parameter isPostNotification. That is to say, this parameter saves a lot of declarations of enumeration types.

Let's take a closer look at the above Code by calling Events at the beginning. the AddHandler method adds the Event to the Events set. At the same time, this key is the Event + Event name we guessed above, through annotations, we can also know that Events are prepared for non-pipeline. In combination with the comments above the if statement, we found that in IIS7 integration mode, these events are added to another place (by encapsulating the event hanlder into the SyncEventExecutionStep type, and then calling container. the AddEvent method adds the event to another place), that is, the Events set above the if is used in the IIS6 and IIS7 classic modes, the data in the following iner is used in the IIS7 integration mode.

Note: In Classic mode, Event + Event name is used as the key value, but in integration mode, RequestNotification enumeration +IsPostNotificationThe Boolean value set is used as the key value. Pay attention to the difference.

Where is the IIS7 integration mode stored? Through the GetModuleContainer method, we can find that these events are stored in the ModuleContainers attribute of HttpApplication. The type of this attribute is PipelineModuleStepContainer [], and the number is the number of HttpModules, that is to say, the events added by each HttpModule on HttpApplication are stored in their respective PipelineModuleStepContainer containers.

Now let's look back at the code of the previous chapter:

// Construct the execution steps array if (HttpRuntime.UseIntegratedPipeline) {     _stepManager = new PipelineStepManager(this);} else {    _stepManager = new ApplicationStepManager(this);} _stepManager.BuildSteps(_resumeStepsWaitCallback);

The integration mode is different from the classic mode (or IIS6). The BuildSteps method of this class is used to create an orderly ExecutionStep, these include events and other operations interspersed between different time periods. The most important operations should be known before, for example, which cycle can determine which HttpHandler to use, and the period in which the BeginProcessRequest method of the HttpHandler is executed.

 

Because different StepManager processing methods are different, Let's first look at the IIS6 and IIS7 Classic Mode processing code:

internal override void BuildSteps(WaitCallback stepCallback ) {     ArrayList steps = new ArrayList();    HttpApplication app = _application;    bool urlMappingsEnabled = false;     UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;    urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count > 0 );      steps.Add(new ValidateRequestExecutionStep(app));    steps.Add(new ValidatePathExecutionStep(app));     if (urlMappingsEnabled)        steps.Add(new UrlMappingsExecutionStep(app)); // url mappings     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 MapHandlerExecutionStep(app));     // map handler     app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);    app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);     app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);     app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);    steps.Add(new CallHandlerExecutionStep(app));  // execute handler     app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);    app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);    app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);    steps.Add(new CallFilterExecutionStep(app));  // filtering     app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);    app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);     _endRequestStepIndex = steps.Count;     app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);    steps.Add(new NoopExecutionStep()); // the last is always there     _execSteps = new IExecutionStep[steps.Count];    steps.CopyTo(_execSteps);     // callback for async completion when reposting to threadpool thread    _resumeStepsWaitCallback = stepCallback; } 

Looking at whether the above Code is familiar, many articles about the Declaration cycle will mention more than 20 events (such as BeginRequest and EndRequest ), let's take a look at what the complete functions of this method are. The summary is as follows:

Note that all executeionsteps are saved in the private field _ execSteps of the ApplicationStepManager instance, the BeginProcessRequest method of HttpApplication will eventually execute these operations through the ResumeSteps method of the Instance (that is, the events we call and four special Steps ).

OK. Let's continue to see how IIS7 works in the integration mode. The Code is as follows:

internal override void BuildSteps(WaitCallback stepCallback) {    Debug.Trace("PipelineRuntime", "BuildSteps");     //ArrayList steps = new ArrayList();    HttpApplication app = _application;    // add special steps that don't currently     // correspond to a configured handler     IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);     // implicit map step     app.AddEventMapping(        HttpApplication.IMPLICIT_HANDLER,        RequestNotification.MapRequestHandler,        false, materializeStep);     // implicit handler routing step     IExecutionStep handlerStep = new CallHandlerExecutionStep(app);     app.AddEventMapping(         HttpApplication.IMPLICIT_HANDLER,        RequestNotification.ExecuteRequestHandler,        false, handlerStep);     // add implicit request filtering step    IExecutionStep filterStep = new CallFilterExecutionStep(app);      // normally, this executes during UpdateRequestCache as a high priority module    app.AddEventMapping(         HttpApplication.IMPLICIT_FILTER_MODULE,        RequestNotification.UpdateRequestCache,        false, filterStep);     // for error conditions, this executes during LogRequest as a high priority module    app.AddEventMapping(         HttpApplication.IMPLICIT_FILTER_MODULE,         RequestNotification.LogRequest,        false, filterStep);     _resumeStepsWaitCallback = stepCallback;}

The above code is different from IIS6 in two places:

There is also a very skillful code: none of the above four Steps are accurate cycles. For example, CallHandlerExecutionStep should be between loading RequestNotification's enumerated values PreExecuteRequestHandler and ExecuteRequestHandler. Why? Because the CallHandlerExecutionStep itself is only a special step without exposing the event, so it does not exist in the enumeration. What should I do? Let's look back at the first parameter of the AddEventMapping method, which represents the name of the HttpModule. Check the code and find that when executing all the events, it traverses all the HttpModuel name sets and then executes all the BeginRequest events first, then all the AuthenticateRequest events, and so on. Can we forge an HttpModule name and pass it as a parameter to the AddEventMapping method, the answer is yes. Looking at the code above, we found that two forged names are constant strings HttpApplication. IMPLICIT_FILTER_MODULE (value: AspNetFilterModule) and HttpApplication. IMPLICIT_HANDLER (value: ManagedPipelineHandler), and because all the events in other HttpModule have been loaded, the two forged HttpModule are placed in the most Later, when executing the ExecuteRequestHandler class event, the last event must be the event that spoofs the HttpModule, and there is no other event in the forged HttpModule, therefore, the execution Effect of ExecutionStep is the same as that of CallHandlerExecutionStep in iis6. in this way, the same goal is achieved through a very strange technique.

Finally, we will summarize that in the classic IIS6 and IIS7 modes, the Event + Event name is used as the key to save all Events in the Events attribute object of HttpApplication, then it is assembled in a unified order in BuildSteps, loaded with four special executionsteps in the middle, and finally executed in a unified manner; in IIS7 integration mode, by using the HttpModule name + RequestNotification enumeration value as the key, all events are saved in the ModuleContainers attribute object of HttpApplication, and then four special executionsteps are loaded in BuildSteps by forging the HttpModule name, finally, all the httpmodules are traversed in the order of enumeration types to execute these events in order. You can write a custom HttpModuel to execute these events to see how they work.

The complete Pipeline diagram is as follows:

 

 

 

Finally, you must note that HttpApplication is not created by HttpRuntime, and HttpRuntime only requests HttpApplicationFactory to return an HttpApplication object. After receiving the request, HttpApplicationFactory checks whether there are existing and idle objects. If yes, It retrieves an HttpApplication object and returns it to HttpRuntime. If no, create an HttpApplication object to HttpRunTime.

 

References:

Http://msdn.microsoft.com/en-us/library/bb470252.aspx

Http://www.cnblogs.com/zhaoyang/archive/2011/11/16/2251200.html

Http://www.brainbell.com/tutorials/ASP/The_ASP.NET_Pipeline.html

Http://learn.iis.net/page.aspx/243/aspnet-integration-with-iis/

Synchronization and recommendation

This article has been synchronized to the Directory Index: the previous MVC Series

The previous MVC series of articles, including original articles, translations, reposts, and other types of articles. If they are useful to you, we recommend that you support them to give uncle the motivation to write.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.