[TOC]

Writing an ISAPI Filter

This section discusses issues related to writing Internet Server API (ISAPI) filters for the Microsoft Internet Information Server.

[TOC]

Overview

This documentation describes the basics of writing an ISAPI (Internet Server API) filter for the Microsoft® Internet Information Server. An ISAPI filter is a replaceable, dynamic-link library (DLL) the server calls on every HTTP (Hypertext Transfer Protocol) request. When the filter is loaded, it tells the server what sort of notifications it will accept. After that, whenever a selected event occurs, the filter is called and given the opportunity to process the event.

ISAPI filters are powerful enough to accomodate the following applications:

· Custom authentication schemes

· Compression

· Encryption

· Logging

· Traffic analysis or other request analysis (for example, looking for requests to "..\..\etc\password")

Multiple filters can be installed. The notification order is based on the priority specified by the filter and then the load order in the registry to resolve any ties.

Note Once a filter has indicated interest in a request, it will receive that data regardless of whether the request is for a file, a CGI (Common Gateway Interface) application, or an ISAPI application.

ISAPI filters can be used to enhance the Microsoft Internet Information Server with custom features such as enhanced logging of HTTP requests, custom encryption, compression schemes, or new authentication methods. The filter applications sit between the network connection to the clients and the HTTP server.

Depending on the options chosen by the filter application, it can act on several server actions, including reading raw data from the client, processing the headers, communications over a secure port (for example, PCT¾Private Communication Technology and SSL¾Secure Sockets Layer), or several other stages in the processing of the HTTP request.

The setup program that installs the ISAPI filter should add it to the registry under "HKEY_LOCAL_MACHINE\ System\ CurrentControlSet\ Services\ W3Svc\ Parameters\ Filter DLLs." This value is a comma-separated list. The filter should be added to the end of the list. When the filter is deinstalled, the string that was added should be removed with care in respect to those situations where other ISAPI filters have been added or removed since the DLL was installed.

[TOC]

Using ISAPI Filter Functions

Using the ISAPI filter functions requires the filter application's path to be installed as described above. When the server starts up, it reads this value and loads the DLLs listed. It then calls the GetFilterVersion entry point to exchange version information and determine the notifications desired and the priority at which to deliver them. As the events occur, the server will notify each filter application that registered for that event in the priority order requested by GetFilterVersion by calling the HttpFilterProc entry point. In the event of a tie, the order listed in the registry is used.

Every ISAPI filter DLL must export at least two entry points. These are GetFilterVersion and HttpFilterProc . The GetFilterVersion entry point is passed an HTTP_FILTER_VERSION structure that must be filled out with version information, requested events, and priority level. ISAPI filter applications should register only for the events that are required in that registering for extraneous events can have a significant, negative impact on performance and scalability.

After this exchange, every time the server processes one of the available events, it will call any filters that have registered for that event. The order in which the server will call the filters depends first on the priority specified in the dwFlags member of HTTP_FILTER_VERSION by GetFilterVersion. In the event that two or more different filters have registered for the same event at the same priority, the order that the filters were loaded from the registry determines the order in which they will be called.

When the HttpFilterProc entry point is called, the filter will typically perform a switch on the notificationType parameter to determine what action to take. For example, an encryption or compression filter will probably register for reading and writing raw data, while a logging filter will probably only register for the log event. Most filters will also register for the end of the net session event. This event is an opportune time to recycle any buffers used by that client request.

For performance reasons, most filters will probably keep a pool of filter buffers and only allocate or free them when the pool becomes empty or too large to save on the overhead of the memory management. One useful callback is the AllocMem callback in the HTTP_FILTER_CONTEXT structure. This allocates memory that is automatically freed when the communication with the client is terminated. As noted, this can have a negative impact on performance, but with careful use it can be a valuable tool.

[TOC]

Functions

The following functions are used by the Internet Information Server.

[TOC]

GetFilterVersion

The GetFilterVersion function is the first entry point called by the Internet Information Server.

BOOL WINAPI GetFilterVersion(
PHTTP_FILTER_VERSION pVer
);

Parameters

pVer

The HTTP_FILTER_VERSION structure pointed to by this parameter contains version information for the server and fields for the client to indicate version number, notifications, and priority desired. There is also a space for the filter application to register a description of itself.

Return Values

The return code indicates if the filter was properly loaded. If the filter returns FALSE, the filter application will be unloaded and will not receive any notifications.

