First look at a diagram, from which you can see how requests enter HttpRuntime from CLR
1, AppManagerAppDomainFactory
This figure starts from AppManagerAppDomainFactory. According to Uncle Tom's blog, this class is loaded during CLR initialization loading Let's take a look at this class
Use Reflector to decompile and search the AppManagerAppDomainFactory class. You can see (since there are not many classes, I'll post a complete one first):
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public sealed class AppManagerAppDomainFactory : IAppManagerAppDomainFactory { // Fields private ApplicationManager _appManager = ApplicationManager.GetApplicationManager(); // Methods public AppManagerAppDomainFactory() { this._appManager.Open(); } internal static string ConstructSimpleAppName(string virtPath) { if (virtPath.Length > 1) { return virtPath.Substring(1).ToLower(CultureInfo.InvariantCulture).Replace('/', '_'); } if (!BuildManagerHost.InClientBuildManager && HostingEnvironment.IsDevelopmentEnvironment) { return "vs"; } return "root"; } [return: MarshalAs(UnmanagedType.Interface)] public object Create(string appId, string appPath) { object obj2; try { if (appPath[0] == '.') { FileInfo info = new FileInfo(appPath); appPath = info.FullName; } if (!StringUtil.StringEndsWith(appPath, '\\')) { appPath = appPath + @"\"; } ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath, false); ISAPIRuntime o = (ISAPIRuntime) this._appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost, false, null); o.StartProcessing(); obj2 = new ObjectHandle(o); } catch (Exception) { throw; } return obj2; } public void Stop() { this._appManager.Close(); } }
As for the detailed explanation here, it is recommended to take a look at the things before MVC. This is not the key point I want to express, so I won't introduce it
As long as you know, according to uncle, here, in the CreateObjectInternal method, you create AppDomain, HostingEnvironment and other column operations
All subsequent applications, such as httpruntime and httpcontext, rely on this AppDomain
2, Theme
After various internal processes I don't know, unmanaged code starts to formally call processrequest (hereinafter referred to as PR method) of ISAPIRuntime
(ISPAIRuntime inherits the IISPAIRuntime interface, which can interact with COM and exposes the ProcessRequest interface method)
Don't ask me why I call the PR method, because I don't know, but it's really this method
public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IISAPIRuntime2, IRegisteredObject { // Fields private static int _isThisAppDomainRemovedFromUnmanagedTable; private const int WORKER_REQUEST_TYPE_IN_PROC = 0; private const int WORKER_REQUEST_TYPE_IN_PROC_VERSION_2 = 2; private const int WORKER_REQUEST_TYPE_OOP = 1; // Methods [SecurityPermission(SecurityAction.Demand, Unrestricted=true)] public ISAPIRuntime(); [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public void DoGCCollect(); public override object InitializeLifetimeService(); [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public int ProcessRequest(IntPtr ecb, int iWRType); internal static void RemoveThisAppDomainFromUnmanagedTable(); [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public void StartProcessing(); [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public void StopProcessing(); [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] void IISAPIRuntime2.DoGCCollect(); [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] int IISAPIRuntime2.ProcessRequest(IntPtr ecb, int iWRType); [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] void IISAPIRuntime2.StartProcessing(); [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] void IISAPIRuntime2.StopProcessing(); void IRegisteredObject.Stop(bool immediate); }
Here is a way to look at the name and feel familiar. OK, click in to have a look:
GC is a familiar name called garbage recycling OK, that's not the point. Let's continue
[SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)] public int ProcessRequest(IntPtr ecb, int iWRType) { IntPtr zero = IntPtr.Zero; if (iWRType == 2) { zero = ecb; ecb = UnsafeNativeMethods.GetEcb(zero); } ISAPIWorkerRequest wr = null; try { bool useOOP = iWRType == 1; wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP); wr.Initialize(); string appPathTranslated = wr.GetAppPathTranslated(); string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal; if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal)) { HttpRuntime.ProcessRequestNoDemand(wr); return 0; } HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated })); return 1; } catch (Exception exception) { try { WebBaseEvent.RaiseRuntimeError(exception, this); } catch { } if ((wr == null) || !(wr.Ecb == IntPtr.Zero)) { throw; } if (zero != IntPtr.Zero) { UnsafeNativeMethods.SetDoneWithSessionCalled(zero); } if (exception is ThreadAbortException) { Thread.ResetAbort(); } return 0; } }
The first thing you notice is the parameter ECB of the method's IntPtr type. What is ECB? ECB is an unmanaged pointer. Its full name is Execution Control Block. It plays a very important role in the whole Http Request Processing process. Let's briefly introduce an ECB.
When ISAPI calls ISAPIRuntime in unmanaged environment, it needs to pass some necessary data. For example, ISAPIRuntime needs to obtain the data of Server variables and the data returned to Server through Post Mehod; And finally return the content of the Response to the unmanaged environment ISAPI, and then present it to the Client user. In general, ISAPIRuntime cannot call ISAPI directly, so an object pointer is used to call it. This object is ECB, which realizes the access to the unmanaged environment ISAPI.
Another point to emphasize is that ISAPI calls ISAPIRutime asynchronously, that is, ISAPI returns immediately after calling ISAPIRutime. This is mainly due to Performance and Responsibility considerations, because ASP Net application is inherently a multithreaded application. In order to have better Response ability, asynchronous operation is the most effective solution. But there will be a problem here. We know that we are interested in ASP Net resource call is essentially a Message Exchange Pattern of Request/Response. Asynchronous call often means that ISAPI passes the Request to ISAPIRuntime and will not get the Response finally generated by ISAPIRuntime, which is obviously unacceptable. ECB solves this problem. ISAPI will pass its corresponding ECB pointer to ISAPIRutime when calling the ProcessRequest method of ISAPIRutime. ISAPIRutime can not only return the final generated Response to ISAPI, but also obtain some required data by calling ISAPI through ECB.
- CreateWorkerRequest
This method still needs to be looked at. There are gains
internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP) { if (useOOP) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); if (EtwTrace.IsTraceEnabled(5, 1)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false); } return new ISAPIWorkerRequestOutOfProc(ecb); } int num = UnsafeNativeMethods.EcbGetVersion(ecb) >> 0x10; if (num >= 7) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb); } else { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); } if (EtwTrace.IsTraceEnabled(5, 1)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true); } if (num >= 7) { return new ISAPIWorkerRequestInProcForIIS7(ecb); } if (num == 6) { return new ISAPIWorkerRequestInProcForIIS6(ecb); } return new ISAPIWorkerRequestInProc(ecb); }
Determine what type of WorkerRequest to create by judging the specific contents of ecb and type types (ISPAIWorkerRequest of the above types inherits from HttpWorkerRequest). The above code can see that different versions of IIS are packaged differently, Initialize some basic information (such as contenttype, length of querystring, filepath, etc.) through its initialize method.
- ProcessRequestNoDemand
This method is really into ASP Net runtime pipeline. The parameters passed are the WorkerRequest object instances that are masked after differentiation Take a look at this method
internal static void ProcessRequestNoDemand(HttpWorkerRequest wr) { RequestQueue queue = _theRuntime._requestQueue; wr.UpdateInitialCounters(); if (queue != null) { wr = queue.GetRequestToExecute(wr); } if (wr != null) { CalculateWaitTimeAndUpdatePerfCounter(wr); wr.ResetStartTime(); ProcessRequestNow(wr); } }
Ok, next, continue to look at the PRNow method. In fact, the internal call is the ProcessRequestInternal method of HttpRuntime
private void ProcessRequestInternal(HttpWorkerRequest wr) { Interlocked.Increment(ref this._activeRequestCount); if (this._disposingHttpRuntime) { try { wr.SendStatus(0x1f7, "Server Too Busy"); wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>"); wr.SendResponseFromMemory(bytes, bytes.Length); wr.FlushResponse(true); wr.EndOfRequest(); } finally { Interlocked.Decrement(ref this._activeRequestCount); } } else { HttpContext context; try { context = new HttpContext(wr, false); } catch { try { wr.SendStatus(400, "Bad Request"); wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); byte[] data = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); wr.SendResponseFromMemory(data, data.Length); wr.FlushResponse(true); wr.EndOfRequest(); return; } finally { Interlocked.Decrement(ref this._activeRequestCount); } } wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); 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) //Asynchronous processing { IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; context.AsyncAppHandler = handler2; handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); } else //Synchronous processing { applicationInstance.ProcessRequest(context); this.FinishRequest(context.WorkerRequest, context, null); } } catch (Exception exception) { context.Response.InitResponseWriter(); this.FinishRequest(wr, context, exception); } } }
Perhaps the most enjoyable thing is to see that HttpContext object and HttpApplication object are created in this method
Next, take a look at the creation of these two objects
1). HttpContext
internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) { this._timeoutStartTimeUtcTicks = -1L; this._timeoutTicks = -1L; this._threadAbortOnTimeout = true; this.ThreadContextId = new object(); this._wr = wr; this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this)); if (initResponseWriter) { this._response.InitResponseWriter(); } PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING); }
We also saw two surprise Codes: the instantiation of HttpRequest and HttpResponse. The required information will be obtained by passing the WorkerRequest and this parameter of HttpContext object
2). HttpApplication
The creation of this object is the red part of the following sentence
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
Get the familiar HttpApplication object instance through the GetApplicationInstance static method of HttpApplicationFactory. Since the HttpApplication object inherits from IHttpAsyncHandler, and IHttpAsyncHandler inherits from IHttpHandler, it is not wrong that the type of the above app is IHttpHandler. Continue to look at the following if (app is IHttpAsyncHandler) code, and you will know that app must take the branch here, and then call asynchandler Beginprocessrequest method.
So far, HttpRuntime has officially played its irreplaceable role, and has officially entered the creation of HttpApplication object and the well-known life cycle of HttpApplication through this object.