HTTP modules - subdirectories and private variables
I recently finished (for now--there's always more to do) one of the more complex HTTP modules I've worked on. I have an application first written in the ASP.NET 1.0 beta 2 time frame that's since been upgraded to 1.0, 1.1, and now 2.0. It had a lot of custom authentication and error handling code in global.asax, and for general architecture and server management purposes, I wanted to move this code into separate HTTP modules. I ran into a couple gotchas I wanted to document.
Lesson 1: You can't disable an HTTP module for a subdirectory. I wanted to remove the HTTP module for one subdirectory using the <location> configuration element, and while it let me put it in my web.config fine and never threw an error a la "It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.", when I ran it, it went into the module's code as if that config section wasn't there.
Scott Guthrie explained to me why: "HttpModules are application specific. When an application starts up, a number of HttpApplication's get configured (one for each logical thread that will execute in the application), and HttpModules are created and assigned to them. That is why you need to configure them at the application root (or higher) level in config. This enables much better pooling of resources." He pointed out that other HTTP modules in ASP.NET handle this with custom configuration sections, which is more work than I was looking at doing for this release.
An exception to this rule is if that subdirectory itself is configured
as an IIS application, but in many cases (including mine), this is more
trouble than it's worth, limiting what user controls it can see,
requiring its own bin directory... and if you're going to do all that,
it's probably going to need its own web.config file anyway.
Lesson 2: You shouldn't use private member variables in an HTTP module. I've been an ASP.NET programmer long enough that I thought I could figure out whether member variables were safe or not. I could argue for it either way, and I wasn't feeling ambitious enough to wade through all that code in Reflector. I finally found official documentation for the HttpApplication class that said, "One instance of the HttpApplication class is used to process many requests in its lifetime; however, it can process only one request at a time. Thus, member variables can be used to store per-request data." So it made sense that if an HTTP module is wired to a particular HttpApplication instance, the same rule would apply, and member variables would be safe in HTTP modules as well.
Again, Scott helped me out by advising me against member variables: "If you have an async operation occur during the request, I believe ASP.NET might switch the HttpModule to another thread to execute. That is my worry with storing local variables. It might work in your dev environment, but generate different results under high-load on a server." And no one needs any more "works great in dev and QA but not production" sorts of issues. He advised storing such things in HttpContext.Items instead, but in my case, the data was so cheap to calculate and consumed rarely enough that I decided to just have a method to calculate it every time. Interestingly, by switching from global.asax (where member variables were fine) to an HTTP module, I took a step back in this department, but overall it was the right move and the way I would have written the app from the beginning had I been more familiar and experienced with ASP.NET.