- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
ISAPI Filters
In addition to the ISAPI server extensions you already have seen, ISAPI also enables you to create extension DLLs that act as filters, processing various HTTP events either before or after the server has processed a request. You can use these filters to provide your own custom authentication, encryption, compression, or logging functions, as well as many other filtering operations.
Installing a Filter
For the Microsoft Internet Information Server (IIS), the filters that are used are specified in the Registry under the following value:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3Svcx\Parameters\Filter DLLs
This value is a comma-separated list of the filters that will currently be used by the server. The filters are processed in order of their priority, as you will see. In the event of a tie in priority, the filters will be processed in the order in which they are listed in the Registry.
ISAPI Filter Architecture
Like the extension DLLs you saw earlier, ISAPI filters are DLLs that are loaded into the Web server's process. Filter DLLs communicate with the server by way of a pair of entry points that are exported by the filter DLL.
The first of these entry points is GetFilterVersion(), which is called when your filter DLL is loaded, allowing your filter to report its supported version and to register for the events that it wants to handle.
When an event that your filter has registered for occurs, the second entry point, HttpFilterProc(), will be called. This function should perform its processing on the event before passing control back to the server. At this point, your filter may decide whether the event also should be handled by other filters in the current filter chain.
GetFilterVersion()
When the Web server loads a filter DLL, it will call the GetFilterVersion() function that is exported by your DLL. This function should have the following prototype:
BOOL WINAPI GetFilterVersion( PHTTP_FILTER_VERSION pVer);
The single parameter is a pointer to an HTTP_FILTER_VERSION structure, which looks like this:
typedef struct _HTTP_FILTER_VERSION
{
[in] DWORD dwServerFilterVersion;
[out] DWORD dwFilterVersion;
[out] CHAR lpszFilterDesc[SF_MAX_FILTER_DESC_LEN+1];
DWORD dwFlags;
} HTTP_FILTER_VERSION, *PHTTP_FILTER_VERSION;
When GetFilterVersion() is called, the server will pass its version in the dwServerFilterVersion field. You should pass the version that your filter is using back to the server in the dwFilterVersion field. For the current version of ISAPI, you can use the HTTP_FILTER_REVISION constant. You also can write a short ASCII string description of your filter in the buffer passed at lpszFilterDesc.
The real meat of this function is what you return in the dwFlags parameter. This value includes a combination (bitwise-OR) of the following flags, which specify which events your filter is interested in processing:
- SF_NOTIFY_READ_RAW_DATA allows the filter to process incoming data from the client, including headers.
- SF_NOTIFY_SEND_RAW_DATA allows the filter to process data that is being sent back to the client.
- SF_NOTIFY_PREPROC_HEADERS allows the filter to access the HTTP headers after they have been preprocessed by the server.
- SF_NOTIFY_AUTHENTICATION allows the filter to be involved in the user- authentication process.
- SF_NOTIFY_URL_MAP allows the filter to participate in the mapping of a URL to a physical path.
- SF_NOTIFY_SEND_RESPONSE allows the filter to process a response after the request has been processed by the ISAPI and before headers are sent back to the client.
- SF_NOTIFY_LOG allows the filter to be involved in the process of writing to the server log.
- SF_NOTIFY_END_OF_NET_SESSION tells the filter when the server is closing a session with a client.
- SF_NOTIFY_END_OF_REQUEST tells the filter when the server is at the end of a request.
- SF_NOTIFY_ACCESS_DENIED allows the server to process 401 Access Denied responses before they are sent to the client.
The following flags also should be included to specify whether you are interested in events on only secure connections, nonsecure connections, or both:
- SF_NOTIFY_SECURE_PORT—The filter will receive notifications only for events on secure ports.
- SF_NOTIFY_NONSECURE_PORT—The filter will receive notifications only for events on nonsecure ports.
You use the last set of flags that can be included in the dwFlags field to set the priority of your filter in relation to the other filters present on the system:
- SF_NOTIFY_ORDER_DEFAULT—The filter will have the default priority. This should be used for most filters.
- SF_NOTIFY_ORDER_LOW—The filter will be processed at a lower priority. This is useful for filters that don't care when they are notified—for example, for logging events.
- SF_NOTIFY_ORDER_MEDIUM—The filter will have a medium priority.
- SF_NOTIFY_ORDER_HIGH—The filter will have a high priority, receiving notifications before filters of lower priority.
You also may include any additional initialization code for your filter in the GetFilterVersion() function. (You also could implement a DllMain().) If all goes well, and your filter has initialized properly, you should return TRUE from GetFilterVersion(). If you return FALSE, the filter will not be loaded.
TerminateFilter()
Before unloading your filter, it is recommended that you call the TerminateFilter() function. This function should have the following prototype:
BOOL WINAPI TerminateFilter( DWORD dwFlags);
This filter enables you to free up any allocated or locked resources before you unload your filter. This filter is considered optional but is strongly recommended for the sake of releasing resources. Before calling this function, you should make sure that all attachments to system resources have been closed.
HttpFilterProc()
After your filter is loaded and has registered for the notifications that it wants to receive, the server will make a call to your HttpFilterProc() whenever one of the requested events occurs.
In this section, you will take a quick look at the basics of implementing HttpFilterProc(). In the following sections, you will take a closer look at more of the specifics of certain operations.
The prototype for your HttpFilterProc() should look something like this:
DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
DWORD notificationType, VOID *pvNotification);
Your filter is passed an HTTP_FILTER_CONTEXT structure via pfc. This structure provides information about the request, as well as pointers to several utility functions, as you will see in just a bit. The type of event that generated the notification is passed in notificationType. This may include any of the SF_NOTIFY_... values that were used in the GetFilterVersion() call. The value of notificationType also determines how the pvNotification pointer is used. You will look at the specifics of handling each notification type in the following sections.
Your implementation of HttpFilterProc() should perform a switch on the value passed in notificationType, doing whatever processing is necessary, and then should return one of the following values:
- SF_STATUS_REQ_FINISHED—The filter has satisfied the client's request, and the server should tear down the connection.
- SF_STATUS_REQ_FINISHED_KEEP_CONN—The filter has satisfied the client's request, but the server should keep the connection open.
- SF_STATUS_REQ_NEXT_NOTIFICATION—The next filter in the chain should be allowed to process the event.
- SF_STATUS_REQ_HANDLED_NOTIFICATION—This filter has handled the event, and no other filters should be notified.
- SF_STATUS_REQ_ERROR—An error has occurred. The server will call GetLastError() and forward the error to the client.
- SF_STATUS_REQ_READ_NEXT—This should be returned only when filtering on the SF_NOTIFY_READ_RAW_DATA event for stream filters that are negotiating session parameters.
The following example shows the complete implementation of a simple filter, which just adds a bit of graffiti to the Web server's log file:
#include <windows.h>
#include <stdio.h>
#include <httpfilt.h>
BOOL WINAPI GetFilterVersion( PHTTP_FILTER_VERSION pVer)
{
pVer->dwFilterVersion = HTTP_FILTER_REVISION;
strcpy(pVer->lpszFilterDesc, "My Sample Extension");
pVer->dwFlags =
SF_NOTIFY_SECURE_PORT | // Notify for both port types
SF_NOTIFY_NONSECURE_PORT |
SF_NOTIFY_LOG | // Notify when writing log
SF_NOTIFY_ORDER_LOW; // Filter at low priority
return TRUE;
}
DWORD WINAPI HttpFilterProc( PHTTP_FILTER_CONTEXT pfc,
DWORD notificationType,
VOID *pvNotification)
{
PHTTP_FILTER_LOG pLog;
char *pBuf;
switch(notificationType)
{
case SF_NOTIFY_LOG:
// This is the only case we are interested in
// We will modify the server name to show we were here
pLog = (PHTTP_FILTER_LOG) pvNotification;
// Allocate new memory for the new string
// The server will deallocate this when the request ends
pBuf = (char *) pfc->AllocMem(pfc, 100, 0);
// Write to our new string
sprintf(pBuf, "Server: [%s] Logged with MyFilt",
pLog->pszServerName);
// Replace the server name pointer
pLog->pszServerName = pBuf;
break;
default:
// We should not receive any other notifications
// Since we only registered for SF_NOTIFY_LOG
break;
}
// Tell the server to call the next filter
return(SF_STATUS_REQ_NEXT_NOTIFICATION);
} // end HttpExtensionProc
Although this filter may not be horribly practical, it does show the basic structure of a filter, including the processing for the SF_NOTIFY_LOG notification and the use of the AllocMem() function, which you will learn about later.
The HTTP_FILTER_CONTEXT Structure
Much of the interaction between your filter and the Web server is done through the HTTP_FILTER_CONTEXT structure that is passed in the call to HttpFilterProc():
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;
The cbSize field gives the size of this structure, and the Revision field gives the version of ISAPI being used. The ServerContext and ulReserved fields are reserved for use by the Web server—keep yer grubbies off. If the notification is for a secure connection, fIsSecurePort will be TRUE; otherwise, it will be FALSE.
If your filter wants to store any context information for this request, you can assign a context value (usually a pointer to a structure) to the pFilterContext field. If you store a value here, it also will be given to subsequent notifications that your filter receives in processing this request.
If you do allocate memory to store data for a request, you should free the memory when the SF_NOTIFY_END_OF_NET_SESSION notification is received (or whenever you know that you are finished with the data). You also should look at the AllocMem() function later in this section.
The remainder of the HTTP_FILTER_CONTEXT structure includes pointers to various utility functions the server provides to your filter.
GetServerVariable()
The GetServerVariable() function pointer passed to your filter DLL is not quite the same as the GetServerVariable() function that you saw for ISAPI extensions. This ver sion takes a pointer to the HTTP_FILTER_CONTEXT structure, which is passed to your HttpFilterProc() function instead of an HCONN. However, the variables that are available via this function and the way it uses the other parameters are the same as the GetServerVariable() function you saw earlier in this chapter.
AddResponseHeaders()
The AddResponseHeaders() function pointer passed to HttpFilterProc() can be used to attach additional HTTP headers to the response sent to the client. Here is the prototype for AddResponseHeaders():
BOOL (WINAPI * AddResponseHeaders) (PHTTP_FILTER_CONTEXT pfc,
LPSTR lpszHeaders, DWORD dwReserved);
This function takes a pointer to the filter context (pfc), which is passed to your HttpFilterProc() function, and a pointer to a null-terminated string containing the additional HTTP headers. dwReserved is reserved for future expansion.
WriteClient()
Like the WriteClient() callback that you saw for ISAPI applications, this function enables you to send data directly to the client. The following is the prototype for WriteClient():
BOOL (WINAPI * WriteClient) (PHTTP_FILTER_CONTEXT pfc,
LPVOID buffer, LPDWORD lpdwBytes, DWORD dwReserved);
When calling WriteClient(), you should pass the pointer to the filter context (as passed to your HttpFilterProc() function) in pfc, a pointer to the data to send in buffer, and a pointer to the length of the data in lpdwBytes. The dwReserved parameter currently is not used.
AllocMem()
If your filter needs to allocate memory when working with a request, you may find the AllocMem() function handy. This will allocate a block of memory that automatically is deallocated when the server is done with a request and tears down the connection. The prototype for AllocMem() follows:
VOID * (WINAPI * AllocMem) (PHTTP_FILTER_CONTEXT pfc,
DWORD cbSize, DWORD dwReserved);
Once again, pfc takes the pointer to the filter context, as passed to HttpFilterProc(), and dwReserved currently is not used. Upon successful completion, AllocMem() will return a pointer to a new block of memory of the size specified in cbSize.
ServerSupportFunction()
Like the ServerSupportFunction() that you saw for ISAPI applications, this function provides a variety of different utility functions to your filter. The following is the prototype for ServerSupportFunction():
BOOL (WINAPI * ServerSupportFunction) (struct _HTTP_FILTER_CONTEXT *pfc,
enum SF_REQ_TYPE sfReq, PVOID pData, DWORD ul1, DWORD ul2);
The operation performed is determined by the value of sfReq, which may have one of the following values:
SF_REQ_NORMALIZE_URL
This function is used to normalize the URL. Normalization involves changes such as decoding hex codes, internationalization coversions, and removing illegal characters.
SF_REQ_DISABLE_NOTIFICATIONS
This option is used to disable specific notification types for the ISAPI filter. It remains disabled for the remaining lifetime of the request.
SF_REQ_GET_PROPERTY
This option is used to retrieve IIS properties defined in SF_PROPERTY_IIS.
SF_REQ_SEND_RESPONSE_HEADER
This option sends a complete HTTP response header to the client. You may choose to specify a status string—for example, 401 Access Denied—in a string pointed to by pData. You also may specify additional headers, such as the content type, to append in a string pointed to by ul1. This string should include a terminating carriage return/linefeed (\r\n).
SF_REQ_ADD_HEADERS_ON_DENIAL
This option enables you to specify additional headers that will be sent to the client in the event that the server denies the HTTP request. The additional headers are passed in a string pointed to by pData. This string should include a terminating cr/lf (\r\n).
SF_REQ_SET_NEXT_READ_SIZE
For raw data filters that return SF_STATUS_READ_NEXT from HttpFilterProc(), you can use this option to specify the number of bytes to read in the next read. The number of bytes to read is passed in ul1.
SF_REQ_SET_PROXY_INFO
You can use this option to specify that a request is a proxy request. You should set ul1 to 1.
SF_REQ_GET_CONNID
This option returns the connection ID that is passed to ISAPI applications for this request. You can use this value to coordinate operations between your ISAPI applications and filters. pData should point to a DWORD that will receive the connection ID. This option is not supported in ISAPI 4.0 or higher.
Handling Filter Notifications
In this section, you will look at the different notifications that can be received by an ISAPI filter and the data that is passed with each type of notification.
SF_NOTIFY_READ_RAW_DATA
This notification is sent to your filter whenever the server is receiving raw data from the client. The pvNotification parameter of HttpFilterProc() will point to an HTTP_ FILTER_RAW_DATA structure:
typedef struct _HTTP_FILTER_RAW_DATA
{
PVOID pvInData;
DWORD cbInData;
DWORD cbInBuffer;
DWORD dwReserved;
} HTTP_FILTER_RAW_DATA, *PHTTP_FILTER_RAW_DATA;
The pvInData field points to a buffer containing the raw data, and the length of the data is passed in the cbInData field. This will include both HTTP headers and additional data. The total size of this buffer is passed in cbInBuffer.
SF_NOTIFY_SEND_RAW_DATA
This notification is sent to your filter when the server is sending data back to the client. The pvNotification parameter of HttpFilterProc() will point to an HTTP_FILTER_RAW_DATA structure, as you saw previously, which refers to the outgoing data.
SF_NOTIFY_PREPROC_HEADERS
This notification is sent to your filter when a request is received from a client. The pvNotification parameter of HttpFilterProc() will point to an HTTP_FILTER_ PREPROC_HEADERS structure, which includes pointers to utility functions that can be used to manipulate the headers for a request. These utility functions are discussed in the following sections.
GetHeader()
The first of these functions is GetHeader():
BOOL (WINAPI * GetHeader) (PHTTP_FILTER_CONTEXT pfc,
LPSTR lpszName, LPVOID lpvBuffer, LPDWORD lpdwSize);
This function enables you to retrieve the header with the name you specify in a string at lpszName. This name should include the trailing colon—for example, Content-Type:. You also may specify the special values of method, url, or version to retrieve portions of the HTTP request line.
If a header is found for the name passed in lpszName, it will be returned in a buffer at lpvBuffer.
SetHeader()
The second of the utility functions is SetHeader(), which you can use to change the value of a header or even delete an existing header:
BOOL (WINAPI * SetHeader) (PHTTP_FILTER_CONTEXT pfc,
LPSTR lpszName, LPSTR lpszValue);
This function enables you to specify a new value for the header named in the string at lpszName. The new value for the header is passed in a string at lpszValue. If this string is empty, the specified header will be deleted.
AddHeader()
You can use the third utility function, AddHeader(), to add additional headers:
BOOL (WINAPI * SetHeader) (PHTTP_FILTER_CONTEXT pfc,
LPSTR lpszName, LPSTR lpszValue);
The name of the new header is specified in a string at lpszName, and the value of the new header is passed in a string at lpszValue.
SF_NOTIFY_AUTHENTICATION
This notification is sent to your filter when the server is about to authenticate a user making a request. The pvNotification parameter of HttpFilterProc() will point to an HTTP_FILTER_AUTHENT structure:
typedef struct _HTTP_FILTER_AUTHENT
{
CHAR * pszUser;
DWORD cbUserBuff;
CHAR * pszPassword;
DWORD cbPasswordBuff;
} HTTP_FILTER_AUTHENT, *PHTTP_FILTER_AUTHENT;
This structure contains pointers to the username (pszUser) and password (pszPassword) for the user being authenticated. If the user is anonymous, these strings will be empty. The cbUserBuff and cbPasswordBuff fields give the total size of the buffers holding the username and password. Your filter can change the contents of these buffers, provided they are not overflowed. These buffers will be at least SF_MAX_USERNAME and SF_MAX_PASSWORD bytes long, respectively.
SF_NOTIFY_URL_MAP
This notification is sent when the server is attempting to map a URL to a physical path. The lpvNotification parameter of HttpFilterProc() will point to an HTTP_FILTER_URL_MAP structure:
typedef struct _HTTP_FILTER_URL_MAP
{
const CHAR * pszURL;
CHAR * pszPhysicalPath;
DWORD cbPathBuff;
} HTTP_FILTER_URL_MAP, *PHTTP_FILTER_URL_MAP;
The requested URL is passed in the string at pszURL, and the physical path that it is being mapped to is given at pszPhysicalPath. Your filter may change the string at pszPhysicalPath, provided you do not go over the size of the buffer given in cbPathBuf.
SF_NOTIFY_LOG
This notification is passed to your filter when the server is about to write an entry to its log file. The lpvNotification parameter of HttpFilterProc() is passed a pointer to an HTTP_FILTER_LOG structure:
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;
The pointers in this structure refer to the client's hostname, username, and server name, as well as the operation, target, and parameters for the HTTP request. In addition, dwHttpStatus holds the HTTP status of the request, and dwWin32Status holds any Win32 error code.
You cannot modify the contents of the strings passed by the HTTP_FILTER_LOG structure, although you can replace the values of the pointers. If you change the pointer values, you should assign them to new buffers that are allocated with the AllocMem() function provided by the HTTP_FILTER_CONTEXT structure so that the memory can be released properly when the session is closed.
SF_NOTIFY_END_OF_NET_SESSION
This notification is sent to your filter when the server is disconnecting a session. No specific data are associated with this notification, although it is a good place to clean up any information you have stored about a request in progress.
SF_NOTIFY_ACCESS_DENIED
When a request has been denied by the server, your filter will receive this notification. The lpvNotification parameter of HttpFilterProc() will be passed a pointer to an HTTP_FILTER_ACCESS_DENIED structure:
typedef struct _HTTP_FILTER_ACCESS_DENIED
{
const CHAR * pszURL;
const CHAR * pszPhysicalPath;
DWORD dwReason;
} HTTP_FILTER_ACCESS_DENIED, *PHTTP_FILTER_ACCESS_DENIED;
This structure includes the requested URL at pszURL and the physical path it was mapped to at pszPhysicalPath. The dwReason field contains a bitmap that specifies why the request was denied. This can include the following values:
- SF_DENIED_LOGON—Logon failed.
- SF_DENIED_RESOURCE—The Access Control List (ACL) for the resource did not allow the operation.
- SF_DENIED_FILTER—An ISAPI filter denied the request.
- SF_DENIED_APPLICATION—An ISAPI application or a CGI script denied the request.
- SF_DENIED_BY_CONFIG—This flag may be included with the others if the server's configuration did not allow the request.
ISAPI Support in MFC | Next Section

Account Sign In
View your cart