This document describes the Windows CGI
interface. The Windows httpd server package includes a
reference implementation in Visual Basic which contains a re-usable
module that creates the CGI environment within a VB program.
Try out the Windows CGI Demo.
Windows has no native command interpreter. Therefore, any back-end must be an executable program. A goal to keep the interface simple and minimize back-end programming requirements. Therefore, a file-based interface has been chosen (as opposed to DDE or OLE). Request content is placed into a content file, and results must be written to an output file.
It is expected that many of the back-end applications will be developed using Microsoft Visual Basic (VB). VB supports generation of a .EXE file, and supports a wide range of features for accessing data in the Windows environment, such as OLE, DDE, Sockets, and ODBC. The latter permits accessing data in a variety of databases, relational and non-relational. The VB application can be developed without the need for any windows (forms), consisting purely of VB code modules. This makes it possible to meet the recommendation that the back-end execute invisibly, or at least as an icon.
It is hoped that this interface will be standardized among Windows based HTTP servers.
back-end-exe cgi-data-file content-file output-file url-args
back-end-exe
cgi-data-file
content-file
This file is created even if there was no content supplied with the request (in which case it will be a zero-length file).
output-file
url-args
The launched process itself
should not cause the appearance of a window nor a change in the Z-order
of the windows on the desktop.
The server supports a back-end/script debugging mode. If that mode is enabled, the back-end is launched such that its window shows and is made active. This will assist in debugging back-end applications.
The CGI data file contains the following sections:
Request Protocol
Request Method
Executable Path
Logical Path
Physical Path
Query String
Content Type
Content Length
Content File
Server Software
Server NameThe network host name or alias of the server, as needed for self-referencing URLs.
Server PortTne network port number on which the server is listening. This is also needed for self-referencing URLs.
Server Admin
CGI Version
Remote Host
Remote Address
Authentication Method
Authenticated Username
Accept: type/subtype {parameters}If the parameters (e.g., "q=0.100") are present, they are passed as the value of the item. If there are no parameters, the value is "Yes".
Note: The accept types may easily be enumerated by the back-end with a call to GetPrivateProfileString() with NULL for the key name. This returns all of the keys in the section as a null-delimited string with a double-null terminator.
Output File
Content File
Note: The extra headers may easily be enumerated by the back-end with a call to GetPrivateProfileString() with NULL for the key name. This returns all of the keys in the section as a null-delimited string with a double-null terminator.
Raw form input is of the form "key=value&key=value&...", with the value parts in url-encoded format. The server splits the key=value pairs at the '&', then splits the key and value at the '=',url-decodes the value string and puts the result into key=(decoded)value form in the [Form Literal] section.
If the form contains any SELECT MULTIPLE
elements, there
will be multiple occurrences of the same key. In this case, the server
generates a normal "key=value" pair for the first occurrence, and it
appends a sequence number to subsequent occurrences. It is up to the
CGI back-end to know about this possibility and to properly recognize
the tagged keys.
key=pathname lengthwhere pathname is the path and name of the tempfile containing the decoded value string, and length is the length in bytes of the decoded value string.
Note: Be sure to open this file in binary mode unless you are certain that the form data is text!
key=offset lengthwhere offset is the offset from the beginning of the Content File at which the raw value string for this key is located, and length is the length in bytes of the raw value string. You can use the offset to perform a "Seek" to the start of the raw value string, and use the length to know when you have read the entire raw string into your decoder. Note: Be sure to open this file in binary mode unless you are certain that the form data is text!
[Form Literal] smallfield=123 Main St. #122 multiple=first selection multiple_1=second selection [Form External] field300chars=C:\TEMP\HS19AF6C.000 300 fieldwithlinebreaks=C:\TEMP\HS19AF6C.001 43 [Form Huge] field230K=C:\TEMP\HS19AF6C.002 276920
The data stream consists of two parts: the header and the body. The header consists of one or more lines of text, and is separated from the body by a blank line. The body contains MIME-conforming data whose content type must be reflected in the header.
The server does not interpret or modify the body in any way. It is essential that the client receive exactly the data that was generated by the back end.
Content-Type
Status
If the back-end response does not contain a Status:
header, the server assumes that the back-end operation succeeded
normally, and generates a "200 OK" status.
Location
The server looks at the results in the Output file, and if the first line starts with "HTTP/1.0", it assumes that the results contain a complete HTTP response, and sends the results to the client without packaging.
--- BEGIN --- Content-type: text/html <== MIME type of body Status: 200 OK <== HTTP status (optional) <== Header-body separator <HTML> <== Body starts here <HEAD> <TITLE>Sample Document</TITLE> </HEAD> <BODY> <H1>Sample Document</H1> [... etc.] </BODY> <HTML> --- END ---
--- BEGIN --- Location: ftp://ftp.netcom.com/pub/www/object.dat <== URL of object <== Blank line --- END ---
--- BEGIN --- HTTP/1.0 200 OK <== Start of HTTP Header Date: Tuesday, 31-May-94 19:04:30 GMT Server: WinHTTPD/1.4 MIME-version: 1.0 Content-type: text/html Last-modified: Sunday, 15-May-94 02:12:32 GMT Content-length: 4109 <== Header-body separator <HTML> <HEAD> <TITLE>A document</TITLE> [... etc.] --- END ---
GetPrivateProfileString()
differs from that normally seen in
VB programs. The difference is compatible, and allows enumeration by passing
NULL for the key name. A '\' at the end of a line indicates continuation.
The code must actually be all on one line.
Declare Function GetPrivateProfileString Lib "Kernel" \ (ByVal lpSection As String, \ ByVal lpKeyName As Any, \ <== This permits NULL key name ByVal lpDefault As String, \ ByVal lpReturnedString As String, \ ByVal nSize As Integer, \ ByVal lpFileName As String) As Integer Type Tuple ' Used for Accept: and "extra" headers Key As String ' and for holding POST form key=value pairs value As String End Type Global CGI_ProfileFile as String ' Pathname of CGI Data File Global CGIAcceptTypes(MAX_ACCTYPE) as Tuple ' Accept: types array Global CGINumAcceptTypes as Integer ' Number of Accept: types in array Const ENUM_BUF_SIZE 8192 ' Size of key enumeration buffer '--------------------------------------------------------------------------- ' ' GetProfile() - Get a value or enumerate keys in CGI_Data file ' ' Get a value given the section and key, or enumerate keys given the ' section name and "" for the key. If enumerating, the list of keys for ' the given section is returned as a null-separated string, with a ' double null at the end. ' ' VB handles this with flair! I couldn't believe my eyes when I tried this. '--------------------------------------------------------------------------- Private Function GetProfile (sSection As String, sKey As String) As String Dim retLen As Integer Dim buf As String * ENUM_BUF_SIZE If sKey <> "" Then retLen = GetPrivateProfileString(sSection, \ sKey, "", buf, ENUM_BUF_SIZE, CGI_ProfileFile) Else retLen = GetPrivateProfileString(sSection, \ 0&, "", buf, ENUM_BUF_SIZE, CGI_ProfileFile) End If If retLen = 0 Then GetProfile = "" Else GetProfile = Left$(buf, retLen) End If End Function '--------------------------------------------------------------------------- ' ' GetAcceptTypes() - Create the array of accept type structs ' ' Enumerate the keys in the [Accept] section of the profile file, ' then get the value for each of the keys. '--------------------------------------------------------------------------- Private Sub GetAcceptTypes () Dim sList As String Dim i As Integer, j As Integer, l As Integer, n As Integer sList = GetProfile("Accept", "") ' Get key list l = Len(sList) ' Length incl. trailing null i = 1 ' Start at 1st character n = 0 ' Index in array Do While ((i < l) And (n < MAX_ACCTYPE))' Safety stop here j = InStr(i, sList, Chr$(0)) ' J -> next null CGI_AcceptTypes(n).Key = Mid$(sList, i, j - i) ' Get Key, then value CGI_AcceptTypes(n).value = GetProfile("Accept", CGI_AcceptTypes(n).Key) i = j + 1 ' Bump pointer n = n + 1 ' Bump array index Loop CGI_NumAcceptTypes = n ' Fill in global count End Sub