/* 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");
int result;
int label_allowed;
ap_xml_doc *doc;
- ap_text *t;
dav_error *err;
/* If no versioning provider, decline the request */
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);
}
#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;
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.
*/
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)
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
*/
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;
}
}
/*
- * 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 *);
{
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)) {
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)