HttpRuntime, HttpContext, and HttpApplicationOh My!
When a request hits, it is routed to the ISAPIRuntime.ProcessRequest()
method. This method in turn calls HttpRuntime.ProcessRequest
that does several important things (look at System.Web.HttpRuntime.ProcessRequestInternal with Reflector):
- Creates a new HttpContext instance for the request
- Retrieves an HttpApplication instance
- Calls HttpApplication.Init() to set up pipeline events
- Init() fires HttpApplication.ResumeProcessing(), which starts the ASP.NET pipeline processing
First, a new HttpContext object is created and passed the ISAPIWorkerRequest that wraps the ISAPI ECB. This context is available throughout the lifetime of the request and always
accessible via the static HttpContext.Current
property. As the name implies, the HttpContext object represents the context of the currently active request, because it contains references to all the vital objects you typically access during the request lifetime: Request, Response, Application, Server, and Cache. At any time during request processing HttpContext.Current
gives you access to all of these objects.
The HttpContext object also contains a very useful Items collection that you can use to store data that is request specific. The context object gets created at the begging of the request cycle and released when the request finishes, so data stored there in the Items collection is specific only to the current request. A good example is a request logging mechanism where you want to track start and end times of a request by hooking the Application_BeginRequest
methods in Global.asax
as shown in Listing 3
. HttpContext is your friendyou'll use it liberally if you need data in different parts of the request or page processing.
After the context has been set up, ASP.NET needs to route your incoming request to the appropriate application/virtual directory by way of an HttpApplication object. Every ASP.NET application must be set up as a virtual (or Web root) directory and each of these "applications" are handled independently.
Master of Your Domain: HttpApplication
Each request is routed to an HttpApplication object. The HttpApplicationFactory class creates a pool of HttpApplication objects for your ASP.NET application depending on the load on the application and hands out references for each incoming request. The size of the pool is limited to the setting of the MaxWorkerThreads
entry in the machine.config
key, which by default is 20.
The pool starts out with a smaller number though, usually one, and it then grows as multiple simultaneous requests need to be processed. The pool is monitored so that under load it may grow to its maximum number of instances, and later scaled back to a smaller number as the load drops.
HttpApplication is the outer container for your specific Web application. It maps to the class that defined in Global.asax
. It's the first entry point into the HTTP runtime that you actually see on a regular basis in your applications. If you look in Global.asax (or the code behind class) you'll find that this class derives directly from HttpApplication.
public class Global : System.Web.HttpApplication
HttpApplication's primary purpose is to act as the event controller for the HTTP pipeline, so its interface consists primarily of events. The event hooks are extensive and include:
|The HttpApplication is like a master of ceremoniesit is where the processing action starts|
Each of these events is also implemented in the Global.asax
file via empty methods that start with an Application_
prefix, for example, Application_BeginRequest()
, or Application_AuthorizeRequest()
. These handlers are provided for convenience since they are frequently used in applications, so that you don't have to explicitly create the event handler delegates.
It's important to understand that each ASP.NET virtual application runs in its own AppDomain and that inside of the AppDomain are multiple HttpApplication instances running simultaneously, fed out of a pool that ASP.NET manages. This is so that multiple requests can process at the same time without interfering with each other.
|Figure 5. AppDomain, Application Pool, and Request Thread Interactions: Using a couple of browser instances, you can see how AppDomains, application pool instances, and request threads interact. When multiple requests fire you'll see the thread and application IDs change, but the AppDomain stays the same.|
To see the relationship between the AppDomain, threads, and the HttpApplication, check out the code in Listing 4
, which is part of the sample code. You can see the running form in Figure 5
. To check this out, run two instances of a browser and then hit this sample page and watch the various IDs.
You'll notice that the AppDomain ID stays steady while thread and HttpApplication IDs change on most requests, although they likely will repeat. HttpApplications run out of a collection and are reused for subsequent requests, so the IDs repeat at times. Note though that an application instance is not
tied to a specific threadrather, an instance is assigned to the active executing thread of the current request.
Threads are served from the .NET ThreadPool and by default are Multithreaded Apartment (MTA) style threads. You can override this apartment state in ASP.NET pages with the ASPCOMPAT="true"
attribute in the @Page
is meant to provide COM components a safe environment to run in, and ASPCOMPAT
uses special Single Threaded Apartment (STA) threads to service those requests. STA threads are set aside and pooled separately, because they require special handling.
The fact that these HttpApplication objects are all running in the same AppDomain is very important. This is how ASP.NET can guarantee that changes to web.config
or individual ASP.NET pages get recognized throughout the AppDomain. Making a change to a value in web.config
causes the AppDomain to be shut down and restarted. This makes sure that all instances of HttpApplication see the changes made because when the AppDomain reloads it re-reads the changes from ASP.NET at startup. Any static references are also reloaded when the AppDomain starts, so if the application reads values from App Configuration settings these values also get refreshed.
To see this in the sample, hit the ApplicationPoolsAndThreads.aspx
page and note the AppDomain ID. Then go in and make a change in web.config
(add a space and save). Then reload the page. You'll l find that a new AppDomain has been created.
In essence the Web Application/Virtual completely "restarts" when this happens. Any requests that are already in the pipeline processing will continue to run through the existing pipeline, while any new requests coming in get routed to the new AppDomain. To avoid the problem of "hung requests," ASP.NET forcefully shuts down the AppDomain after the request timeout period is up even if requests are still pending. So it's actually possible for two AppDomains to exist for the same HttpApplication at a given point in time (when an old one's shutting down and a new one is ramping up). Both AppDomains continue to serve their clients until the old one has run out its pending requests and shuts down, leaving just the new AppDomain running.