httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Paul J. Reder" <rede...@raleigh.ibm.com>
Subject [Patch]: Implement hook/hash extensions for mod_include handlers
Date Wed, 06 Dec 2000 15:46:40 GMT
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)        \
 {                                                                 \

Mime
View raw message