You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by "Paul J. Reder" <re...@raleigh.ibm.com> on 2000/12/06 16:46:40 UTC
[Patch]: Implement hook/hash extensions for mod_include handlers
This patch converts mod_include to use a hash table registry of handlers for
the different tags (with the tag as the hashed item and the function to call
as the hashed value).
The entries in the hash table are added during the running of a hook. Any module
(i.e. mod_perl, etc) that wants to add an SSI directive tag can do so by registering
the hook and adding their hashed tag and function to the table when called.
Mod_include registers all of its own handlers via the hook instead of cheating,
so the nicely documented mod_include code can be used as sample code for any
other module authors.
--
Paul J. Reder
-----------------------------------------------------------
"The strength of the Constitution lies entirely in the determination of each
citizen to defend it. Only if every single citizen feels duty bound to do
his share in this defense are the constitutional rights secure."
-- Albert Einstein
Index: filters/mod_include.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/filters/mod_include.c,v
retrieving revision 1.81
diff -u -r1.81 mod_include.c
--- filters/mod_include.c 2000/11/28 23:07:01 1.81
+++ filters/mod_include.c 2000/12/06 15:08:45
@@ -93,6 +93,10 @@
#endif
#include "util_ebcdic.h"
+
+static apr_hash_t *hash_table;
+
+
/* ------------------------ Environment function -------------------------- */
/* XXX: could use ap_table_overlap here */
@@ -180,7 +184,7 @@
apr_size_t start_index;
/* We want to split the bucket at the '<'. */
- ctx->state = PARSE_TAG;
+ ctx->state = PARSE_DIRECTIVE;
ctx->tag_length = 0;
ctx->parse_pos = 0;
ctx->tag_start_bucket = dptr;
@@ -250,7 +254,7 @@
}
while (c - buf != len) {
if (*c == str[ctx->parse_pos]) {
- if (ctx->state == PARSE_TAG) {
+ if (ctx->state != PARSE_TAIL) {
ctx->state = PARSE_TAIL;
ctx->tail_start_bucket = dptr;
ctx->tail_start_index = c - buf;
@@ -258,18 +262,28 @@
ctx->parse_pos++;
}
else {
- if (ctx->state == PARSE_TAG) {
+ if (ctx->state == PARSE_DIRECTIVE) {
if (ctx->tag_length == 0) {
if (!apr_isspace(*c)) {
ctx->tag_start_bucket = dptr;
ctx->tag_start_index = c - buf;
ctx->tag_length = 1;
+ ctx->directive_length = 1;
}
}
else {
+ if (!apr_isspace(*c)) {
+ ctx->directive_length++;
+ }
+ else {
+ ctx->state = PARSE_TAG;
+ }
ctx->tag_length++;
}
}
+ else if (ctx->state == PARSE_TAG) {
+ ctx->tag_length++;
+ }
else {
if (str[ctx->parse_pos] == '\0') {
ap_bucket *tmp_buck = dptr;
@@ -294,16 +308,24 @@
ctx->tag_length += ctx->parse_pos;
if (*c == str[0]) {
- ctx->parse_pos = 1;
ctx->state = PARSE_TAIL;
ctx->tail_start_bucket = dptr;
ctx->tail_start_index = c - buf;
+ ctx->tag_length += ctx->parse_pos;
+ ctx->parse_pos = 1;
}
else {
- ctx->parse_pos = 0;
- ctx->state = PARSE_TAG;
+ if (ctx->tag_length > ctx->directive_length) {
+ ctx->state = PARSE_TAG;
+ }
+ else {
+ ctx->state = PARSE_DIRECTIVE;
+ ctx->directive_length += ctx->parse_pos;
+ }
ctx->tail_start_bucket = NULL;
ctx->tail_start_index = 0;
+ ctx->tag_length += ctx->parse_pos;
+ ctx->parse_pos = 0;
}
}
}
@@ -557,51 +579,7 @@
return;
}
-static char *get_directive(include_ctx_t *ctx, dir_token_id *fnd_token)
-{
- char *c = ctx->curr_tag_pos;
- char *dest;
- int len = 0;
- SKIP_TAG_WHITESPACE(c);
-
- dest = c;
- /* now get directive */
- while ((*c != '\0') && (!apr_isspace(*c))) {
- *c = apr_tolower(*c);
- c++;
- len++;
- }
-
- *c++ = '\0';
- ctx->curr_tag_pos = c;
-
- *fnd_token = TOK_UNKNOWN;
- switch (len) {
- case 2: if (!strcmp(dest, "if")) *fnd_token = TOK_IF;
- break;
- case 3: if (!strcmp(dest, "set")) *fnd_token = TOK_SET;
- break;
- case 4: if (!strcmp(dest, "else")) *fnd_token = TOK_ELSE;
- else if (!strcmp(dest, "elif")) *fnd_token = TOK_ELIF;
- else if (!strcmp(dest, "exec")) *fnd_token = TOK_EXEC;
- else if (!strcmp(dest, "echo")) *fnd_token = TOK_ECHO;
- break;
- case 5: if (!strcmp(dest, "endif")) *fnd_token = TOK_ENDIF;
- else if (!strcmp(dest, "fsize")) *fnd_token = TOK_FSIZE;
- break;
- case 6: if (!strcmp(dest, "config")) *fnd_token = TOK_CONFIG;
- break;
- case 7: if (!strcmp(dest, "include")) *fnd_token = TOK_INCLUDE;
- break;
- case 8: if (!strcmp(dest, "flastmod")) *fnd_token = TOK_FLASTMOD;
- else if (!strcmp(dest, "printenv")) *fnd_token = TOK_PRINTENV;
- break;
- }
-
- return (dest);
-}
-
/*
* Do variable substitution on strings
*/
@@ -2688,7 +2666,7 @@
}
/* Adjust the current bucket position based on what was found... */
- if ((tmp_dptr != NULL) && (ctx->state == PARSE_TAG)) {
+ if ((tmp_dptr != NULL) && (ctx->state == PARSE_DIRECTIVE)) {
if (ctx->tag_start_bucket != NULL) {
dptr = ctx->tag_start_bucket;
}
@@ -2702,7 +2680,9 @@
}
/* State to check for the ENDING_SEQUENCE. */
- if (((ctx->state == PARSE_TAG) || (ctx->state == PARSE_TAIL)) &&
+ if (((ctx->state == PARSE_DIRECTIVE) ||
+ (ctx->state == PARSE_TAG) ||
+ (ctx->state == PARSE_TAIL)) &&
(dptr != AP_BRIGADE_SENTINEL(*bb))) {
tmp_dptr = find_end_sequence(dptr, ctx, *bb);
@@ -2729,9 +2709,10 @@
/* State to processed the directive... */
if (ctx->state == PARSED) {
ap_bucket *content_head = NULL, *tmp_bkt;
+ apr_size_t tmp_i;
char tmp_buf[TMP_BUF_SIZE];
- char *directive_str = NULL;
- dir_token_id directive_token;
+ int (*handle_func)(include_ctx_t *, ap_bucket_brigade **, request_rec *,
+ ap_filter_t *, ap_bucket *, ap_bucket **);
/* By now the full tag (all buckets) should either be set aside into
* ssi_tag_brigade or contained within the current bb. All tag
@@ -2783,55 +2764,26 @@
* the contents of a single bucket!
*/
- /* pjr - This is about to change to be a generic function
- * call from a hash table lookup. All functions need
- * to have the same parms...
+ /* Retrieve the handler function to be called for this directive from the
+ * functions registered in the hash table.
+ * Need to lower case the directive for proper matching. Also need to have
+ * it NULL terminated (and include the NULL in the length) for proper
+ * hash matching.
*/
- directive_str = get_directive(ctx, &directive_token);
-
- switch (directive_token) {
- case TOK_IF:
- ret = handle_if(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_ELSE:
- ret = handle_else(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_ELIF:
- ret = handle_elif(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_ENDIF:
- ret = handle_endif(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_EXEC:
- ret = handle_exec(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_INCLUDE:
- ret = handle_include(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_SET:
- ret = handle_set(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_ECHO:
- ret = handle_echo(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_FSIZE:
- ret = handle_fsize(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_CONFIG:
- ret = handle_config(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_FLASTMOD:
- ret = handle_flastmod(ctx, bb, r, f, dptr, &content_head);
- break;
- case TOK_PRINTENV:
- ret = handle_printenv(ctx, bb, r, f, dptr, &content_head);
- break;
+ for (tmp_i = 0; tmp_i < ctx->directive_length; tmp_i++) {
+ ctx->combined_tag[tmp_i] = apr_tolower(ctx->combined_tag[tmp_i]);
+ }
+ ctx->combined_tag[ctx->directive_length] = '\0';
+ ctx->curr_tag_pos = &ctx->combined_tag[ctx->directive_length+1];
- case TOK_UNKNOWN:
- default:
+ handle_func = apr_hash_get(hash_table, ctx->combined_tag, ctx->directive_length+1);
+ if (handle_func != NULL) {
+ ret = (*handle_func)(ctx, bb, r, f, dptr, &content_head);
+ }
+ else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"unknown directive \"%s\" in parsed doc %s",
- directive_str, r->filename);
+ ctx->combined_tag, r->filename);
CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
}
@@ -2888,6 +2840,7 @@
ctx->tail_start_index = 0;
ctx->curr_tag_pos = NULL;
ctx->tag_length = 0;
+ ctx->directive_length = 0;
if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
@@ -3075,6 +3028,82 @@
return OK;
}
+
+/*********************************************************
+ * Beginning of stuff that module writers need to implement if they
+ * want to offer an SSI directive handler of their own...
+ *********************************************************/
+
+/* Anyone who wants to register an ssi handler must implement a version of this
+ * function (named the same as in the ap_hook_ssi_handler_register() call...
+ * This function is registered and called via the ap_hook_ssi_handler_register
+ * hook function.
+ */
+static void register_ssi_handle_x(apr_hash_t *hash)
+{
+ /* The nice handler author just needs to add any handlers they have
+ * written into the hash table with their tag as the key. The handler
+ * function must have a prototype of the following form:
+ * (the nice user will have to include the mod_include.h file)
+ *
+ * static int handle_x(include_ctx_t *ctx, ap_bucket_brigade **bb,
+ * request_rec *r, ap_filter_t *f,
+ * ap_bucket *head_ptr, ap_bucket **inserted_head
+ */
+ apr_hash_set(hash, "if", sizeof("if"), handle_if);
+ apr_hash_set(hash, "set", sizeof("set"), handle_set);
+ apr_hash_set(hash, "else", sizeof("else"), handle_else);
+ apr_hash_set(hash, "elif", sizeof("elif"), handle_elif);
+ apr_hash_set(hash, "exec", sizeof("exec"), handle_exec);
+ apr_hash_set(hash, "echo", sizeof("echo"), handle_echo);
+ apr_hash_set(hash, "endif", sizeof("endif"), handle_endif);
+ apr_hash_set(hash, "fsize", sizeof("fsize"), handle_fsize);
+ apr_hash_set(hash, "config", sizeof("config"), handle_config);
+ apr_hash_set(hash, "include", sizeof("include"), handle_include);
+ apr_hash_set(hash, "flastmod", sizeof("flastmod"), handle_flastmod);
+ apr_hash_set(hash, "printenv", sizeof("printenv"), handle_printenv);
+}
+
+
+/*
+** Global Module Initialization (post_config hook)
+** [called from read_config() after all config commands were already called]
+*/
+static void register_ssi_handlers(apr_pool_t *p,
+ apr_pool_t *plog,
+ apr_pool_t *ptemp,
+ server_rec *s)
+{
+ /* Anyone who wants to register an ssi handler must do this as part of their
+ * post_config handling...
+ */
+ ap_hook_ssi_handler_register(register_ssi_handle_x, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
+/*********************************************************
+ * End of stuff that module writers need to implement if they
+ * want to offer an SSI directive handler of their own.
+ *********************************************************/
+
+/*
+** Per-Child Module Initialization. This must run REALLY last
+** so that all of the other modules/filters will have had a
+** chance to run ap_hook_ssi_handler_register() and run their
+** own init processing before this is called.
+** [called after a child process is spawned]
+*/
+static void init_include_hash(apr_pool_t *p, server_rec *s)
+{
+ /* setup and initialize the hash table. */
+ hash_table = apr_make_hash(p);
+
+ /* Run the hash table initialization hook. */
+ ap_run_ssi_handler_register(hash_table);
+}
+
+/*
+** Module definition and configuration data structs...
+*/
static const command_rec includes_cmds[] =
{
AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS,
@@ -3084,6 +3113,8 @@
static void register_hooks(void)
{
+ ap_hook_post_config(register_ssi_handlers,NULL,NULL,AP_HOOK_MIDDLE);
+ ap_hook_child_init(init_include_hash,NULL,NULL,AP_HOOK_LAST+1);
ap_register_output_filter("INCLUDES", includes_filter, AP_FTYPE_CONTENT);
}
Index: filters/mod_include.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/filters/mod_include.h,v
retrieving revision 1.3
diff -u -r1.3 mod_include.h
--- filters/mod_include.h 2000/11/28 03:19:21 1.3
+++ filters/mod_include.h 2000/12/06 15:08:46
@@ -60,6 +60,8 @@
#define _MOD_INCLUDE_H 1
+#include "ap_hooks.h"
+#include "apr_hash.h"
#define STARTING_SEQUENCE "<!--#"
#define ENDING_SEQUENCE "-->"
@@ -77,6 +79,14 @@
module AP_MODULE_DECLARE_DATA includes_module;
+AP_DECLARE_HOOK(void,ssi_handler_register,(apr_hash_t *hash_table));
+
+AP_HOOK_STRUCT(
+ AP_HOOK_LINK(ssi_handler_register)
+)
+
+AP_IMPLEMENT_HOOK_VOID(ssi_handler_register, (apr_hash_t *hash_table), (hash_table))
+
/* just need some arbitrary non-NULL pointer which can't also be a request_rec */
#define NESTED_INCLUDE_MAGIC (&includes_module)
@@ -139,7 +149,7 @@
* ssi_tag_brigade: The temporary brigade used by this filter to set aside
* the buckets containing parts of the ssi tag and headers.
*/
-typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_TAG, PARSE_TAIL, PARSED} states;
+typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL, PARSED} states;
typedef struct include_filter_ctx {
states state;
long flags; /* See the FLAG_XXXXX definitions. */
@@ -157,6 +167,7 @@
char *combined_tag;
char *curr_tag_pos;
+ apr_size_t directive_length;
apr_size_t tag_length;
apr_size_t error_length;
@@ -176,10 +187,6 @@
#define FLAG_SIZE_ABBREV 0xFFFFFFFB /* Reset SIZE_IN_BYTES bit. */
#define FLAG_CLEAR_PRINT_COND 0xFFFFFFFC /* Reset PRINTING and COND_TRUE*/
#define FLAG_CLEAR_PRINTING 0xFFFFFFFE /* Reset just PRINTING bit. */
-
-typedef enum {TOK_UNKNOWN, TOK_IF, TOK_SET, TOK_ECHO, TOK_ELIF, TOK_ELSE,
- TOK_EXEC, TOK_PERL, TOK_ENDIF, TOK_FSIZE, TOK_CONFIG,
- TOK_INCLUDE, TOK_FLASTMOD, TOK_PRINTENV} dir_token_id;
#define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head) \
{ \
Re: [Patch]: Implement hook/hash extensions for mod_include handlers
Posted by rb...@covalent.net.
In modifying this patch to not use a hook, but just publish a function, I
have noticed some problems. I am fixing all of these, but I want to
explain them here, so that there are not surprises.
> +/*
> +** Per-Child Module Initialization. This must run REALLY last
> +** so that all of the other modules/filters will have had a
> +** chance to run ap_hook_ssi_handler_register() and run their
> +** own init processing before this is called.
> +** [called after a child process is spawned]
> +*/
> +static void init_include_hash(apr_pool_t *p, server_rec *s)
> +{
> + /* setup and initialize the hash table. */
> + hash_table = apr_make_hash(p);
> +
> + /* Run the hash table initialization hook. */
> + ap_run_ssi_handler_register(hash_table);
> +}
This shouldn't be done in the child_init phase. If we do it there, then
every child has to re-populate the hash table when it is
started. Populating the hash table should be done in the parent process,
so that child processes get it for free.
> static void register_hooks(void)
> {
> + ap_hook_post_config(register_ssi_handlers,NULL,NULL,AP_HOOK_MIDDLE);
> + ap_hook_child_init(init_include_hash,NULL,NULL,AP_HOOK_LAST+1);
If you want a hook to run absolutely first or absolutely last, just use
AP_HOOK_REALLY_FIRST and AP_HOOK_REALLY_LAST. Those two values are almost
never used, because you have to feel VERY strong about being first or last
to use them, so they are safe.
Ryan
_______________________________________________________________________________
Ryan Bloom rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------
Re: [Patch]: Implement hook/hash extensions for mod_include handlers
Posted by rb...@covalent.net.
On Wed, 6 Dec 2000, Paul J. Reder wrote:
> This patch converts mod_include to use a hash table registry of handlers for
> the different tags (with the tag as the hashed item and the function to call
> as the hashed value).
>
> The entries in the hash table are added during the running of a hook. Any module
> (i.e. mod_perl, etc) that wants to add an SSI directive tag can do so by registering
> the hook and adding their hashed tag and function to the table when called.
>
> Mod_include registers all of its own handlers via the hook instead of cheating,
> so the nicely documented mod_include code can be used as sample code for any
> other module authors.
In general the idea is good, but I disagree with parts of the
implementation. I am going to re-work this patch a bit to remove the
hook, because as we discussed just after the hash table was mentioned, the
hook function is unnecessary. Plus, I would like to just export a
function that other include modules can call to register their functions,
because that way we don't need to export the mod_include hash table.
I expect to use this patch as a basis and just re-work it a little
bit. Expect a commit at some point today.
Ryan
_______________________________________________________________________________
Ryan Bloom rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------