vendor : update cpp-httplib to 0.37.0 (#20207)

This commit is contained in:
Alessandro de Oliveira Faria (A.K.A.CABELO) 2026-03-11 00:03:53 -03:00 committed by GitHub
parent 4f2f0a163d
commit e1a399992b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 56 deletions

View File

@ -5,7 +5,7 @@ import os
import sys import sys
import subprocess import subprocess
HTTPLIB_VERSION = "refs/tags/v0.35.0" HTTPLIB_VERSION = "refs/tags/v0.37.0"
vendor = { vendor = {
"https://github.com/nlohmann/json/releases/latest/download/json.hpp": "vendor/nlohmann/json.hpp", "https://github.com/nlohmann/json/releases/latest/download/json.hpp": "vendor/nlohmann/json.hpp",

View File

@ -813,17 +813,13 @@ bool is_websocket_upgrade(const Request &req) {
// Check Upgrade: websocket (case-insensitive) // Check Upgrade: websocket (case-insensitive)
auto upgrade_it = req.headers.find("Upgrade"); auto upgrade_it = req.headers.find("Upgrade");
if (upgrade_it == req.headers.end()) { return false; } if (upgrade_it == req.headers.end()) { return false; }
auto upgrade_val = upgrade_it->second; auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
std::transform(upgrade_val.begin(), upgrade_val.end(), upgrade_val.begin(),
::tolower);
if (upgrade_val != "websocket") { return false; } if (upgrade_val != "websocket") { return false; }
// Check Connection header contains "Upgrade" // Check Connection header contains "Upgrade"
auto connection_it = req.headers.find("Connection"); auto connection_it = req.headers.find("Connection");
if (connection_it == req.headers.end()) { return false; } if (connection_it == req.headers.end()) { return false; }
auto connection_val = connection_it->second; auto connection_val = case_ignore::to_lower(connection_it->second);
std::transform(connection_val.begin(), connection_val.end(),
connection_val.begin(), ::tolower);
if (connection_val.find("upgrade") == std::string::npos) { return false; } if (connection_val.find("upgrade") == std::string::npos) { return false; }
// Check Sec-WebSocket-Key is a valid base64-encoded 16-byte value (24 chars) // Check Sec-WebSocket-Key is a valid base64-encoded 16-byte value (24 chars)
@ -2615,10 +2611,15 @@ bool can_compress_content_type(const std::string &content_type) {
switch (tag) { switch (tag) {
case "image/svg+xml"_t: case "image/svg+xml"_t:
case "application/javascript"_t: case "application/javascript"_t:
case "application/x-javascript"_t:
case "application/json"_t: case "application/json"_t:
case "application/ld+json"_t:
case "application/xml"_t: case "application/xml"_t:
case "application/protobuf"_t: case "application/xhtml+xml"_t:
case "application/xhtml+xml"_t: return true; case "application/rss+xml"_t:
case "application/atom+xml"_t:
case "application/xslt+xml"_t:
case "application/protobuf"_t: return true;
case "text/event-stream"_t: return false; case "text/event-stream"_t: return false;
@ -3038,17 +3039,13 @@ bool read_websocket_upgrade_response(Stream &strm,
// Verify Upgrade: websocket (case-insensitive) // Verify Upgrade: websocket (case-insensitive)
auto upgrade_it = headers.find("Upgrade"); auto upgrade_it = headers.find("Upgrade");
if (upgrade_it == headers.end()) { return false; } if (upgrade_it == headers.end()) { return false; }
auto upgrade_val = upgrade_it->second; auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
std::transform(upgrade_val.begin(), upgrade_val.end(), upgrade_val.begin(),
::tolower);
if (upgrade_val != "websocket") { return false; } if (upgrade_val != "websocket") { return false; }
// Verify Connection header contains "Upgrade" (case-insensitive) // Verify Connection header contains "Upgrade" (case-insensitive)
auto connection_it = headers.find("Connection"); auto connection_it = headers.find("Connection");
if (connection_it == headers.end()) { return false; } if (connection_it == headers.end()) { return false; }
auto connection_val = connection_it->second; auto connection_val = case_ignore::to_lower(connection_it->second);
std::transform(connection_val.begin(), connection_val.end(),
connection_val.begin(), ::tolower);
if (connection_val.find("upgrade") == std::string::npos) { return false; } if (connection_val.find("upgrade") == std::string::npos) { return false; }
// Verify Sec-WebSocket-Accept header value // Verify Sec-WebSocket-Accept header value
@ -3934,14 +3931,10 @@ public:
file_.content_type = file_.content_type =
trim_copy(header.substr(str_len(header_content_type))); trim_copy(header.substr(str_len(header_content_type)));
} else { } else {
thread_local const std::regex re_content_disposition( std::string disposition_params;
R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~", if (parse_content_disposition(header, disposition_params)) {
std::regex_constants::icase);
std::smatch m;
if (std::regex_match(header, m, re_content_disposition)) {
Params params; Params params;
parse_disposition_params(m[1], params); parse_disposition_params(disposition_params, params);
auto it = params.find("name"); auto it = params.find("name");
if (it != params.end()) { if (it != params.end()) {
@ -3956,13 +3949,14 @@ public:
it = params.find("filename*"); it = params.find("filename*");
if (it != params.end()) { if (it != params.end()) {
// Only allow UTF-8 encoding... // RFC 5987: only UTF-8 encoding is allowed
thread_local const std::regex re_rfc5987_encoding( const auto &val = it->second;
R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase); constexpr const char utf8_prefix[] = "UTF-8''";
constexpr size_t prefix_len = str_len(utf8_prefix);
std::smatch m2; if (val.size() > prefix_len &&
if (std::regex_match(it->second, m2, re_rfc5987_encoding)) { start_with_case_ignore(val, utf8_prefix)) {
file_.filename = decode_path_component(m2[1]); // override... file_.filename = decode_path_component(
val.substr(prefix_len)); // override...
} else { } else {
is_valid_ = false; is_valid_ = false;
return false; return false;
@ -4030,17 +4024,48 @@ private:
file_.headers.clear(); file_.headers.clear();
} }
bool start_with_case_ignore(const std::string &a, const char *b) const { bool start_with_case_ignore(const std::string &a, const char *b,
size_t offset = 0) const {
const auto b_len = strlen(b); const auto b_len = strlen(b);
if (a.size() < b_len) { return false; } if (a.size() < offset + b_len) { return false; }
for (size_t i = 0; i < b_len; i++) { for (size_t i = 0; i < b_len; i++) {
if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) { if (case_ignore::to_lower(a[offset + i]) != case_ignore::to_lower(b[i])) {
return false; return false;
} }
} }
return true; return true;
} }
// Parses "Content-Disposition: form-data; <params>" without std::regex.
// Returns true if header matches, with the params portion in `params_out`.
bool parse_content_disposition(const std::string &header,
std::string &params_out) const {
constexpr const char prefix[] = "Content-Disposition:";
constexpr size_t prefix_len = str_len(prefix);
if (!start_with_case_ignore(header, prefix)) { return false; }
// Skip whitespace after "Content-Disposition:"
auto pos = prefix_len;
while (pos < header.size() && (header[pos] == ' ' || header[pos] == '\t')) {
pos++;
}
// Match "form-data;" (case-insensitive)
constexpr const char form_data[] = "form-data;";
constexpr size_t form_data_len = str_len(form_data);
if (!start_with_case_ignore(header, form_data, pos)) { return false; }
pos += form_data_len;
// Skip whitespace after "form-data;"
while (pos < header.size() && (header[pos] == ' ' || header[pos] == '\t')) {
pos++;
}
params_out = header.substr(pos);
return true;
}
const std::string dash_ = "--"; const std::string dash_ = "--";
const std::string crlf_ = "\r\n"; const std::string crlf_ = "\r\n";
std::string boundary_; std::string boundary_;
@ -4992,9 +5017,10 @@ bool match_hostname(const std::string &pattern,
// Verify certificate using Windows CertGetCertificateChain API. // Verify certificate using Windows CertGetCertificateChain API.
// This provides real-time certificate validation with Windows Update // This provides real-time certificate validation with Windows Update
// integration, independent of the TLS backend (OpenSSL or MbedTLS). // integration, independent of the TLS backend (OpenSSL or MbedTLS).
bool verify_cert_with_windows_schannel( bool
const std::vector<unsigned char> &der_cert, const std::string &hostname, verify_cert_with_windows_schannel(const std::vector<unsigned char> &der_cert,
bool verify_hostname, unsigned long &out_error) { const std::string &hostname,
bool verify_hostname, uint64_t &out_error) {
if (der_cert.empty()) { return false; } if (der_cert.empty()) { return false; }
out_error = 0; out_error = 0;
@ -7987,7 +8013,7 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
#else #else
try { try {
routed = routing(req, res, strm); routed = routing(req, res, strm);
} catch (std::exception &e) { } catch (std::exception &) {
if (exception_handler_) { if (exception_handler_) {
auto ep = std::current_exception(); auto ep = std::current_exception();
exception_handler_(req, res, ep); exception_handler_(req, res, ep);
@ -11811,7 +11837,7 @@ bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
server_certificate_verification_) { server_certificate_verification_) {
verify_result_ = tls::get_verify_result(session); verify_result_ = tls::get_verify_result(session);
if (verify_result_ != 0) { if (verify_result_ != 0) {
last_backend_error_ = static_cast<unsigned long>(verify_result_); last_backend_error_ = static_cast<uint64_t>(verify_result_);
error = Error::SSLServerVerification; error = Error::SSLServerVerification;
output_error_log(error, nullptr); output_error_log(error, nullptr);
return false; return false;
@ -11850,7 +11876,7 @@ bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
ca_cert_dir_path_.empty() && ca_cert_pem_.empty()) { ca_cert_dir_path_.empty() && ca_cert_pem_.empty()) {
std::vector<unsigned char> der; std::vector<unsigned char> der;
if (get_cert_der(server_cert, der)) { if (get_cert_der(server_cert, der)) {
unsigned long wincrypt_error = 0; uint64_t wincrypt_error = 0;
if (!detail::verify_cert_with_windows_schannel( if (!detail::verify_cert_with_windows_schannel(
der, host_, server_hostname_verification_, wincrypt_error)) { der, host_, server_hostname_verification_, wincrypt_error)) {
last_backend_error_ = wincrypt_error; last_backend_error_ = wincrypt_error;
@ -11974,16 +12000,26 @@ bool is_ipv4_address(const std::string &str) {
// Parse IPv4 address string to bytes // Parse IPv4 address string to bytes
bool parse_ipv4(const std::string &str, unsigned char *out) { bool parse_ipv4(const std::string &str, unsigned char *out) {
int parts[4]; const char *p = str.c_str();
if (sscanf(str.c_str(), "%d.%d.%d.%d", &parts[0], &parts[1], &parts[2],
&parts[3]) != 4) {
return false;
}
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (parts[i] < 0 || parts[i] > 255) return false; if (i > 0) {
out[i] = static_cast<unsigned char>(parts[i]); if (*p != '.') { return false; }
p++;
}
int val = 0;
int digits = 0;
while (*p >= '0' && *p <= '9') {
val = val * 10 + (*p - '0');
if (val > 255) { return false; }
p++;
digits++;
}
if (digits == 0) { return false; }
// Reject leading zeros (e.g., "01.002.03.04") to prevent ambiguity
if (digits > 1 && *(p - digits) == '0') { return false; }
out[i] = static_cast<unsigned char>(val);
} }
return true; return *p == '\0';
} }
#ifdef _WIN32 #ifdef _WIN32
@ -13285,11 +13321,11 @@ void update_server_certs_from_x509(ctx_t ctx, X509 *cert, EVP_PKEY *key,
ctx_t create_client_context_from_x509(X509 *cert, EVP_PKEY *key, ctx_t create_client_context_from_x509(X509 *cert, EVP_PKEY *key,
const char *password, const char *password,
unsigned long &out_error) { uint64_t &out_error) {
out_error = 0; out_error = 0;
auto ctx = create_client_context(); auto ctx = create_client_context();
if (!ctx) { if (!ctx) {
out_error = static_cast<unsigned long>(get_error()); out_error = get_error();
return nullptr; return nullptr;
} }
@ -13303,7 +13339,7 @@ ctx_t create_client_context_from_x509(X509 *cert, EVP_PKEY *key,
} }
if (!set_client_cert_pem(ctx, cert_pem.c_str(), key_pem.c_str(), if (!set_client_cert_pem(ctx, cert_pem.c_str(), key_pem.c_str(),
password)) { password)) {
out_error = static_cast<unsigned long>(get_error()); out_error = get_error();
free_context(ctx); free_context(ctx);
return nullptr; return nullptr;
} }

View File

@ -8,8 +8,8 @@
#ifndef CPPHTTPLIB_HTTPLIB_H #ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.35.0" #define CPPHTTPLIB_VERSION "0.37.0"
#define CPPHTTPLIB_VERSION_NUM "0x002300" #define CPPHTTPLIB_VERSION_NUM "0x002500"
/* /*
* Platform compatibility check * Platform compatibility check
@ -575,6 +575,14 @@ inline unsigned char to_lower(int c) {
return table[(unsigned char)(char)c]; return table[(unsigned char)(char)c];
} }
inline std::string to_lower(const std::string &s) {
std::string result = s;
std::transform(
result.begin(), result.end(), result.begin(),
[](unsigned char c) { return static_cast<char>(to_lower(c)); });
return result;
}
inline bool equal(const std::string &a, const std::string &b) { inline bool equal(const std::string &a, const std::string &b) {
return a.size() == b.size() && return a.size() == b.size() &&
std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) { std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
@ -1859,23 +1867,23 @@ public:
: res_(std::move(res)), err_(err), : res_(std::move(res)), err_(err),
request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {} request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers, Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
int ssl_error, unsigned long ssl_backend_error) int ssl_error, uint64_t ssl_backend_error)
: res_(std::move(res)), err_(err), : res_(std::move(res)), err_(err),
request_headers_(std::move(request_headers)), ssl_error_(ssl_error), request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
ssl_backend_error_(ssl_backend_error) {} ssl_backend_error_(ssl_backend_error) {}
int ssl_error() const { return ssl_error_; } int ssl_error() const { return ssl_error_; }
unsigned long ssl_backend_error() const { return ssl_backend_error_; } uint64_t ssl_backend_error() const { return ssl_backend_error_; }
private: private:
int ssl_error_ = 0; int ssl_error_ = 0;
unsigned long ssl_backend_error_ = 0; uint64_t ssl_backend_error_ = 0;
#endif #endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
public: public:
[[deprecated("Use ssl_backend_error() instead")]] [[deprecated("Use ssl_backend_error() instead")]]
unsigned long ssl_openssl_error() const { uint64_t ssl_openssl_error() const {
return ssl_backend_error_; return ssl_backend_error_;
} }
#endif #endif
@ -2345,7 +2353,7 @@ protected:
bool server_hostname_verification_ = true; bool server_hostname_verification_ = true;
std::string ca_cert_pem_; // Store CA cert PEM for redirect transfer std::string ca_cert_pem_; // Store CA cert PEM for redirect transfer
int last_ssl_error_ = 0; int last_ssl_error_ = 0;
unsigned long last_backend_error_ = 0; uint64_t last_backend_error_ = 0;
#endif #endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_OPENSSL_SUPPORT