Server Side Scripting with CGI and ISAPI
Server Side Scripting with CGI and ISAPI
The Internet Server Application Programmers
Interface (ISAPI) is a tool with which ActiveX programmers can write server-side scripts.
These server scripts are the "next generation" of the CGI (Common Gateway
These kinds of scripts are run through your HTTP
daemon (server). The scripts act as an interface between the users (and their software)
and the server (and its software).
Without server scripting, Web surfing is relegated
to simply serving up and viewing static documents. The users can't interact with these
documents, and features such as HTML forms and clickable image maps become impossible.
Before we get into the meat and potatoes of this,
let's take a look at the words CGI represents: Common Gateway Interface.
Common means that this scripting
specification is used with a variety of different HTTP server software packages. This
allows programmers to be relatively certain that a CGI script that is written for a
package like O'Reilly's WebSite will still work on Microsoft's Internet Information Server
as well as Netscape HTTP server or any of the shareware and freeware servers out
thereas long as they support CGI.
Gateway interface means that this scripting
specification is used to allow network access to local programs. (See Figure 11.1) In and
of itself, this is not very spectacular, but in the interest of security, you do not want
to allow network access to just any old program or data file. Some of these files are just
Gateways also provide the interface for allowing
users to interact with Web content through clickable images and forms. Then, when the user
selects whatever options he wants in the forms and images, the gateway can take that input
and return dynamic content. This means that the document that is returned to the
client may not actually exist until the user requests it. The gateway does this by taking
the input provided by the user and creating a document on-the-fly that meets the user's
One example of these gateway interfaces is ODBC
(Open DataBase Connectivity). ODBC is the database feature of Windows that allows the
system to access any of a number of different kinds of databases without having to launch
a full-blown database application. Web services do not generally know how to access a
database, so they need a gateway between the user and the ODBC driver.
The user's request is translated by the gateway into
a process that the ODBC system can understand. Then, when ODBC returns its results, the
gateway creates an HTML document based on those results. That's dynamic content.
CGIs can be created with any of a number of
different languages. They come in two flavorsscripted and compiled. Either way they
will work in the same manner, which I will describe throughout this chapter.
If you want to compile your CGI script into an .exe
or .dll file, you will need to use a programming language like C, C++, or Visual Basic.
These languages create a binary file that many newer programmers prefer.
The preference of a programming language over a
scripting language is due, for the most part, to the power and ease with which compiled
applications are developed. Most have some sort of windowed design interface that aids in
the creation of the CGI. Visual Basic, for instance, uses a context-sensitive help system
and will even reformat and capitalize your code as you type. (See Figure 11.2.)
Alternatively, most seasoned Internet programmers
prefer to use one of the scripting languages of the Internet such as Perl, UNIX shell, or
even AppleScript. There are several good reasons for using a script instead of a compiled
application. For one, scripts are just text files.
Real-time editing and debugging of these scripts is
quick and simple because you don't have to recompile every time. Also, because they are
text files, they can usually be edited and tested from a remote location, whereas compiled
binaries have to be deleted from the CGI-BIN directory, recompiled, and then re-sent to
the server before they can be tested.
CGI Script Installation
For security reasons, CGIs are normally kept in a
restricted-access directory. This directory is usually named something like \cgi-bin\ or a
derivative thereof. Most ISPs will not allow users to upload their CGIs directly to this
directory, although they can always retrieve information from that directory.
The potential threat from which ISPs want to protect
themselves comes in the form of malicious or undesirable functions being called by the
script. For example, if you were to create a script, and it had a bug that placed it into
some sort of endless loop, the system resources used by that script could not be recovered
until a human being came along and reset the server.
If the flawed CGI script were called several times
before the Webmaster gets around to fixing it, the system could hang and crash. This would
crash the Web site for every single one of the ISP's users. Such a disaster would put the
ISP in hot water with hundreds of users, and would cost them and their users several more
thousands of dollars each. It would also, most likely, cost you your Web privileges. This
is a good argument for debugging your programs.
For this reason, many ISPs require you to send your
CGI to an individual within its organization. That individual has the responsibility of
crash-testing the script to find hidden hazards before they can cause any serious grief.
Only then is the script posted to the CGI directory. This process can take days or weeks.
This is a good argument for having a strong, trusting relationship with your Internet
The concept behind CGI programming is pretty simple.
The CGI takes command-line parameters and environment variables and something called StdIn
(standard input) and returns a document through the StdOut (standard output).
Command-line Parameters and Environment Variables
In a standard DOS or Windows environment, programs
can be launched with a command-line parameter . This means, for example, that if you were
to type the following at a prompt
the program MyProgram.exe would be launched, and it
would use the parameters Parameter1 and Parameter2 as part of the start process.
When a user requests a CGI from an HTTP server, it
usually does so while passing a set of parameters, but these may not always be passed to
the CGI as command-line parameters. Usually, the URL parameters are passed to the
CGI through environment variables instead. For example:
In this example, "Bucky" would be passed
to the CGI application MyCGI.exe as a command-line parameter. This would cause the server
to execute the command MyCGI.exe Bucky. An example of a CGI request that would pass the
parameters through an environment variable instead of a command line would look like this:
In this example, Bucky would not be passed to the
CGI application as a parameter. Instead the HTTP daemon would set an environment
variable called MyName to the value Bucky. This would cause the server to execute the
command MyCGI.exe all by itself. It is up to the CGI script itself to retrieve data from
The = is the key here. If there is no equal sign,
the query string (everything after the ?) is passed to the CGI application as a
If there exists an = anywhere in the query string,
everything to the left of the equal sign is treated as an environment variable to be set.
Everything to the right of the equal sign is treated as a value to be assigned to that
HTTP Query Syntax
A CGI program is usually launched by a client
request in the form of an HTTP query. The query can be formatted to send a command-line
variable to the CGI or to set one or more environment variables to be used by the CGI in
its operation. The parameter will be sent to the CGI on the command line unless there is
an = in the query string. If there is an equal sign, the parameters will be used to send
This line would launch MyCGI.exe Parm1 from the
This line would launch MyCGI.exe from the command
line and set the environment variable for Couple to the value of 2 and for Few to the
value of 3:
This is a good time to mention the & (ampersand)
character as well. Some of the more popular search engines (such as Yahoo! [Figure 11.3]
and WebCrawler) use CGIs that require several different environment variables to be set.
In this case, each variable and its associated value is set off from the one before it
with an ampersand:
In this example, the server would set three
environment variables. The variable DOSCommand would be set to the value Format, the
variable Drive would be set to C, and the variable Parm would be set to u. It would not
pass any command-line parameters to MyCGI.exe because there was an = in the query string.
Another way the & character is used is in string
concantenation . String concatenation is when you append an entire string to the
end of another string. For example, suppose you had to form fields (HTML or VBIt
doesnt matter). One of them contains an Area Code value and the other contains a
Phone Number value. You may not care to separate the two for this particular form, since
you are treating both as one string; so you concatenate the two with an &
character like so:
Dim strAreaCode as String Dim strPhoneNumber as String Dim strBothNumbers as String strBothNumbers = strAreaCode & strPhoneNumber
This would set the value of strBothNumbers to the
area code with the phone number appended to it.
Many tutorials teach that the + sign can be used
instead of the & sign to achieve the same concatenation results. This is sometimes
true. If you use the + sign with two string values, both of which begin with letters
instead of numbers, the two may concatenate well. If, however, one of those variables has
not already been defined as a string or begins with an integer, unexpected results may
Standard input (StdIn) is the information that is
sent from the client to the script server with an HTTP Put or Post method. It refers to
two variablesContent_Length and Content_Type.
Content_Type is the MIME type for the object that is
being sent to the server script as data. This could be a picture, video, sound, or just
about anything that can exist on a computer (except, maybe, a dust cover). If the data
being sent is a Web form, the MIME type is application/x-www-form-urlencoded.
After the script server receives the StdIn, it
performs any functions contained within the script and returns the Standard output
(StdOut). This can be a document that goes back to the client that made the CGI request,
or it can be instructions to the HTTP daemon as to what information the daemon should send
back to the client. Either way, the StdOut consists of just a few headers and a data
stream of some kind.
In the StdOut you can perform any one of four
If the end result of the CGI is to simply send a
file that already exists on some remote server to the client, all you have to do is send a
Location: header followed by the full URL of the remote document, like this:
If the result of the CGI is to send a file that
already exists on the same server as the CGI, you send that same Location: header, only
this time you need only follow it with the relative path, like this:
If the CGI result is to send a file it created by
itself, on-the-fly, you need to send the Content-Type: header followed by a blank line and
the document data:
This line would let the server take care of passing
the HTTP header information to the client, but in some cases it may be necessary,
expedient, or just downright fun to send the header information yourself while preventing
the HTTP daemon from sending that same type of header information. Also, by not requiring
the HTTP daemon to process your HTTP headers, the execution speed of the CGI is increased
Should you choose to send the header information
yourself, you can report various information back to the client. This includes such things
as the level of HTML that you support, and the name and version number of the application
you are using. This information can be important when your users are scanning through
their log files, trying to identify which programs performed what actions on their
The way you tell the server that your CGI is going
to talk to the client directly is to meet these two requirements:
An StdOut that the CGI returns in this manner would
look something like this:
Using CGIs with HTML Forms
Now that you know how data is sent to the script
server from the client, as well as how response data is sent back to the client from the
script server, now you can learn how an HTML form is used to command a client to start
sending that data.
How these forms are placed within an HTML document
is a topic that will be covered in the last seven chapters of this guide. An example of
one, however, can be seen in Figure 11.3.
For now, it should be enough to know that there are
two different methods that an HTML Form can use to interact with CGI"Get"
In the Get method, all the information that follows
the ? on the URL line is placed in the environment variable Query_String. It is then up to
the CGI program to parse that string appropriately to retrieve the data that the user
entered into the HTML form.
In the Post method, the user-specified HTML form
information is sent to StdOut. This is sent as a block of HTTP data, rather than an
extended URL such as in the Get method.
From the data stream of a Post method itself, there
is really no way for your CGI to automatically know when it has received all of the data
that was sent from the client. As a result, your CGI must determine the length of the
string in bytes from the Content_Length environment variable. Then it will use that byte
count to figure out when it has received everything that was to be transmitted.
Repeating what I mentioned earlier, ISAPI is not
just a replacement for CGIit's a strong enhancement to it. CGI is used on almost
every kind of HTTP daemon, but it is not OLE capable like ISAPI. As a result, CGI scripts
require more overhead in terms of memory and system resources.
Executables Versus Dynamic Link Libraries
A gateway interface can be compiled in one of two
ways. It can be compiled into an executable (.exe) or into a Dynamic Link Library (.DLL).
Although each has its own benefits as well as drawbacks, DLLs are the preferred form. Both
are referred to as PEs (portable executables).
If you are at all familiar with compiling programs
(as you should be if you are reading this guide), you already know how to compile an
executable (.exe) file.
Dynamic Link Libraries
A dynamic link library (.dll file) is a bit more
complex to create. When you create this type of file, you are creating something that can
not be run from the operating system's command line, but rather is called by some other
process external to the .dll itself.
When an external process makes a call to a .DLL, it
does not actually "run" the library, but rather makes reference to a procedure
within that library. These calls are often referred to as API calls. When you
create an ISAPI .DLL, you are creating an ActiveX OLE control. This calling of a process
within a .DLL is called linking. Two types of linking can be used with ActiveX
DLLsruntime and load-time.
In the case of load-time linking, the library sits
idle until an explicit call is made to it. When this happens, the program that called it
will perform whatever functions it needs and then, when the library is no longer needed,
it is unloaded and remains idle until called upon again. These types of libraries are also
referred to as out-of-process or new process servers.
Runtime linking is very similar to load-time
linking; however, when the library is a runtime library, it is called with the LoadLibrary
function. It then uses the GetProcAddress method to identify the address that is used in
launching the library's functions.
In an HTTP server, runtime ISAPI libraries are
loaded when the HTTP server is loaded and then shares resources with it by running in the
server's process. This eliminates a significant amount of overhead normally associated
with CGI scripts. CGIs run in a process separate from the serverdemanding equal
access to the system resources. Finally, when the library is not needed anymore, it can be
As you see, ISAPI DLLs are loaded and unloaded as
necessary by the HTTP daemon. When a call is made to an ISAPI library it is loaded into
memory. Then, when the server decides that it no longer needs that process, it unloads it,
freeing up memory and resources for other processes. Alternatively, the DLL can be
"preloaded" by the server so that when the first user attempts to access it,
there won't be a pause when it's loaded.
In-Process Versus New Process
A significant difference between the way CGI and
ISAPI DLLs are run is the addresses in which they are loaded.
In the case of CGIs, they are loaded completely
separate from the server process. This is called running in a new process.
In the case of ISAPIs, they are loaded into the same
process as the HTTP server. This economizes memory use by avoiding all of the overhead
associated with running several different processes in several different areas of memory.
From a system administrator's standpoint, the whole
daemon will run smoother and will be less prone to bottlenecks when multiple simultaneous
connections are initiated by remote clients.
There is another, very powerful advantage to running
ISAPI processes. When a .DLL is first called, be it CGI or ISAPI, it is loaded into memory
and run. In ISAPI, if the .DLL is still loaded and several other clients also request it,
it will not need to load another copy of itself for each request. CGI needs to run a
separate instance for each request, but ISAPI does not.
HTTP and ISAPI Interaction
From a user standpoint, the making of a request to
and the receiving of data from an ISAPI server is done exactly the same as with CGI. This
is as it ought to be, because the client's browsers are using the HTML and HTTP standards
for queries and responses.
The browsers don't know or care if the server script
is CGI, ISAPI, Perl, or a custom proprietary script. They send and receive their data
according to the World Wide Web Consortium's standardsnot some unofficial
"standard" proposed by Microsoft, Netscape, or anyone else.
Although the data and procedures used by ISAPI are
similar to those of CGI, from a programmer's standpoint the information is passed back and
forth in a very different way. The reason for this is the OLE nature of ISAPIs.
Theses advanced OLE features also make the process
much more complex, and fall well outside the scope of anything with which a new user may
be familiar. However, because ISAPI is such a powerful feature of the ActiveX family of
technologies, I am including it here.
The Extension Control Block
The Extension Control Block (ECB) is ISAPI's
equivalent of StdIn and StdOut. It is a data structure that performs the passing of data
from a client to the script and back. The actual data that is in this includes everything
you would have sent and received through StdIn/StdOut plus some.
In the ECB structure are variables such as
Query_String, Path_Info, and Path_Translated. In addition, this structure includes several
functions that are used to pass information back and forth. These functions include
GetServerVariable, WriteClient, and ReadClient.
Every ISAPI DLL requires certain entry points .
Entry points are features of an object server through which the DLL will interact with the
rest of the system. They include GetExtensionVersion, HTTPExtensionProc,
TerminateExtension, and a set of return values.
This is the first process called when the HTTP
daemon attempts to run an ISAPI server. If the DLL does not support it, the whole process
This process is called in response to every client
request. It can be compared to Visual Basic's Sub_Main feature because it runs as soon as
the DLL is loaded. It uses the ReadClient callback functions to read the client data.
When the ISAPI server gets this data, it can then go
about figuring out what it is supposed to do with it.
After the ISAPI server has done whatever it was
written to do, it needs to return a value indicating its status to the daemon. This status
will indicate whether the request is still pending, if there was an error, or that the
process has been completed successfully.
One type of return value that you will want to make
sure to use is the data to be sent back to the client using the WriteClient or
This process is the exact opposite of the
HTTPExtensionProc process. It is run when the DLL is to be shut down and is very useful
for freeing up system resources.
Just because your program has run its course and is
ready to be unloaded doesn't necessarily mean that every process it initiated is complete.
When the DLL makes a call to other OLE servers on the machine, those servers may get hung
up or otherwise fail to exit properly. The TerminateExtension process provides a place
where programmers can put any code to clean up the mess left behind when these errors
Failure to take advantage of this process can put
your system in a resource crunch. As each of the different programs and processes are
executed, they take up a certain amount of memory and/or processor time. If these keep
getting loaded and are never unloaded, they will tie up more and more of your system.
Eventually your 128MB Web server could be left with only 2MB or 3MB of memory
availablethe rest being reserved for those incomplete processes and threads that
were not cleaned up.
Within the HTTPExtensionProc process are several
functions that can be called to read and set information about the current connection.
GetServerVariable is the function ISAPIs use to
retrieve information for which a CGI would have used an environment variable. This
includes information about the current connection as well as any server-specific
This function, like its namesake and counterpart in
CGI, is used to send output to the client host. When the function is complete, it will
return a boolean (True/False) value indicating whether the operation was successful. If it
was not, and it returns a False value, you can use the GetLastError function to find out
the nature of the problem.
The ReadClient function is another function that has
a namesake counterpart. It is very similar to the WriteClient CGI process. It is used to
read the information that the client sends through the HTTP connection. When this function
is used, it will return a boolean response indicating whether it was successful.
If the ReadClient function returns a False response,
it means that there was some sort of problem. The server can then use the GetLastError
function to determine the nature of the problem. The opposite would seem to be indicated
by a True response, but this is not necessarily going to be the case every time.
When the ReadClient function returns a True
response, it can either mean that the function was successful, or it could mean that the
Windows socket closed before the operation was complete. If the latter is the case, and
the socket timed out or was otherwise shut down prematurely, the ReadClient will return a
True response, but at the same time, the data it retrieves will be 0 bytes in length.
The reason for this seemingly illogical way of
handling a premature closure of the socket is due to the fact that in the event of a
failure without a closed socket, you may want to retry the ReadClient function until it
works properly. However, if you keep retrying to ReadClient when the socket is closed and
you keep returning a False value, the server will get stuck in an infinite loop.
When the function reports the process as successful,
but with a 0-length output, then a retry won't be triggered. The server will then try to
read the data that was returned (or the data that was not returned, in this case) and,
seeing that it is 0 bytes long, this result can be used as a trigger to check for a closed
Considerations for Building an ISAPI DLL
After you have completed the basic functionality of
your ISAPI OLE server, there are a few points you should take into consideration. These
points are best left for the rewriting and debugging process. How they are implemented
depends on the purpose of the server. They are not supposed to be the framework within
which to write the program.
Avoiding confusion of the HTTPExtensionProc with the
CGI StdIn, you will want to make sure that each of the server variables is taken from the
ECB data structure and that you are not referencing environment variablesas you
would do with CGI.
In this process you will receive a pointer to the
ECB data, which is the only parameter passed. Then you can use the GetServerVariable
feature to access whichever variables you need in your ISAPI. The ISAPI can also use the
ReadClient feature to retrieve data from the remote host that requested the process.
You can't debug an OLE server enough, but you sure
better try. In the case of ISAPI servers , this is particularly critical because an
ill-performing ISAPI that runs in the same process as the daemon can bring the system down
if it tries to perform an OLE function on something that is not ready for it. One area
where this type of debugging is of special importance is in an environment where several
ISAPI OLE servers are used.
As with CGIs, there can be (and usually is) more
than one ISAPI server that you will want to make available to your users. When multiple
ISAPIs are running, they stand a risk of bumping into each other and trying to modify the
same bit of data at the same time.
When testing your ISAPI server, you will want to
make sure that you test itnot only alone, but also in combination with other ISAPI
servers that will be on the same machine. To resolve access violations, and to prevent
them in the first place, you should obtain a copy of the Win32 API. This document details
many different features of Windows that keep the OLE programmer and his project out of
trouble. A few of them are described here.
This type of entry function is called by an ActiveX
Web daemon during the LoadLibrary and FreeLibrary processes. In it you should have any
information necessary to both get the ISAPI server up and running, and to bring it back
down and unload it.
The ECB structure uses a field named lpszLogData to
provide information for logging of activity by the ISAPI OLE server. When your ISAPI needs
to record log data, it is usually best to log at least some information to the server's
log. Doing this provides the system administrators for the Web daemons the information
they need to determine the cause of any mysterious ISAPI-related problems.
If you log your information where the system
administrators can see it, they will be better equipped to lay blame in the proper place
without too much fishing around. It is very easy for a system administrator to kick your
ISAPI server back to you and blame it for all kinds of problems if you are not logging to
his logs and a problem occurs. Logging to this file also provides a way for the system
administrator to track the use of different OLE servers and determine which ones are used
and which are not.
Converting CGI to ISAPI
CGIs and ISAPIs share a common
purposeserver-side scripting. Although they perform very similar purposes,
converting your older CGI applications to new ISAPI ones involves a technological shift
from command-line-oriented programming to object-oriented programming.
You won't have to change the logic of your gateway
interfacealthough you will have to change much of the programming syntax used to
Converting the other way however, from ISAPI to CGI,
would not be quite so simpleif it were even possible. Taking an ISAPI and converting
it to CGI could be impossible in many cases because ISAPI takes advantage of many OLE
features with which CGI is utterly incapable of dealing.
Let's take a look at some of the considerations you
will need to take in converting your CGI into an ISAPI OLE server.
StdIn/StdOut to ECB
CGI receives its input from the StdIn features. You
should go through your ISAPI project, ensuring that it retrieves this data from the
lpbData field of the ECB, and through use of the ReadClient callback function instead of
Then, when execution of the CGI is complete and it
is ready to return its results, it sends signals through StdOut. This is done by sending
Status: ### XXXX (where # is an integer and X is a character) through
the WriteClient method.
You should find these places within your project and
make sure that you use one of the ISAPI methods to send data to the client. This is done
by using the WriteClient method, defined in the ECB. This method is very similar to the
CGI equivalent by the same name. Alternatively, you can use the
HSW_REQ_SEND_RESPONSE_HEADER feature of the ServerSupportFunction.
If your CGI uses Location: or URL: to return a prior
existing file, these processes need to be replaced with their ISAPI equivalents within the
ServerSupportFunction. This includes HSE_REQ_SEND_URL for files that are local to the
server and HSE_REQ_SEND_URL_REDIRECT_RESP for files that are on a remote server or other
unknown location .
In this chapter you have become acquainted with
server-side scripting. The lessons here deal with CGI and ISAPI scripts, but other scripts
(such as Perl and LISP) can be used also.
Both CGI and ISAPI pass infomration between a Web
server and a Web client (usually a browser). The information for CGI is formatted exactly
the same as for ISAPI because the information that flows back and forth across the Net
must conform to the HTTP standard. The difference between CGI and ISAPI is in how the
information is handled on the server.
Most CGI parameters can be sent through environment
variables; if not, information can be passed on the command line.
In the case of ISAPI, information is passed through
OLE interfaces defined in the ISAPI library file.
ISAPI is a feature of ActiveX that works on an
Internet server. It has certain benefits over CGI. When several clients request the same
CGI process at the same time, a separate instance of the CGI must be run for each client
request. An ISAPI need only have one instance of itself loaded to service multiple
simultaneous requests. Also, a CGI runs in a process separate from the server, while
ISAPIs run within the server process.
Using a site such as Yahoo! (http://www.yahoo.com) or Deja News (http://www.dejanews.com), create a customized HTML form
that will search its database. Visit its site and observe the HTML source for its forms.
This is called reverse engineering.