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

Script for testing and benchmarking the relative efficiencies of CGI, CGIplus
and Apache loadable modules.

WASD CGI         http://the.host.name/cgi-bin/cgiplustest?<integer>
WASD CGIplus     http://the.host.name/cgiplus-bin/cgiplustest?<integer> 
Apache module    http://the.host.name/cgiplustest?<integer>

The Apache module also provides functionality akin to WASD ResponseStream().
For loadable module configuration see below.


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!)
---------------
20-APR-2020  MGD  v1.6.0, Apache loadable module for performance comparison
                            also equivalent functionality WASD ResponseStream()
19-OCT-2009  MGD  v1.5.0, use CGILIB to make the OSU test-bench more equitable
                          and "Pragma: no-cache" - gotcha!
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.6.0"
#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
#ifdef __x86_64
#  define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN
#endif

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

#include <cgilib.h>

int  AfterCount,
     BeforeCount,
     Debug,
     IsCgiPlus,
     RequestedCount;

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

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

#ifndef APACHE_MOD

main (int argc, char* argv[])

{
   char  *cptr;

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

   Debug = getenv ("CGIPLUSTEST$DBUG") != NULL;

   CgiLibEnvironmentInit (argc, argv, TRUE);
   if (Debug)
      CgiLibEnvironmentRecordOut ();
   else
   if (!CgiLibEnvironmentIsOsu())
      if (getenv("SYS$NET"))
         stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin", "mrs=4096");

   IsCgiPlus = CgiLibEnvironmentIsCgiPlus ();

   for (;;)
   {
       /* once for standard CGI, multiple times for CGIplus */
      RequestedCount = -1;

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

      cptr = CgiLibVar ("WWW_QUERY_STRING");

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

      fprintf (stdout,
"Status: 200\r\n\
Content-Type: text/plain\r\n\
Content-Length: %d\r\n\
Script-Control: X-stream-mode\r\n\
Pragma: no-cache\r\n\
Expires: Fri, 13 Jan 1978 14:00:00 GMT\r\n\
\r\n",
               RequestedCount * (sizeof(Line80Chars)-1));
      fflush (stdout);

      if (*cptr == '*')
      {
         /* return all CGIplus variables in successive calls */
         while ((cptr = CgiLibVar ("*")) != NULL)
            fprintf (stdout, "|%s|\n", cptr);
      }
      else
      if (RequestedCount < 0)
      {
         fprintf (stdout,
"%s\n\
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", SOFTWAREID);
      }
      else
      {
         while (RequestedCount--)
            fputs (Line80Chars, stdout);
      }

      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 (AfterCount) sleep (AfterCount);

      if (!IsCgiPlus) break;

      CgiLibCgiPlusEOF ();
   } 

   exit (1);
}

#endif

#ifdef APACHE_MOD

/*****************************************************************************/
/* 
           MOD_CGIPLUSTEST.C - VMS Apache cgiplustest module

$ copy [.OBJ_<arch>]MOD_CGIPLUSTEST.EXE APACHE$COMMON:[MODULES]

Activate it in Apache's httpd.conf file:

   # httpd.conf
   LoadModule cgiplustest_module modules/mod_cgiplustest.so
   <Location /cgiplustest>
      SetHandler cgiplustest
   </Location>

Restart Apache using:

   $ apachectl restart

Monkey see - monkey do.  Speaks volumes of my Apache sophistication.

Developed for VSI AXPVMS CSWS V2.4-38C.
*/
/*****************************************************************************/

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

static void cgiplustest_lines (request_rec *req, int number)
{
   ap_set_content_type (req, "text/plain");
   while (number--)
   {
      ap_rputs (Line80Chars, req);
      if (req->connection->aborted) break;
   }
}

