|
|
|
@@ -16,10 +16,6 @@
|
|
|
|
|
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
|
|
|
|
|
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
|
|
|
|
|
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
|
|
|
|
|
#endif
|
|
|
|
@@ -180,6 +176,7 @@ using socket_t = int;
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <atomic>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <cctype>
|
|
|
|
|
#include <climits>
|
|
|
|
|
#include <condition_variable>
|
|
|
|
|
#include <errno.h>
|
|
|
|
@@ -193,6 +190,7 @@ using socket_t = int;
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <random>
|
|
|
|
|
#include <regex>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <thread>
|
|
|
|
@@ -581,7 +579,8 @@ public:
|
|
|
|
|
Server &Options(const char *pattern, Handler handler);
|
|
|
|
|
|
|
|
|
|
bool set_base_dir(const char *dir, const char *mount_point = nullptr);
|
|
|
|
|
bool set_mount_point(const char *mount_point, const char *dir);
|
|
|
|
|
bool set_mount_point(const char *mount_point, const char *dir,
|
|
|
|
|
Headers headers = Headers());
|
|
|
|
|
bool remove_mount_point(const char *mount_point);
|
|
|
|
|
void set_file_extension_and_mimetype_mapping(const char *ext,
|
|
|
|
|
const char *mime);
|
|
|
|
@@ -595,6 +594,7 @@ public:
|
|
|
|
|
void set_socket_options(SocketOptions socket_options);
|
|
|
|
|
|
|
|
|
|
void set_keep_alive_max_count(size_t count);
|
|
|
|
|
void set_keep_alive_timeout(time_t sec);
|
|
|
|
|
void set_read_timeout(time_t sec, time_t usec = 0);
|
|
|
|
|
void set_write_timeout(time_t sec, time_t usec = 0);
|
|
|
|
|
void set_idle_interval(time_t sec, time_t usec = 0);
|
|
|
|
@@ -619,6 +619,7 @@ protected:
|
|
|
|
|
|
|
|
|
|
std::atomic<socket_t> svr_sock_;
|
|
|
|
|
size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
|
|
|
|
|
time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
|
|
|
|
|
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
|
|
|
|
|
time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
|
|
|
|
|
time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
|
|
|
|
@@ -663,9 +664,15 @@ private:
|
|
|
|
|
ContentReceiver multipart_receiver);
|
|
|
|
|
|
|
|
|
|
virtual bool process_and_close_socket(socket_t sock);
|
|
|
|
|
|
|
|
|
|
struct MountPointEntry {
|
|
|
|
|
std::string mount_point;
|
|
|
|
|
std::string base_dir;
|
|
|
|
|
Headers headers;
|
|
|
|
|
};
|
|
|
|
|
std::vector<MountPointEntry> base_dirs_;
|
|
|
|
|
|
|
|
|
|
std::atomic<bool> is_running_;
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> base_dirs_;
|
|
|
|
|
std::map<std::string, std::string> file_extension_and_mimetype_map_;
|
|
|
|
|
Handler file_request_handler_;
|
|
|
|
|
Handlers get_handlers_;
|
|
|
|
@@ -839,6 +846,10 @@ public:
|
|
|
|
|
void set_proxy_digest_auth(const char *username, const char *password);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
void enable_server_certificate_verification(bool enabled);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void set_logger(Logger logger);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
@@ -859,6 +870,8 @@ protected:
|
|
|
|
|
|
|
|
|
|
Error get_last_error() const;
|
|
|
|
|
|
|
|
|
|
void copy_settings(const ClientImpl &rhs);
|
|
|
|
|
|
|
|
|
|
// Error state
|
|
|
|
|
mutable Error error_ = Error::Success;
|
|
|
|
|
|
|
|
|
@@ -916,41 +929,11 @@ protected:
|
|
|
|
|
std::string proxy_digest_auth_password_;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
Logger logger_;
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
bool server_certificate_verification_ = true;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void copy_settings(const ClientImpl &rhs) {
|
|
|
|
|
client_cert_path_ = rhs.client_cert_path_;
|
|
|
|
|
client_key_path_ = rhs.client_key_path_;
|
|
|
|
|
connection_timeout_sec_ = rhs.connection_timeout_sec_;
|
|
|
|
|
read_timeout_sec_ = rhs.read_timeout_sec_;
|
|
|
|
|
read_timeout_usec_ = rhs.read_timeout_usec_;
|
|
|
|
|
write_timeout_sec_ = rhs.write_timeout_sec_;
|
|
|
|
|
write_timeout_usec_ = rhs.write_timeout_usec_;
|
|
|
|
|
basic_auth_username_ = rhs.basic_auth_username_;
|
|
|
|
|
basic_auth_password_ = rhs.basic_auth_password_;
|
|
|
|
|
bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
digest_auth_username_ = rhs.digest_auth_username_;
|
|
|
|
|
digest_auth_password_ = rhs.digest_auth_password_;
|
|
|
|
|
#endif
|
|
|
|
|
keep_alive_ = rhs.keep_alive_;
|
|
|
|
|
follow_location_ = rhs.follow_location_;
|
|
|
|
|
tcp_nodelay_ = rhs.tcp_nodelay_;
|
|
|
|
|
socket_options_ = rhs.socket_options_;
|
|
|
|
|
compress_ = rhs.compress_;
|
|
|
|
|
decompress_ = rhs.decompress_;
|
|
|
|
|
interface_ = rhs.interface_;
|
|
|
|
|
proxy_host_ = rhs.proxy_host_;
|
|
|
|
|
proxy_port_ = rhs.proxy_port_;
|
|
|
|
|
proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
|
|
|
|
|
proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
|
|
|
|
|
proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
|
|
|
|
|
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
|
|
|
|
|
#endif
|
|
|
|
|
logger_ = rhs.logger_;
|
|
|
|
|
}
|
|
|
|
|
Logger logger_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
socket_t create_client_socket() const;
|
|
|
|
@@ -1096,16 +1079,18 @@ public:
|
|
|
|
|
void set_proxy_digest_auth(const char *username, const char *password);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
void enable_server_certificate_verification(bool enabled);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void set_logger(Logger logger);
|
|
|
|
|
|
|
|
|
|
// SSL
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
Client &set_ca_cert_path(const char *ca_cert_file_path,
|
|
|
|
|
const char *ca_cert_dir_path = nullptr);
|
|
|
|
|
void set_ca_cert_path(const char *ca_cert_file_path,
|
|
|
|
|
const char *ca_cert_dir_path = nullptr);
|
|
|
|
|
|
|
|
|
|
Client &set_ca_cert_store(X509_STORE *ca_cert_store);
|
|
|
|
|
|
|
|
|
|
Client &enable_server_certificate_verification(bool enabled);
|
|
|
|
|
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
|
|
|
|
|
|
|
|
|
long get_openssl_verify_result() const;
|
|
|
|
|
|
|
|
|
@@ -1163,8 +1148,6 @@ public:
|
|
|
|
|
|
|
|
|
|
void set_ca_cert_store(X509_STORE *ca_cert_store);
|
|
|
|
|
|
|
|
|
|
void enable_server_certificate_verification(bool enabled);
|
|
|
|
|
|
|
|
|
|
long get_openssl_verify_result() const;
|
|
|
|
|
|
|
|
|
|
SSL_CTX *ssl_context() const;
|
|
|
|
@@ -1195,8 +1178,6 @@ private:
|
|
|
|
|
|
|
|
|
|
std::string ca_cert_file_path_;
|
|
|
|
|
std::string ca_cert_dir_path_;
|
|
|
|
|
X509_STORE *ca_cert_store_ = nullptr;
|
|
|
|
|
bool server_certificate_verification_ = true;
|
|
|
|
|
long verify_result_ = 0;
|
|
|
|
|
|
|
|
|
|
friend class ClientImpl;
|
|
|
|
@@ -1255,7 +1236,7 @@ inline std::string from_i_to_hex(size_t n) {
|
|
|
|
|
inline bool start_with(const std::string &a, const std::string &b) {
|
|
|
|
|
if (a.size() < b.size()) { return false; }
|
|
|
|
|
for (size_t i = 0; i < b.size(); i++) {
|
|
|
|
|
if (std::tolower(a[i]) != std::tolower(b[i])) { return false; }
|
|
|
|
|
if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@@ -1739,7 +1720,7 @@ private:
|
|
|
|
|
size_t position = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline bool keep_alive(socket_t sock) {
|
|
|
|
|
inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
|
|
|
|
|
using namespace std::chrono;
|
|
|
|
|
auto start = steady_clock::now();
|
|
|
|
|
while (true) {
|
|
|
|
@@ -1749,8 +1730,7 @@ inline bool keep_alive(socket_t sock) {
|
|
|
|
|
} else if (val == 0) {
|
|
|
|
|
auto current = steady_clock::now();
|
|
|
|
|
auto duration = duration_cast<milliseconds>(current - start);
|
|
|
|
|
auto timeout = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND * 1000 +
|
|
|
|
|
CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND / 1000;
|
|
|
|
|
auto timeout = keep_alive_timeout_sec * 1000;
|
|
|
|
|
if (duration.count() > timeout) { return false; }
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
} else {
|
|
|
|
@@ -1760,13 +1740,13 @@ inline bool keep_alive(socket_t sock) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline bool process_server_socket_core(socket_t sock,
|
|
|
|
|
size_t keep_alive_max_count,
|
|
|
|
|
T callback) {
|
|
|
|
|
inline bool
|
|
|
|
|
process_server_socket_core(socket_t sock, size_t keep_alive_max_count,
|
|
|
|
|
time_t keep_alive_timeout_sec, T callback) {
|
|
|
|
|
assert(keep_alive_max_count > 0);
|
|
|
|
|
auto ret = false;
|
|
|
|
|
auto count = keep_alive_max_count;
|
|
|
|
|
while (count > 0 && keep_alive(sock)) {
|
|
|
|
|
while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) {
|
|
|
|
|
auto close_connection = count == 1;
|
|
|
|
|
auto connection_closed = false;
|
|
|
|
|
ret = callback(close_connection, connection_closed);
|
|
|
|
@@ -1777,14 +1757,14 @@ inline bool process_server_socket_core(socket_t sock,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline bool process_server_socket(socket_t sock, size_t keep_alive_max_count,
|
|
|
|
|
time_t read_timeout_sec,
|
|
|
|
|
time_t read_timeout_usec,
|
|
|
|
|
time_t write_timeout_sec,
|
|
|
|
|
time_t write_timeout_usec, T callback) {
|
|
|
|
|
inline bool
|
|
|
|
|
process_server_socket(socket_t sock, size_t keep_alive_max_count,
|
|
|
|
|
time_t keep_alive_timeout_sec, time_t read_timeout_sec,
|
|
|
|
|
time_t read_timeout_usec, time_t write_timeout_sec,
|
|
|
|
|
time_t write_timeout_usec, T callback) {
|
|
|
|
|
return process_server_socket_core(
|
|
|
|
|
sock, keep_alive_max_count,
|
|
|
|
|
[&](bool close_connection, bool connection_closed) {
|
|
|
|
|
sock, keep_alive_max_count, keep_alive_timeout_sec,
|
|
|
|
|
[&](bool close_connection, bool &connection_closed) {
|
|
|
|
|
SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
|
|
|
|
|
write_timeout_sec, write_timeout_usec);
|
|
|
|
|
return callback(strm, close_connection, connection_closed);
|
|
|
|
@@ -1933,7 +1913,11 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
#if !defined _WIN32 && !defined ANDROID
|
|
|
|
|
#define USE_IF2IP
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef USE_IF2IP
|
|
|
|
|
inline std::string if2ip(const std::string &ifn) {
|
|
|
|
|
struct ifaddrs *ifap;
|
|
|
|
|
getifaddrs(&ifap);
|
|
|
|
@@ -1963,7 +1947,7 @@ inline socket_t create_client_socket(const char *host, int port,
|
|
|
|
|
host, port, 0, tcp_nodelay, socket_options,
|
|
|
|
|
[&](socket_t sock, struct addrinfo &ai) -> bool {
|
|
|
|
|
if (!intf.empty()) {
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
#ifdef USE_IF2IP
|
|
|
|
|
auto ip = if2ip(intf);
|
|
|
|
|
if (ip.empty()) { ip = intf; }
|
|
|
|
|
if (!bind_ip_address(sock, ip.c_str())) {
|
|
|
|
@@ -2392,9 +2376,7 @@ public:
|
|
|
|
|
|
|
|
|
|
if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
|
|
|
|
|
|
|
|
|
|
if (!callback(buff.data(), buff.size() - avail_out)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
|
|
|
|
@@ -2460,7 +2442,7 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p < end) {
|
|
|
|
|
fn(std::string(beg, key_end), decode_url(std::string(p, end), true));
|
|
|
|
|
fn(std::string(beg, key_end), decode_url(std::string(p, end), false));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3840,11 +3822,12 @@ inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
|
|
|
|
|
return set_mount_point(mount_point, dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
|
|
|
|
|
inline bool Server::set_mount_point(const char *mount_point, const char *dir,
|
|
|
|
|
Headers headers) {
|
|
|
|
|
if (detail::is_dir(dir)) {
|
|
|
|
|
std::string mnt = mount_point ? mount_point : "/";
|
|
|
|
|
if (!mnt.empty() && mnt[0] == '/') {
|
|
|
|
|
base_dirs_.emplace_back(mnt, dir);
|
|
|
|
|
base_dirs_.push_back({mnt, dir, std::move(headers)});
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -3853,7 +3836,7 @@ inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
|
|
|
|
|
|
|
|
|
|
inline bool Server::remove_mount_point(const char *mount_point) {
|
|
|
|
|
for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
|
|
|
|
|
if (it->first == mount_point) {
|
|
|
|
|
if (it->mount_point == mount_point) {
|
|
|
|
|
base_dirs_.erase(it);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@@ -3891,6 +3874,10 @@ inline void Server::set_keep_alive_max_count(size_t count) {
|
|
|
|
|
keep_alive_max_count_ = count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Server::set_keep_alive_timeout(time_t sec) {
|
|
|
|
|
keep_alive_timeout_sec_ = sec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Server::set_read_timeout(time_t sec, time_t usec) {
|
|
|
|
|
read_timeout_sec_ = sec;
|
|
|
|
|
read_timeout_usec_ = usec;
|
|
|
|
@@ -3974,10 +3961,11 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
|
|
|
|
|
// Headers
|
|
|
|
|
if (close_connection || req.get_header_value("Connection") == "close") {
|
|
|
|
|
res.set_header("Connection", "close");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!close_connection && req.get_header_value("Connection") == "Keep-Alive") {
|
|
|
|
|
res.set_header("Connection", "Keep-Alive");
|
|
|
|
|
} else {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "timeout=" << keep_alive_timeout_sec_
|
|
|
|
|
<< ", max=" << keep_alive_max_count_;
|
|
|
|
|
res.set_header("Keep-Alive", ss.str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res.has_header("Content-Type") &&
|
|
|
|
@@ -4270,15 +4258,12 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
|
|
|
|
|
|
|
|
|
|
inline bool Server::handle_file_request(Request &req, Response &res,
|
|
|
|
|
bool head) {
|
|
|
|
|
for (const auto &kv : base_dirs_) {
|
|
|
|
|
const auto &mount_point = kv.first;
|
|
|
|
|
const auto &base_dir = kv.second;
|
|
|
|
|
|
|
|
|
|
for (const auto &entry : base_dirs_) {
|
|
|
|
|
// Prefix match
|
|
|
|
|
if (!req.path.compare(0, mount_point.size(), mount_point)) {
|
|
|
|
|
std::string sub_path = "/" + req.path.substr(mount_point.size());
|
|
|
|
|
if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
|
|
|
|
|
std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
|
|
|
|
|
if (detail::is_valid_path(sub_path)) {
|
|
|
|
|
auto path = base_dir + sub_path;
|
|
|
|
|
auto path = entry.base_dir + sub_path;
|
|
|
|
|
if (path.back() == '/') { path += "index.html"; }
|
|
|
|
|
|
|
|
|
|
if (detail::is_file(path)) {
|
|
|
|
@@ -4286,6 +4271,9 @@ inline bool Server::handle_file_request(Request &req, Response &res,
|
|
|
|
|
auto type =
|
|
|
|
|
detail::find_content_type(path, file_extension_and_mimetype_map_);
|
|
|
|
|
if (type) { res.set_header("Content-Type", type); }
|
|
|
|
|
for (const auto& kv : entry.headers) {
|
|
|
|
|
res.set_header(kv.first.c_str(), kv.second);
|
|
|
|
|
}
|
|
|
|
|
res.status = 200;
|
|
|
|
|
if (!head && file_request_handler_) {
|
|
|
|
|
file_request_handler_(req, res);
|
|
|
|
@@ -4578,8 +4566,8 @@ inline bool Server::is_valid() const { return true; }
|
|
|
|
|
|
|
|
|
|
inline bool Server::process_and_close_socket(socket_t sock) {
|
|
|
|
|
auto ret = detail::process_server_socket(
|
|
|
|
|
sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
|
|
|
|
|
write_timeout_sec_, write_timeout_usec_,
|
|
|
|
|
sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
|
|
|
|
|
read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
|
|
|
|
|
[this](Stream &strm, bool close_connection, bool &connection_closed) {
|
|
|
|
|
return process_request(strm, close_connection, connection_closed,
|
|
|
|
|
nullptr);
|
|
|
|
@@ -4610,6 +4598,43 @@ inline bool ClientImpl::is_valid() const { return true; }
|
|
|
|
|
|
|
|
|
|
inline Error ClientImpl::get_last_error() const { return error_; }
|
|
|
|
|
|
|
|
|
|
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
|
|
|
|
|
client_cert_path_ = rhs.client_cert_path_;
|
|
|
|
|
client_key_path_ = rhs.client_key_path_;
|
|
|
|
|
connection_timeout_sec_ = rhs.connection_timeout_sec_;
|
|
|
|
|
read_timeout_sec_ = rhs.read_timeout_sec_;
|
|
|
|
|
read_timeout_usec_ = rhs.read_timeout_usec_;
|
|
|
|
|
write_timeout_sec_ = rhs.write_timeout_sec_;
|
|
|
|
|
write_timeout_usec_ = rhs.write_timeout_usec_;
|
|
|
|
|
basic_auth_username_ = rhs.basic_auth_username_;
|
|
|
|
|
basic_auth_password_ = rhs.basic_auth_password_;
|
|
|
|
|
bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
digest_auth_username_ = rhs.digest_auth_username_;
|
|
|
|
|
digest_auth_password_ = rhs.digest_auth_password_;
|
|
|
|
|
#endif
|
|
|
|
|
keep_alive_ = rhs.keep_alive_;
|
|
|
|
|
follow_location_ = rhs.follow_location_;
|
|
|
|
|
tcp_nodelay_ = rhs.tcp_nodelay_;
|
|
|
|
|
socket_options_ = rhs.socket_options_;
|
|
|
|
|
compress_ = rhs.compress_;
|
|
|
|
|
decompress_ = rhs.decompress_;
|
|
|
|
|
interface_ = rhs.interface_;
|
|
|
|
|
proxy_host_ = rhs.proxy_host_;
|
|
|
|
|
proxy_port_ = rhs.proxy_port_;
|
|
|
|
|
proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
|
|
|
|
|
proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
|
|
|
|
|
proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
|
|
|
|
|
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
server_certificate_verification_ = rhs.server_certificate_verification_;
|
|
|
|
|
#endif
|
|
|
|
|
logger_ = rhs.logger_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline socket_t ClientImpl::create_client_socket() const {
|
|
|
|
|
if (!proxy_host_.empty() && proxy_port_ != -1) {
|
|
|
|
|
return detail::create_client_socket(
|
|
|
|
@@ -4647,7 +4672,17 @@ inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
|
|
|
|
|
const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n");
|
|
|
|
|
|
|
|
|
|
std::cmatch m;
|
|
|
|
|
if (std::regex_match(line_reader.ptr(), m, re)) {
|
|
|
|
|
if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
|
|
|
|
|
res.version = std::string(m[1]);
|
|
|
|
|
res.status = std::stoi(std::string(m[2]));
|
|
|
|
|
res.reason = std::string(m[3]);
|
|
|
|
|
|
|
|
|
|
// Ignore '100 Continue'
|
|
|
|
|
while (res.status == 100) {
|
|
|
|
|
if (!line_reader.getline()) { return false; } // CRLF
|
|
|
|
|
if (!line_reader.getline()) { return false; } // next response line
|
|
|
|
|
|
|
|
|
|
if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
|
|
|
|
|
res.version = std::string(m[1]);
|
|
|
|
|
res.status = std::stoi(std::string(m[2]));
|
|
|
|
|
res.reason = std::string(m[3]);
|
|
|
|
@@ -4764,7 +4799,7 @@ inline bool ClientImpl::redirect(const Request &req, Response &res) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto location = res.get_header_value("location");
|
|
|
|
|
auto location = detail::decode_url(res.get_header_value("location"), true);
|
|
|
|
|
if (location.empty()) { return false; }
|
|
|
|
|
|
|
|
|
|
const static std::regex re(
|
|
|
|
@@ -4854,7 +4889,10 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
|
|
|
|
auto length = std::to_string(req.content_length);
|
|
|
|
|
headers.emplace("Content-Length", length);
|
|
|
|
|
} else {
|
|
|
|
|
headers.emplace("Content-Length", "0");
|
|
|
|
|
if (req.method == "POST" || req.method == "PUT" ||
|
|
|
|
|
req.method == "PATCH") {
|
|
|
|
|
headers.emplace("Content-Length", "0");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!req.has_header("Content-Type")) {
|
|
|
|
@@ -4867,7 +4905,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) {
|
|
|
|
|
if (!basic_auth_password_.empty()) {
|
|
|
|
|
headers.insert(make_basic_authentication_header(
|
|
|
|
|
basic_auth_username_, basic_auth_password_, false));
|
|
|
|
|
}
|
|
|
|
@@ -5473,6 +5511,12 @@ inline void ClientImpl::set_proxy_digest_auth(const char *username,
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
|
|
|
|
|
server_certificate_verification_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
inline void ClientImpl::set_logger(Logger logger) {
|
|
|
|
|
logger_ = std::move(logger);
|
|
|
|
|
}
|
|
|
|
@@ -5522,12 +5566,13 @@ inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline bool
|
|
|
|
|
process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count,
|
|
|
|
|
time_t keep_alive_timeout_sec,
|
|
|
|
|
time_t read_timeout_sec, time_t read_timeout_usec,
|
|
|
|
|
time_t write_timeout_sec, time_t write_timeout_usec,
|
|
|
|
|
T callback) {
|
|
|
|
|
return process_server_socket_core(
|
|
|
|
|
sock, keep_alive_max_count,
|
|
|
|
|
[&](bool close_connection, bool connection_closed) {
|
|
|
|
|
sock, keep_alive_max_count, keep_alive_timeout_sec,
|
|
|
|
|
[&](bool close_connection, bool &connection_closed) {
|
|
|
|
|
SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
|
|
|
|
|
write_timeout_sec, write_timeout_usec);
|
|
|
|
|
return callback(strm, close_connection, connection_closed);
|
|
|
|
@@ -5733,8 +5778,9 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
|
|
|
|
|
|
|
|
|
|
if (ssl) {
|
|
|
|
|
auto ret = detail::process_server_socket_ssl(
|
|
|
|
|
ssl, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
|
|
|
|
|
write_timeout_sec_, write_timeout_usec_,
|
|
|
|
|
ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
|
|
|
|
|
read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
|
|
|
|
|
write_timeout_usec_,
|
|
|
|
|
[this, ssl](Stream &strm, bool close_connection,
|
|
|
|
|
bool &connection_closed) {
|
|
|
|
|
return process_request(strm, close_connection, connection_closed,
|
|
|
|
@@ -5808,11 +5854,16 @@ inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
|
|
|
|
if (ca_cert_store) { ca_cert_store_ = ca_cert_store; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void SSLClient::enable_server_certificate_verification(bool enabled) {
|
|
|
|
|
server_certificate_verification_ = enabled;
|
|
|
|
|
if (ca_cert_store) {
|
|
|
|
|
if (ctx_) {
|
|
|
|
|
if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
|
|
|
|
|
// Free memory allocated for old cert and use new store `ca_cert_store`
|
|
|
|
|
SSL_CTX_set_cert_store(ctx_, ca_cert_store);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
X509_STORE_free(ca_cert_store);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline long SSLClient::get_openssl_verify_result() const {
|
|
|
|
@@ -5890,10 +5941,6 @@ inline bool SSLClient::load_certs() {
|
|
|
|
|
ca_cert_dir_path_.c_str())) {
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
} else if (ca_cert_store_ != nullptr) {
|
|
|
|
|
if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) {
|
|
|
|
|
SSL_CTX_set_cert_store(ctx_, ca_cert_store_);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
|
|
|
|
@@ -6395,31 +6442,27 @@ inline void Client::set_proxy_digest_auth(const char *username,
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
inline void Client::enable_server_certificate_verification(bool enabled) {
|
|
|
|
|
cli_->enable_server_certificate_verification(enabled);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
inline Client &Client::set_ca_cert_path(const char *ca_cert_file_path,
|
|
|
|
|
const char *ca_cert_dir_path) {
|
|
|
|
|
inline void Client::set_ca_cert_path(const char *ca_cert_file_path,
|
|
|
|
|
const char *ca_cert_dir_path) {
|
|
|
|
|
if (is_ssl_) {
|
|
|
|
|
static_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path,
|
|
|
|
|
ca_cert_dir_path);
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Client &Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
|
|
|
|
inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
|
|
|
|
|
if (is_ssl_) {
|
|
|
|
|
static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Client &Client::enable_server_certificate_verification(bool enabled) {
|
|
|
|
|
if (is_ssl_) {
|
|
|
|
|
static_cast<SSLClient &>(*cli_).enable_server_certificate_verification(
|
|
|
|
|
enabled);
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline long Client::get_openssl_verify_result() const {
|
|
|
|
|