vendor : update cpp-httplib to 0.37.0 (#20207)
This commit is contained in:
parent
4f2f0a163d
commit
e1a399992b
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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 ¶ms_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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue