mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-23 11:54:42 +00:00
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:
1 parent
32a0ca8b35
commit
fbd3e2cc9b
2 files changed
+39
-22
No files matched your search
@@ -15135,6 +15135,16 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
|||||||
return write_response(strm, close_connection, req, res);
|
return write_response(strm, close_connection, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RFC 9112 6.1: a lone "chunked" is the only transfer coding implemented
|
||||||
|
if (req.has_header("Transfer-Encoding") &&
|
||||||
|
(req.get_header_value_count("Transfer-Encoding") != 1 ||
|
||||||
|
!detail::case_ignore::equal(req.get_header_value("Transfer-Encoding"),
|
||||||
|
"chunked"))) {
|
||||||
|
connection_closed = true;
|
||||||
|
res.status = StatusCode::NotImplemented_501;
|
||||||
|
return write_response(strm, close_connection, req, res);
|
||||||
|
}
|
||||||
|
|
||||||
// RFC 9112 §6.1: a Transfer-Encoding alongside a Content-Length, or in an
|
// RFC 9112 §6.1: a Transfer-Encoding alongside a Content-Length, or in an
|
||||||
// HTTP/1.0 request, is honoured, but the connection closes after the
|
// HTTP/1.0 request, is honoured, but the connection closes after the
|
||||||
// response.
|
// response.
|
||||||
|
|||||||
+29
-22
@@ -30108,32 +30108,39 @@ TEST(RequestSmugglingTest, NonFinalChunkedTransferEncodingRejected) {
|
|||||||
EXPECT_EQ("HTTP/1.1 400 Bad Request",
|
EXPECT_EQ("HTTP/1.1 400 Bad Request",
|
||||||
response.substr(0, response.find("\r\n")));
|
response.substr(0, response.find("\r\n")));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A sequence ending in chunked stays valid.
|
TEST(RequestSmugglingTest, TransferEncodingOtherThanChunkedNotImplemented) {
|
||||||
auto req = "POST /test HTTP/1.1\r\n"
|
Server svr;
|
||||||
"Host: localhost\r\n"
|
svr.Post("/test", [&](const Request &, Response &res) {
|
||||||
"Transfer-Encoding: gzip, chunked\r\n"
|
res.set_content("ok", "text/plain");
|
||||||
"Connection: close\r\n"
|
});
|
||||||
"\r\n"
|
|
||||||
"0\r\n\r\n";
|
|
||||||
|
|
||||||
std::string response;
|
auto port = svr.bind_to_any_port(HOST);
|
||||||
ASSERT_TRUE(send_request(1, req, &response));
|
ASSERT_GT(port, 0);
|
||||||
EXPECT_EQ("HTTP/1.1 200 OK", response.substr(0, response.find("\r\n")));
|
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.
|
for (auto transfer_encoding :
|
||||||
auto split_req = "POST /test HTTP/1.1\r\n"
|
{"Transfer-Encoding: gzip, chunked\r\n",
|
||||||
"Host: localhost\r\n"
|
"Transfer-Encoding: gzip\r\nTransfer-Encoding: chunked\r\n",
|
||||||
"Transfer-Encoding: gzip\r\n"
|
"Transfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n"}) {
|
||||||
"Transfer-Encoding: chunked\r\n"
|
auto req = std::string("POST /test HTTP/1.1\r\nHost: localhost\r\n") +
|
||||||
"Connection: close\r\n"
|
transfer_encoding + "\r\n0\r\n\r\n";
|
||||||
"\r\n"
|
|
||||||
"0\r\n\r\n";
|
|
||||||
|
|
||||||
std::string split_response;
|
std::string response;
|
||||||
ASSERT_TRUE(send_request(1, split_req, &split_response));
|
ASSERT_TRUE(send_request(1, req, &response, port)) << transfer_encoding;
|
||||||
EXPECT_EQ("HTTP/1.1 200 OK",
|
EXPECT_EQ("HTTP/1.1 501 Not Implemented",
|
||||||
split_response.substr(0, split_response.find("\r\n")));
|
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
|
// Regression for issue #2450: a DELETE without Content-Length on a
|
||||||
|
|||||||
Reference in new issue
Block a user