Http1Driver chunk-encodes 204/304 responses and appends a terminating chunk
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 1.3k
- Forks
- 103
- PR merge metrics
- No merged PRs in 30d
Description
Version
amphp/http-server v3.4.6 (via pestphp/pest-plugin-browser), PHP 8.4, Linux.
Problem
Http1Driver::write() decides on chunked encoding with
$chunked = !$shouldClose
&& (!isset($headers["content-length"]) || $trailers !== null)
&& $protocol === "1.1"
&& $status >= HttpStatus::OK;
A 204 No Content (or 304 Not Modified) response that carries no Content-Length header is therefore sent as
HTTP/1.1 204 No Content
transfer-encoding: chunked
...
0\r\n\r\n
RFC 7230 §3.3.1 and §3.3.3: a server MUST NOT send a Transfer-Encoding header field in any response with a status code of 1xx or 204, and 204/304 responses have no message body regardless of header fields. Clients follow the RFC: Chromium treats the 204 as complete after the headers and returns the keep-alive socket to its pool with the five bytes 0\r\n\r\n still unread. The next request on that socket then fails with net::ERR_INVALID_HTTP_RESPONSE because the parser reads the stray chunk terminator before the status line.
How it was found
A Laravel app served through pest-plugin-browser's in-process server (which uses this package) fires POST /cookie-consent/impression → 204 early in page load; when Chromium reused that socket for the next script request, the script silently failed and the page never booted. Confirmed with a Chromium NetLog (SOCKET_BYTES_RECEIVED byte_count: 5 after the 204, then HTTP_STREAM_PARSER_READ_HEADERS net_error: -370 on the following request) and with tcpdump on the loopback interface. Setting Content-Length: 0 on the 204 makes the driver skip the body and the problem disappears.
Expected
Skip the body and never emit Transfer-Encoding for 1xx/204/304 the way HEAD is already special-cased in the same method; treating an explicit Content-Length: 0 from the handler as an opt-out is not enough because frameworks (Symfony HttpFoundation, for one) strip Content-Length from those responses on purpose.
Happy to send a PR if you agree with the shape.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in Http1Driver::write() and compare the existing HEAD special case with the response framing logic described in the issue. Verify that 1xx, 204, and 304 responses omit Transfer-Encoding and the terminating chunk while ordinary responses retain their current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend, networking
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 76/100