This document aims to answer all your questions about getting started with ISAPI. You may also wish to refer to the ISAPI Programming Documentation.
-
What exactly does ISAPI provide?
-
ISAPI provides a vendor-independent way of extending the functionality of your web server. It offers far more flexibility than the CGI interface and avoids all the performance limitations. ISAPI code is written in C and compiled as a shared object, and can either be single-threaded or multi-threaded.
-
I thought ISAPI was a Windows-based API. Can I use it under UNIX?
-
Sure you can. Although the types may look unfamiliar to most seasoned UNIX developers (since the standard was originally designed by Process Software and Microsoft), it is actually a very elegant and efficient way of extending the functionality of your web server.
-
How does ISAPI code link into the web server?
-
ISAPI code is compiled as a shared object, also known as a dynamic library. This code automatically becomes part of the web server when a web site is started, and can then interact with data structures and talk with a connected client. Unlike a CGI which is a separate process, ISAPI code has access to far more of the internal data and can therefore provide much more functionality than CGI.
-
What about performance? Is ISAPI fast?
-
Yes! Since the web server already has ISAPI code loaded, it is a very fast mechanism for dynamically providing or modifying content.
-
I want to write an ISAPI module - where do I start?
-
Firstly, you'll need to decide whether to use an ISAPI filter or an extension. Filters are called on every HTTP request and are chained together in a specific order. Extensions generate content and are mapped to a specific URL path requested.
-
Should I write an ISAPI filter or an extension?
-
You should work out if your primary purpose is to dynamically generate content, or to redirect/authenticate/rewrite client headers prior to a request. You also need to know if you wish to be called for specific URLs, or for every request. Both are equally easy to write, although your code must be "thread-safe". (The exception to this rule is "in-process" filters and extensions, which don't have to be thread-safe, see below for more details).
-
I want to write an extension to write data to the client. Now what?
-
You need to register your extension with the web server. Firstly you'll need to define the GetExtensionVersion() function, something like this:
BOOL WINAPI
GetExtensionVersion
( HSE_VERSION_INFO *pVer )
{
pVer->dwExtensionVersion = HSE_VERSION_MAJOR;
strncpy(pVer->lpszExtensionDesc,
"A simple page counter",
HSE_MAX_EXT_DLL_NAME_LEN);
return TRUE;
}
This registers and defines what your extension's purpose is. Once you'vecompiled your code, you should register with the web server which URL it is called for. The web server will call the HttpExtensionProc() function, which you should complete (shown below) when a request for this URL arrives, and you can call ReadClient() or WriteClient() using the Extension Control Block structure (ECB) provided.
DWORD WINAPI
HttpExtensionProc
( LPEXTENSION_CONTROL_BLOCK ecb )
{
/* your code */
return HSE_STATUS_SUCCESS;
}
-
What about if I want to write an ISAPI filter?
-
You will need to register your filter in a similar way to an extension shown above. This can be done by defining a GetFilterVersion() function:
BOOL WINAPI
GetFilterVersion
( HTTP_FILTER_VERSION *pVer )
{
pVer->dwFilterVersion = HTTP_FILTER_REVISION;
strncpy( pVer->lpszFilterDesc,
"A Cookie Filter",
SF_MAX_FILTER_DESC_LEN );
/* Notify me when headers have been processed */
pVer->dwFlags = SF_NOTIFY_PREPROC_HEADERS;
| SF_NOTIFY_SECURE_PORT
| SF_NOTIFY_NONSEURE_PORT;
return TRUE;
}
Once you've compiled your code, you will also need to register your filter with the web server. The HttpFilterProc() function will be called for every HTTP request for this virtual server, regardless of URL. ISAPI filters are called in a specific order, and more than one may be called per request. The return value from this function will determine if other filters are also called. Here is an example function for you= to complete:
DWORD WINAPI
HttpFilterProc
( PHTTP_FILTER_CONTEXT pfc,
DWORD notificationType,
PHTTP_FILTER_PREPROC_HEADERS headers )
{
/* your code here */
/* return, tell server to notify the next module */
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
-
Cool, what else can I do with ISAPI?
-
Many things! If you're writing a filter, you'll want to specify when you're called. You can hook into the following events which happen in the lifetime of a client connection:
| Event |
When called |
| SF_NOTIFY_READ_RAW_DATA |
when the web server has read data from the client |
| SF_NOTIFY_PREPROC_HEADERS |
when data has been read and headers have been parsed |
| SF_NOTIFY_URL_MAP |
when the web server is mapping a URL to a file |
| SF_NOTIFY_AUTHENTICATION |
when the web server is authenticating a user |
| SF_NOTIFY_ACCESS_DENIED |
if the user is denied access |
| SF_NOTIFY_SEND_RAW_DATA |
when data is sent to the client |
| SF_NOTIFY_END_OF_REQUEST |
when the request has ended |
| SF_NOTIFY_LOG |
when a log entry is made |
| SF_NOTIFY_END_OF_NET_SESSION |
when a connection has closed |
-
Ok, so how do I compile my ISAPI-based code?
-
You will need a working C compiler and a copy of the ISAPI header files. These are provided with the Zeus Web Server and can be found in the $ZEUSHOME/web/include/ directory. The flags you need to specify will also depend on whether you are running the filter or extension as in-process or out-of-process. (See the next question).
You can find further information about compilation in our FAQ.
-
Why does my ISAPI module stop the webserver serving requests?
-
Uh-oh. The core Zeus Web Server code is single-threaded (for speed) so your ISAPI module code must not block for too long. You should run your code in the out-of-process ISAPI runner.
-
What's the difference between "out-of-process" and
"in-process"?
-
Out-of-process means that a separate ISAPI runner process handles your code. In-process means that your code gets loaded into the main web server process, which is potentially more dangerous! If your code has a bug in it, you will crash the entire server. Out-of-process is safer since you can only crash the ISAPI runner with poor code. Zeus is fault-tolerant however, and will restart itself if it notices one of its processes has crashed.
Here's a table summarizing the features of extensions and filters, to help you decide:
| |
Extension |
Filter |
| Purpose |
generate content |
rewrite headers and URLs perform authentication redirect, forbid, etc.
|
| Calling model |
by requested URL |
on every request |
| Where run |
out-of-process or in-process |
out-of-process or in-process |
-
How do I make my ISAPI module run faster?
-
There is a small degradation in speed if you run your ISAPI module out-of-process. Moving it into the core web server means that the operating system does not have to schedule two processes, and data does not have to be piped between them.
-
My code has random corruption problems
-
Your code needs to be "thread-safe". This means that your code must not depend on the value of variables, if it is possible that a subequent request may overwrite whilst the first request is still executing. You will need to re-structure your code or lock access to critical shared variables via mutexes. (The only exception to this rule is "in-process" modules, which are run sequentially).