Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Caveats with the runAllManagedModulesForAllRequests in IIS 7/8


:P
On this page:

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.

IISModulesInConsole

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.

The good news about all of this for .NET devs is that ASP.NET style modules can be used for just about every kind of IIS request. All you need to do is create a new Web Application and enable ASP.NET on it, and then attach managed handlers. Handlers can look at ASP.NET content (ie. ASPX pages, MVC, WebAPI etc. requests) as well as non-ASP.NET content including static content like HTML files, images, javascript and css resources etc. It's very cool that this capability has been surfaced.

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:

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true" >
    </modules>  </system.webServer> 

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" 
preCondition="managedHandler" />

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.

runAllManagedModulesForAllRequests and Http Application Events

Another place the runAllManagedModulesForAllRequests attribute affects is the Global Http Application object (typically in global.asax) and the Application_XXXX events that you can hook up there. So while the events there are dynamically hooked up to the application class, they basically behave as if they were set with the preCodition="managedHandler" configuration switch.

The end result is that if you have runAllManagedModulesForAllRequests="true" you'll see every Http request passed through the Application_XXXX events, and you only see ASP.NET requests with the flag set to "false".

What's all that mean?

Configuring an application to handle requests for both ASP.NET and other content requests can be tricky especially if you need to mix modules that might require both.

Couple of things are important to remember. If your module doesn't need to look at every request, by all means set a preCondition="managedHandler" on it. This will at least allow it to respond to the runAllManagedModulesForAllRequests="false" flag and then only process ASP.NET requests. There's no reason for static files to pass through your module if you're not doing anything related to static files - it only adds overhead. Use the managedHandler preCondition to pre-filter your module's input if it applies.

Look really carefully to see whether you actually need runAllManagedModulesForAllRequests="true" in your applications as set by the default new project templates in Visual Studio. Part of the reason this is the default, is because it was required for the initial versions of IIS 7 and ASP.NET 2 in order to handle MVC extensionless URLs.

However, if you are running IIS 7 or later and .NET 4.0 you can use the ExtensionlessUrlHandler instead to allow you MVC functionality without requiring runAllManagedModulesForAllRequests="true":

    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />      
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." 
verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" /> </handlers>

Oddly runAllManagedModulesForAllRequests="true" continues to be the default for Visual Studio 2012 MVC template apps, so I'm not sure why - it should be enabled only if there's a specific need to access non ASP.NET requests.

Note that on the original Windows Server 2008 and Vista (IIS 7.0) you might need a HotFix in order for ExtensionLessUrlHandler to work properly for MVC projects. On my live server I needed it (about 6 months ago), but others have observed that the latest service updates have integrated this functionality and the hotfix is not required. On IIS 7.5 and later I've not needed any patches for things to just work.

As a side note, it's interesting that when you access a static HTML resource, you can actually write into the Response object and get the output to show, which is trippy, because if you look in the IIS Module Configuration you'll find that .HTM files are mapped to a native extension dll. So for example, I can access a default.htm page, and then intercept it in a module and say in the PostRequestHandlerExecute  do a Response.Write() and have that output show up in the HTTP output. That's pretty cool, but a bit of mystery on how it works…

I haven't looked closely to see how this works - whether ASP.NET just fires directly into the native output stream or whether the static requests are re-routed directly through the ASP.NET pipeline once a managed code modules are detected. Note that this doesn't work for all non ASP.NET resources - for example, I can't do the same with ASP classic requests, but it makes for an interesting demo when injecting HTML content into a static HTML page :-)

Plan for non-ASP.NET Requests

It's important to remember that if you write a .NET Module to run on IIS 7, there's no way for you to prevent non-ASP.NET requests from hitting your module. So make sure you plan to support requests to extensionless URLs, to static resources like files. Luckily ASP.NET creates a full Request and full Response object for you for non ASP.NET content. So even for static files and even for ASP classic for example, you can look at Request.FilePath or Request.ContentType (in post handler pipeline events) to determine what content you are dealing with.

As always with Module design make sure you check for filter conditions to access your module early in your code and if a filter fails immediately exit - always minimize the code that runs if your module doesn't need to process the request.

runAllManagedModulesForAllRequests Summary

To recap here are the important things to remember about runAllManagedModulesForAllRequests:

  • If runAllManagedModulesForAllRequests="true", all modules - regardless of their preCondition attribute setting fire on all requests. This also true for Application_XXXX events implement on the HttpApplication
  • runAllManagedModulesForAllRequests="false" has no effect of keeping unmanaged requests from hitting modules, *unless* preCondition="managedHandler" is set
  • runAllManagedModulesForAllRequests="false" does affect Application_XXXX events, causing those events to only fire on managed requests then. IOW, Application_XXXX behaves as if the 'module' implementation had a preCondition="managedHandler"
Posted in IIS7  ASP.NET  

The Voices of Reason


 

Lelala
October 26, 2012

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

AH, ok - now i understand the problem i had with my own modules and the requests going through it when it comes to requests to "WebResource.axd"/scripts and other static content, e.g. the image-folder in the webapp root folder.
I hadn't to care about this until i switched to next version of our ORM, for which i had to develop a custom HTTP module to get the stuff working.

John K
November 29, 2012

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

I think in the statement "Unmanaged modules point at physical files on disk, while unmanaged modules point at .NET types" the second "unmanaged" should be "managed".

Vladimir
April 14, 2013

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

Hi great article. I have been looking into possibility to deffer my handlers on Post map handler event and i see that for static resources in handler and currentHandler is null. Does this mean that non manged (native) handlers are not visible in asp.net. Is this good way to deffer managed from not manged handlers? Cause i would like to add some logic, but not if it is a static resource.

Rick Strahl
April 14, 2013

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

@Vladimir - you can only see managed handlers and modules. Nothing unmanaged shows up in the pipeline.

Vladimir
April 15, 2013

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

Yep, that is what i was thinking. If I set it runAllManagedModulesForAllRequests to false, event's like PostMapRequestHandler want even trigger for static resources. So that is one option also. Thank you!

William Gross
August 14, 2014

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

Thank you so much for this post, Rick! Your post in combination with http://blogs.msdn.com/b/tmarq/archive/2010/05/26/how-extensionless-urls-are-handled-by-asp-net-v4.aspx was the only documentation I could find on the *true* purpose of ExtensionlessUrlHandler, which is to force all extensionless requests to run in managed mode so that they have the opportunity to be routed by MVC or Web API.

Agustin
July 07, 2015

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

Hey there Rick.
I had this issue, a custom HttpApplication, defined in global.asax, was not firing its events for non-managed requests.
Toggling runAllManagedModulesForAllRequests=true was doing the trick. But I was not able to find a freaking reason or explanation as to why it was like that.
Until I found this article. It gave me closure !
Thanks :)

Alex
April 20, 2019

# re: Caveats with the runAllManagedModulesForAllRequests in IIS 7/8

In case anyone lands on this post in 2019 (after all it's the #1 result if you Google for "runAllManagedModulesForAllRequests") - afaik Visual Studio default project template does not include "ExtensionlessUrlHandler" these days, so touching runAllManagedModulesForAllRequests is not recommended these days, cause this will break your MVC projects.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024