[next] [previous] [contents] [full-page]3.1 - CGIplus Programming
3.2 - Code Examples
3.3 - Other Considerations
|
plus lower latency,
plus greater throughput, plus far less system impact! |
I know, I know! The term CGIplus is a bit too cute but I had to call it something!
CGIplus attempts to eliminate the overhead associated with creating the subprocess and then executing the image of a CGI script. It does this by allowing the subprocess and any associated image/application to continue executing between uses, eliminating any startup overheads. This reduces both the load on the system and the request latency. In this sense these advantages parallel those offered by commercial HTTP server-integration APIs, such as Netscape NSAPI and Microsoft ISAPI, without the disadvantages of such proprietory interfaces, the API complexity, language dependency and server process integration.
Existing CGI scripts can rapidly and elegantly be modified to
additionally support CGIplus. The capability of scripts to easily
differentiate between and operate in both standard CGI and CGIplus
environments with a minimum of code revision offers great versatility. Many
WASD scripts operate in both environments.
CGIplus Performance
A simple performance evaluation indicates the advantage of CGIplus. See "Techncial Overview, Performance" for some test results comparing the CGI and CGIplus environments.
Without a doubt, the subjective difference in activating the same script
within the standard CGI and CGIplus environments is quite startling!
3.1 - CGIplus Programming
The script interface is still CGI, with all the usual environment variables and input/output streams available, which means a new API does not need to be learned and existing CGI scripts are simple to modify.
See examples in HT_ROOT:[SRC.CGIPLUS]
Instead of having the CGI variables available from the environment (generally accessed via the C Language getenv() standard library call) a CGIplus script must read the CGI variables from CGIPLUSIN. They are supplied as a series of records (lines) containing a CGI variable name (in upper-case), an equate symbol and then the variable value. The format may be easily parsed and as the value contains no encoded characters may be directly used. The quantity of characters in each record depends on the size of the variable name and the length of the associated value. The value can vary from zero, to tens, hundreds, even thousands of characters. It is limited by the size of the CGIPLUSIN mailbox, which is in turn set by the [BufferSizeDclCgiPlusIn] configuration directive.
Requirements when using:
After processing, the CGIplus script can loop, waiting to read the details of the next request from CGIPLUSIN.
Request output (to the client) is written to SYS$OUTPUT (<stdout>) as per normal CGI behaviour. End of output MUST be indicated by writing a special EOF record to the output stream. A unique EOF sequence is generated for each use of DCL via a zombie or CGIplus subprocess. A non-repeating series of bits most unlikely to occur in normal output is employed ... but there is still a very, very, very small chance of premature termination of output (one in 2^224 I think!) See CGI.c for how the value is generated.
The CGIplus EOF string is obtained by the script from the logical name CGIPLUSEOF, defined in the script subprocess' process table, using the scripting language's equivalent of F$TRNLNM(), SYS$TRNLNM(), or a getenv() call (in the C standard library). This string will always contain less than 64 characters and comprise only printable characters. It must be written at the conclusion of a request's output to the output stream as a single record (line) but may also contain a <CR><LF> or just <LF> trailing carriage-control (to allow for programming language requirements). It only has to be evaluated once, as the processing begins, remaining the same for all requests over the life-time of that instance of the script.
HTTP input (raw request body stream) is still available to a CGIplus script.
CGI Function Library
A source code collection of C language functions useful for processing the
more vexing aspects of CGI/CGIplus/RTE programming. See
1.7 - Scripting Function Library.
3.2 - Code Examples
Of course a CGIplus script should only have a single exit point and should explicitly close files, free allocated memory, etc., after processing a request (i.e. not rely on image run-down to clean-up after itself). It is particularly important when modifying existing scripts to work in the CGIplus environment to ensure this requirement is met (who of us hasn't thought "well, this file will close when the image exits anyway"?)
It is a simple task to design a script to modify it's behaviour according
to the environment it is executing in. Detecting the presence or absence of
the CGIPLUSEOF logical is sufficient indication. The following C code fragment
shows simultaneously determining whether it is a standard or CGIplus
environment (and setting an appropriate boolean), and getting the CGIplus EOF
sequence (if it exists).
int IsCgiPlus;
char *CgiPlusEofPtr;
IsCgiPlus = ((CgiPlusEofPtr = getenv("CGIPLUSEOF")) != NULL);
The following C code fragment shows a basic CGIplus request loop, reading
lines from CGIPLUSIN, and some basic processing to select required CGI
variables for request processing.
if (IsCgiPlus)
{
char *cptr;
char Line [1024],
RemoteHost [128];
FILE *CgiPlusIn;
if ((CgiPlusIn = fopen (getenv("CGIPLUSIN"), "r")) == NULL)
{
perror ("CGIplus: fopen");
exit (0);
}
for (;;)
{
/* will block waiting for subsequent requests */
for (;;)
{
/* should never have a problem reading CGIPLUSIN, but */
if (fgets (Line, sizeof(Line), CgiPlusIn) == NULL)
{
perror ("CGIplus: fgets");
exit (0);
}
/* first empty line signals the end of CGIplus variables */
if (Line[0] == '\n') break;
/* remove the trailing newline */
if ((cptr = strchr(Line, '\n')) != NULL) *cptr = '\0';
/* process the CGI variable(s) we are interested in */
if (!strncmp (Line, "WWW_REMOTE_HOST=", 16))
strcpy (RemoteHost, Line+16);
}
(process request, signal end-of-output)
}
}
CGI scripts can write output in record (line-by-line) or binary mode
(more efficient because of buffering by the C RTL). When in binary mode the
output stream must be flushed immediately before and after writing the CGIplus
EOF sequence (note that in binary a full HTTP stream must also be used). This
code fragment shows placing a script output stream into binary mode and the
flushing steps.
/* reopen output stream so that the '\r' and '\n' are not filtered */
if ((stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin")) == NULL)
exit (vaxc$errno);
do {
(read request ...)
/* HTTP response header */
fprintf (stdout, "HTTP/1.0 200 Success\r\nContent-Type: text/html\r\n\r\n");
(other output ...)
if (IsCgiPlus)
{
/* the CGIplus EOF must be an independant I/O record */
fflush (stdout);
fprintf (stdout, "%s", CgiPlusEofPtr);
fflush (stdout);
}
} while (IsCgiPlus);
If the script output is not binary (using default <stdout>) it
is only necessary to ensure the EOF string has a record-delimiting new-line.
fprintf (stdout, "%s\n", CgiPlusEofPtr);
Other languages may not have this same requirement. DCL procedures are
quite capable of being used as CGIplus scripts.
See examples in HT_ROOT:[SRC.CGIPLUS]
Hint!
Whenever developing CGIplus scripts/applications (unlike standard CGI) don't forget that after compiling, the old image must be purged from the server before trying out the new!!! (I've been caught a number of times :^)Scripting subprocesses may be purged or deleted using (see "Techncial Overview, Server Command Line Control"):
$ HTTPD /DO=DCL=DELETE $ HTTPD /DO=DCL=PURGE
Multiple CGIplus scripts may be executing in subprocesses at any one time. This includes multiple instances of any particular script. It is the server's task to track these, distributing appropriate requests to idle subprocesses, monitoring those currently processing requests, creating new instances if and when necessary, and deleting the least-used, idle CGIplus subprocesses when configurable thresholds are reached. Of course it is the script's job to maintain coherency if multiple instances may result in resource conflicts or race conditions, etc., between the scripts.
The CGIplus subprocess can be given a finite life-time set by configuration parameter (see "Technical Overview, Server Configuration"). If this life-time is not set then the CGIplus will persist indefinitely (i.e. until purged due to soft-limits being reached, or explicitly purged/deleted). When a life-time has been set the CGIplus subprocess is automatically deleted after being idle for the specified period (i.e. not having processed a request). This can be useful in preventing sporadically used scripts from cluttering up the system indefinitely.
In addition, an idle CGIplus script can be terminated by the server at any time the subprocess soft-limit is reached (the subprocess SYS$DELPRC()ed) so resources should be largely quiescent when not actually processing. Of course a CGIplus subprocesses may also be manually terminated from the command line (e.g. STOP/ID=).
Some CGIplus scripting information and management is available via the
server administration menu, see "Technical Overview, Server Reports".
CGIplus Rule Mapping
CGIplus scripts are differentiated from standard CGI scripts in the mapping rule configuration file using the "script+" and "exec+" directives. See "Technical Overview, Mapping Rules".
Scripts capable of operating in both standard CGI and CGIplus environments
may simply be accessed in either via rules such as
exec /cgi-bin/* /cgi-bin/*
exec+ /cgiplus-bin/* /cgi-bin/*
while specific scripts can be individually designated as CGIplus using
script+ /cgiplus_example* /cgi-bin/cgiplus_example*
Caution! If changing CGIplus script mapping it is advised to restart the server rather than reloading the rules. Some conflict is possible when using new rules while existing CGIplus scripts are executing.