]> granicus.if.org Git - apache/commitdiff
Add the ability to extend the methods that Apache understands
authorRyan Bloom <rbb@apache.org>
Thu, 2 Aug 2001 04:25:20 +0000 (04:25 +0000)
committerRyan Bloom <rbb@apache.org>
Thu, 2 Aug 2001 04:25:20 +0000 (04:25 +0000)
and have those methods <limit>able in the httpd.conf. It uses
the same bit mask/shifted offset as the original HTTP methods
such as M_GET or M_POST, but expands the total bits from an int to
an ap_int64_t to handle more bits for new request methods than
an int provides.
Submitted by: Cody Sherr <csherr@covalent.net>

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@89869 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
include/http_config.h
include/http_core.h
include/http_protocol.h
include/httpd.h
modules/aaa/mod_access.c
modules/http/http_protocol.c
server/config.c
server/core.c
server/protocol.c

diff --git a/CHANGES b/CHANGES
index 21b5ade14f8b8216e459b217f3634fbaec68a89a..32a9510be3fb119a105e1258c51036ebe966ca0c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,12 @@
 Changes with Apache 2.0.23-dev
 
+  *) Add the ability to extend the methods that Apache understands
+     and have those methods <limit>able in the httpd.conf. It uses 
+     the same bit mask/shifted offset as the original HTTP methods 
+     such as M_GET or M_POST, but expands the total bits from an int to 
+     an ap_int64_t to handle more bits for new request methods than 
+     an int provides.  [Cody Sherr <csherr@covalent.net>]
+
   *) Fix broken mod_mime behavior in merging its arguments.  Possible
      cause of unexplicable crashes introduced in 2.0.20.  [William Rowe]
 
index 5fe4825439bb186cda19cf3e57567ce8b5d6ae8d..9e4a524d47a7b5fb4bd3ce52d4b944ece2f7eb7d 100644 (file)
@@ -260,7 +260,7 @@ struct cmd_parms_struct {
     /** Which allow-override bits are set */
     int override;
     /** Which methods are <Limit>ed */
-    int limited;
+    apr_int64_t limited;
     apr_array_header_t *limited_xmethods;
     ap_method_list_t *xlimited;
 
index 4e0d2ecfd6bcb63b88e7c39498c839ddc82aad39..1bf9f485e25995bf36976191f7ce31ecd251fd24 100644 (file)
@@ -277,7 +277,7 @@ typedef struct require_line require_line;
 /** A structure to keep track of authorization requirements */
 struct require_line {
     /** Where the require line is in the config file. */
-    int method_mask;
+    apr_int64_t method_mask;
     /** The complete string from the command line */
     char *requirement;
 };
index 5ab522cd2bfef2df620ec9141da570dbd923c915..a27d5e77b89d5e039faa88b8d4b5311e0aeb45c5 100644 (file)
@@ -227,6 +227,40 @@ AP_DECLARE(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset,
                              size_t length);
 #endif
 
+/* The index of the first bit field that is used to index into a limit
+ * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
+ */
+#define METHOD_NUMBER_FIRST M_INVALID + 1
+
+/* The max method number. Method numbers are used to shift bitmasks,
+ * so this cannot exceed 63, and all bits high is equal to -1, which is a
+ * special flag, so the last bit used has index 62.
+ */
+#define METHOD_NUMBER_LAST  62
+
+/**
+ * Register a new request method, and return the offset that will be
+ * associated with that method.
+ *
+ * @param p        The pool to create registered method numbers from.
+ * @param methname The name of the new method to register.
+ * @return         Ab int value representing an offset into a bitmask.
+ */
+AP_DECLARE(int) ap_method_register(apr_pool_t *p, char *methname);
+
+/**
+ * Initialize the method_registry and allocate memory for it.
+ *
+ * @param p Pool to allocate memory for the registry from.
+ */
+AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p);
+
+/*
+ * This is a convenience macro to ease with checking a mask
+ * against a method name.
+ */
+#define AP_METHOD_CHECK_ALLOWED(mask, methname) ((mask) & (1 << ap_method_number_of((methname))))
+
 /**
  * Create a new method list with the specified number of preallocated
  * slots for extension methods.
index c2eba3bc8fbb26c98afbb337e0c39ca1d7d8d01e..e32ed8ef4a2a2fea7cb76736c9e149abd7d06e97 100644 (file)
@@ -498,7 +498,10 @@ AP_DECLARE(const char *) ap_get_server_built(void);
 #define M_UNLOCK    14
 #define M_INVALID   15
 
-#define METHODS     16
+/* METHODS needs to be equal to the number of bits
+ * we are using for limit masks.
+ */
+#define METHODS     64
 
 typedef struct ap_method_list_t ap_method_list_t;
 /**
@@ -508,8 +511,8 @@ typedef struct ap_method_list_t ap_method_list_t;
  */
 struct ap_method_list_t {
     /* The bitmask used for known methods */
-    int method_mask;
-    /* The array used for extension methods */
+    apr_int64_t method_mask;
+    /* the array used for extension methods */
     apr_array_header_t *method_list;
 };
 
