svn commit: r1934976 - in httpd/httpd/trunk/test: modules modules/ssl pyhttpd
[email protected] Thu, 04 Jun 2026 09:40:10 -0000
| Newsgroups | gmane.comp.apache.cvs |
|---|---|
| Message-ID | <178056601077.2712543.17803515334461690662@svn03-he-fi> |
Author: ichristod
Date: Thu Jun 4 09:40:10 2026
New Revision: 1934976
Log:
mod_log_config: Add regression for SNI logging (r1927034)
Add SSL test env and SNI logging validation.
Github: closes #621
Added:
httpd/httpd/trunk/test/modules/__init__.py (contents, props changed)
httpd/httpd/trunk/test/modules/ssl/
httpd/httpd/trunk/test/modules/ssl/__init__.py (contents, props changed)
httpd/httpd/trunk/test/modules/ssl/conftest.py (contents, props changed)
httpd/httpd/trunk/test/modules/ssl/env.py (contents, props changed)
httpd/httpd/trunk/test/modules/ssl/test_001_sni.py (contents, props changed)
Modified:
httpd/httpd/trunk/test/pyhttpd/conf.py
httpd/httpd/trunk/test/pyhttpd/env.py
Added: httpd/httpd/trunk/test/modules/__init__.py
==============================================================================
Added: httpd/httpd/trunk/test/modules/ssl/__init__.py
==============================================================================
Added: httpd/httpd/trunk/test/modules/ssl/conftest.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ httpd/httpd/trunk/test/modules/ssl/conftest.py Thu Jun 4 09:40:10 2026 (r1934976)
@@ -0,0 +1,40 @@
+import logging
+import os
+import pytest
+import sys
+
+from .env import SSLTestEnv
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
+
+
+def pytest_report_header(config, start_path):
+ env = SSLTestEnv()
+ return f"mod_ssl [apache: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]"
+
+
[email protected](scope="package")
+def env(pytestconfig) -> SSLTestEnv:
+ level = logging.INFO
+ console = logging.StreamHandler()
+ console.setLevel(level)
+ console.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
+ logging.getLogger('').addHandler(console)
+ logging.getLogger('').setLevel(level=level)
+ env = SSLTestEnv(pytestconfig=pytestconfig)
+ env.setup_httpd()
+ env.apache_access_log_clear()
+ env.httpd_error_log.clear_log()
+ return env
+
+
[email protected](autouse=True, scope="package")
+def require_openssl(env):
+ if not env.has_tool("openssl"):
+ pytest.skip("openssl not installed")
+
+
[email protected](autouse=True, scope="package")
+def _stop_package_scope(env):
+ yield
+ assert env.apache_stop() == 0
Added: httpd/httpd/trunk/test/modules/ssl/env.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ httpd/httpd/trunk/test/modules/ssl/env.py Thu Jun 4 09:40:10 2026 (r1934976)
@@ -0,0 +1,25 @@
+import inspect
+import logging
+import os
+
+from pyhttpd.env import HttpdTestEnv, HttpdTestSetup
+
+log = logging.getLogger(__name__)
+
+
+class SSLTestSetup(HttpdTestSetup):
+
+ def __init__(self, env: 'HttpdTestEnv'):
+ super().__init__(env=env)
+ self.add_source_dir(os.path.dirname(inspect.getfile(SSLTestSetup)))
+ self.add_modules(["ssl"])
+
+
+class SSLTestEnv(HttpdTestEnv):
+
+ def __init__(self, pytestconfig=None):
+ super().__init__(pytestconfig=pytestconfig)
+ self.add_httpd_log_modules(["http", "ssl", "core"])
+
+ def setup_httpd(self, setup: HttpdTestSetup = None):
+ super().setup_httpd(setup=SSLTestSetup(env=self))
\ No newline at end of file
Added: httpd/httpd/trunk/test/modules/ssl/test_001_sni.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ httpd/httpd/trunk/test/modules/ssl/test_001_sni.py Thu Jun 4 09:40:10 2026 (r1934976)
@@ -0,0 +1,38 @@
+import os
+import pytest
+
+from pyhttpd.conf import HttpdConf
+
+
+class TestSNI:
+ LOG_FILE = "test_sni.log"
+
+ @pytest.fixture(autouse=True, scope="class")
+ def _class_scope(self, env):
+ conf = HttpdConf(env, extras={
+ "base":
+ f'CustomLog logs/{self.LOG_FILE} "%{{SSL_TLS_SNI}}x"'
+ })
+ conf.add_vhost_test1()
+ conf.install()
+ assert env.apache_restart() == 0
+
+ # check sni logging characters
+ def test_ssl_001_01(self, env):
+ log_path = os.path.join(env.server_logs_dir, self.LOG_FILE)
+
+ open(log_path, 'w').close()
+ sni = "httpd\x01\n2024\".org"
+
+ r = env.run(args=[
+ 'openssl', 's_client',
+ '-connect', f"localhost:{env.https_port}",
+ '-servername', sni
+ ], intext="GET / HTTP/1.1\n\n")
+ assert r.exit_code == 0
+
+ with open(log_path, 'rb') as f:
+ log_content = f.read()
+
+ assert sni.encode() not in log_content, \
+ f"found unescaped characters in {self.LOG_FILE}.log"
Modified: httpd/httpd/trunk/test/pyhttpd/conf.py
==============================================================================
--- httpd/httpd/trunk/test/pyhttpd/conf.py Thu Jun 4 09:03:50 2026 (r1934975)
+++ httpd/httpd/trunk/test/pyhttpd/conf.py Thu Jun 4 09:40:10 2026 (r1934976)
@@ -69,7 +69,8 @@ class HttpdConf(object):
# In fact it should go in the corresponding VirtualHost... Not sure how to do that.
l = "SSLEngine On"
else:
- if line != "":
+ # conflict with the SSL_TLS_SNI
+ if line.lstrip().startswith("TLS"):
l = line.replace("TLS", "SSL")
else:
l = line
@@ -175,7 +176,7 @@ class HttpdConf(object):
if domains[0] in self._extras:
self.add(self._extras[domains[0]])
return self
-
+
def end_vhost(self):
self._indents -= 1
self.add("</VirtualHost>")
@@ -196,7 +197,7 @@ class HttpdConf(object):
f"ProxyPassReverse /h2proxy/ https://{host}.{self.env.http_tld}:self.env.https_port/",
])
return self
-
+
def add_vhost_test1(self, proxy_self=False, h2proxy_self=False):
domain = f"test1.{self.env.http_tld}"
self.start_vhost(domains=[domain, f"www1.{self.env.http_tld}"],
Modified: httpd/httpd/trunk/test/pyhttpd/env.py
==============================================================================
--- httpd/httpd/trunk/test/pyhttpd/env.py Thu Jun 4 09:03:50 2026 (r1934975)
+++ httpd/httpd/trunk/test/pyhttpd/env.py Thu Jun 4 09:40:10 2026 (r1934976)
@@ -225,6 +225,10 @@ class HttpdTestEnv:
LIBEXEC_DIR = None
+ @staticmethod
+ def has_tool(name: str) -> bool:
+ return bool(shutil.which(name))
+
@classmethod
def has_python_package(cls, name: str) -> bool:
if name in sys.modules: