From c7db3da9825ac9220c3b7a7fdec1d4c4a748920b Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 28 Aug 2026 08:26:08 -0400 Subject: [PATCH] Document that the multipart part-count cap only applies to the buffered form path CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT is enforced only in Server::read_content(), where parts are accumulated into req.form. The streaming ContentReader path keeps nothing and was never in scope, but this was undocumented (GHSA-923p-8q8g-xcqj). Note the split in the README and show how to bound the part count from inside a ContentReader handler. --- README.md | 6 ++++ .../pages/en/cookbook/s07-multipart-reader.md | 32 +++++++++++++++++++ .../pages/ja/cookbook/s07-multipart-reader.md | 32 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/README.md b/README.md index 59ba8d3..5c6ddcd 100644 --- a/README.md +++ b/README.md @@ -730,6 +730,12 @@ svr.Post("/content_receiver", }); ``` +`CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT` (default 1024) caps the number of +form-data parts only on the buffered path, where every part is accumulated into +`req.form`. The content receiver keeps nothing, so the cap does not apply here. +If your handler needs an upper bound on the number of parts, count them yourself +and return `false` from the callback to stop the parser. + ### Send content with the content provider ```cpp diff --git a/docs-src/pages/en/cookbook/s07-multipart-reader.md b/docs-src/pages/en/cookbook/s07-multipart-reader.md index 23c6bd0..c862c8d 100644 --- a/docs-src/pages/en/cookbook/s07-multipart-reader.md +++ b/docs-src/pages/en/cookbook/s07-multipart-reader.md @@ -66,6 +66,38 @@ svr.Post("/upload", Only a small chunk sits in memory at any moment, so gigabyte-scale files are no problem. +## Count the parts yourself + +There is a cap on the number of parts, `CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT` (1024 by default), but it only applies to the buffered path, where every part is accumulated into `req.form`. The `ContentReader` keeps nothing on the library side, so the cap does not apply here. + +If you want an upper bound, count the parts yourself and return `false` from the header callback. The parser stops right there. + +```cpp +svr.Post("/upload", + [](const httplib::Request &req, httplib::Response &res, + const httplib::ContentReader &content_reader) { + size_t count = 0; + + auto ok = content_reader( + [&](const httplib::FormData &file) { + if (++count > 100) { return false; } // stop here + return true; + }, + [&](const char *data, size_t len) { + return true; + }); + + if (!ok) { + res.status = httplib::StatusCode::BadRequest_400; + return; + } + + res.set_content("ok", "text/plain"); + }); +``` + +When `content_reader` returns `false`, set the response status yourself. The rest of the body is left unread and the connection is closed, so a client that is still sending sees the connection drop. + > **Warning:** When you use `HandlerWithContentReader`, `req.body` stays **empty**. Handle the body yourself inside the callbacks. > For the client side of multipart uploads, see [C07. Upload a file as multipart form data](../c07-multipart-upload). diff --git a/docs-src/pages/ja/cookbook/s07-multipart-reader.md b/docs-src/pages/ja/cookbook/s07-multipart-reader.md index c31cc69..bfa16b7 100644 --- a/docs-src/pages/ja/cookbook/s07-multipart-reader.md +++ b/docs-src/pages/ja/cookbook/s07-multipart-reader.md @@ -66,6 +66,38 @@ svr.Post("/upload", メモリには常に小さなチャンクしか載らないので、ギガバイト級のファイルでも扱えます。 +## パート数は自分で数える + +`CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT`(デフォルト1024)というパート数の上限がありますが、これが効くのは`req.form`にすべてのパートを溜め込むバッファリング側だけです。`ContentReader`はライブラリ側で何も溜め込まないので、この上限は適用されません。 + +パート数に上限をつけたいときは、自分で数えてヘッダーのコールバックから`false`を返してください。パースはその場で止まります。 + +```cpp +svr.Post("/upload", + [](const httplib::Request &req, httplib::Response &res, + const httplib::ContentReader &content_reader) { + size_t count = 0; + + auto ok = content_reader( + [&](const httplib::FormData &file) { + if (++count > 100) { return false; } // ここで打ち切る + return true; + }, + [&](const char *data, size_t len) { + return true; + }); + + if (!ok) { + res.status = httplib::StatusCode::BadRequest_400; + return; + } + + res.set_content("ok", "text/plain"); + }); +``` + +`content_reader`が`false`を返したら、レスポンスのステータスは自分でセットしてください。ボディの残りは読まずに接続を閉じるので、送信中のクライアントには接続が切れたように見えます。 + > **Warning:** `HandlerWithContentReader`を使うと、`req.body`は**空のまま**です。ボディはコールバック内で自分で処理してください。 > クライアント側でマルチパートを送る方法は[C07. ファイルをマルチパートフォームとしてアップロードする](../c07-multipart-upload)を参照してください。