<COMMENT>(--------------------------------------------------------------------)

<CHAPTER>(CGIplus\chapter_scripting_cgiplus)

<P> 

<COMMENT>(HTML=
<TABLE BORDER=0>
<TR><TD VALIGN=top>
<NOBR><FONT SIZE=+1><B>
Common Gateway Interface ... 
<B></FONT></NOBR>
</TD><TD>
<FONT SIZE=+1><B>
plus lower latency,
<BR>plus greater throughput,
<BR>plus far less system impact!
<B></FONT>
</TD></TR>
</TABLE>
)

<COMMENT>(HTML/OFF)
<EMPHASIS>(Common Gateway Interface ... plus lower latency, plus greater
efficiency, plus far less system impact!\BOLD)
<COMMENT>(HTML/ON)

<P> I know, I know!  The term <UNDERLINE>(CGIplus) is a bit <UNDERLINE>(too
cute) but I had to call it something!

<P> CGIplus attempts to eliminate the overhead associated with creating the
script process and then executing the image of a CGI script.  It does this by
allowing the script process and any associated image/application to continue
executing between uses, eliminating 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.

<P> 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.

<HEAD>(CGIplus Performance\head_cgiplus_perf)

<P> A simple performance evaluation indicates the advantage of CGIplus.  See
<QUOTE>(Techncial Overview, Performance) for some test results comparing the
CGI and CGIplus environments.

<P> Without a doubt, the subjective difference in activating the same script
within the standard CGI and CGIplus environments is quite startling!

<HEAD1>(CGIplus Programming\head_cgiplus_programming)

<P> <EMPHASIS>(The script interface is still CGI\BOLD), 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.

<COMMENT>(....................................................................)

<COMMENT>(HTML=
<P> See examples in
<A HREF="/ht_root/src/CGIplus/*.*">HT_ROOT:[SRC.CGIPLUS]</A>
)

<COMMENT>(HTML/OFF)
<P> See examples in HT_ROOT:[SRC.CGIPLUS]
<P> <BOX>(online hypertext link)
<COMMENT>(HTML/ON)

<COMMENT>(....................................................................)

<P> Instead of having the CGI variables available from the environment
(generally accessed via the C Language <EMPHASIS>(getenv()) standard library
call, or via DCL symbols) a CGIplus script must read the CGI variables from an
I/O stream named CGIPLUSIN.  The variables can be supplied in one of two modes.

<LIST>(UNNUMBERED)

<LE> <EMPHASIS>(Record Mode - \BOLD)  Each CGI variable is supplied as an
individual record (line).  This is the default, and simplest method.  Each
contains 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.

<LE> <EMPHASIS>(Struct Mode - \BOLD)  All variables are supplied in a binary
structure which must be parsed by the receiving script.  This is the most
efficient method for providing the CGIplus script with it's CGI environment. 
Performance improvements in the order of 50-100% have been measured.  This size
of this structure is limited by the size of the CGIPLUSIN mailbox, which is in
turn set as described above.

<ENDLIST>

<HEAD>(Record-Mode CGIplus\head_cgiplus_record_mode)

<P> This default and simple record-oriented mode allows any environment that
can read records from an input source to process CGIplus variables.  This of
course includes DCL (examples referenced below).

<LIST>(UNNUMBERED)

<LE> The read will block between subsequent requests and so may be used to
coordinate the application.

<LE> The first record read in any request can always be discarded.  This is
provided so that a script may be synchronized outside of the general CGIplus
variable read loop.  Record-mode can always be recognised by the single
exclamation symbol comprising this record.

<LE> The CGIplus variable stream <EMPHASIS>(must always be completely
read\BOLD), record-by-record up until the the first empty record (blank line,
see below) before beginning any request processing. 

<LE> An empty record (blank line) indicates the end of a single request's
CGIplus variable stream. Reading MUST be halted at this stage.  Request
processing may then commence.

<ENDLIST>

<HEAD>(Struct-Mode CGIplus\head_cgiplus_struct_mode)

