Explorar o código

Report a response body over the limit as ExceedMaxPayloadSize

A buffered request whose response body went over set_payload_max_length(),
also once decompressed, failed with Error::Read, while the Stream API
reports the same limit as Error::ExceedMaxPayloadSize. read_content()
already marks it with a 413 status, so map that status to
ExceedMaxPayloadSize next to the 415 and 500 the client maps already.
yhirose hai 1 día
pai
achega
d99c697be6

+ 2 - 0
README.md

@@ -1268,6 +1268,8 @@ cli.set_max_timeout(std::chrono::seconds(5));
 cli.set_payload_max_length(1024 * 1024 * 512); // 512MB
 ```
 
+A response body over the limit, also once decompressed, fails the request with `Error::ExceedMaxPayloadSize`.
+
 ### Receive content with a content receiver
 
 ```c++

+ 1 - 0
docs-src/pages/en/cookbook/c17-error-codes.md

@@ -30,6 +30,7 @@ Use `if (res)` to check success. On failure, `res.error()` returns a `httplib::E
 | `Error::Connection` | Couldn't connect to the server, or the `Client` was built from a URL it can't use (userinfo or a backslash in the authority, a bad port). In the latter case `cli.is_valid()` is `false` |
 | `Error::ConnectionTimeout` | Connection timeout (`set_connection_timeout`) |
 | `Error::Read` / `Error::Write` | Error during send or receive. A timeout from `set_read_timeout` or `set_max_timeout` is also reported as `Error::Read` |
+| `Error::ExceedMaxPayloadSize` | The response body is larger than `set_payload_max_length()` allows, also once decompressed |
 | `Error::Timeout` | A body read timed out in `stream::Get()` or `SSEClient` |
 | `Error::ExceedRedirectCount` | Too many redirects |
 | `Error::SSLConnection` | TLS handshake failed |

+ 1 - 0
docs-src/pages/ja/cookbook/c17-error-codes.md

@@ -30,6 +30,7 @@ if (res) {
 | `Error::Connection` | サーバーに接続できなかった。または、使えないURL(authorityにuserinfoやバックスラッシュがある、ポートが不正など)から`Client`を作った。後者の場合は`cli.is_valid()`が`false`になる |
 | `Error::ConnectionTimeout` | 接続タイムアウト(`set_connection_timeout`) |
 | `Error::Read` / `Error::Write` | 送受信中のエラー。`set_read_timeout`や`set_max_timeout`によるタイムアウトも`Error::Read`になる |
+| `Error::ExceedMaxPayloadSize` | レスポンスボディが`set_payload_max_length()`の上限を超えた(解凍後に超えた場合も含む) |
 | `Error::Timeout` | `stream::Get()`や`SSEClient`で、ボディの読み取りがタイムアウトした |
 | `Error::ExceedRedirectCount` | リダイレクト回数が上限を超えた |
 | `Error::SSLConnection` | TLSハンドシェイクに失敗 |

+ 6 - 2
httplib.h

@@ -17019,9 +17019,13 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
                                 std::move(progress), std::move(out),
                                 decompress_)) {
         if (error != Error::Canceled) {
-          // Tell the caller apart from a plain read failure when the body could
-          // not be decoded because of its Content-Encoding.
+          // Tell the caller apart from a plain read failure when the body was
+          // over the payload limit or could not be decoded because of its
+          // Content-Encoding.
           switch (content_status) {
+          case StatusCode::PayloadTooLarge_413:
+            error = Error::ExceedMaxPayloadSize;
+            break;
           case StatusCode::UnsupportedMediaType_415:
             error = Error::UnsupportedContentEncoding;
             break;

+ 33 - 0
test/test.cc

@@ -14389,6 +14389,39 @@ TEST(ClientPayloadTest, DoesNotReserveTheClaimedLength) {
   EXPECT_FALSE(cli.Get("/"));
 }
 
+// A response body over the limit is reported as ExceedMaxPayloadSize, as the
+// Stream API does, not as a plain read failure
+TEST(ClientPayloadTest, OverLimitIsExceedMaxPayloadSize) {
+  Server svr;
+  svr.Get("/big", [](const Request &, Response &res) {
+    res.set_content(std::string(100000, 'b'), "text/plain");
+  });
+
+  auto port = svr.bind_to_any_port(HOST);
+  auto thread = std::thread([&]() { svr.listen_after_bind(); });
+  auto se = detail::scope_exit([&] {
+    svr.stop();
+    thread.join();
+  });
+  svr.wait_until_ready();
+
+  Client cli(HOST, port);
+  cli.set_payload_max_length(1000);
+
+  auto res = cli.Get("/big", Headers{{"Accept-Encoding", ""}});
+  EXPECT_EQ(Error::ExceedMaxPayloadSize, res.error());
+
+  res = cli.Get("/big", Headers{{"Accept-Encoding", ""}},
+                [](const char *, size_t) { return true; });
+  EXPECT_EQ(Error::ExceedMaxPayloadSize, res.error());
+
+#ifdef CPPHTTPLIB_ZLIB_SUPPORT
+  // Small on the wire, over the limit once decompressed
+  res = cli.Get("/big", Headers{{"Accept-Encoding", "gzip"}});
+  EXPECT_EQ(Error::ExceedMaxPayloadSize, res.error());
+#endif
+}
+
 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
 // `payload_max_length` is not enforced on decompressed body in ContentReader
 // path.