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]

Reply via email to