1
0
mirror of https://github.com/apache/httpd.git synced 2026-10-23 22:49:01 +03:00

* modules/http/http_protocol.c (make_allow): Don't check for the

M_INVALID bit being set in the method mask if unregistered methods
  are allowed. That bit is never set by ap_method_list_add(), and this
  branch adding unregistered methods to Allow: never followed.

* test/pyhttpd/mod_aptest/mod_aptest.c (aptest_allow_methods): New; pass
  the methods named in an AP-Test-Allow-Methods request header to
  ap_allow_methods().

* test/modules/core/test_009_allow_methods.py: New test suite.

GitHub: closes #758, closes #428
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1938291 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Joe Orton
2026-09-16 14:27:37 +00:00
parent 1422c19ea3
commit d3a6a151c5
3 changed files with 71 additions and 2 deletions

View File

@@ -897,8 +897,7 @@ static char *make_allow(request_rec *r)
/* ### this is rather annoying. we should enforce registration of
### these methods */
if ((mask & (AP_METHOD_BIT << M_INVALID))
&& (r->allowed_methods->method_list != NULL)
if (r->allowed_methods->method_list != NULL
&& (r->allowed_methods->method_list->nelts != 0)) {
apr_array_cat(allow, r->allowed_methods->method_list);
}

View File

@@ -0,0 +1,46 @@
import pytest
from pyhttpd.conf import HttpdConf
class TestAllowMethods:
@pytest.fixture(autouse=True, scope='class')
def _class_scope(self, env):
conf = HttpdConf(env)
conf.add_vhost_test1()
conf.install()
assert env.apache_restart() == 0
# mod_aptest passes the methods named in AP-Test-Allow-Methods to
# ap_allow_methods() from a fixups hook; the default handler then
# merges its own and answers OPTIONS with the Allow header.
def options(self, env, methods=None):
options = ['-X', 'OPTIONS']
if methods is not None:
options.extend(['-H', f'AP-Test-Allow-Methods: {methods}'])
r = env.curl_get(env.mkurl("http", "test1", "/index.html"),
options=options)
assert r.response, f"no response: {r.stderr}"
assert r.response["status"] == 200
return [m.strip() for m in r.response["header"]["allow"].split(',')]
def test_core_009_01(self, env):
allow = self.options(env)
for method in ["GET", "POST", "OPTIONS"]:
assert method in allow, f"expected {method} in Allow: {allow}"
def test_core_009_02(self, env):
allow = self.options(env, "DELETE")
assert "DELETE" in allow, f"expected DELETE in Allow: {allow}"
# An extension method reaches make_allow() only through method_list,
# which is emitted just when the M_INVALID bit is set in method_mask.
def test_core_009_03(self, env):
allow = self.options(env, "FROBNICATE")
assert "FROBNICATE" in allow, f"expected FROBNICATE in Allow: {allow}"
def test_core_009_04(self, env):
allow = self.options(env, "DELETE, FROBNICATE")
for method in ["DELETE", "FROBNICATE"]:
assert method in allow, f"expected {method} in Allow: {allow}"

View File

@@ -51,6 +51,29 @@ static int aptest_post_read_request(request_rec *r)
return DECLINED;
}
/*
* Register the methods named in an "AP-Test-Allow-Methods" request header
* via ap_allow_methods(), so a test can check how they are reflected in the
* Allow response header.
*/
static int aptest_allow_methods(request_rec *r)
{
const char *methods = apr_table_get(r->headers_in, "AP-Test-Allow-Methods");
char *list, *name, *last;
if (methods == NULL) {
return DECLINED;
}
list = apr_pstrdup(r->pool, methods);
for (name = apr_strtok(list, ", ", &last); name != NULL;
name = apr_strtok(NULL, ", ", &last)) {
ap_allow_methods(r, MERGE_ALLOW, name, NULL);
}
return DECLINED;
}
/* Install this module into the apache2 infrastructure.
*/
static void aptest_hooks(apr_pool_t *pool)
@@ -61,6 +84,7 @@ static void aptest_hooks(apr_pool_t *pool)
/* test case monitoring */
ap_hook_post_read_request(aptest_post_read_request, NULL,
NULL, APR_HOOK_MIDDLE);
ap_hook_fixups(aptest_allow_methods, NULL, NULL, APR_HOOK_MIDDLE);
}