Implementing an Asynchronous HttpModule

When I was researching REST and writing my part of Scott's and my book on REST (Effective REST Services via .NET: For .NET Framework 3.5), on page 234 I mentioned that the authentication/authorization module I'd written should be asynchronous and that someday I'd write an asynchronous version. Thanks to reader Carl asking me to come through on my promise, I've completed the asynchronous version. Actually, I refactored the synchronous version as well so that both implementations can share the code that actually deals with authentication and authorization. If you've read the book and have downloaded the source (which I updated, downloadable from here), you can download the AuthModule.cs files themselves for Chapters 6-8 here.

I noticed a distinct lack of example implementations on the Web while deciding how I wanted to implement my version of the asynch module. Jeff Prosise has the most-referenced version (available here), but this wasn't complete enough for my needs. Jeff's version uses FileStream.BeginWrite to generate the necessary IAsyncResult object. In my case, I couldn't use that (of course) since my module is performing authentication and authorization for RESTful services.

Jeff still provides a hint, though. In his article he shows an asynchronous HttpHandler, and the code for that handler implements IAsyncResult. I wanted a touch more functionality--a boolean return value and exception support--so I modified his work to look like the following:

/// <summary>
/// A class containing an IAsyncResult implementation
/// that wraps a ManualResetEvent. Note this is  relatively
/// simple implementation. A more complete (generic)
/// implementation is available by Jeffrey Richter, found at
/// http://msdn.microsoft.com/en-us/magazine/cc163467.aspx.
/// My thanks to Jeff Prosise for the basis of this
/// implementation.
/// </summary>
internal class AsyncAuthResult : IAsyncResult
{
    private AsyncCallback _callback;
    private object _state;
    private ManualResetEvent _event;
    private bool _completed = false;
    private bool _result = false;
    private object _lock = new object();
    private Exception _ex = null;

    public AsyncAuthResult(AsyncCallback callback,
            object state)
    {
        _callback = callback;
        _state = state;
    }

    public Object AsyncState
    {
        get { return _state; }
    }

    public bool CompletedSynchronously
    {
        get { return false; }
    }

    public bool IsCompleted
    {
        get { return _completed; }
    }

    public WaitHandle AsyncWaitHandle
    {
        get
        {
            lock (_lock)
            {
                if (_event == null)
                    _event = new ManualResetEvent(IsCompleted);
                return _event;
            }
        }
    }

    public void CompleteCall(bool result, Exception exception)
    {
        // Passing null for exception means no error occurred
        _ex = exception;

        // Cache result
        _result = result;

        lock (_lock)
        {
            _completed = true;
            if (_event != null)
                _event.Set();
        }

        if (_callback != null)
        {
            // Invoke callback...
            _callback(this);
        }
    }

    public bool EndInvoke()
    {
        // Operation is done...if an exception occured, throw it
        if (_ex != null) throw _ex;

        return _result;
    }
}

I then created an "authentication manager" that implements the "begin" and "end" methods I'd need in my module:

internal sealed class AuthenticationManager
{
    const string BasicAuthProtocol = "basic";
    HttpContext _context = null;

    public AuthenticationManager(HttpContext currentContext)
    {
        _context = currentContext;
    }

    public bool AuthenticateUser()
    {
        return InternalAuthenticateUser();
    }

    public IAsyncResult BeginAuthenticateUser(AsyncCallback callback,
                                              Object state)
    {
        // Create IAsyncResult object identifying the 
        // asynchronous operation.
        AsyncAuthResult ar = new AsyncAuthResult(callback, state);

        // Create a new thread to perform the operation...note we can't
        // use a thread from the thread pool as this defeats the purpose
        // of the asynchronous implementation (we're trying to leave
        // *those* threads alone for ASP.NET to use to service incoming
        // requests).
        Thread worker = new Thread(this.InternalAuthenticateUserAsync);
        worker.Start(ar);

        return ar;  // Return the IAsyncResult to the caller
    }

    public bool EndAuthenticateUser(IAsyncResult asyncResult)
    {
        // We know that it's really an AsyncAuthResult object
        AsyncAuthResult ar = asyncResult as AsyncAuthResult;
        return ar.EndInvoke();
    }