<P> This mode offers significantly lower system overheads and improves latency
and performance at the cost of the additional complexity of recovering the
variables from a binary structure.  Code examples and CGILIB functions make
this relatively trivial at the application level.

<LIST>(UNNUMBERED)

<LE> The read will block between subsequent requests and so may be used to
coordinate the application.

<LE> The first record read in any request contains the size of the following
binary record.  It is also provided so that a script may be synchronized outside
of the general CGIplus variable read loop.  Struct-mode can always be
recognised by the two, successive, leading exclamation marks preceding the
variable structure record size integer.

<LE> The CGIplus variables are read as a single, binary I/O.

<LE> The contents of the binary structure must be parsed to obtain the
individual CGI variables. Request processing may then commence.

<ENDLIST>

<HEAD>(Requirements when using CGIplus\head_cgiplus_requirements)

<P> After processing, the CGIplus script can loop, waiting to read the details
of the next request from CGIPLUSIN.

<P> Request output (to the client) is written to SYS$OUTPUT
(<LITERAL>(<stdout>)) as per normal CGI behaviour. <EMPHASIS>(End of output
MUST be indicated by writing a special EOF record to the output stream.\BOLD) 
A unique EOF sequence is  generated for each use of DCL via a zombie or CGIplus
script process. A non-repeating series of bits most unlikely to occur in normal
output is employed <HELLIPSIS> but there is still a very, very, very small
chance of premature termination of output

<COMMENT>(HTML=
(one in 2^<SUP>224</SUP> I think!)
See <A HREF="/ht_root/src/httpd/cgi.c">HT_ROOT:[SRC.HTTPD]CGI.C</A> for how the
value is generated.
)

<COMMENT>(HTML/OFF)
(one in 2^224 I think!)
See HT_ROOT:[SRC.HTTPD]CGI.C for how the value is generated.
<COMMENT>(HTML/ON)

<P> The CGIplus EOF string is obtained by the script from the logical name
CGIPLUSEOF, defined in the script process' 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 <LITERAL>(<CR>)<LITERAL>(<LF>) or just
<LITERAL>(<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.

<P> HTTP input (raw request body stream) is still available to a CGIplus script.

<HEAD>(CGI Function Library\head_cgiplus_cgilib)

<P> A source code collection of C language functions useful for processing the
more vexing aspects of CGI/CGIplus/RTE programming (<REFERENCE>(head_cgilib)).

<HEAD1>(Code Examples\head_cgiplus_code_examples)

<P> 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
<QUOTE>(well, this file will close when the image exits anyway)?)

<P> 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).

<CODE_EXAMPLE>
  int  IsCgiPlus;
  char  *CgiPlusEofPtr;

  IsCgiPlus = ((CgiPlusEofPtr = getenv("CGIPLUSEOF")) != NULL);
<ENDCODE_EXAMPLE>

<HEAD>(Record-Mode Code\head_cgiplus_record_mode_code)

<P> The following C code fragment shows a basic CGIplus record-mode request
loop, reading lines from CGIPLUSIN, and some basic processing to select
required CGI variables for request processing.  Generally this level of coding
is not required as it is recommended to employ the functionality of something
like the CGILIB functiona library.

<CODE_EXAMPLE>
  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);
        }

        <EMPHASIS>((process request, signal end-of-output))
     }
  }
<ENDCODE_EXAMPLE>

<HEAD>(Struct-Mode Code\head_cgiplus_struct_mode_code)

<P> This mode requires significantly more code than record-mode.  A
self-contained C language function, allowing CGI variable processing in
standard CGI, CGIplus record-mode and CGIplus struct-mode, is available for
inclusion in user applications.  It automatically detects the environment and
changes behaviour to suit.  This or CGILIB is strongly recommended.

<COMMENT>(....................................................................)

<COMMENT>(HTML=
<P> See source in
<A HREF="/ht_root/src/CGIplus/CGIplus_cgivar.c">HT_ROOT:[SRC.CGIPLUS]CGIPLUS_CGIVAR.C</A>
)

<COMMENT>(HTML/OFF)
<P> See source in HT_ROOT:[SRC.CGIPLUS]CGIPLUS_CGIVAR.C
<P> <BOX>(online hypertext link)
<COMMENT>(HTML/ON)

