IIS7 modules vs. IIS6 ISAPI #10: C++ Class-based encapsulation model

For the first post in this series, lets start with the basics.

ISAPI Extensions and Filters are implemented as global exported functions within the extension/filter DLL.  For example, an ISAPI Filter will always contain an exported HttpFilterProc function that gets invoked by the server for each request mapped to it:

 

DWORD WINAPI HttpFilterProc( 
         PHTTP_FILTER_CONTEXT pfc,

         DWORD notificationType,

         LPVOID pvNotification );

This function would then typically call into several function pointers on the provided HTTP_FILTER_CONTEXT structure to interact with the server, such as getting request information or sending a response.

The new IIS7 module model is based on C++ classes – on module initialization, the module DLL’s registration entrypoint associates a factory class, implementing the IHttpModuleFactory interface, with one or more request notifications.  The factory in turn is responsible for creating an instance of your module class, deriving from the CHttpModule, for each request.

The server uses the factory to create an instance of your module's class for each request, and then calls a specific event handler function on your module’s instance for each of the events it registered for.  For example, if your module registered for AuthenticateRequest event, the OnAuthenticateRequest method of your module class will be called:

REQUEST_NOTIFICATION_STATUS
CMyHttpModule::OnAuthenticateRequest(    

         IN IHttpContext* pHttpContext,    

         IN IAuthenticationProvider* pProvider );

Ok, so at this point you may say “Great, you made me write a few extra classes to do what I would do before with a single function”.  You call this better?

Yes, much.  Here is why:

No giant switch statements. 
The server invokes an appropriate handler method on your module class based on the event, instead of calling into a single static entry-point that forces you to determine what the module is to do. 

Type safe interactions with the server. 

ISAPI Filter writers were forced to cast the PHTTP_FILTER_CONTEXT->pvNotification pointer to a specific structure based on the request processing stage that was determined in the giant switch statement (above), which of course would lead to memory access violations when they got it wrong. 

In contrast, the CHttpModule class provides a separate method for each server event, with type-safe “provider” arguments. 

Compare the code required to write an ISAPI Filter to authenticate requests vs. an IIS7 module – here is the filter:

DWORD WINAPI HttpFilterProc(

      PHTTP_FILTER_CONTEXT pfc,

      DWORD notificationType,

      LPVOID pvNotification

)

{

      PHTTP_FILTER_AUTHENT    pAuthContext = NULL;

      PHTTP_FILTER_SEND_RESPONSE pSendResponseContext = NULL;

      switch( notificationType )

      {

      case SF_NOTIFY_AUTHENTICATION:

            pAuthContext = (PHTTP_FILTER_AUTHENT)pfc->pvNotification;

            // use pAuthContext to set the user

            // …

            break;

      case SF_NOTIFY_SEND_RESPONSE:

            pSendResponseContext = (PHTTP_FILTER_SEND_RESPONSE)pfc->pvNotification;

            //  Use the pSendResponseContext to append authentication challenge headers

            // 

            break;

      default:

            // fail

            return false;

      }

}

With an IIS7 module, you simply implement the desired methods on your module class, and use the type-safe notification providers to do your work:

class CAuthenticationModule : CHttpModule

{

      REQUEST_NOTIFICATION_STATUS OnAuthenticateRequest(

            IN IHttpContext* pHttpContext,

            IN IAuthenticationProvider* pProvider

      )

      {

            // Set authenticated user

            // pProvider.SetUser( … );

      }

< /p>

      REQUEST_NOTIFICATION_STATUS OnSendResponse(

            IN IHttpContext* pHttpContext,

            IN ISendResponseProvider* pProvider

      )

      {

            // Append authentication challenge headers …

      }

);

Storing request scoped state in your module

One other huge advantage of the class encapsulation is the ability to store request-scoped state simply by declaring member variables in your module, as opposed to creating and managing void pointers to custom contexts throughout the duration of the request.  Managing request state, and module memory management will be the subject of a subsequent  post.

Using the server object model to do you work

Also notice that I didn’t actually show the code to do the actual work to set the authenticated user in the AuthenticateRequest event, or append the challenge header to force the browser to authenticate in the SendResponse event.  This is the topic for the next post – how the new type-safe IIS7 object model makes these types of tasks a walk in the park when compared to doing it through the generic ISAPI SupportFunction interface.

That’s it for the first post in this series.  If you are not convinced, or have questions about specific aspects of the old/new APIs, let me know and I will cover them in the upcoming posts on this topic.

4 Comments

  1. Anonymous

    Posted on 10/13/2006 6:44 PM:

    This is really interesting, a C++ API for IIS server. The fact that you can derive from CHttpModule and only re-implement functions you want is really cool.
    Good stuff, keep em coming.

  2. Anonymous

    Posted on 10/18/2006 9:46 AM:
    Though it would have been really interesting to see a ML API for the IIS server.

    But yeah, good stuff, keep em coming.

Leave a Reply

Your email address will not be published. Required fields are marked *