Skip to content

Rooms are served by a build without WebSocket - #166

Merged
EdmondDantes merged 1 commit into
mainfrom
feat/rooms-without-websocket
Aug 16, 2026
Merged

Rooms are served by a build without WebSocket#166
EdmondDantes merged 1 commit into
mainfrom
feat/rooms-without-websocket

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

The room core stopped knowing what a connection is three PRs ago. The build gate
still said otherwise: src/room/*.c sat behind --enable-websocket, the Room
class was guarded, and each of the six HttpServer room methods carried an
#else arm that threw "Rooms require the extension built with WebSocket
support"
. A build configured with --disable-websocket now compiles the core,
registers Room, and delivers a publish from one thread to a recv() in
another.

The BC break

RoomDeliveryException moves to stubs/RoomExceptions.php and extends
HttpServerException instead of WebSocketException — rooms are served where
WebSocketException does not exist. A handler that caught WebSocketException
around Room::send() / HttpServer::send() no longer catches it. CHANGELOG.md
carries it under Unreleased, and test 059, which asserted the old chain, asserts
the new one.

Nothing else about the exception changed: same delivered / pending counts,
same message, same distinctness from WebSocketBackpressureException.

Two defects only this configuration could show

Both were found by building it, and both are fixed here:

  • getRuntimeStats() reported no room counters. The whole block sat behind
    the WebSocket guard, though the counters belong to the core. Every build
    reports them now; the ws_* key names stay, because php-claw and the tests
    read them.
  • room_hub_thread_sweep() sat behind the guard too. A thread that
    subscribed to a room has no other detach, so without WebSocket it never
    detached: a leaked mailbox and a live libuv handle at request shutdown
    (uv_loop_close failed (EBUSY)).

The gate

A step on the debug leg builds --disable-websocket in a copy of the tree and
runs tests/phpt/room. Review found the first version of it broken in three
ways, all fixed:

  • It probed with -d extension=<abs path> while the runner's scan dir already
    loads the same module by name from the WebSocket build. PHP keeps the first
    load and discards the second, so the probe saw TrueAsync\WebSocket and failed
    on every run. Reproduced locally with a scan-dir ini, and -d extension_dir
    fixes it — measured both ways.
  • It configured only --enable-http-server, so it built an HTTP/1-only,
    TLS-less extension: it answered "do the room sources link" rather than "does
    the product build". It now takes the same flags as the real build, minus
    WebSocket.
  • It ran one test that is allowed to skip, and run-tests exits 0 when
    everything skips. The ten connectionless room tests move from
    tests/phpt/websocket/ to tests/phpt/room/, the leak lane follows them, and
    the step now asserts that at least ten tests actually passed.

Measured

  • WebSocket build: tests/phpt 382 passed, 0 failed, 1 warned (an XFAIL section
    on a passing test, pre-existing). tests/valgrind-rooms.sh 10 tests, 0 leaked.
  • --disable-websocket build: no warnings of ours, nm -u shows no undefined
    symbol of ours, the extension loads, TrueAsync\Room and
    TrueAsync\RoomDeliveryException exist with WebSocket absent, and all 11
    tests in tests/phpt/room pass.

Corrected on the way

The "Rooms are not available" message told the caller to use
addWebSocketHandler(), which never allocated the hub in either build; the same
false claim was in stubs/HttpServer.php. Also fixed: a runtime message about
"the WebSocket topic hub", the Room stub describing a subscriber as a WebSocket
connection, docs/PLAN_RELIABLE_ROOM_PUBLISH.md on the exception's parent, and
three file comments that asserted the opposite of what the code now does.

The pub/sub core has not known what a connection is since it moved to
src/room/, but the build gate still said otherwise: the sources sat
behind --enable-websocket, the Room class was guarded, and every room
entry point had an #else arm that threw. All of that goes.

RoomDeliveryException moves to its own stub and extends
HttpServerException: rooms are served where WebSocketException does not
exist. This is a BC break and CHANGELOG says so.

Two defects only the new configuration could show, both fixed here:
getRuntimeStats() reported no room counters without WebSocket, and
room_hub_thread_sweep() sat behind the guard, so a thread that subscribed
to a room never detached — a leaked mailbox and a live libuv handle at
shutdown.

The ten connectionless room tests move to tests/phpt/room/ so the new CI
leg runs them all, and the leak lane follows them.
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Total lines: 82.24% → 82.33% (+0.09 pp)

File Baseline Current Δ Touched
src/core/http_connection.c 75.26% 75.55% +0.29 pp
src/http3/http3_callbacks.c 82.98% 83.09% +0.11 pp
src/http3/http3_io.c 89.09% 89.45% +0.36 pp
src/http3/http3_listener.c 76.88% 78.53% +1.65 pp
src/http3/http3_packet.c 85.22% 90.43% +5.22 pp
src/http_server.c 95.20% 95.20% +0.00 pp
src/http_server_class.c 75.07% 75.03% -0.04 pp
src/room/php_room.c 82.37% 82.46% +0.09 pp
src/websocket/php_websocket.c 78.68% 78.61% -0.07 pp
src/websocket/ws_session.c 90.41% 90.59% +0.18 pp

@EdmondDantes
EdmondDantes merged commit 1baa08d into main Aug 16, 2026
9 checks passed
@EdmondDantes
EdmondDantes deleted the feat/rooms-without-websocket branch August 16, 2026 10:42
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.

1 participant