You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Cody Sherr <cs...@covalent.net> on 2001/08/02 03:45:39 UTC

[PATCH] repost: directive extended to arbitrary methods

I've added a macro to ease checking if a method is allowed in a mask:

ap_method_check_allowed(mask, methname)

in accordance with Will Rowe's recommendation today.

thanks,

-- 
Cody Sherr

Engineer
Covalent Technologies

phone: (415)536-5292
email: csherr@covalent.net
? build.log
? build2.log
? create_scoreboard.patch
? create_scoreboard2.patch
? cg
? build/config_vars.mk
? include/http_protocol.h.new
? include/http_protocol.h.cody
? modules/http/http_protocol.c.cody
? server/core.c.good
? server/scoreboard.c.new
? srclib/apr
Index: include/http_config.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/include/http_config.h,v
retrieving revision 1.82
diff -u -r1.82 http_config.h
--- include/http_config.h	2001/05/28 15:32:50	1.82
+++ include/http_config.h	2001/08/02 01:41:17
@@ -260,7 +260,7 @@
     /** 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: include/http_core.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/include/http_core.h,v
retrieving revision 1.46
diff -u -r1.46 http_core.h
--- include/http_core.h	2001/07/30 18:51:57	1.46
+++ include/http_core.h	2001/08/02 01:41:17
@@ -277,7 +277,7 @@
 /** 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: include/http_protocol.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/include/http_protocol.h,v
retrieving revision 1.60
diff -u -r1.60 http_protocol.h
--- include/http_protocol.h	2001/07/27 21:01:16	1.60
+++ include/http_protocol.h	2001/08/02 01:41:17
@@ -227,6 +227,40 @@
                              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: include/httpd.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/include/httpd.h,v
retrieving revision 1.156
diff -u -r1.156 httpd.h
--- include/httpd.h	2001/07/26 15:53:15	1.156
+++ include/httpd.h	2001/08/02 01:41:18
@@ -478,8 +478,6 @@
 /* Methods recognized (but not necessarily handled) by the server.
  * These constants are used in bit shifting masks of size int, so it is
  * unsafe to have more methods than bits in an int.  HEAD == M_GET.
- * This list must be tracked by the list in http_protocol.c in routine
- * ap_method_name_of().
  */
 #define M_GET        0
 #define M_PUT        1
@@ -498,7 +496,10 @@
 #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 +509,8 @@
  */
 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 +680,7 @@
      *  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: modules/aaa/mod_access.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/aaa/mod_access.c,v
retrieving revision 1.32
diff -u -r1.32 mod_access.c
--- modules/aaa/mod_access.c	2001/04/12 13:35:39	1.32
+++ modules/aaa/mod_access.c	2001/08/02 01:41:18
@@ -91,7 +91,7 @@
 };

 typedef struct {
-    int limited;
+    apr_int64_t limited;
     union {
 	char *from;
         apr_ipsubnet_t *ip;
@@ -237,8 +237,9 @@

 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: modules/http/http_protocol.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/http/http_protocol.c,v
retrieving revision 1.339
diff -u -r1.339 http_protocol.c
--- modules/http/http_protocol.c	2001/08/01 04:08:36	1.339
+++ modules/http/http_protocol.c	2001/08/02 01:41:18
@@ -299,6 +299,83 @@
     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;
+
+/**
+ * This keeps track of the currently available method number.
+ */
+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;
+}
+
+/**
+ * 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)
+{
+    methods_registry = apr_hash_make(p);
+    apr_pool_cleanup_register(p, NULL,
+			      ap_method_registry_destroy,
+			      apr_pool_cleanup_null);
+}
+
+/**
+ * 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         An int value representing an offset into a bitmask.
+ *                 M_INVALID is return if methname is NULL or we
+ *                 have run out of space in the allowed mask.
+ */
+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,11 +385,13 @@
  */
 AP_DECLARE(int) ap_method_number_of(const char *method)
 {
+    int *methnum = NULL;
+
     switch (*method) {
         case 'H':
            if (strcmp(method, "HEAD") == 0)
                return M_GET;   /* see header_only in request_rec */
-           break;
+           break;
         case 'G':
            if (strcmp(method, "GET") == 0)
                return M_GET;
@@ -361,7 +440,17 @@
            if (strcmp(method, "UNLOCK") == 0)
                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 +993,7 @@
 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 +2162,8 @@
     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: server/config.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/server/config.c,v
retrieving revision 1.130
diff -u -r1.130 config.c
--- server/config.c	2001/07/04 03:16:33	1.130
+++ server/config.c	2001/08/02 01:41:19
@@ -354,30 +354,18 @@

 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);
-    }
-    /*
-     * 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  (cmd->limited & (1<<methnum));
     }
-    return 0;
+
+    return 0; /* not found */
 }

 AP_DECLARE(void) ap_register_hooks(module *m, apr_pool_t *p)
Index: server/core.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/server/core.c,v
retrieving revision 1.32
diff -u -r1.32 core.c
--- server/core.c	2001/08/01 19:15:22	1.32
+++ server/core.c	2001/08/02 01:41:19
@@ -1466,55 +1466,31 @@
 						  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;
-
+    int i = 0;
     const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+
     if (err != NULL) {
         return err;
     }

     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.
+	else if (methnum == M_INVALID) {
+	    /* method has not been registered yet, but resorce restriction
+	     * is always checked before method handling, so register it.
 	     */
-	    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--;
-		    }
-		}
-	    }
-        }
+	    methnum = ap_method_register(cmd->pool, method);
+	}
 	limited |= (1 << methnum);
     }

