Kaynağa Gözat

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.
yhirose 1 gün önce
ebeveyn
işleme
84ff13ec47
2 değiştirilmiş dosya ile 55 ekleme ve 4 silme
  1. 6 4
      httplib.h
  2. 49 0
      test/test.cc

+ 6 - 4
httplib.h

@@ -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;
     }
 

+ 49 - 0
test/test.cc

@@ -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_);