One of the nice enhancements in IIS 7 (and now 8) is the ability to be able to intercept non-managed - ie. non ASP.NET served - requests from within ASP.NET managed modules. This opened up a ton of new functionality that could be applied across non-managed content using .NET code.
I thought I had a pretty good handle on how IIS 7's Integrated mode pipeline works. But when I put together some samples last night I realized that the way that managed and unmanaged requests fire into the pipeline is downright confusing especially when it comes to the runAllManagedModulesForAllRequests attribute. There are a number of settings that can affect whether a managed module receives non-ASP.NET content requests such as static files or requests from other frameworks like PHP or ASP classic.
Native and Managed Modules
The integrated mode IIS pipeline for IIS 7 and later - as the name suggests - allows for integration of ASP.NET pipeline events in the IIS request pipeline. Natively IIS runs unmanaged code and there are a host of native mode modules that handle the core behavior of IIS. All the default modules that IIS installs with are unmanaged ones. If you set up a new IIS site or application without managed code support only the native modules are supported and fired without any interaction between native and managed code.
If you use the Integrated pipeline with managed code enabled however things get a little more confusing as there both native modules and .NET managed modules can fire against the same IIS request.
If you open up the IIS Modules dialog you see both managed and unmanaged modules. Unmanaged modules point at physical files on disk, while managed modules point at .NET types and files referenced from the GAC or the current project's BIN folder.
Both native and managed modules can co-exist and execute side by side and on the same request. When running in IIS 7 the IIS pipeline actually instantiates a the ASP.NET runtime (via the System.Web.PipelineRuntime class) which unlike the core HttpRuntime classes in ASP.NET receives notification callbacks when IIS integrated mode events fire. The IIS pipeline is smart enough to detect whether managed handlers are attached and if there are none these notifications don't fire, improving performance.
However, with that functionality comes a lot of responsibility. Because every request passes through the ASP.NET pipeline, if managed modules (or handlers) are attached there are possible performance implications that come with it. Running through the ASP.NET pipeline does add some overhead.
ASP.NET and Your Own Modules
When you create a new ASP.NET project typically the Visual Studio templates create the modules section like this:
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" >
Specifically the interesting thing about this is the runAllManagedModulesForAllRequest="true" flag. It would seem that it determines whether any registered managed modules always run. If you assume that though, you'd only be partially right :-). Realistically this flag does not control whether managed code is fired for all requests or not. Rather it is an override for the preCondition="managedHandler" flag on a particular module registration.
The preCondition="managedHandler" attribute on an HttpModule mean "Only handle ASP.NET pipeline events - no native pipeline events". The runAllManagedModulesForAllRequests flag essentially manipulates the preCondition attribute to perform its actions.
runAllManagedModulesForAllRequests="true" - works as you'd expect
With the flag set to true all requests - native and managed - fire through your ASP.NET modules. This flag essentially overrides the preCondition="managedHandler" and removes the managed handler flag, and thus forces all requests through your module (and any other managed module).
In other words, your module will have to handle all requests. So far so obvious - it does as the name suggests.
runAllManagedModulesForAllRequests="false" - different than you might think!
When the value is false though, the behavior is a little bit unexpected. You probably expect that native, non-ASP.NET requests no longer get funneled through the ASP.NET Module pipeline. But that's only partially true. A value of true is not the opposite of what the true behavior does.
When the value is false the default settings for preCondition="" are used on the module. Which means that if you have a preCondition="managedHandler" it only fires managed requests. If it doesn't have a preCondition="managedHandler" then all requests - native and managed - are fired against your module.
For example, if I create a module like this:
<add name="SharewareModule" type="HowAspNetWorks.SharewareMessageModule" />
it will still fire against ALL requests regardless of the runAllManagedModulesForAllRequests flag. Even if the value runAllManagedModulesForAllRequests="false", the module is fired with unmanaged requests going through it. Not quite as expected.
So the only way to really prevent the managed module from firing native requests is by explicitly providing preCondition="managedHandler":
<add name="SharewareModule" type="HowAspNetWorks.SharewareMessageModule"
and then set runAllManagedModulesForAllRequests="false" my module only fires against managed requests. If I switch the flag to true, now my module ends up handling all IIS requests that are passed through from IIS.
The moral of the story here is that if you intend to only look at ASP.NET content, you should always set the preCondition="managedHandler" attribute to ensure that only managed requests are fired on this module. But even if you do this, realize that runAllManagedModulesForAllRequests="true" can override this setting, so your module still can fire on native requests and has to anticipate handling any kind of request.
Other Posts you might also like