    private void InternalAuthenticateUserAsync(Object asyncResult)
    {
        // We know that it's really an AsyncAuthResult object
        AsyncAuthResult ar = asyncResult as AsyncAuthResult;

        // Perform the operation..if successful set the result. If
        // not, record the exception.
        try
        {
            bool authorized = this.InternalAuthenticateUser();
            ar.CompleteCall(authorized, null);
        }
        catch (Exception ex)
        {
            ar.CompleteCall(false, ex);
        }
    }

    private bool InternalAuthenticateUser()
    {
        // Authorize the user (using HttpContext for Authorization
        // header access) and return success/failure.
    }
}

The module itself then performs the typical asynchronous event connections. In the "begin" authentication event, I create an instance of my authentication manager, call its "begin" method (which spins up a new thread...more in a moment on that), and waits for authentication to complete. Upon completion, it calls the authentication manager's "end" method to retrieve success/failure and proceeds from there:

public class AsyncAuthModule : IHttpModule
{
    #region Fields

    // We *must* cache the context. When the "end" method is
    // invoked, a CLR thread will be executing (versus an IIS
    // thread), so there is no "current" context available
    // for the thread to use. That is, HttpContext.Current
    // will return null. We can cache the context in this manner
    // even though three different threads will eventually
    // use this object since the runtime suspends this module
    // invocation entirely and restarts it in the "end" 
    // method.
    HttpContext _context = null;

    // We'll cache this as well so we can call its "end"
    // method.
    AuthenticationManager _authMgr = null;

    #endregion

    #region IHttpModule Members

    public void Dispose()
    {
    }

    public void Init(HttpApplication application)
    {
        // Hook authentication
        application.AddOnAuthenticateRequestAsync(
            new BeginEventHandler(BeginAuthenticateRequestHandlerExecute),
            new EndEventHandler(EndAuthenticateRequestHandlerExecute)
        );
    }

    #endregion

    #region Event handlers

    IAsyncResult BeginAuthenticateRequestHandlerExecute(Object source,
        EventArgs e, AsyncCallback cb, Object state)
    {
        // Pull the current context
        HttpApplication application = source as HttpApplication;
        _context = application.Context;

        // Create an instance of the authentication manager and invoke
        // its asynchronous method.
        _authMgr = new AuthenticationManager(_context);
        return _authMgr.BeginAuthenticateUser(cb, state);
    }

    void EndAuthenticateRequestHandlerExecute(IAsyncResult ar)
    {
        // Collect authentication/authorziation result...if true, the
        // user is good to go. If false, the context will contain the
        // appropriate response code and headers.
        bool authorized = _authMgr.EndAuthenticateUser(ar); // can throw...

        // If the context contains anything but "OK" we know
        // to terminate the request. The appropriate headers
        // will already be set up by the authentication manager
        // object.
        if (!authorized ||
            _context.Response.StatusCode != (Int32)HttpStatusCode.OK)
        {
            // End the request
            _context.Response.End();
        }
    }

    #endregion
}

As far as I can tell, this is the only example of a complete asynchronous HttpModule that fully implements IAsyncResult for a custom processing workflow that you can search (at least that I could find). I'm not sure why it's so hard to find others, but hopefully this helps someone else. Laughing With this example, and with Jeff's you should be able to get a working asynchronous HttpModule up and running.

One "gotcha" I'd mention is don't fall into the trap I fell into, which is using the wrong callback. That is, ASP.NET provides you with an instance of AsyncCallback as a parameter to the module's "begin" method. That callback is the one your processing should invoke so that ASP.NET knows your workflow is complete. It then will call the module's "end" method for you. Initially I created a new instance of AsyncCallback and passed that in, bypassing ASP.NET entirely. That resulted in the server hanging the page since I broke the HTTP pipeline. (In my best Homer Simpson voice, "Doh!"). In code, the following is correct:

return _authMgr.BeginAuthenticateUser(cb, state);

The following is most assuredly NOT correct:

