IHttpModule Gotchas – The Init() Method Can Get Called Multiple Times

10 March 2010 - 11:35 PM / by Dominic Pettifer. 1 Image 3 Comments

Development Gotchas - When creating and registering IHttpModules, some people mistakenly think that only a single instance of the IHttpModule is created, and that the Init() method is only called once. I’ll explain how and why this is incorrect, and how this misunderstanding can lead to subtle bugs in your ASP.NET web application.

HttpModules

HttpModules are handy for hooking into various events fired by your ASP.NET application, such as Begin_Request, and End_Request, and allow behaviour and functionality to be dynamically injected into an ASP.NET app. Indeed certain ASP.NET features, such as the UrlRouting functionality used ASP.NET MVC, are actually HttpModules. To create one, simply create a class that implements System.Web.IHttpModule:

using System;
using System.Web;

namespace MyAspNetApp.Modules
{
    public class PageExecutionTimer : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute +=
			    new EventHandler(Context_PreRequestHandlerExecute);
            context.PostRequestHandlerExecute +=
			    new EventHandler(Context_PostRequestHandlerExecute);
        }

        private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            // Start a timer and store it somewhere, possibly in Request context
        }

        private void Context_PostRequestHandlerExecute(object sender, EventArgs e)
        {
            // Grab timer out of where-ever it's stored,
            // and write out execution time onto end of HTML response
        }

        public void Dispose() { }
    }
}

...and register it in your web.config:

<httpModules>
  // ...other modules (snip)... //
  <add name="PageExecutionTimer" type="MyAspNetApp.Modules.PageExecutionTimer, MyAspNetApp"/>
</httpModules>

Some people mistakenly think that only a single instance of your IHttpModule is instantiated. This belief leads people to think that the Init() method only gets called once, and then proceed to add code into this Init method that they only want to run once on Application StartUp, similar to how the Application_Start event works in Global.asax. This simply isn't the case at all. In fact multiple copies of your IHttpModule will be created, and the Init method run multiple times. But how?

HttpApplication context

An HttpApplication object, the thing that's passed into the Init() method, is used to process a single HTTP request. When an application starts up, the ASP.NET Worker process will instantiate as many HttpApplication objects as it thinks it needs, then it'll pool them for performance reasons, reusing instances as new requests come in before sending them back into the pool. Similar to how database connection pooling works. Basically creating these types of objects is expensive, so ASP.NET tries to keep hold of them and reuses them.

Now for each HttpApplication object, it will also instantiate one copy of each IHttpModule that is registered and call the Init method for each instance. So if 5 HttpApplication objects are created, 5 copies of your IHttpModule will be created, and your Init method called 5 times (once per instance).

Now why is it instantiating 5 HttpApplications objects? Well maybe your ASPX page has links to other resources which your browser will try to download, css, javascript, WebResource.aspx, maybe an <iframe> somewhere. Your browser will try to pull these down at roughly the same time, so the webserver thinks it's seeing lots of HTTP requests all coming in at the same time, so will create lots of HttpApplication objects to process them. The 'number' of HttpApplication instances that are created is an internal detail/optimisation of the ASP.NET process running under IIS (or the VS built in web-server), and isn't relevant for this discussion.

Making the Init Method Only Run Once

Now if you want to put code in your HttpModule's Init method that you only want to run once on Application start-up, this is possible, you could try something like the following in your IHttpModule:

private static bool HasAppStarted = false;
private readonly static object _syncObject = new object();

public void Init(HttpApplication context)
{
    if (!HasAppStarted)
    {
        lock (_syncObject)
        {
            if (!HasAppStarted)
            {
                // Run application StartUp code here

                HasAppStarted = true;
            }
        }
    }
}

We're using the double check/lock strategy so that only the first bunch of request will hit the lock, and all subsequent requests won't get past the first if statement. Hitting locks is expensive.

And before you ask, I've tried looking for an Application_Start event in the HttpApplication context object, there isn't one unfortunately.

This begs the question, why not just use the Application_StartUp event in the Global.asax which is guaranteed to run just once? Well there's nothing wrong with that method if you have full control over the web application code. But you can't use this method if you're writing a reusable HttpModule that can be dropped into any ASP.NET web application via a *.dll file, with bare minimum configuration required (just registration in web.config), so you have to rely on the Init method in this case.

