/*****************************************************************************/ /* RTE_example.c Simple example showing the essential elements of CGIplus-enabled, persistant Run-Time Environments. These ARE FUNDAMENTALLY DIFFERENT to CGIplus scripts, although built using the same mechanisms. A CGIplus script is a single-purpose, persistant script, that once activated does essentially the same task for each request it is passed. By way of contrast, a persistant run-time environment can be given a different script "source" each time and so can perform a different task each time. It is intended for environments such as Perl and Java where the "interpreter" startup costs can be expensive on a per-script basis. If the intepreter can be started once, then "initialized" afresh for each request passed to it both response latency and system impact can be significantly improved. This example demonstrates the essential elements, environment startup, waiting for each request in a CGIplus loop, getting CGI environment variables, processing a single request, indicating end-of-request. The actual "interpretation" performed by this example is just to list each record (line) against a line number. As can be seen from this example the basic infrastructure for this type of environment is quite straight-forward. Needless-to-say, with persistant environments it is important the "interpreter" cleans up thoroughly after each request (i.e. releases all dynamic memory allocated, closes all file handles, etc.) and ensures a thorough initialization of itself before commencing each request's processing. Remember, the behaviours of these scripts are WATCHable. WARNING! -------- Don't forget these are persistant environments. This means that especially during development, once changes have been made to source code and the environment rebuilt any currently executing instances of the previous build must be purged from the server environment (wish I had a dollar for every time I'd been caught like this myself!) $ HTTPD/DO=DCL=PURGE MAPPING RULES ------------- Each persistant run-time environment must have it's own mapping environment. This may, or may not, correspond to physically distinct directory areas. It is the mapping rules that identify run-time environments. Hence the following example shows two such environments each with it's own interpreter. exec+ /plbin/* (/cgi-bin/perlrt.exe)/wasd_root/perl_local/* exec+ /pybin/* (/cgi-bin/pythonrt.exe)/wasd_root/python_local/* The "/plbin/*" identifies request paths beginning with this string as a hypothetical, persistant Perl run-time interpreter, with the Perl source scripts available to it from WASD_ROOT:[PERL_LOCAL]. Similarly with a hypothetical Python interpreter. If a request with the following URI is given to the server http://the.host.name/plbin/plsearch/web/doc/?search=whatever the following components will be derived run-time interpreter ... CGI-BIN:[000000]PERLRT.EXE Perl source script ..... WASD_ROOT:[PERL_LOCAL]PLSEARCH.PL request path-info ...... WEB:[DOC] request query-string ... search=whatever This particular example RTE script "interpreter" can be mapped using exec+ /rtbin/* (/cgi-bin/rte_example.exe)/wasd_root/src/httpd/* The HTTPd server will normally search for the script file as with any other scripting environment, and report "script not found" if it does not map or exist as a "real" file. This behaviour may be suppressed for RTE's that "know" where their own scripts are (i.e. may not be files, but some "internal" script), or will do their own error reporting for missing script files. Use the following HTTPD$MAP mapping rule to suppress the server's script finding functionality. set /rtbin/* script=nofind exec+ /rtbin/* (/cgi-bin/rte_example.exe)/wasd_root/src/httpd/* LOGICAL NAMES ------------- RTE_EXAMPLE$DBUG turns on all "if (Debug)" statements BUILD DETAILS ------------- $ @BUILD_RTE_EXAMPLE BUILD !compile+link $ @BUILD_RTE_EXAMPLE LINK !link-only COPYRIGHT --------- Copyright (c) 2000 Mark G.Daniel This program, comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 2. VERSION HISTORY --------------- 28-OCT-2000 MGD v1.0.0, initial development */ /*****************************************************************************/ /* standard C header files */ #include #include #include #include /* VMS-specific header files */ #include /* CGILIB header file */ #include /* macros */ #define FI_LI __FILE__, __LINE__ /* global storage */ int Debug; /*****************************************************************************/ /* */ main () { /*********/ /* begin */ /*********/ Debug = (getenv ("RTE_EXAMPLE$DBUG") != NULL); CgiLibEnvironmentSetDebug (Debug); CgiLibEnvironmentInit (0, NULL, 0); if (!CgiLibEnvironmentIsCgiPlus()) { /* no point in a persistant environment if it never persists! */ CgiLibResponseSetErrorStatus (500); CgiLibResponseError (FI_LI, 0, "Not CGIplus environment!"); exit (SS$_NORMAL); } for (;;) { /* block waiting for the first/next request */ CgiLibVar (""); ProcessRequest (); CgiLibCgiPlusEOF (); } } /*****************************************************************************/ /* */ ProcessRequest () { static int UsageCount; char *cptr, *ScriptName, *ScriptFileName; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest()\n"); UsageCount++; if (strcmp (CgiLibVar ("WWW_REQUEST_METHOD"), "GET")) { CgiLibResponseSetErrorStatus (501); CgiLibResponseError (FI_LI, 0, "Only supports GET method!"); return; } ScriptName = CgiLibVar ("WWW_SCRIPT_NAME"); if (!ScriptName[0]) { CgiLibResponseSetErrorStatus (500); CgiLibResponseError (FI_LI, 0, "Internal error: no CGI \"SCRIPT_NAME\""); return; } ScriptFileName = CgiLibVar ("WWW_SCRIPT_FILENAME"); if (!ScriptFileName[0]) { CgiLibResponseSetErrorStatus (500); CgiLibResponseError (FI_LI, 0, "Internal error: no CGI \"SCRIPT_FILENAME\""); return; } CgiLibResponseHeader (200, "text/plain"); fprintf (stdout, "CGIplus-enabled Run-time Environment Example\n\ --------------------------------------------\n\ \n\ ***** FIRST, EVIDENCE OF PERSISTANCE *****\n\ \n\ Usage Count: %d\n\ \n\ ***** SECOND, THE CGI ENVIRONMENT AVAILABLE *****\n\ \n", UsageCount); /* retrieve a string displaying each successive CGI variable, NULL ends */ while ((cptr = CgiLibVar ("*")) != NULL) fprintf (stdout, "%s\n", cptr); fprintf (stdout, "\n\ ***** THIRD, AN \"INTERPRETED\" FILE (WWW_SCRIPT_NAME/WWW_SCRIPT_FILENAME) *****\n\ \n"); ProcessSourceFile (ScriptFileName); } /*****************************************************************************/ /* Simple file "processing". Read each line of the specified "source" file, outputing it preceded by the line number. Of course, for a "real" interpreter this would be the initiation point of the main "thread" of execution for the script. */ ProcessSourceFile (char *ScriptFileName) { int LineCount = 0; char Line [256]; FILE *SourceFile; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessSourceFile()\n"); if ((SourceFile = fopen (ScriptFileName, "r", "shr=get")) == NULL) { fprintf (stdout, "Error: could not open \"%s\", %%X%08.08X\n", ScriptFileName, vaxc$errno); return; } while (fgets (Line, sizeof(Line), SourceFile) != NULL) fprintf (stdout, "[%04.04d] %s", ++LineCount, Line); fclose (SourceFile); } /*****************************************************************************/