Skip to content

h3: deliver $res->sendFile() over HTTP/3 (#60) - #64

Merged
EdmondDantes merged 1 commit into
mainfrom
60-h3-static-delivery
May 31, 2026
Merged

EdmondDantes merged 1 commit into
mainfrom
60-h3-static-delivery

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

Wires $res->sendFile() to the H3 static-file pump. Previously sendFile over H3 returned 200 + empty body + no FIN.

What was wrong

The H3 pump (http3_static_response.c, in the h3_stream_ops vtable via .send_static_response) was fully written but never invoked — h3_handler_coroutine_dispose never checked http_response_has_send_file() / called http_send_file_dispatch(). H1/H2 do.

Fix

Mirror H2 (h2_sendfile_arm/h2_sendfile_on_done): in the buffered dispose branch, hand off to the pump via http_send_file_dispatch with an on_done that runs the deferred dispose tail. Because submit_response reads s->response_zv live and the pump submits asynchronously, the dispose must not drop the response zval / release the stream on hand-off — the tail (extracted into h3_dispose_tail) runs from on_done after the file streams. Arm-failure (synthesized 500) falls through to the buffered submit. Same async-refcount care as #62.

Verification

  • Re-enables tests/phpt/server/h3/026 — sendFile byte-integrity (sha1) across sizes 1 / 16383 / 16384 / 16385 / 49152 / 50000 (the 16 KiB chunk boundary).
  • h3 phpt 26/26.
  • c=16 sendFile load: 4000/4000 2xx, 0 errored, 12.6k req/s.
  • Sustained c=64 sendFile stress: no crash (async hand-off holds).

Follow-up (still under #60)

Static mount auto-routing (addStaticHandler → http_static_try_serve in the H3 dispatch, like H2) — separate, smaller commit.

Addresses #60.

The H3 static-file pump (http3_static_response.c, registered in the
h3_stream_ops vtable) was complete but never invoked: h3_handler_coroutine_
dispose never checked http_response_has_send_file() nor called
http_send_file_dispatch(). sendFile() over H3 therefore returned 200 with an
empty body and no FIN.

Wire it the way H2 does (h2_sendfile_arm / on_done): in the buffered dispose
branch hand off to the pump via http_send_file_dispatch with an on_done that
runs the deferred dispose tail. submit_response reads s->response_zv live and
the pump submits asynchronously, so the dispose must NOT drop the response
zval or release the stream when it hands off — the tail (extracted into
h3_dispose_tail) runs from on_done once the file has streamed. An arm failure
(synthesized 500) falls through to the regular buffered submit.

Re-enables tests/phpt/server/h3/026 (sendFile byte-integrity across the
16 KiB chunk boundary). h3 phpt 26/26; c=16 sendFile load 4000/4000 2xx,
0 errored; sustained c=64 sendFile — no crash.

Static mount auto-routing (addStaticHandler / http_static_try_serve in the
H3 dispatch) remains a follow-up under #60.
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Total lines: 80.86% → 80.77% (-0.09 pp)

File Baseline Current Δ Touched
src/http3/http3_callbacks.c 76.63% 79.21% +2.57 pp
src/http3/http3_connection.c 81.74% 81.79% +0.05 pp
src/http3/http3_dispatch.c 77.44% 78.53% +1.10 pp ●
src/http3/http3_listener.c 74.80% 66.21% -8.59 pp
src/http3/http3_static_response.c 0.00% 16.49% +16.49 pp

@EdmondDantes
EdmondDantes merged commit 2ac39df into main May 31, 2026
8 checks passed
@EdmondDantes
EdmondDantes deleted the 60-h3-static-delivery branch May 31, 2026 10: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.

1 participant