Skip to content

feat(sse): first-class Server-Sent Events API on HttpResponse (#3)#91

Merged
EdmondDantes merged 1 commit into
mainfrom
3-server-sent-events-sse-support
Jun 26, 2026
Merged

feat(sse): first-class Server-Sent Events API on HttpResponse (#3)#91
EdmondDantes merged 1 commit into
mainfrom
3-server-sent-events-sse-support

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

Closes #3.

First-class Server-Sent Events (text/event-stream) as thin WHATWG §9.2
framing helpers over the existing send() streaming pipeline — SSE is not a
separate protocol, so the same handler streams over HTTP/1.1, HTTP/2 and
HTTP/3
(the client picks the protocol via ALPN / Upgrade).

The old 3-server-sent-events-sse-support branch was stale; this branch was
recreated from main and reimplemented from scratch.

API (HttpResponse)

Method Purpose
sseStart(): static Commit SSE headers, lock into streaming mode (optional — first event auto-starts).
sseEvent(?string $data, ?string $event, ?string $id, ?int $retry): static Emit one event record.
sseComment(string $text = ""): static : heartbeat to hold the connection through proxy idle timeouts.
sseRetry(int $milliseconds): static Bare retry: reconnect-delay directive.

sseStart() sets Content-Type: text/event-stream, Cache-Control: no-cache, no-transform, X-Accel-Buffering: no and marks the response non-compressible
(a buffering gzip stream would defeat real-time delivery).

Correctness

  • Framing (WHATWG §9.2): multiline data is split into one data: line per
    LF/CR/CRLF; conventional field order id, event, retry, data; record
    terminated by a blank line.
  • Injection-safe: single-line fields (event, id) reject CR/LF, id
    rejects NUL, retry rejects negatives — all before the stream commits, so
    a rejected call leaves the response buffered.
  • Backpressure: dispatch goes through the protocol stream_ops->append_chunk,
    which suspends the handler under H2/H3 flow control; a dead stream surfaces as
    499, matching send().

Implementation

  • Dedicated TU src/http_sse.c (wired into config.m4 / config.w32 / CMakeLists.txt).
  • Stub + regenerated arginfo; methods clang-format clean (tabs).

Tests

  • h1/022 — chunked framing + headers + Transfer-Encoding: chunked + streaming-lock + telemetry
  • h1/023 — full input-validation surface (buffered route)
  • h2/025 — h2c DATA-frame stream (headers survive HPACK)
  • h3/041 — QUIC stream via h3client (body byte-exact, status, header count, h3 stats)

Full server suite 214/214 locally (1 pre-existing XFAIL warn, unrelated).

Add text/event-stream helpers — sseStart(), sseEvent($data, $event, $id,
$retry), sseComment() and sseRetry() — layered on the existing send()
streaming pipeline, so the same handler streams over HTTP/1.1, HTTP/2 and
HTTP/3 (the client picks the protocol via ALPN/Upgrade).

sseStart() sets the canonical headers (Content-Type: text/event-stream,
Cache-Control: no-cache, no-transform, X-Accel-Buffering: no) and marks the
response non-compressible. Framing follows WHATWG §9.2: multiline data is
split per line, single-line fields reject CR/LF (injection) and id rejects
NUL. All input validation runs before the stream commits, so a rejected
call leaves the response buffered.

Implemented in a dedicated TU (src/http_sse.c). phpt coverage for H1/H2/H3
end-to-end framing plus the validation surface; example in
examples/sse-server.php.
@EdmondDantes EdmondDantes linked an issue Jun 26, 2026 that may be closed by this pull request
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Total lines: 75.71% → 76.04% (+0.33 pp)

File Baseline Current Δ Touched
src/core/http_connection.c 68.09% 70.08% +1.99 pp
src/http1/http1_format.c 94.51% 95.05% +0.55 pp
src/http3/http3_callbacks.c 61.04% 61.47% +0.43 pp
src/http3/http3_listener.c 64.74% 66.35% +1.60 pp
src/http_response.c 87.35% 87.70% +0.35 pp
src/http_response_server_api.c 87.30% 88.36% +1.06 pp
src/http_sse.c 0.00% 84.24% +84.24 pp

@EdmondDantes EdmondDantes merged commit a8a2a06 into main Jun 26, 2026
8 checks passed
@EdmondDantes EdmondDantes deleted the 3-server-sent-events-sse-support branch June 27, 2026 03:41
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.

Server-Sent Events (SSE) support

1 participant