The HTTP server frames a request body by Content-Length when its Transfer-Encoding is not exactly chunked.
LoadBody() reads only the first Transfer-Encoding header. With gzip, chunked, gzip, or identity followed by a second chunked header, the body is read by Content-Length, or not at all. RFC 9112 section 6.3 requires a 400 when chunked is not the final coding. The server supports no other coding. I now accept only a single chunked, across all Transfer-Encoding headers, and answer anything else with a 400. Unlike #36243, I still allow Content-Length next to chunked, as the existing interface_http.py test expects. The new rejection cases in httpserver_tests and interface_http.py fail on master and pass with this change. The framing check in the http_request fuzz target needed the same change, or it fails on Transfer-Encoding: chunked, .
This is one of several PRs from a large audit of Bitcoin Core that I did with the help of LLM tools. The audit produced its evidence all at once, which is why the PRs arrive together. I understand each change fully and will answer review questions myself.