@@ -4642,52 +4642,79 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
46424642 }
46434643}
46444644
4645- inline bool read_content_without_length (Stream &strm,
4646- ContentReceiverWithProgress out) {
4645+ enum class ReadContentResult {
4646+ Success, // Successfully read the content
4647+ PayloadTooLarge, // The content exceeds the specified payload limit
4648+ Error // An error occurred while reading the content
4649+ };
4650+
4651+ inline ReadContentResult
4652+ read_content_without_length (Stream &strm, size_t payload_max_length,
4653+ ContentReceiverWithProgress out) {
46474654 char buf[CPPHTTPLIB_RECV_BUFSIZ];
46484655 uint64_t r = 0 ;
46494656 for (;;) {
46504657 auto n = strm.read (buf, CPPHTTPLIB_RECV_BUFSIZ);
4651- if (n == 0 ) { return true ; }
4652- if (n < 0 ) { return false ; }
4658+ if (n == 0 ) { return ReadContentResult::Success; }
4659+ if (n < 0 ) { return ReadContentResult::Error; }
4660+
4661+ // Check if adding this data would exceed the payload limit
4662+ if (r > payload_max_length ||
4663+ payload_max_length - r < static_cast <uint64_t >(n)) {
4664+ return ReadContentResult::PayloadTooLarge;
4665+ }
46534666
4654- if (!out (buf, static_cast <size_t >(n), r, 0 )) { return false ; }
4667+ if (!out (buf, static_cast <size_t >(n), r, 0 )) {
4668+ return ReadContentResult::Error;
4669+ }
46554670 r += static_cast <uint64_t >(n);
46564671 }
46574672
4658- return true ;
4673+ return ReadContentResult::Success ;
46594674}
46604675
46614676template <typename T>
4662- inline bool read_content_chunked (Stream &strm, T &x,
4663- ContentReceiverWithProgress out) {
4677+ inline ReadContentResult read_content_chunked (Stream &strm, T &x,
4678+ size_t payload_max_length,
4679+ ContentReceiverWithProgress out) {
46644680 const auto bufsiz = 16 ;
46654681 char buf[bufsiz];
46664682
46674683 stream_line_reader line_reader (strm, buf, bufsiz);
46684684
4669- if (!line_reader.getline ()) { return false ; }
4685+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
46704686
46714687 unsigned long chunk_len;
4688+ uint64_t total_len = 0 ;
46724689 while (true ) {
46734690 char *end_ptr;
46744691
46754692 chunk_len = std::strtoul (line_reader.ptr (), &end_ptr, 16 );
46764693
4677- if (end_ptr == line_reader.ptr ()) { return false ; }
4678- if (chunk_len == ULONG_MAX) { return false ; }
4694+ if (end_ptr == line_reader.ptr ()) { return ReadContentResult::Error ; }
4695+ if (chunk_len == ULONG_MAX) { return ReadContentResult::Error ; }
46794696
46804697 if (chunk_len == 0 ) { break ; }
46814698
4699+ // Check if adding this chunk would exceed the payload limit
4700+ if (total_len > payload_max_length ||
4701+ payload_max_length - total_len < chunk_len) {
4702+ return ReadContentResult::PayloadTooLarge;
4703+ }
4704+
4705+ total_len += chunk_len;
4706+
46824707 if (!read_content_with_length (strm, chunk_len, nullptr , out)) {
4683- return false ;
4708+ return ReadContentResult::Error ;
46844709 }
46854710
4686- if (!line_reader.getline ()) { return false ; }
4711+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
46874712
4688- if (strcmp (line_reader.ptr (), " \r\n " ) != 0 ) { return false ; }
4713+ if (strcmp (line_reader.ptr (), " \r\n " ) != 0 ) {
4714+ return ReadContentResult::Error;
4715+ }
46894716
4690- if (!line_reader.getline ()) { return false ; }
4717+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
46914718 }
46924719
46934720 assert (chunk_len == 0 );
@@ -4704,14 +4731,18 @@ inline bool read_content_chunked(Stream &strm, T &x,
47044731 //
47054732 // According to the reference code in RFC 9112, cpp-httplib now allows
47064733 // chunked transfer coding data without the final CRLF.
4707- if (!line_reader.getline ()) { return true ; }
4734+ if (!line_reader.getline ()) { return ReadContentResult::Success ; }
47084735
47094736 size_t trailer_header_count = 0 ;
47104737 while (strcmp (line_reader.ptr (), " \r\n " ) != 0 ) {
4711- if (line_reader.size () > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false ; }
4738+ if (line_reader.size () > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4739+ return ReadContentResult::Error;
4740+ }
47124741
47134742 // Check trailer header count limit
4714- if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false ; }
4743+ if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {
4744+ return ReadContentResult::Error;
4745+ }
47154746
47164747 // Exclude line terminator
47174748 constexpr auto line_terminator_len = 2 ;
@@ -4724,10 +4755,10 @@ inline bool read_content_chunked(Stream &strm, T &x,
47244755
47254756 trailer_header_count++;
47264757
4727- if (!line_reader.getline ()) { return false ; }
4758+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
47284759 }
47294760
4730- return true ;
4761+ return ReadContentResult::Success ;
47314762}
47324763
47334764inline bool is_chunked_transfer_encoding (const Headers &headers) {
@@ -4801,9 +4832,26 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
48014832 auto exceed_payload_max_length = false ;
48024833
48034834 if (is_chunked_transfer_encoding (x.headers )) {
4804- ret = read_content_chunked (strm, x, out);
4835+ auto result = read_content_chunked (strm, x, payload_max_length, out);
4836+ if (result == ReadContentResult::Success) {
4837+ ret = true ;
4838+ } else if (result == ReadContentResult::PayloadTooLarge) {
4839+ exceed_payload_max_length = true ;
4840+ ret = false ;
4841+ } else {
4842+ ret = false ;
4843+ }
48054844 } else if (!has_header (x.headers , " Content-Length" )) {
4806- ret = read_content_without_length (strm, out);
4845+ auto result =
4846+ read_content_without_length (strm, payload_max_length, out);
4847+ if (result == ReadContentResult::Success) {
4848+ ret = true ;
4849+ } else if (result == ReadContentResult::PayloadTooLarge) {
4850+ exceed_payload_max_length = true ;
4851+ ret = false ;
4852+ } else {
4853+ ret = false ;
4854+ }
48074855 } else {
48084856 auto is_invalid_value = false ;
48094857 auto len = get_header_value_u64 (
0 commit comments