/*
 * WebStone load module for Apache.
 *
 * By default this module copies the behavior of the CGI load module
 * distributed with version 1.1 of the WebStone benchmark.  Optionally
 * it can be configured to use optimizations similar to those
 * described by Netscape in their benchmark report "Performance
 * Benchmark Test of the NetScape FastTrack Beta 3 Web Server"
 * (http://www.mindcraft.com/services/web/ns01-fasttrack-nt.html).
 *
 * To add this module to your server add a line like this:
 *
 *    Module webstone_module    mod_webstone.o
 *
 * to src/Configuration and rebuild apache.  Then add a line
 * like this to srm.conf:
 *
 *    AddType httpd/webstone .ws
 *
 * Having done this configure a WebStone filelist to make requests like
 * this:
 *
 *    /file.ws?size=2048
 *
 * And you are off and running.
 *
 * The size of the buffer used and the degree of optimization employed
 * can be set by adding the following lines to httpd.conf:
 *
 *    # Configuration for mod_webstone
 *    WebstoneMaxBuffer 204800
 *    WebstoneOptimization 0
 *
 * See the code below for the various optimizations employed.  Questions,
 * bugs, whatever to: dlu@bsdi.com
 */

#include "httpd.h"
#include "http_config.h"

module webstone_module;

#define DEFAULT_SIZE 10240

#define DEFAULT_MAX_BUFFER 204800

/*
 * The versions below correspond to the different levels of optimization
 * described by Netscape in their benchmark report.  Also see the comments
 * below.
 */
#define STOCK     0
#define VERSION_A 1
#define VERSION_B 2
#define VERSION_C 3

/*
 * Module state.
 */
typedef struct {
  char *buffer;
  int maxbuffer;
  int opt;
} webstone_state;

/*
 * Initialize the module.
 */
void *make_webstone_state (pool *p, server_rec *s)
{
  webstone_state *ws =
	(webstone_state *)palloc (p, sizeof (webstone_state));

  ws->maxbuffer = DEFAULT_MAX_BUFFER;
  ws->opt = STOCK;

  return (void *) ws;
}

/*
 * Implement the commands.
 */
char *set_ws_maxbuffer (cmd_parms *parms, void *dummy, char *arg)
{
  webstone_state *ws = get_module_config(parms->server->module_config,
										 &webstone_module);
  ws->maxbuffer = atoi(arg);
  return NULL;
}

char *set_ws_opt(cmd_parms *parms, void *dummy, char *arg)
{
  webstone_state *ws = get_module_config (parms->server->module_config,
					       &webstone_module);
  ws->opt = atoi(arg);
  return NULL;
}

/*
 * Configuration commands specific to this module.
 */
command_rec webstone_cmds[] = {
  { "WebstoneMaxBuffer", set_ws_maxbuffer, NULL, RSRC_CONF, TAKE1,
	  "set size of buffer used by optimization levels 1 - 3." },
  { "WebstoneOptimization", set_ws_opt, NULL, RSRC_CONF, TAKE1,
	  "level of optimization to use (0 - 3)" },
  { NULL }
};

/*
 * Initialize the module.
 */
void init_webstone(server_rec *s, pool *p)
{
  webstone_state *ws = get_module_config (s->module_config,
					       &webstone_module);
  switch (ws->opt) {
  case STOCK:
	break;
  case VERSION_A:
  case VERSION_B:
  case VERSION_C:
	if ((ws->buffer = (char *) palloc(p, ws->maxbuffer)) == NULL) {
	  fprintf(stderr, "Failed to allocate WebStone buffer\n");
	}
	break;
  }
}

/*
 * Declare handlers implemented by this module.
 */
int webstone_handler (request_rec *r)
{
  char *buffer;
  int filesize = DEFAULT_SIZE;
  int index;
  webstone_state *ws = get_module_config (r->server->module_config,
					       &webstone_module);

  /* Get the query string, if any; check to see if an alternate
   * file size was specified.
   */

  if (r->args) {
	if (strncmp(r->args, "size=", 5) == 0)
	  filesize = atoi(&r->args[5]);
  }

  r->content_type = "text/plain";
  r->no_cache = 1;

  switch (ws->opt) {
  case STOCK:
	/* Allocate the output buffer */
	if ((buffer = (char *) palloc(r->pool, filesize)) == NULL) {
	  return SERVER_ERROR;
	}

	/* Generate the output */
	for (index = 0; index < filesize; index++)
	  /* generate random characters from A-Z */
	  buffer[index] = rand() % 26 + 65;
	break;

  case VERSION_A:
	/*
	 * Use automatic array rather than palloc().
	 */
	buffer = ws->buffer;

	/* Generate the output */
	for (index = 0; index < filesize; index++)
	  /* generate random characters from A-Z */
	  buffer[index] = rand() % 26 + 65;
	break;

  case VERSION_B:
	/*
	 * Don't use rand() to generate data.
	 */
	buffer = ws->buffer;

	/* Generate the output */
	for (index = 0; index < filesize; index++)
	  /* generate random characters from A-Z */
	  buffer[index] = index % 26 + 65;
	break;

  case VERSION_C:
	/*
	 * Don't generate the data :-)
	 */
	buffer = ws->buffer;
	break;
	
  default:
	return SERVER_ERROR;
  }

  /* Send the output */
  soft_timeout("send", r);
  send_http_header(r);
  if (bwrite(r->connection->client, buffer, filesize) != filesize) {
	return SERVER_ERROR;
  }
	
  return OK;
}

handler_rec webstone_handlers[] = {
  { "httpd/webstone", webstone_handler },
  { "webstone", webstone_handler },
  { NULL }
};

module webstone_module = {
   STANDARD_MODULE_STUFF,
   init_webstone,	   /* initializer */
   NULL,			   /* create per-dir config */
   NULL,			   /* merge per-dir config */
   make_webstone_state,	           /* server config */
   NULL,			   /* merge server config */
   webstone_cmds,	   /* command table */
   webstone_handlers,  /* handlers */
   NULL,			   /* filename translation */
   NULL,			   /* check_user_id */
   NULL,			   /* check auth */
   NULL,			   /* check access */
   NULL,			   /* type_checker */
   NULL,			   /* fixups */
   NULL                /* logger */
};