Remarks

The GetFilterVersion function, implemented in the ISAPI filter application, is the first entry point called by the Internet Information Server. It is important to specify only the necessary notifications in the pVer->dwFlags member. Some notifications can have a strong impact on performance and scalability. In addition to the notification flags described under the HttpFilterProc function, there are also priority flags to specify the order in which to call the filter:
ValueMeaning
SF_NOTIFY_ORDER_DEFAULTLoad the filter at the default priority (recommended).
SF_NOTIFY_ORDER_LOWLoad the filter at the low priority.
SF_NOTIFY_ORDER_MEDIUMLoad the filter at a medium priority.
SF_NOTIFY_ORDER_HIGHLoad the filter at a high priority.

See Also

HttpFilterProc , HTTP_FILTER_VERSION

[TOC]

HttpFilterProc

DWORD WINAPI HttpFilterProc(
PHTTP_FILTER_CONTEXT pfc,
DWORD notificationType,
LPVOID pvNotification
);

Parameters

pfc

The HTTP_FILTER_CONTEXT structure pointed to by this parameter contains context information. The pFilterContext member can be used by the filter to associate any context information with the HTTP request. The SF_NOTIFY_END_OF_NET_SESSION notification can be used to release any such context information.

notificationType

This indicates the type of event being processed. Valid types are:
ValueMeaning
SF_NOTIFY_SECURE_PORTNotify application only for sessions over a secure port.
SF_NOTIFY_NONSECURE_PORTNotify application only for sessions over a nonsecure port.
SF_NOTIFY_READ_RAW_DATAAllow the application to see the raw data. The data returned will contain both headers and data.
SF_NOTIFY_PREPROC_HEADERSThe server has preprocessed the headers.
SF_NOTIFY_AUTHENTICATIONThe server is authenticating the client.
SF_NOTIFY_URL_MAPThe server is mapping a logical URL to a physical path.
SF_NOTIFY_SEND_RAW_DATAThe server is sending raw data back to the client.
SF_NOTIFY_LOGThe server is writing information to the server log.
SF_NOTIFY_END_OF_NET_SESSIONThe session with the client is ending.

pvNotification

The notification-specific structure.
Notification Type pvNotification points to
SF_NOTIFY_READ_RAW_DATA SF_NOTIFY_SEND_RAW_DATA HTTP_FILTER_RAW_DATA
SF_NOTIFY_PREPROC_HEADERSHTTP_FILTER_PREPROC_HEADERS
SF_NOTIFY_AUTHENTICATIONHTTP_FILTER_AUTHENT
SF_NOTIFY_URL_MAPHTTP_FILTER_URL_MAP
SF_NOTIFY_LOGHTTP_FILTER_LOG

Return Values

The return codes indicate how the application handled the event. Possible return codes are:
Return Values Meaning
SF_STATUS_REQ_FINISHEDThe filter has handled the HTTP request. The server should disconnect the session.
SF_STATUS_REQ_FINISHED_KEEP_CONNThis is the same as SF_STATUS_REQ_FINISHED, except that the server should keep the TCP session open if the option was negotiated.
SF_STATUS_REQ_NEXT_NOTIFICATIONThe next filter in the notification chain should be called.
SF_STATUS_REQ_HANDLED_NOTIFICATIONThis filter handled the notification. No other handlers should be called for this particular notification type.
SF_STATUS_REQ_ERRORAn error occurred. The server should call GetLastError and indicate the error to the client.
SF_STATUS_REQ_READ_NEXTThe filter is an opaque stream filter and the session parameters are being negotiated. This is valid only for raw read notification.

Remarks

This is where the core work of the ISAPI filter applications is done. The various structures pointed to by pvNotification contain data and function pointers specific to these operations. See the structure details for more information.

See Also

HTTP_FILTER_CONTEXT , HTTP_FILTER_RAW_DATA , HTTP_FILTER_PREPROC_HEADERS , HTTP_FILTER_AUTHENT , HTTP_FILTER_URL_MAP , HTTP_FILTER_LOG

[TOC]

Structures

The following structures are used by the Internet Information Server.

[TOC]

HTTP_FILTER_VERSION

typedef struct _HTTP_FILTER_VERSION
{
DWORD dwServerFilterVersion;
DWORD dwFilterVersion;
CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1];
DWORD dwFlags;
} HTTP_FILTER_VERSION, *PHTTP_FILTER_VERSION;

