From: Greg Stein Date: Tue, 19 Mar 2002 10:11:33 +0000 (+0000) Subject: The underlying change here was to add the new WebDAV/DeltaV methods X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=38679b5391ac0cd7345e7d22cac09512636e188c;p=apache The underlying change here was to add the new WebDAV/DeltaV methods now that it has an RFC. At the same time, I revamped a good chunk of the name <-> number mapping code in http_protocol.c * add M_FOO constants for the new RFC 3253 (DeltaV) methods. label where each of the builtin methods comes from. * moved METHOD_NUMBER_FIRST/LAST from http_protocol.h into http_protocol.c since they weren't used anywhere else and they weren't namespace-protected. * create register_one_method() and use it to insert all builtin methods (at _init() time) and extended methods into the registry. * add a lookup_builtin_method() to quickly map a method name to a builtin method number. * rebuild ap_method_number_of() to use the new lookup function. * revamp ap_method_name_of() to use the registry to locate the name for any method number. add a pool argument (no callers in the core code needed to be updated) * revamp make_allow() to deal with the new method numbers and all extended methods. * in mod_dav, use the new method numbers rather than registering the DeltaV methods. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@94015 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/include/http_protocol.h b/include/http_protocol.h index fd04b436c7..8aa5b0d3c5 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -232,16 +232,6 @@ 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 @@ -571,23 +561,23 @@ AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n, apr_size_t *read, request_rec *r, int fold); + /** * Get the method number associated with the given string, assumed to * contain an HTTP method. Returns M_INVALID if not recognized. * @param method A string containing a valid HTTP method * @return The method number - * @deffunc int ap_method_number_of(const char *method) */ AP_DECLARE(int) ap_method_number_of(const char *method); /** * Get the method name associated with the given internal method * number. Returns NULL if not recognized. + * @param p A pool to use for temporary allocations. * @param methnum An integer value corresponding to an internal method number * @return The name corresponding to the method number - * @deffunc const char *ap_method_name_of(int methnum) */ -AP_DECLARE(const char *) ap_method_name_of(int methnum); +AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum); /* Hooks */ diff --git a/include/httpd.h b/include/httpd.h index a1b696263c..143f00f8a0 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -522,22 +522,33 @@ AP_DECLARE(const char *) ap_get_server_built(void); * 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 -#define M_POST 2 -#define M_DELETE 3 -#define M_CONNECT 4 -#define M_OPTIONS 5 -#define M_TRACE 6 -#define M_PATCH 7 -#define M_PROPFIND 8 -#define M_PROPPATCH 9 -#define M_MKCOL 10 -#define M_COPY 11 -#define M_MOVE 12 -#define M_LOCK 13 -#define M_UNLOCK 14 -#define M_INVALID 15 +#define M_GET 0 /* RFC 2616: HTTP */ +#define M_PUT 1 /* : */ +#define M_POST 2 +#define M_DELETE 3 +#define M_CONNECT 4 +#define M_OPTIONS 5 +#define M_TRACE 6 /* RFC 2616: HTTP */ +#define M_PATCH 7 /* no rfc(!) ### remove this one? */ +#define M_PROPFIND 8 /* RFC 2518: WebDAV */ +#define M_PROPPATCH 9 /* : */ +#define M_MKCOL 10 +#define M_COPY 11 +#define M_MOVE 12 +#define M_LOCK 13 +#define M_UNLOCK 14 /* RFC 2518: WebDAV */ +#define M_VERSION_CONTROL 15 /* RFC 3253: WebDAV Versioning */ +#define M_CHECKOUT 16 /* : */ +#define M_UNCHECKOUT 17 +#define M_CHECKIN 18 +#define M_UPDATE 19 +#define M_LABEL 20 +#define M_REPORT 21 +#define M_MKWORKSPACE 22 +#define M_MKACTIVITY 23 +#define M_BASELINE_CONTROL 24 +#define M_MERGE 25 +#define M_INVALID 26 /* RFC 3253: WebDAV Versioning */ /** * METHODS needs to be equal to the number of bits @@ -548,7 +559,7 @@ AP_DECLARE(const char *) ap_get_server_built(void); /** * The method mask bit to shift for anding with a bitmask. */ -#define AP_METHOD_BIT (apr_int64_t)1 +#define AP_METHOD_BIT ((apr_int64_t)1) /** @} */ diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c index ccdc361965..e35cb3563a 100644 --- a/modules/dav/main/mod_dav.c +++ b/modules/dav/main/mod_dav.c @@ -132,40 +132,18 @@ extern module DAV_DECLARE_DATA dav_module; /* DAV methods */ enum { - DAV_M_VERSION_CONTROL = 0, - DAV_M_CHECKOUT, - DAV_M_UNCHECKOUT, - DAV_M_CHECKIN, - DAV_M_UPDATE, - DAV_M_LABEL, - DAV_M_REPORT, - DAV_M_MKWORKSPACE, - DAV_M_MKACTIVITY, - DAV_M_BASELINE_CONTROL, - DAV_M_MERGE, - DAV_M_BIND, + DAV_M_BIND = 0, DAV_M_LAST }; - static int dav_methods[DAV_M_LAST]; + static int dav_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { /* DBG0("dav_init_handler"); */ /* Register DAV methods */ - dav_methods[DAV_M_VERSION_CONTROL] = ap_method_register(p, "VERSION-CONTROL"); - dav_methods[DAV_M_CHECKOUT] = ap_method_register(p, "CHECKOUT"); - dav_methods[DAV_M_UNCHECKOUT] = ap_method_register(p, "UNCHECKOUT"); - dav_methods[DAV_M_CHECKIN] = ap_method_register(p, "CHECKIN"); - dav_methods[DAV_M_UPDATE] = ap_method_register(p, "UPDATE"); - dav_methods[DAV_M_LABEL] = ap_method_register(p, "LABEL"); - dav_methods[DAV_M_REPORT] = ap_method_register(p, "REPORT"); - dav_methods[DAV_M_MKWORKSPACE] = ap_method_register(p, "MKWORKSPACE"); - dav_methods[DAV_M_MKACTIVITY] = ap_method_register(p, "MKACTIVITY"); - dav_methods[DAV_M_BASELINE_CONTROL] = ap_method_register(p, "BASELINE-CONTROL"); - dav_methods[DAV_M_MERGE] = ap_method_register(p, "MERGE"); dav_methods[DAV_M_BIND] = ap_method_register(p, "BIND"); ap_add_version_component(p, "DAV/2"); @@ -3868,7 +3846,6 @@ static int dav_method_report(request_rec *r) int result; int label_allowed; ap_xml_doc *doc; - ap_text *t; dav_error *err; /* If no versioning provider, decline the request */ @@ -4488,47 +4465,47 @@ static int dav_handler(request_rec *r) return dav_method_unlock(r); } - if (r->method_number == dav_methods[DAV_M_VERSION_CONTROL]) { + if (r->method_number == M_VERSION_CONTROL) { return dav_method_vsn_control(r); } - if (r->method_number == dav_methods[DAV_M_CHECKOUT]) { + if (r->method_number == M_CHECKOUT) { return dav_method_checkout(r); } - if (r->method_number == dav_methods[DAV_M_UNCHECKOUT]) { + if (r->method_number == M_UNCHECKOUT) { return dav_method_uncheckout(r); } - if (r->method_number == dav_methods[DAV_M_CHECKIN]) { + if (r->method_number == M_CHECKIN) { return dav_method_checkin(r); } - if (r->method_number == dav_methods[DAV_M_UPDATE]) { + if (r->method_number == M_UPDATE) { return dav_method_update(r); } - if (r->method_number == dav_methods[DAV_M_LABEL]) { + if (r->method_number == M_LABEL) { return dav_method_label(r); } - if (r->method_number == dav_methods[DAV_M_REPORT]) { + if (r->method_number == M_REPORT) { return dav_method_report(r); } - if (r->method_number == dav_methods[DAV_M_MKWORKSPACE]) { + if (r->method_number == M_MKWORKSPACE) { return dav_method_make_workspace(r); } - if (r->method_number == dav_methods[DAV_M_MKACTIVITY]) { + if (r->method_number == M_MKACTIVITY) { return dav_method_make_activity(r); } - if (r->method_number == dav_methods[DAV_M_BASELINE_CONTROL]) { + if (r->method_number == M_BASELINE_CONTROL) { return dav_method_baseline_control(r); } - if (r->method_number == dav_methods[DAV_M_MERGE]) { + if (r->method_number == M_MERGE) { return dav_method_merge(r); } diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 205c8732bf..cffdcb7ddb 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -100,6 +100,18 @@ #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 + + AP_DECLARE(int) ap_set_keepalive(request_rec *r) { int ka_sent = 0; @@ -317,6 +329,16 @@ AP_DECLARE(int) ap_meets_conditions(request_rec *r) static apr_hash_t *methods_registry = NULL; static int cur_method_number = METHOD_NUMBER_FIRST; +/* internal function to register one method/number pair */ +static void register_one_method(apr_pool_t *p, const char *methname, + int methnum) +{ + int *pnum = apr_palloc(p, sizeof(*pnum)); + + *pnum = methnum; + apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum); +} + /* This internal function is used to clear the method registry * and reset the cur_method_number counter. */ @@ -333,6 +355,35 @@ AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p) apr_pool_cleanup_register(p, NULL, ap_method_registry_destroy, apr_pool_cleanup_null); + + /* put all the standard methods into the registry hash to ease the + mapping operations between name and number */ + register_one_method(p, "GET", M_GET); + register_one_method(p, "PUT", M_PUT); + register_one_method(p, "POST", M_POST); + register_one_method(p, "DELETE", M_DELETE); + register_one_method(p, "CONNECT", M_CONNECT); + register_one_method(p, "OPTIONS", M_OPTIONS); + register_one_method(p, "TRACE", M_TRACE); + register_one_method(p, "PATCH", M_PATCH); + register_one_method(p, "PROPFIND", M_PROPFIND); + register_one_method(p, "PROPPATCH", M_PROPPATCH); + register_one_method(p, "MKCOL", M_MKCOL); + register_one_method(p, "COPY", M_COPY); + register_one_method(p, "MOVE", M_MOVE); + register_one_method(p, "LOCK", M_LOCK); + register_one_method(p, "UNLOCK", M_UNLOCK); + register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL); + register_one_method(p, "CHECKOUT", M_CHECKOUT); + register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT); + register_one_method(p, "CHECKIN", M_CHECKIN); + register_one_method(p, "UPDATE", M_UPDATE); + register_one_method(p, "LABEL", M_LABEL); + register_one_method(p, "REPORT", M_REPORT); + register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE); + register_one_method(p, "MKACTIVITY", M_MKACTIVITY); + register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL); + register_one_method(p, "MERGE", M_MERGE); } AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname) @@ -366,11 +417,180 @@ AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname) return M_INVALID; } - methnum = (int *)apr_palloc(p, sizeof(int)); - *methnum = cur_method_number++; - apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, methnum); + register_one_method(p, methname, cur_method_number); + return cur_method_number++; +} + +#define UNKNOWN_METHOD (-1) - return *methnum; +static int lookup_builtin_method(const char *method, apr_size_t len) +{ + /* Note: the following code was generated by the "shilka" tool from + the "cocom" parsing/compilation toolkit. It is an optimized lookup + based on analysis of the input keywords. Postprocessing was done + on the shilka output, but the basic structure and analysis is + from there. Should new HTTP methods be added, then manual insertion + into this code is fine, or simply re-running the shilka tool on + the appropriate input. */ + + /* Note: it is also quite reasonable to just use our method_registry, + but I'm assuming (probably incorrectly) we want more speed here + (based on the optimizations the previous code was doing). */ + + switch (len) + { + case 3: + switch (method[0]) + { + case 'P': + return (method[1] == 'U' + && method[2] == 'T' + ? M_PUT : UNKNOWN_METHOD); + case 'G': + return (method[1] == 'E' + && method[2] == 'T' + ? M_GET : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 4: + switch (method[0]) + { + case 'P': + return (method[1] == 'O' + && method[2] == 'S' + && method[3] == 'T' + ? M_POST : UNKNOWN_METHOD); + case 'M': + return (method[1] == 'O' + && method[2] == 'V' + && method[3] == 'E' + ? M_MOVE : UNKNOWN_METHOD); + case 'L': + return (method[1] == 'O' + && method[2] == 'C' + && method[3] == 'K' + ? M_LOCK : UNKNOWN_METHOD); + case 'C': + return (method[1] == 'O' + && method[2] == 'P' + && method[3] == 'Y' + ? M_COPY : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 5: + switch (method[2]) + { + case 'T': + return (memcmp(method, "PATCH", 5) == 0 + ? M_PATCH : UNKNOWN_METHOD); + case 'R': + return (memcmp(method, "MERGE", 5) == 0 + ? M_MERGE : UNKNOWN_METHOD); + case 'C': + return (memcmp(method, "MKCOL", 5) == 0 + ? M_MKCOL : UNKNOWN_METHOD); + case 'B': + return (memcmp(method, "LABEL", 5) == 0 + ? M_LABEL : UNKNOWN_METHOD); + case 'A': + return (memcmp(method, "TRACE", 5) == 0 + ? M_TRACE : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 6: + switch (method[0]) + { + case 'U': + switch (method[5]) + { + case 'K': + return (memcmp(method, "UNLOCK", 6) == 0 + ? M_UNLOCK : UNKNOWN_METHOD); + case 'E': + return (memcmp(method, "UPDATE", 6) == 0 + ? M_UPDATE : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + case 'R': + return (memcmp(method, "REPORT", 6) == 0 + ? M_REPORT : UNKNOWN_METHOD); + case 'D': + return (memcmp(method, "DELETE", 6) == 0 + ? M_DELETE : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 7: + switch (method[1]) + { + case 'P': + return (memcmp(method, "OPTIONS", 7) == 0 + ? M_OPTIONS : UNKNOWN_METHOD); + case 'O': + return (memcmp(method, "CONNECT", 7) == 0 + ? M_CONNECT : UNKNOWN_METHOD); + case 'H': + return (memcmp(method, "CHECKIN", 7) == 0 + ? M_CHECKIN : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 8: + switch (method[0]) + { + case 'P': + return (memcmp(method, "PROPFIND", 8) == 0 + ? M_PROPFIND : UNKNOWN_METHOD); + case 'C': + return (memcmp(method, "CHECKOUT", 8) == 0 + ? M_CHECKOUT : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 9: + return (memcmp(method, "PROPPATCH", 9) == 0 + ? M_PROPPATCH : UNKNOWN_METHOD); + + case 10: + switch (method[0]) + { + case 'U': + return (memcmp(method, "UNCHECKOUT", 10) == 0 + ? M_UNCHECKOUT : UNKNOWN_METHOD); + case 'M': + return (memcmp(method, "MKACTIVITY", 10) == 0 + ? M_MKACTIVITY : UNKNOWN_METHOD); + default: + return UNKNOWN_METHOD; + } + + case 11: + return (memcmp(method, "MKWORKSPACE", 11) == 0 + ? M_MKWORKSPACE : UNKNOWN_METHOD); + + case 15: + return (memcmp(method, "VERSION-CONTROL", 15) == 0 + ? M_VERSION_CONTROL : UNKNOWN_METHOD); + + case 16: + return (memcmp(method, "BASELINE-CONTROL", 16) == 0 + ? M_BASELINE_CONTROL : UNKNOWN_METHOD); + + default: + return UNKNOWN_METHOD; + } + + /* NOTREACHED */ } /* Get the method number associated with the given string, assumed to @@ -382,84 +602,16 @@ AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname) */ AP_DECLARE(int) ap_method_number_of(const char *method) { - int *methnum = NULL; + int len = strlen(method); + int which = lookup_builtin_method(method, len); - switch (*method) { - case 'H': - if (strcmp(method, "HEAD") == 0) { - return M_GET; /* see header_only in request_rec */ - } - break; - case 'G': - if (strcmp(method, "GET") == 0) { - return M_GET; - } - break; - case 'P': - if (strcmp(method, "POST") == 0) { - return M_POST; - } - if (strcmp(method, "PUT") == 0) { - return M_PUT; - } - if (strcmp(method, "PATCH") == 0) { - return M_PATCH; - } - if (strcmp(method, "PROPFIND") == 0) { - return M_PROPFIND; - } - if (strcmp(method, "PROPPATCH") == 0) { - return M_PROPPATCH; - } - break; - case 'D': - if (strcmp(method, "DELETE") == 0) { - return M_DELETE; - } - break; - case 'C': - if (strcmp(method, "CONNECT") == 0) { - return M_CONNECT; - } - if (strcmp(method, "COPY") == 0) { - return M_COPY; - } - break; - case 'M': - if (strcmp(method, "MKCOL") == 0) { - return M_MKCOL; - } - if (strcmp(method, "MOVE") == 0) { - return M_MOVE; - } - break; - case 'O': - if (strcmp(method, "OPTIONS") == 0) { - return M_OPTIONS; - } - break; - case 'T': - if (strcmp(method, "TRACE") == 0) { - return M_TRACE; - } - break; - case 'L': - if (strcmp(method, "LOCK") == 0) { - return M_LOCK; - } - break; - case 'U': - if (strcmp(method, "UNLOCK") == 0) { - return M_UNLOCK; - } - break; - } + if (which != UNKNOWN_METHOD) + return which; /* check if the method has been dynamically registered */ if (methods_registry != NULL) { - methnum = (int*)apr_hash_get(methods_registry, - method, - APR_HASH_KEY_STRING); + int *methnum = apr_hash_get(methods_registry, method, len); + if (methnum != NULL) { return *methnum; } @@ -469,49 +621,25 @@ AP_DECLARE(int) ap_method_number_of(const char *method) } /* - * Turn a known method number into a name. Doesn't work for - * extension methods, obviously. + * Turn a known method number into a name. */ -AP_DECLARE(const char *) ap_method_name_of(int methnum) +AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum) { - static const char *AP_HTTP_METHODS[METHODS] = { NULL }; + apr_hash_index_t *hi = apr_hash_first(p, methods_registry); - /* - * This is ugly, but the previous incantation made Windows C - * varf. I'm not even sure it was ANSI C. However, ugly as it - * is, this works, and we only have to do it once. - */ - if (AP_HTTP_METHODS[0] == NULL) { - AP_HTTP_METHODS[M_GET] = "GET"; - AP_HTTP_METHODS[M_PUT] = "PUT"; - AP_HTTP_METHODS[M_POST] = "POST"; - AP_HTTP_METHODS[M_DELETE] = "DELETE"; - AP_HTTP_METHODS[M_CONNECT] = "CONNECT"; - AP_HTTP_METHODS[M_OPTIONS] = "OPTIONS"; - AP_HTTP_METHODS[M_TRACE] = "TRACE"; - AP_HTTP_METHODS[M_PATCH] = "PATCH"; - AP_HTTP_METHODS[M_PROPFIND] = "PROPFIND"; - AP_HTTP_METHODS[M_PROPPATCH] = "PROPPATCH"; - AP_HTTP_METHODS[M_MKCOL] = "MKCOL"; - AP_HTTP_METHODS[M_COPY] = "COPY"; - AP_HTTP_METHODS[M_MOVE] = "MOVE"; - AP_HTTP_METHODS[M_LOCK] = "LOCK"; - AP_HTTP_METHODS[M_UNLOCK] = "UNLOCK"; - AP_HTTP_METHODS[M_INVALID] = NULL; - /* - * Since we're using symbolic names, make sure we only do - * this once by forcing a value into the first slot IFF it's - * still NULL. - */ - if (AP_HTTP_METHODS[0] == NULL) { - AP_HTTP_METHODS[0] = "INVALID"; - } - } + /* scan through the hash table, looking for a value that matches + the provided method number. */ + for (; hi; hi = apr_hash_next(hi)) { + const void *key; + void *val; - if ((methnum == M_INVALID) || (methnum >= METHODS)) { - return NULL; + apr_hash_this(hi, &key, NULL, &val); + if (*(int *)val == methnum) + return key; } - return AP_HTTP_METHODS[methnum]; + + /* it wasn't found in the hash */ + return NULL; } static long get_chunk_size(char *); @@ -955,25 +1083,32 @@ static char *make_allow(request_rec *r) { char *list; apr_int64_t mask; + apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *)); + apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry); mask = r->allowed_methods->method_mask; - list = apr_pstrcat(r->pool, - (mask & (AP_METHOD_BIT << M_GET)) ? ", GET, HEAD" : "", - (mask & (AP_METHOD_BIT << M_POST)) ? ", POST" : "", - (mask & (AP_METHOD_BIT << M_PUT)) ? ", PUT" : "", - (mask & (AP_METHOD_BIT << M_DELETE)) ? ", DELETE" : "", - (mask & (AP_METHOD_BIT << M_CONNECT)) ? ", CONNECT" : "", - (mask & (AP_METHOD_BIT << M_OPTIONS)) ? ", OPTIONS" : "", - (mask & (AP_METHOD_BIT << M_PATCH)) ? ", PATCH" : "", - (mask & (AP_METHOD_BIT << M_PROPFIND)) ? ", PROPFIND" : "", - (mask & (AP_METHOD_BIT << M_PROPPATCH)) ? ", PROPPATCH" : "", - (mask & (AP_METHOD_BIT << M_MKCOL)) ? ", MKCOL" : "", - (mask & (AP_METHOD_BIT << M_COPY)) ? ", COPY" : "", - (mask & (AP_METHOD_BIT << M_MOVE)) ? ", MOVE" : "", - (mask & (AP_METHOD_BIT << M_LOCK)) ? ", LOCK" : "", - (mask & (AP_METHOD_BIT << M_UNLOCK)) ? ", UNLOCK" : "", - ", TRACE", - NULL); + + for (; hi; hi = apr_hash_next(hi)) { + const void *key; + void *val; + + apr_hash_this(hi, &key, NULL, &val); + if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) { + *(const char **)apr_array_push(allow) = key; + + /* the M_GET method actually refers to two methods */ + if (*(int *)val == M_GET) + *(const char **)apr_array_push(allow) = "HEAD"; + } + } + + /* TRACE is always allowed */ + *(const char **)apr_array_push(allow) = "TRACE"; + + list = apr_array_pstrcat(r->pool, allow, ','); + + /* ### this is rather annoying. we should enforce registration of + ### these methods */ if ((mask & (AP_METHOD_BIT << M_INVALID)) && (r->allowed_methods->method_list != NULL) && (r->allowed_methods->method_list->nelts != 0)) { @@ -987,11 +1122,8 @@ static char *make_allow(request_rec *r) list = apr_pstrcat(r->pool, list, ", ", xmethod[i], NULL); } } - /* - * Space past the leading ", ". Wastes two bytes, but that's better - * than futzing around to find the actual length. - */ - return list + 2; + + return list; } AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)