Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 70 additions & 22 deletions httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -4642,52 +4642,79 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
}
}

inline bool read_content_without_length(Stream &strm,
ContentReceiverWithProgress out) {
enum class ReadContentResult {
Success, // Successfully read the content
PayloadTooLarge, // The content exceeds the specified payload limit
Error // An error occurred while reading the content
};

inline ReadContentResult
read_content_without_length(Stream &strm, size_t payload_max_length,
ContentReceiverWithProgress out) {
char buf[CPPHTTPLIB_RECV_BUFSIZ];
uint64_t r = 0;
for (;;) {
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
if (n == 0) { return true; }
if (n < 0) { return false; }
if (n == 0) { return ReadContentResult::Success; }
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<uint64_t>(n)) {
return ReadContentResult::PayloadTooLarge;
}

if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
if (!out(buf, static_cast<size_t>(n), r, 0)) {
return ReadContentResult::Error;
}
r += static_cast<uint64_t>(n);
}

return true;
return ReadContentResult::Success;
}

template <typename T>
inline bool read_content_chunked(Stream &strm, T &x,
ContentReceiverWithProgress out) {
inline ReadContentResult read_content_chunked(Stream &strm, T &x,
size_t payload_max_length,
ContentReceiverWithProgress out) {
const auto bufsiz = 16;
char buf[bufsiz];

stream_line_reader line_reader(strm, buf, bufsiz);

if (!line_reader.getline()) { return false; }
if (!line_reader.getline()) { return ReadContentResult::Error; }

unsigned long chunk_len;
uint64_t total_len = 0;
while (true) {
char *end_ptr;

chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);

if (end_ptr == line_reader.ptr()) { return false; }
if (chunk_len == ULONG_MAX) { return false; }
if (end_ptr == line_reader.ptr()) { return ReadContentResult::Error; }
if (chunk_len == ULONG_MAX) { return ReadContentResult::Error; }

if (chunk_len == 0) { break; }

// Check if adding this chunk would exceed the payload limit
if (total_len > payload_max_length ||
payload_max_length - total_len < chunk_len) {
return ReadContentResult::PayloadTooLarge;
}

total_len += chunk_len;

if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
return false;
return ReadContentResult::Error;
}

if (!line_reader.getline()) { return false; }
if (!line_reader.getline()) { return ReadContentResult::Error; }

if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
if (strcmp(line_reader.ptr(), "\r\n") != 0) {
return ReadContentResult::Error;
}

if (!line_reader.getline()) { return false; }
if (!line_reader.getline()) { return ReadContentResult::Error; }
}

assert(chunk_len == 0);
Expand All @@ -4704,14 +4731,18 @@ inline bool read_content_chunked(Stream &strm, T &x,
//
// According to the reference code in RFC 9112, cpp-httplib now allows
// chunked transfer coding data without the final CRLF.
if (!line_reader.getline()) { return true; }
if (!line_reader.getline()) { return ReadContentResult::Success; }

size_t trailer_header_count = 0;
while (strcmp(line_reader.ptr(), "\r\n") != 0) {
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
return ReadContentResult::Error;
}

// Check trailer header count limit
if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {
return ReadContentResult::Error;
}

// Exclude line terminator
constexpr auto line_terminator_len = 2;
Expand All @@ -4724,10 +4755,10 @@ inline bool read_content_chunked(Stream &strm, T &x,

trailer_header_count++;

if (!line_reader.getline()) { return false; }
if (!line_reader.getline()) { return ReadContentResult::Error; }
}

return true;
return ReadContentResult::Success;
}

inline bool is_chunked_transfer_encoding(const Headers &headers) {
Expand Down Expand Up @@ -4801,9 +4832,26 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
auto exceed_payload_max_length = false;

if (is_chunked_transfer_encoding(x.headers)) {
ret = read_content_chunked(strm, x, out);
auto result = read_content_chunked(strm, x, payload_max_length, out);
if (result == ReadContentResult::Success) {
ret = true;
} else if (result == ReadContentResult::PayloadTooLarge) {
exceed_payload_max_length = true;
ret = false;
} else {
ret = false;
}
} else if (!has_header(x.headers, "Content-Length")) {
ret = read_content_without_length(strm, out);
auto result =
read_content_without_length(strm, payload_max_length, out);
if (result == ReadContentResult::Success) {
ret = true;
} else if (result == ReadContentResult::PayloadTooLarge) {
exceed_payload_max_length = true;
ret = false;
} else {
ret = false;
}
} else {
auto is_invalid_value = false;
auto len = get_header_value_u64(
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ project(
'b_lto=true',
'warning_level=3'
],
meson_version: '>=0.62.0'
meson_version: '>=0.63.0'
)

cxx = meson.get_compiler('cpp')
Expand Down
23 changes: 16 additions & 7 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
#
# SPDX-License-Identifier: MIT

option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
option('cpp-httplib_macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices')
option('cpp-httplib_non_blocking_getaddrinfo', type: 'feature', value: 'auto', description: 'Enable asynchronous name lookup')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
option('openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
option('zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
option('brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
option('macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices')
option('non_blocking_getaddrinfo', type: 'feature', value: 'auto', description: 'Enable asynchronous name lookup')
option('compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
option('test', type: 'boolean', value: false, description: 'Build tests')

# Old option names
option('cpp-httplib_openssl', type: 'feature', deprecated: 'openssl')
option('cpp-httplib_zlib', type: 'feature', deprecated: 'zlib')
option('cpp-httplib_brotli', type: 'feature', deprecated: 'brotli')
option('cpp-httplib_macosx_keychain', type: 'feature', deprecated: 'macosx_keychain')
option('cpp-httplib_non_blocking_getaddrinfo', type: 'feature', deprecated: 'non_blocking_getaddrinfo')
option('cpp-httplib_compile', type: 'boolean', value: false, deprecated: 'compile')
option('cpp-httplib_test', type: 'boolean', value: false, deprecated: 'test')
Loading
Loading