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: