From 5cc737ec159d2d7a83bbfa2bc8b9e5fff54308c3 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Tue, 8 Sep 2026 09:24:40 +0200 Subject: [PATCH 01/13] Add install and test for windows. --- .github/workflows/windows.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3df096799bd..22305c7237b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -87,3 +87,12 @@ jobs: - name: Build run: | cmake --build ${{github.workspace}}/build --config ${{ matrix.build-type }} + + - name: Install + run: | + cmake --install ${{github.workspace}}/build --config ${{ matrix.build-type }} + + - name: Test + run: | + ${{github.workspace}}\test\pyhttpd\install_tools.bat "C:\Program Files (x86)\HTTPD" + ${{github.workspace}}\test\pyhttpd\runtests.bat --httpd "C:\Program Files (x86)\HTTPD\bin\httpd.exe" --conf "C:\Program Files (x86)\HTTPD\conf\httpd.conf" From ab1cfec57978f4ca55b8e1a7e1a52faf1cbdfded Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Tue, 8 Sep 2026 10:26:08 +0200 Subject: [PATCH 02/13] Add nghttp2 curl libxml2 jansson to vcpkg install and cmake dependencies. --- .github/workflows/windows.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 22305c7237b..b535be08854 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -71,7 +71,7 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Install dependencies - run: vcpkg install --triplet ${{ matrix.triplet }} apr[private-headers] apr-util pcre2 openssl + run: vcpkg install --triplet ${{ matrix.triplet }} apr[private-headers] apr-util pcre2 openssl nghttp2 curl libxml2 jansson - uses: actions/checkout@v6 @@ -82,7 +82,11 @@ jobs: -G "${{ matrix.generator }}" ` -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake ` -DAPR_INCLUDE_DIR=C:/vcpkg/installed/${{ matrix.triplet }}/include ` - "-DAPR_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/libapr-1.lib;C:/vcpkg/installed/${{ matrix.triplet }}/lib/libaprutil-1.lib" + "-DAPR_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/libapr-1.lib;C:/vcpkg/installed/${{ matrix.triplet }}/lib/libaprutil-1.lib" ` + -DNGHTTP2_INCLUDE_DIR=C:/vcpkg/installed/${{ matrix.triplet }}/include ` + -DNGHTTP2_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/nghttp2.lib ` + -DJANSSON_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/jansson.lib ` + -DBUILD_TEST_MODULES=true - name: Build run: | From 8c207c43070d8c8ee5ef26418d047dea8e18205d Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Tue, 8 Sep 2026 12:05:25 +0200 Subject: [PATCH 03/13] Don't reinstall vcpkg use the existing one. --- .github/workflows/windows.yml | 3 +++ test/pyhttpd/install_tools.bat | 31 +++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b535be08854..719cc2cefb7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -97,6 +97,9 @@ jobs: cmake --install ${{github.workspace}}/build --config ${{ matrix.build-type }} - name: Test + env: + VCPKG_DIR: C:\vcpkg + VCPKG_TRIPLET: ${{ matrix.triplet }} run: | ${{github.workspace}}\test\pyhttpd\install_tools.bat "C:\Program Files (x86)\HTTPD" ${{github.workspace}}\test\pyhttpd\runtests.bat --httpd "C:\Program Files (x86)\HTTPD\bin\httpd.exe" --conf "C:\Program Files (x86)\HTTPD\conf\httpd.conf" diff --git a/test/pyhttpd/install_tools.bat b/test/pyhttpd/install_tools.bat index c487d389b86..ad0a243d2d3 100644 --- a/test/pyhttpd/install_tools.bat +++ b/test/pyhttpd/install_tools.bat @@ -169,9 +169,34 @@ echo install_tools.bat: Strawberry Perl installed at "!PERL!" >&2 :perl_ok rem --- vcpkg location --------------------------------------------------------- +if not defined VCPKG_TRIPLET set "VCPKG_TRIPLET=x64-windows" + +rem Honor VCPKG_DIR from the environment (e.g. set by CI) +if defined VCPKG_DIR ( + if not defined VCPKG_INSTALLED set "VCPKG_INSTALLED=%VCPKG_DIR%\installed\%VCPKG_TRIPLET%" + if exist "%VCPKG_DIR%\vcpkg.exe" ( + set "VCPKG_EXE=%VCPKG_DIR%\vcpkg.exe" + echo install_tools.bat: using VCPKG_DIR from environment: "!VCPKG_DIR!" >&2 + goto :vcpkg_ok + ) +) + +rem Try vcpkg on PATH (e.g. GitHub Actions has C:\vcpkg) +set "VCPKG_EXE=" +for /f "delims=" %%V in ('where vcpkg.exe 2^>nul') do ( + if not defined VCPKG_EXE set "VCPKG_EXE=%%V" +) +if defined VCPKG_EXE ( + echo install_tools.bat: using vcpkg from PATH: "!VCPKG_EXE!" >&2 + for %%F in ("!VCPKG_EXE!") do set "VCPKG_DIR=%%~dpF" + if "!VCPKG_DIR:~-1!"=="\" set "VCPKG_DIR=!VCPKG_DIR:~0,-1!" + if not defined VCPKG_INSTALLED set "VCPKG_INSTALLED=!VCPKG_DIR!\installed\%VCPKG_TRIPLET%" + goto :vcpkg_ok +) + +rem Fall back to cloning vcpkg locally set "VCPKG_DIR=%TEST_DIR%\vcpkg" -set "VCPKG_TRIPLET=x64-windows" -set "VCPKG_INSTALLED=%VCPKG_DIR%\installed\%VCPKG_TRIPLET%" +if not defined VCPKG_INSTALLED set "VCPKG_INSTALLED=%VCPKG_DIR%\installed\%VCPKG_TRIPLET%" rem --- bootstrap vcpkg if needed ---------------------------------------------- if not exist "%VCPKG_DIR%\" ( @@ -204,6 +229,8 @@ if not exist "%VCPKG_DIR%\vcpkg.exe" ( icacls "%VCPKG_DIR%\vcpkg.exe" /grant "%USERNAME%":RX >nul 2>&1 set "VCPKG_EXE=%VCPKG_DIR%\vcpkg.exe" +:vcpkg_ok + rem --- install packages ------------------------------------------------------- echo install_tools.bat: installing curl[core,tool,http2,openssl] and nghttp2 via vcpkg... >&2 "%VCPKG_EXE%" install --recurse --triplet %VCPKG_TRIPLET% curl[core,tool,http2,openssl] nghttp2 From 1393aafcc0835d8471e0f9afac497ebf0fe6f5d7 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Tue, 8 Sep 2026 13:49:07 +0200 Subject: [PATCH 04/13] Try to fail early. --- .github/workflows/windows.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 719cc2cefb7..3cc99245416 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -96,10 +96,24 @@ jobs: run: | cmake --install ${{github.workspace}}/build --config ${{ matrix.build-type }} + - name: Check httpd + run: | + & "C:\Program Files (x86)\HTTPD\bin\httpd.exe" -V + & "C:\Program Files (x86)\HTTPD\bin\httpd.exe" -t -f "C:\Program Files (x86)\HTTPD\conf\httpd.conf" + - name: Test env: VCPKG_DIR: C:\vcpkg VCPKG_TRIPLET: ${{ matrix.triplet }} run: | ${{github.workspace}}\test\pyhttpd\install_tools.bat "C:\Program Files (x86)\HTTPD" - ${{github.workspace}}\test\pyhttpd\runtests.bat --httpd "C:\Program Files (x86)\HTTPD\bin\httpd.exe" --conf "C:\Program Files (x86)\HTTPD\conf\httpd.conf" + ${{github.workspace}}\test\pyhttpd\runtests.bat --httpd "C:\Program Files (x86)\HTTPD\bin\httpd.exe" --conf "C:\Program Files (x86)\HTTPD\conf\httpd.conf" -vvvv -x + + - name: Upload test logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-logs + path: | + C:\Program Files (x86)\HTTPD\logs\ + ${{github.workspace}}\test\gen\apache\logs\ From 3483d24c89ae5b181fa6373bb2abad57e9426364 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Tue, 8 Sep 2026 15:26:38 +0200 Subject: [PATCH 05/13] Try to prevent Access is denied. --- .github/workflows/windows.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3cc99245416..c66ef1d13a3 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -46,7 +46,7 @@ jobs: - name: Default triplet: x64-windows arch: x64 - build-type: Debug + build-type: Release generator: "Ninja" runs-on: windows-latest @@ -90,23 +90,27 @@ jobs: - name: Build run: | - cmake --build ${{github.workspace}}/build --config ${{ matrix.build-type }} + cmake --build ${{github.workspace}}/build --config ${{ matrix.build-type }} -- -j 1 - name: Install run: | cmake --install ${{github.workspace}}/build --config ${{ matrix.build-type }} + - name: Install dependencies + env: + VCPKG_DIR: C:\vcpkg + VCPKG_TRIPLET: ${{ matrix.triplet }} + run: | + ${{github.workspace}}\test\pyhttpd\install_tools.bat "C:\Program Files (x86)\HTTPD" + - name: Check httpd run: | + $env:PATH = "C:\Program Files (x86)\HTTPD\bin;C:\vcpkg\installed\x64-windows\bin;$env:PATH" & "C:\Program Files (x86)\HTTPD\bin\httpd.exe" -V & "C:\Program Files (x86)\HTTPD\bin\httpd.exe" -t -f "C:\Program Files (x86)\HTTPD\conf\httpd.conf" - - name: Test - env: - VCPKG_DIR: C:\vcpkg - VCPKG_TRIPLET: ${{ matrix.triplet }} + - name: Test httpd run: | - ${{github.workspace}}\test\pyhttpd\install_tools.bat "C:\Program Files (x86)\HTTPD" ${{github.workspace}}\test\pyhttpd\runtests.bat --httpd "C:\Program Files (x86)\HTTPD\bin\httpd.exe" --conf "C:\Program Files (x86)\HTTPD\conf\httpd.conf" -vvvv -x - name: Upload test logs From 10cf3fc206095a4650e6814c0384c68cedfa67be Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 09:12:29 +0200 Subject: [PATCH 06/13] ignore AH00332 if IPv6 on the box. --- test/conftest.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index 3e4989af7e9..d1bebe34386 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,3 +1,4 @@ +import socket import sys import os import re @@ -11,6 +12,16 @@ from pyhttpd.env import HttpdTestEnv +def _has_ipv6(): + try: + s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + s.bind(('::1', 0)) + s.close() + return True + except (socket.error, OSError): + return False + + def pytest_ignore_collect(collection_path, config): if "pytest_suite" in collection_path.parts: return True @@ -177,6 +188,8 @@ def _package_scope(env, request): "AH00338", "AH00341", ]) + if sys.platform == "win32" and _has_ipv6(): + env.httpd_error_log.add_ignored_lognos(["AH00332"]) yield assert env.apache_stop() == 0 env.check_error_log() From c5a33e4ece7be2dc44ccc34910cfb6214f76f6cb Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 10:51:07 +0200 Subject: [PATCH 07/13] xfail more tests on windows. --- test/modules/aaa/test_009_restart.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/modules/aaa/test_009_restart.py b/test/modules/aaa/test_009_restart.py index a5f2b3c8ed8..d6954b20996 100644 --- a/test/modules/aaa/test_009_restart.py +++ b/test/modules/aaa/test_009_restart.py @@ -39,10 +39,6 @@ import pytest -import sys - -import pytest - from . import digest_client as dc from .env import AAATestEnv @@ -152,6 +148,7 @@ def test_digest_091_returning_client_is_stale_even_if_its_id_was_reused(self, en "a returning client was reported as a possible replay attack " \ "because its id had been given to somebody else" + @pytest.mark.xfail(condition=_WIN32, reason="ap_retained_data lost across mpm_winnt restart") def test_digest_092_onetime_nonce_from_before_a_restart_is_not_accepted(self, env): # One-time nonces are ordered by a counter which restarts at 0 with # the segment, while the nonce itself stays verifiable. A client From 4e9dca784fd23ed020bdfe7ecce1ced83760d386 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 10:52:12 +0200 Subject: [PATCH 08/13] use x64-windows-release get a faster vcpkg. --- test/pyhttpd/install_tools.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pyhttpd/install_tools.bat b/test/pyhttpd/install_tools.bat index ad0a243d2d3..c18bb81bf25 100644 --- a/test/pyhttpd/install_tools.bat +++ b/test/pyhttpd/install_tools.bat @@ -169,7 +169,7 @@ echo install_tools.bat: Strawberry Perl installed at "!PERL!" >&2 :perl_ok rem --- vcpkg location --------------------------------------------------------- -if not defined VCPKG_TRIPLET set "VCPKG_TRIPLET=x64-windows" +if not defined VCPKG_TRIPLET set "VCPKG_TRIPLET=x64-windows-release" rem Honor VCPKG_DIR from the environment (e.g. set by CI) if defined VCPKG_DIR ( From 23de14316f00a00c00f13310e07250065dd98a90 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 14:38:08 +0200 Subject: [PATCH 09/13] xfail more tests on windows. --- test/modules/aaa/test_009_restart.py | 7 ++++++- test/modules/aaa/test_010_eviction.py | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/test/modules/aaa/test_009_restart.py b/test/modules/aaa/test_009_restart.py index d6954b20996..a7369e879bc 100644 --- a/test/modules/aaa/test_009_restart.py +++ b/test/modules/aaa/test_009_restart.py @@ -148,7 +148,9 @@ def test_digest_091_returning_client_is_stale_even_if_its_id_was_reused(self, en "a returning client was reported as a possible replay attack " \ "because its id had been given to somebody else" - @pytest.mark.xfail(condition=_WIN32, reason="ap_retained_data lost across mpm_winnt restart") + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") def test_digest_092_onetime_nonce_from_before_a_restart_is_not_accepted(self, env): # One-time nonces are ordered by a counter which restarts at 0 with # the segment, while the nonce itself stays verifiable. A client @@ -184,6 +186,9 @@ def test_digest_092_onetime_nonce_from_before_a_restart_is_not_accepted(self, en self.header(location, newcomer)).response["status"] == 200, \ "the stale nonce locked out the client holding that id" + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") def test_digest_093_onetime_request_cannot_be_replayed_across_a_restart(self, env): # The severity case. A one-time nonce is single-use because the server # remembers the last nonce each client used -- and that memory does diff --git a/test/modules/aaa/test_010_eviction.py b/test/modules/aaa/test_010_eviction.py index ed6af5bb69d..aaf90d4174b 100644 --- a/test/modules/aaa/test_010_eviction.py +++ b/test/modules/aaa/test_010_eviction.py @@ -25,13 +25,17 @@ ever 0. The tests below run against both to pin that. """ +import sys + import pytest from . import digest_client as dc from .env import AAATestEnv NC_FAILED = "AH01774" +NONCE_INVALID = "AH01776" CLIENT_UNKNOWN = "AH10618" +_WIN32 = sys.platform == "win32" # A location tracking clients for the nonce-count, and one tracking them for # one-time nonces: the two put different kinds of value in last_nonce_time. @@ -78,6 +82,9 @@ def flood(self, env, location, count): for _ in range(count): env.curl_get(self.url(env, location)) + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") @pytest.mark.parametrize("location", BOTH) def test_digest_100_authenticated_client_survives_a_flood(self, env, location): # The legitimate client authenticates, so its entry now records the @@ -94,7 +101,8 @@ def test_digest_100_authenticated_client_survives_a_flood(self, env, location): # The victim carries on. Its entry must still be there: it is the # only one in the table which is worth keeping. r = self.send(env, location, self.header(location, challenge, nc)) - env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN]) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN] + + ([NONCE_INVALID] if _WIN32 else [])) assert r.response["status"] == 200, \ "a flood of unauthenticated requests evicted an authenticated client" @@ -110,7 +118,8 @@ def test_digest_101_unused_entries_are_the_ones_discarded(self, env, location): self.flood(env, location, 60) r = self.send(env, location, self.header(location, first)) - env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN]) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN] + + ([NONCE_INVALID] if _WIN32 else [])) assert r.response["status"] == 401 again = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) assert again.opaque != first.opaque, \ From c6fa2fbaa4326aa5c97c574f858e1214060de8a9 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 16:52:24 +0200 Subject: [PATCH 10/13] Use VCPKG_TRIPLET with release have a faster build. --- test/pyhttpd/build-win.bat | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/pyhttpd/build-win.bat b/test/pyhttpd/build-win.bat index 89697a9acce..57c8ce3efa3 100644 --- a/test/pyhttpd/build-win.bat +++ b/test/pyhttpd/build-win.bat @@ -107,9 +107,10 @@ cd /d "%HTTPD_SRC%\test\pyhttpd" SET CWD=%CD% SET "CWD=%CWD:\=/%" @REM ------------------------------------- -SET ARCH="x64-windows" -SET BUILD_TYPE="Debug" -SET VCPKG_DIRECTORY="vcpkg\installed\x64-windows" +SET BUILD_TYPE="Release" +SET VCPKG_ROOT=%CWD%\vcpkg +SET VCPKG_TRIPLET=x64-windows-release +SET VCPKG_DIRECTORY="vcpkg\installed\%VCPKG_TRIPLET%" SET VCPKG_DIRECTORY_LIB=%VCPKG_DIRECTORY% SET GENERATOR="NMake Makefiles" @REM SET GENERATOR="Ninja" @@ -230,9 +231,8 @@ if NOT EXIST vcpkg\ ( PUSHD vcpkg\ ) -SET VCPKG_ROOT=%CWD%\vcpkg @REM For some reason using the manifest doesn't install the default-features -vcpkg.exe install --triplet x64-windows apr[private-headers] apr-util pcre2 openssl nghttp2 curl libxml2 jansson +vcpkg.exe install --triplet %VCPKG_TRIPLET% apr[private-headers] apr-util pcre2 openssl nghttp2 curl libxml2 jansson POPD @@ -256,13 +256,13 @@ cmake "%HTTPD_SRC%" -B . ^ -G %GENERATOR% ^ -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^ -DCMAKE_TOOLCHAIN_FILE=%CWD%/vcpkg/scripts/buildsystems/vcpkg.cmake ^ - -DVCPKG_TARGET_TRIPLET=%ARCH% ^ - -DNGHTTP2_INCLUDE_DIR=%CWD%/vcpkg/installed/x64-windows/include ^ - -DAPR_INCLUDE_DIR=%CWD%/vcpkg/installed/x64-windows/include ^ - -DJANSSON_INCLUDE_DIR=%CWD%/vcpkg/installed/x64-windows/include ^ - "-DAPR_LIBRARIES=%CWD%/vcpkg/installed/x64-windows/lib/libapr-1.lib;%CWD%/vcpkg/installed/x64-windows/lib/libaprutil-1.lib" ^ - -DNGHTTP2_LIBRARIES=%CWD%/vcpkg/installed/x64-windows/lib/nghttp2.lib ^ - -DJANSSON_LIBRARIES=%CWD%/vcpkg/installed/x64-windows/lib/jansson.lib ^ + -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% ^ + -DNGHTTP2_INCLUDE_DIR=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/include ^ + -DAPR_INCLUDE_DIR=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/include ^ + -DJANSSON_INCLUDE_DIR=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/include ^ + "-DAPR_LIBRARIES=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/libapr-1.lib;%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/libaprutil-1.lib" ^ + -DNGHTTP2_LIBRARIES=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/nghttp2.lib ^ + -DJANSSON_LIBRARIES=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/jansson.lib ^ -DBUILD_TEST_MODULES=true ^ -DCMAKE_POLICY_VERSION_MINIMUM=%CMAKE_VERSION% ^ --install-prefix %HTTPD_INSTALL_DIRECTORY% From 0ab03b7bc9673de22e161e4ec4a0961af402dea0 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 17:56:55 +0200 Subject: [PATCH 11/13] Arrange stopping httpd on windows. --- test/pyhttpd/env.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/pyhttpd/env.py b/test/pyhttpd/env.py index f579a55d6fd..02d82ecae25 100644 --- a/test/pyhttpd/env.py +++ b/test/pyhttpd/env.py @@ -907,10 +907,18 @@ def _win_start(self) -> int: def _win_stop(self) -> int: if self._httpd_proc is not None: - log.debug("stopping httpd (terminate parent)") - self._httpd_proc.terminate() + import ctypes + log.debug("stopping httpd (signal shutdown event)") + event_name = f"ap{self._httpd_proc.pid}_shutdown" + handle = ctypes.windll.kernel32.OpenEventW(0x0002, False, event_name) + if handle: + ctypes.windll.kernel32.SetEvent(handle) + ctypes.windll.kernel32.CloseHandle(handle) + else: + log.warning(f"cannot open {event_name}, falling back to terminate") + self._httpd_proc.terminate() try: - self._httpd_proc.wait(timeout=10) + self._httpd_proc.wait(timeout=30) except subprocess.TimeoutExpired: self._httpd_proc.kill() self._httpd_proc.wait(timeout=5) From b5230090ade1339baf8cc257215b23e4750d0c79 Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Wed, 9 Sep 2026 19:59:52 +0200 Subject: [PATCH 12/13] Add 10 minutes to the timeout. --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c66ef1d13a3..463f096ee84 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -50,7 +50,7 @@ jobs: generator: "Ninja" runs-on: windows-latest - timeout-minutes: 30 + timeout-minutes: 40 name: ${{ matrix.name }} env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" From bfcfe6908388756fc51b7b9ebb7c68bbc496cd4f Mon Sep 17 00:00:00 2001 From: Jean-Frederic Clere Date: Thu, 10 Sep 2026 07:00:08 +0200 Subject: [PATCH 13/13] Try 1 hour. --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 463f096ee84..09742d9e66d 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -50,7 +50,7 @@ jobs: generator: "Ninja" runs-on: windows-latest - timeout-minutes: 40 + timeout-minutes: 60 name: ${{ matrix.name }} env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"