Subtle Bugs

I ran into this problem in some IoC (Inversion of Control) Container registration logic contained in the Init method. If the code was run more than once, it would try to register the same types multiple times which would cause the IoC Container (Castle Windsor) to throw an exception. During my development and testing, only a single HttpApplication object was being created, and the Init method only being called once. However certain situations were creating lots of HttpApplication instances, causing this exception. Had I not discovered this issue, I could have deployed the application in this state, where the onslaught of multiple HTTP requests from incoming users created the conditions to have the Init method called multiple times, throwing the aforementioned exception.

Obviously proper load testing would pick this up, but it just goes to show that you can never be too sure what your code is doing, and when it runs, unless you have a deep understanding of what's going on under the covers.

3 Comments on "IHttpModule Gotchas - Init()"

Post a Comment
  • RE: IHttpModule Gotchas - Init()

    The number of HttpApplication instances (and thus, the number of instances for each HTTP Module) is tied to the number of worker threads. It has nothing to do with the number of connections each client opens to the server.

    From the one BeingRequest event until the next BeginRequest event you can rely on being on the same request. This reduces locking, for example.

    HTTP modules should be built to be independent form each other (other instances of the same module or other modules). If some shared state or configuration is needed, it should be on another class.

    http://msdn.microsoft.com/en-us/library/ms227673(VS.100).aspx

    Posted on 15 March 2010 - 5:48 PM / by Paulo Morgado

  • RE: IHttpModule Gotchas - Init()

    I used a mutex to handle this:

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using BitFactory.Logging;
    using System.Threading;
    namespace MutexModule
    {
    public class MutexHttpModule : IHttpModule
    {
    #region IHttpModule Members
    static Boolean BeenSet;
    Boolean ownsMutext;
    public void Dispose()
    {
    }
    public void Init(HttpApplication context)
    {
    EmailLogger mailer = new EmailLogger(new System.Net.Mail.SmtpClient(), "mutex@xyz.org", "ghandlin@xyz.org");
    FileLogger filer = new FileLogger(@"c:\temp\mutex.log");
    mailer.LogInfo("Init - this may come many times.");
    filer.LogInfo("Init - this may come many times.");
    if (!BeenSet)
    {
    BeenSet = true;
    using (Mutex mutex = new Mutex(true, "MutexModule", out ownsMutext))
    {
    if (ownsMutext)
    {
    mailer.LogInfo("This is in the mutex - should only get one.");
    filer.LogInfo("This is in the mutex - should only get one.");
    mutex.ReleaseMutex();
    }
    }
    }
    }
    #endregion
    }
    }

    Posted on 12 March 2010 - 3:49 PM / by George Handlin

  • RE: IHttpModule Gotchas - Init()

    Having just recently been bitten by this particularly nasty critter I have one simple question: Where were you with this about six months ago!? Well-done documentation of a subtle bug and its fix.

    Posted on 12 March 2010 - 2:51 PM / by Seth Derrick

Leave a Comment

Comment Details
*
* BBCode: [b]bold[/b], [i]italics[/i], [code]code[/code], [li]bullet point[/li], [h]Heading[/h], [url="http://www.example.com"]link[/url], [quote author="John Smith"]quote[/quote]

Random Image

Insanely Cute Hamster

Insanely Cute Hamster (from the blog And So It Begins (part 2) )

Quick Poll

What is your DIP/IOC Container of choice?

Poll Vote
(see results)
View Comments (0) (See previous polls)

Latest Tweets

  • Red Bull gives you wings....that generate huge amounts of downforce #F1

    about 18 hours ago from Twitterrific
  • .vampire { -webkit-box-shadow: none; -webkit-box-reflection: none; } #cssjokes

    7:44 PM July 30th from Echofon
  • @edhenderson lol, lets get a trending topic going - .gangster .wrapper { color: #000; width: 150%; text-decoration: bling; } #cssjokes

    7:36 PM July 30th from Echofon
  • @weblivz I think the petition should be resubmitted but with security stuff taken out, as that's what the response purely focused on

    6:13 PM July 30th from Echofon
  • @weblivz I still think Chrome Frame can come to the rescue here, still keep their old browsers + legacy systems, no retraining costs etc.

    6:12 PM July 30th from Echofon

View Dominic Pettifer's Twitter page.