Members

dwServerFilterVersion

[in] The version of the specification used by the server. The version of the current header file is HTTP_FILTER_REVISION.

dwFilterVersion

[out] The version of the specification used by the server. The version of the current header file is HTTP_FILTER_REVISION.

lpszFilterDesc

[out] The location in which to store a short string description of the ISAPI filter application.

dwFlags

[out] The combination of SF_NOTIFY_* flags to specify which events this application is interested in. See HttpFilterProc for a list of valid flags.

Remarks

This structure is passed to the application's HttpFilterProc entry point by the server.

See Also

HttpFilterProc

[TOC]

HTTP_FILTER_CONTEXT

typedef struct _HTTP_FILTER_CONTEXT
{
DWORD cbSize;
DWORD Revision;
PVOID ServerContext;
DWORD ulReserved;
BOOL fIsSecurePort;
PVOID pFilterContext;
BOOL (WINAPI * GetServerVariable) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszVariableName,
LPVOID lpvBuffer,
LPDWORD lpdwSize
);
BOOL (WINAPI * AddResponseHeaders) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszHeaders,
DWORD dwReserved
);
BOOL (WINAPI * WriteClient) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPVOID Buffer,
LPDWORD lpdwBytes,
DWORD dwReserved
);
VOID * (WINAPI * AllocMem) (
struct _HTTP_FILTER_CONTEXT * pfc,
DWORD cbSize,
DWORD dwReserved
);
BOOL (WINAPI * ServerSupportFunction) (
struct _HTTP_FILTER_CONTEXT * pfc,
enum SF_REQ_TYPE sfReq,
PVOID pData,
DWORD ul1,
DWORD ul2
);
} HTTP_FILTER_CONTEXT, *PHTTP_FILTER_CONTEXT;

Members

cbSize

[in] The size of this structure, in bytes.

Revision

[in] The revision level of this structure. This is less than or equal to the version of the specification, HTTP_FILTER_REVISION.

ServerContext

[in] Reserved for server use.

ulReserved

[in] Reserved for server use.

fIsSecurePort

[in] TRUE indicates that this event is over a secure port.

pFilterContext

[in/out] A pointer to be used by the filter for any context information that the filter wants to associate with this request. Any memory associated with this request can be safely freed during the SF_NOTIFY_END_OF_NET_SESSION notification.

BOOL (WINAPI * GetServerVariable) (
struct _HTTP_FILTER_CONTEXT *pfc,
LPSTR lpszVariableName,
LPVOID lpvBuffer,
LPDWORD lpdwSize
);

A pointer to a function to retrieve information about the server and this connection. See the ISAPI application documentation for GetServerVariable for more information.

Parameters

pfc

The pfc passed to HttpFilterProc.

lpszVariableName

The server variable to retrieve.

lpvBuffer

The buffer in which to store the value of the variable

lpdwSize

The size of the buffer pointed to by lpvBuffer.

BOOL (WINAPI * AddResponseHeaders) (
struct _HTTP_FILTER_CONTEXT *pfc,
LPSTR lpszHeaders,
DWORD dwReserved
);

A pointer to a function that adds a header to the HTTP response. See the ISAPI documentation on ServerSupportFunction, HSE_SEND_RESPONSE_HEADER for more information.

Parameters

pfc

The pfc passed to HttpFilterProc.

lpszHeaders

A pointer string containing the headers to add.

dwReserved

Reserved for future use. This must be zero.

BOOL (WINAPI * ) (
struct _HTTP_FILTER_CONTEXT *pfc,
. LPVOID buffer,
LPDWORD lpdwBytes,
DWORD dwReserved
);

A pointer to a function that sends raw data back to the client. See the ISAPI documentation on WriteClient for more information.

Parameters

pfc

The pfc passed to HttpFilterProc.

buffer

A buffer containing data to send to the client.

lpdwBytes

The size of the buffer pointed to by buffer.

dwReserved

Reserved for future use.

VOID * (WINAPI * AllocMem) (
struct _HTTP_FILTER_CONTEXT *pfc,
DWORD cbSize,
DWORD dwReserved
);

A pointer to a function used to allocate memory. Any memory allocated with this function will automatically be freed when the request is completed.

Parameters

pfc

The pfc passed to HttpFilterProc.

cbSize

The size of the buffer to allocate.

dwReserved

Reserved for future use.

