DEV410: Inside the ASP.NET Runtime

Michele Leroux Bustamente presented an insightful guide to the extensible capabilities of the ASP.NET architecture.

IIS and ASP.NET Configuration
HTTP requests in IIS arrive at the appropriate extension: aspnet_isapi.dll or asp.dll. ASP.NET requests get passed through to the ASP.NET runtime. It's this application that processes incoming requests.

In IIS 5, the inetinfo.exe process hosts aspnet_isapi.dll. This passes requests into the managed ASP.NET runtime to invoke an HttpHandler. There is a thread that processes this request, running within the ASP.NET worker process inside an appdomain. In IIS 5, therefore, every application has its own appdomain. Every time an HTTP request comes in, it gets its own thread from the thread pool.

In IIS 6, the process is slightly different. The kernel (http.sys) directly passes requests to the correct pool within the HTTP runtime, thus bypassing a step. We can now have multiple processes running (w3wp.exe).

Configurable pipeline components include HTTP handler factories, HTTP handlers, HTTP modules and SOAP extensions. You can configure any or all of them together as part of the round-trip.

HTTP Modules
These are objects that can interact with every HTTP request, and use an event-driven model to interact with web applications. You can create custom modules that listen to incoming requests and process things such as custom authorisation, error handling, diagnostics, session state management or diagnostics. Shipped in the box are: WindowsAuthenticationModule, FormsAuthenticationModule, PassportAuthenticationModule, FileAuthorizationModule, SessionModule and others. In the same way as for handlers, you can configure modules through a <httpModules> section in machine.config. All modules implement IHttpModule and add an event handler for events occurring within the Application object. Within a event handler, you can access the HttpApplication object - sent as the event source.

Michele showed how you could create a custom authentication module so that guest users could gain access to a site without being fully logged in, using the module to give non-logged in users a set of roles and set the auth cookie.

HTTP Handler Factories
HTTP Handler factories return an HTTP handler to process incoming requests. You can use this to intercept requests for specific resources or format custom types. You could even use an HTTP Handler Factory to create a request for a file that doesn't even exist, and construct it on the fly (a classic example of this is the trace handler that deals with .axd requests). All handler factories implement IHttpHandlerFactory, which has two methods: GetHandler() returns an IHttpHandler given the HttpContext and ReleaseHandler() cleans up any resources. The handler factory could even return different handlers depending on the context (for example, different user roles).

machine.config has an <httpHandlers> setting to select which class factory is used to handle incoming requests. For example, .aspx files are handled by System.Web.UI.PageHandlerFactory and .config files are handled by System.Web.HttpForbiddenHandler (which prevents their download). The default configuration in machine.config can of course be overridden in web.config to alter settings at the application level. For example, you might choose to handle XML files and apply some settings, or disallow additional file extensions by routing other calls to the HttpForbiddenHandler.

Handlers themselves implement IHttpHandler - an interface with a ProcessRequest() method to do the work, and an IsReusable read-only boolean property. To access the session from a custom handler, you also need to implement a marker interface IRequiresSessionState in the HTTP handler. There's also an asynchronous Begin/End design pattern that enables you to offload incoming requests to their own thread. But this may not improve performance as the work still needs to be done!

Don't forget that you need to add a mapping to the aspnet_isapi.dll extension if you want it to handle file types that aren't already passed to ASP.NET. Otherwise the file will be handled by inetinfo.exe directly and will never be seen by the handler.

You can also use handlers without having to modify a config file by creating an endpoint with an .ashx extension. This allows you to create a web handler class using a <%@ WebHandler %> attribute - the handler factory is already provided for you.

SOAP Extensions
SOAP extensions provide a method to interact with incoming web service requests. You can create your own custom SOAP extensions in a pipeline process to handle the data en route between aspnet_isapi.dll and the web service method. This is how WSE is implemented.

Incoming requests are routed to an HTTP handler that derives from WebServiceHandler. (There are four variants to handle aspects such as sessions and asynchronous communication.) The handler routes calls to a SOAP extension before and after execution of the web service method.

To implement a SOAP extension, you can either create an extension class that derives from SoapExtension, or create an attribute class that derives from SoapExtensionAttribute. The extension class has a ProcessMessage() method (allowing the message to be handled) and a ChainStream() (allowing the stream to be edited). These extension classes are once again invoked using web.config or machine.config.

More samples here and here.