svn commit: r1934936 - in httpd/httpd/trunk/test: . pytest_suite pytest_suite/apache_pytest pytest_suite/t/conf/ssl pytest_suite/t/htdocs/apache/http_strict pytest_suite/t/htdocs/modules/cgi pytest_suite/t/htdocs/modules/ext_filter pytest_suite/t/htdoc...

[email protected]
Newsgroups gmane.comp.apache.cvs
Message-ID <178050728259.2435401.17230758294747508486@svn03-he-fi>
Author: jim
Date: Wed Jun  3 17:21:22 2026
New Revision: 1934936

Log:
pytest_suite: fix startup failures and add missing Perl helper templates

Three bugs fixed in the pytest framework:

1. cmodules.py: compiled C test modules were not rebuilt when apxs changed
   (e.g. upgrading from Apache 2.4 to 2.5). The staleness check now also
   compares the .so mtime against apxs itself, so a newer apxs always
   triggers a rebuild and prevents API-signature mismatches (AP24 vs AP25).
   Add clean_modules() to remove all apxs build artifacts (make clean
   equivalent), exposed as --clean-modules pytest option and routed only
   to pytest_suite (not pyhttpd) in run-all-tests.sh.

2. server.py: httpd was started without -DFOREGROUND, so the MPM parent
   forked and exited (code 0) before the port opened. The framework
   misread the fork-exit as a crash and killed the running daemon.
   Add -DFOREGROUND so the Popen process IS the httpd parent.

3. Missing Perl helper scripts: t/conf/extra.conf.in declares a prg-type
   RewriteMap (numbers-prg) whose program file did not exist, causing
   httpd to refuse to start. More broadly, all *.pl CGI scripts,
   ext_filter programs, rewrite-map programs, and the SSL passphrase
   helper referenced by the config and tests were absent.
   Add the full set of *.pl.PL source templates (copied from the
   httpd-tests framework), covering:
     - t/conf/ssl/httpd-passphrase.pl.PL
     - t/htdocs/apache/http_strict/send_hdr.pl.PL (new dir)
     - t/htdocs/modules/cgi/  (28 CGI scripts)
     - t/htdocs/modules/ext_filter/  (2 filter programs, new dir)
     - t/htdocs/modules/negotiation/query/test.pl.PL
     - t/htdocs/modules/rewrite/  (numbers, numbers2, db)
   generate_pl_scripts() regenerates the .pl executables from these
   templates at each test-session start with the correct local perl shebang.

Added:
   httpd/httpd/trunk/test/pytest_suite/t/conf/ssl/httpd-passphrase.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/apache/http_strict/
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/apache/http_strict/send_hdr.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/action.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/big.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/bogus-perl.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/bogus1k.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/empty.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/env.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/not-modified.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-102.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-dripfeed.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-foldhdr.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-interim1.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-interim2.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-stderr.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-test.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/ocsp.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl_echo.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl_post.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/pr37166.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/ranged.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/redirect.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr1.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr2.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr3.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/unique-id.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/xother.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/eval-cmd.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/sleepycat.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/negotiation/query/test.pl.PL   (contents, props changed)
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/db.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/numbers.pl.PL
   httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/numbers2.pl.PL   (contents, props changed)
Modified:
   httpd/httpd/trunk/test/pytest_suite/apache_pytest/__init__.py
   httpd/httpd/trunk/test/pytest_suite/apache_pytest/cmodules.py
   httpd/httpd/trunk/test/pytest_suite/apache_pytest/server.py
   httpd/httpd/trunk/test/pytest_suite/conftest.py
   httpd/httpd/trunk/test/run-all-tests.sh

