【转】ASP.NET页面请求管道流程
2012-07-09 19:04 cnb_mtime 阅读(466) 评论(0) 编辑 收藏 举报[转]http://blog.csdn.net/tycyd/article/details/3755708
当客户端向web服务器请求*.aspx的页面文件时,这个http请求也会被inetinfo.exe进程截获(www服务),它判断文件后缀之后,把这个请求转交给ASPNET_ISAPI.DLL而ASPNET_ISAPI.DLL则会通过一个Http PipeLine的管道,将这个http请求发送给ASPNET_WP.EXE进程,当这个HTTP请求进入ASPNET_WP.EXE进程之后,asp.net framework就会通过HttpRuntime来处理这个Http请求,处理完毕后将结果返回给客户端。 大致流程如下: HttpRequest-->inetinfo.exe->ASPNET_ISAPI.DLL-->Http Pipeline-->ASPNET_WP.EXE-->HttpRuntime-->HttpApplication Factory-->HttpApplication-->HttpModule-->HttpHandler Factory-->HttpHandler-->HttpHandler.ProcessRequest() 通过反编译器,我们来简单看看它是如何实现的(.Net Version=2.0.0.0) 工作进程(IIS5中是ASPNET_WP.EXE,IIS6中是W3WP.EXE)寄宿.NET运行时和ISAPI DLL,它(工作进程)通过调用COM对象的一个小的非托管接口最终将调用发送到ISAPIRuntime类的一个实例上。进入运行时的第一个入口就是这个没有被文档记载的类,这个类实现了IISAPIRuntime接口(对于调用者说明来说,这个接口是一个COM接口)这个基于Iunknown的底层 COM接口是从ISAPI扩展到ASP.NET的一个预定的接口 1. [ComImport, Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 2. public interface IISAPIRuntime 3. { 4. void StartProcessing(); 5. void StopProcessing(); 6. [return: MarshalAs(UnmanagedType.I4)] 7. int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel); 8. void DoGCCollect(); 9. } 代码展示了IISAPIRuntime接口和它的调用签名代码 以下是ISAPIRuntime类 1. public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IRegisteredObject 2. { 3. // Fields 4. private static int _isThisAppDomainRemovedFromUnmanagedTable; 5. private static string s_thisAppDomainsIsapiAppId; 6. private const int WORKER_REQUEST_TYPE_IN_PROC = 0; 7. private const int WORKER_REQUEST_TYPE_IN_PROC_VERSION_2 = 2; 8. private const int WORKER_REQUEST_TYPE_OOP = 1; 9. 10. // Methods 11. [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Minimal), SecurityPermission(SecurityAction.Demand, Unrestricted=true)] 12. public ISAPIRuntime(); 13. public void DoGCCollect(); 14. public override object InitializeLifetimeService(); 15. public int ProcessRequest(IntPtr ecb, int iWRType); 16. internal static void RemoveThisAppDomainFromUnmanagedTable(); 17. internal void SetThisAppDomainsIsapiAppId(string appId); 18. public void StartProcessing(); 19. public void StopProcessing(); 20. void IRegisteredObject.Stop(bool immediate); 21. } 我们知道了接口,那具体处理,就让我们看一下ISAPIRuntime.ProcessRequest函数 1. public int ProcessRequest(IntPtr ecb, int iWRType) 2. { 3. 。。。。。。 4. 。。。。。。 5. ISAPIWorkerRequest wr = null; 6. try 7. { 8. 。。。。。。 9. wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP); 10. wr.Initialize(); 11. string appPathTranslated = wr.GetAppPathTranslated(); 12. string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal; 13. if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal)) 14. { 15. HttpRuntime.ProcessRequestNoDemand(wr); 16. return 0; 17. } 18. 。。。。 19. return 1; 20. } 21. catch (Exception exception) 22. { 23. 。。。。 24. } 25. } 26. 27. 在这个函数中首先创建了一个ISAPIWorkerRequest实例,即是被.Net`经过一层封装的Http请求, 随后继续调用HttpRuntime.ProcessRequestNoDemand(wr);进行处理 1. internal static void ProcessRequestNoDemand(HttpWorkerRequest wr) 2. { 3. RequestQueue queue = _theRuntime._requestQueue; 4. if (queue != null) 5. { 6. wr = queue.GetRequestToExecute(wr); 7. } 8. if (wr != null) 9. { 10. 。。。。 11. 。。。。 12. ProcessRequestNow(wr); 13. } 14. } 如果请求有多个,则进行请求队列的筛选,取得要处理的请求,继续转交ProcessRequestNow(wr)处理 1. internal static void ProcessRequestNow(HttpWorkerRequest wr) 2. { 3. _theRuntime.ProcessRequestInternal(wr); 4. } 调用当前应用程序域的方法_theRuntime.ProcessRequestInternal(wr) 1. private void ProcessRequestInternal(HttpWorkerRequest wr) 2. { 3. HttpContext context; 4. try 5. { 6. context = new HttpContext(wr, false); 7. } 8. catch 9. { 10. wr.SendStatus(400, "Bad Request"); 11. .......... 12. return; 13. } 14. ......... 15. try 16. { 17. ........ 18. context.Response.InitResponseWriter(); 19. IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); 20. if (applicationInstance == null) 21. { 22. throw new HttpException(SR.GetString("Unable_create_app_object")); 23. } 24. ........ 25. if (applicationInstance is IHttpAsyncHandler) 26. { 27. IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; 28. context.AsyncAppHandler = handler2; 29. handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); 30. } 31. else 32. { 33. applicationInstance.ProcessRequest(context); 34. this.FinishRequest(context.WorkerRequest, context, null); 35. } 36. } 37. catch (Exception exception) 38. { 39. ......... 40. } 41. } 在这个方法内主要完成了 1.HttpContext的创建(非常重要的实例,提供了绝大多数的接口) 2.通过HttpApplicationFactory.GetApplicationInstance(context)生成或回收HttpApplicaiton实例(确切地说实现IHttpAsyncHandler接口或者IHttpHandler接口的实例),并转交请求处理 handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); 或者applicationInstance.ProcessRequest(context); 其中,HttpApplicationFactory.GetApplicationInstance(context),若Http请求是第一个到达的时候,将会初始化HttpApplicationFactory 1. internal static IHttpHandler GetApplicationInstance(HttpContext context) 2. { 3. 。。。。。。。。。。。 4. _theApplicationFactory.EnsureInited(); 5. _theApplicationFactory.EnsureAppStartCalled(context); 6. return _theApplicationFactory.GetNormalApplicationInstance(context); 7. } 8. _theApplicationFactory.EnsureInited();中将判断工厂是否被初始化,若没则会先进行ApplicationFactory初始化 _theApplicationFactory.EnsureAppStartCalled(context);创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的 Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。 ApplicationFactory初始化最终调用Init() 方法 1. private void Init() 2. { 3. if (_customApplication == null) 4. { 5. try 6. { 7. try 8. { 9. this._appFilename = GetApplicationFile(); 10. this.CompileApplication(); 11. } 12. 。。。。。 13. } 14. catch 15. { 16. throw; 17. } 18. } 19. } 20. 21. this._appFilename = GetApplicationFile();取得global.asax文件路径 1. internal static string GetApplicationFile() 2. { 3. return Path.Combine(HttpRuntime.AppDomainAppPathInternal, "global.asax"); 4. } 可见在.Net中global.asax文件名是被规定死的。 this.CompileApplication也是比较重要的 1. private void CompileApplication() 2. { 3. this._theApplicationType = BuildManager.GetGlobalAsaxType(); 4. BuildResultCompiledGlobalAsaxType globalAsaxBuildResult = BuildManager.GetGlobalAsaxBuildResult(); 5. if (globalAsaxBuildResult != null) 6. { 7. if (globalAsaxBuildResult.HasAppOrSessionObjects) 8. { 9. this.GetAppStateByParsingGlobalAsax(); 10. } 11. this._fileDependencies = globalAsaxBuildResult.VirtualPathDependencies; 12. } 13. if (this._state == null) 14. { 15. this._state = new HttpApplicationState(); 16. } 17. this.ReflectOnApplicationType(); 18. } 19. 20. this._theApplicationType = BuildManager.GetGlobalAsaxType(); 若存在global.asax文件则返回与该文件绑定编译的类型 若不存在则返回HttpApplication类型 05至12的代码则是在存在global.asax文件的情况下,生成文件解分器,应用程序状态就从其中被创建(HttpApplicationState)。 13至16,在不存在global.asax文件的情况下,创建默认HttpApplicationState; 在这里可以看到应用程序状态HttpApplicationState实例其实是HttpApplicationFactory的成员变量,所以其生存周期是整个应用程序域,作用域则是可被所有客户端应用程序(HttpApplication)共享。 this.ReflectOnApplicationType();通过映射theApplicationType,保存theApplicationType中的函数信息(用于之后HttpApplication实例事件函数输出),但是有三个函数是特别的,他们属于HttpApplicationFactory级别,就是应用程序域级别的事件函数输出,它们是Application_OnStart,Application_OnEnd,Session_OnEnd。 首先看一下ReflectOnApplicationType函数 1. private void ReflectOnApplicationType() 2. { 3. ArrayList list = new ArrayList(); 4. foreach (MethodInfo info in this._theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) 5. { 6. if (this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info)) 7. { 8. list.Add(info); 9. } 10. } 11. Type baseType = this._theApplicationType.BaseType; 12. if ((baseType != null) && (baseType != typeof(HttpApplication))) 13. { 14. 。。。。。。 15. } 16. this._eventHandlerMethods = new MethodInfo[list.Count]; 17. for (int i = 0; i < this._eventHandlerMethods.Length; i++) 18. { 19. this._eventHandlerMethods[i] = (MethodInfo) list[i]; 20. } 21. } 22. 23. 可以看出theApplicationType中的函数信息是被保存到ArrayList类型中,并最终this._eventHandlerMethods[i] = (MethodInfo) list[i];存入_eventHandlerMethods成员变量中(MethodInfo[]类型 ) 接下来看一下ReflectOnMethodInfoIfItLooksLikeEventHandler(info)函数做的处理 1. private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) 2. { 3. 。。。。。。。 4. 。。。。。。。 5. 。。。。。。。 6. if (StringUtil.EqualsIgnoreCase(str, "Application_OnStart") || StringUtil.EqualsIgnoreCase(str, "Application_Start")) 7. { 8. this._onStartMethod = m; 9. this._onStartParamCount = parameters.Length; 10. } 11. else if (StringUtil.EqualsIgnoreCase(str, "Application_OnEnd") || StringUtil.EqualsIgnoreCase(str, "Application_End")) 12. { 13. this._onEndMethod = m; 14. this._onEndParamCount = parameters.Length; 15. } 16. else if (StringUtil.EqualsIgnoreCase(str, "Session_OnEnd") || StringUtil.EqualsIgnoreCase(str, "Session_End")) 17. { 18. this._sessionOnEndMethod = m; 19. this._sessionOnEndParamCount = parameters.Length; 20. } 21. return true; 22. } 23. 24. 省略的部分是对theApplicationType中函数的检验(返回值,参数) 如之前所说Application_OnStart函数(或Application_Start),Application_OnEnd函数(或Application_End),Session_OnEnd函数(或Session_End),若theApplicationType中存在这三个函数信息会被存贮在HttpApplicationFactory,并在适当的时机,通过invoke来调用,所以在HttpApplication中事件并不包括此三类。 这样整个HttpApplicationFactory的Init处理就完成了。 回顾上述GetApplicationInstance(HttpContext context) 方法 接下来要做的就是_theApplicationFactory.EnsureAppStartCalled(context); 创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的 Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。 这里不详细分析了,有兴趣可以去找一下:)形如以下操作 _onStartMethod.Invoke(this, new object[] { eventSource, eventArgs }) 最后总算可以取HttpApplication实例了,在这之中,我们看一下HttpApplication初始化的实现 1. private HttpApplication GetNormalApplicationInstance(HttpContext context) 2. { 3. HttpApplication application = null; 4. lock (this._freeList) 5. { 6. if (this._numFreeAppInstances > 0) 7. { 8. application = (HttpApplication) this._freeList.Pop(); 9. 。。。。。。。 10. } 11. } 12. if (application == null) 13. { 14. application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType); 15. using (new ApplicationImpersonationContext()) 16. { 17. application.InitInternal(context, this._state, this._eventHandlerMethods); 18. } 19. } 20. return application; 21. } 22. 23. 04到10的操作是在ApplicationFactory池中取空闲的HttpApplication(当客户端程序结束后HttpApplication不会被释放,而是被存在ApplicationFactory池中供其它用户复用,这样显然更有效),如果不存在则进行初始化创建 接下来我们看一下HttpApplication级的初始化操作 1. internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) 2. { 3. 。。。。。。。 4. try 5. { 6. try 7. { 8. 。。。。。。。。。。。。 9. using (new HttpContextWrapper(context)) 10. { 11. 。。。。。。。。 12. this.InitModules(); 13. Label_006B: 14. if (handlers != null) 15. { 16. this.HookupEventHandlersForApplicationAndModules(handlers); 17. } 18. 。。。。。。 19. 。。。。。。。 20. try 21. { 22. this.Init(); 23. } 24. catch (Exception exception) 25. { 26. 。。。。。。。。。。。 27. } 28. } 29. 。。。。。。。。。。。 30. if (HttpRuntime.UseIntegratedPipeline) 31. { 32. this._stepManager = new PipelineStepManager(this); 33. } 34. else 35. { 36. this._stepManager = new ApplicationStepManager(this); 37. } 38. this._stepManager.BuildSteps(this._resumeStepsWaitCallback); 39. } 40. finally 41. { 42. 。。。。。。。。 43. } 44. } 45. catch 46. { 47. throw; 48. } 49. } 50. 在这个方法中,首先this.InitModules();这里做的是1.从配置文件中读取节点内容(machine.config web.config) 2.创建HttpModules模块并逐一调用HttpModule的Init初始化方法(HttpModules相关内容这里不作介绍) this.HookupEventHandlersForApplicationAndModules(handlers);参数handlers其实就是保存在ApplicationFactory中ApplicationType中的函数信息(以上提到过)。根据HttpApplication的事件信息,进行动态的创建委托,并且进行方法的绑定(指针挂钩),但是对函数名有要求 例如:事件BeginRequest 能自动绑定的函数名为 Application_OnBeginRequest 或者 Application_BeginRequest 若函数名不为上述名称,想绑定事件BeginRequest的话,可在this.Init()中通过委托绑定(+=),这是个虚拟函数(多态) this._stepManager.BuildSteps(this._resumeStepsWaitCallback); 1. internal override void BuildSteps(WaitCallback stepCallback) 2. { 3. ArrayList steps = new ArrayList(); 4. 。。。。。。。。。。。 5. app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps); 6. app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps); 7. app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps); 8. app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps); 9. app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps); 10. app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps); 11. app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps); 12. app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps); 13. steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); 14. app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps); 15. app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps); 16. app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps); 17. app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps); 18. steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); 19. app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); 20. app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); 21. app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); 22. steps.Add(new HttpApplication.CallFilterExecutionStep(app)); 23. app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); 24. app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); 25. this._endRequestStepIndex = steps.Count; 26. app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); 27. steps.Add(new HttpApplication.NoopExecutionStep()); 28. this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; 29. steps.CopyTo(this._execSteps); 30. this._resumeStepsWaitCallback = stepCallback; 31. } 32. 可以看到这个方法创建了事件函数执行顺序,通过不同的类将事件函数封装后,并最终存入_execSteps。 值得注意的是steps.Add(new HttpApplication.MapHandlerExecutionStep(app));和steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); 前者读取HttpHandle节点配置,后者则是调用HttpHandle节点中类,进行处理。留意一下它们所处的事件流程的位置 以上的阶段,是HttpApplication的初始化阶段。 取得HttpApplication实例后就是按照上述取得_execSteps来依次执行事件函数的调用了 回顾HttpRuntime类中的ProcessRequestInternal(HttpWorkerRequest wr) 函数,有这么一段 # 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); # } 对于取得HttpApplication实例的类型判断,如果实现的是异步接口调用BeginProcessRequest,非异步接口调用ProcessRequest 但实际上HttpApplication的非异步接口会抛出异常,.Net只支持异步的调用 void IHttpHandler.ProcessRequest(HttpContext context) { throw new HttpException(SR.GetString("Sync_not_supported")); } handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context)实现中 通过调用this.ResumeSteps(null) 并最终调用到HttpApplication的内部类 ApplicationStepManager的 ResumeSteps函数。 1. [DebuggerStepperBoundary] 2. internal override void ResumeSteps(Exception error) 3. { 4. 。。。。。。 5. lock (base._application) 6. { 7. 。。。。。。 8. try 9. { 10. try 11. { 12. Label_0040: 13. if (syncContext.Error != null) 14. { 15. 。。。。。 16. } 17. if (error != null) 18. { 19. 。。。。。。 20. } 21. if (syncContext.PendingOperationsCount > 0) 22. { 23. 。。。。。。。 24. } 25. else 26. { 27. if ((this._currentStepIndex < this._endRequestStepIndex) && ((context.Error != null) || base._requestCompleted)) 28. { 29. context.Response.FilterOutput(); 30. this._currentStepIndex = this._endRequestStepIndex; 31. } 32. else 33. { 34. this._currentStepIndex++; 35. } 36. if (this._currentStepIndex >= this._execSteps.Length) 37. { 38. flag = true; 39. } 40. else 41. { 42. this._numStepCalls++; 43. context.SyncContext.Enable(); 44. error = application.ExecuteStep(this._execSteps[this._currentStepIndex], ref completedSynchronously); 45. if (completedSynchronously) 46. { 47. this._numSyncStepCalls++; 48. goto Label_0040; 49. } 50. } 51. } 52. } 53. finally 54. { 55. 。。。。。。 56. } 57. } 58. catch 59. { 60. throw; 61. } 62. } 63. if (flag) 64. { 65. 。。。。。。 66. } 67. 其中error = application.ExecuteStep(this._execSteps[this._currentStepIndex], ref completedSynchronously); 便是通过_execSteps来依次执行事件函数的调用了,并最终通过step.Execute()调用 以下只对执行HttpHandle的过程做一个了解 之前知道steps.Add(new HttpApplication.CallHandlerExecutionStep(app));已经创建了事件顺序 接下来就是看一下CallHandlerExecutionStep中Execute的实现了 1. void HttpApplication.IExecutionStep.Execute() 2. { 3. HttpContext context = this._application.Context; 4. IHttpHandler handler = context.Handler; 5. 。。。。。。 6. 。。。。。。 7. if (handler is IHttpAsyncHandler) 8. { 9. 。。。。。。 10. IAsyncResult result = handler2.BeginProcessRequest(context, this._completionCallback, null); 11. if (result.CompletedSynchronously) 12. { 13. 。。。。。。。 14. } 15. } 16. else 17. { 18. 。。。。。。。。 19. try 20. { 21. handler.ProcessRequest(context); 22. } 23. finally 24. { 25. 。。。。。。 26. } 27. } 28. } 29. 以上程序可以看出根据HttpHandle所继承的接口,分别调用BeginProcessRequest或者ProcessRequest 另外HttpHandle实例的创建正是之前steps.Add(new HttpApplication.MapHandlerExecutionStep(app))的Execute完成的 Execute中 context.Handler = this._application.MapHttpHandler(。。。。。);取得 值得注意的是MapHttpHandler中通过读取配置文件,首先判断HttpHandle节点是否有自定义IHttpHandlerFactory处理,有的话生成该类实例,如果没有,则生成默认类HandlerFactoryCache 最后通过IHttpHandlerFactory的接口函数GetHandler取得处理句柄(默认句柄情况下就是配置文件HttpHandle节点的处理类,若不存在会报错) 经过几个事件函数的调用后, 在CallHandlerExecutionStep的Execute中被处理(如上) 在所有事件函数被调用完成之后,HttpApplication实例会被回收,ISAPIRuntime.ProcessRequest处理完毕,结果返回给COM,并通过COM的再一次处理,返回给客户端。这样一次请求就至此结束了。 这个文章主要针对的是HttpApplication层面的,下次如果有时间再对aspx文件的HttpHandle层面的重要类Page也做一个分析