mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-12 10:22:39 +00:00
Treat a payload max length of 0 as unlimited on every read path
set_payload_max_length(0) is documented to disable the limit, but the readers for a chunked body and for a body delimited by the connection closing compared the size against the limit without checking for 0. A server configured that way answered a chunked request with 413, and a client failed to read a chunked or unframed response. A body with Content-Length was already unlimited. Give both readers the same `payload_max_length > 0` guard the other payload checks use.
This commit is contained in:
@@ -8412,8 +8412,9 @@ read_content_without_length(Stream &strm, size_t payload_max_length,
|
||||
if (n < 0) { return ReadContentResult::Error; }
|
||||
|
||||
// Check if adding this data would exceed the payload limit
|
||||
if (r > payload_max_length ||
|
||||
payload_max_length - r < static_cast<size_t>(n)) {
|
||||
if (payload_max_length > 0 &&
|
||||
(r > payload_max_length ||
|
||||
payload_max_length - r < static_cast<size_t>(n))) {
|
||||
return ReadContentResult::PayloadTooLarge;
|
||||
}
|
||||
|
||||
@@ -8448,8 +8449,9 @@ inline ReadContentResult read_content_chunked(Stream &strm, T &x,
|
||||
return ReadContentResult::Success;
|
||||
}
|
||||
|
||||
if (total_len > payload_max_length ||
|
||||
payload_max_length - total_len < static_cast<size_t>(n)) {
|
||||
if (payload_max_length > 0 &&
|
||||
(total_len > payload_max_length ||
|
||||
payload_max_length - total_len < static_cast<size_t>(n))) {
|
||||
return ReadContentResult::PayloadTooLarge;
|
||||
}
|
||||
|
||||
|
||||
@@ -21191,6 +21191,55 @@ TEST(ClientResponseSmugglingTest, BodylessResponseNotRejected) {
|
||||
}
|
||||
}
|
||||
|
||||
// A payload max length of 0 means no limit, however the body is framed.
|
||||
TEST(PayloadMaxLengthZeroTest, ClientReadsChunkedAndUnframedResponse) {
|
||||
for (const char *response : {"HTTP/1.1 200 OK\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"5\r\nhello\r\n0\r\n\r\n",
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"hello"}) {
|
||||
with_single_response(response, [&](Client &cli) {
|
||||
cli.set_payload_max_length(0);
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res) << response;
|
||||
EXPECT_EQ("hello", res->body) << response;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PayloadMaxLengthZeroTest, ServerReadsChunkedRequest) {
|
||||
Server svr;
|
||||
svr.set_payload_max_length(0);
|
||||
svr.Post("/echo", [](const Request &req, Response &res) {
|
||||
res.set_content(req.body, "text/plain");
|
||||
});
|
||||
|
||||
auto port = svr.bind_to_any_port(HOST);
|
||||
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();
|
||||
|
||||
Client cli(HOST, port);
|
||||
auto res = cli.Post(
|
||||
"/echo",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
sink.write("hello", 5);
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
EXPECT_EQ("hello", res->body);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
TEST_F(OpenStreamTest, Gzip) {
|
||||
Client cli("127.0.0.1", port_);
|
||||
|
||||
Reference in New Issue
Block a user