Index: server/protocol.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/server/protocol.c,v
retrieving revision 1.35
diff -u -r1.35 protocol.c
--- server/protocol.c	2001/07/30 04:38:02	1.35
+++ server/protocol.c	2001/08/02 01:41:20
@@ -1135,14 +1135,31 @@

 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;



Re: [PATCH] repost: directive extended to arbitrary methods

Posted by "Roy T. Fielding" <fi...@ebuilt.com>.
> I'll be committing this tonight.  The only change I will be adding, is that
> the macro is going to be capitalized, because all macros should always be
> capitalized.

No, that is wrong.  A macro that is taking the place of a past or potentially
future function call should always be lowercase, just like any other function
call.  The only reason to all-CAP a macro with parameters is if it cannot
ever be a C funtion (usually because it depends on pass-by-name).

....Roy


Re: [PATCH] repost: directive extended to arbitrary methods

Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
From: "Ryan Bloom" <rb...@covalent.net>
Sent: Wednesday, August 01, 2001 11:14 PM

> > Add one more placeholder, FIXED_METHODS, corresponding to the last
> > M_INVALID + 1.
> 
> It's already there.  Look for METHOD_NUMBER_FIRST in http_protocol.h.  :-)
> 
> > Stuff them in the hash and pull out all the bogus tests from
> > ap_method_number_of!!! You can't add a new 'GOOBLE' method, it will map to
> > 'G' -- 'GET'.
> 
> I dislike changing how the core works in the same patch as when we introduce a
> new API.  Let's commit the API change, and then fix the core.  And GOOBLE won't
> map to GET, because we strcmp the two values.

Yup... I missed that, and yes, these improvements can go in through another patch.


Re: [PATCH] repost: directive extended to arbitrary methods

Posted by Ryan Bloom <rb...@covalent.net>.
> I caught that... the other bit that I hinted at in my last message was
> pretty simple...
>
> when we first query, if the table doesn't exist, stuff elements n ..
> FIXED_METHODS

Get this done after the initial commit is in.  Since we don't currently stuff any
of the core methods in, we don't want to add them in the first commit.

>
> +++ include/httpd.h 2001/08/02 01:41:18
> @@ -478,8 +478,6 @@
>  /* Methods recognized (but not necessarily handled) by the server.
>   * These constants are used in bit shifting masks of size int, so it is
>   * unsafe to have more methods than bits in an int.  HEAD == M_GET.
> - * This list must be tracked by the list in http_protocol.c in routine
> - * ap_method_name_of().
>
>
> Don't you dare eliminate that comment!!!

Don't worry, I put it back.

> @@ -498,7 +496,10 @@
>  #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
>
>
> Add one more placeholder, FIXED_METHODS, corresponding to the last
> M_INVALID + 1.

It's already there.  Look for METHOD_NUMBER_FIRST in http_protocol.h.  :-)

> Stuff them in the hash and pull out all the bogus tests from
> ap_method_number_of!!! You can't add a new 'GOOBLE' method, it will map to
> 'G' -- 'GET'.

I dislike changing how the core works in the same patch as when we introduce a
new API.  Let's commit the API change, and then fix the core.  And GOOBLE won't
map to GET, because we strcmp the two values.

Ryan

_____________________________________________________________________________
Ryan Bloom                        	rbb@apache.org
Covalent Technologies			rbb@covalent.net
-----------------------------------------------------------------------------

Re: [PATCH] repost: directive extended to arbitrary methods

Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
From: "Ryan Bloom" <rb...@covalent.net>
Sent: Wednesday, August 01, 2001 10:28 PM


> I'll be committing this tonight.  The only change I will be adding, is that the macro is
> going to be capitalized, because all macros should always be capitalized.

I caught that... the other bit that I hinted at in my last message was pretty simple...

when we first query, if the table doesn't exist, stuff elements n .. FIXED_METHODS

+++ include/httpd.h 2001/08/02 01:41:18
@@ -478,8 +478,6 @@
 /* Methods recognized (but not necessarily handled) by the server.
  * These constants are used in bit shifting masks of size int, so it is
  * unsafe to have more methods than bits in an int.  HEAD == M_GET.
- * This list must be tracked by the list in http_protocol.c in routine
- * ap_method_name_of().


Don't you dare eliminate that comment!!!


@@ -498,7 +496,10 @@
 #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


Add one more placeholder, FIXED_METHODS, corresponding to the last M_INVALID + 1.

Stuff them in the hash and pull out all the bogus tests from ap_method_number_of!!!
You can't add a new 'GOOBLE' method, it will map to 'G' -- 'GET'.


Other than that?

+1 :)

Bill



> On Wednesday 01 August 2001 18:45, Cody Sherr wrote:
> > I've added a macro to ease checking if a method is allowed in a mask:
> >
> > ap_method_check_allowed(mask, methname)
> >
> > in accordance with Will Rowe's recommendation today.
> >
> > thanks,
> 
> -- 
> 
> _____________________________________________________________________________
> Ryan Bloom                        rbb@apache.org
> Covalent Technologies rbb@covalent.net
> -----------------------------------------------------------------------------
> 


Re: [PATCH] repost: directive extended to arbitrary methods

Posted by Ryan Bloom <rb...@covalent.net>.
I'll be committing this tonight.  The only change I will be adding, is that the macro is
going to be capitalized, because all macros should always be capitalized.

Ryan

On Wednesday 01 August 2001 18:45, Cody Sherr wrote:
> I've added a macro to ease checking if a method is allowed in a mask:
>
> ap_method_check_allowed(mask, methname)
>
> in accordance with Will Rowe's recommendation today.
>
> thanks,

-- 

_____________________________________________________________________________
Ryan Bloom                        	rbb@apache.org
Covalent Technologies			rbb@covalent.net
-----------------------------------------------------------------------------