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.