BOOL (WINAPI * ServerSupportFunction) (
struct _HTTP_FILTER_CONTEXT *pfc,
enum SF_REQ_TYPE sfReq,
PVOID pData,
DWORD ul1,
DWORD ul2
);

A pointer to a function used to extend the ISAPI filter functions. Parameters are specific to the extensions. Possible values for sfReq are SF_REQ_SEND_RESPONSE_HEADER, SF_REQ_ADD_HEADERS_ON_DENIAL, and SF_REQ_SET_NEXT_READ_SIZE.

SF_REQ_SEND_RESPONSE_HEADER

This sends a complete HTTP server response header including the status, server version, message time, and MIME version. Server extensions should append other information, such as Content-type and Content-length, followed by an extra "\r\n".

Parameters

pData

A zero-terminated string pointing to optional status string (for example, "401 Access Denied") or NULL for the default response of "200 OK".

ul1

A zero-terminated string pointing to optional data to be appended and sent with the header. If NULL, the header will be terminated with an empty line.

SF_REQ_ADD_HEADERS_ON_DENIAL

If the server denies the HTTP request, add the specified headers to the server error response. This allows an authentication filter to advertise its services without filtering every request. Generally, the headers will be WWW-Authenticate headers with custom authentication schemes. However, no restriction is placed on what headers can be specified.

Parameter

pData

A zero-terminated string pointing to one or more header lines with terminating "\r\n."

SF_REQ_SET_NEXT_READ_SIZE

This is used only by raw data filters that return SF_STATUS_READ_NEXT.

Parameter

ul1

The size in bytes for the next read.

[TOC]

HTTP_FILTER_RAW_DATA

typedef struct _HTTP_FILTER_RAW_DATA
{
PVOID pvInData;
DWORD cbInData;
DWORD cbInBuffer;
DWORD dwReserved;
} HTTP_FILTER_RAW_DATA, *PHTTP_FILTER_RAW_DATA;

Members

pvInData

[in] A pointer to the data buffer (input or output).

cbInData

[in] The amount of data in the buffer pointed to by pvInData.

cbInBuffer

[in] The size of the buffer pointed to by pvInData.

dwReserved

[in] Reserved for future use.

Remarks

This structure is passed to the SF_NOTIFY_READ_RAW_DATA and SF_NOTIFY_SEND_RAW_DATA notification routines.

See Also

HttpFilterProc

[TOC]

HTTP_FILTER_PREPROC_HEADERS

typedef struct _HTTP_FILTER_PREPROC_HEADERS
{
BOOL (WINAPI * GetHeader) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszName,
LPVOID lpvBuffer,
LPDWORD lpdwSize
);
BOOL (WINAPI * SetHeader) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszName,
LPSTR lpszValue
);
BOOL (WINAPI * AddHeader) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszName,
LPSTR lpszValue
);
DWORD dwReserved;
} HTTP_FILTER_PREPROC_HEADERS, *PHTTP_FILTER_PREPROC_HEADERS;

Members

BOOL (WINAPI * GetHeader) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszName,
LPVOID lpvBuffer,
LPDWORD lpdwSizeofBuffer
);

A pointer to a function that retrieves the specified header value. Header names should include the trailing colon (":"). The special values "method", "url", and "version" can be used to retrieve the individual portions of the request line.

Parameters

pfc

The filter context for this request from the pfc passed to the HttpFilterProc.

lpszName

The name of the header to retrieve.

lpvBuffer

A pointer to a buffer of size lpdwSizeofBuffer where the value of the header will be stored.

lpdwSizeofBuffer

Should be set to the size of the buffer lpvBuffer, e.g., sizeof(lpvBuffer). After the call, it contains the number of bytes retrieved including the null terminator, therefore for retrieved strings it is equal to strlen(lpvBuffer)+1.

BOOL (WINAPI * SetHeader) (
struct _HTTP_FILTER_CONTEXT *pfc,
LPSTR lpszName,
LPSTR lpszValue
);

A pointer to a function used to change or delete the value of a header.

Parameters

pfc

The filter context for this request from the pfc passed to the HttpFilterProc.

lpszName

A pointer to the name of the header to change or delete.

lpszValue

A pointer to the string to change the header to, or a pointer to "\0" to delete the header.

BOOL (WINAPI * AddHeader) (
struct _HTTP_FILTER_CONTEXT *pfc,
LPSTR lpszName,
LPSTR lpszValue
);

A pointer to a function to add a header.

