From ff7acba2a79e27c22d01aaf2c952575c5559b875 Mon Sep 17 00:00:00 2001 From: Edmond <1571649+EdmondDantes@users.noreply.github.com> Date: Tue, 25 Aug 2026 22:52:47 +0300 Subject: [PATCH] build(win): compile WebSocket on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `config.w32` listed core, http1, formats, log, room, static and llhttp, and never `src/websocket` or the bundled wslay: the class did not exist in a Windows build and its whole phpt group failed, which is most of what `WINDOWS_X64_ZTS_RELEASE` reports as red on main. Nothing platform specific was in the way — the recipe simply did not name the sources. Three things the port needed. wslay's translation units include no PHP header, so the macros this extension owns are passed on the command line as the compression block already does. `wslay.h` types its callbacks with `ssize_t`, which MSVC does not have and php-src answers with `#define ssize_t SSIZE_T` in a header those units never see; SSIZE_T comes from BaseTsd.h, force-included because wslay includes no Windows header of its own. And the block sits below the include-path section because ADD_FLAG skips a flag that is a substring of one already added: the wslay include path added first swallowed the module root's own `/I`. CI needs no change: it does not pass `--disable-all`, so the new `--enable-websocket` takes its default. Evidence: the group goes from absent to 60 of 61 executed tests passing on Windows; the whole phpt tree reads 496 tests, 270 passed, 2 failed. One is `websocket/035-recv-queue-overflow`, which fails 2 of 3 runs here and is not a build problem: its client reads the stream expecting the CLOSE frame first and misparses anything the server sends ahead of it. The other is a test of an unrelated branch present in the working copy. --- config.w32 | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/config.w32 b/config.w32 index fd880e0..5e86e5d 100644 --- a/config.w32 +++ b/config.w32 @@ -6,6 +6,7 @@ ARG_ENABLE('http3', 'Enable HTTP/3 in true_async_server (requires ngtcp2, nghttp ARG_ENABLE('http-compression', 'Enable HTTP body compression (gzip via zlib)', 'yes'); ARG_ENABLE('brotli', 'Enable Brotli backend (requires libbrotli)', 'yes'); ARG_ENABLE('zstd', 'Enable zstd backend (requires libzstd)', 'yes'); +ARG_ENABLE('websocket', 'Enable WebSocket (RFC 6455) via bundled wslay', 'yes'); if (PHP_TRUE_ASYNC_SERVER == "yes") { @@ -169,6 +170,42 @@ if (PHP_TRUE_ASYNC_SERVER == "yes") { "Drop libssl.lib/libcrypto.lib into '" + PHP_PHP_BUILD + "\\lib' and headers into '\\include\\openssl'."); } + // WebSocket (RFC 6455) — bundled wslay frame parser plus our strategy, + // session bridge, handshake, dispatch and PHP object layer. Mirrors the + // --enable-websocket block of config.m4. + // + // It sits below the include-path section because ADD_FLAG skips a flag + // that is a substring of one already added: the wslay include path added + // first would swallow the module root's own /I, and every file reached + // through php_true_async_server.h would stop resolving. + if (PHP_WEBSOCKET == "yes") { + AC_DEFINE("HAVE_WSLAY", 1, "Whether bundled wslay is available"); + AC_DEFINE("HAVE_HTTP_SERVER_WEBSOCKET", 1, "Whether WebSocket support is enabled"); + + // The pure-C wslay translation units include no PHP header, so a macro + // reaching them through main/config.w32.h never arrives: pass the two + // this extension owns on the command line, as the compression block + // does. htons()/ntohs() come from winsock2.h, which wslay includes + // only under HAVE_WINSOCK2_H. + ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/D HAVE_WSLAY=1"); + ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/D HAVE_HTTP_SERVER_WEBSOCKET=1"); + ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/D HAVE_WINSOCK2_H=1"); + // wslay.h types its callbacks with ssize_t, which MSVC does not have. + // php-src answers this with `#define ssize_t SSIZE_T` in config.w32.h, + // a header these translation units never see; SSIZE_T itself comes from + // BaseTsd.h, force-included because wslay.h includes no Windows header. + ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/FI BaseTsd.h"); + ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/D ssize_t=SSIZE_T"); + ADD_FLAG("CFLAGS_TRUE_ASYNC_SERVER", "/I " + configure_module_dirname + "/deps/wslay/includes"); + + ADD_SOURCES(configure_module_dirname + "/deps/wslay/lib", + "wslay_event.c wslay_frame.c wslay_net.c wslay_queue.c wslay_stack.c", + "true_async_server"); + ADD_SOURCES(configure_module_dirname + "/src/websocket", + "websocket_strategy.c ws_session.c ws_handshake.c ws_dispatch.c php_websocket.c", + "true_async_server"); + } + // HTTP/2 (optional) if (PHP_HTTP2 == "yes") { if (CHECK_HEADER_ADD_INCLUDE("nghttp2/nghttp2.h", "CFLAGS_TRUE_ASYNC_SERVER", PHP_PHP_BUILD + "\\include")