/*****************************************************************************/
/*
                              CGIplusTest.c

Script for testing the relative efficiencies of CGI and CGIplus.
Also for scripting benchmarks between the WASD, OSU and Apache environments!

This script can detect whether it is in a CGI or CGIplus environment and
adjust behaviour accordingly.  Call using the AB (ApcheBench) utility in both
environments to test throughput and response latency performance in both.
For WASD and OSU this script re-opens <stdout> in binary-mode, hence the full
HTTP response.  For Apache it leaves it in record-mode and uses a CGI response. 
Requires the CGI environment variables to be available as symbols.

September 2000 Note: Apache 1.3-12 seems intent on adding a <CR><LF> to each
record received, distorting the content-length and generally causing problems. 
This seems to lead to ApacheBench reporting errors, including length issues.

January 2001 Note: The above issue seems to have been worked around.
See description of ApacheFixBg().

Can also be used as example code for working in both environments (although
trying to be all things to all people the code is rather cluttered).
Demonstrates the alternative mechanism for accessing CGIplus variable values,
reading from <stdin> and parsing each line for required variables, including
the starndard/historical CGIplus variable record mode and weel as the WASD V7.2
and later 'struct' mode.   Define a system-wide logical CGIPLUSTEST$STRUCT to
force it into this latter mode (remembering any previous instance(s) of this
script should be deleted using HTTPD/DO=DCL=DELETE).

See the #included general-purpose [SRC.CGIPLUS]CGIPLUS_GETVAR.C function.

Also see [SRC.MISC]CGILIB.C for facilities that ease CGIplus implementation!


USAGE
-----
Supply a query string ("?blah-blah") of/to ...

*                  list all CGI(plus) variables
integer            output number of 80 character lines
before=integer     sleep number of seconds before beginning output
after=integer      sleep number of seconds after completing output
non-cgi-response   upset the server by outputting a non-CGI response


CGIPLUS_VAR_RECORD
------------------
To "force" the use of 'record' mode (perhaps for performance comparison
purposes) assign a non-empty environment variable (symbol or logical) named
CGIPLUS_VAR_RECORD.  This is detected in the CGIPLUS_CGIVAR.C module.


VERSION HISTORY (update SOFTWAREVN as well!)
---------------
23-DEC-2003  MGD  v1.4.2, minor conditional mods to support IA64
18-JUN-2003  MGD  v1.4.1, CSWS APACHE_SHARED_SOCKET to APACHE$SHARED_SOCKET,
09-APR-2001  MGD  v1.4.0, facility to test/measure CGIplus 'struct' mode
11-JAN-2001  MGD  v1.3.0, CSWS V1.0-1 (Apache) "fixbg" support
23-SEP-2000  MGD  v1.2,0, minor mods for comparison with VMS Apache
05-MAR-1999  MGD  v1.1.0, minor mods for comparison with OSU
08-JUN-1997  MGD  v1.0.0, initial development
*/
/*****************************************************************************/

#define SOFTWAREVN "1.4.2"
#define SOFTWARENM "CGIPLUSTEST"
#ifdef __ALPHA
#  define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN
#endif
#ifdef __ia64
#  define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN
#endif
#ifdef __VAX
#  define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN
#endif

/* standard C headers */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

/* VMS headers */
#include <descrip.h>
#include <ssdef.h>

int  AfterCount,
     ApacheEnvironment,
     BeforeCount,
     Debug,
     IsCgiPlus,
     OsuEnvironment,
     RequestedCount;

char  *CgiPlusEofPtr,
      *CgiPlusEotPtr,
      *CgiPlusEscPtr;

char  Utility [] = "CGIPLUSTEST",
      Line80Chars [] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789\
!@#$%^&*()_+-=<>,\n";

/* function prototypes */
int ApacheFixBgHandler ();
void ApacheFixBg ();
void AtOsuExit ();
char* CgiVar (char*);

/*****************************************************************************/
/*
*/

main ()

