ISAPI Filters

An ISAPI server extension DLL is loaded the first time a client references it in a GET or POST request. An ISAPI filter DLL is loaded (based on a Registry entry) when the WWW service is started. The filter is then in the loop for all HTTP requests, so you can read and/or change any data that enters or leaves the server.

Writing an ISAPI Filter DLL

The ISAPI Extension Wizard makes writing filters as easy as writing server extensions. Choose Generate A Filter Object, and Step 2 looks like this.

Click to view at full size.

The list of options under Which Notifications Will Your Filter Process? refers to seven places where your filter can get control during the processing of an HTTP request. You check the boxes, and the wizard generates the code.

The MFC ISAPI Filter Classes

There are two MFC classes for ISAPI filters, CHttpFilter and CHttpFilterContext.

CHttpFilter

With the help of the ISAPI Extension Wizard, you derive a class from CHttpFilter for each ISAPI filter you create. There's just one object of this class. The class has virtual functions for each of seven notifications. The list of filters in the order in which IIS calls them is below.

virtual DWORD OnReadRawData(CHttpFilterContext* pCtxt,
                            PHTTP_FILTER_RAW_DATA pRawData);
virtual DWORD OnPreprocHeaders(CHttpFilterContext* pCtxt,
                               PHTTP_FILTER_PREPROC_HEADERS pHeaderInfo);
virtual DWORD OnUrlMap(CHttpFilterContext* pCtxt,

                       PHTTP_FILTER_URL_MAP pMapInfo);
virtual DWORD OnAuthentication(CHttpFilterContext* pCtxt,
                               PHTTP_FILTER_AUTHENT pAuthent);
virtual DWORD OnSendRawData(CHttpFilterContext* pCtxt,
                            PHTTP_FILTER_RAW_DATA pRawData);
virtual DWORD OnLog(CHttpFilterContext* pfc, PHTTP_FILTER_LOG pLog);
virtual DWORD OnEndOfNetSession(CHttpFilterContext* pCtxt);

If you override a function, you get control. It would be inefficient, however, if IIS made virtual function calls for every notification for each transaction. Another virtual function, GetFilterVersion, is called once when the filter is loaded. The ISAPI Extension Wizard always overrides this function for you, and it sets flags in the function's pVer parameter, depending on which notifications you want. Here's a simplified sample with all the flags set:

BOOL CMyFilter::GetFilterVersion(PHTTP_FILTER_VERSION pVer)
{
    CHttpFilter::GetFilterVersion(pVer);
    pVer->dwFlags |= SF_NOTIFY_ORDER_LOW | SF_NOTIFY_NONSECURE_PORT | 
        SF_NOTIFY_LOG | SF_NOTIFY_AUTHENTICATION | 
        SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_READ_RAW_DATA | 
        SF_NOTIFY_SEND_RAW_DATA | SF_NOTIFY_URL_MAP | 
        SF_NOTIFY_END_OF_NET_SESSION;
    return TRUE;
}

If you had specified URL mapping requests only, the wizard would have set only the SF_NOTIFY_URL_MAP flag and it would have overridden only OnUrlMap. IIS would not call the other virtual functions, even if they were overridden in your derived class.

CHttpFilterContext

An object of this second MFC class exists for each server transaction, and each of the notification functions gives you a pointer to that object. The CHttpFilterContext member functions you might call are GetServerVariable, AddResponseHeaders, and WriteClient.