As we learn in Chapter 13, "Understanding Filters," an Internet Server Application Programming Interface (ISAPI) filter is made up of 32-bit, dynamic-linked libraries (DLLs) and has the following characteristics:
![]()
Once a filter registers for a request, it gets that data regardless of whether the request is for a file, a common gateway interface (CGI) application, or an ISAPI application.
In this chapter, we discuss how Windows NT Server and Microsoft's Internet Information Server (IIS) deal with several of the more common network functions. Included are examples of ISAPI code you can write to tailor some of these functions to your network's needs.
Whether a port is secure or nonsecure, and the types of data to pass through a port.
Whether the user logging on to the network is an authorized to use the network.
Translating a Web resource's URL into a physical address.
Building a Web page "from scratch."
Changing the appearance of (customizing) a received page or the received results of an ISAPI process.
Recording the administrative details of a number of server functions. For example, filters can log the number of times a Web server is accessed or the number of logon attempts to that server from unauthorized users.
For example, the number of times requests for http://www.abcdef.com/shop/books.html were received.
When secure socket layer (SSL) is running, all data sent to or from a client is encrypted.
IIS 3.0 uses the secure sockets layer (SSL) security protocol. IIS enables and disables SSL through its Key Manager Tool, which can be executed from the Windows NT Start button or from the Internet Service Manager tool.
But there's more to the port security picture than that. Your network must be certified to use SSL. One certifying body is VeriSign.
Instructions are available online from VeriSign about getting an SSL certificate. Look for the "SSL Certificates for IIS" selection at the following Web address: http://www.verisign.com/microsoft/index.html
![]()
Problems have been reported with installing VeriSign keys. Loading these keys has sometimes caused the error message:
Key check failed with error 0x........
Retrace your attempts to install the key. If you can find no reason for the failure, check one last thing. Be sure that when you installed the key, you used the file sent to you by VeriSign and not the file you sent to them.
Using the wrong file to install the key is a frequent cause of the error message.
![]()
SSL has also been reported to have trouble dealing with input from a form. One fix is to use the command GET instead of the more frequently used POST to send input from the form to the destination.
Apparently, when SSL is activated, IIS does not know where to send form input. So that input may as well have gone into a black hole-as far as your server's ability to process it is concerned, it disappears.
The IIS FAQ recently reported that both a Perl script and an ISAPI filter that used this fix worked successfully. The latest version of the IIS FAQ is available at
http://rampages.onramp.net/~steveg/iis.html
SSL allows both data encryption and server authentication. When SSL is running, all data sent to or from a client is encrypted.
For instance, when HTTP basic authentication is configured and SSL is used, the user name and password are encrypted. Only then are they transmitted to the server for authentication.
To further ensure the security of selected services, you can write an ISAPI filter that causes an application to accept data only if that data originates from a secure port. Such a filter would involve the use of the SF_NOTIFY_SECURE_PORT notification or the testing of the HTTP_FILTER_CONTEXT fIsSecurePort member variable.
IIS can confirm the validity of attempts by users to access Web, gopher, and FTP services. Such verification is fully integrated with user and file access permissions for Windows NT server.
IIS by default allows anonymous connections. An anonymous connection is a users attempt to access a Web, gopher, or FTP service without supplying a user name and password. This is the typical access scenario for the Internet environment.
![]()
The NT server file system (NTFS) uses Access Control Lists (ACLs) to grant or deny certain NT user accounts and groups of users access to files and directories.
An ACL is a list of files and directories. Associated with every file or directory on the list are the names of the users and groups allowed to use that item.
When an attempt to access the file or directory named in an ACL is denied, it is because the user or the group to which the user is assigned is not associated with that file or directory in the ACL.
How does an ISAPI filter handle anonymous connection attempts? IIS maintains a "fictitious user" account for each of its Internet services. This anonymous user account is intended solely to process anonymous requests.
Such an account is created and incorporated into NT server's domain user management scheme when IIS is installed. The user name for this account has the form
IUSR_<server_name>
For example, if the server on which IIS is being loaded is called Platform1, the anonymous user account has the name
IUSR_Platform1
IUSR_Platform1 is automatically made a member of the Guest domain user group at the time of the creation of this account. IIS can handle a number of domains, or run from more than one server in a network, and still manage automatically created anonymous user accounts.
Each such account has the name of the computer on which it runs. Accounts created by domain-member servers are placed on the local computer. In this way, every account name created when IIS is installed is unique.
When an anonymous request is received, the service to which the request has been directed impersonates a user. It "pretends" that the request comes from the fictitious user identified in the anonymous user account. The request succeeds if the anonymous user has permission in NT server's ACL to access the resource.
Your ISAPI filter might be written to change the way IIS handles failed anonymous access attempts. In the next section, we discuss this possibility.
Suppose an anonymous user account does not have permission in the ACL to access a resource. Also suppose that IIS is configured to use basic HTTP authentication or Windows NT challenge and response authentication. In this case, most Web browsers respond by
![]()
As of mid-November 1996, Web browsers that accept Windows NT native user authentication (NTLM) include Microsoft Internet Explorer (IE) versions 2.0 and later, and Netscape Navigator versions 2.0 and later.
Coding the Change to User Authentication
To change how IIS does user authentication by preventing the pop-up of the dialog described above, you could use the SF_NOTIFY_ACCESS_DENIED notification (available only in IIS 2.0 and later).
This notification allows an ISAPI filter to be notified whenever the server is about to return a status code of 401 Access Denied. This notification allows the filter to analyze the failure and return a custom message.
To request the access-denied event, the filter sets the SF_NOTIFY_ACCESS_DENIED flag when the GetFilterVersion method is called by IIS at the filter's initialization. Listing 12.1 is an example of how to request notification of the SF_NOTIFY_ACCESS_DENIED event.
Listing 12.1 LST12_1.CPP - GetFilterVersion-Access Denied
/*
GetFilterVersion - An ISAPI/Win32 API method
This method is required by IIS. It is called
following the process load to ensure that the
filter is compatible with the server.
*/
BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
pVer->dwFilterVersion = MAKELONG( 0, 1 ); // Version 1.0
pVer->dwFlags = (SF_NOTIFY_ACCESS_DENIED |
SF_NOTIFY_ORDER_DEFAULT);
/*
A brief one line description of the filter
*/
strcpy( pVer->lpszFilterDesc, TEXT("Sample Filter, v1.1"));
return TRUE;
}
When the filter requests notification for an event, it gets a notification each time the event occurs. HttpFilterProc is called by the Web service just before it issues a status code of 401 Access Denied. This triggers the SF_NOTIFY_ACCESS_DENIED event. Listing 12.2 is an HttpFilterProc function to handle the SF_NOTIFY_ACCESS_DENIED event.
Listing 12.2 LST12_2CPP - HttpFilterProc-Access Denied
/*
HttpFilterProc - ISAPI / Win32 API method
This method is a required by IIS. It is called
for each notification event requested. This is
where the filter accomplishes its purpose in life.
*/
DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *pfc,
DWORD NotificationType,
VOID * pvData)
{
DWORD dwRet;
/*
Direct the notification to the appropriate
routine for processing.
*/
switch ( NotificationType )
{
case SF_NOTIFY_ACCESS_DENIED:
dwRet = OnAccessDenied(pfc, (PHTTP_FILTER_ACCESS_DENIED)
pvData );
break;
default:
dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
break;
}
return dwRet;
}
When the SF_NOTIFY_ACCESS_DENIED event is received, the filter calls OnAccessDenied to provide the custom processing necessary. Listing 12.3 is an example of how to return a custom message to the client browser instead of the standard error code message.
Listing 12.3 LST12_3CPP - OnAccessDenied-Access Denied
/*
OnAccessDenied -
The data returned at pvData->pvInData includes the
header, types of data accepted and the browsers type
*/
DWORD OnAccessDenied(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_ACCESS_DENIED *pvData)
{
char* pszBuffer;
DWORD dwBufferSize=400;
pszBuffer=(CHAR*)pfc->AllocMem(pfc, dwBufferSize, (DWORD)NULL);
strcpy(pszBuffer, TEXT("<HTML><BODY><H1>Sorry this section is
closed!</H1></BODY></HTML>"));
dwBufferSize = (DWORD) strlen(pszBuffer);
pfc->WriteClient(pfc, (LPVOID)pszBuffer, &dwBufferSize,
(DWORD)NULL);
return SF_STATUS_REQ_FINISHED;
}
/*
When you install IIS, performance monitoring objects are added to the list of available objects in the Performance Monitor of Windows NT server.
![]()
Performance Monitor is a graphical tool that charts the features of a number of network or station functions. Among these features are
- Processor usage
- Memory usage
- Cache usage
- Progress and status of threads
- Status of processes
Every object Performance Monitor reports on has associated with it a set of counters that track factors like device usage, queue delays, and other vital statistics.
Performance Monitor can also be set up to provide administrator alerts and system log entries in certain conditions .
Some of the performance objects made available to the Performance Monitor offer statistics, as shown in Table 12.1.
Table 12.1 IIS Performance Monitoring Objects
Counter | Definition |
BGI Requests | The number of requests to installed, custom DLLs that use the binary gateway interface (BGI) and forms processing, or other dynamic data sources |
Bytes Received/Sec | Bytes received per second by the server |
Bytes Sent/Sec | Bytes sent per second by the server |
Bytes Total/Sec | Total bytes received and sent by the server |
CGI Requests | Number of requests to installed, custom EXE files compiled from CGI source code; these executables add forms processing or other dynamic data sources |
Connection Attempts | Total number of connection attempts made to the HTTP server Connections/Sec; number of HTTP requests the server is handling per second |
Current Anonymous Users | Number of connected anonymous users |
Current BGI Requests | Number of current BGI requests being processed simultaneously by the server |
Current CGI Requests | Number of current CGI requests being processed simultaneously by the server; includes WAIS index queries |
Current Connections | Total number of active connections to the server |
Current NonAnonymous Users | Total number of nonanonymous users connected to the server |
Files Received | Total number of files received by the server |
Files Sent | Total number of files sent by the server |
Files Total | Files Sent plus Files Received Get Requests Number of current HTTP requests using the GET method (usually the retrieval of text or image files, but can be applied to form fill-in) |
Read Requests | Number of current HTTP requests using the READ method (which usually indicates that a client is querying the condition of the current document to determine, for example, if it needs to be refreshed) |
Logon Attempts | Total logon attempts made to the server |
Maximum Anonymous Users | Maximum number of simultaneous anonymous user connections to the server |
Maximum BGI Requests | Maximum number of simultaneous BGI requests being processed by the server |
Maximum CGI Requests | Maximum number of simultaneous BGI requests being processed by the server; includes WAIS index queries |
Maximum Connections | Maximum number of simultaneous user connections to the HTTP server |
Maximum NonAnonymous Users | Maximum number of simultaneous nonanonymous user connections to the server |
Not Found Errors | Total number of requests that could not be satisfied by the server because the document in question could not be found |
Other Request Methods | Total number of requests to the server that do not use GET, POST, or READ; can include but are not limited to PUT, DELETE, and LINK |
Post Requests | Total number of requests to the server that use the POST method (most commonly associated with forms fill-in or with gateway requests) |
Total Anonymous Users | Total number of anonymous users who have ever connected to the server |
Total NonAnonymous Users | Total number of users who have ever connected to the server |
You can construct a filter to change the IIS log file or produce a separate custom log file. The best place to produce a changed or custom log file is when the SF_NOTIFY_LOG event is triggered. When this event is triggered, a pointer to the HTTP_FILTER_LOG structure is available and provides the following information:
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 - The client's host name.
pszClientUserName - The client's user name.
pszServerName - The server the client is connected to.
pszOperation - The HTTP command.
pszTarget - The target of the HTTP command.
pszParameters - Parameters passed to the HTTP command.
dwHttpStatus - The HTTP return status.
dwWin32Status - The Win32 error code.
To supply custom logging, your filter should request notification of the SF_NOTIFY_LOG event. To register for the logging event, the filter sets the SF_NOTIFY_LOG flag when the GetFilterVersion method is called by IIS at the filter's initialization, as shown in Listing 12.4.
Listing 12.4 LST12_4.CPP -GetFilterVersion-Change Log
/*
GetFilterVersion - An ISAPI/Win32 API method
This method is required by IIS. It is called
following the process load to ensure that the
filter is compatible with the server.
*/
BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
pVer->dwFilterVersion = MAKELONG( 0, 1 ); // Version 1.0
pVer->dwFlags = (SF_NOTIFY_LOG |
SF_NOTIFY_ORDER_DEFAULT);
/*
A brief one line description of the filter
*/
strcpy( pVer->lpszFilterDesc, TEXT("Sample Filter, v1.1"));
return TRUE;
}
When the filter requests notification of an event, it gets notification each time the event occurs. When HttpFilterProc is called by IIS, SF_NOTIFY_LOG is triggered, the function associated with the event is executed, and logging information can be changed or analyzed.
Listing 12.5 and Listing 12.6 show you how to change the logging information.
Listing 12.5 LST12_5.CPP - HttpFilterProc-Change Log
/*
HttpFilterProc - ISAPI / Win32 API method
This method is a required by IIS. It is called
for each notification event requested. This is
where the filter accomplishes its purpose in life.
*/
DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *pfc,
DWORD NotificationType,
VOID * pvData)
{
DWORD dwRet;
/*
Direct the notification to the appropriate
routine for processing.
*/
switch ( NotificationType )
{
case SF_NOTIFY_LOG:
dwRet = OnLog(pfc, (PHTTP_FILTER_LOG) pvData );
break;
default:
dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
break;
}
return dwRet;
}
Listing 12.6 LST12_6.CPP - OnLog-Change Log
/*
OnLog -
This routine is called following the data
being sent to the browser. The data within
pvData includes the client host name,
client user name, server name, operation
requested (e.g. GET, POST, etc.), target
item (e.g. /default.htm), parameters passed
with the target and the status returned to
the browser.
*/
DWORD OnLog(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_LOG *pvData)
{
char* pszBuffer;
// Check for a user name and if it is blank
// then replace it with "Unknown User"
if (strstr((char*) pvData->pszClientUserName,TEXT("")))
{
pszBuffer=(CHAR*)pfc->AllocMem(pfc, 100, (DWORD)NULL);
memset(pszBuffer,'\0',100);
strcpy(pszBuffer, TEXT("Unknown User"));
pvData->pszClientUserName = pszBuffer;
}
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
/*
Web servers have to map a Web resource's URL to a physical address. In this section, we learn how to customize URL mapping.
An ISAPI filter enables you to customize URL mapping. The filter does this using the HTTP_FILTER_URL_MAP structure, which is defined as follows:
typedef struct _HTTP_FILTER_URL_MAP
{
const CHAR * pszURL;
CHAR * pszPhysicalPath;
DWORD cbPathBuff;
} HTTP_FILTER_URL_MAP, *PHTTP_FILTER_URL_MAP;
Members
pszURL - A pointer to the URL that is being mapped to a physical path.
pszPhysicalPath - A pointer to the buffer where the physical path is stored.
cbPathBuffThe - size of the buffer pointed to by pszPhysicalPath.
This structure is pointed to by pvData in the HttpFilterProc when NotificationType is SF_NOTIFY_URL_MAP. This is done when the server is about to map the specified URL to a physical path. Filters can change the physical path, or analyze the path or resource requested.
To register for physical directory mapping, the filter sets the SF_NOTIFY_URL_MAP flag when the GetFilterVersion method is called by IIS the filter's initialization. This is shown in Listing 12.7.
Listing 12.7 LST12_7.CPP - GetFilterVersion-URL Map
/*
GetFilterVersion - An ISAPI/Win32 API method
This method is required by IIS. It is called
following the process load to ensure that the
filter is compatible with the server.
*/
BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
pVer->dwFilterVersion = MAKELONG( 0, 1 ); // Version 1.0
pVer->dwFlags = (SF_NOTIFY_URL_MAP |
SF_NOTIFY_ORDER_DEFAULT);
/*
A brief one line description of the filter
*/
strcpy( pVer->lpszFilterDesc, TEXT("Sample Filter, v1.1"));
return TRUE;
}
When the filter requests notification for an event, it gets notification each time the event occurs. When HttpFilterProc is called by IIS, SF_NOTIFY_URL_MAP is triggered, the function associated with the event is executed, and logging information can be changed or analyzed.
Listing 12.8 and Listing 12.9 show you how to analyze a URL mapping.
Listing 12.8 LST12_8.CPP - HttpFilterProc-URL Map
/*
HttpFilterProc - ISAPI / Win32 API method
This method is a required by IIS. It is called
for each notification event requested. This is
where the filter accomplishes its purpose in life.
*/
DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *pfc,
DWORD NotificationType,
VOID * pvData)
{
DWORD dwRet;
/*
Direct the notification to the appropriate
routine for processing.
*/
switch ( NotificationType )
{
case SF_NOTIFY_URL_MAP:
dwRet = OnUrlMap(pfc, (PHTTP_FILTER_URL_MAP) pvData );
break;
default:
dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
break;
}
return dwRet;
}
Listing 12.9 LST12_9.CPP - OnUrlMap - URL Map
/*
OnUrlMap -
The data returned within pvData includes the URL
requested (pvData->pszURL) and the full path to
the physical data (pvData->pszPhysicalPath.
*/
DWORD OnUrlMap(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_URL_MAP *pvData)
{
// This exampl looks at the URL path and analyzes it
// to see if it is a specific page. If so performs some
// special processing.
if (!strcmp(pvData->pszURL, TEXT("/ads.htm"))) {
// Do your special processing if the resource
// requested is named "ads.htm"
}
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
You can write an ISAPI filter to develop custom HTML tags or text placeholders. The custom HTML tags or text placeholders can be developed to do date and time stamping of pages, make consistent headers or footers, or to add counters to a page as it is written back to the browser.
Changing the raw data sent back to the browser uses the HTTP_FILTER_RAW_DATA structure, which is defined as follows:
typedef struct _HTTP_FILTER_RAW_DATA
{
PVOID pvInData;
DWORD cbInData;
DWORD cbInBuffer;
DWORD dwReserved;
} HTTP_FILTER_URL_MAP, *PHTTP_FILTER_URL_MAP;
Members
pvInData - A pointer to the buffer containing the data to be
written back to the browser.
cbInData - The length of the data in the buffer at pvInData.
cbInBuffer - The total size of the buffer as defined by pvInData.
This structure is pointed to by the void pointer, pvData, in the HttpFilterProc when NotificationType is SF_NOTIFY_SEND_RAW_DATA. SF_NOTIFY_SEND_RAW_DATA is signaled when the server is about send the raw data back to the browser.
For each HTML page sent to a browser, SF_NOTIFY_SEND_RAW_DATA is triggered twice: when the page headers are returned to the browser and when the page is returned to the browser.
![]()
When changing the size of a page, especially enlarging the page size, you must also change the Content-Length: header. This is among the headers sent to the browser before the page.
See the AdFlipperFilter sample code on the companion CD to this book to learn how to change the content length in the header and the page.
To register for the raw data sent to the browser, the filter sets the SF_NOTIFY_SEND_RAW_DATA flag when the GetFilterVersion method is called by IIS at the filter's initialization. This is shown in Listing 12.10.
Listing 12.10 LST12_10.CPP - GetFilterVersion-Send Raw Data
/*
GetFilterVersion - An ISAPI/Win32 API method
This method is required by IIS. It is called
following the process load to ensure that the
filter is compatible with the server.
*/
BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
pVer->dwFilterVersion = MAKELONG( 0, 1 ); // Version 1.0
pVer->dwFlags = (SF_NOTIFY_SEND_RAW_DATA |
SF_NOTIFY_ORDER_DEFAULT);
/*
A brief one line description of the filter
*/
strcpy( pVer->lpszFilterDesc, TEXT("Sample Filter, v1.1"));
return TRUE;
}
When the filter requests notification for an event, it gets notification each time the event occurs. When HttpFilterProc is called by IIS, SF_NOTIFY_SEND_RAW_DATA is triggered twice for an HTML page.
Listing 12.11 and Listing 12.12 show you how to handle multiple calls for SF_NOTIFY_SEND_RAW_DATA.
Listing 12.11 LST12_11.CPP - HttpFilterProc-Send Raw Data
/*
HttpFilterProc - ISAPI / Win32 API method
This method is a required by IIS. It is called
for each notification event requested. This is
where the filter accomplishes its purpose in life.
*/
DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *pfc,
DWORD NotificationType,
VOID * pvData)
{
DWORD dwRet;
/*
Direct the notification to the appropriate
routine for processing.
*/
switch ( NotificationType )
{
case SF_NOTIFY_SEND_RAW_DATA:
dwRet = OnSendRawData(pfc, (PHTTP_FILTER_RAW_DATA) pvData );
break;
default:
dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
break;
}
return dwRet;
}
Listing 12.12 LST12_12.CPP - OnSendRawData-Send Raw Data
/*
OnSendRawData -
This routine is called twice for this event.
The first time it is called is when it sends
the browser a notification of the actual data
it is about to transmit(e.g. text/html, image/gif,
etc.) The second time this routine is called
is when the actual data (e.g. text, gif, etc.)
is being transmitted to the browser.
*/
DWORD OnSendRawData(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_RAW_DATA *pvData)
{
CHAR* pszBuffer;
CHAR szTime[10];
DWORD i=0;
DWORD j=0;
HTTP_FILTER_RAW_DATA* pRawData = (PHTTP_FILTER_RAW_DATA)
pvData;
pszBuffer=pvData->pvInData;
if (strstr((char*) pRawData->pvInData,"HTTP/1.0"))
{
// This is were HTML header manipulation takes place
}
else
{
// This is were the custom HTML tag <%TIME%> will be
// replaced with the servers current system time.
for (i = pvData->cbInData-10; i > 0; i--)
{
if (!_memicmp(pszBuffer+i, TEXT("<%TIME%>"), 8))
{
strtime(szTime);
memcpy(pszBuffer+i, szTime, 8);
break;
}
}
}
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
From the examples in this chapter, you can see that filters can change and customize how IIS works. Filters offer a lower level of access to HTTP data during its communication cycle.
When a filter requests notification for an event, it is called each time the event occurs. For this reason, you have to be careful not to request notification of events for which the filter won't do processing.
In the next few sections, we outline some of the advantages of using filters.
In this section, we discuss the setting of event notification flags for secure and nonsecure ports, and processing priority flags. These flags allow the filter to take control at certain predefined events and with a predefined priority relative to the other filters executed by the server.
Following the discussion of the flags, we discuss the order in which events occur in the HTTP communication cycle.
Filter Notification Flags
Filter notification flags are set in the GetFilterVersion function. Filter notification flags are defined in the SDK header file HTTPFILT.H. They can all be identified as beginning with SF_NOTIFY_*.
The three groups of notification flags are priorities, port, and events. An explanation of the three groups of flags is next.
Filter Priority Processing
The filter processing priority is set in the GetFilterVersion function by turning on an SF_NOTIFY_ORDER_* flag in the HTTP_FILTER_VERSION structure's member variable dwFlags. A filter can have one of four flags set to indicate to the Web service what priority level the filter should get when processing HTTP events.
The four flags signify a processing priority of low, medium, high, or default. A priority of default is the same as a priority of low. The syntax for the priority flags is shown in Table 12.2.
Table 12.2 Filter Priority Flags
Value | Description |
SF_NOTIFY_ORDER_DEFAULT | Load the filter at the default priority (recommended). |
SF_NOTIFY_ORDER_LOW | Load the filter at the low priority. |
SF_NOTIFY_ORDER_MEDIUM | Load the filter at a medium priority. |
SF_NOTIFY_ORDER_HIGH | Load the filter at a high priority. |
If multiple filters are processing the same HTTP events, the filter with the highest priority gets the notification of the event first. Each priority gets the event notification in descending order (from highest to lowest).
If multiple filters are processing the same HTTP events and have the same priority, the order of the filters in the registry takes precedence. The key where filters are registered is HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3SCVC\Parameters with an entry name of Filter Dlls.
![]()
Unless you have a reason for setting a priority other the default setting, it is recommended that you set SF_NOTIFY_ORDER_DEFAULT as a priority. If no priority is set in the HTTP_FILTER_VERSION member dwFlags, the Web service assumes a priority of low.
Filters can process HTTP events for a port that is secure, non-secure, or both. A secure port request is a request to the Web service that is specified with the HTTPS protocol.
The port processing flags are set in the GetFilterVersion function by turning on either SF_NOTIFY_SECURE_PORT, SF_NOTIFY_NONSECURE_PORT, or both flags in the HTTP_FILTER_VERSION structure's member variable dwFlags. The syntax for the port flags is shown in Table 12.3
Table 12.3 Port Processing Flags
Value | Description |
SF_NOTIFY_SECURE_PORT | Notifies the filter that it is passing data through a secure port. |
SF_NOTIFY_NONSECURE_PORT | Notifies the filter that it is passing data through a nonsecure port. |
![]()
If neither SF_NOTIFY_SECURE_PORT or SF_NOTIFY_NONSECURE_PORT are specified, the server defaults to both, which allows filter processing to occur through any port.
The HTTP communication event notification flags are set in the GetFilterVersion by turning on appropriate SF_NOTIFY_* flag in the HTTP_FILTER_VERSION structure's member variable dwFlags. A filter can have any number of the available event notification flags set in the filter.
The syntax for the HTTP event notification flags is shown in Table 12.4
Table 12.4 HTTP Event Notification Flags
Value | Description |
SF_NOTIFY_READ_RAW_DATA | Allow the filter to see the raw data. The data returned will contain both headers and data. |
SF_NOTIFY_PREPROC_HEADERS | The server has preprocessed the headers. |
SF_NOTIFY_AUTHENTICATION | The server is authenticating the client. |
SF_NOTIFY_ACCESS_DENIED | Allows the filter to be notified whenever the server is about to return a 401 Access Denied. This allows the filter to analyze the failure and return a custom message. |
SF_NOTIFY_URL_MAP | The server is mapping a logical URL to a physical path. |
SF_NOTIFY_SEND_RAW_DATA | The server is sending raw data back to the client. |
SF_NOTIFY_LOG | The server is writing information to the server log. |
SF_NOTIFY_END_OF_NET_SESSION | The session with the client is ending. |
![]()
Only specify the HTTP event notifications flags in which the filter has requested notification of data available or if the filter is going to process at a certain point in the HTTP communication. Unnecessary use of HTTP event notification flags reduces the performance of the Web service.
The HTTP event-processing order is significant for determining which events to request for a filter solution. The order shown in Table 12.4 is the order of events for an HTTP communication cycle.
To monitor the HTTP communication cycle, set up the GenesisFilter project in debug mode and set breakpoints throughout the code to see the what, why, and how of the request flow. The GenesisFilter project, on the companion CD, is the tutorial for this chapter.
Review Chapter 17, "Troubleshooting and Debugging Extensions and Filters," to learn how to set up Microsoft Visual C++ in debug mode for the GenesisFilter.
Be careful when you develop filters because they can be resource- intensive. When a filter requests notification for an event, it gets notification every time the event occurs.
Because filters are executed in a HTTP communication cycle, make sure that the filter's function code is as efficient as possible. Some techniques to avoid or to use in moderation include database accesses, unnecessary file reads and writes, and dynamic page creation.
If a process does not need access to the HTTP transaction information (such as authentication, logging, HTTP headers, and URL mapping), an ISAPI extension is preferable to an ISAPI filter.
The GenesisFilter project is on the companion CD. GenesisFilter does nothing but request all the available HTTP communication events. It passes control to a worker function that returns control back to the Web server.
Why include such an unproductive example? The tutorial is a way to monitor the HTTP communication event process and analyze what data is available at each event in the cycle.
You should only use the GenesisFilter when in an interactive debugging mode. Review Chapter 17, "Troubleshooting and Debugging Extensions and Filters," for the steps to use Visual C++ for debugging ISAPI extensions.
While in debug mode, set breakpoints among the GetFilterVersion, HttpFilterProc, and worker functions to see what data is available at each event processed.
You can also use the GenesisFilter as the starting point for any new filter development since the backbone of the filter process is already coded. All you need to do is add the worker functions to do your processing and remove the events notification flags from GetFilterVersion to turn off specific notification processing.
Listing 12.13 is the source code for the GenesisFilter. This source code is on the companion CD.
Listing 12.13 GenesisFilter.C - GenesisFilter Source
/*
Copyright (c) 1996 ClearMind, Inc. and David A. Torres
Module Name:
GenesisFilter.c
Abstract:
This filter does NOTHING, but its a great place
observe how the IIS world works. This filter
requests notification of all IIS filter events
and is the starting point for all of my filter
development.
*/
#include <windows.h>
#include <httpfilt.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
/*
DebugMsg() is used for debugging.
Choose debugger output or log file.
*/
//#define TO_FILE // uncomment out to use a log file
#ifdef TO_FILE
#define DEST ghFile
#define DebugMsg(x) WriteToFile x;
HANDLE ghFile;
#define LOGFILE "c:\\GenesisFilter.log"
void WriteToFile (HANDLE hFile, char *szFormat, ...) {
char szBuf[1024];
DWORD dwWritten;
va_list list;
va_start (list, szFormat);
vsprintf (szBuf, szFormat, list);
hFile = CreateFile (LOGFILE, GENERIC_WRITE,
0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
SetFilePointer (hFile, 0, NULL, FILE_END);
WriteFile (hFile, szBuf, lstrlen (szBuf),
&dwWritten, NULL);
CloseHandle (hFile);
}
va_end (list);
}
#else
#define DEST buff
#define DebugMsg(x) { \
char buff[256]; \
wsprintf x; \
OutputDebugString( buff ); \
}
#endif
/*
Private prototypes
These are the methods executed for each of
the related filter events.
*/
DWORD OnAuthentication(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_AUTHENT *pvData);
DWORD OnAccessDenied(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_ACCESS_DENIED *pvData);
DWORD OnLog(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_LOG *pvData);
DWORD OnUrlMap(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_URL_MAP *pvData);
DWORD OnPreprocHeaders(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_PREPROC_HEADERS *pvData);
DWORD OnEndOfNetSession(HTTP_FILTER_CONTEXT *pfc);
DWORD OnSendRawData(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_RAW_DATA *pvData);
DWORD OnReadRawData(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_RAW_DATA *pvData);
void WhatError(DWORD dwError);
/*
Globals
*/
CRITICAL_SECTION gCS; // A critical section handle
// is used to protect global
// state properties
BOOL gfHTML;
/*
This is the entry and exit point for the filter
it is called when the filter is loaded and unloaded
by IIS. This is where state properties need to be
retrieved and store on persistant storage.
*/
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
switch( ul_reason_for_call ) {
case DLL_PROCESS_ATTACH:
{
InitializeCriticalSection(&gCS);
gfHTML = FALSE;
break;
}
// case DLL_THREAD_ATTACH:
// case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
{
DeleteCriticalSection(&gCS);
break;
}
}
return TRUE;
}
/*
GetFilterVersion - An ISAPI/Win32 API method
This method is required by IIS. It is called
following the process load to ensure that the
filter is compatable with the server.
*/
BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION * pVer)
{
WORD wMajorVersion=0;
WORD wMinorVersion=0;
wMajorVersion = HIWORD( pVer->dwServerFilterVersion );
wMinorVersion = LOWORD( pVer->dwServerFilterVersion );
DebugMsg(( DEST,
"[GetFilterVersion] Server version is %d.%d\n",
HIWORD( pVer->dwServerFilterVersion ),
LOWORD( pVer->dwServerFilterVersion ) ));
/*
This filter is intended for IIS version 2.0 or greater.
This is an example of how to test the server version.
*/
if (wMajorVersion < 2) return FALSE;
pVer->dwFilterVersion = MAKELONG( 0, 2 ); // Version 1.0
DebugMsg(( DEST,
"[GetFilterVersion] Server version is %d.%d\n",
HIWORD( pVer->dwFilterVersion ),
LOWORD( pVer->dwFilterVersion ) ));
/*
Specify the security level of notifications
(secured port, nonsecured port, or both), the
types of events and order of notification for
this filter (high, medium or low, default=low).
*/
pVer->dwFlags = (
SF_NOTIFY_SECURE_PORT |
SF_NOTIFY_NONSECURE_PORT |
SF_NOTIFY_READ_RAW_DATA |
SF_NOTIFY_PREPROC_HEADERS |
SF_NOTIFY_URL_MAP |
SF_NOTIFY_AUTHENTICATION |
SF_NOTIFY_ACCESS_DENIED |
SF_NOTIFY_SEND_RAW_DATA |
SF_NOTIFY_LOG |
SF_NOTIFY_END_OF_NET_SESSION |
SF_NOTIFY_ORDER_DEFAULT
);
/*
A brief one line description of the filter
*/
strcpy( pVer->lpszFilterDesc, "Genesis Filter, v1.0" );
return TRUE;
}
/*
HttpFilterProc - ISAPI / Win32 API method
This method is a required by IIS. It is called
for each notification event requested. This is
where the filter accomplishes its purpose in life.
*/
DWORD WINAPI HttpFilterProc(HTTP_FILTER_CONTEXT *pfc,
DWORD NotificationType,
VOID * pvData)
{
DWORD dwRet;
/*
Direct the notification to the appropriate
routine for processing.
*/
switch ( NotificationType )
{
case SF_NOTIFY_READ_RAW_DATA:
dwRet = OnReadRawData(pfc, (PHTTP_FILTER_RAW_DATA) pvData );
break;
case SF_NOTIFY_PREPROC_HEADERS:
dwRet = OnPreprocHeaders(pfc, (PHTTP_FILTER_PREPROC_HEADERS)
pvData );
break;
case SF_NOTIFY_URL_MAP:
dwRet = OnUrlMap(pfc, (PHTTP_FILTER_URL_MAP) pvData );
break;
case SF_NOTIFY_AUTHENTICATION:
dwRet = OnAuthentication(pfc, (PHTTP_FILTER_AUTHENT)
pvData );
break;
case SF_NOTIFY_ACCESS_DENIED:
dwRet = OnAccessDenied(pfc, (PHTTP_FILTER_ACCESS_DENIED)
pvData );
break;
case SF_NOTIFY_SEND_RAW_DATA:
dwRet = OnSendRawData(pfc, (PHTTP_FILTER_RAW_DATA) pvData );
break;
case SF_NOTIFY_LOG:
dwRet = OnLog(pfc, (PHTTP_FILTER_LOG) pvData );
break;
case SF_NOTIFY_END_OF_NET_SESSION:
dwRet = OnEndOfNetSession(pfc);
break;
default:
DebugMsg(( DEST,
"[HttpFilterProc] Unknown notification type,
%d\r\n",
NotificationType ));
dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
break;
}
return dwRet;
}
/*
IIS Filter Event Routines
*/
/*
OnReadRawData -
The data returned at pvData->pvInData includes the
header, types of data accepted and the browsers type
*/
DWORD OnReadRawData(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_RAW_DATA *pvData)
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
/*
OnPreprocHeaders -
The data returned within pvData includes three
callback methods to get, set and/or add to the
header
*/
DWORD OnPreprocHeaders(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_PREPROC_HEADERS *pvData)
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
/*
OnUrlMap -
The data returned within pvData includes the URL
requested (pvData->pszURL) and the full path to
the physical data (pvData->pszPhysicalPath).
*/
DWORD OnUrlMap(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_URL_MAP *pvData)
{
return SF_STATUS_REQ_FINISHED;
}
/*
OnAuthentication -
The data returned within pvData includes
User identification (pvData->pszUser) and
the user's password (pvData->pszPassword).
*/
DWORD OnAuthentication(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_AUTHENT *pvData)
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
/*
OnAccessDenied -
The data returned at pvData->pvInData includes the
header, types of data accepted and the browsers type
*/
DWORD OnAccessDenied(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_ACCESS_DENIED *pvData)
{
return SF_STATUS_REQ_FINISHED;
}
/*
OnSendRawData -
This routine is called twice for this event.
The first time it is called is when it sends
the browser a notification of the actual data
it is about to transmit(e.g. text/html, image/gif,
etc.) The second time this routine is called
is when the actual data (e.g. text, gif, etc.)
is being transmitted to the browser.
*/
DWORD OnSendRawData(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_RAW_DATA *pvData)
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
/*
OnLog -
This routine is called following the data
being sent to the browser. The data within
pvData includes the client host name,
client user name, server name, operation
requested (e.g. GET, POST, etc.), target
item (e.g. /default.htm), parameters passed
with the target and the status returned to
the browser.
*/
DWORD OnLog(HTTP_FILTER_CONTEXT *pfc,
HTTP_FILTER_LOG *pvData)
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
/*
OnEndOfNetSession -
This routine is called following the
transmission of all the data requested
by the browser.
*/
DWORD OnEndOfNetSession(HTTP_FILTER_CONTEXT *pfc)
{
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
void WhatError(DWORD dwError)
{
switch ( dwError )
{
case ERROR_INVALID_PARAMETER:
break;
case ERROR_INVALID_INDEX:
break;
case ERROR_INSUFFICIENT_BUFFER:
break;
case ERROR_MORE_DATA:
break;
case ERROR_NO_DATA:
break;
}
}
This chapter outlines some scenarios in which ISAPI filters play an important role. It gives you models for building a filter in each of the categories discussed. For more information about ISAPI filters, see the following chapters.
© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.