Modified: httpd/httpd/trunk/test/pytest_suite/apache_pytest/__init__.py
==============================================================================
--- httpd/httpd/trunk/test/pytest_suite/apache_pytest/__init__.py	Wed Jun  3 17:09:40 2026	(r1934935)
+++ httpd/httpd/trunk/test/pytest_suite/apache_pytest/__init__.py	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -5,7 +5,7 @@ Phase 2: test-facing API (t_cmp, need_*
 """
 
 from .client import TestClient
-from .cmodules import compile_all
+from .cmodules import clean_modules, compile_all
 from .config import TestConfig
 from .probe import HttpdInfo, probe
 from .server import HttpdServer
@@ -24,6 +24,7 @@ __all__ = [
     "HttpdServer",
     "TestClient",
     "TestConfig",
+    "clean_modules",
     "compile_all",
     "need_cgi",
     "need_lwp",

Modified: httpd/httpd/trunk/test/pytest_suite/apache_pytest/cmodules.py
==============================================================================
--- httpd/httpd/trunk/test/pytest_suite/apache_pytest/cmodules.py	Wed Jun  3 17:09:40 2026	(r1934935)
+++ httpd/httpd/trunk/test/pytest_suite/apache_pytest/cmodules.py	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -21,6 +21,7 @@ actually compiling and loading in the sm
 from __future__ import annotations
 
 import re
+import shutil
 import subprocess
 from dataclasses import dataclass
 from pathlib import Path
@@ -199,6 +200,25 @@ def _apxs_cmd(apxs: Path, defines: list[
     return cmd
 
 
+def clean_modules(cmodules_dir: Path) -> None:
+    """Remove all apxs build artifacts from ``cmodules_dir`` (emulate ``make clean``).
+
+    Deletes the ``.libs/`` directory and libtool intermediates (``*.o``, ``*.lo``,
+    ``*.slo``, ``*.la``) from every module subdirectory, and the generated
+    ``apache_httpd_test.h`` header.  Safe to call when nothing has been built yet.
+    The next ``compile_all`` call will rebuild everything from scratch.
+    """
+    for sub in sorted(p for p in cmodules_dir.iterdir() if p.is_dir()):
+        libs = sub / _LIB_SUBDIR
+        if libs.is_dir():
+            shutil.rmtree(libs)
+        for pattern in ("*.o", "*.lo", "*.slo", "*.la"):
+            for f in sub.glob(pattern):
+                f.unlink(missing_ok=True)
+    header = cmodules_dir / "apache_httpd_test.h"
+    header.unlink(missing_ok=True)
+
+
 def compile_all(
     cmodules_dir: Path,
     apxs: Path,
@@ -216,10 +236,13 @@ def compile_all(
     generate_header(cmodules_dir / "apache_httpd_test.h")
 
     mods, skipped = discover(cmodules_dir, info)
+    apxs_mtime = apxs.stat().st_mtime
     loads: list[tuple[str, Path]] = []
     for mod in mods:
         needs = force or not mod.so.exists() or (
             mod.so.stat().st_mtime < mod.src.stat().st_mtime
+        ) or (
+            mod.so.stat().st_mtime < apxs_mtime
         )
         if needs:
             cmd = _apxs_cmd(apxs, defines, cmodules_dir, mod.src)

Modified: httpd/httpd/trunk/test/pytest_suite/apache_pytest/server.py
==============================================================================
--- httpd/httpd/trunk/test/pytest_suite/apache_pytest/server.py	Wed Jun  3 17:09:40 2026	(r1934935)
+++ httpd/httpd/trunk/test/pytest_suite/apache_pytest/server.py	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -108,6 +108,7 @@ class HttpdServer:
             vars_["serverroot"],
             "-f",
             vars_["t_conf_file"],
+            "-DFOREGROUND",
         ]
         for d in self.dversion_defines():
             argv += ["-D", d]

Modified: httpd/httpd/trunk/test/pytest_suite/conftest.py
==============================================================================
--- httpd/httpd/trunk/test/pytest_suite/conftest.py	Wed Jun  3 17:09:40 2026	(r1934935)
+++ httpd/httpd/trunk/test/pytest_suite/conftest.py	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -24,6 +24,7 @@ from pathlib import Path
 import pytest
 
 from apache_pytest import HttpdServer, TestClient, TestConfig, compile_all, probe
+from apache_pytest.cmodules import clean_modules
 from apache_pytest.probe import HttpdInfo
 
 # The suite is self-contained: all assets it needs (t/conf templates, t/htdocs
@@ -58,6 +59,12 @@ def pytest_addoption(parser: pytest.Pars
         default=8999,
         help="TCP port for the managed php-fpm pool (default 8999)",
     )
+    group.addoption(
+        "--clean-modules",
+        action="store_true",
+        default=False,
+        help="remove all compiled C-module artifacts before building (emulate make clean)",
+    )
 
 
 def pytest_configure(config: pytest.Config) -> None:
@@ -224,8 +231,11 @@ def framework(request: pytest.FixtureReq
 
     cmodule_loads: list[tuple[str, Path]] = []
     if apxs is not None:
+        cmodules_dir = REPO_ROOT / "c-modules"
+        if request.config.getoption("--clean-modules"):
+            clean_modules(cmodules_dir)
         cmodule_loads, _skipped = compile_all(
-            REPO_ROOT / "c-modules", apxs, info, defines=["APACHE2", "APACHE2_4", *defines]
+            cmodules_dir, apxs, info, defines=["APACHE2", "APACHE2_4", *defines]
         )
 
     config.generate(cmodule_loads=cmodule_loads)

Added: httpd/httpd/trunk/test/pytest_suite/t/conf/ssl/httpd-passphrase.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/conf/ssl/httpd-passphrase.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,2 @@
+#for testing SSLPassPhraseDialog  exec:@ServerRoot@/conf/ssl/httpd-passphrase.pl
+print "httpd\n";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/apache/http_strict/send_hdr.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/apache/http_strict/send_hdr.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,10 @@
+use MIME::Base64;
+use strict;
+use warnings;
+
+print "Content-type: text/plain\r\n";
+print decode_base64($ENV{QUERY_STRING}), "\r\n";
+print "\r\n";
+print "Hi!\n";
+print "SERVERNAME=$ENV{SERVER_NAME}\n";
+print "HTTP_HOST=$ENV{HTTP_HOST}\n";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/action.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/action.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,10 @@
+use strict;
+
+print "Content-type: text/plain\n\n";
+
+print $ENV{PATH_INFO} . "\n";
+
+if (my $ct = $ENV{CONTENT_LENGTH}) {
+    read STDIN, my $buffer, $ct;
+    print $buffer;
+}

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/big.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/big.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,22 @@
+# This is a regression test for PR 31247. 
+
+# By sleeping, it ensures that the CGI bucket is left in the brigade
+# (the first 8K will be morphed into a HEAP bucket), and hence *must*
+# be setaside correctly when the byterange filter calls
+# ap_save_brigade().
+
+# Without the fix for PR 31247, the STDOUT content does not get
+# consumed as expected, so the server will deadlock as it tries to
+# consume STDERR after script execution in mod_cgi, whilst the script
+# tries to write to STDOUT.  So close STDERR to avoid that.
+
+close STDERR;
+
+print "Content-type: text/plain\n\n";
+
+print "x"x8192;
+
+sleep 1;
+
+print "x"x8192;
+

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/bogus-perl.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/bogus-perl.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,2 @@
+
+print "perl cgi";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/bogus1k.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/bogus1k.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,2 @@
+
+print "N"x1024;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/empty.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/empty.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,6 @@
+use strict;
+
+print "Content-type: text/plain\r\n";
+print "Content-Length: 0\r\n";
+print "\r\n";
+

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/env.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/env.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,7 @@
+use strict;
+
+print "Content-type: text/plain\n\n";
+
+for (sort keys %ENV) {
+    print "$_ = $ENV{$_}\n";
+}

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/not-modified.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/not-modified.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,4 @@
+use strict;
+
+print "Status: 304 Not Modified\r\n\r\n";
+

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-102.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-102.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,9 @@
+
+print "HTTP/1.1 102 Please Wait...\r\n";
+print "Host: nph-102\r\n\r\n";
+
+print "HTTP/1.1 200 OK\r\n";
+print "Content-Type: text/plain\r\n\r\n";
+
+print "this is nph-stdout";
+

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-dripfeed.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-dripfeed.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,17 @@
+print "HTTP/1.1 200 OK\r\n";
+print "Transfer-Encoding: chunked\r\n";
+print "\r\n";
+
+$| = 1;
+
+sub dripfeed {
+    my $s = shift;
+
+    while (length($s)) {
+        select(undef, undef, undef, 0.2);
+        print substr($s, 0, 1);
+        $s = substr($s, 1);
+    }
+}
+
+dripfeed "0005\r\nabcde\r\n1; foo=bar\r\nf\r\n0\r\n\r\n";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-foldhdr.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-foldhdr.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,12 @@
+# produces output with folded response headers
+
+print "HTTP/1.0 200 OK\r\n";
+
+for (1..50) {
+    print "X-Foo-Bar-$_:\n " . 'x'x($_*10) . "\n";
+    print "X-Bar-$_:\n gamm\r\n beta\n theta\r\n";
+}
+
+print "Content-type: \n  text/plain\n\n";
+
+print "hello, world";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-interim1.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-interim1.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,16 @@
+foreach $i (1..5) {
+print <<EOT1
+HTTP/1.1 100 Continue
+Server: Sausages/1.0
+
+EOT1
+;
+}
+
+print <<EOT2
+HTTP/1.1 200 OK
+Content-Type: text/html
+
+Hello world
+EOT2
+;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-interim2.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-interim2.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,16 @@
+foreach $i (1..50) {
+print <<EOT1
+HTTP/1.1 100 Continue
+Server: Sausages/1.0
+
+EOT1
+;
+}
+
+print <<EOT2
+HTTP/1.1 200 OK
+Content-Type: text/html
+
+Hello world
+EOT2
+;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-stderr.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-stderr.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,12 @@
+# produces lots of stderr output
+
+print "HTTP/1.0 200 OK\r\n";
+
+print STDERR 'x'x8192;
+print "Content-Type: text/plain\r\n\r\n";
+
+print "this is nph-stdout";
+
+close STDOUT;
+
+print STDERR "this is nph-stderr";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-test.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/nph-test.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,9 @@
+print "HTTP/1.0 200 OK\r\n";
+print join("\n",
+     'Content-type: text/html',
+     'Pragma: no-cache',
+     'Cache-control: must-revalidate, no-cache, no-store',
+     'Expires: -1',
+     "\n");
+
+print "ok\n";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/ocsp.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/ocsp.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,78 @@
+use File::Temp qw/:POSIX/;
+
+my $caroot = $ENV{SSL_CA_ROOT};
+
+if (! -d $caroot) {
+    print <<EOT
+Status: 500 Internal Server Error
+Content-Type: text/plain
+
+Cannot find CA root at "$ENV{SSL_CA_ROOT}"
+EOT
+    ;
+    print STDERR "SSL_CA_ROOT env var not set or can't find CA root.\n";
+    exit(1);
+}
+
+chdir($caroot);
+
+my $filein = tmpnam();
+my $fileout = tmpnam();
+
+# Enable slurp mode (read all lines at once)
+local $/;
+
+# Copy STDIN to $filein, which will be used as input for openssl
+open(IN, '>', "$filein") or die "Could not open file '$filein' for write: $!";
+binmode IN;
+print IN <STDIN>;
+close(IN);
+
+my $cmd = 'openssl ocsp -CA certs/ca.crt'.
+    ' -index index.txt'.
+    ' -rsigner certs/server.crt'.
+    ' -rkey keys/server.pem'.
+    ' -reqin ' . $filein .
+    ' -respout ' . $fileout;
+system($cmd);
+
+# Check system result
+my $err = '';
+if ($? == -1) {
+    my $err = "failed to execute '$cmd': $!\n";
+}
+elsif ($? & 127) {
+    my $err = sprintf("child '$cmd' died with signal %d, %s coredump\n",
+        ($? & 127),  ($? & 128) ? 'with' : 'without');
+}
+else {
+    my $rc = $? >> 8;
+    my $err = "child '$cmd' exited with value $rc\n" if $rc;
+}
+
+unlink($filein);
+
+if ($err ne '') {
+    print <<EOT
+Status: 500 Internal Server Error
+Content-Type: text/plain
+
+$err
+EOT
+    ;
+    print STDERR $err;
+    exit(1);
+}
+
+print <<EOT
+Content-Type: application/ocsp-response
+
+EOT
+;
+
+# Copy openssl result from $fileout to STDOUT
+open(OUT, '<', "$fileout") or die "Could not open file '$fileout' for read: $!";
+binmode OUT;
+print <OUT>;
+close(OUT);
+unlink($fileout);

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,3 @@
+
+print "Content-type: text/plain\n\n";
+print "perl cgi";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl_echo.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl_echo.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,14 @@
+#echo some data back to the client
+
+print "Content-type: text/plain\n\n";
+
+if (my $ct = $ENV{CONTENT_LENGTH}) {
+    read STDIN, my $buffer, $ct;
+    print $buffer;
+}
+elsif (my $qs = $ENV{QUERY_STRING}) {
+    print $qs;
+}
+else {
+    print "nada";
+}

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl_post.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/perl_post.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,23 @@
+local ($buffer, @pairs, $pair, $name, $value);
+
+print "Content-type: text/plain\n\n";
+
+$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
+print "$ENV{'REQUEST_METHOD'}\n";
+
+# Read in text
+if ($ENV{'REQUEST_METHOD'} eq "POST") {
+    read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
+} else {
+    $buffer = $ENV{'QUERY_STRING'};
+}
+
+# Split information into name/value pairs
+@pairs = split(/&/, $buffer);
+foreach $pair (@pairs) {
+    ($name, $value) = split(/=/, $pair);
+    $value =~ tr/+/ /;
+    $value =~ s/%(..)/pack("C", hex($1))/eg;
+
+    print "$name: $value\n";
+}

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/pr37166.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/pr37166.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,8 @@
+print <<EOT
+Status: 200
+Last-Modified: Tue, 15 Feb 2005 15:00:00 GMT
+Content-Type: text/html
+
+Hello world
+EOT
+;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/ranged.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/ranged.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,11 @@
+use strict;
+
+print "Content-type: text/plain\n";
+
+if ($ENV{'HTTP_RANGE'} eq 'bytes=5-10/10') {
+    print "Content-Range: bytes 5-10/10\n\n";
+    print "hello\n";
+} else {
+    print "\npardon?\n";
+}
+

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/redirect.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/redirect.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,5 @@
+print <<EOT
+Location: /foobar.html
+
+EOT
+;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr1.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr1.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,7 @@
+# produces lots of stderr output
+
+print STDERR 'x'x8192;
+
+print "Content-Type: text/plain\n\n";
+
+print "this is stdout";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr2.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr2.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,9 @@
+# closes stderr during script execution
+
+close STDERR;
+
+print "Content-Type: text/plain\n\n";
+
+sleep 1;
+
+print "this is also stdout";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr3.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/stderr3.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,8 @@
+# closes stderr during script execution
+
+print "Content-Type: text/plain\n\n";
+print "this is more stdout";
+
+close STDOUT;
+
+print STDERR "this is a post-stdout-closure error message";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/unique-id.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/unique-id.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,3 @@
+print "Content-type: text/plain\n\n";
+
+print $ENV{UNIQUE_ID};

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/xother.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/cgi/xother.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,6 @@
+use strict;
+
+print "X-Foo: bar\n";
+print "Content-type: text/plain\n\n";
+
+print "helloworld";

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/eval-cmd.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/eval-cmd.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,6 @@
+use strict;
+
+$| = 1;
+
+my $cmd = shift;
+do {eval $cmd; print } while <>;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/sleepycat.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/ext_filter/sleepycat.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,3 @@
+$| = 1;
+
+print && select undef,undef,undef,.2 while <>;

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/negotiation/query/test.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/negotiation/query/test.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,8 @@
+
+print "Content-type: text/html\n\n";
+
+foreach my $key (keys %ENV) {
+   if ($key eq "QUERY_STRING") {
+   	print "$key --> $ENV{$key}\n";
+   }
+}

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/db.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/db.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,10 @@
+
+($txtmap, $dbmmap) = @ARGV;
+open(TXT, "<$txtmap");
+dbmopen(%DB, $dbmmap, 0644);
+while (<TXT>) {
+ next if (m|^s*#.*| or m|^s*$|);
+ $DB{$1} = $2 if (m|^\s*(\S+)\s+(\S+)$|);
+}
+dbmclose(%DB);
+close(TXT)

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/numbers.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/numbers.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,26 @@
+
+# numbers.pl
+# program rewrite map for mod_rewrite testing
+#
+$|=1;
+my %map = (	1	=>	'one',
+		2	=>	'two',
+		3	=>	'three',
+		4	=>	'four',
+		5	=>	'five',
+		6	=>	'six'	);
+
+while (<STDIN>) {
+	chomp;
+
+	print STDERR "GOT: ->$_<-\n";
+	my $m = $map{$_};
+	print STDERR "MAPPED: ->$_<-\n";
+
+	if ($m) {
+		print STDOUT "$m\n";
+	} else {
+		print STDOUT "NULL";
+	}
+}
+close (LOG);

Added: httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/numbers2.pl.PL
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ httpd/httpd/trunk/test/pytest_suite/t/htdocs/modules/rewrite/numbers2.pl.PL	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -0,0 +1,9 @@
+
+# numbers.pl
+# program rewrite map for mod_rewrite testing
+#
+funk cold medina.
+$|=1;
+while (<STDIN>) {
+	print $_;
+}

Modified: httpd/httpd/trunk/test/run-all-tests.sh
==============================================================================
--- httpd/httpd/trunk/test/run-all-tests.sh	Wed Jun  3 17:09:40 2026	(r1934935)
+++ httpd/httpd/trunk/test/run-all-tests.sh	Wed Jun  3 17:21:22 2026	(r1934936)
@@ -41,6 +41,7 @@ config_ini="$here/pyhttpd/config.ini"
 only=""
 apxs_opt=""
 flags=""
+pysuite_flags=""
 paths=""
 expect_apxs=0
 expect_flagval=0
@@ -51,6 +52,7 @@ for arg in "$@"; do
         --only=*) only="${arg#--only=}" ;;
         --apxs)   expect_apxs=1 ;;
         --apxs=*) apxs_opt="${arg#--apxs=}" ;;
+        --clean-modules) pysuite_flags="$pysuite_flags $arg" ;;  # pysuite-only; pyhttpd has no C modules
         -k|-m|-p) flags="$flags $arg"; expect_flagval=1 ;;  # take a value next
         -*)       flags="$flags $arg" ;;
         *)        paths="$paths $arg" ;;
@@ -86,8 +88,9 @@ run_pysuite() {
     fi
     # runtests.sh handles venv + cgisock cleanup + flag assembly.
     # pytest_suite gets both the shared flags and any positional paths.
+    # pysuite_flags holds options only meaningful to this suite (e.g. --clean-modules).
     # shellcheck disable=SC2086
-    ( cd "$suite_dir" && ./runtests.sh --apxs="$apxs_opt" $php_args $flags $paths ) || return $?
+    ( cd "$suite_dir" && ./runtests.sh --apxs="$apxs_opt" $php_args $flags $pysuite_flags $paths ) || return $?
 }
 
 run_pyhttpd() {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.