Skip to content

Windows portability: fix TCP bind (#82) + StaticHandler/static serving#83

Merged
EdmondDantes merged 5 commits into
mainfrom
82-windows-11-operation-not-supported
Jun 4, 2026
Merged

Windows portability: fix TCP bind (#82) + StaticHandler/static serving#83
EdmondDantes merged 5 commits into
mainfrom
82-windows-11-operation-not-supported

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

Summary

Fixes #82Async\AsyncException: Failed to bind to <host>:<port>: operation not supported on socket on Windows — plus three adjacent Windows-portability bugs surfaced while running the server phpt suite on Windows for the first time.

Fixes

  • TCP listeners bind on Windows (Windows 11 operation not supported #82). The listener requested SO_REUSEPORT, which libuv's uv_tcp_bind() rejects with UV_ENOTSUP on Windows (Winsock has no SO_REUSEPORT). REUSEPORT is now a platform capability (http_server_use_reuseport()), never requested on Windows; the default single-listener server binds directly. No change on Linux/BSD/macOS.
  • StaticHandler accepts native Windows absolute paths. Root validation accepted only a leading /, rejecting every C:\... path. Now uses IS_ABSOLUTE_PATH (drive-letter / UNC on Windows, leading / on POSIX).
  • Static bodies served binary-clean on Windows. The send_file engine opened files without O_BINARY → text-mode CRLF/0x1A corruption/truncation of binary bodies (precompressed .br/.gz, byte ranges, images). Now opens O_BINARY, matching open_for_policy.
  • POSIX-only symlink-policy tests skipped on Windows (documented). O_NOFOLLOW is POSIX-only; the SKIPIF records the gap rather than silently muting.

Verification

The server phpt suite now executes on Windows (it was structurally skipped before). Local run across core/h1/h2/static/tls/compression/chaos: 119 pass / 10 fail / 22 skip.

Known remaining Windows issues (follow-up)

Deeper reactor/lifecycle bugs, several in the separate ext/async repo — tracked for focused follow-up:

  • Peer RST_STREAM doesn't cancel a blocked handler — connection read isn't pumped while the handler awaits (h2/007, h2/008, chaos/001). Basic coroutine cancellation works on Windows; this is reactor read-pumping.
  • 413 / response body lost on abrupt close (h1/005); range body lost (static/011-range).
  • h3 UDP recv-request leak on listener teardown.
  • ThreadPool cross-thread transfer crash (core/007) — needs a debugger.
  • Windows CI still builds the extension static and skips the whole suite — needs a shared build + load step to actually run these tests in CI.

TCP bind failed on Windows with 'Async\AsyncException: Failed to bind to <host>:<port>: operation not supported on socket'.

The listener setup derived 'use REUSEPORT' as !http_server_use_shared_listen_fd(). That boolean models two platform camps (kernel-LB REUSEPORT vs shared-fd dup), but Windows is neither: Winsock has no SO_REUSEPORT and libuv's uv_tcp_bind() returns UV_ENOTSUP when UV_TCP_REUSEPORT is set. Since use_shared_listen_fd() is false on Windows (no POSIX dup), !false wrongly requested REUSEPORT, so every TCP bind failed.

Add http_server_use_reuseport() as an explicit capability predicate: false on Windows, !use_shared_listen_fd() on POSIX. Single-listener Windows now binds directly; POSIX behaviour is unchanged.

Verified locally on Windows: tests/phpt/server/h1 binds and serves (21/22 pass).
Root validation tested only for a leading '/', so every Windows path (C:\...) was rejected and StaticHandler was unusable on Windows. Use IS_ABSOLUTE_PATH (leading '/' on POSIX; drive-letter / UNC on Windows); POSIX behaviour unchanged.

Found while running the server phpt suite on Windows for #82; unblocks ~8 static tests + core/033.
…nted)

009 (OwnerMatch, uid-based) and 021 (REJECT via open(O_NOFOLLOW)) exercise symlink enforcement that is POSIX-specific: O_NOFOLLOW does not exist on Windows and symlink() needs privilege there. SKIPIF records WHY and flags the Windows reparse-point enforcement as a tracked gap — explicitly not a silent mute. A real Windows reparse-point reject is a separate follow-up.
The async send_file engine opened files with O_RDONLY|O_CLOEXEC only. On Windows that is text mode: CRLF translation and a 0x1A byte treated as EOF corrupt/truncate binary bodies (precompressed .br/.gz, byte ranges, images). Add O_BINARY, mirroring open_for_policy in http_static_safety.c. No-op on POSIX (O_BINARY is 0/undefined).
REUSEPORT bind (#82), StaticHandler absolute-path acceptance, and the send_file O_BINARY fix.
@EdmondDantes EdmondDantes linked an issue Jun 4, 2026 that may be closed by this pull request
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Coverage

Total lines: 81.36% → 81.42% (+0.06 pp)

File Baseline Current Δ Touched
src/core/http_connection.c 68.09% 70.08% +1.99 pp
src/http3/http3_callbacks.c 81.02% 80.43% -0.59 pp
src/http3/http3_listener.c 76.16% 74.34% -1.82 pp
src/http_server_class.c 67.76% 67.80% +0.04 pp
src/send_file.c 87.09% 87.09% +0.00 pp
src/static/static_handler_class.c 96.85% 96.85% +0.00 pp

@EdmondDantes EdmondDantes merged commit 31f21a1 into main Jun 4, 2026
8 checks passed
@EdmondDantes EdmondDantes deleted the 82-windows-11-operation-not-supported branch June 4, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows 11 operation not supported

1 participant