Parameters

pfc

The filter context for this request from the pfc passed to the HttpFilterProc.

lpszName

A pointer to the name of the header to change or delete.

lpszValue

A pointer to the string to change the header to, or a pointer to "\0" to delete the header.

Remarks

This structure is pointed to by the pvNotification in the HttpFilterProc when notificationType is SF_NOTIFY_PREPROC_HEADERS, when the server is about to process the client headers.

See Also

HttpFilterProc

[TOC]

HTTP_FILTER_AUTHENT

typedef struct _HTTP_FILTER_AUTHENT
{
CHAR * pszUser;
DWORD cbUserBuff;
CHAR * pszPassword;
DWORD cbPasswordBuff;
} HTTP_FILTER_AUTHENT, *PHTTP_FILTER_AUTHENT;

Members

pszUser

[in/out] A pointer to a string containing the username for this request. An empty string indicates an anonymous user.

cbUserBuff

[in] The size of the buffer pointed to by pszUser. This is guaranteed to be at least SF_MAX_USERNAME.

pszPassword

[in/out] A pointer to a string containing the password for this request.

cbPasswordBuff

[in] The size of the buffer pointed to by pszPassword. This is guaranteed to be at least SF_MAX_PASSWORD.

Remarks

This structure is pointed to by the pvNotification in the HttpFilterProc when notificationType is SF_NOTIFY_AUTHENTICATION, when the server is about to authenticate the client. This can be used to implement a different authentication scheme.

See Also

HttpFilterProc

[TOC]

HTTP_FILTER_URL_MAP

typedef struct _HTTP_FILTER_URL_MAP
{
const CHAR * pszURL;
CHAR * pszPhysicalPath;
DWORD cbPathBuff;
} HTTP_FILTER_URL_MAP, *PHTTP_FILTER_URL_MAP;

Members

pszURL

[in] A pointer to the URL that is being mapped to a physical path.

pszPhysicalPath

[in/out] A pointer to the buffer where the physical path is stored.

cbPathBuff

[in] The size of the buffer pointed to by pszPhysicalPath.

Remarks

This structure is pointed to by the pvNotification in the HttpFilterProc when notificationType is SF_NOTIFY_URL_MAP, when the server is about to map the specified URL to a physical path. Filters can modify the physical path in place.

See Also

HttpFilterProc

[TOC]

HTTP_FILTER_LOG

typedef struct _HTTP_FILTER_LOG
{
const CHAR * pszClientHostName;
const CHAR * pszClientUserName;
const CHAR * pszServerName;
const CHAR * pszOperation;
const CHAR * pszTarget;
const CHAR * pszParameters;
DWORD dwHttpStatus;
DWORD dwWin32Status;
} HTTP_FILTER_LOG, *PHTTP_FILTER_LOG;

Members

pszClientHostName

[in/out] The client's host name.

pszClientUserName

[in/out] The client's user name.

pszServerName

[in/out] The name of the server the client is connected to.

pszOperation

[in/out] The HTTP command.

pszTarget

[in/out] The target of the HTTP command.

pszParameters

[in/out] The parameters passed to the HTTP command.

dwHttpStatus

[in/out] The HTTP return status.

dwWin32Status

[in/out] The Win32 error code.

Remarks

This structure is pointed to by the pvNotification in the HttpFilterProc when notificationType is SF_NOTIFY_LOG, when the server is about to log information to the server log file. The strings cannot be changed, but pointers can be replaced. If string pointers are changed, the memory they point to must have been allocated by the AllocMem callback function in the HTTP_FILTER_CONTEXT structure.

[TOC]

The DLL Entry Points

Every filter is contained in a separate DLL with two common entry points: GetFilterVersion and HttpFilterProc . When the DLL is loaded, GetFilterVersion is called, which lets the filter know the version of the server and lets the filter tell the server the version of the filter and the events that the filter is interested in.

After this, the server will call the filter's HttpFilterProc entry point with appropriate notifications. Note that filters should only register for notifications that the filter needs to see ¾ some filter notifications are very expensive in terms of CPU resources and I/O throughput, and can have a significant effect on the speed and scalability of the Microsoft Internet Information Server.

BOOL WINAPI GetFilterVersion(
HTTP_FILTER_VERSION *pVer
);
DWORD WINAPI HttpFilterProc(
HTTP_FILTER_CONTEXT *pfc,
DWORD notificationType,
VOID *pvNotification
);