diff --git a/include/http_request.h b/include/http_request.h index 7d5bed28f9..803eb15c95 100644 --- a/include/http_request.h +++ b/include/http_request.h @@ -106,7 +106,23 @@ API_EXPORT(void) ap_internal_redirect_handler(const char *new_uri, request_rec * API_EXPORT(int) ap_some_auth_required(request_rec *r); API_EXPORT(int) ap_is_initial_req(request_rec *r); API_EXPORT(void) ap_update_mtime(request_rec *r, apr_time_t dependency_mtime); - +/** + * Add one or more methods to the list permitted to access the resource. + * Usually executed by the content handler before the response header is + * sent, but sometimes invoked at an earlier phase if a module knows it + * can set the list authoritatively. Note that the methods are ADDED + * to any already permitted unless the reset flag is non-zero. The + * list is used to generate the Allow response header field when it + * is needed. + * @param r The pointer to the request identifying the resource. + * @param reset Boolean flag indicating whether this list should + * completely replace any current settings. + * @param ... A NULL-terminated list of strings, each identifying a + * method name to add. + * @return None. + * @deffunc void ap_allow_methods(request_rec *r, int reset, ...) + */ +API_EXPORT(void) ap_allow_methods(request_rec *r, int reset, ...); #ifdef CORE_PRIVATE /* Function called by main.c to handle first-level request */ void ap_process_request(request_rec *); diff --git a/include/httpd.h b/include/httpd.h index 26d9c227a2..b7ca03bb1d 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -666,6 +666,7 @@ struct request_rec { * handler can't be installed by mod_actions. */ int allowed; /* Allowed methods - for 405, OPTIONS, etc */ + apr_array_header_t *allowed_xmethods; /* Array of extension methods */ /** byte count in stream is for body */ int sent_bodyct; diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index 0cfb497b7e..8a687aa0b7 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -1572,23 +1572,41 @@ static void terminate_header(request_rec *r) */ static char *make_allow(request_rec *r) { - return 2 + apr_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_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); + char *list; + + list = apr_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_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); + if ((r->allowed & (1 << M_INVALID)) && (r->allowed_xmethods->nelts)) { + int i; + char **xmethod = (char **) r->allowed_xmethods->elts; + + /* + * Append all of the elements of r->allowed_xmethods + */ + for (i = 0; i < r->allowed_xmethods->nelts; ++i) { + list = ap_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; } API_EXPORT(int) ap_send_http_trace(request_rec *r) diff --git a/modules/http/http_request.c b/modules/http/http_request.c index 2049386e2d..6fc8be11bd 100644 --- a/modules/http/http_request.c +++ b/modules/http/http_request.c @@ -79,6 +79,9 @@ #include "apr_strings.h" #include "apr_file_io.h" #include "apr_fnmatch.h" +#ifdef APR_HAVE_STDARG_H +#include +#endif AP_HOOK_STRUCT( AP_HOOK_LINK(translate_name) @@ -1425,3 +1428,46 @@ API_EXPORT(void) ap_update_mtime(request_rec *r, apr_time_t dependency_mtime) r->mtime = dependency_mtime; } } + +API_EXPORT(void) ap_allow_methods(request_rec *r, int reset, ...) { + int mnum; + const char *method; + const char **xmethod; + va_list methods; + + /* + * Get rid of any current settings if requested; not just the + * well-known methods but any extensions as well. + */ + if (reset) { + r->allowed = 0; + if (r->allowed_xmethods != NULL) { + r->allowed_xmethods->nelts = 0; + } + } + + va_start(methods, reset); + while ((method = va_arg(methods, const char *)) != NULL) { + /* + * Look up our internal number for this particular method. + * Even if it isn't one of the ones we know about, the return + * value is used in the same way. + */ + mnum = ap_method_number_of(method); + r->allowed |= (1 << mnum); + /* + * Now, if we don't know about it, we regard it as an + * extension method. Add it to our array of such. This means + * that anything that checks for M_INVALID needs to make an + * additional check of this array if it *is* invalid. + */ + if (mnum == M_INVALID) { + if (r->allowed_xmethods == NULL) { + r->allowed_xmethods = apr_make_array(r->pool, 2, + sizeof(char *)); + } + xmethod = (const char **) apr_push_array(r->allowed_xmethods); + *xmethod = method; + } + } +}