Answer 501 to a request with a transfer coding besides chunked

A request whose Transfer-Encoding ended in chunked but listed more
(another coding before chunked, or chunked twice) was read with only
the chunked framing removed, so the handler got a body still in the
other coding. RFC 9112 6.1 lets a server answer 501 to a transfer
coding it does not implement. The server now accepts only a single
Transfer-Encoding field whose value is chunked, as Go and nginx do,
and answers any other list ending in chunked with 501 and closes the
connection. A list whose final coding is not chunked still gets 400.
This commit is contained in:
yhirose committed 2026-10-22 07:58:28 -04:00
1 parent 32a0ca8b35
commit fbd3e2cc9b
2 files changed
+39 -22

No files matched your search

+29 -22
View File
@@ -30108,32 +30108,39 @@ TEST(RequestSmugglingTest, NonFinalChunkedTransferEncodingRejected) {
EXPECT_EQ("HTTP/1.1 400 Bad Request",
response.substr(0, response.find("\r\n")));
}
}
// A sequence ending in chunked stays valid.
auto req = "POST /test HTTP/1.1\r\n"
"Host: localhost\r\n"
"Transfer-Encoding: gzip, chunked\r\n"
"Connection: close\r\n"
"\r\n"
"0\r\n\r\n";
TEST(RequestSmugglingTest, TransferEncodingOtherThanChunkedNotImplemented) {
Server svr;
svr.Post("/test", [&](const Request &, Response &res) {
res.set_content("ok", "text/plain");
});
std::string response;
ASSERT_TRUE(send_request(1, req, &response));
EXPECT_EQ("HTTP/1.1 200 OK", response.substr(0, response.find("\r\n")));
auto port = svr.bind_to_any_port(HOST);
ASSERT_GT(port, 0);
thread t = thread([&] { svr.listen_after_bind(); });
auto se = detail::scope_exit([&] {
svr.stop();
t.join();
ASSERT_FALSE(svr.is_running());
});
svr.wait_until_ready();
// ...including when it is spread over several lines.
auto split_req = "POST /test HTTP/1.1\r\n"
"Host: localhost\r\n"
"Transfer-Encoding: gzip\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n"
"\r\n"
"0\r\n\r\n";
for (auto transfer_encoding :
{"Transfer-Encoding: gzip, chunked\r\n",
"Transfer-Encoding: gzip\r\nTransfer-Encoding: chunked\r\n",
"Transfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n"}) {
auto req = std::string("POST /test HTTP/1.1\r\nHost: localhost\r\n") +
transfer_encoding + "\r\n0\r\n\r\n";
std::string split_response;
ASSERT_TRUE(send_request(1, split_req, &split_response));
EXPECT_EQ("HTTP/1.1 200 OK",
split_response.substr(0, split_response.find("\r\n")));
std::string response;
ASSERT_TRUE(send_request(1, req, &response, port)) << transfer_encoding;
EXPECT_EQ("HTTP/1.1 501 Not Implemented",
response.substr(0, response.find("\r\n")))
<< transfer_encoding;
EXPECT_NE(std::string::npos, response.find("Connection: close"))
<< transfer_encoding;
}
}
// Regression for issue #2450: a DELETE without Content-Length on a