@@ -4642,52 +4642,79 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
4642
4642
}
4643
4643
}
4644
4644
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) {
4647
4654
char buf[CPPHTTPLIB_RECV_BUFSIZ];
4648
4655
uint64_t r = 0 ;
4649
4656
for (;;) {
4650
4657
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
+ }
4653
4666
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
+ }
4655
4670
r += static_cast <uint64_t >(n);
4656
4671
}
4657
4672
4658
- return true ;
4673
+ return ReadContentResult::Success ;
4659
4674
}
4660
4675
4661
4676
template <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) {
4664
4680
const auto bufsiz = 16 ;
4665
4681
char buf[bufsiz];
4666
4682
4667
4683
stream_line_reader line_reader (strm, buf, bufsiz);
4668
4684
4669
- if (!line_reader.getline ()) { return false ; }
4685
+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
4670
4686
4671
4687
unsigned long chunk_len;
4688
+ uint64_t total_len = 0 ;
4672
4689
while (true ) {
4673
4690
char *end_ptr;
4674
4691
4675
4692
chunk_len = std::strtoul (line_reader.ptr (), &end_ptr, 16 );
4676
4693
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 ; }
4679
4696
4680
4697
if (chunk_len == 0 ) { break ; }
4681
4698
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
+
4682
4707
if (!read_content_with_length (strm, chunk_len, nullptr , out)) {
4683
- return false ;
4708
+ return ReadContentResult::Error ;
4684
4709
}
4685
4710
4686
- if (!line_reader.getline ()) { return false ; }
4711
+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
4687
4712
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
+ }
4689
4716
4690
- if (!line_reader.getline ()) { return false ; }
4717
+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
4691
4718
}
4692
4719
4693
4720
assert (chunk_len == 0 );
@@ -4704,14 +4731,18 @@ inline bool read_content_chunked(Stream &strm, T &x,
4704
4731
//
4705
4732
// According to the reference code in RFC 9112, cpp-httplib now allows
4706
4733
// chunked transfer coding data without the final CRLF.
4707
- if (!line_reader.getline ()) { return true ; }
4734
+ if (!line_reader.getline ()) { return ReadContentResult::Success ; }
4708
4735
4709
4736
size_t trailer_header_count = 0 ;
4710
4737
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
+ }
4712
4741
4713
4742
// 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
+ }
4715
4746
4716
4747
// Exclude line terminator
4717
4748
constexpr auto line_terminator_len = 2 ;
@@ -4724,10 +4755,10 @@ inline bool read_content_chunked(Stream &strm, T &x,
4724
4755
4725
4756
trailer_header_count++;
4726
4757
4727
- if (!line_reader.getline ()) { return false ; }
4758
+ if (!line_reader.getline ()) { return ReadContentResult::Error ; }
4728
4759
}
4729
4760
4730
- return true ;
4761
+ return ReadContentResult::Success ;
4731
4762
}
4732
4763
4733
4764
inline 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,
4801
4832
auto exceed_payload_max_length = false ;
4802
4833
4803
4834
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
+ }
4805
4844
} 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
+ }
4807
4855
} else {
4808
4856
auto is_invalid_value = false ;
4809
4857
auto len = get_header_value_u64 (
0 commit comments