return _authMgr.BeginAuthenticateUser(
        new AsyncCallback(this.EndAuthenticateRequestHandlerExecute), state);

Now, as for threading. A good question to ask is "why spin up your own thread and not use the thread pool?" The reason is asynchronous modules (and handlers/pages as well) all are assigned CLR threads from the AppDomain's thread pool when IIS hands off the request. If we were to consume one of the AppDomain's pooled threads, we're defeating the purpose of the asynchronous implementation entirely. This means you should not only avoid queueing a thread pool task but also should not use Delegate.BeginInvoke (a trick I often use to gain asynchronous behavior in other apps). Spinning up a new thread is the only alternative you have when you create your own custom asynchronous module/handler/page workflow objects. I've not cracked open other CLR implementations (like FileStream.BeginWrite), but hopefully if I did I'd find those aren't using the AppDomain's thread pool as well.


Tags: , , , ,
Categories: .NET General | ASP.NET | RESTful Services

9 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Comments

January 11. 2010 22:21

Jesse

Unlike the three spam comments above, I'd like to actually thank you for making good on your promise to rewrite the auth module in an asynchronous manner.

One question I have: why do we want to do this? I'm not used to working at such a low level as HttpModules, and I've never had good reason to take on the added complexity to make any of my pages asynchronous.

I guess this is an optimization:

(a) user loads page -> (b) HttpModule does some long running synchronous operation -> (c) page returns

Am I correct that during (b), an IIS worker thread is tied up, and thus not available for requests? And that if we rewrite (b) to be asynchronous, this frees up an IIS thread, at least until (c) runs, and then we can service more requests?

In other words, it's not so much a performance benefit (the page still returns in the same amount of clock time) but a capacity benefit (more requests can be served while waiting for response).

Am I on the right track? Do the various Microsoft-built HttpModules do operations in a similar asynchronous fashion?

Jesse

January 12. 2010 15:31

kenn

How about those spam comments. Smile I'm wondering why anyone would even want to do that, unless it somehow helps their stuff attain higher scores in a search engine. But at least they aren't in poor taste. Sometime I'll have to see if BlogEngine.Net can filter those.

Thread management and throughput. Most of us don't deal with Web applications that have truly stellar throughput requirements, and so we don't often see the need for going to such lengths. But a truly correct implementation would in fact be async, which works in either case (assuming it's implemented correctly). The throughput comes in when we don't tie up IIS threads...we're free to service those requests while the async task runs.

So basically you're 100% correct. Running a page, or a module/handler in an asynchronous manner doesn't speed the underlying request...it can't possibly. But it does allow IIS to serve other requests while the async request is running, so others don't sit and wait. And yes, the "out of the box" ASP.NET implementations for handlers are generally asynchronous (which you can see using Reflector).

kenn

April 19. 2010 05:36

Long Gentes

You made some good points there about asynchronous . I did a search on the topic and found people i've talked to will agree with your blog.

Long Gentes

April 20. 2010 18:47

Lyndsey Bankes

asynchronous asynchronous asynchronous Hello, U write some vastly attractive blogs. I always check back here repeatedly to determine in case you have updated

Lyndsey Bankes

April 20. 2010 20:30

Horacio Nesvig

Horacio Nesvig

April 20. 2010 20:30

Horacio Nesvig

Hello everybody. This weblog was fantastic! Lots of cool data and inspiration. Keep 'em coming... u all perform such a necessary work at writing... can not tell you just how much I, for one appreciate all you do! asynchronous asynchronous asynchronous ROCKS

Horacio Nesvig

April 21. 2010 14:13

graphic designer adelaide

Transfer text from Webapplication to a word document?

graphic designer adelaide

April 21. 2010 14:17

graphic designers melbourne

Closing a javascript pop up window in ASP.Net webapplication?

graphic designers melbourne

July 8. 2010 21:59

pingback

Pingback from topsy.com

Twitter Trackbacks for
        
        Kenn Scribner | Implementing an Asynchronous HttpModule
        [endurasoft.com]
        on Topsy.com

topsy.com

Add comment




  Country flag

Click to change captcha
biuquote
  • Comment
  • Preview
Loading