bryancall opened a new issue, #13778: URL: https://github.com/apache/trafficserver/issues/13778
## Summary If an HTTP/2 origin sends a bodyless response (HEADERS with END_STREAM) whose header block is larger than one IOBuffer block (about 4 KB), ATS fails to parse it. The client gets a 502 or 500 instead of the origin's response. It mostly shows up on redirects and 204s that carry a large `Content-Security-Policy` or `Set-Cookie`. ## Cause 1. `Http2Stream::send_headers`, which hands a received header to the HttpSM, prints the decoded response header into `_receive_buffer` one block at a time, adding a block whenever `print()` doesn't finish. A header of more than about 4 KB therefore spans several blocks, and a field can be split at a block boundary. 2. With `receive_end_stream` set on an outbound stream, it then signals `VC_EVENT_EOS`, not READ_READY or READ_COMPLETE. 3. `HttpSM::state_read_server_response_header` handles that EOS by parsing with `eof = true`. It logs `Server closed connection while reading response header` at this point, even though nothing closed. 4. `HTTPHdr::parse_resp` (`HdrTSOnly.cc`) loops over the reader one block at a time and passes the same `eof` flag for every block. When a field is cut at the end of the first block, `MIMEScanner::get` treats it as the end of input and returns `ParseResult::ERROR` (unterminated field). 5. The result is `handle_server_setup_error`, and the client gets an error. squid-style logs show the origin's real status (a 3xx or 204) next to the 5xx sent to the client, with `o_bytes` stopping just short of the field that crosses the 4 KB boundary. ## Observed / reproduction Reproduced on a 10.0.x build; `HdrTSOnly.cc` is identical on master. - An HTTP/2 origin returns `302` with no body and a header block of about 8.8 KB, including one long `Content-Security-Policy` field. - A GET through ATS: the origin logs a 302, and the client gets a 502 every time, with `o_bytes=202` and the request retried once (two attempts). - The same responses had no errors while that origin was reached over HTTP/1.1. - In production traffic, one bodyless 3xx endpoint failed 150 of 150 requests. ## Suggested fix Either of these, or both: - In `HTTPHdr::parse_resp`, pass `eof` only for the last block, when there is no further block to read. - In `Http2Stream`, write the whole decoded header into one block sized to fit it, so a header from an HTTP/2 frame is never split. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
