• ASP.NET初始化流程分析2


    上一篇讲了从创建应用程序域到创建ISAPIRuntime实例的过程,本篇继续讲Asp.net处理第一次请求的必要的初始化过程。

    ISAPIRuntime分析

    ISAPIRuntime在System.Web.Hosting中实现,它的ProcessRequest是我们处理web请求的入口。

        public int ProcessRequest(IntPtr ecb, int iWRType) {
            IntPtr pHttpCompletion = IntPtr.Zero;
            if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) {
                pHttpCompletion = ecb;
                ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion);
            }
            ISAPIWorkerRequest wr = null;
            try {
                bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
                wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
                wr.Initialize();
                String wrPath = wr.GetAppPathTranslated();
                String adPath = HttpRuntime.AppDomainAppPathInternal;            
                if (adPath == null ||StringUtil.EqualsIgnoreCase(wrPath, adPath)) {
                    HttpRuntime.ProcessRequestNoDemand(wr);
                    return 0;
                }
                else {
                     HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString(SR.Hosting_Phys_Path_Changed, adPath, wrPath));
                     return 1;
                }
            }
            catch(Exception e) {
                try {
                    WebBaseEvent.RaiseRuntimeError(e, this);
                } catch {}
                if (wr != null && wr.Ecb == IntPtr.Zero) {
                    if (pHttpCompletion != IntPtr.Zero) {
                        UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion);
                    }
                    if (e is ThreadAbortException) {
                        Thread.ResetAbort();
                    }                   
                    return 0;
                 }
                throw;
            }
        }

    注意方法的IntPtr类型的参数ecb, 它是一个非托管的指针,用于传递一些必须的数据,以及最终将Response的内容返回给非托管环境ISAPI(异步方式),然后呈现给Client用户。方法中调用ISAPIWorkerRequest的静态方法CreateWorkerRequest而创建ISAPIWorkerRequest对象实例,参数分别为ecb和代表WorkerRequest类型的int参数iWRType,通过判断ecb和type类型的具体内容,来决定创建什么类型的WorkerRequest(上述类型的ISPAIWorkerRequest都继承于HttpWorkerRequest),上面的代码可以看出对不同版本的IIS进行了不同的包装,通过其Initialize方法来初始化一些基本的信息(比如:contentType, querystring的长度,filepath等相关信息)。然后调用HttpRuntime.ProcessRequestNoDemand(wr)转入HttpRuntime处理请求,最终体现在调用ProcessRequestInternal方法上。

    HttpRuntime分析

    Httpruntime在System.Web下实现,我们来看其处理请求的ProcessRequestInternal方法。

        private void ProcessRequestInternal(HttpWorkerRequest wr) {
            Interlocked.Increment(ref _activeRequestCount);
            if (_disposingHttpRuntime) {
                try {
                    wr.SendStatus(503, "Server Too Busy");
                    wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                    byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
                    wr.SendResponseFromMemory(body, body.Length);
                    wr.FlushResponse(true);
                    wr.EndOfRequest();
                } finally {
                    Interlocked.Decrement(ref _activeRequestCount);
                }
                return;
            }
            HttpContext context;
            try {
                context = new HttpContext(wr, false);
            }
            catch {
                try {
                    wr.SendStatus(400, "Bad Request");
                    wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                    byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
                    wr.SendResponseFromMemory(body, body.Length);
                    wr.FlushResponse(true);
                    wr.EndOfRequest();
                    return;
                } finally {
                    Interlocked.Decrement(ref _activeRequestCount);
                }
            }
            wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context);
            HostingEnvironment.IncrementBusyCount();
            try {
                try {
                    EnsureFirstRequestInit(context);
                }
                catch {
                    if (!context.Request.IsDebuggingRequest) {
                        throw;
                    }
                }
                context.Response.InitResponseWriter();
                IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
                if (app == null)
                    throw new HttpException(SR.GetString(SR.Unable_create_app_object));
                if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start");
                if (app is IHttpAsyncHandler) {
                    IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
                    context.AsyncAppHandler = asyncHandler;
                    asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
                }
                else {
                    app.ProcessRequest(context);
                    FinishRequest(context.WorkerRequest, context, null);
                }
            }
            catch (Exception e) {
                context.Response.InitResponseWriter();
                FinishRequest(wr, context, e);
            }
        }

    该方法中创建了熟悉的HttpContext并同时创建了HttpRequest与HttpResponse

    internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) {
    
            _wr = wr;
    
            Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
    
    if (initResponseWriter)
    
                _response.InitResponseWriter();
    
            PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
    
        }

    然后通过HttpApplicationFactory的GetApplicationInstance静态方法,获取我们熟悉的HttpApplication对象实例(注:HttpApplication对象是继承IHttpAsyncHandler,而IHttpAsyncHandler又继承于IHttpHandler),然后执行调用BeginProcessRequest方法。至此正式进入了HttpApplication对象的创建以及大家熟知的HttpApplication以后的生命周期了。

    HttpApplicationFactory分析

    HttpApplicationFactory在System.Web下实现。

    查看HttpApplicationFactory用来创建Httpapplication的GetApplicationInstance方法。

        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);
        }

    该方法有三个步骤:首先是EnsureInited,会检查是否已经初始化,如果没有会调用Init方法先获取global.asax文件的完整路径,然后调用CompileApplication()对global.asax进行编译,Init方法如下。

        private void Init() {
            if (_customApplication != null)
                return;
            try {
                try {
                    _appFilename = GetApplicationFile();
                    CompileApplication();
                 }
                finally {
                    SetupChangesMonitor();
                }
            }
            catch {
                throw;
            }
    }

    然后是EnsureAppStartCalled方法如果未开始启动会调用FireApplicationOnStart。

       private void EnsureAppStartCalled(HttpContext context) {
            if (!_appOnStartCalled) {
                lock (this) {
                    if (!_appOnStartCalled) {
                        using (new DisposableHttpContextWrapper(context)) {
                            WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
                            FireApplicationOnStart(context);
                            }
                        _appOnStartCalled = true;
                    }
                }
            }
        }
    
        private void FireApplicationOnStart(HttpContext context) {
            if (_onStartMethod != null) {
                HttpApplication app = GetSpecialApplicationInstance();
                app.ProcessSpecialRequest(context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null);
                RecycleSpecialApplicationInstance(app);
            }
        }

    这里创建特定的HttpApplication实例,触发ApplicationOnStart事件(会执行global.asax中的Application_Start方法)。然后在处理完事件以后就立即被回收掉,因为系统初始化只需要一次。

    最后是GetNormalApplicationInstance,如果在有空闲的HttpApplication实例,就直接用,如果没有就新创建,然后调用InitInternal方法进行初始化相关的内容,最后返回该HttpApplication实例。

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

    HttpApplication分析

    HttpApplication在System.Web下实现,首先查看HttpApplication的InitInternal方法,该方法用于初始化。

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

    该代码主要有2个功能,一个是初始化大家熟悉的HttpModules,一个是通过BuildSteps执行多个生命周期事件的处理函数。通过上面的代码我们可以看出,每个功能都有一个特殊判断,判断IIS是否是IIS7的集成模式,如果是就有特殊的步骤,如果不是就走一般的步骤(两者直接的差异分别是:经典模式初始化HttpModules的时候会从网站配置的Modules里读取,集成模式会预加载CLR和大量Modules,比如加载服务器上设置的HttpModules;另外在BuildSteps的时候, IIS7集成模式走的是自己特殊的流程)。

    总结一下,InitInternal方法的主要功能如下:

    InitModules():根据Web.Config的设置,加载相应的HttpModules。

    InitIntegratedModules():会加载IIS7集成模式下在服务器上设定的HttpModuels和Web.config里system.webserver下的HttpModuels。

    HookupEventHandlersForAppplicationAndModules:绑定HttpApplication实例中相应的事件处理函数(在Global.asax中定义的事件处理函数)。

    创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中(包括HttpModules中定义的周期事件处理函数和查找匹配的HttpHandler、执行HttpHandler的方法以及过滤输出等特殊事件),等待回调时执行。从这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。

    在 HttpApplication的事件如下形式定义:

        public event EventHandler BeginRequest {
            add { AddSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
            remove { RemoveSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
        }

    所有的事件都是调用AddSyncEventHookup方法添加进去的,其中第一个参数是以Event+事件名称的值。

        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(); Events.AddHandler(key, handler); if (IsContainerInitalizationAllowed) { PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey); if (container != null) { SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler)handler); container.AddEvent(notification, isPostNotification, step); } } }

    经典模式下在初始化HttpModlue的时候通过调用Events.AddHandler方法,将事件添加到Events集合里,同时这个key就是Event+事件名称,而集成模式下这些事件是添加到另外一个地方的(通过将事件hanlder包装成SyncEventExecutionStep类型,然后调用container.AddEvent方法将事件添加到另外一个地方),也就是说if上面的Events集合是给经典模式用的,下面的Container里的数据是给集成模式用的,这些事件是存放在HttpApplication的ModuleContainers属性里,这个属性的类型是PipelineModuleStepContainer[],个数就是HttpModules的个数,也就是说每个HttpModule在HttpApplication上添加的事件都放在各自的PipelineModuleStepContainer容器里。

        private PipelineModuleStepContainer[] ModuleContainers {
            get {
                if (_moduleContainers == null) {
                    Debug.Assert(_moduleIndexMap != null && _moduleIndexMap.Count > 0, "_moduleIndexMap != null && _moduleIndexMap.Count > 0");
                    _moduleContainers = new PipelineModuleStepContainer[_moduleIndexMap.Count];
                    for (int i = 0; i < _moduleContainers.Length; i++) {
                        _moduleContainers[i] = new PipelineModuleStepContainer();
                    }
                }
                return _moduleContainers;
            }
    }

    StepManager分析

    集成模式和经典模式(或IIS6)使用的是不同的StepManager,这个类的BuildSteps方法就是为了创建有序的ExecutionStep,其中包括各种事件的事情以及其它在各时间周期之间穿插的操作,最主要的操作,大家以前就应该知道的,比如哪个周期可以判定使用哪个HttpHandler,以及在哪个周期内执行这个HttpHandler的BeginProcessRequest方法。StepManager的具体实现类(ApplicationStepManager、PipelineStepManager)和HttpApplication类在同一个文件中定义。

    ApplicationStepManager的BuildSteps方法(用于经典模式)

        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(app.CreateImplicitAsyncPreloadExecutionStep());        
            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);
            _resumeStepsWaitCallback = stepCallback;
        }
    
        private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
            AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
            if (asyncHandler != null) {
                asyncHandler.CreateExecutionSteps(this, steps);
            }
            EventHandler handler = (EventHandler)Events[eventIndex];
            if (handler != null) {
                Delegate[] handlers = handler.GetInvocationList();
                for (int i = 0; i < handlers.Length; i++)  {
                    steps.Add(new SyncEventExecutionStep(this, (EventHandler)handlers[i]));
                }
             }
        }

    这个方法的完整功能归纳总结有以下几点:

    对请求的Request进行验证,ValidateRequestExecutionStep。

    对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。 

    如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。

    执行事件处理函数,比如将BeginRequest、AuthenticateRequest转化成可执行ExecutionStep在正式调用时候执行。

    在这多个个事件操作处理期间,根据不同的时机加了4个特殊的ExecutionStep。

    MapHandlerExecutionStep:查找匹配的HttpHandler

    CallHandlerExecutionStep:执行HttpHandler的BeginProcessRequest

    CallFilterExecutionStep:调用Response.FilterOutput方法过滤输出

    NoopExecutionStep:空操作,留着以后扩展用

    所有的ExecuteionStep都保存在ApplicationStepManager实例下的私有字段_execSteps里,而HttpApplication的BeginProcessRequest方法最终会通过该实例的ResumeSteps方法来执行这些操作。

    PipelineStepManager的BuildSteps(用于集成模式)

        internal override void BuildSteps(WaitCallback stepCallback) {
            Debug.Trace("PipelineRuntime", "BuildSteps");
            HttpApplication app = _application;
            IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);
            app.AddEventMapping(ttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.MapRequestHandler,
                        false, materializeStep);
            app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.ExecuteRequestHandler,
                        false, app.CreateImplicitAsyncPreloadExecutionStep());
            IExecutionStep handlerStep = new CallHandlerExecutionStep(app);
            app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.ExecuteRequestHandler,
                        false, handlerStep);
            IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app);
            app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.EndRequest,
                        true, webSocketsStep);
            IExecutionStep filterStep = new CallFilterExecutionStep(app);
            app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
                        RequestNotification.UpdateRequestCache,
                        false, filterStep);
            app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
                        RequestNotification.LogRequest,
                        false, filterStep);
            _resumeStepsWaitCallback = stepCallback;
        }
    
        private void AddEventMapping(string moduleName,RequestNotification requestNotification, bool isPostNotification,IExecutionStep step) {
            ......
            PipelineModuleStepContainer container = GetModuleContainer(moduleName);
            container.AddEvent(requestNotification, isPostNotification, step);
        }

    以上代码有2个地方和经典模式不相同:

    集成模式没有使用MapHandlerExecutionStep来装载ExecutionStep(也就是查找对应的HttpHandler),而是通过MaterializeHandlerExecutionStep类来获得HttpHandler,方式不一样。

    集成模式是通过HttpApplication的AddEventMapping方法来添加事件的,从而将事件加入到前面所说的ModuleContainers容器。

    总结一下,在经典模式下,是用 Event+事件名称做key将所有事件的保存在HttpApplication的Events属性对象里,然后在BuildSteps里统一按照顺序组装,中间加载4个特殊的ExecutionStep,最后在统一执行;在集成模式下,是通过HttpModule名称+RequestNotification枚举值作为key将所有的事件保存在HttpApplication的ModuleContainers属性对象里,然后也在BuildSteps里通过伪造HttpModule名称加载那4个特殊的ExecutionStep,最后按照枚举类型的顺序,遍历所有的HttpModule按顺序来执行这些事件,可以自行编写一个自定义的HttpModuel来执行这些事件看看效果如何。

    下面是总结一下处理第一次请求的大体处理流程。

  • 相关阅读:
    Codeforces 543E. Listening to Music
    UOJ #138. 【UER #3】开学前的涂鸦
    bzoj 3569: DZY Loves Chinese II
    bzoj 2428: [HAOI2006]均分数据
    bzoj 4589: Hard Nim
    UOJ #119. 【UR #8】决战圆锥曲线
    spoj5973
    codeforces555E
    poj1275
    bzoj4152
  • 原文地址:https://www.cnblogs.com/ssxg/p/7085877.html
Copyright © 2020-2023  润新知