{
   char  *cptr;
   char  ContentLength [32];

   /*********/
   /* begin */
   /*********/

   if (getenv ("WWWEXEC_RUNDOWN_STRING") != NULL)
   {
      OsuEnvironment = 1;
      atexit (&AtOsuExit);
   }
   else
   {
      if (getenv ("APACHE$SHARED_SOCKET") != NULL)
      ApacheEnvironment = 1;
   }
   
   Debug = getenv ("CGIPLUSTEST$DBUG") != NULL;

   if (!Debug)
   {
      /* OSU spits with output > 4096 */
      if (OsuEnvironment)
      {
         stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin", "mrs=4096");
         fprintf (stdout, "<DNETCGI>");
         fflush (stdout);
      }
      else
      if (ApacheEnvironment)
      {
         ApacheFixBg ();
         stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin", "mrs=4096");
      }
      else
         stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin");
      if (stdout == NULL) exit (vaxc$errno);
   }

   IsCgiPlus = ((CgiPlusEofPtr = getenv("CGIPLUSEOF")) != NULL);
   CgiPlusEotPtr = getenv("CGIPLUSEOT");
   CgiPlusEscPtr = getenv("CGIPLUSESC");

   /* do once for standard CGI, multiple times for CGIplus */

   for (;;)
   {
      RequestedCount = -1;

      if (IsCgiPlus)
      {
         /* synchronize and read the CGIplus variable stream */
         CgiVar ("");
      }
      else
      if (Debug)
      {
         /* won't work for OSU! */
         fprintf (stdout, "Content-Type: text/plain\n\n");
         system ("show sym *");
      }

      if ((cptr = CgiVar ("WWW_QUERY_STRING")) == NULL)
         if ((cptr = CgiVar ("QUERY_STRING")) == NULL)
            cptr = "";

      AfterCount = BeforeCount = RequestedCount = 0;
      if (isdigit(*cptr))
         RequestedCount = atoi(cptr);
      else
      if (!strncmp (cptr, "after=", 6))
      {
         RequestedCount = 10;
         AfterCount = atoi(cptr+6);
      }
      else
      if (!strncmp (cptr, "before=", 7))
      {
         RequestedCount = 10;
         BeforeCount = atoi(cptr+7);
      }
      else
      if (!strncmp (cptr, "non-cgi", 6))
         fprintf (stdout, "Output to make it a non-CGI-compliant response\n");
         
      if (BeforeCount) sleep (BeforeCount);

      if (RequestedCount > 0)
         sprintf (ContentLength, "Content-Length: %d\r\n",
                  RequestedCount * (sizeof(Line80Chars)-1));
      else
         ContentLength[0] = '\0';

      fprintf (stdout,
"Content-Type: text/plain\r\n\
%s\
Script-Control: X-stream-mode\r\n\
Expires: Fri, 13 Jan 1978 14:00:00 GMT\r\n\
\r\n",
         ContentLength);

      if (*cptr == '*')
      {
         /* return all CGIplus variables in successive calls */
         while ((cptr = CgiVar ("*")) != NULL)
            fprintf (stdout, "|%s|\n", cptr);
      }
      else
      if (RequestedCount < 0)
      {
         fprintf (stdout,
"Usage: Supply a URL with a query string containing \
an integer representing the number of 80 character lines \
to be returned during the test.\n");
      }
      else
      {
         while (RequestedCount--)
            fputs (Line80Chars, stdout);
      }

      if (AfterCount) sleep (AfterCount);

      if (!IsCgiPlus) break;

      /* the CGIplus EOF must be an independant I/O record */
      fflush (stdout);
      fputs (CgiPlusEofPtr, stdout);
      fflush (stdout);
   } 

   exit (1);
}

/*****************************************************************************/
/*
Script executing in OSU environment exits.
*/

void AtOsuExit ()

{
   if (Debug)
      fprintf (stdout, "</DNETTEXT>\n");
   else
   {
      fflush (stdout);
      fprintf (stdout, "</DNETCGI>");
      fflush (stdout);
   }
}

/*****************************************************************************/
/*
According to "Compaq Secure Web Server Version 1.0-1 for OpenVMS Alpha (based
on Apache) Version 1.0-1 Release Notes" this is required to support the
transfer of any binary content in excess of 32 kbytes.  This is based on a code
snippet in these release notes, but makes the image activation and symbol
resolution dynamic to allow the object module to be linked on systems without
VMS Apache and the APACHE$FIXBG.EXE shareable image available.
*/

void ApacheFixBg ()

{
   $DESCRIPTOR (StdOutputDsc, "SYS$OUTPUT");
   $DESCRIPTOR (ApacheFixBgImageDsc, "APACHE$FIXBG");
   $DESCRIPTOR (ApacheFixBgDsc, "APACHE$FIXBG");

   int  status;
   unsigned short  SysOutputChannel;
   int (*ApacheFixBg)(unsigned short, int);

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ApacheFixBg()\n");

   lib$establish (ApacheFixBgHandler);
   status = lib$find_image_symbol (&ApacheFixBgImageDsc, &ApacheFixBgDsc,
                                   &ApacheFixBg, 0);
   if (Debug)
      fprintf (stdout, "lib$find_image_symbol() %%X%08.08X\n", status);
   lib$revert ();
   if (!(status & 1)) exit (status);

   status = sys$assign (&StdOutputDsc, &SysOutputChannel, 0, 0);
   if (status & 1) status = ApacheFixBg (SysOutputChannel, 1);
   if (!(status & 1)) exit (status);
   sys$dassgn (SysOutputChannel);
}

/*****************************************************************************/
/*
Just continue, to report an error if the image couldn't be activated or the
required symbol not found.
*/

int ApacheFixBgHandler ()

{
   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ApacheFixBgHandler()\n");

   return (SS$_CONTINUE);
}

/*****************************************************************************/
/*
Just include the code for the generic CGI/plus variable function.
*/

#define DEBUGIT (Debug)
#include "cgiplus_cgivar.c"

/*****************************************************************************/