static void cgiplustest_stream (request_rec *req, char flag, int number)
{
   int  cnt, octet = 0;
   unsigned __int64  ranum;
   char  *cptr, *sptr, *zptr;
   char  buf [4096+1];

   if (flag == 'b' || flag == 'o')
      ap_set_content_type (req, "application/binary");
   else
      ap_set_content_type (req, "text/plain");

   if (flag == 'b')
   {
      while (number > 0)
      {
         zptr = (sptr = buf) + sizeof(buf)-1;
         SYS$GETTIM (&ranum);
         while (sptr < zptr)
         {
            /* cheap (no subroutine call) MTH$RANDOM() */
            ranum = ranum * 69069 + 1;
            cptr = (char*)&ranum;
            for (cnt = sizeof(ranum); cnt && sptr < zptr && number > 0; cnt--)
               *sptr++ = *cptr++;
         }
         ap_rwrite (buf, sptr - buf, req);
         if (req->connection->aborted) break;
      }
   }
   else
   if (flag == 'o')
   {
      while (number > 0)
      {
         zptr = (sptr = buf) + sizeof(buf)-1;
         while (sptr < zptr && number-- > 0) *sptr++ = (uchar)octet++;
         ap_rwrite (buf, sptr - buf, req);
         if (req->connection->aborted) break;
      }
   }
   else
   if (flag == 't')
   {
      while (number > 0)
      {
         zptr = (sptr = buf) + sizeof(buf)-1;
         while (sptr < zptr && number > 0)
         {
            for (cnt = ('z'-80+1);
                 cnt <= 'z' && sptr < zptr && number-- > 0;
                 cnt++) *sptr++ = (char)cnt;
            if (cnt == 'z'+1) *sptr++ = '\n';
         }
         /* only complete lines */
         while (sptr > buf && *(sptr-1) != '\n') { sptr--; number++; }
         *sptr = '\0';
         ap_rputs (buf, req);
         if (req->connection->aborted) break;
      }
   }
}

static int cgiplustest_handler (request_rec *req)
{
   int  number = -1,
        query = 0;
   char  stream = 0;
   char  *cptr;

/** should work but doesn't **/
//   if (!req->handler || strcmp (req->handler, "cgiplustest")) {
//       return DECLINED;
//   }
/** so use this alternative **/
   if (!req->the_request || !strstr (req->the_request, "cgiplustest"))
      return DECLINED;

   if (req->the_request)
   {
      if (!strstr (req->the_request, "cgiplustest")) return DECLINED;

      if (cptr = strstr (req->the_request, "/stream/"))
      {
         /* using the equivalent syntax to WASD ResponseStream() */
         cptr += 8;
         if (isalpha (*cptr))
            stream = *cptr;
         else
         {
            stream = 't';
            if (isdigit (*cptr)) number = atoi(cptr) * 1024;
         }
         while (*cptr && *cptr != ':') cptr++;
         if (*cptr)
            number = atoi(cptr+1) * 1024;
         else
         if (number < 0)
            number = INT_MAX;
         if (!(stream == 'b' || stream != 'o' || stream != 't')) number = -1;
         if (strchr (req->the_request, '?')) query = 1;
      }
      else
      if (cptr = strchr (req->the_request, '?'))
         number = atoi(cptr+1);
   }

   if (query)
   {
      ap_set_content_type (req, "text/plain");
      ap_rprintf (req, "%s stream:%c number:%d\n", SOFTWAREID, stream, number);
      return OK;
   }

   if (number < 0)
   {
      ap_set_content_type (req, "text/plain");
      ap_rprintf (req,
"%s\n\
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", SOFTWAREID);
   }
   else
   if (stream)
      cgiplustest_stream (req, stream, number);
   else
      cgiplustest_lines (req, number);

   return OK;
}

static void cgiplustest_register_hooks (apr_pool_t *pool)
{
   fprintf (stdout, "\n ** cgiplustest_register_hooks  **\n\n");
   ap_hook_handler (cgiplustest_handler, NULL, NULL, APR_HOOK_LAST);
}

/* dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA cgiplustest_module =
{
   STANDARD20_MODULE_STUFF, 
   NULL, /* create per-dir    config structures */
   NULL, /* merge  per-dir    config structures */
   NULL, /* create per-server config structures */
   NULL, /* merge  per-server config structures */
   NULL, /* table of config file commands       */
   cgiplustest_register_hooks  /* register hooks */
};

#endif

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