vendor : update cpp-httplib to 0.57.0 (#29214)

Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>
This commit is contained in:
Adrien Gallouët 2026-09-21 13:44:43 +02:00 • committed by GitHub
parent ec91ab5add
commit 6f41ac59e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 25 deletions

View file

@ -5,7 +5,7 @@ import os
import sys
import subprocess
HTTPLIB_VERSION = "refs/tags/v0.56.0"
HTTPLIB_VERSION = "refs/tags/v0.57.0"
# used by examples/gguf-hash, these repos have no release tag, so we pin a commit
XXHASH_COMMIT = "9f465f1ea932d6ad9a26cd77496311ffa544cd68"

View file

@ -1248,6 +1248,13 @@ bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
// to look up.
split(trailer_header.data(), trailer_header.data() + trailer_header.size(),
',', [&](const char *b, const char *e) {
// A legitimate message declares only a handful of trailers. Cap the
// set so a peer cannot grow it without bound: an oversized set only
// arises from an attempt to force many colliding names into
// quadratic lookups (case_ignore::hash is unkeyed).
if (declared_trailers.size() >= CPPHTTPLIB_HEADER_MAX_COUNT) {
return;
}
std::string key(b, e);
if (prohibited_trailers.find(key) == prohibited_trailers.end()) {
declared_trailers.insert(key);
@ -1258,6 +1265,8 @@ bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
size_t trailer_header_count = 0;
while (strcmp(line_reader.ptr(), "\r\n") != 0) {
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
// Count every received trailer field, not only the declared ones stored in
// dest, so undeclared fields cannot keep this loop running past the limit.
if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
constexpr auto line_terminator_len = 2;
@ -1270,12 +1279,13 @@ bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
if (declared_trailers.find(key) !=
declared_trailers.end()) {
dest.emplace(key, val);
trailer_header_count++;
}
})) {
return false;
}
trailer_header_count++;
if (!line_reader.getline()) { return false; }
}
@ -8626,7 +8636,10 @@ bool Server::write_response_core(Stream &strm, bool close_connection,
// Prepare additional headers
if (close_connection ||
detail::has_header_token(req.headers, "Connection", "close") ||
400 <= res.status) { // Don't leave connections open after errors
400 <= res.status || // Don't leave connections open after errors
// The client withholds the body until `100 Continue`, which was never
// sent, so whether and when the body follows is unknown.
(req.expect_100_continue_pending_ && detail::has_framed_body(req))) {
res.set_header("Connection", "close");
} else {
std::string s = "timeout=";
@ -8877,6 +8890,13 @@ bool Server::read_content_core(
}
#endif
// The client is waiting for this before it sends the body.
if (req.expect_100_continue_pending_) {
req.expect_100_continue_pending_ = false;
detail::write_response_line(strm, StatusCode::Continue_100);
strm.write("\r\n");
}
if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
out, true)) {
return false;
@ -9714,19 +9734,20 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
// case-insensitive, and a 100-continue expectation in an HTTP/1.0 request
// must be ignored. An expectation we do not recognize is left alone; the
// 417 the section allows for one is a MAY, not a requirement.
//
// `100 Continue` itself is deferred until the body is actually read (see
// read_content_core), so a request rejected by a later handler never
// invites the client to send a body nobody will read.
if (req.version != "HTTP/1.0" &&
detail::has_header_token(req.headers, "Expect", "100-continue")) {
int status = StatusCode::Continue_100;
if (expect_100_continue_handler_) {
status = expect_100_continue_handler_(req, res);
}
switch (status) {
case StatusCode::Continue_100:
case StatusCode::ExpectationFailed_417:
detail::write_response_line(strm, status);
strm.write("\r\n");
break;
default:
if (status == StatusCode::Continue_100) {
req.expect_100_continue_pending_ = true;
} else {
if (res.status == -1) { res.status = status; }
connection_closed = true;
return write_response(strm, true, req, res);
}
@ -9739,18 +9760,25 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
};
// WebSocket upgrade
// Check pre_routing_handler_ before upgrading so that authentication
// and other middleware can reject the request with an HTTP response
// (e.g., 401) before the protocol switches.
// Run pre_routing_handler_ and pre_request_handler_ before upgrading so
// that authentication and other middleware can reject the request with an
// HTTP response (e.g., 401) before the protocol switches.
if (detail::is_websocket_upgrade(req)) {
if (pre_routing_handler_ &&
pre_routing_handler_(req, res) == HandlerResponse::Handled) {
if (res.status == -1) { res.status = StatusCode::OK_200; }
return write_response(strm, close_connection, req, res);
return write_response_with_content(strm, close_connection, req, res);
}
// Find matching WebSocket handler
for (const auto &entry : websocket_handlers_) {
if (entry.matcher->match(req)) {
req.matched_route = entry.matcher->pattern();
if (pre_request_handler_ &&
pre_request_handler_(req, res) == HandlerResponse::Handled) {
if (res.status == -1) { res.status = StatusCode::OK_200; }
return write_response_with_content(strm, close_connection, req, res);
}
// Compute accept key
auto client_key = req.get_header_value("Sec-WebSocket-Key");
auto accept_key = detail::websocket_accept_key(client_key);
@ -10610,22 +10638,45 @@ ssize_t ChunkedDecoder::read_payload(char *buf, size_t len,
stream_line_reader lr(strm, line_buf, sizeof(line_buf));
if (!lr.getline()) { return -1; }
// Everything below is bounded by eol rather than by the buffer's NUL, so
// the line terminator is never mistaken for line content.
const char *eol = lr.ptr() + lr.size();
if (lr.end_with_crlf()) {
eol -= 2;
} else if (eol != lr.ptr() && eol[-1] == '\n') {
// Only reachable under CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR, where
// getline() ends the line on a bare LF. That LF is the terminator, so it
// has to come off here or the check below would reject the line.
eol -= 1;
}
// RFC 9112 §7.1: chunk-size = 1*HEXDIG
const char *p = lr.ptr();
int v = 0;
if (!is_hex(*p, v)) { return -1; }
if (p == eol || !is_hex(*p, v)) { return -1; }
size_t chunk_len = 0;
constexpr size_t chunk_len_max = (std::numeric_limits<size_t>::max)();
for (; is_hex(*p, v); ++p) {
for (; p < eol && is_hex(*p, v); ++p) {
if (chunk_len > (chunk_len_max >> 4)) { return -1; }
chunk_len = (chunk_len << 4) | static_cast<size_t>(v);
}
while (is_space_or_tab(*p)) {
while (p < eol && is_space_or_tab(*p)) {
++p;
}
if (*p != '\0' && *p != ';' && *p != '\r' && *p != '\n') { return -1; }
// RFC 9112 §7.1.1: only a chunk-ext may sit between the size and the line
// terminator, and it is built from tokens and quoted-strings, so it never
// holds a CR, LF or any other control character. getline() reads up to the
// CRLF, so a bare LF left in here would be swallowed as extension text
// while an intermediary that ends the line on it delimits the chunks
// differently, and the two disagree on where the body ends (request
// smuggling).
if (p < eol && *p != ';') { return -1; }
for (; p < eol; ++p) {
if (!is_space_or_tab(*p) && !fields::is_field_vchar(*p)) { return -1; }
}
if (chunk_len == 0) {
chunk_remaining = 0;
@ -14650,11 +14701,11 @@ void shutdown(session_t session, bool graceful) {
auto ssl = static_cast<SSL *>(session);
if (graceful) {
// First call sends close_notify
if (SSL_shutdown(ssl) == 0) {
// Second call waits for peer's close_notify
SSL_shutdown(ssl);
}
// Send close_notify without waiting for the peer's. The connection is
// closed right after this, so a unidirectional shutdown is enough, and an
// idle peer that never answers would otherwise hold this thread until the
// read timeout. The other backends do not wait either.
SSL_shutdown(ssl);
}
}

View file

@ -8,8 +8,8 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.56.0"
#define CPPHTTPLIB_VERSION_NUM "0x003800"
#define CPPHTTPLIB_VERSION "0.57.0"
#define CPPHTTPLIB_VERSION_NUM "0x003900"
#ifdef _WIN32
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
@ -1756,6 +1756,7 @@ struct Request {
// private members...
bool body_consumed_ = false;
bool expect_100_continue_pending_ = false;
size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
size_t content_length_ = 0;
ContentProvider content_provider_;