diff options
-rw-r--r-- | test/conftest.py | 7 | ||||
-rw-r--r-- | test/modules/http2/test_200_header_invalid.py | 8 | ||||
-rw-r--r-- | test/modules/http2/test_700_load_get.py | 2 | ||||
-rw-r--r-- | test/modules/http2/test_710_load_post_static.py | 3 | ||||
-rw-r--r-- | test/modules/http2/test_711_load_post_cgi.py | 3 | ||||
-rw-r--r-- | test/pyhttpd/env.py | 52 | ||||
-rw-r--r-- | test/pyhttpd/mod_aptest/mod_aptest.c | 66 | ||||
-rw-r--r-- | test/pyhttpd/nghttp.py | 10 |
8 files changed, 135 insertions, 16 deletions
diff --git a/test/conftest.py b/test/conftest.py index 3472e969f2..2ae35f34ed 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,6 +1,8 @@ import sys import os +import pytest + sys.path.append(os.path.join(os.path.dirname(__file__), '.')) from pyhttpd.env import HttpdTestEnv @@ -21,4 +23,9 @@ def pytest_generate_tests(metafunc): metafunc.fixturenames.append('tmp_ct') metafunc.parametrize('repeat', range(count)) +@pytest.fixture(autouse=True, scope="function") +def _function_scope(env, request): + env.set_current_test_name(request.node.name) + yield + env.set_current_test_name(None) diff --git a/test/modules/http2/test_200_header_invalid.py b/test/modules/http2/test_200_header_invalid.py index 5f8c976808..44ad69bc6b 100644 --- a/test/modules/http2/test_200_header_invalid.py +++ b/test/modules/http2/test_200_header_invalid.py @@ -88,7 +88,8 @@ class TestInvalidHeaders: def test_h2_200_12(self, env): url = env.mkurl("https", "cgi", "/") opt = [] - for i in range(98): # curl sends 2 headers itself (user-agent and accept) + # curl sends 3 headers itself (user-agent, accept, and our AP-Test-Name) + for i in range(97): opt += ["-H", "x: 1"] r = env.curl_get(url, options=opt) assert r.response["status"] == 200 @@ -100,8 +101,9 @@ class TestInvalidHeaders: def test_h2_200_13(self, env): url = env.mkurl("https", "cgi", "/") opt = [] - for i in range(98): # curl sends 2 headers itself (user-agent and accept) - opt += ["-H", "x{0}: 1".format(i)] + # curl sends 3 headers itself (user-agent, accept, and our AP-Test-Name) + for i in range(97): + opt += ["-H", f"x{i}: 1"] r = env.curl_get(url, options=opt) assert r.response["status"] == 200 r = env.curl_get(url, options=(opt + ["-H", "y: 2"])) diff --git a/test/modules/http2/test_700_load_get.py b/test/modules/http2/test_700_load_get.py index d1121a6065..9ee8898dfd 100644 --- a/test/modules/http2/test_700_load_get.py +++ b/test/modules/http2/test_700_load_get.py @@ -30,6 +30,7 @@ class TestLoadGet: 1000, 80000 ]) def test_h2_700_10(self, env, start): + assert env.is_live() text = "X" chunk = 32 for n in range(0, 5): @@ -47,6 +48,7 @@ class TestLoadGet: 1, 2, 16, 32 ]) def test_h2_700_11(self, env, conns): + assert env.is_live() text = "X" start = 1200 chunk = 64 diff --git a/test/modules/http2/test_710_load_post_static.py b/test/modules/http2/test_710_load_post_static.py index fd1b5abe80..ad8ae96aef 100644 --- a/test/modules/http2/test_710_load_post_static.py +++ b/test/modules/http2/test_710_load_post_static.py @@ -26,6 +26,7 @@ class TestLoadPostStatic: # test POST on static file, slurped in by server def test_h2_710_00(self, env, repeat): + assert env.is_live() url = env.mkurl("https", "test1", "/index.html") n = 10 m = 1 @@ -38,6 +39,7 @@ class TestLoadPostStatic: self.check_h2load_ok(env, r, n) def test_h2_710_01(self, env, repeat): + assert env.is_live() url = env.mkurl("https", "test1", "/index.html") n = 1000 m = 100 @@ -50,6 +52,7 @@ class TestLoadPostStatic: self.check_h2load_ok(env, r, n) def test_h2_710_02(self, env, repeat): + assert env.is_live() url = env.mkurl("https", "test1", "/index.html") n = 100 m = 50 diff --git a/test/modules/http2/test_711_load_post_cgi.py b/test/modules/http2/test_711_load_post_cgi.py index 94c16002a7..82529d1764 100644 --- a/test/modules/http2/test_711_load_post_cgi.py +++ b/test/modules/http2/test_711_load_post_cgi.py @@ -26,6 +26,7 @@ class TestLoadCgi: # test POST on cgi, where input is read def test_h2_711_10(self, env, repeat): + assert env.is_live() url = env.mkurl("https", "test1", "/echo.py") n = 100 m = 5 @@ -41,6 +42,7 @@ class TestLoadCgi: # test POST on cgi via http/1.1 proxy, where input is read def test_h2_711_11(self, env, repeat): + assert env.is_live() url = env.mkurl("https", "test1", "/proxy/echo.py") n = 100 m = 5 @@ -56,6 +58,7 @@ class TestLoadCgi: # test POST on cgi via h2proxy, where input is read def test_h2_711_12(self, env, repeat): + assert env.is_live() url = env.mkurl("https", "test1", "/h2proxy/echo.py") n = 100 m = 5 diff --git a/test/pyhttpd/env.py b/test/pyhttpd/env.py index 45f6d2f066..991ead9e11 100644 --- a/test/pyhttpd/env.py +++ b/test/pyhttpd/env.py @@ -90,6 +90,7 @@ class HttpdTestSetup: self.add_modules([self.env.ssl_module]) self._make_modules_conf() self._make_htdocs() + self._add_aptest() self.env.clear_curl_headerfiles() def _make_dirs(self): @@ -179,6 +180,21 @@ class HttpdTestSetup: st = os.stat(py_file) os.chmod(py_file, st.st_mode | stat.S_IEXEC) + def _add_aptest(self): + local_dir = os.path.dirname(inspect.getfile(HttpdTestSetup)) + p = subprocess.run([self.env.apxs, '-c', 'mod_aptest.c'], + capture_output=True, + cwd=os.path.join(local_dir, 'mod_aptest')) + rv = p.returncode + if rv != 0: + log.error(f"compiling mod_aptest failed: {p.stderr}") + raise Exception(f"compiling mod_aptest failed: {p.stderr}") + + modules_conf = os.path.join(self.env.server_dir, 'conf/modules.conf') + with open(modules_conf, 'a') as fd: + # load our test module which is not installed + fd.write(f"LoadModule aptest_module \"{local_dir}/mod_aptest/.libs/mod_aptest.so\"\n") + class HttpdTestEnv: @@ -255,7 +271,7 @@ class HttpdTestEnv: self._verbosity = pytestconfig.option.verbose if pytestconfig is not None else 0 self._test_conf = os.path.join(self._server_conf_dir, "test.conf") self._httpd_base_conf = [] - self._httpd_log_modules = [] + self._httpd_log_modules = ['aptest'] self._log_interesting = None self._setup = None @@ -269,6 +285,8 @@ class HttpdTestEnv: self._verify_certs = False self._curl_headerfiles_n = 0 + self._h2load_version = None + self._current_test = None def add_httpd_conf(self, lines: List[str]): self._httpd_base_conf.extend(lines) @@ -403,6 +421,13 @@ class HttpdTestEnv: return self._ca @property + def current_test_name(self) -> str: + return self._current_test + + def set_current_test_name(self, val) -> None: + self._current_test = val + + @property def apachectl_stderr(self): return self._apachectl_stderr @@ -416,6 +441,7 @@ class HttpdTestEnv: return [] def _versiontuple(self, v): + v = re.sub(r'(\d+\.\d+(\.\d+)?)(-\S+)?', r'\1', v) return tuple(map(int, v.split('.'))) def httpd_is_at_least(self, minv): @@ -428,14 +454,16 @@ class HttpdTestEnv: def h2load_is_at_least(self, minv): if not self.has_h2load(): return False - p = subprocess.run([self._h2load, '--version'], capture_output=True, text=True) - if p.returncode != 0: - return False - s = p.stdout.strip() - m = re.match(r'h2load nghttp2/(\S+)', s) - if m: - hv = self._versiontuple(m.group(1)) - return hv >= self._versiontuple(minv) + if self._h2load_version is None: + p = subprocess.run([self._h2load, '--version'], capture_output=True, text=True) + if p.returncode != 0: + return False + s = p.stdout.strip() + m = re.match(r'h2load nghttp2/(\S+)', s) + if m: + self._h2load_version = self._versiontuple(m.group(1)) + if self._h2load_version is not None: + return self._h2load_version >= self._versiontuple(minv) return False def has_nghttp(self): @@ -627,6 +655,9 @@ class HttpdTestEnv: if ca_pem: args.extend(["--cacert", ca_pem]) + if self._current_test is not None: + args.extend(["-H", f'AP-Test-Name: {self._current_test}']) + if force_resolve and u.hostname and u.hostname != 'localhost' \ and u.hostname != self._httpd_addr \ and not re.match(r'^(\d+|\[|:).*', u.hostname): @@ -739,7 +770,8 @@ class HttpdTestEnv: return -1 def nghttp(self): - return Nghttp(self._nghttp, connect_addr=self._httpd_addr, tmp_dir=self.gen_dir) + return Nghttp(self._nghttp, connect_addr=self._httpd_addr, + tmp_dir=self.gen_dir, test_name=self._current_test) def h2load_status(self, run: ExecResult): stats = {} diff --git a/test/pyhttpd/mod_aptest/mod_aptest.c b/test/pyhttpd/mod_aptest/mod_aptest.c new file mode 100644 index 0000000000..d1a8e0533d --- /dev/null +++ b/test/pyhttpd/mod_aptest/mod_aptest.c @@ -0,0 +1,66 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <apr_optional.h> +#include <apr_optional_hooks.h> +#include <apr_strings.h> +#include <apr_cstr.h> +#include <apr_want.h> + +#include <httpd.h> +#include <http_protocol.h> +#include <http_request.h> +#include <http_log.h> + +static void aptest_hooks(apr_pool_t *pool); + +AP_DECLARE_MODULE(aptest) = { + STANDARD20_MODULE_STUFF, + NULL, /* func to create per dir config */ + NULL, /* func to merge per dir config */ + NULL, /* func to create per server config */ + NULL, /* func to merge per server config */ + NULL, /* command handlers */ + aptest_hooks, +#if defined(AP_MODULE_FLAG_NONE) + AP_MODULE_FLAG_ALWAYS_MERGE +#endif +}; + + +static int aptest_post_read_request(request_rec *r) +{ + const char *test_name = apr_table_get(r->headers_in, "AP-Test-Name"); + if (test_name) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "test[%s]: %s", + test_name, r->the_request); + } + return DECLINED; +} + +/* Install this module into the apache2 infrastructure. + */ +static void aptest_hooks(apr_pool_t *pool) +{ + ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, + "installing hooks and handlers"); + + /* test case monitoring */ + ap_hook_post_read_request(aptest_post_read_request, NULL, + NULL, APR_HOOK_MIDDLE); + +} + diff --git a/test/pyhttpd/nghttp.py b/test/pyhttpd/nghttp.py index 84b8f20c6f..6dea97b55c 100644 --- a/test/pyhttpd/nghttp.py +++ b/test/pyhttpd/nghttp.py @@ -15,10 +15,12 @@ def _get_path(x): class Nghttp: - def __init__(self, path, connect_addr=None, tmp_dir="/tmp"): + def __init__(self, path, connect_addr=None, tmp_dir="/tmp", + test_name: str = None): self.NGHTTP = path self.CONNECT_ADDR = connect_addr self.TMP_DIR = tmp_dir + self._test_name = test_name @staticmethod def get_stream(streams, sid): @@ -104,7 +106,7 @@ class Nghttp: body += m.group(1) s = self.get_stream(streams, m.group(2)) if s: - print("stream %d: recv %d header" % (s["id"], len(s["header"]))) + print("stream %d: recv %d header" % (s["id"], len(s["header"]))) response = s["response"] hkey = "header" if "header" in response: @@ -194,9 +196,11 @@ class Nghttp: output["response"] = streams[main_stream]["response"] output["paddings"] = streams[main_stream]["paddings"] return output - + def _raw(self, url, timeout, options): args = ["-v"] + if self._test_name is not None: + args.append(f'--header=AP-Test-Name: {self._test_name}') if options: args.extend(options) r = self._baserun(url, timeout, args) |