@@ -679,7 +682,7 @@ struct request_rec {
      *  HTTP_METHOD_NOT_ALLOWED.  Unfortunately this means that a Script GET
      *  handler can't be installed by mod_actions.
      */
-    int allowed;
+    apr_int64_t allowed;
     /** Array of extension methods */
     apr_array_header_t *allowed_xmethods; 
     /** List of allowed methods */
index d51df93a400fb42d4c3bbcc84cd33ed1ca3e10cf..41d6133879ff7ab7642a61900fd3bccab96b018f 100644 (file)
@@ -91,7 +91,7 @@ enum allowdeny_type {
 };
 
 typedef struct {
-    int limited;
+    apr_int64_t limited;
     union {
        char *from;
         apr_ipsubnet_t *ip;
@@ -237,8 +237,9 @@ static int in_domain(const char *domain, const char *what)
 
 static int find_allowdeny(request_rec *r, apr_array_header_t *a, int method)
 {
+
     allowdeny *ap = (allowdeny *) a->elts;
-    int mmask = (1 << method);
+    apr_int64_t mmask = (1 << method);
     int i;
     int gothost = 0;
     const char *remotehost = NULL;
index cf74ccfce9c372bdf4c209f6b9f206d9d39d913e..fffb61909052296b1c2e05ea4fc56860ca9c6e43 100644 (file)
@@ -299,6 +299,64 @@ AP_DECLARE(int) ap_meets_conditions(request_rec *r)
     return OK;
 }
 
+/**
+ * Singleton registry of additional methods. This maps new method names
+ * such as "MYGET" to methnums, which are int offsets into bitmasks.
+ *
+ * This follows the same technique as standard M_GET, M_POST, etc. These
+ * are dynamically assigned when modules are loaded and <Limit GET MYGET>
+ * directives are processed.
+ */
+static apr_hash_t *methods_registry=NULL;
+static int cur_method_number = METHOD_NUMBER_FIRST;
+
+/* This internal function is used to clear the method registry
+ * and reset the cur_method_number counter.
+ */
+static apr_status_t ap_method_registry_destroy(void *notused)
+{
+    methods_registry = NULL;
+    cur_method_number = METHOD_NUMBER_FIRST;
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
+{
+    methods_registry = apr_hash_make(p);
+    apr_pool_cleanup_register(p, NULL,
+                             ap_method_registry_destroy,
+                             apr_pool_cleanup_null);
+}
+
+AP_DECLARE(int) ap_method_register(apr_pool_t *p, char *methname)
+{
+    int *newmethnum;
+
+    if (methods_registry == NULL) {
+       ap_method_registry_init(p);
+    }
+
+    if (methname == NULL) {
+       return M_INVALID;
+    }
+
+    if (cur_method_number > METHOD_NUMBER_LAST) {
+       /* The method registry  has run out of dynamically
+        * assignable method numbers. Log this and return M_INVALID.
+        */
+       ap_log_perror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, p,
+                     "Maximum new request methods %d reached while registering method %s.",
+                     METHOD_NUMBER_LAST, methname);
+       return M_INVALID;
+    }
+
+    newmethnum  = (int*)apr_palloc(p,sizeof(int));
+    *newmethnum = cur_method_number++;
+    apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, newmethnum);
+
+    return *newmethnum;
+}
+
 /* Get the method number associated with the given string, assumed to
  * contain an HTTP method.  Returns M_INVALID if not recognized.
  *
@@ -308,6 +366,8 @@ AP_DECLARE(int) ap_meets_conditions(request_rec *r)
  */
 AP_DECLARE(int) ap_method_number_of(const char *method)
 {
+    int *methnum = NULL;
+
     switch (*method) {
         case 'H':
            if (strcmp(method, "HEAD") == 0)
@@ -362,6 +422,16 @@ AP_DECLARE(int) ap_method_number_of(const char *method)
                return M_UNLOCK;
            break;
     }
+
+    /* check if the method has been dynamically registered */
+    if (methods_registry != NULL) {
+       methnum = (int*)apr_hash_get(methods_registry,
+                                    method,
+                                    APR_HASH_KEY_STRING);
+       if (methnum != NULL)
+           return *methnum;
+    }
+
     return M_INVALID;
 }
 
@@ -904,7 +974,7 @@ static void terminate_header(apr_bucket_brigade *bb)
 static char *make_allow(request_rec *r)
 {
     char *list;
-    int mask;
+    apr_int64_t mask;
 
     mask = r->allowed_methods->method_mask;
     list = apr_pstrcat(r->pool,
@@ -2073,8 +2143,8 @@ AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
     char **methods;
  
     /*
-     * If it's one of our known methods, use the shortcut and use the
-     * bitmask.
+     * If it's a known methods, either builtin or registered
+     * by a module, use the bitmask.
      */
     methnum = ap_method_number_of(method);
     l->method_mask |= ~(1 << methnum);
index 7b7cf32f74d2c3c8af07266c4aff6f6a44e350bc..1896d617725d6e1c86f25554f306fba9a8b28a3b 100644 (file)
@@ -354,30 +354,18 @@ AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r)
 
 AP_DECLARE(int) ap_method_is_limited(cmd_parms *cmd, const char *method) {
     int methnum;
-    int i;
-    char **xmethod;
 
     methnum = ap_method_number_of(method);
+
     /*
-     * The simple case: a method hard-coded into Apache.
+     * A method number either hardcoded into apache or
+     * added by a module and registered.
      */
     if (methnum != M_INVALID) {
-       return (methnum & cmd->limited);
+       return  (cmd->limited & (1<<methnum));
     }
-    /*
-     * Some extension method we don't know implicitly.
-     */
-    if ((cmd->limited_xmethods == NULL)
-       || (cmd->limited_xmethods->nelts == 0)) {
-       return 0;
-    }
-    xmethod = (char **) cmd->limited_xmethods->elts;
-    for (i = 0; i < cmd->limited_xmethods->nelts; ++i) {
-       if (strcmp(method, xmethod[i]) == 0) {
-           return 1;
-       }
-    }
-    return 0;
+
+    return 0; /* not found */
 }
 
 AP_DECLARE(void) ap_register_hooks(module *m, apr_pool_t *p)
index e65316c4ea131f9cb892818f28c2f0a50b1dba04..093efce4fd7e484c3d4d88cb9768f2f2aa162886 100644 (file)
@@ -1466,7 +1466,7 @@ AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dumm
                                                  const char *arg) {
     const char *limited_methods = ap_getword(cmd->pool, &arg, '>');
     void *tog = cmd->cmd->cmd_data;
-    int limited = 0;
+    apr_int64_t limited = 0;
     const char *errmsg;
   
     const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
@@ -1476,46 +1476,21 @@ AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dumm
 
     while (limited_methods[0]) {
         char *method = ap_getword_conf(cmd->pool, &limited_methods);
-        int  methnum = ap_method_number_of(method);
+        int methnum;
+
+        /* check for builtin or module registered method number */
+        methnum = ap_method_number_of(method);
 
         if (methnum == M_TRACE && !tog) {
             return "TRACE cannot be controlled by <Limit>";
         }
         else if (methnum == M_INVALID) {
-           char **xmethod;
-           register int i, j, k;
-
-           /*
-            * Deal with <Limit> by adding the method to the list.
-            */
-           if (!tog) {
-               if (cmd->limited_xmethods == NULL) {
-                   cmd->limited_xmethods = apr_array_make(cmd->pool, 2,
-                                                          sizeof(char *));
-               }
-               xmethod = (char **) apr_array_push(cmd->limited_xmethods);
-               *xmethod = apr_pstrdup(cmd->pool, method);
-           }
-           /*
-            * <LimitExcept>, so remove any/all occurrences of the method
-            * in the extension array.
-            */
-           else if ((cmd->limited_xmethods != NULL)
-                    && (cmd->limited_xmethods->nelts != 0)) {
-               xmethod = (char **) cmd->limited_xmethods->elts;
-               for (i = 0; i < cmd->limited_xmethods->nelts; i++) {
-                   if (strcmp(xmethod[i], method) == 0) {
-                       for (j = i, k = i + 1;
-                            k < cmd->limited_xmethods->nelts;
-                            ++j, ++k) {
-                           xmethod[j] = xmethod[k];
-                       }
-                       cmd->limited_xmethods->nelts--;
-                   }
-               }
-           }
+            /* method has not been registered yet, but resorce restriction
+             * is always checked before method handling, so register it.
+             */
+            methnum = ap_method_register(cmd->pool, method);
         }
-       limited |= (1 << methnum);
+        limited |= (1 << methnum);
     }
 
     /* Killing two features with one function,
index 2d1a69e1914beb1e877129989c1906c64c027bf6..210d9078e435783b53e36ff6005bb29104e56598 100644 (file)
@@ -1135,14 +1135,31 @@ AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
 
 AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
 {
-    char buf[4096];
+    char *buf;
+    int   buf_size = 4096; /* start with a 4k buffer */
     apr_size_t written;
 
     if (r->connection->aborted)
         return -1;
 
-    /* ### fix this mechanism to allow more than 4K of output */
-    written = apr_vsnprintf(buf, sizeof(buf), fmt, va);
+    buf = apr_palloc(r->pool, buf_size);
+    while (1) {
+       written = apr_vsnprintf(buf, buf_size, fmt, va);
+
+       /*
+        * Per the apr_vsnprintf comments, in no event does apr_snprintf return a negative number.
+        * Therefore, it's not possible to distinguish between an output which was truncated,
+        * and an output which exactly filled the buffer.
+        */
+       if (written == buf_size) {
+           buf_size *= 2;
+           buf = apr_palloc(r->pool, buf_size); /* want realloc */
+       }
+       else {
+           break;
+       }
+    }
+
     if (buffer_output(r, buf, written) != APR_SUCCESS)
         return -1;