fix(php-wasm/web): do not build a request body stream for HEAD in tcpOverFetch#3632
Open
erseco wants to merge 1 commit into
Open
fix(php-wasm/web): do not build a request body stream for HEAD in tcpOverFetch#3632erseco wants to merge 1 commit into
erseco wants to merge 1 commit into
Conversation
…OverFetch
`RawBytesFetch.parseHttpRequest()` builds an outbound `ReadableStream` body
for every parsed request whose method is not exactly `'GET'`, then
constructs `new Request(url, { method, body })`. The Fetch spec forbids
GET and HEAD from carrying a request body, so for HEAD this raises:
TypeError: Failed to construct 'Request':
Request with GET/HEAD method cannot have body.
The exception surfaces as an uncaught promise rejection from
`tcp-over-fetch-websocket.fetchOverTLS` and corrupts the cURL call that
emitted the HEAD probe. cURL emits HEAD whenever `CURLOPT_NOBODY` is set
— a common path used by libraries for cheap size pre-checks and by some
CMS plugins during URL-import flows.
Reported from a Moodle build that runs on `@php-wasm/web` (Moodle
Playground): downloads through Moodle's built-in URL repository all
silently failed because the HEAD probe issued before each GET threw the
above TypeError, leaving cURL in a half-state. Mirroring the GET branch
to also exclude HEAD makes the error go away and unblocks the download
chain. See:
- ateeducacion/moodle-playground#92 (root cause analysis)
- ateeducacion/moodle-playground#93 (downstream string-replace workaround)
Adds a `parseHttpRequest` test that asserts `request.body === null`
for a parsed HEAD request, mirroring the existing GET-shaped tests in
the same `describe('RawBytesFetch', ...)` block.
This was referenced May 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3631
Problem
RawBytesFetch.parseHttpRequest()inpackages/php-wasm/web/src/lib/tcp-over-fetch-websocket.ts(line 643) builds an outboundReadableStreambody for every parsed request whose method is not exactly'GET', then constructs:The Fetch spec forbids GET and HEAD from carrying a request body, so for HEAD this raises:
That exception surfaces as an uncaught promise rejection at the top of
parseHttpRequest(tcp-over-fetch-websocket.ts:736in the current minified bundle). Whatever cURL call emitted the HEAD probe is left in a half-state — the GET request that follows still completes but the buffered bytes ride on top of corrupted internal state.Where this trips in practice
Moodle Playground (a Moodle build that uses
@php-wasm/web) reports that downloads through Moodle's built-in URL downloader repository (repository_url) all silently fail because Moodle's cURL emits HEAD wheneverCURLOPT_NOBODYis set — a common path used for cheap size pre-checks and during redirect follow-ups. Local form-upload paths are unaffected because they never traversetcpOverFetch.Console excerpt from the bug report:
Downstream tracking:
@php-wasm/webbundle, to be removed once this PR lands and@php-wasm/webis bumped.Fix
Mirror the GET branch and also exclude HEAD from the body-building path. One-line change in
tcp-over-fetch-websocket.ts:Tests
Added a
parseHttpRequest should not build a body stream for HEAD requestscase inpackages/php-wasm/web/src/test/tcp-over-fetch-websocket.spec.ts, mirroring the existing GET-shaped tests in the samedescribe('RawBytesFetch', ...)block. Without the fix, the assertionexpect(request.body).toBeNull()fails becausenew Request('http://host/probe', { method: 'HEAD', body: <stream> })throws before the assertion runs.Local CI not run here (the fork is shallow); please rely on the action workflow.
Manual verification
Reproduced the URL-downloader regression in Moodle Playground, confirmed it still fails on
WordPress/wordpress-playground@trunk, then verified it goes away after rebuilding@php-wasm/webwith this patch applied. The chrome-devtools test was the built-in URL downloader Moodle repository (<img class="fp-repo-icon" src=".../repository_url/.../icon">): pasting a remote image URL into the file picker now downloads and renders correctly, whereas before this fix it produced a broken-thumbnail icon and a 404 fromdraftfile.php.