<COMMENT>(....................................................................)

<HEAD>(CGIplus Output\head_cgiplus_output)

<CP> 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.

<CODE_EXAMPLE>
  /* 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 {

     <EMPHASIS>((read request ...))

     /* CGI response header */
     fprintf (stdout, "Content-Type: text/html\n\n");

     <EMPHASIS>((other output ...))

     if (IsCgiPlus)
     {
        /* the CGIplus EOF must be an independant I/O record */
        fflush (stdout);
        fprintf (stdout, "%s", CgiPlusEofPtr);
        fflush (stdout);
     }

  } while (IsCgiPlus);
<ENDCODE_EXAMPLE>

<CP> If the script output is not binary (using default <LITERAL>(<stdout>)) it
is only necessary to ensure the EOF string has a record-delimiting new-line. 

<CODE_EXAMPLE>
  fprintf (stdout, "%s\n", CgiPlusEofPtr);
<ENDCODE_EXAMPLE>

<CP> Other languages may not have this same requirement.  DCL procedures are
quite capable of being used as CGIplus scripts.

<COMMENT>(....................................................................)

<COMMENT>(HTML=
<P> See examples in
<A HREF="/ht_root/src/CGIplus/*.*">HT_ROOT:[SRC.CGIPLUS]</A>
)

<COMMENT>(HTML/OFF)
<P> See examples in HT_ROOT:[SRC.CGIPLUS]
<P> <BOX>(online hypertext link)
<COMMENT>(HTML/ON)

<COMMENT>(....................................................................)

<NOTE>(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 :^) 

<P> Scripting processes may be purged or deleted using (
<QUOTE>(Techncial Overview, Server Command Line Control)):

<CODE_EXAMPLE>
  $ HTTPD /DO=DCL=DELETE
  $ HTTPD /DO=DCL=PURGE
<ENDCODE_EXAMPLE>
<ENDNOTE>

<HEAD1>(Other Considerations\head_cgiplus_other)

<P> Multiple CGIplus scripts may be executing in multiple processes 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
processes, monitoring those currently processing requests, creating new
instances if and when necessary, and deleting the least-used, idle CGIplus
processes 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.

<P> The CGIplus script process can be given a finite life-time set by
configuration parameter (see <QUOTE>(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 process 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.

<P> In addition, an idle CGIplus script can be run-down by the server at any
time the script process soft-limit is reached, so resources should be largely
quiescent when not actually processing (<REFERENCE>(head_script_rundown)). 
Of course, in extreme situations, a CGIplus process may also be manually
terminated from the command line (e.g. STOP/ID=).

<P> Some CGIplus scripting information and management is available via the
server administration menu, see <QUOTE>(Technical Overview, Server Reports).

<HEAD>(CGIplus Rule Mapping\head_cgiplus_mapping_rules)

<P> CGIplus scripts are differentiated from standard CGI scripts in the
mapping rule configuration file using the <QUOTE>(script+) and <QUOTE>(exec+)
directives.  See <QUOTE>(Technical Overview, Mapping Rules).

<P> Scripts capable of operating in both standard CGI and CGIplus environments
may simply be accessed in either via rules such as

<CODE_EXAMPLE>
  exec /cgi-bin/* /cgi-bin/*
  exec+ /cgiplus-bin/* /cgi-bin/*
<ENDCODE_EXAMPLE>

<CP> while specific scripts can be individually designated as CGIplus using

<CODE_EXAMPLE>
  script+ /cgiplus_example* /cgi-bin/cgiplus_example*
<ENDCODE_EXAMPLE>

<NOTE>(Hint!)
When changing CGIplus script mapping it is advised to purge execution of
existing scripts then reloading the mapping rules.  Some conflict is possible
when using new rules while existing CGIplus scripts are executing.

<CODE_EXAMPLE>
  $ HTTPD /DO=DCL=PURGE
  $ HTTPD /DO=MAP
<ENDCODE_EXAMPLE>
<ENDNOTE>

<COMMENT>(--------------------------------------------------------------------)
