/* ==================================================================== * Copyright (c) 2000, MeepZor Consulting. * All rights reserved. * * The use and distribution of this code or document is * governed by version 1.0.1 of the MeepZor Consulting Public * Licence (MCPL), which may be found on the Internet at * . * * * $Id: mod_babble.c,v 1.5 2001/11/23 14:58:58 coar Exp $ * * Abstract: *+ * A test module for the Apache Web server. It simply fills a buffer * with the appropriate number of bytes in a given pattern string, and * then emits it as the response body. The content length field is * included so the response is cacheable. *- */ /* * Apache babble-test module. This simply sends a single large chunk * of content; the size is determined by directives and URL query info. * * The query-string keywords may be combined and separated by any * non-alphanumeric character legal in URIs (such as '&' or ';'). * * ?size=1000 * ?pattern=string * ?size=1000;pattern=string * * The pattern URL keyword is ignored if the URL is within the scope of a * 'BabblePatternLock On' directive. This is to prevent malicious use, * such as a link on a page with a pattern value that might cause * browsers problems when displayed. This is one of the reasons for the * BabbleMaxSize directive; the other is to limit server resource * consumption. */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_main.h" #include "http_protocol.h" #include "http_request.h" #include "util_script.h" #include "util_date.h" #include /*--------------------------------------------------------------------------*/ /* */ /* Private data declarations. */ /* */ /*--------------------------------------------------------------------------*/ /* * Per-directory configuration record defining defaults and limits on * the babble text. */ typedef struct mbbl_conf_t { int maxsize; /* Maximum size of reponse body */ #define F_DEFAULT_MAXSIZE 8192 int defaultsize; /* Default size in this scope */ #define F_DEFAULT_DEFAULTSIZE 8192 const char *pattern; /* Pattern to replicate into response */ int plen; /* Length of the pattern */ int patternlocked; /* Ignore ?pattern= keywords */ #define F_UNSET 3 /* Marker for inheritance purposes */ int canstream; /* Permit ?size=-1 for endless babbling */ } mbbl_conf_t; #define BUFSIZE 8192 /* * Declare ourselves so the configuration routines can find and know us. * We'll fill it in at the end of the module. */ module babble_module; /* * Command handler for BabbleMaxSize directive. Record the maximum * number of babble bytes per request in this scope. */ static const char *cmd_maxsize(cmd_parms *cmd, void *mconfig, char *word1) { mbbl_conf_t *dconf; int size; dconf = (mbbl_conf_t *) mconfig; size = atoi(word1); /* if (size <= 0) { return "illegal value for BabbleMaxSize directive"; } */ dconf->maxsize = size; return NULL; } /* * Command handler for BabbleDefaultSize directive, which sets the * default number of babble bytes per request (if not overridden * by the URL keywords). Maximised against the BabbleMaxSize * setting at runtime. We can't do that here because we might not * have encountered a BabbleMaxSize directive yet. */ static const char *cmd_defaultsize(cmd_parms *cmd, void *mconfig, char *word1) { mbbl_conf_t *dconf; int size; dconf = (mbbl_conf_t *) mconfig; size = atoi(word1); if (size <= 0) { return "illegal value for BabbleDefaultSize directive"; } dconf->defaultsize = size; return NULL; } /* * Command handler for the BabbleDefaultPattern directive. This * pattern overrides the default default, which is "deadbeef". * It in turn can be overridden by the 'pattern' URL keyword * under the right conditions. */ static const char *cmd_defaultpattern(cmd_parms *cmd, void *mconfig, char *word1) { mbbl_conf_t *dconf; int size; dconf = (mbbl_conf_t *) mconfig; size = strlen(word1); if (size <= 0) { return "illegal value for BabbleDefaultPattern directive"; } dconf->plen = size; dconf->pattern = (const char *) ap_pstrdup(cmd->pool, word1); return NULL; } /* * Command handler for the BabblePatternLock Boolean directive, which * controls whether the 'pattern' URL keyword is interpreted or ignored. */ static const char *cmd_patternlock(cmd_parms *cmd, void *mconfig, int locked) { mbbl_conf_t *dconf; dconf = (mbbl_conf_t *) mconfig; dconf->patternlocked = locked ? 1 : 0; return NULL; } /* * Command handler for the BabbleStreaming Boolean directive, which * controls whether infinite babbling (triggered by a URL 'size=-1' * keyword) is enabled. */ static const char *cmd_streaming(cmd_parms *cmd, void *mconfig, int canstream) { mbbl_conf_t *dconf; dconf = (mbbl_conf_t *) mconfig; dconf->canstream = canstream ? 1 : 0; return NULL; } /* * This function gets called to create up a per-directory configuration * record. This will be called for the "default" server environment, and for * each directory for which the parser finds any of our directives applicable. * If a directory doesn't have any of our directives involved (i.e., they * aren't in the .htaccess file, or a , , or related * block), this routine will *not* be called - the configuration for the * closest ancestor is used. * * The return value is a pointer to the created module-specific * structure. */ static void *mbbl_create_dconf(pool *p, char *dirspec) { mbbl_conf_t *dconf; /* * Allocate the space for our record from the pool supplied. */ dconf = (mbbl_conf_t *) ap_pcalloc(p, sizeof(mbbl_conf_t)); /* * Now fill in the defaults. If there are any 'parent' configuration * records, they'll get merged as part of a separate callback. */ dconf->maxsize = F_DEFAULT_MAXSIZE; dconf->defaultsize = F_DEFAULT_DEFAULTSIZE; dconf->pattern = NULL; dconf->plen = 0; dconf->patternlocked = F_UNSET; dconf->canstream = F_UNSET; return (void *) dconf; } /* * This function gets called to merge two per-directory configuration * records. This is typically done to cope with things like .htaccess files * or directives for directories that are beneath one for which a * configuration record was already created. The routine has the * responsibility of creating a new record and merging the contents of the * other two into it appropriately. If the module doesn't declare a merge * routine, the record for the closest ancestor location (that has one) is * used exclusively. * * The routine MUST NOT modify any of its arguments! * * The return value is a pointer to the created module-specific structure * containing the merged values. */ static void *mbbl_merge_dconf(pool *p, void *parent_conf, void *newloc_conf) { mbbl_conf_t *merged_config; mbbl_conf_t *pconf = (mbbl_conf_t *) parent_conf; mbbl_conf_t *nconf = (mbbl_conf_t *) newloc_conf; merged_config = (mbbl_conf_t *) ap_pcalloc(p, sizeof(mbbl_conf_t)); /* * Inherit the settings from the closest config, or from the parent * if the closest one doesn't have an explicit setting. Zero is * the magic value indicating and unset field. */ if (nconf->maxsize != 0) { merged_config->maxsize = nconf->maxsize; } else if ((pconf != NULL) && (pconf->maxsize != 0)) { merged_config->maxsize = pconf->maxsize; } else { merged_config->maxsize = F_DEFAULT_MAXSIZE; } /* * Repeat for defaultsize. */ if (nconf->defaultsize != 0) { merged_config->defaultsize = nconf->defaultsize; } else if ((pconf != NULL) && (pconf->defaultsize != 0)) { merged_config->defaultsize = pconf->defaultsize; } else { merged_config->defaultsize = merged_config->maxsize; } /* * And again for the pattern, which uses a NULL pointer as a * flag for unsetness. */ if (nconf->pattern != NULL) { merged_config->pattern = nconf->pattern; } else if (pconf->pattern != NULL) { merged_config->pattern = pconf->pattern; } else { merged_config->pattern = "deadbeef"; } merged_config->plen = strlen(merged_config->pattern); /* * Finally the patternlocked field. Since this is an int used as * a Boolean, we need to use a magic value that's different * from the ones used by the command handler. */ if (nconf->patternlocked != F_UNSET) { merged_config->patternlocked = nconf->patternlocked; } else if (pconf->patternlocked != F_UNSET) { merged_config->patternlocked = pconf->patternlocked; } else { merged_config->patternlocked = 0; } /* * Now the can-babble-forever flag. */ if (nconf->canstream != F_UNSET) { merged_config->canstream = nconf->canstream; } else if (pconf->canstream != F_UNSET) { merged_config->canstream = pconf->canstream; } else { merged_config->canstream = 0; } return (void *) merged_config; } /*--------------------------------------------------------------------------*/ /* Now we declare our content handlers, which are invoked when the server */ /* encounters a document which our module is supposed to have a chance to */ /* see. */ /*--------------------------------------------------------------------------*/ /* * Figure out what the parameters should be for the response to this request. * Get the defaults from the config record, and adjust them according * to any keywords in the query_string. */ static mbbl_conf_t *getparams(request_rec *r) { mbbl_conf_t *dconf; mbbl_conf_t *rconf; int size; char *ptr; char delim[2]; ptr = NULL; delim[0] = '\0'; delim[1] = '\0'; dconf = (mbbl_conf_t *) ap_get_module_config(r->per_dir_config, &babble_module); /* * If there aren't any keywords in the query string, or the only * ones are to be ignored, return the preset config record. */ if ((r->args == NULL) || (strlen(r->args) == 0) || (((ptr = strstr(r->args, "size=")) == NULL) && (dconf->patternlocked || ((! dconf->patternlocked) && (strstr(r->args, "pattern=") == NULL))))) { return dconf; } /* * We've got something, so copy config settings as defaults. */ rconf = ap_pcalloc(r->pool, sizeof(mbbl_conf_t)); rconf->maxsize = dconf->maxsize; rconf->defaultsize = dconf->defaultsize; rconf->pattern = dconf->pattern; rconf->plen = dconf->plen; /* * If we have a size=n keyword, deal with it. ptr was set in the * previous condition test. If it isn't the first part of the * query string, take the character immediately preceding as * the delimiter if it isn't alphanumeric. */ if ((ptr != NULL) && ((ptr == r->args) || (! ap_isalnum(ptr[-1])))) { size = atoi(&ptr[5]); if ((size < 0) && (! dconf->canstream)) { size = dconf->maxsize; } if (size <= dconf->maxsize) { rconf->defaultsize = size; } if (ptr > r->args) { delim[0] = ptr[-1]; } } /* * Repeat the process for the pattern keyword if it's not being * ignored. Once again we try to determine delimiter from the * byte immediately preceding the keyword IFF we haven't already * gotten one. */ if ((! dconf->patternlocked) && ((ptr = strstr(r->args, "pattern=")) != NULL) && ((ptr == r->args) || (! ap_isalnum(ptr[-1])))) { if ((delim[0] == '\0') && (ptr > r->args)) { delim[0] = ptr[-1]; } rconf->pattern = ap_pstrdup(r->pool, &ptr[8]); /* * Here's why we care about the delimiter: so we can correctly * truncate the pattern for query values like "?pattern=x&size=2". */ if (delim[0] != '\0') { ptr = strstr(rconf->pattern, delim); if (ptr != NULL) { *ptr = '\0'; } } rconf->plen = strlen(rconf->pattern); } return rconf; } /* * The guts of the module: the content handler. Create a buffer of the * appropriate size, fill it with the specified pattern, and dump it. * * The return value instructs the caller concerning what happened and what to * do next: * OK ("we did our thing") * DECLINED ("this isn't something with which we want to get involved") * HTTP_mumble ("an error status should be reported") */ static int mbbl_handler(request_rec *r) { mbbl_conf_t *dconf; char *body; char *seg; int seglen; int remaining; int bufsize; int rstat; dconf = getparams(r); if (dconf->defaultsize > 0) { ap_set_content_length(r, dconf->defaultsize); } r->content_type = "text/plain"; ap_hard_timeout("send babble output", r); ap_send_http_header(r); /* * If we're only supposed to send header information (HEAD request), we're * already there. */ if (r->header_only) { ap_kill_timeout(r); return OK; } /* * Allocate a buffer (maximum BUFSIZE bytes) for the content; add a * byte for the terminating NUL character. */ bufsize = ((BUFSIZE / dconf->plen) * dconf->plen) + 1; if (bufsize < dconf->plen) { bufsize += dconf->plen; } body = (char *) ap_palloc(r->pool, bufsize); /* * Replicate the string into the buffer as many times as it will * fit, and then hang the NUL on the end. */ remaining = bufsize - 1; seg = body; while (remaining > 0) { seglen = (remaining < (dconf->plen)) ? remaining : (dconf->plen); ap_cpystrn(seg, dconf->pattern, seglen + 1); remaining -= seglen; seg = &seg[seglen]; } /* * body should now point to a NUL-terminated string with an integral * number of copies of the pattern in it. */ /* * Now send our actual output, resending the buffer as many times as * necessary. */ remaining = dconf->defaultsize; while ((unsigned int) remaining > (bufsize - 1)) { rstat = ap_rputs(body, r); if (rstat == EOF) { break; } remaining -= (bufsize - 1); } if (rstat != EOF) { /* * Only a partial (or possibly one complete) buffer left to write. * Make sure we're NUL-terminated before writing it. */ body[remaining] = '\0'; rstat = ap_rputs(body, r); } /* * We're all done, so cancel the timeout we set. Since this is probably * the end of the request we *could* assume this would be done during * post-processing - but it's possible that another handler might be * called and inherit our outstanding timer. Not good; to each its own. */ ap_kill_timeout(r); return OK; } /*--------------------------------------------------------------------------*/ /* */ /* All of the routines have been declared now */ /* */ /*--------------------------------------------------------------------------*/ /* * List of directives specific to our module. */ static const command_rec mbbl_cmds[] = { { "BabbleMaxSize", /* directive name */ cmd_maxsize, /* config action routine */ NULL, /* argument to include in call */ ACCESS_CONF, /* where available */ TAKE1, /* arguments */ "maximum number of bytes in response" /* directive description */ }, { "BabbleDefaultSize", /* directive name */ cmd_defaultsize, /* config action routine */ NULL, /* argument to include in call */ OR_OPTIONS, /* where available */ TAKE1, /* arguments */ "default number of bytes in response" /* directive description */ }, { "BabbleDefaultPattern", /* directive name */ cmd_defaultpattern, /* config action routine */ NULL, /* argument to include in call */ OR_OPTIONS, /* where available */ TAKE1, /* arguments */ "pattern string to be replicated to comprise" /* directive description */ }, { "BabblePatternLock", /* directive name */ cmd_patternlock, /* config action routine */ NULL, /* argument to include in call */ OR_OPTIONS, /* where available */ FLAG, /* arguments */ "flag indicating whether ?pattern= should be ignored" /* directive description */ }, { "BabbleStreaming", /* directive name */ cmd_streaming, /* config action routine */ NULL, /* argument to include in call */ ACCESS_CONF, /* where available */ FLAG, /* arguments */ "flag indicating whether ?size=-1 infinite babbling is allowed" /* directive description */ }, { NULL } }; /*--------------------------------------------------------------------------*/ /* */ /* Now the list of content handlers available from this module. */ /* */ /*--------------------------------------------------------------------------*/ /* * List of content handlers our module supplies. Each handler is defined by * two parts: a name by which it can be referenced (such as by * {Add,Set}Handler), and the actual routine name. The list is terminated by * a NULL block, since it can be of variable length. * * Note that content-handlers are invoked on a most-specific to least-specific * basis; that is, a handler that is declared for "text/plain" will be * invoked before one that was declared for "text / *". Note also that * if a content-handler returns anything except DECLINED, no other * content-handlers will be called. */ static const handler_rec mbbl_handlers[] = { { "babbler", mbbl_handler }, { NULL } }; /*--------------------------------------------------------------------------*/ /* */ /* Finally, the list of callback routines and data structures that */ /* provide the hooks into our module from the other parts of the server. */ /* */ /*--------------------------------------------------------------------------*/ /* * Module definition for configuration. If a particular callback is not * needed, its slot contains NULL. * */ module babble_module = { STANDARD_MODULE_STUFF, NULL, /* module initializer */ mbbl_create_dconf, /* per-directory config creator */ mbbl_merge_dconf, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ mbbl_cmds, /* command table */ mbbl_handlers, /* [7] list of handlers */ NULL, /* [2] filename-to-URI translation */ NULL, /* [5] check/validate user_id */ NULL, /* [6] check user_id is valid *here* */ NULL, /* [4] check access by host address */ NULL, /* [7] MIME type checker/setter */ NULL, /* [8] fixups */ NULL, /* [10] logger */ #if MODULE_MAGIC_NUMBER >= 19970103 NULL, /* [3] header parser */ #endif #if MODULE_MAGIC_NUMBER >= 19970719 NULL, /* process initializer */ #endif #if MODULE_MAGIC_NUMBER >= 19970728 NULL, /* process exit/cleanup */ #endif #if MODULE_MAGIC_NUMBER >= 19970902 NULL /* [1] post read_request handling */ #endif }; /* * Emacs settings * * Local Variables: * mode: C * c-file-style: "bsd" * End: */