|
|
|
|
@@ -72,6 +72,10 @@
|
|
|
|
|
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_TCP_NODELAY
|
|
|
|
|
#define CPPHTTPLIB_TCP_NODELAY false
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_RECV_BUFSIZ
|
|
|
|
|
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
|
|
|
|
|
#endif
|
|
|
|
|
@@ -148,6 +152,8 @@ using ssize_t = int;
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
#pragma comment(lib, "ws2_32.lib")
|
|
|
|
|
#pragma comment(lib, "crypt32.lib")
|
|
|
|
|
#pragma comment(lib, "cryptui.lib")
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef strcasecmp
|
|
|
|
|
@@ -166,6 +172,7 @@ using socket_t = SOCKET;
|
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
|
#include <netdb.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
|
#ifdef CPPHTTPLIB_USE_POLL
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
#endif
|
|
|
|
|
@@ -573,6 +580,7 @@ public:
|
|
|
|
|
void set_expect_100_continue_handler(Expect100ContinueHandler handler);
|
|
|
|
|
void set_logger(Logger logger);
|
|
|
|
|
|
|
|
|
|
void set_tcp_nodelay(bool on);
|
|
|
|
|
void set_socket_options(SocketOptions socket_options);
|
|
|
|
|
|
|
|
|
|
void set_keep_alive_max_count(size_t count);
|
|
|
|
|
@@ -661,6 +669,8 @@ private:
|
|
|
|
|
Handler error_handler_;
|
|
|
|
|
Logger logger_;
|
|
|
|
|
Expect100ContinueHandler expect_100_continue_handler_;
|
|
|
|
|
|
|
|
|
|
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
|
|
|
|
SocketOptions socket_options_ = default_socket_options;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -802,6 +812,9 @@ public:
|
|
|
|
|
|
|
|
|
|
void stop();
|
|
|
|
|
|
|
|
|
|
void set_tcp_nodelay(bool on);
|
|
|
|
|
void set_socket_options(SocketOptions socket_options);
|
|
|
|
|
|
|
|
|
|
CPPHTTPLIB_DEPRECATED void set_timeout_sec(time_t timeout_sec);
|
|
|
|
|
void set_connection_timeout(time_t sec, time_t usec = 0);
|
|
|
|
|
void set_read_timeout(time_t sec, time_t usec = 0);
|
|
|
|
|
@@ -876,6 +889,9 @@ protected:
|
|
|
|
|
bool keep_alive_ = false;
|
|
|
|
|
bool follow_location_ = false;
|
|
|
|
|
|
|
|
|
|
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
|
|
|
|
SocketOptions socket_options_ = nullptr;
|
|
|
|
|
|
|
|
|
|
bool compress_ = false;
|
|
|
|
|
bool decompress_ = true;
|
|
|
|
|
|
|
|
|
|
@@ -909,6 +925,8 @@ protected:
|
|
|
|
|
#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_;
|
|
|
|
|
@@ -1046,6 +1064,8 @@ private:
|
|
|
|
|
bool connect_with_proxy(Socket &sock, Response &res, bool &success);
|
|
|
|
|
bool initialize_ssl(Socket &socket);
|
|
|
|
|
|
|
|
|
|
bool load_certs();
|
|
|
|
|
|
|
|
|
|
bool verify_host(X509 *server_cert) const;
|
|
|
|
|
bool verify_host_with_subject_alt_name(X509 *server_cert) const;
|
|
|
|
|
bool verify_host_with_common_name(X509 *server_cert) const;
|
|
|
|
|
@@ -1053,12 +1073,14 @@ private:
|
|
|
|
|
|
|
|
|
|
SSL_CTX *ctx_;
|
|
|
|
|
std::mutex ctx_mutex_;
|
|
|
|
|
std::once_flag initialize_cert_;
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> host_components_;
|
|
|
|
|
|
|
|
|
|
std::string ca_cert_file_path_;
|
|
|
|
|
std::string ca_cert_dir_path_;
|
|
|
|
|
X509_STORE *ca_cert_store_ = nullptr;
|
|
|
|
|
bool server_certificate_verification_ = false;
|
|
|
|
|
bool server_certificate_verification_ = true;
|
|
|
|
|
long verify_result_ = 0;
|
|
|
|
|
|
|
|
|
|
friend class Client;
|
|
|
|
|
@@ -1297,6 +1319,12 @@ public:
|
|
|
|
|
|
|
|
|
|
void stop() { cli_->stop(); }
|
|
|
|
|
|
|
|
|
|
void set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
|
|
|
|
|
|
|
|
|
|
void set_socket_options(SocketOptions socket_options) {
|
|
|
|
|
cli_->set_socket_options(socket_options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client2 &set_connection_timeout(time_t sec, time_t usec) {
|
|
|
|
|
cli_->set_connection_timeout(sec, usec);
|
|
|
|
|
return *this;
|
|
|
|
|
@@ -1850,26 +1878,19 @@ private:
|
|
|
|
|
size_t position = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline bool keep_alive(socket_t sock, std::function<bool()> is_shutting_down) {
|
|
|
|
|
inline bool keep_alive(socket_t sock) {
|
|
|
|
|
using namespace std::chrono;
|
|
|
|
|
auto start = steady_clock::now();
|
|
|
|
|
while (true) {
|
|
|
|
|
auto val = select_read(sock, 0, 10000);
|
|
|
|
|
if (is_shutting_down && is_shutting_down()) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (val < 0) {
|
|
|
|
|
if (val < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (val == 0) {
|
|
|
|
|
auto current = steady_clock::now();
|
|
|
|
|
auto sec = duration_cast<seconds>(current - start);
|
|
|
|
|
if (sec.count() > CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (sec.count() == CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND) {
|
|
|
|
|
auto usec = duration_cast<nanoseconds>(current - start);
|
|
|
|
|
if (usec.count() > CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
auto duration = duration_cast<milliseconds>(current - start);
|
|
|
|
|
auto timeout = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND * 1000 +
|
|
|
|
|
CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND / 1000;
|
|
|
|
|
if (duration.count() > timeout) { return false; }
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
} else {
|
|
|
|
|
return true;
|
|
|
|
|
@@ -1877,14 +1898,14 @@ inline bool keep_alive(socket_t sock, std::function<bool()> is_shutting_down) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T, typename U>
|
|
|
|
|
template <typename T>
|
|
|
|
|
inline bool process_server_socket_core(socket_t sock,
|
|
|
|
|
size_t keep_alive_max_count,
|
|
|
|
|
T is_shutting_down, U callback) {
|
|
|
|
|
T callback) {
|
|
|
|
|
assert(keep_alive_max_count > 0);
|
|
|
|
|
auto ret = false;
|
|
|
|
|
auto count = keep_alive_max_count;
|
|
|
|
|
while (count > 0 && keep_alive(sock, is_shutting_down)) {
|
|
|
|
|
while (count > 0 && keep_alive(sock)) {
|
|
|
|
|
auto close_connection = count == 1;
|
|
|
|
|
auto connection_closed = false;
|
|
|
|
|
ret = callback(close_connection, connection_closed);
|
|
|
|
|
@@ -1894,14 +1915,14 @@ inline bool process_server_socket_core(socket_t sock,
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T, typename U>
|
|
|
|
|
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 is_shutting_down, U callback) {
|
|
|
|
|
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) {
|
|
|
|
|
return process_server_socket_core(
|
|
|
|
|
sock, keep_alive_max_count, is_shutting_down,
|
|
|
|
|
sock, keep_alive_max_count,
|
|
|
|
|
[&](bool close_connection, bool connection_closed) {
|
|
|
|
|
SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
|
|
|
|
|
write_timeout_sec, write_timeout_usec);
|
|
|
|
|
@@ -1929,7 +1950,7 @@ inline int shutdown_socket(socket_t sock) {
|
|
|
|
|
|
|
|
|
|
template <typename BindOrConnect>
|
|
|
|
|
socket_t create_socket(const char *host, int port, int socket_flags,
|
|
|
|
|
SocketOptions socket_options,
|
|
|
|
|
bool tcp_nodelay, SocketOptions socket_options,
|
|
|
|
|
BindOrConnect bind_or_connect) {
|
|
|
|
|
// Get address info
|
|
|
|
|
struct addrinfo hints;
|
|
|
|
|
@@ -1978,18 +1999,14 @@ socket_t create_socket(const char *host, int port, int socket_flags,
|
|
|
|
|
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (tcp_nodelay) {
|
|
|
|
|
int yes = 1;
|
|
|
|
|
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes),
|
|
|
|
|
sizeof(yes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (socket_options) { socket_options(sock); }
|
|
|
|
|
|
|
|
|
|
// Make 'reuse address' option available
|
|
|
|
|
int yes = 1;
|
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
|
|
|
|
|
sizeof(yes));
|
|
|
|
|
|
|
|
|
|
#ifdef SO_REUSEPORT
|
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes),
|
|
|
|
|
sizeof(yes));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (rp->ai_family == AF_INET6) {
|
|
|
|
|
int no = 0;
|
|
|
|
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no),
|
|
|
|
|
@@ -2074,11 +2091,12 @@ inline std::string if2ip(const std::string &ifn) {
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
inline socket_t create_client_socket(const char *host, int port,
|
|
|
|
|
bool tcp_nodelay,
|
|
|
|
|
SocketOptions socket_options,
|
|
|
|
|
time_t timeout_sec, time_t timeout_usec,
|
|
|
|
|
const std::string &intf) {
|
|
|
|
|
return create_socket(
|
|
|
|
|
host, port, 0, socket_options,
|
|
|
|
|
host, port, 0, tcp_nodelay, socket_options,
|
|
|
|
|
[&](socket_t sock, struct addrinfo &ai) -> bool {
|
|
|
|
|
if (!intf.empty()) {
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
@@ -2762,7 +2780,7 @@ inline std::string params_to_query_str(const Params ¶ms) {
|
|
|
|
|
if (it != params.begin()) { query += "&"; }
|
|
|
|
|
query += it->first;
|
|
|
|
|
query += "=";
|
|
|
|
|
query += detail::encode_url(it->second);
|
|
|
|
|
query += encode_url(it->second);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return query;
|
|
|
|
|
@@ -2787,9 +2805,12 @@ inline bool parse_multipart_boundary(const std::string &content_type,
|
|
|
|
|
std::string &boundary) {
|
|
|
|
|
auto pos = content_type.find("boundary=");
|
|
|
|
|
if (pos == std::string::npos) { return false; }
|
|
|
|
|
|
|
|
|
|
boundary = content_type.substr(pos + 9);
|
|
|
|
|
return true;
|
|
|
|
|
if (boundary.length() >= 2 && boundary.front() == '"' &&
|
|
|
|
|
boundary.back() == '"') {
|
|
|
|
|
boundary = boundary.substr(1, boundary.size() - 2);
|
|
|
|
|
}
|
|
|
|
|
return !boundary.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
|
|
|
|
|
@@ -2830,7 +2851,7 @@ class MultipartFormDataParser {
|
|
|
|
|
public:
|
|
|
|
|
MultipartFormDataParser() = default;
|
|
|
|
|
|
|
|
|
|
void set_boundary(std::string boundary) { boundary_ = std::move(boundary); }
|
|
|
|
|
void set_boundary(std::string &&boundary) { boundary_ = boundary; }
|
|
|
|
|
|
|
|
|
|
bool is_valid() const { return is_valid_; }
|
|
|
|
|
|
|
|
|
|
@@ -2854,9 +2875,7 @@ public:
|
|
|
|
|
auto pattern = dash_ + boundary_ + crlf_;
|
|
|
|
|
if (pattern.size() > buf_.size()) { return true; }
|
|
|
|
|
auto pos = buf_.find(pattern);
|
|
|
|
|
if (pos != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (pos != 0) { return false; }
|
|
|
|
|
buf_.erase(0, pattern.size());
|
|
|
|
|
off_ += pattern.size();
|
|
|
|
|
state_ = 1;
|
|
|
|
|
@@ -2898,6 +2917,7 @@ public:
|
|
|
|
|
pos = buf_.find(crlf_);
|
|
|
|
|
}
|
|
|
|
|
if (state_ != 3) { return true; }
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 3: { // Body
|
|
|
|
|
{
|
|
|
|
|
@@ -3207,6 +3227,33 @@ inline std::string SHA_512(const std::string &s) {
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
// NOTE: This code came up with the following stackoverflow post:
|
|
|
|
|
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
|
|
|
|
|
inline bool load_system_certs_on_windows(X509_STORE *store) {
|
|
|
|
|
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
|
|
|
|
|
|
|
|
|
|
if (!hStore) { return false; }
|
|
|
|
|
|
|
|
|
|
PCCERT_CONTEXT pContext = NULL;
|
|
|
|
|
while (pContext = CertEnumCertificatesInStore(hStore, pContext)) {
|
|
|
|
|
auto encoded_cert =
|
|
|
|
|
static_cast<const unsigned char *>(pContext->pbCertEncoded);
|
|
|
|
|
|
|
|
|
|
auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
|
|
|
|
|
if (x509) {
|
|
|
|
|
X509_STORE_add_cert(store, x509);
|
|
|
|
|
X509_free(x509);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CertFreeCertificateContext(pContext);
|
|
|
|
|
CertCloseStore(hStore, 0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
class WSInit {
|
|
|
|
|
public:
|
|
|
|
|
WSInit() {
|
|
|
|
|
@@ -3480,10 +3527,11 @@ inline ssize_t Stream::write(const std::string &s) {
|
|
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
|
|
|
|
|
std::array<char, 2048> buf;
|
|
|
|
|
const auto bufsiz = 2048;
|
|
|
|
|
std::array<char, bufsiz> buf;
|
|
|
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
|
|
|
auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
|
|
|
|
|
auto sn = _snprintf_s(buf, bufsiz - 1, buf.size() - 1, fmt, args...);
|
|
|
|
|
#else
|
|
|
|
|
auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
|
|
|
|
|
#endif
|
|
|
|
|
@@ -3570,7 +3618,7 @@ inline bool BufferStream::is_readable() const { return true; }
|
|
|
|
|
inline bool BufferStream::is_writable() const { return true; }
|
|
|
|
|
|
|
|
|
|
inline ssize_t BufferStream::read(char *ptr, size_t size) {
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER <= 1900
|
|
|
|
|
auto len_read = buffer._Copy_s(ptr, size, size, position);
|
|
|
|
|
#else
|
|
|
|
|
auto len_read = buffer.copy(ptr, size, position);
|
|
|
|
|
@@ -3697,6 +3745,12 @@ inline void Server::set_error_handler(Handler handler) {
|
|
|
|
|
error_handler_ = std::move(handler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
|
|
|
|
|
|
|
|
|
|
inline void Server::set_socket_options(SocketOptions socket_options) {
|
|
|
|
|
socket_options_ = socket_options;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
|
|
|
|
|
|
|
|
|
|
inline void
|
|
|
|
|
@@ -3886,13 +3940,14 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
|
|
|
|
|
strm.write(data.data(), data.size());
|
|
|
|
|
|
|
|
|
|
// Body
|
|
|
|
|
auto ret = true;
|
|
|
|
|
if (req.method != "HEAD") {
|
|
|
|
|
if (!res.body.empty()) {
|
|
|
|
|
if (!strm.write(res.body)) { return false; }
|
|
|
|
|
if (!strm.write(res.body)) { ret = false; }
|
|
|
|
|
} else if (res.content_provider_) {
|
|
|
|
|
if (!write_content_with_provider(strm, req, res, boundary,
|
|
|
|
|
content_type)) {
|
|
|
|
|
return false;
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -3900,7 +3955,7 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
|
|
|
|
|
// Log
|
|
|
|
|
if (logger_) { logger_(req, res); }
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
|
@@ -4063,7 +4118,7 @@ inline socket_t
|
|
|
|
|
Server::create_server_socket(const char *host, int port, int socket_flags,
|
|
|
|
|
SocketOptions socket_options) const {
|
|
|
|
|
return detail::create_socket(
|
|
|
|
|
host, port, socket_flags, socket_options,
|
|
|
|
|
host, port, socket_flags, tcp_nodelay_, socket_options,
|
|
|
|
|
[](socket_t sock, struct addrinfo &ai) -> bool {
|
|
|
|
|
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
|
|
|
|
|
return false;
|
|
|
|
|
@@ -4341,13 +4396,11 @@ 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_,
|
|
|
|
|
[this]() { return this->svr_sock_ == INVALID_SOCKET; },
|
|
|
|
|
[this](Stream &strm, bool close_connection, bool &connection_closed) {
|
|
|
|
|
return process_request(strm, close_connection, connection_closed,
|
|
|
|
|
nullptr);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
|
detail::shutdown_socket(sock);
|
|
|
|
|
detail::close_socket(sock);
|
|
|
|
|
return ret;
|
|
|
|
|
@@ -4363,7 +4416,7 @@ inline Client::Client(const std::string &host, int port)
|
|
|
|
|
inline Client::Client(const std::string &host, int port,
|
|
|
|
|
const std::string &client_cert_path,
|
|
|
|
|
const std::string &client_key_path)
|
|
|
|
|
: /*cli_sock_(INVALID_SOCKET),*/ host_(host), port_(port),
|
|
|
|
|
: host_(host), port_(port),
|
|
|
|
|
host_and_port_(host_ + ":" + std::to_string(port_)),
|
|
|
|
|
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
|
|
|
|
|
|
|
|
|
|
@@ -4373,12 +4426,12 @@ inline bool Client::is_valid() const { return true; }
|
|
|
|
|
|
|
|
|
|
inline socket_t Client::create_client_socket() const {
|
|
|
|
|
if (!proxy_host_.empty()) {
|
|
|
|
|
return detail::create_client_socket(proxy_host_.c_str(), proxy_port_,
|
|
|
|
|
nullptr, connection_timeout_sec_,
|
|
|
|
|
connection_timeout_usec_, interface_);
|
|
|
|
|
return detail::create_client_socket(
|
|
|
|
|
proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_,
|
|
|
|
|
connection_timeout_sec_, connection_timeout_usec_, interface_);
|
|
|
|
|
}
|
|
|
|
|
return detail::create_client_socket(host_.c_str(), port_, nullptr,
|
|
|
|
|
connection_timeout_sec_,
|
|
|
|
|
return detail::create_client_socket(host_.c_str(), port_, tcp_nodelay_,
|
|
|
|
|
socket_options_, connection_timeout_sec_,
|
|
|
|
|
connection_timeout_usec_, interface_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -4404,7 +4457,7 @@ inline bool Client::read_response_line(Stream &strm, Response &res) {
|
|
|
|
|
|
|
|
|
|
if (!line_reader.getline()) { return false; }
|
|
|
|
|
|
|
|
|
|
const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n");
|
|
|
|
|
const static std::regex re("(HTTP/1\\.[01]) (\\d+).*?\r\n");
|
|
|
|
|
|
|
|
|
|
std::cmatch m;
|
|
|
|
|
if (std::regex_match(line_reader.ptr(), m, re)) {
|
|
|
|
|
@@ -5109,6 +5162,12 @@ inline void Client::set_keep_alive(bool on) { keep_alive_ = on; }
|
|
|
|
|
|
|
|
|
|
inline void Client::set_follow_location(bool on) { follow_location_ = on; }
|
|
|
|
|
|
|
|
|
|
inline void Client::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
|
|
|
|
|
|
|
|
|
|
inline void Client::set_socket_options(SocketOptions socket_options) {
|
|
|
|
|
socket_options_ = socket_options;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void Client::set_compress(bool on) { compress_ = on; }
|
|
|
|
|
|
|
|
|
|
inline void Client::set_decompress(bool on) { decompress_ = on; }
|
|
|
|
|
@@ -5183,9 +5242,9 @@ inline bool
|
|
|
|
|
process_server_socket_ssl(SSL *ssl, 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,
|
|
|
|
|
std::function<bool()> is_shutting_down, T callback) {
|
|
|
|
|
T callback) {
|
|
|
|
|
return process_server_socket_core(
|
|
|
|
|
sock, keep_alive_max_count, is_shutting_down,
|
|
|
|
|
sock, keep_alive_max_count,
|
|
|
|
|
[&](bool close_connection, bool connection_closed) {
|
|
|
|
|
SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
|
|
|
|
|
write_timeout_sec, write_timeout_usec);
|
|
|
|
|
@@ -5263,7 +5322,24 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
|
|
|
|
|
: sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
|
|
|
|
|
read_timeout_usec_(read_timeout_usec),
|
|
|
|
|
write_timeout_sec_(write_timeout_sec),
|
|
|
|
|
write_timeout_usec_(write_timeout_usec) {}
|
|
|
|
|
write_timeout_usec_(write_timeout_usec) {
|
|
|
|
|
{
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(read_timeout_sec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
|
|
|
|
|
|
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
|
|
|
|
|
sizeof(tv));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
timeval tv;
|
|
|
|
|
tv.tv_sec = static_cast<long>(write_timeout_sec);
|
|
|
|
|
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
|
|
|
|
|
|
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
|
|
|
|
|
sizeof(tv));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline SSLSocketStream::~SSLSocketStream() {}
|
|
|
|
|
|
|
|
|
|
@@ -5377,7 +5453,6 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
|
|
|
|
|
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_,
|
|
|
|
|
[this]() { return this->svr_sock_ == INVALID_SOCKET; },
|
|
|
|
|
[this, ssl](Stream &strm, bool close_connection,
|
|
|
|
|
bool &connection_closed) {
|
|
|
|
|
return process_request(strm, close_connection, connection_closed,
|
|
|
|
|
@@ -5518,23 +5593,44 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool SSLClient::load_certs() {
|
|
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
|
|
std::call_once(initialize_cert_, [&]() {
|
|
|
|
|
std::lock_guard<std::mutex> guard(ctx_mutex_);
|
|
|
|
|
if (!ca_cert_file_path_.empty()) {
|
|
|
|
|
if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
|
|
|
|
|
nullptr)) {
|
|
|
|
|
ret = false;
|
|
|
|
|
}
|
|
|
|
|
} else if (!ca_cert_dir_path_.empty()) {
|
|
|
|
|
if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
|
|
|
|
|
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_));
|
|
|
|
|
#else
|
|
|
|
|
SSL_CTX_set_default_verify_paths(ctx_);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool SSLClient::initialize_ssl(Socket &socket) {
|
|
|
|
|
auto ssl = detail::ssl_new(
|
|
|
|
|
socket.sock, ctx_, ctx_mutex_,
|
|
|
|
|
[&](SSL *ssl) {
|
|
|
|
|
if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) {
|
|
|
|
|
SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
|
|
|
|
|
} else if (!ca_cert_file_path_.empty()) {
|
|
|
|
|
if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
|
|
|
|
|
nullptr)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
|
|
|
|
|
} 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_);
|
|
|
|
|
}
|
|
|
|
|
SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
|
|
|
|
|
if (server_certificate_verification_) {
|
|
|
|
|
if (!load_certs()) { return false; }
|
|
|
|
|
SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SSL_connect(ssl) != 1) { return false; }
|
|
|
|
|
@@ -5646,7 +5742,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
|
|
|
|
|
|
|
|
|
|
auto count = sk_GENERAL_NAME_num(alt_names);
|
|
|
|
|
|
|
|
|
|
for (auto i = 0; i < count && !dsn_matched; i++) {
|
|
|
|
|
for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
|
|
|
|
|
auto val = sk_GENERAL_NAME_value(alt_names, i);
|
|
|
|
|
if (val->type == type) {
|
|
|
|
|
auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
|
|
|
|
|
@@ -5724,3 +5820,4 @@ inline bool SSLClient::check_host_name(const char *pattern,
|
|
|
|
|
} // namespace httplib
|
|
|
|
|
|
|
|
|
|
#endif // CPPHTTPLIB_HTTPLIB_H
|
|
|
|
|
|
|
|
|
|
|