Return-Path: Delivered-To: new-httpd-archive@hyperreal.org Received: (qmail 7569 invoked by uid 6000); 31 Oct 1998 09:53:04 -0000 Received: (qmail 7562 invoked from network); 31 Oct 1998 09:53:03 -0000 Received: from paris.ics.uci.edu (mmdf@128.195.1.50) by taz.hyperreal.org with SMTP; 31 Oct 1998 09:53:03 -0000 Received: from kiwi.ics.uci.edu by paris.ics.uci.edu id aa15713; 31 Oct 98 1:50 PST To: new-httpd@apache.org Subject: [PATCH] Enable all WebDAV methods In-reply-to: Your message of "Thu, 29 Oct 1998 16:31:05 PST." <9810291631.aa28818@paris.ics.uci.edu> Date: Sat, 31 Oct 1998 01:50:14 -0800 From: "Roy T. Fielding" Message-ID: <9810310150.aa15713@paris.ics.uci.edu> Sender: new-httpd-owner@apache.org Precedence: bulk Reply-To: new-httpd@apache.org This needs to be tested before being committed. I need someone else to do it since I am leaving in the morning for a conference trip in Orlando and won't have net access again til Thursday night. *) Enabled all of the WebDAV method names for use by third-party modules, Limit, and Script directives. That includes PATCH, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, and UNLOCK. Improved mod_actions.c so that it can use any of the methods defined in httpd.h. Added ap_method_number_of(method) for getting the internal method number. [Roy Fielding] It also requires an MMN bump. I could abstract it more by making the methods a configurable array of pointers to static method name strings and then replacing the functions in http_protocol.c with simple loops through the array (the performance difference is negligible provided that the array is ordered by method use frequency). But that can wait. ....Roy Index: include/http_protocol.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/http_protocol.h,v retrieving revision 1.45 diff -u -r1.45 http_protocol.h --- http_protocol.h 1998/08/09 14:33:10 1.45 +++ http_protocol.h 1998/10/31 09:35:05 @@ -209,6 +209,11 @@ CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri); +/* Get the method number associated with the given string, assumed to + * contain an HTTP method. Returns M_INVALID if not recognized. + */ +API_EXPORT(int) ap_method_number_of(const char *method); + #ifdef __cplusplus } #endif Index: include/httpd.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/httpd.h,v retrieving revision 1.249 diff -u -r1.249 httpd.h --- httpd.h 1998/10/28 19:26:28 1.249 +++ httpd.h 1998/10/31 09:35:05 @@ -531,16 +531,27 @@ ((x) == HTTP_SERVICE_UNAVAILABLE) || \ ((x) == HTTP_NOT_IMPLEMENTED)) - -#define METHODS 8 -#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_INVALID 7 +/* 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. + */ +#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_INVALID 7 +#define M_PATCH 8 +#define M_PROPFIND 9 +#define M_PROPPATCH 10 +#define M_MKCOL 11 +#define M_COPY 12 +#define M_MOVE 13 +#define M_LOCK 14 +#define M_UNLOCK 15 +#define METHODS 16 #define CGI_MAGIC_TYPE "application/x-httpd-cgi" #define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html" Index: main/http_core.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_core.c,v retrieving revision 1.238 diff -u -r1.238 http_core.c --- http_core.c 1998/10/30 03:08:55 1.238 +++ http_core.c 1998/10/31 09:35:06 @@ -1068,28 +1068,18 @@ while (limited_methods[0]) { char *method = ap_getword_conf(cmd->pool, &limited_methods); - if (!strcmp(method, "GET")) { - limited |= (1 << M_GET); - } - else if (!strcmp(method, "PUT")) { - limited |= (1 << M_PUT); - } - else if (!strcmp(method, "POST")) { - limited |= (1 << M_POST); - } - else if (!strcmp(method, "DELETE")) { - limited |= (1 << M_DELETE); - } - else if (!strcmp(method, "CONNECT")) { - limited |= (1 << M_CONNECT); - } - else if (!strcmp(method, "OPTIONS")) { - limited |= (1 << M_OPTIONS); - } - else { - return ap_pstrcat(cmd->pool, "unknown method \"", - method, "\" in ", NULL); - } + int methnum = ap_method_number_of(method); + + if (methnum == M_TRACE) { + return "TRACE cannot be controlled by "; + } + else if (methnum == M_INVALID) { + return ap_pstrcat(cmd->pool, "unknown method \"", + method, "\" in ", NULL); + } + else { + limited |= (1 << methnum); + } } cmd->limited = limited; Index: main/http_protocol.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_protocol.c,v retrieving revision 1.247 diff -u -r1.247 http_protocol.c --- http_protocol.c 1998/10/30 22:41:24 1.247 +++ http_protocol.c 1998/10/31 09:35:06 @@ -517,6 +517,72 @@ ap_gm_timestr_822(r->pool, mod_time)); } +/* Get the method number associated with the given string, assumed to + * contain an HTTP method. Returns M_INVALID if not recognized. + * + * This is the first step toward placing method names in a configurable + * list. Hopefully it (and other routines) can eventually be moved to + * something like a mod_http_methods.c, complete with config stuff. + */ +API_EXPORT(int) ap_method_number_of(const char *method) +{ + 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; + } + return M_INVALID; +} + /* Get a line of protocol input, including any continuation lines * caused by MIME folding (or broken clients) if fold != 0, and place it * in the buffer s, of size n bytes, without the ending newline. @@ -678,26 +744,11 @@ uri = ap_getword_white(r->pool, &ll); /* Provide quick information about the request method as soon as known */ - if (!strcmp(r->method, "HEAD")) { + + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'H') { r->header_only = 1; - r->method_number = M_GET; } - else if (!strcmp(r->method, "GET")) - r->method_number = M_GET; - else if (!strcmp(r->method, "POST")) - r->method_number = M_POST; - else if (!strcmp(r->method, "PUT")) - r->method_number = M_PUT; - else if (!strcmp(r->method, "DELETE")) - r->method_number = M_DELETE; - else if (!strcmp(r->method, "CONNECT")) - r->method_number = M_CONNECT; - else if (!strcmp(r->method, "OPTIONS")) - r->method_number = M_OPTIONS; - else if (!strcmp(r->method, "TRACE")) - r->method_number = M_TRACE; - else - r->method_number = M_INVALID; /* Will eventually croak. */ ap_parse_uri(r, uri); @@ -1237,13 +1288,22 @@ static char *make_allow(request_rec *r) { return 2 + ap_pstrcat(r->pool, - (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "", - (r->allowed & (1 << M_POST)) ? ", POST" : "", - (r->allowed & (1 << M_PUT)) ? ", PUT" : "", - (r->allowed & (1 << M_DELETE)) ? ", DELETE" : "", - (r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "", - ", TRACE", - NULL); + (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "", + (r->allowed & (1 << M_POST)) ? ", POST" : "", + (r->allowed & (1 << M_PUT)) ? ", PUT" : "", + (r->allowed & (1 << M_DELETE)) ? ", DELETE" : "", + (r->allowed & (1 << M_CONNECT)) ? ", CONNECT" : "", + (r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "", + (r->allowed & (1 << M_PATCH)) ? ", PATCH" : "", + (r->allowed & (1 << M_PROPFIND)) ? ", PROPFIND" : "", + (r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "", + (r->allowed & (1 << M_MKCOL)) ? ", MKCOL" : "", + (r->allowed & (1 << M_COPY)) ? ", COPY" : "", + (r->allowed & (1 << M_MOVE)) ? ", MOVE" : "", + (r->allowed & (1 << M_LOCK)) ? ", LOCK" : "", + (r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "", + ", TRACE", + NULL); } API_EXPORT(int) ap_send_http_trace(request_rec *r) Index: modules/standard/mod_actions.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_actions.c,v retrieving revision 1.28 diff -u -r1.28 mod_actions.c --- mod_actions.c 1998/08/06 17:30:53 1.28 +++ mod_actions.c 1998/10/31 09:35:07 @@ -56,7 +56,7 @@ */ /* - * mod_actions.c: executes scripts based on MIME type + * mod_actions.c: executes scripts based on MIME type or HTTP method * * by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c, * adapted by rst from original NCSA code by Rob McCool @@ -69,6 +69,12 @@ * requested. It sends the URL and file path of the requested document using * the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. * + * Script PUT /cgi-bin/script + * + * will activate /cgi-bin/script when a request is received with the + * HTTP method "PUT". The available method names are defined in httpd.h. + * If the method is GET, the script will only be activated if the requested + * URI includes query information (stuff after a ?-mark). */ #include "httpd.h" @@ -81,11 +87,8 @@ #include "util_script.h" typedef struct { - table *action_types; /* Added with Action... */ - char *get; /* Added with Script GET */ - char *post; /* Added with Script POST */ - char *put; /* Added with Script PUT */ - char *delete; /* Added with Script DELETE */ + table *action_types; /* Added with Action... */ + char *scripted[METHODS]; /* Added with Script... */ } action_dir_config; module action_module; @@ -96,10 +99,7 @@ (action_dir_config *) ap_palloc(p, sizeof(action_dir_config)); new->action_types = ap_make_table(p, 4); - new->get = NULL; - new->post = NULL; - new->put = NULL; - new->delete = NULL; + memset(new->scripted, 0, sizeof(new->scripted)); return new; } @@ -108,17 +108,17 @@ { action_dir_config *base = (action_dir_config *) basev; action_dir_config *add = (action_dir_config *) addv; - action_dir_config *new = - (action_dir_config *) ap_palloc(p, sizeof(action_dir_config)); + action_dir_config *new = (action_dir_config *) ap_palloc(p, + sizeof(action_dir_config)); + int i; new->action_types = ap_overlay_tables(p, add->action_types, base->action_types); - new->get = add->get ? add->get : base->get; - new->post = add->post ? add->post : base->post; - new->put = add->put ? add->put : base->put; - new->delete = add->delete ? add->delete : base->delete; - + for (i = 0; i < METHODS; ++i) { + new->scripted[i] = add->scripted[i] ? add->scripted[i] + : base->scripted[i]; + } return new; } @@ -129,19 +129,18 @@ return NULL; } -static const char *set_script(cmd_parms *cmd, action_dir_config * m, char *method, - char *script) +static const char *set_script(cmd_parms *cmd, action_dir_config * m, + char *method, char *script) { - if (!strcmp(method, "GET")) - m->get = script; - else if (!strcmp(method, "POST")) - m->post = script; - else if (!strcmp(method, "PUT")) - m->put = script; - else if (!strcmp(method, "DELETE")) - m->delete = script; + int methnum; + + methnum = ap_method_number_of(method); + if (methnum == M_TRACE) + return "TRACE not allowed for Script"; + else if (methnum == M_INVALID) + return "Unknown method type for Script"; else - return "Unknown method type for Script"; + m->scripted[methnum] = script; return NULL; } @@ -157,30 +156,28 @@ static int action_handler(request_rec *r) { - action_dir_config *conf = - (action_dir_config *) ap_get_module_config(r->per_dir_config, &action_module); + action_dir_config *conf = (action_dir_config *) + ap_get_module_config(r->per_dir_config, &action_module); const char *t, *action = r->handler ? r->handler : r->content_type; - const char *script = NULL; + const char *script; + int i; /* Set allowed stuff */ - if (conf->get) - r->allowed |= (1 << M_GET); - if (conf->post) - r->allowed |= (1 << M_POST); - if (conf->put) - r->allowed |= (1 << M_PUT); - if (conf->delete) - r->allowed |= (1 << M_DELETE); + for (i = 0; i < METHODS; ++i) { + if (conf->scripted[i]) + r->allowed |= (1 << i); + } /* First, check for the method-handling scripts */ - if ((r->method_number == M_GET) && r->args && conf->get) - script = conf->get; - else if ((r->method_number == M_POST) && conf->post) - script = conf->post; - else if ((r->method_number == M_PUT) && conf->put) - script = conf->put; - else if ((r->method_number == M_DELETE) && conf->delete) - script = conf->delete; + if (r->method_number == M_GET) { + if (r->args) + script = conf->scripted[M_GET]; + else + script = NULL; + } + else { + script = conf->scripted[r->method_number]; + } /* Check for looping, which can happen if the CGI script isn't */ if (script && r->prev && r->prev->prev)