8 #ifndef CPPHTTPLIB_HTTPLIB_H
9 #define CPPHTTPLIB_HTTPLIB_H
11 #define CPPHTTPLIB_VERSION "0.14.0"
17 #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
18 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
21 #ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
22 #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
25 #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
26 #define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
29 #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
30 #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
33 #ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
34 #define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
37 #ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
38 #define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
41 #ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
42 #define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
45 #ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
46 #define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
49 #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
50 #define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
53 #ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
55 #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
57 #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
61 #ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
62 #define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
65 #ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
66 #define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
69 #ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
70 #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
73 #ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
74 #define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
77 #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
78 #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
81 #ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
82 #define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
85 #ifndef CPPHTTPLIB_TCP_NODELAY
86 #define CPPHTTPLIB_TCP_NODELAY false
89 #ifndef CPPHTTPLIB_RECV_BUFSIZ
90 #define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
93 #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
94 #define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
97 #ifndef CPPHTTPLIB_THREAD_POOL_COUNT
98 #define CPPHTTPLIB_THREAD_POOL_COUNT \
99 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
100 ? std::thread::hardware_concurrency() - 1 \
104 #ifndef CPPHTTPLIB_RECV_FLAGS
105 #define CPPHTTPLIB_RECV_FLAGS 0
108 #ifndef CPPHTTPLIB_SEND_FLAGS
109 #define CPPHTTPLIB_SEND_FLAGS 0
112 #ifndef CPPHTTPLIB_LISTEN_BACKLOG
113 #define CPPHTTPLIB_LISTEN_BACKLOG 5
121 #ifndef _CRT_SECURE_NO_WARNINGS
122 #define _CRT_SECURE_NO_WARNINGS
123 #endif //_CRT_SECURE_NO_WARNINGS
125 #ifndef _CRT_NONSTDC_NO_DEPRECATE
126 #define _CRT_NONSTDC_NO_DEPRECATE
127 #endif //_CRT_NONSTDC_NO_DEPRECATE
129 #if defined(_MSC_VER)
131 #error Sorry, Visual Studio versions prior to 2015 are not supported
134 #pragma comment(lib, "ws2_32.lib")
137 using ssize_t = __int64;
139 using ssize_t = long;
144 #define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
148 #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
156 #include <winsock2.h>
157 #include <ws2tcpip.h>
159 #ifndef WSA_FLAG_NO_HANDLE_INHERIT
160 #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
164 #define strcasecmp _stricmp
168 #ifdef CPPHTTPLIB_USE_POLL
169 #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
174 #include <arpa/inet.h>
175 #if !defined(_AIX) && !defined(__MVS__)
181 #define NI_MAXHOST 1025
186 #include <netinet/in.h>
190 #include <netinet/tcp.h>
191 #ifdef CPPHTTPLIB_USE_POLL
196 #include <sys/mman.h>
197 #include <sys/select.h>
198 #include <sys/socket.h>
203 #ifndef INVALID_SOCKET
204 #define INVALID_SOCKET (-1)
214 #include <condition_variable>
219 #include <functional>
231 #include <sys/stat.h>
233 #include <unordered_map>
234 #include <unordered_set>
237 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
239 #include <wincrypt.h>
244 #undef X509_CERT_PAIR
245 #undef X509_EXTENSIONS
246 #undef PKCS7_SIGNER_INFO
249 #pragma comment(lib, "crypt32.lib")
250 #pragma comment(lib, "cryptui.lib")
252 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
253 #include <TargetConditionals.h>
255 #include <CoreFoundation/CoreFoundation.h>
256 #include <Security/Security.h>
257 #endif // TARGET_OS_OSX
260 #include <openssl/err.h>
261 #include <openssl/evp.h>
262 #include <openssl/ssl.h>
263 #include <openssl/x509v3.h>
265 #if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
266 #include <openssl/applink.c>
272 #if OPENSSL_VERSION_NUMBER < 0x1010100fL
273 #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
274 #elif OPENSSL_VERSION_NUMBER < 0x30000000L
275 #define SSL_get1_peer_certificate SSL_get_peer_certificate
280 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
284 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
285 #include <brotli/decode.h>
286 #include <brotli/encode.h>
306 template <
class T,
class... Args>
307 typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>
::type
310 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
314 typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>
::type
318 return std::unique_ptr<T>(
new RT[n]);
323 bool operator()(
const std::string& s1,
const std::string& s2)
const
325 return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
326 [](
unsigned char c1,
unsigned char c2)
327 { return ::tolower(c1) < ::tolower(c2); });
337 : exit_function(std::move(f)), execute_on_destruction{
true}
342 : exit_function(std::move(rhs.exit_function)),
343 execute_on_destruction{rhs.execute_on_destruction}
350 if (execute_on_destruction)
352 this->exit_function();
356 void release() { this->execute_on_destruction =
false; }
363 std::function<
void(
void)> exit_function;
364 bool execute_on_destruction;
369 using Headers = std::multimap<std::string, std::string, detail::ci>;
371 using Params = std::multimap<std::string, std::string>;
374 using Progress = std::function<
bool(uint64_t current, uint64_t total)>;
399 std::function<
bool(
const char* data,
size_t data_len)>
write;
405 class data_sink_streambuf :
public std::streambuf
408 explicit data_sink_streambuf(
DataSink& sink) : sink_(sink) {}
411 std::streamsize xsputn(
const char* s, std::streamsize n)
413 sink_.write(s,
static_cast<size_t>(n));
421 data_sink_streambuf sb_;
440 uint64_t offset, uint64_t total_length)>;
469 using Range = std::pair<ssize_t, ssize_t>;
497 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
498 const SSL* ssl =
nullptr;
501 bool has_header(
const std::string& key)
const;
505 void set_header(
const std::string& key,
const std::string& val);
507 bool has_param(
const std::string& key)
const;
508 std::string
get_param_value(
const std::string& key,
size_t id = 0)
const;
513 bool has_file(
const std::string& key)
const;
515 std::vector<MultipartFormData>
get_file_values(
const std::string& key)
const;
534 bool has_header(
const std::string& key)
const;
538 void set_header(
const std::string& key,
const std::string& val);
541 void set_content(
const char* s,
size_t n,
const std::string& content_type);
542 void set_content(
const std::string& s,
const std::string& content_type);
585 virtual ssize_t
read(
char* ptr,
size_t size) = 0;
586 virtual ssize_t
write(
const char* ptr,
size_t size) = 0;
591 template <
typename... Args> ssize_t
write_format(
const char* fmt,
const Args&... args);
592 ssize_t
write(
const char* ptr);
593 ssize_t
write(
const std::string& s);
602 virtual void enqueue(std::function<
void()> fn) = 0;
615 threads_.emplace_back(
worker(*
this));
623 void enqueue(std::function<
void()> fn)
override
626 std::unique_lock<std::mutex>
lock(mutex_);
627 jobs_.push_back(std::move(fn));
637 std::unique_lock<std::mutex>
lock(mutex_);
644 for (
auto& t : threads_) { t.join(); }
656 std::function<
void()> fn;
658 std::unique_lock<std::mutex>
lock(pool_.mutex_);
660 pool_.cond_.wait(
lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
662 if (pool_.shutdown_ && pool_.jobs_.empty())
667 fn = std::move(pool_.jobs_.front());
668 pool_.jobs_.pop_front();
671 assert(
true ==
static_cast<bool>(fn));
680 std::vector<std::thread> threads_;
681 std::list<std::function<
void()>> jobs_;
685 std::condition_variable cond_;
735 static constexpr
char marker =
':';
739 static constexpr
char separator =
'/';
744 std::vector<std::string> static_fragments_;
747 std::vector<std::string> param_names_;
807 bool set_base_dir(
const std::string& dir,
const std::string& mount_point = std::string());
808 bool set_mount_point(
const std::string& mount_point,
const std::string& dir,
812 const std::string& mime);
835 template <
class Rep,
class Period>
839 template <
class Rep,
class Period>
843 template <
class Rep,
class Period>
848 bool bind_to_port(
const std::string& host,
int port,
int socket_flags = 0);
852 bool listen(
const std::string& host,
int port,
int socket_flags = 0);
862 const std::function<
void(
Request&)>& setup_request);
876 using Handlers = std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
Handler>>;
877 using HandlersForContentReader =
880 static std::unique_ptr<detail::MatcherBase> make_matcher(
const std::string& pattern);
882 socket_t create_server_socket(
const std::string& host,
int port,
int socket_flags,
884 int bind_internal(
const std::string& host,
int port,
int socket_flags);
885 bool listen_internal();
888 bool handle_file_request(
const Request& req,
Response& res,
bool head =
false);
889 bool dispatch_request(
Request& req,
Response& res,
const Handlers& handlers);
892 const HandlersForContentReader& handlers);
894 bool parse_request_line(
const char* s,
Request& req);
895 void apply_ranges(
const Request& req,
Response& res, std::string& content_type,
896 std::string& boundary);
898 bool write_response_with_content(
Stream& strm,
bool close_connection,
const Request& req,
901 bool need_apply_ranges);
903 const std::string& boundary,
const std::string& content_type);
913 virtual bool process_and_close_socket(
socket_t sock);
915 std::atomic<bool> is_running_{
false};
916 std::atomic<bool> done_{
false};
918 struct MountPointEntry
920 std::string mount_point;
921 std::string base_dir;
924 std::vector<MountPointEntry> base_dirs_;
925 std::map<std::string, std::string> file_extension_and_mimetype_map_;
926 std::string default_file_mimetype_ =
"application/octet-stream";
929 Handlers get_handlers_;
930 Handlers post_handlers_;
931 HandlersForContentReader post_handlers_for_content_reader_;
932 Handlers put_handlers_;
933 HandlersForContentReader put_handlers_for_content_reader_;
934 Handlers patch_handlers_;
935 HandlersForContentReader patch_handlers_for_content_reader_;
936 Handlers delete_handlers_;
937 HandlersForContentReader delete_handlers_for_content_reader_;
938 Handlers options_handlers_;
948 int address_family_ = AF_UNSPEC;
986 : res_(std::move(res)), err_(err), request_headers_(std::move(request_headers))
990 operator bool()
const {
return res_ !=
nullptr; }
991 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
992 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1010 std::unique_ptr<Response> res_;
1022 explicit ClientImpl(
const std::string&
host,
int port,
const std::string& client_cert_path,
1023 const std::string& client_key_path);
1060 Result Post(
const std::string& path,
const char* body,
size_t content_length,
1061 const std::string& content_type);
1063 size_t content_length,
const std::string& content_type);
1064 Result Post(
const std::string& path,
const std::string& body,
const std::string& content_type);
1065 Result Post(
const std::string& path,
const Headers& headers,
const std::string& body,
1066 const std::string& content_type);
1068 const std::string& content_type);
1070 const std::string& content_type);
1071 Result Post(
const std::string& path,
const Headers& headers,
size_t content_length,
1087 Result Put(
const std::string& path,
const char* body,
size_t content_length,
1088 const std::string& content_type);
1089 Result Put(
const std::string& path,
const Headers& headers,
const char* body,
1090 size_t content_length,
const std::string& content_type);
1091 Result Put(
const std::string& path,
const std::string& body,
const std::string& content_type);
1092 Result Put(
const std::string& path,
const Headers& headers,
const std::string& body,
1093 const std::string& content_type);
1095 const std::string& content_type);
1097 const std::string& content_type);
1098 Result Put(
const std::string& path,
const Headers& headers,
size_t content_length,
1108 const std::string& boundary);
1113 Result Patch(
const std::string& path,
const char* body,
size_t content_length,
1114 const std::string& content_type);
1116 size_t content_length,
const std::string& content_type);
1117 Result Patch(
const std::string& path,
const std::string& body,
const std::string& content_type);
1118 Result Patch(
const std::string& path,
const Headers& headers,
const std::string& body,
1119 const std::string& content_type);
1121 const std::string& content_type);
1123 const std::string& content_type);
1131 Result Delete(
const std::string& path,
const char* body,
size_t content_length,
1132 const std::string& content_type);
1134 size_t content_length,
const std::string& content_type);
1135 Result Delete(
const std::string& path,
const std::string& body,
1136 const std::string& content_type);
1138 const std::string& content_type);
1148 std::string
host()
const;
1163 template <
class Rep,
class Period>
1167 template <
class Rep,
class Period>
1171 template <
class Rep,
class Period>
1174 void set_basic_auth(
const std::string& username,
const std::string& password);
1176 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1177 void set_digest_auth(
const std::string& username,
const std::string& password);
1194 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1195 void set_proxy_digest_auth(
const std::string& username,
const std::string& password);
1198 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1199 void set_ca_cert_path(
const std::string& ca_cert_file_path,
1200 const std::string& ca_cert_dir_path = std::string());
1201 void set_ca_cert_store(X509_STORE* ca_cert_store);
1202 X509_STORE* create_ca_cert_store(
const char* ca_cert, std::size_t size);
1205 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1206 void enable_server_certificate_verification(
bool enabled);
1215 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1277 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1278 std::string digest_auth_username_;
1279 std::string digest_auth_password_;
1302 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1303 std::string proxy_digest_auth_username_;
1304 std::string proxy_digest_auth_password_;
1307 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1308 std::string ca_cert_file_path_;
1309 std::string ca_cert_dir_path_;
1311 X509_STORE* ca_cert_store_ =
nullptr;
1314 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1315 bool server_certificate_verification_ =
true;
1330 std::unique_ptr<Response>
1331 send_with_content_provider(
Request& req,
const char* body,
size_t content_length,
1334 const std::string& content_type,
Error& error);
1335 Result send_with_content_provider(
const std::string& method,
const std::string& path,
1336 const Headers& headers,
const char* body,
1339 const std::string& content_type);
1344 std::string adjust_host_string(
const std::string&
host)
const;
1346 virtual bool process_socket(
const Socket&
socket, std::function<
bool(
Stream& strm)> callback);
1347 virtual bool is_ssl()
const;
1354 explicit Client(
const std::string& scheme_host_port);
1356 explicit Client(
const std::string& scheme_host_port,
const std::string& client_cert_path,
1357 const std::string& client_key_path);
1362 explicit Client(
const std::string&
host,
int port,
const std::string& client_cert_path,
1363 const std::string& client_key_path);
1402 Result Post(
const std::string& path,
const char* body,
size_t content_length,
1403 const std::string& content_type);
1405 size_t content_length,
const std::string& content_type);
1406 Result Post(
const std::string& path,
const std::string& body,
const std::string& content_type);
1407 Result Post(
const std::string& path,
const Headers& headers,
const std::string& body,
1408 const std::string& content_type);
1410 const std::string& content_type);
1412 const std::string& content_type);
1413 Result Post(
const std::string& path,
const Headers& headers,
size_t content_length,
1429 Result Put(
const std::string& path,
const char* body,
size_t content_length,
1430 const std::string& content_type);
1431 Result Put(
const std::string& path,
const Headers& headers,
const char* body,
1432 size_t content_length,
const std::string& content_type);
1433 Result Put(
const std::string& path,
const std::string& body,
const std::string& content_type);
1434 Result Put(
const std::string& path,
const Headers& headers,
const std::string& body,
1435 const std::string& content_type);
1437 const std::string& content_type);
1439 const std::string& content_type);
1440 Result Put(
const std::string& path,
const Headers& headers,
size_t content_length,
1450 const std::string& boundary);
1455 Result Patch(
const std::string& path,
const char* body,
size_t content_length,
1456 const std::string& content_type);
1458 size_t content_length,
const std::string& content_type);
1459 Result Patch(
const std::string& path,
const std::string& body,
const std::string& content_type);
1460 Result Patch(
const std::string& path,
const Headers& headers,
const std::string& body,
1461 const std::string& content_type);
1463 const std::string& content_type);
1465 const std::string& content_type);
1473 Result Delete(
const std::string& path,
const char* body,
size_t content_length,
1474 const std::string& content_type);
1476 size_t content_length,
const std::string& content_type);
1477 Result Delete(
const std::string& path,
const std::string& body,
1478 const std::string& content_type);
1480 const std::string& content_type);
1490 std::string
host()
const;
1505 template <
class Rep,
class Period>
1509 template <
class Rep,
class Period>
1513 template <
class Rep,
class Period>
1516 void set_basic_auth(
const std::string& username,
const std::string& password);
1518 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1519 void set_digest_auth(
const std::string& username,
const std::string& password);
1536 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1537 void set_proxy_digest_auth(
const std::string& username,
const std::string& password);
1540 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1541 void enable_server_certificate_verification(
bool enabled);
1547 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1548 void set_ca_cert_path(
const std::string& ca_cert_file_path,
1549 const std::string& ca_cert_dir_path = std::string());
1551 void set_ca_cert_store(X509_STORE* ca_cert_store);
1552 void load_ca_cert_store(
const char* ca_cert, std::size_t size);
1554 long get_openssl_verify_result()
const;
1556 SSL_CTX* ssl_context()
const;
1560 std::unique_ptr<ClientImpl> cli_;
1562 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1563 bool is_ssl_ =
false;
1567 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1568 class SSLServer :
public Server
1571 SSLServer(
const char* cert_path,
const char* private_key_path,
1572 const char* client_ca_cert_file_path =
nullptr,
1573 const char* client_ca_cert_dir_path =
nullptr,
1574 const char* private_key_password =
nullptr);
1576 SSLServer(X509* cert, EVP_PKEY* private_key, X509_STORE* client_ca_cert_store =
nullptr);
1578 SSLServer(
const std::function<
bool(SSL_CTX& ssl_ctx)>& setup_ssl_ctx_callback);
1580 ~SSLServer()
override;
1582 bool is_valid()
const override;
1584 SSL_CTX* ssl_context()
const;
1587 bool process_and_close_socket(
socket_t sock)
override;
1593 class SSLClient :
public ClientImpl
1596 explicit SSLClient(
const std::string& host);
1598 explicit SSLClient(
const std::string& host,
int port);
1600 explicit SSLClient(
const std::string& host,
int port,
const std::string& client_cert_path,
1601 const std::string& client_key_path);
1603 explicit SSLClient(
const std::string& host,
int port, X509* client_cert, EVP_PKEY* client_key);
1605 ~SSLClient()
override;
1607 bool is_valid()
const override;
1609 void set_ca_cert_store(X509_STORE* ca_cert_store);
1610 void load_ca_cert_store(
const char* ca_cert, std::size_t size);
1612 long get_openssl_verify_result()
const;
1614 SSL_CTX* ssl_context()
const;
1617 bool create_and_connect_socket(Socket& socket,
Error& error)
override;
1618 void shutdown_ssl(Socket& socket,
bool shutdown_gracefully)
override;
1621 bool process_socket(
const Socket& socket, std::function<
bool(Stream& strm)> callback)
override;
1622 bool is_ssl()
const override;
1624 bool connect_with_proxy(Socket& sock, Response& res,
bool& success,
Error& error);
1625 bool initialize_ssl(Socket& socket,
Error& error);
1629 bool verify_host(X509* server_cert)
const;
1630 bool verify_host_with_subject_alt_name(X509* server_cert)
const;
1631 bool verify_host_with_common_name(X509* server_cert)
const;
1632 bool check_host_name(
const char* pattern,
size_t pattern_len)
const;
1636 std::once_flag initialize_cert_;
1638 std::vector<std::string> host_components_;
1640 long verify_result_ = 0;
1642 friend class ClientImpl;
1653 template <
typename T,
typename U>
1656 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
1658 std::chrono::duration_cast<std::chrono::microseconds>(duration - std::chrono::seconds(sec))
1660 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
1666 auto rng = headers.equal_range(key);
1667 auto it = rng.first;
1668 std::advance(it,
static_cast<ssize_t
>(
id));
1669 if (it != rng.second)
1671 return std::strtoull(it->second.data(),
nullptr, 10);
1688 template <
typename... Args>
1691 const auto bufsiz = 2048;
1692 std::array<char, bufsiz> buf{};
1694 auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
1700 auto n =
static_cast<size_t>(sn);
1702 if (n >= buf.size() - 1)
1704 std::vector<char> glowable_buf(buf.size());
1706 while (n >= glowable_buf.size() - 1)
1708 glowable_buf.resize(glowable_buf.size() * 2);
1709 n =
static_cast<size_t>(
1710 snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
1712 return write(&glowable_buf[0], n);
1716 return write(buf.data(), n);
1724 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*
>(&yes),
sizeof(yes));
1725 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast<const char*
>(&yes),
1729 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
reinterpret_cast<const void*
>(&yes),
sizeof(yes));
1731 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const void*
>(&yes),
sizeof(yes));
1740 case 100:
return "Continue";
1741 case 101:
return "Switching Protocol";
1742 case 102:
return "Processing";
1743 case 103:
return "Early Hints";
1744 case 200:
return "OK";
1745 case 201:
return "Created";
1746 case 202:
return "Accepted";
1747 case 203:
return "Non-Authoritative Information";
1748 case 204:
return "No Content";
1749 case 205:
return "Reset Content";
1750 case 206:
return "Partial Content";
1751 case 207:
return "Multi-Status";
1752 case 208:
return "Already Reported";
1753 case 226:
return "IM Used";
1754 case 300:
return "Multiple Choice";
1755 case 301:
return "Moved Permanently";
1756 case 302:
return "Found";
1757 case 303:
return "See Other";
1758 case 304:
return "Not Modified";
1759 case 305:
return "Use Proxy";
1760 case 306:
return "unused";
1761 case 307:
return "Temporary Redirect";
1762 case 308:
return "Permanent Redirect";
1763 case 400:
return "Bad Request";
1764 case 401:
return "Unauthorized";
1765 case 402:
return "Payment Required";
1766 case 403:
return "Forbidden";
1767 case 404:
return "Not Found";
1768 case 405:
return "Method Not Allowed";
1769 case 406:
return "Not Acceptable";
1770 case 407:
return "Proxy Authentication Required";
1771 case 408:
return "Request Timeout";
1772 case 409:
return "Conflict";
1773 case 410:
return "Gone";
1774 case 411:
return "Length Required";
1775 case 412:
return "Precondition Failed";
1776 case 413:
return "Payload Too Large";
1777 case 414:
return "URI Too Long";
1778 case 415:
return "Unsupported Media Type";
1779 case 416:
return "Range Not Satisfiable";
1780 case 417:
return "Expectation Failed";
1781 case 418:
return "I'm a teapot";
1782 case 421:
return "Misdirected Request";
1783 case 422:
return "Unprocessable Entity";
1784 case 423:
return "Locked";
1785 case 424:
return "Failed Dependency";
1786 case 425:
return "Too Early";
1787 case 426:
return "Upgrade Required";
1788 case 428:
return "Precondition Required";
1789 case 429:
return "Too Many Requests";
1790 case 431:
return "Request Header Fields Too Large";
1791 case 451:
return "Unavailable For Legal Reasons";
1792 case 501:
return "Not Implemented";
1793 case 502:
return "Bad Gateway";
1794 case 503:
return "Service Unavailable";
1795 case 504:
return "Gateway Timeout";
1796 case 505:
return "HTTP Version Not Supported";
1797 case 506:
return "Variant Also Negotiates";
1798 case 507:
return "Insufficient Storage";
1799 case 508:
return "Loop Detected";
1800 case 510:
return "Not Extended";
1801 case 511:
return "Network Authentication Required";
1804 case 500:
return "Internal Server Error";
1808 template <
class Rep,
class Period>
1816 template <
class Rep,
class Period>
1824 template <
class Rep,
class Period>
1839 case Error::Read:
return "Failed to read connection";
1840 case Error::Write:
return "Failed to write connection";
1847 return "Unsupported HTTP multipart boundary characters";
1870 template <
class Rep,
class Period>
1877 template <
class Rep,
class Period>
1884 template <
class Rep,
class Period>
1891 template <
class Rep,
class Period>
1894 cli_->set_connection_timeout(duration);
1897 template <
class Rep,
class Period>
1900 cli_->set_read_timeout(duration);
1903 template <
class Rep,
class Period>
1906 cli_->set_write_timeout(duration);
1923 const std::string& password,
1924 bool is_proxy =
false);
1931 std::string
decode_url(
const std::string& s,
bool convert_plus_to_space);
1933 void read_file(
const std::string& path, std::string& out);
1935 std::string
trim_copy(
const std::string& s);
1937 void split(
const char* b,
const char*
e,
char d, std::function<
void(
const char*,
const char*)> fn);
1940 time_t write_timeout_sec, time_t write_timeout_usec,
1941 std::function<
bool(
Stream&)> callback);
1944 int address_family,
bool tcp_nodelay,
SocketOptions socket_options,
1945 time_t connection_timeout_sec, time_t connection_timeout_usec,
1946 time_t read_timeout_sec, time_t read_timeout_usec,
1947 time_t write_timeout_sec, time_t write_timeout_usec,
1948 const std::string& intf,
Error& error);
1951 const char* def =
nullptr);
1984 ssize_t
read(
char* ptr,
size_t size)
override;
1985 ssize_t
write(
const char* ptr,
size_t size)
override;
1994 size_t position = 0;
2003 virtual bool compress(
const char* data,
size_t data_length,
bool last,
Callback callback) = 0;
2014 virtual bool decompress(
const char* data,
size_t data_length,
Callback callback) = 0;
2022 bool compress(
const char* data,
size_t data_length,
bool ,
Callback callback)
override;
2025 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2032 bool compress(
const char* data,
size_t data_length,
bool last, Callback callback)
override;
2035 bool is_valid_ =
false;
2039 class gzip_decompressor :
public decompressor
2042 gzip_decompressor();
2043 ~gzip_decompressor();
2045 bool is_valid()
const override;
2047 bool decompress(
const char* data,
size_t data_length, Callback callback)
override;
2050 bool is_valid_ =
false;
2055 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2056 class brotli_compressor :
public compressor
2059 brotli_compressor();
2060 ~brotli_compressor();
2062 bool compress(
const char* data,
size_t data_length,
bool last, Callback callback)
override;
2065 BrotliEncoderState* state_ =
nullptr;
2068 class brotli_decompressor :
public decompressor
2071 brotli_decompressor();
2072 ~brotli_decompressor();
2074 bool is_valid()
const override;
2076 bool decompress(
const char* data,
size_t data_length, Callback callback)
override;
2079 BrotliDecoderResult decoder_r;
2080 BrotliDecoderState* decoder_s =
nullptr;
2090 const char*
ptr()
const;
2091 size_t size()
const;
2096 void append(
char c);
2099 char* fixed_buffer_;
2100 const size_t fixed_buffer_size_;
2101 size_t fixed_buffer_used_size_ = 0;
2102 std::string glowable_buffer_;
2108 mmap(
const char* path);
2111 bool open(
const char* path);
2115 size_t size()
const;
2116 const char*
data()
const;
2142 if (0x20 <= c && isdigit(c))
2147 else if (
'A' <= c && c <=
'F')
2152 else if (
'a' <= c && c <=
'f')
2168 for (; cnt; i++, cnt--)
2189 static const auto charset =
"0123456789abcdef";
2192 ret = charset[n & 15] + ret;
2202 buff[0] = (code & 0x7F);
2205 else if (code < 0x0800)
2207 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2208 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
2211 else if (code < 0xD800)
2213 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2214 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2215 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2218 else if (code < 0xE000)
2222 else if (code < 0x10000)
2224 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2225 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2226 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2229 else if (code < 0x110000)
2231 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2232 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2233 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2234 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
2246 static const auto lookup =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2249 out.reserve(in.size());
2256 val = (val << 8) + static_cast<uint8_t>(c);
2260 out.push_back(lookup[(val >> valb) & 0x3F]);
2267 out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]);
2270 while (out.size() % 4) { out.push_back(
'='); }
2278 return _access_s(path.c_str(), 0) == 0;
2281 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
2288 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
2297 while (i < path.size() && path[i] ==
'/') { i++; }
2299 while (i < path.size())
2303 while (i < path.size() && path[i] !=
'/') { i++; }
2308 if (!path.compare(beg, len,
"."))
2312 else if (!path.compare(beg, len,
".."))
2326 while (i < path.size() && path[i] ==
'/') { i++; }
2334 std::ostringstream escaped;
2336 escaped << std::hex;
2338 for (
auto c : value)
2340 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' || c ==
'.' || c ==
'!' ||
2341 c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' || c ==
')')
2347 escaped << std::uppercase;
2348 escaped << '%' << std::setw(2) << static_cast<int>(
static_cast<unsigned char>(c));
2349 escaped << std::nouppercase;
2353 return escaped.str();
2359 result.reserve(s.size());
2361 for (
size_t i = 0; s[i]; i++)
2365 case ' ': result +=
"%20";
break;
2366 case '+': result +=
"%2B";
break;
2367 case '\r': result +=
"%0D";
break;
2368 case '\n': result +=
"%0A";
break;
2369 case '\'': result +=
"%27";
break;
2370 case ',': result +=
"%2C";
break;
2372 case ';': result +=
"%3B";
break;
2374 auto c =
static_cast<uint8_t
>(s[i]);
2379 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
2381 result.append(hex,
static_cast<size_t>(len));
2394 inline std::string
decode_url(
const std::string& s,
bool convert_plus_to_space)
2398 for (
size_t i = 0; i < s.size(); i++)
2400 if (s[i] ==
'%' && i + 1 < s.size())
2402 if (s[i + 1] ==
'u')
2409 size_t len =
to_utf8(val, buff);
2412 result.append(buff, len);
2427 result +=
static_cast<char>(val);
2436 else if (convert_plus_to_space && s[i] ==
'+')
2449 inline void read_file(
const std::string& path, std::string& out)
2451 std::ifstream fs(path, std::ios_base::binary);
2452 fs.seekg(0, std::ios_base::end);
2453 auto size = fs.tellg();
2455 out.resize(
static_cast<size_t>(size));
2456 fs.read(&out[0],
static_cast<std::streamsize
>(size));
2462 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2463 if (std::regex_search(path, m, re))
2467 return std::string();
2472 inline std::pair<size_t, size_t>
trim(
const char* b,
const char*
e,
size_t left,
size_t right)
2476 return std::make_pair(left, right);
2481 auto r =
trim(s.data(), s.data() + s.size(), 0, s.size());
2482 return s.substr(r.first, r.second - r.first);
2487 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"')
2489 return s.substr(1, s.size() - 2);
2494 inline void split(
const char* b,
const char*
e,
char d,
2495 std::function<
void(
const char*,
const char*)> fn)
2500 while (
e ? (b + i <
e) : (b[i] !=
'\0'))
2504 auto r =
trim(b,
e, beg, i);
2505 if (r.first < r.second)
2507 fn(&b[r.first], &b[r.second]);
2516 auto r =
trim(b,
e, beg, i);
2517 if (r.first < r.second)
2519 fn(&b[r.first], &b[r.second]);
2525 size_t fixed_buffer_size)
2526 : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size)
2532 if (glowable_buffer_.empty())
2534 return fixed_buffer_;
2538 return glowable_buffer_.data();
2544 if (glowable_buffer_.empty())
2546 return fixed_buffer_used_size_;
2550 return glowable_buffer_.size();
2557 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
2562 fixed_buffer_used_size_ = 0;
2563 glowable_buffer_.clear();
2565 for (
size_t i = 0;; i++)
2568 auto n = strm_.
read(&
byte, 1);
2597 inline void stream_line_reader::append(
char c)
2599 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1)
2601 fixed_buffer_[fixed_buffer_used_size_++] = c;
2602 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
2606 if (glowable_buffer_.empty())
2608 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
2609 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2611 glowable_buffer_ += c;
2628 std::runtime_error(
"");
2639 hFile_ = ::CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
2640 FILE_ATTRIBUTE_NORMAL, NULL);
2642 if (hFile_ == INVALID_HANDLE_VALUE)
2647 size_ = ::GetFileSize(hFile_, NULL);
2649 hMapping_ = ::CreateFileMapping(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
2651 if (hMapping_ == NULL)
2657 addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
2659 fd_ =
::open(path, O_RDONLY);
2666 if (fstat(fd_, &sb) == -1)
2671 size_ =
static_cast<size_t>(sb.st_size);
2673 addr_ =
::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
2676 if (addr_ ==
nullptr)
2689 inline const char*
mmap::data()
const {
return (
const char*)addr_; }
2696 ::UnmapViewOfFile(addr_);
2702 ::CloseHandle(hMapping_);
2706 if (hFile_ != INVALID_HANDLE_VALUE)
2708 ::CloseHandle(hFile_);
2709 hFile_ = INVALID_HANDLE_VALUE;
2712 if (addr_ !=
nullptr)
2714 munmap(addr_, size_);
2729 return closesocket(sock);
2741 if (res < 0 && errno == EINTR)
2757 static_cast<char*
>(ptr),
static_cast<int>(size),
2772 static_cast<const char*
>(ptr),
static_cast<int>(size),
2782 #ifdef CPPHTTPLIB_USE_POLL
2783 struct pollfd pfd_read;
2785 pfd_read.events = POLLIN;
2787 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2789 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2792 if (sock >= FD_SETSIZE)
2803 tv.tv_sec =
static_cast<long>(sec);
2804 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2807 [&]() {
return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv); });
2813 #ifdef CPPHTTPLIB_USE_POLL
2814 struct pollfd pfd_read;
2816 pfd_read.events = POLLOUT;
2818 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2820 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2823 if (sock >= FD_SETSIZE)
2834 tv.tv_sec =
static_cast<long>(sec);
2835 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2838 [&]() {
return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv); });
2844 #ifdef CPPHTTPLIB_USE_POLL
2845 struct pollfd pfd_read;
2847 pfd_read.events = POLLIN | POLLOUT;
2849 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
2851 auto poll_res =
handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
2858 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT))
2861 socklen_t len =
sizeof(error);
2862 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char*
>(&error), &len);
2863 auto successful = res >= 0 && !error;
2870 if (sock >= FD_SETSIZE)
2878 FD_SET(sock, &fdsr);
2884 tv.tv_sec =
static_cast<long>(sec);
2885 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
2888 [&]() {
return select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv); });
2895 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)))
2898 socklen_t len =
sizeof(error);
2899 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char*
>(&error), &len);
2900 auto successful = res >= 0 && !error;
2914 else if (val < 0 && errno == EBADF)
2926 time_t write_timeout_sec, time_t write_timeout_usec);
2931 ssize_t
read(
char* ptr,
size_t size)
override;
2932 ssize_t
write(
const char* ptr,
size_t size)
override;
2939 time_t read_timeout_sec_;
2940 time_t read_timeout_usec_;
2941 time_t write_timeout_sec_;
2942 time_t write_timeout_usec_;
2944 std::vector<char> read_buff_;
2945 size_t read_buff_off_ = 0;
2946 size_t read_buff_content_size_ = 0;
2948 static const size_t read_buff_size_ = 1024 * 4;
2951 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2952 class SSLSocketStream :
public Stream
2955 SSLSocketStream(
socket_t sock, SSL* ssl, time_t read_timeout_sec, time_t read_timeout_usec,
2956 time_t write_timeout_sec, time_t write_timeout_usec);
2957 ~SSLSocketStream()
override;
2959 bool is_readable()
const override;
2960 bool is_writable()
const override;
2961 ssize_t read(
char* ptr,
size_t size)
override;
2962 ssize_t write(
const char* ptr,
size_t size)
override;
2970 time_t read_timeout_sec_;
2971 time_t read_timeout_usec_;
2972 time_t write_timeout_sec_;
2973 time_t write_timeout_usec_;
2979 using namespace std::chrono;
2980 auto start = steady_clock::now();
2990 auto current = steady_clock::now();
2991 auto duration = duration_cast<milliseconds>(current - start);
2992 auto timeout = keep_alive_timeout_sec * 1000;
2993 if (duration.count() > timeout)
2997 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3006 template <
typename T>
3008 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
3011 assert(keep_alive_max_count > 0);
3013 auto count = keep_alive_max_count;
3016 auto close_connection = count == 1;
3017 auto connection_closed =
false;
3018 ret = callback(close_connection, connection_closed);
3019 if (!ret || connection_closed)
3028 template <
typename T>
3030 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
3031 time_t read_timeout_sec, time_t read_timeout_usec,
3032 time_t write_timeout_sec, time_t write_timeout_usec, T callback)
3035 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3036 [&](
bool close_connection,
bool& connection_closed)
3038 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec,
3039 write_timeout_usec);
3040 return callback(strm, close_connection, connection_closed);
3045 time_t write_timeout_sec, time_t write_timeout_usec,
3046 std::function<
bool(
Stream&)> callback)
3048 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec,
3049 write_timeout_usec);
3050 return callback(strm);
3056 return shutdown(sock, SD_BOTH);
3058 return shutdown(sock, SHUT_RDWR);
3062 template <
typename BindOrConnect>
3064 int socket_flags,
bool tcp_nodelay,
SocketOptions socket_options,
3065 BindOrConnect bind_or_connect)
3068 const char* node =
nullptr;
3069 struct addrinfo hints;
3070 struct addrinfo* result;
3072 memset(&hints, 0,
sizeof(
struct addrinfo));
3073 hints.ai_socktype = SOCK_STREAM;
3074 hints.ai_protocol = 0;
3080 hints.ai_family = AF_UNSPEC;
3081 hints.ai_flags = AI_NUMERICHOST;
3087 node = host.c_str();
3089 hints.ai_family = address_family;
3090 hints.ai_flags = socket_flags;
3094 if (hints.ai_family == AF_UNIX)
3096 const auto addrlen = host.length();
3097 if (addrlen >
sizeof(sockaddr_un::sun_path))
3100 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3104 addr.sun_family = AF_UNIX;
3105 std::copy(host.begin(), host.end(), addr.sun_path);
3107 hints.ai_addr =
reinterpret_cast<sockaddr*
>(&addr);
3109 static_cast<socklen_t
>(
sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
3111 fcntl(sock, F_SETFD, FD_CLOEXEC);
3114 socket_options(sock);
3117 if (!bind_or_connect(sock, hints))
3129 if (getaddrinfo(node, service.c_str(), &hints, &result))
3131 #if defined __linux__ && !defined __ANDROID__
3137 for (
auto rp = result; rp; rp = rp->ai_next)
3141 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
3142 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3159 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3162 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3170 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1)
3181 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const char*
>(&yes),
3184 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<const void*
>(&yes),
3191 socket_options(sock);
3194 if (rp->ai_family == AF_INET6)
3198 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char*
>(&no),
3201 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const void*
>(&no),
3207 if (bind_or_connect(sock, *rp))
3209 freeaddrinfo(result);
3216 freeaddrinfo(result);
3223 auto flags = nonblocking ? 1UL : 0UL;
3224 ioctlsocket(sock, FIONBIO, &flags);
3226 auto flags = fcntl(sock, F_GETFL, 0);
3227 fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3234 return WSAGetLastError() != WSAEWOULDBLOCK;
3236 return errno != EINPROGRESS;
3242 struct addrinfo hints;
3243 struct addrinfo* result;
3245 memset(&hints, 0,
sizeof(
struct addrinfo));
3246 hints.ai_family = AF_UNSPEC;
3247 hints.ai_socktype = SOCK_STREAM;
3248 hints.ai_protocol = 0;
3250 if (getaddrinfo(host.c_str(),
"0", &hints, &result))
3256 for (
auto rp = result; rp; rp = rp->ai_next)
3258 const auto& ai = *rp;
3259 if (!::
bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen)))
3266 freeaddrinfo(result);
3270 #if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3275 inline std::string
if2ip(
int address_family,
const std::string& ifn)
3277 struct ifaddrs* ifap;
3279 std::string addr_candidate;
3280 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next)
3282 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3283 (AF_UNSPEC == address_family || ifa->ifa_addr->sa_family == address_family))
3285 if (ifa->ifa_addr->sa_family == AF_INET)
3287 auto sa =
reinterpret_cast<struct sockaddr_in*
>(ifa->ifa_addr);
3288 char buf[INET_ADDRSTRLEN];
3289 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN))
3292 return std::string(buf, INET_ADDRSTRLEN);
3295 else if (ifa->ifa_addr->sa_family == AF_INET6)
3297 auto sa =
reinterpret_cast<struct sockaddr_in6*
>(ifa->ifa_addr);
3298 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
3300 char buf[INET6_ADDRSTRLEN] = {};
3301 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN))
3304 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3305 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd)
3307 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3312 return std::string(buf, INET6_ADDRSTRLEN);
3320 return addr_candidate;
3325 int address_family,
bool tcp_nodelay,
3326 SocketOptions socket_options, time_t connection_timeout_sec,
3327 time_t connection_timeout_usec, time_t read_timeout_sec,
3328 time_t read_timeout_usec, time_t write_timeout_sec,
3329 time_t write_timeout_usec,
const std::string& intf,
3333 host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options),
3334 [&](
socket_t sock2,
struct addrinfo& ai) ->
bool
3339 auto ip_from_if = if2ip(address_family, intf);
3340 if (ip_from_if.empty())
3346 error = Error::BindIPAddress;
3354 auto ret =
::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
3364 connection_timeout_usec);
3376 static_cast<uint32_t
>(read_timeout_sec * 1000 + read_timeout_usec / 1000);
3377 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout),
3381 tv.tv_sec =
static_cast<long>(read_timeout_sec);
3382 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec);
3383 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void*
>(&tv),
3391 static_cast<uint32_t
>(write_timeout_sec * 1000 + write_timeout_usec / 1000);
3392 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char*
>(&timeout),
3396 tv.tv_sec =
static_cast<long>(write_timeout_sec);
3397 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec);
3398 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const void*
>(&tv),
3423 std::string& ip,
int& port)
3425 if (addr.ss_family == AF_INET)
3427 port = ntohs(
reinterpret_cast<const struct sockaddr_in*
>(&addr)->sin_port);
3429 else if (addr.ss_family == AF_INET6)
3431 port = ntohs(
reinterpret_cast<const struct sockaddr_in6*
>(&addr)->sin6_port);
3438 std::array<char, NI_MAXHOST> ipstr{};
3439 if (getnameinfo(
reinterpret_cast<const struct sockaddr*
>(&addr), addr_len, ipstr.data(),
3440 static_cast<socklen_t
>(ipstr.size()),
nullptr, 0, NI_NUMERICHOST))
3451 struct sockaddr_storage addr;
3452 socklen_t addr_len =
sizeof(addr);
3453 if (!getsockname(sock,
reinterpret_cast<struct sockaddr*
>(&addr), &addr_len))
3461 struct sockaddr_storage addr;
3462 socklen_t addr_len =
sizeof(addr);
3464 if (!getpeername(sock,
reinterpret_cast<struct sockaddr*
>(&addr), &addr_len))
3467 if (addr.ss_family == AF_UNIX)
3469 #if defined(__linux__)
3471 socklen_t len =
sizeof(ucred);
3472 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0)
3476 #elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
3478 socklen_t len =
sizeof(pid);
3479 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0)
3491 inline constexpr
unsigned int str2tag_core(
const char* s,
size_t l,
unsigned int h)
3496 (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^
3497 static_cast<unsigned char>(*s));
3505 inline constexpr
unsigned int operator"" _t(
const char* s,
size_t l)
3513 const std::map<std::string, std::string>& user_data,
3514 const std::string& default_content_type)
3518 auto it = user_data.find(ext);
3519 if (it != user_data.end())
3521 return it->second.c_str();
3524 using udl::operator
""_t;
3528 default:
return default_content_type;
3530 case "css"_t:
return "text/css";
3531 case "csv"_t:
return "text/csv";
3533 case "html"_t:
return "text/html";
3535 case "mjs"_t:
return "text/javascript";
3536 case "txt"_t:
return "text/plain";
3537 case "vtt"_t:
return "text/vtt";
3539 case "apng"_t:
return "image/apng";
3540 case "avif"_t:
return "image/avif";
3541 case "bmp"_t:
return "image/bmp";
3542 case "gif"_t:
return "image/gif";
3543 case "png"_t:
return "image/png";
3544 case "svg"_t:
return "image/svg+xml";
3545 case "webp"_t:
return "image/webp";
3546 case "ico"_t:
return "image/x-icon";
3547 case "tif"_t:
return "image/tiff";
3548 case "tiff"_t:
return "image/tiff";
3550 case "jpeg"_t:
return "image/jpeg";
3552 case "mp4"_t:
return "video/mp4";
3553 case "mpeg"_t:
return "video/mpeg";
3554 case "webm"_t:
return "video/webm";
3556 case "mp3"_t:
return "audio/mp3";
3557 case "mpga"_t:
return "audio/mpeg";
3558 case "weba"_t:
return "audio/webm";
3559 case "wav"_t:
return "audio/wave";
3561 case "otf"_t:
return "font/otf";
3562 case "ttf"_t:
return "font/ttf";
3563 case "woff"_t:
return "font/woff";
3564 case "woff2"_t:
return "font/woff2";
3566 case "7z"_t:
return "application/x-7z-compressed";
3567 case "atom"_t:
return "application/atom+xml";
3568 case "pdf"_t:
return "application/pdf";
3569 case "json"_t:
return "application/json";
3570 case "rss"_t:
return "application/rss+xml";
3571 case "tar"_t:
return "application/x-tar";
3573 case "xhtml"_t:
return "application/xhtml+xml";
3574 case "xslt"_t:
return "application/xslt+xml";
3575 case "xml"_t:
return "application/xml";
3576 case "gz"_t:
return "application/gzip";
3577 case "zip"_t:
return "application/zip";
3578 case "wasm"_t:
return "application/wasm";
3584 using udl::operator
""_t;
3586 auto tag =
str2tag(content_type);
3590 case "image/svg+xml"_t:
3591 case "application/javascript"_t:
3592 case "application/json"_t:
3593 case "application/xml"_t:
3594 case "application/protobuf"_t:
3595 case "application/xhtml+xml"_t:
return true;
3597 default:
return !content_type.rfind(
"text/", 0) && tag !=
"text/event-stream"_t;
3612 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3614 ret = s.find(
"br") != std::string::npos;
3621 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3623 ret = s.find(
"gzip") != std::string::npos;
3640 return callback(data, data_length);
3643 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3644 inline gzip_compressor::gzip_compressor()
3646 std::memset(&strm_, 0,
sizeof(strm_));
3647 strm_.zalloc = Z_NULL;
3648 strm_.zfree = Z_NULL;
3649 strm_.opaque = Z_NULL;
3652 deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK;
3655 inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3657 inline bool gzip_compressor::compress(
const char* data,
size_t data_length,
bool last,
3663 constexpr
size_t max_avail_in = (std::numeric_limits<decltype(strm_.avail_in)>::max)();
3666 static_cast<decltype(strm_.avail_in)
>((std::min)(data_length, max_avail_in));
3667 strm_.next_in =
const_cast<Bytef*
>(
reinterpret_cast<const Bytef*
>(data));
3669 data_length -= strm_.avail_in;
3670 data += strm_.avail_in;
3672 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
3675 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3677 strm_.avail_out =
static_cast<uInt
>(buff.size());
3678 strm_.next_out =
reinterpret_cast<Bytef*
>(buff.data());
3680 ret = deflate(&strm_, flush);
3681 if (ret == Z_STREAM_ERROR)
3686 if (!callback(buff.data(), buff.size() - strm_.avail_out))
3690 }
while (strm_.avail_out == 0);
3692 assert((flush == Z_FINISH && ret == Z_STREAM_END) || (flush == Z_NO_FLUSH && ret == Z_OK));
3693 assert(strm_.avail_in == 0);
3694 }
while (data_length > 0);
3699 inline gzip_decompressor::gzip_decompressor()
3701 std::memset(&strm_, 0,
sizeof(strm_));
3702 strm_.zalloc = Z_NULL;
3703 strm_.zfree = Z_NULL;
3704 strm_.opaque = Z_NULL;
3710 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
3713 inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3715 inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
3717 inline bool gzip_decompressor::decompress(
const char* data,
size_t data_length, Callback callback)
3724 constexpr
size_t max_avail_in = (std::numeric_limits<decltype(strm_.avail_in)>::max)();
3727 static_cast<decltype(strm_.avail_in)
>((std::min)(data_length, max_avail_in));
3728 strm_.next_in =
const_cast<Bytef*
>(
reinterpret_cast<const Bytef*
>(data));
3730 data_length -= strm_.avail_in;
3731 data += strm_.avail_in;
3733 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3734 while (strm_.avail_in > 0 && ret == Z_OK)
3736 strm_.avail_out =
static_cast<uInt
>(buff.size());
3737 strm_.next_out =
reinterpret_cast<Bytef*
>(buff.data());
3739 ret = inflate(&strm_, Z_NO_FLUSH);
3741 assert(ret != Z_STREAM_ERROR);
3746 case Z_MEM_ERROR: inflateEnd(&strm_);
return false;
3749 if (!callback(buff.data(), buff.size() - strm_.avail_out))
3755 if (ret != Z_OK && ret != Z_STREAM_END)
3758 }
while (data_length > 0);
3764 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3765 inline brotli_compressor::brotli_compressor()
3767 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
3770 inline brotli_compressor::~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
3772 inline bool brotli_compressor::compress(
const char* data,
size_t data_length,
bool last,
3775 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3777 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
3778 auto available_in = data_length;
3779 auto next_in =
reinterpret_cast<const uint8_t*
>(data);
3785 if (BrotliEncoderIsFinished(state_))
3798 auto available_out = buff.size();
3799 auto next_out = buff.data();
3801 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, &available_out,
3802 &next_out,
nullptr))
3807 auto output_bytes = buff.size() - available_out;
3810 callback(
reinterpret_cast<const char*
>(buff.data()), output_bytes);
3817 inline brotli_decompressor::brotli_decompressor()
3819 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
3820 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT : BROTLI_DECODER_RESULT_ERROR;
3823 inline brotli_decompressor::~brotli_decompressor()
3827 BrotliDecoderDestroyInstance(decoder_s);
3831 inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
3833 inline bool brotli_decompressor::decompress(
const char* data,
size_t data_length, Callback callback)
3835 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_ERROR)
3840 auto next_in =
reinterpret_cast<const uint8_t*
>(data);
3841 size_t avail_in = data_length;
3844 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
3846 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3847 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT)
3849 char* next_out = buff.data();
3850 size_t avail_out = buff.size();
3853 BrotliDecoderDecompressStream(decoder_s, &avail_in, &next_in, &avail_out,
3854 reinterpret_cast<uint8_t**
>(&next_out), &total_out);
3856 if (decoder_r == BROTLI_DECODER_RESULT_ERROR)
3861 if (!callback(buff.data(), buff.size() - avail_out))
3867 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
3868 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
3874 return headers.find(key) != headers.end();
3880 auto rng = headers.equal_range(key);
3881 auto it = rng.first;
3882 std::advance(it,
static_cast<ssize_t
>(
id));
3883 if (it != rng.second)
3885 return it->second.c_str();
3892 if (a.size() != b.size())
3896 for (
size_t i = 0; i < b.size(); i++)
3898 if (::tolower(a[i]) != ::tolower(b[i]))
3906 template <
typename T>
inline bool parse_header(
const char* beg,
const char* end, T fn)
3912 while (p < end && *p !=
':') { p++; }
3930 auto key = std::string(beg, key_end);
3933 fn(std::move(key), std::move(val));
3942 const auto bufsiz = 2048;
3954 auto line_terminator_len = 2;
3958 if (line_reader.
size() == 2)
3962 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3967 if (line_reader.
size() == 1)
3971 line_terminator_len = 1;
3987 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
3990 [&](std::string&& key, std::string&& val)
3991 { headers.emplace(std::move(key), std::move(val)); });
4005 auto read_len =
static_cast<size_t>(len - r);
4012 if (!out(buf,
static_cast<size_t>(n), r, len))
4016 r +=
static_cast<uint64_t
>(n);
4020 if (!progress(r, len))
4036 auto read_len =
static_cast<size_t>(len - r);
4042 r +=
static_cast<uint64_t
>(n);
4062 if (!out(buf,
static_cast<size_t>(n), r, 0))
4066 r +=
static_cast<uint64_t
>(n);
4072 template <
typename T>
4075 const auto bufsiz = 16;
4085 unsigned long chunk_len;
4090 chunk_len = std::strtoul(line_reader.
ptr(), &end_ptr, 16);
4092 if (end_ptr == line_reader.
ptr())
4096 if (chunk_len == ULONG_MAX)
4116 if (strcmp(line_reader.
ptr(),
"\r\n"))
4127 assert(chunk_len == 0);
4135 while (strcmp(line_reader.
ptr(),
"\r\n"))
4143 constexpr
auto line_terminator_len = 2;
4144 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
4147 [&](std::string&& key, std::string&& val)
4148 { x.headers.emplace(std::move(key), std::move(val)); });
4161 return !strcasecmp(
get_header_value(headers,
"Transfer-Encoding", 0,
""),
"chunked");
4164 template <
typename T,
typename U>
4166 bool decompress, U callback)
4170 std::string encoding = x.get_header_value(
"Content-Encoding");
4173 if (encoding ==
"gzip" || encoding ==
"deflate")
4175 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4176 decompressor = detail::make_unique<gzip_decompressor>();
4182 else if (encoding.find(
"br") != std::string::npos)
4184 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4185 decompressor = detail::make_unique<brotli_decompressor>();
4197 [&](
const char* buf,
size_t n, uint64_t off, uint64_t len)
4200 [&](
const char* buf2,
size_t n2)
4201 {
return receiver(buf2, n2, off, len); });
4203 return callback(std::move(out));
4214 {
return receiver(buf, n, off, len); };
4215 return callback(std::move(out));
4218 template <
typename T>
4223 x,
status, std::move(receiver), decompress,
4227 auto exceed_payload_max_length =
false;
4231 ret = read_content_chunked(strm, x, out);
4233 else if (!
has_header(x.headers,
"Content-Length"))
4235 ret = read_content_without_length(strm, out);
4239 auto len = get_header_value_u64(x.headers,
"Content-Length", 0, 0);
4240 if (len > payload_max_length)
4242 exceed_payload_max_length = true;
4243 skip_content_with_length(strm, len);
4254 status = exceed_payload_max_length ? 413 : 400;
4262 ssize_t write_len = 0;
4263 for (
const auto& x : headers)
4265 auto len = strm.
write_format(
"%s: %s\r\n", x.first.c_str(), x.second.c_str());
4272 auto len = strm.
write(
"\r\n");
4286 auto length = strm.
write(d + offset, l - offset);
4291 offset +=
static_cast<size_t>(length);
4296 template <
typename T>
4298 size_t length, T is_shutting_down,
Error& error)
4300 size_t end_offset = offset + length;
4304 data_sink.
write = [&](
const char* d,
size_t l) ->
bool
4320 while (offset < end_offset && !is_shutting_down())
4324 error = Error::Write;
4327 else if (!content_provider(offset, end_offset - offset, data_sink))
4329 error = Error::Canceled;
4334 error = Error::Write;
4339 error = Error::Success;
4343 template <
typename T>
4345 size_t length,
const T& is_shutting_down)
4347 auto error = Error::Success;
4348 return write_content(strm, content_provider, offset, length, is_shutting_down, error);
4351 template <
typename T>
4353 const T& is_shutting_down)
4356 auto data_available =
true;
4360 data_sink.
write = [&](
const char* d,
size_t l) ->
bool
4373 data_sink.
done = [&](
void) { data_available =
false; };
4375 while (data_available && !is_shutting_down())
4381 else if (!content_provider(offset, 0, data_sink))
4393 template <
typename T,
typename U>
4398 auto data_available =
true;
4402 data_sink.
write = [&](
const char* d,
size_t l) ->
bool
4406 data_available = l > 0;
4409 std::string payload;
4411 [&](
const char* data,
size_t data_len)
4413 payload.append(data, data_len);
4417 if (!payload.empty())
4420 auto chunk =
from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4435 auto done_with_trailer = [&](
const Headers* trailer)
4442 data_available =
false;
4444 std::string payload;
4446 [&](
const char* data,
size_t data_len)
4448 payload.append(data, data_len);
4456 if (!payload.empty())
4459 auto chunk =
from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4467 static const std::string done_marker(
"0\r\n");
4468 if (!
write_data(strm, done_marker.data(), done_marker.size()))
4476 for (
const auto& kv : *trailer)
4478 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
4479 if (!
write_data(strm, field_line.data(), field_line.size()))
4486 static const std::string crlf(
"\r\n");
4487 if (!
write_data(strm, crlf.data(), crlf.size()))
4493 data_sink.
done = [&](
void) { done_with_trailer(
nullptr); };
4497 while (data_available && !is_shutting_down())
4501 error = Error::Write;
4504 else if (!content_provider(offset, 0, data_sink))
4506 error = Error::Canceled;
4511 error = Error::Write;
4516 error = Error::Success;
4520 template <
typename T,
typename U>
4524 auto error = Error::Success;
4528 template <
typename T>
4530 const std::string& location,
Error& error)
4533 new_req.
path = path;
4539 new_req.
body.clear();
4545 auto ret = cli.send(new_req, new_res, error);
4561 for (
auto it = params.begin(); it != params.end(); ++it)
4563 if (it != params.begin())
4576 std::set<std::string> cache;
4577 split(s.data(), s.data() + s.size(),
'&',
4578 [&](
const char* b,
const char*
e)
4580 std::string kv(b, e);
4581 if (cache.find(kv) != cache.end())
4590 [&](
const char* b2,
const char* e2)
4604 params.emplace(decode_url(key, true), decode_url(val, true));
4611 auto boundary_keyword =
"boundary=";
4612 auto pos = content_type.find(boundary_keyword);
4613 if (pos == std::string::npos)
4617 auto end = content_type.find(
';', pos);
4618 auto beg = pos + strlen(boundary_keyword);
4620 return !boundary.empty();
4625 std::set<std::string> cache;
4626 split(s.data(), s.data() + s.size(),
';',
4627 [&](
const char* b,
const char*
e)
4629 std::string kv(b, e);
4630 if (cache.find(kv) != cache.end())
4639 [&](
const char* b2,
const char* e2)
4653 params.emplace(trim_double_quotes_copy((key)), trim_double_quotes_copy((val)));
4658 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
4666 static auto re_first_range = std::regex(R
"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
4668 if (std::regex_match(s, m, re_first_range))
4670 auto pos =
static_cast<size_t>(m.position(1));
4671 auto len =
static_cast<size_t>(m.length(1));
4672 auto all_valid_ranges =
true;
4673 split(&s[pos], &s[pos + len],
',',
4674 [&](
const char* b,
const char*
e)
4676 if (!all_valid_ranges)
4678 static auto re_another_range = std::regex(R
"(\s*(\d*)-(\d*))");
4680 if (std::regex_match(b,
e, cm, re_another_range))
4683 if (!cm.str(1).empty())
4685 first =
static_cast<ssize_t
>(std::stoll(cm.str(1)));
4689 if (!cm.str(2).empty())
4691 last =
static_cast<ssize_t
>(std::stoll(cm.str(2)));
4694 if (first != -1 && last != -1 && first > last)
4696 all_valid_ranges =
false;
4699 ranges.emplace_back(std::make_pair(first, last));
4702 return all_valid_ranges;
4705 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
4722 boundary_ = boundary;
4723 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4724 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4734 while (buf_size() > 0)
4740 buf_erase(buf_find(dash_boundary_crlf_));
4741 if (dash_boundary_crlf_.size() > buf_size())
4745 if (!buf_start_with(dash_boundary_crlf_))
4749 buf_erase(dash_boundary_crlf_.size());
4761 auto pos = buf_find(crlf_);
4766 while (pos < buf_size())
4771 if (!header_callback(file_))
4776 buf_erase(crlf_.size());
4781 static const std::string header_name =
"content-type:";
4782 const auto header = buf_head(pos);
4783 if (start_with_case_ignore(header, header_name))
4785 file_.content_type =
trim_copy(header.substr(header_name.size()));
4789 static const std::regex re_content_disposition(
4790 R
"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4791 std::regex_constants::icase);
4794 if (std::regex_match(header, m, re_content_disposition))
4799 auto it = params.find(
"name");
4800 if (it != params.end())
4802 file_.name = it->second;
4810 it = params.find(
"filename");
4811 if (it != params.end())
4813 file_.filename = it->second;
4816 it = params.find(
"filename*");
4817 if (it != params.end())
4820 static const std::regex re_rfc5987_encoding(
4821 R
"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4824 if (std::regex_match(it->second, m2, re_rfc5987_encoding))
4841 buf_erase(pos + crlf_.size());
4842 pos = buf_find(crlf_);
4852 if (crlf_dash_boundary_.size() > buf_size())
4856 auto pos = buf_find(crlf_dash_boundary_);
4857 if (pos < buf_size())
4859 if (!content_callback(buf_data(), pos))
4864 buf_erase(pos + crlf_dash_boundary_.size());
4869 auto len = buf_size() - crlf_dash_boundary_.size();
4872 if (!content_callback(buf_data(), len))
4885 if (crlf_.size() > buf_size())
4889 if (buf_start_with(crlf_))
4891 buf_erase(crlf_.size());
4896 if (dash_.size() > buf_size())
4900 if (buf_start_with(dash_))
4902 buf_erase(dash_.size());
4904 buf_erase(buf_size());
4920 void clear_file_info()
4923 file_.filename.clear();
4924 file_.content_type.clear();
4927 bool start_with_case_ignore(
const std::string& a,
const std::string& b)
const
4929 if (a.size() < b.size())
4933 for (
size_t i = 0; i < b.size(); i++)
4935 if (::tolower(a[i]) != ::tolower(b[i]))
4943 const std::string dash_ =
"--";
4944 const std::string crlf_ =
"\r\n";
4945 std::string boundary_;
4946 std::string dash_boundary_crlf_;
4947 std::string crlf_dash_boundary_;
4950 bool is_valid_ =
false;
4951 MultipartFormData file_;
4954 bool start_with(
const std::string& a,
size_t spos,
size_t epos,
const std::string& b)
const
4956 if (epos - spos < b.size())
4960 for (
size_t i = 0; i < b.size(); i++)
4962 if (a[i + spos] != b[i])
4970 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
4972 const char* buf_data()
const {
return &buf_[buf_spos_]; }
4974 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_, l); }
4976 bool buf_start_with(
const std::string& s)
const
4978 return start_with(buf_, buf_spos_, buf_epos_, s);
4981 size_t buf_find(
const std::string& s)
const
4985 size_t off = buf_spos_;
4986 while (off < buf_epos_)
4991 if (pos == buf_epos_)
5002 auto remaining_size = buf_epos_ - pos;
5003 if (s.size() > remaining_size)
5008 if (start_with(buf_, pos, buf_epos_, s))
5010 return pos - buf_spos_;
5019 void buf_append(
const char* data,
size_t n)
5021 auto remaining_size = buf_size();
5022 if (remaining_size > 0 && buf_spos_ > 0)
5024 for (
size_t i = 0; i < remaining_size; i++) { buf_[i] = buf_[buf_spos_ + i]; }
5027 buf_epos_ = remaining_size;
5029 if (remaining_size + n > buf_.size())
5031 buf_.resize(remaining_size + n);
5034 for (
size_t i = 0; i < n; i++) { buf_[buf_epos_ + i] = data[i]; }
5038 void buf_erase(
size_t size) { buf_spos_ += size; }
5041 size_t buf_spos_ = 0;
5042 size_t buf_epos_ = 0;
5045 inline std::string
to_lower(
const char* beg,
const char* end)
5051 out +=
static_cast<char>(::tolower(*it));
5059 static const char data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5064 std::random_device seed_gen;
5067 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
5068 std::mt19937 engine(seed_sequence);
5070 std::string result =
"--cpp-httplib-multipart-data-";
5072 for (
auto i = 0; i < 16; i++) { result += data[engine() % (
sizeof(data) - 1)]; }
5080 for (
size_t i = 0; i < boundary.size(); i++)
5082 auto c = boundary[i];
5083 if (!std::isalnum(c) && c !=
'-' && c !=
'_')
5092 template <
typename T>
5094 const std::string& boundary)
5096 std::string body =
"--" + boundary +
"\r\n";
5097 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
5098 if (!item.filename.empty())
5100 body +=
"; filename=\"" + item.filename +
"\"";
5103 if (!item.content_type.empty())
5105 body +=
"Content-Type: " + item.content_type +
"\r\n";
5116 return "--" + boundary +
"--\r\n";
5121 return "multipart/form-data; boundary=" + boundary;
5125 const std::string& boundary,
bool finish =
true)
5129 for (
const auto& item : items)
5142 size_t content_length,
size_t index)
5144 auto r = req.
ranges[index];
5146 if (r.first == -1 && r.second == -1)
5148 return std::make_pair(0, content_length);
5151 auto slen =
static_cast<ssize_t
>(content_length);
5155 r.first = (std::max)(
static_cast<ssize_t
>(0), slen - r.second);
5156 r.second = slen - 1;
5161 r.second = slen - 1;
5163 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
5167 size_t content_length)
5169 std::string
field =
"bytes ";
5170 if (range.first != -1)
5175 if (range.second != -1)
5184 template <
typename SToken,
typename CToken,
typename Content>
5186 const std::string& content_type, SToken stoken, CToken ctoken,
5189 for (
size_t i = 0; i < req.
ranges.size(); i++)
5194 if (!content_type.empty())
5196 ctoken(
"Content-Type: ");
5197 stoken(content_type);
5201 ctoken(
"Content-Range: ");
5202 const auto& range = req.
ranges[i];
5208 auto offset = offsets.first;
5209 auto length = offsets.second;
5210 if (!content(offset, length))
5225 const std::string& boundary,
const std::string& content_type,
5229 req, res, boundary, content_type, [&](
const std::string& token) { data += token; },
5230 [&](
const std::string& token) { data += token; },
5231 [&](
size_t offset,
size_t length)
5233 if (offset < res.
body.size())
5235 data += res.
body.substr(offset, length);
5243 const std::string& boundary,
5244 const std::string& content_type)
5246 size_t data_length = 0;
5249 req, res, boundary, content_type,
5250 [&](
const std::string& token) { data_length += token.size(); },
5251 [&](
const std::string& token) { data_length += token.size(); },
5252 [&](
size_t ,
size_t length)
5254 data_length += length;
5261 template <
typename T>
5263 const std::string& boundary,
5264 const std::string& content_type,
const T& is_shutting_down)
5267 req, res, boundary, content_type, [&](
const std::string& token) { strm.
write(token); },
5268 [&](
const std::string& token) { strm.
write(token); },
5269 [&](
size_t offset,
size_t length)
5276 auto r = req.
ranges[index];
5283 return std::make_pair(r.first, r.second - r.first + 1);
5302 if (*p ==
'\r' || *p ==
'\n')
5311 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5312 inline std::string message_digest(
const std::string& s,
const EVP_MD* algo)
5315 std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
5317 unsigned int hash_length = 0;
5318 unsigned char hash[EVP_MAX_MD_SIZE];
5320 EVP_DigestInit_ex(context.get(), algo,
nullptr);
5321 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
5322 EVP_DigestFinal_ex(context.get(),
hash, &hash_length);
5324 std::stringstream ss;
5325 for (
auto i = 0u; i < hash_length; ++i)
5327 ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(
hash[i]);
5333 inline std::string MD5(
const std::string& s) {
return message_digest(s, EVP_md5()); }
5335 inline std::string SHA_256(
const std::string& s) {
return message_digest(s, EVP_sha256()); }
5337 inline std::string SHA_512(
const std::string& s) {
return message_digest(s, EVP_sha512()); }
5340 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5344 inline bool load_system_certs_on_windows(X509_STORE* store)
5346 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L
"ROOT");
5352 auto result =
false;
5353 PCCERT_CONTEXT pContext = NULL;
5354 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr)
5356 auto encoded_cert =
static_cast<const unsigned char*
>(pContext->pbCertEncoded);
5358 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5361 X509_STORE_add_cert(store, x509);
5367 CertFreeCertificateContext(pContext);
5368 CertCloseStore(hStore, 0);
5372 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5374 template <
typename T>
5377 inline void cf_object_ptr_deleter(CFTypeRef obj)
5385 inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef>& certs)
5387 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
5388 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, kCFBooleanTrue};
5390 CFObjectPtr<CFDictionaryRef> query(
5391 CFDictionaryCreate(
nullptr,
reinterpret_cast<const void**
>(keys), values,
5392 sizeof(keys) /
sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks,
5393 &kCFTypeDictionaryValueCallBacks),
5394 cf_object_ptr_deleter);
5401 CFTypeRef security_items =
nullptr;
5402 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
5403 CFArrayGetTypeID() != CFGetTypeID(security_items))
5408 certs.reset(
reinterpret_cast<CFArrayRef
>(security_items));
5412 inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef>& certs)
5414 CFArrayRef root_security_items =
nullptr;
5415 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess)
5420 certs.reset(root_security_items);
5424 inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE* store)
5426 auto result =
false;
5427 for (
auto i = 0; i < CFArrayGetCount(certs); ++i)
5430 reinterpret_cast<const __SecCertificate*
>(CFArrayGetValueAtIndex(certs, i));
5432 if (SecCertificateGetTypeID() != CFGetTypeID(cert))
5437 CFDataRef cert_data =
nullptr;
5438 if (SecItemExport(cert, kSecFormatX509Cert, 0,
nullptr, &cert_data) != errSecSuccess)
5443 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
5446 static_cast<const unsigned char*
>(CFDataGetBytePtr(cert_data_ptr.get()));
5448 auto x509 = d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
5452 X509_STORE_add_cert(store, x509);
5461 inline bool load_system_certs_on_macos(X509_STORE* store)
5463 auto result =
false;
5464 CFObjectPtr<CFArrayRef> certs(
nullptr, cf_object_ptr_deleter);
5465 if (retrieve_certs_from_keychain(certs) && certs)
5467 result = add_certs_to_x509_store(certs.get(), store);
5470 if (retrieve_root_certs_from_keychain(certs) && certs)
5472 result = add_certs_to_x509_store(certs.get(), store) || result;
5477 #endif // TARGET_OS_OSX
5479 #endif // CPPHTTPLIB_OPENSSL_SUPPORT
5488 if (WSAStartup(0x0002, &wsaData) == 0)
5498 bool is_valid_ =
false;
5501 static WSInit wsinit_;
5504 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5505 inline std::pair<std::string, std::string> make_digest_authentication_header(
5506 const Request& req,
const std::map<std::string, std::string>& auth,
size_t cnonce_count,
5507 const std::string& cnonce,
const std::string& username,
const std::string& password,
5508 bool is_proxy =
false)
5512 std::stringstream ss;
5513 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
5518 if (auth.find(
"qop") != auth.end())
5520 qop = auth.at(
"qop");
5521 if (qop.find(
"auth-int") != std::string::npos)
5525 else if (qop.find(
"auth") != std::string::npos)
5535 std::string algo =
"MD5";
5536 if (auth.find(
"algorithm") != auth.end())
5538 algo = auth.at(
"algorithm");
5541 std::string response;
5543 auto H = algo ==
"SHA-256" ? detail::SHA_256
5544 : algo ==
"SHA-512" ? detail::SHA_512
5547 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
5549 auto A2 = req.method +
":" + req.path;
5550 if (qop ==
"auth-int")
5552 A2 +=
":" + H(req.body);
5557 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
5561 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
":" + qop +
5566 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
5569 "Digest username=\"" + username +
"\", realm=\"" + auth.at(
"realm") +
"\", nonce=\"" +
5570 auth.at(
"nonce") +
"\", uri=\"" + req.path +
"\", algorithm=" + algo +
5571 (qop.empty() ?
", response=\""
5572 :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" + cnonce +
"\", response=\"") +
5573 response +
"\"" + (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
5575 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5576 return std::make_pair(key,
field);
5583 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
5586 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5588 auto pos = s.find(
' ');
5589 if (pos != std::string::npos)
5591 auto type = s.substr(0, pos);
5592 if (
type ==
"Basic")
5596 else if (
type ==
"Digest")
5598 s = s.substr(pos + 1);
5599 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
5600 for (
auto i = beg; i != std::sregex_iterator(); ++i)
5603 auto key = s.substr(
static_cast<size_t>(m.position(1)),
5604 static_cast<size_t>(m.length(1)));
5605 auto val = m.length(2) > 0 ? s.substr(
static_cast<size_t>(m.position(2)),
5606 static_cast<size_t>(m.length(2)))
5607 : s.substr(
static_cast<size_t>(m.position(3)),
5608 static_cast<size_t>(m.length(3)));
5621 auto randchar = []() ->
char
5623 const char charset[] =
"0123456789"
5624 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
5625 "abcdefghijklmnopqrstuvwxyz";
5626 const size_t max_index = (
sizeof(charset) - 1);
5627 return charset[
static_cast<size_t>(std::rand()) % max_index];
5629 std::string
str(length, 0);
5630 std::generate_n(
str.begin(), length, randchar);
5638 : content_provider_(content_provider)
5644 return content_provider_(offset, sink);
5655 std::vector<std::string> addrs;
5659 return std::string();
5666 struct addrinfo hints;
5667 struct addrinfo* result;
5669 memset(&hints, 0,
sizeof(
struct addrinfo));
5670 hints.ai_family = AF_UNSPEC;
5671 hints.ai_socktype = SOCK_STREAM;
5672 hints.ai_protocol = 0;
5674 if (getaddrinfo(
hostname.c_str(),
nullptr, &hints, &result))
5676 #if defined __linux__ && !defined __ANDROID__
5682 for (
auto rp = result; rp; rp = rp->ai_next)
5684 const auto& addr = *
reinterpret_cast<struct sockaddr_storage*
>(rp->ai_addr);
5689 addrs.push_back(ip);
5693 freeaddrinfo(result);
5698 std::string path_with_query = path;
5699 const static std::regex re(
"[^?]+\\?.*");
5700 auto delm = std::regex_match(path, re) ?
'&' :
'?';
5702 return path_with_query;
5708 std::string
field =
"bytes=";
5710 for (
auto r : ranges)
5727 return std::make_pair(
"Range", std::move(
field));
5730 inline std::pair<std::string, std::string>
5735 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5736 return std::make_pair(key, std::move(
field));
5739 inline std::pair<std::string, std::string>
5742 auto field =
"Bearer " + token;
5743 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5744 return std::make_pair(key, std::move(
field));
5758 inline size_t Request::get_header_value_count(
const std::string& key)
const
5760 auto r = headers.equal_range(key);
5761 return static_cast<size_t>(std::distance(r.first, r.second));
5764 inline void Request::set_header(
const std::string& key,
const std::string& val)
5768 headers.emplace(key, val);
5772 inline bool Request::has_param(
const std::string& key)
const
5774 return params.find(key) != params.end();
5777 inline std::string Request::get_param_value(
const std::string& key,
size_t id)
const
5779 auto rng = params.equal_range(key);
5780 auto it = rng.first;
5781 std::advance(it,
static_cast<ssize_t
>(
id));
5782 if (it != rng.second)
5786 return std::string();
5789 inline size_t Request::get_param_value_count(
const std::string& key)
const
5791 auto r = params.equal_range(key);
5792 return static_cast<size_t>(std::distance(r.first, r.second));
5795 inline bool Request::is_multipart_form_data()
const
5798 return !content_type.rfind(
"multipart/form-data", 0);
5801 inline bool Request::has_file(
const std::string& key)
const
5803 return files.find(key) != files.end();
5808 auto it = files.find(key);
5809 if (it != files.end())
5816 inline std::vector<MultipartFormData> Request::get_file_values(
const std::string& key)
const
5818 std::vector<MultipartFormData> values;
5819 auto rng = files.equal_range(key);
5820 for (
auto it = rng.first; it != rng.second; it++) { values.push_back(it->second); }
5827 return headers.find(key) != headers.end();
5835 inline size_t Response::get_header_value_count(
const std::string& key)
const
5837 auto r = headers.equal_range(key);
5838 return static_cast<size_t>(std::distance(r.first, r.second));
5841 inline void Response::set_header(
const std::string& key,
const std::string& val)
5845 headers.emplace(key, val);
5849 inline void Response::set_redirect(
const std::string& url,
int stat)
5853 set_header(
"Location", url);
5854 if (300 <= stat && stat < 400)
5865 inline void Response::set_content(
const char* s,
size_t n,
const std::string& content_type)
5869 auto rng = headers.equal_range(
"Content-Type");
5870 headers.erase(rng.first, rng.second);
5871 set_header(
"Content-Type", content_type);
5874 inline void Response::set_content(
const std::string& s,
const std::string& content_type)
5876 set_content(s.data(), s.size(), content_type);
5879 inline void Response::set_content_provider(
size_t in_length,
const std::string& content_type,
5883 set_header(
"Content-Type", content_type);
5884 content_length_ = in_length;
5887 content_provider_ = std::move(provider);
5889 content_provider_resource_releaser_ = resource_releaser;
5890 is_chunked_content_provider_ =
false;
5893 inline void Response::set_content_provider(
const std::string& content_type,
5897 set_header(
"Content-Type", content_type);
5898 content_length_ = 0;
5900 content_provider_resource_releaser_ = resource_releaser;
5901 is_chunked_content_provider_ =
false;
5905 Response::set_chunked_content_provider(
const std::string& content_type,
5909 set_header(
"Content-Type", content_type);
5910 content_length_ = 0;
5912 content_provider_resource_releaser_ = resource_releaser;
5913 is_chunked_content_provider_ =
true;
5917 inline bool Result::has_request_header(
const std::string& key)
const
5919 return request_headers_.find(key) != request_headers_.end();
5922 inline std::string Result::get_request_header_value(
const std::string& key,
size_t id)
const
5927 inline size_t Result::get_request_header_value_count(
const std::string& key)
const
5929 auto r = request_headers_.equal_range(key);
5930 return static_cast<size_t>(std::distance(r.first, r.second));
5934 inline ssize_t Stream::write(
const char* ptr) {
return write(ptr, strlen(ptr)); }
5936 inline ssize_t Stream::write(
const std::string& s) {
return write(s.data(), s.size()); }
5942 inline SocketStream::SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5943 time_t write_timeout_sec, time_t write_timeout_usec)
5945 read_timeout_sec_(read_timeout_sec),
5946 read_timeout_usec_(read_timeout_usec),
5947 write_timeout_sec_(write_timeout_sec),
5948 write_timeout_usec_(write_timeout_usec),
5949 read_buff_(read_buff_size_, 0)
5957 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
5962 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
5969 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5971 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
5974 if (read_buff_off_ < read_buff_content_size_)
5976 auto remaining_size = read_buff_content_size_ - read_buff_off_;
5977 if (size <= remaining_size)
5979 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
5980 read_buff_off_ += size;
5981 return static_cast<ssize_t
>(size);
5985 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
5986 read_buff_off_ += remaining_size;
5987 return static_cast<ssize_t
>(remaining_size);
5997 read_buff_content_size_ = 0;
5999 if (size < read_buff_size_)
6006 else if (n <=
static_cast<ssize_t
>(size))
6008 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
6013 memcpy(ptr, read_buff_.data(), size);
6014 read_buff_off_ = size;
6015 read_buff_content_size_ =
static_cast<size_t>(n);
6016 return static_cast<ssize_t
>(size);
6032 #if defined(_WIN32) && !defined(_WIN64)
6033 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
6058 #if defined(_MSC_VER) && _MSC_VER < 1910
6059 auto len_read = buffer._Copy_s(ptr, size, size, position);
6061 auto len_read = buffer.copy(ptr, size, position);
6063 position +=
static_cast<size_t>(len_read);
6064 return static_cast<ssize_t
>(len_read);
6069 buffer.append(ptr, size);
6070 return static_cast<ssize_t
>(size);
6084 std::size_t last_param_end = 0;
6086 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
6091 std::unordered_set<std::string> param_name_set;
6096 const auto marker_pos = pattern.find(marker, last_param_end);
6097 if (marker_pos == std::string::npos)
6102 static_fragments_.push_back(pattern.substr(last_param_end, marker_pos - last_param_end));
6104 const auto param_name_start = marker_pos + 1;
6106 auto sep_pos = pattern.find(separator, param_name_start);
6107 if (sep_pos == std::string::npos)
6109 sep_pos = pattern.length();
6112 auto param_name = pattern.substr(param_name_start, sep_pos - param_name_start);
6114 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
6115 if (param_name_set.find(param_name) != param_name_set.cend())
6117 std::string
msg =
"Encountered path parameter '" + param_name +
6118 "' multiple times in route pattern '" + pattern +
"'.";
6119 throw std::invalid_argument(
msg);
6123 param_names_.push_back(std::move(param_name));
6125 last_param_end = sep_pos + 1;
6128 if (last_param_end < pattern.length())
6130 static_fragments_.push_back(pattern.substr(last_param_end));
6136 request.
matches = std::smatch();
6141 std::size_t starting_pos = 0;
6142 for (
size_t i = 0; i < static_fragments_.size(); ++i)
6144 const auto& fragment = static_fragments_[i];
6146 if (starting_pos + fragment.length() > request.
path.length())
6153 if (std::strncmp(request.
path.c_str() + starting_pos, fragment.c_str(),
6154 fragment.length()) != 0)
6159 starting_pos += fragment.length();
6164 if (i >= param_names_.size())
6169 auto sep_pos = request.
path.find(separator, starting_pos);
6170 if (sep_pos == std::string::npos)
6172 sep_pos = request.
path.length();
6175 const auto& param_name = param_names_[i];
6178 request.
path.substr(starting_pos, sep_pos - starting_pos));
6181 starting_pos = sep_pos + 1;
6184 return starting_pos >= request.
path.length();
6190 return std::regex_match(request.
path, request.
matches, regex_);
6200 signal(SIGPIPE, SIG_IGN);
6206 inline std::unique_ptr<detail::MatcherBase> Server::make_matcher(
const std::string& pattern)
6208 if (pattern.find(
"/:") != std::string::npos)
6210 return detail::make_unique<detail::PathParamsMatcher>(pattern);
6214 return detail::make_unique<detail::RegexMatcher>(pattern);
6220 get_handlers_.push_back(std::make_pair(make_matcher(pattern), std::move(handler)));
6226 post_handlers_.push_back(std::make_pair(make_matcher(pattern), std::move(handler)));
6232 post_handlers_for_content_reader_.push_back(
6233 std::make_pair(make_matcher(pattern), std::move(handler)));
6239 put_handlers_.push_back(std::make_pair(make_matcher(pattern), std::move(handler)));
6245 put_handlers_for_content_reader_.push_back(
6246 std::make_pair(make_matcher(pattern), std::move(handler)));
6252 patch_handlers_.push_back(std::make_pair(make_matcher(pattern), std::move(handler)));
6258 patch_handlers_for_content_reader_.push_back(
6259 std::make_pair(make_matcher(pattern), std::move(handler)));
6265 delete_handlers_.push_back(std::make_pair(make_matcher(pattern), std::move(handler)));
6271 delete_handlers_for_content_reader_.push_back(
6272 std::make_pair(make_matcher(pattern), std::move(handler)));
6278 options_handlers_.push_back(std::make_pair(make_matcher(pattern), std::move(handler)));
6292 std::string mnt = !mount_point.empty() ? mount_point :
"/";
6293 if (!mnt.empty() && mnt[0] ==
'/')
6295 base_dirs_.push_back({mnt, dir, std::move(headers)});
6304 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it)
6306 if (it->mount_point == mount_point)
6308 base_dirs_.erase(it);
6316 const std::string& mime)
6318 file_extension_and_mimetype_map_[ext] = mime;
6324 default_file_mimetype_ = mime;
6330 file_request_handler_ = std::move(handler);
6336 error_handler_ = std::move(handler);
6352 exception_handler_ = std::move(handler);
6358 pre_routing_handler_ = std::move(handler);
6364 post_routing_handler_ = std::move(handler);
6370 logger_ = std::move(logger);
6376 expect_100_continue_handler_ = std::move(handler);
6382 address_family_ = family;
6394 socket_options_ = std::move(socket_options);
6400 default_headers_ = std::move(headers);
6445 if (bind_internal(host, port, socket_flags) < 0)
6451 return bind_internal(host, 0, socket_flags);
6457 return listen_internal();
6463 return bind_to_port(host, port, socket_flags) && listen_internal();
6470 while (!
is_running() && !done_) { std::this_thread::sleep_for(std::chrono::milliseconds{1}); }
6484 inline bool Server::parse_request_line(
const char* s,
Request& req)
6486 auto len = strlen(s);
6487 if (len < 2 || s[len - 2] !=
'\r' || s[len - 1] !=
'\n')
6497 [&](
const char* b,
const char*
e)
6501 case 0: req.
method = std::string(b,
e);
break;
6502 case 1: req.
target = std::string(b,
e);
break;
6503 case 2: req.
version = std::string(b,
e);
break;
6515 static const std::set<std::string> methods{
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
6516 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
6518 if (methods.find(req.
method) == methods.end())
6530 for (
size_t i = 0; i < req.
target.size(); i++)
6532 if (req.
target[i] ==
'#')
6542 [&](
const char* b,
const char*
e)
6547 req.path = detail::decode_url(std::string(b, e), false);
6553 detail::parse_query_text(std::string(b, e), req.params);
6571 inline bool Server::write_response(Stream& strm,
bool close_connection,
const Request& req,
6574 return write_response_core(strm, close_connection, req, res,
false);
6577 inline bool Server::write_response_with_content(Stream& strm,
bool close_connection,
6578 const Request& req, Response& res)
6580 return write_response_core(strm, close_connection, req, res,
true);
6583 inline bool Server::write_response_core(Stream& strm,
bool close_connection,
const Request& req,
6584 Response& res,
bool need_apply_ranges)
6586 assert(res.status != -1);
6590 need_apply_ranges =
true;
6593 std::string content_type;
6594 std::string boundary;
6595 if (need_apply_ranges)
6597 apply_ranges(req, res, content_type, boundary);
6601 if (close_connection || req.get_header_value(
"Connection") ==
"close")
6603 res.set_header(
"Connection",
"close");
6607 std::stringstream ss;
6609 res.set_header(
"Keep-Alive", ss.str());
6612 if (!res.has_header(
"Content-Type") &&
6613 (!res.body.empty() || res.content_length_ > 0 || res.content_provider_))
6615 res.set_header(
"Content-Type",
"text/plain");
6618 if (!res.has_header(
"Content-Length") && res.body.empty() && !res.content_length_ &&
6619 !res.content_provider_)
6621 res.set_header(
"Content-Length",
"0");
6624 if (!res.has_header(
"Accept-Ranges") && req.method ==
"HEAD")
6626 res.set_header(
"Accept-Ranges",
"bytes");
6629 if (post_routing_handler_)
6631 post_routing_handler_(req, res);
6636 detail::BufferStream bstrm;
6638 if (!bstrm.write_format(
"HTTP/1.1 %d %s\r\n", res.status,
status_message(res.status)))
6649 auto& data = bstrm.get_buffer();
6655 if (req.method !=
"HEAD")
6657 if (!res.body.empty())
6664 else if (res.content_provider_)
6666 if (write_content_with_provider(strm, req, res, boundary, content_type))
6668 res.content_provider_success_ =
true;
6672 res.content_provider_success_ =
false;
6687 inline bool Server::write_content_with_provider(Stream& strm,
const Request& req, Response& res,
6688 const std::string& boundary,
6689 const std::string& content_type)
6693 if (res.content_length_ > 0)
6695 if (req.ranges.empty())
6700 else if (req.ranges.size() == 1)
6703 auto offset = offsets.first;
6704 auto length = offsets.second;
6716 if (res.is_chunked_content_provider_)
6720 std::unique_ptr<detail::compressor> compressor;
6723 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
6724 compressor = detail::make_unique<detail::gzip_compressor>();
6729 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
6730 compressor = detail::make_unique<detail::brotli_compressor>();
6735 compressor = detail::make_unique<detail::nocompressor>();
6737 assert(compressor !=
nullptr);
6750 inline bool Server::read_content(Stream& strm,
Request& req, Response& res)
6752 MultipartFormDataMap::iterator cur;
6753 auto file_count = 0;
6754 if (read_content_core(
6757 [&](
const char* buf,
size_t n)
6759 if (req.body.size() + n > req.body.max_size())
6763 req.body.append(buf, n);
6767 [&](
const MultipartFormData& file)
6773 cur = req.files.emplace(file.name, file);
6776 [&](
const char* buf,
size_t n)
6778 auto& content = cur->second.content;
6779 if (content.size() + n > content.max_size())
6783 content.append(buf, n);
6787 const auto& content_type = req.get_header_value(
"Content-Type");
6788 if (!content_type.find(
"application/x-www-form-urlencoded"))
6802 inline bool Server::read_content_with_content_receiver(Stream& strm,
Request& req, Response& res,
6807 return read_content_core(strm, req, res, std::move(receiver), std::move(multipart_header),
6808 std::move(multipart_receiver));
6811 inline bool Server::read_content_core(Stream& strm,
Request& req, Response& res,
6816 detail::MultipartFormDataParser multipart_form_data_parser;
6819 if (req.is_multipart_form_data())
6821 const auto& content_type = req.get_header_value(
"Content-Type");
6822 std::string boundary;
6829 multipart_form_data_parser.set_boundary(std::move(boundary));
6830 out = [&](
const char* buf,
size_t n, uint64_t , uint64_t )
6843 return multipart_form_data_parser.parse(buf, n, multipart_receiver, multipart_header);
6848 out = [receiver](
const char* buf,
size_t n, uint64_t , uint64_t )
6849 {
return receiver(buf, n); };
6852 if (req.method ==
"DELETE" && !req.has_header(
"Content-Length"))
6862 if (req.is_multipart_form_data())
6864 if (!multipart_form_data_parser.is_valid())
6874 inline bool Server::handle_file_request(
const Request& req, Response& res,
bool head)
6876 for (
const auto& entry : base_dirs_)
6879 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point))
6881 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
6884 auto path = entry.base_dir + sub_path;
6885 if (path.back() ==
'/')
6887 path +=
"index.html";
6892 for (
const auto& kv : entry.headers)
6894 res.set_header(kv.first.c_str(), kv.second);
6897 auto mm = std::make_shared<detail::mmap>(path.c_str());
6903 res.set_content_provider(
6906 default_file_mimetype_),
6907 [mm](
size_t offset,
size_t length, DataSink& sink) ->
bool
6909 sink.write(mm->data() + offset, length);
6913 if (!head && file_request_handler_)
6915 file_request_handler_(req, res);
6926 inline socket_t Server::create_server_socket(
const std::string& host,
int port,
int socket_flags,
6930 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
6931 std::move(socket_options),
6932 [](
socket_t sock,
struct addrinfo& ai) ->
bool
6934 if (::
bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen)))
6946 inline int Server::bind_internal(
const std::string& host,
int port,
int socket_flags)
6953 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
6961 struct sockaddr_storage addr;
6962 socklen_t addr_len =
sizeof(addr);
6963 if (getsockname(
svr_sock_,
reinterpret_cast<struct sockaddr*
>(&addr), &addr_len) == -1)
6967 if (addr.ss_family == AF_INET)
6969 return ntohs(
reinterpret_cast<struct sockaddr_in*
>(&addr)->sin_port);
6971 else if (addr.ss_family == AF_INET6)
6973 return ntohs(
reinterpret_cast<struct sockaddr_in6*
>(&addr)->sin6_port);
6986 inline bool Server::listen_internal()
6990 auto se = detail::scope_exit([&]() { is_running_ =
false; });
7004 task_queue->on_idle();
7014 if (errno == EMFILE)
7018 std::this_thread::sleep_for(std::chrono::milliseconds(1));
7021 else if (errno == EINTR || errno == EAGAIN)
7041 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout),
7047 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const void*
>(&tv),
7055 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char*
>(&timeout),
7061 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const void*
>(&tv),
7066 task_queue->enqueue([
this, sock]() { process_and_close_socket(sock); });
7069 task_queue->shutdown();
7075 inline bool Server::routing(
Request& req, Response& res, Stream& strm)
7083 auto is_head_request = req.method ==
"HEAD";
7084 if ((req.method ==
"GET" || is_head_request) && handle_file_request(req, res, is_head_request))
7093 ContentReader reader(
7096 return read_content_with_content_receiver(strm, req, res, std::move(receiver),
7101 return read_content_with_content_receiver(
7102 strm, req, res,
nullptr, std::move(header), std::move(receiver));
7105 if (req.method ==
"POST")
7107 if (dispatch_request_for_content_reader(req, res, std::move(reader),
7108 post_handlers_for_content_reader_))
7113 else if (req.method ==
"PUT")
7115 if (dispatch_request_for_content_reader(req, res, std::move(reader),
7116 put_handlers_for_content_reader_))
7121 else if (req.method ==
"PATCH")
7123 if (dispatch_request_for_content_reader(req, res, std::move(reader),
7124 patch_handlers_for_content_reader_))
7129 else if (req.method ==
"DELETE")
7131 if (dispatch_request_for_content_reader(req, res, std::move(reader),
7132 delete_handlers_for_content_reader_))
7147 if (req.method ==
"GET" || req.method ==
"HEAD")
7149 return dispatch_request(req, res, get_handlers_);
7151 else if (req.method ==
"POST")
7153 return dispatch_request(req, res, post_handlers_);
7155 else if (req.method ==
"PUT")
7157 return dispatch_request(req, res, put_handlers_);
7159 else if (req.method ==
"DELETE")
7161 return dispatch_request(req, res, delete_handlers_);
7163 else if (req.method ==
"OPTIONS")
7165 return dispatch_request(req, res, options_handlers_);
7167 else if (req.method ==
"PATCH")
7169 return dispatch_request(req, res, patch_handlers_);
7176 inline bool Server::dispatch_request(
Request& req, Response& res,
const Handlers& handlers)
7178 for (
const auto& x : handlers)
7180 const auto& matcher = x.first;
7181 const auto& handler = x.second;
7183 if (matcher->match(req))
7192 inline void Server::apply_ranges(
const Request& req, Response& res, std::string& content_type,
7193 std::string& boundary)
7195 if (req.ranges.size() > 1)
7199 auto it = res.headers.find(
"Content-Type");
7200 if (it != res.headers.end())
7202 content_type = it->second;
7203 res.headers.erase(it);
7206 res.set_header(
"Content-Type",
"multipart/byteranges; boundary=" + boundary);
7211 if (res.body.empty())
7213 if (res.content_length_ > 0)
7216 if (req.ranges.empty())
7218 length = res.content_length_;
7220 else if (req.ranges.size() == 1)
7223 length = offsets.second;
7225 auto content_range =
7227 res.set_header(
"Content-Range", content_range);
7237 if (res.content_provider_)
7239 if (res.is_chunked_content_provider_)
7241 res.set_header(
"Transfer-Encoding",
"chunked");
7244 res.set_header(
"Content-Encoding",
"gzip");
7248 res.set_header(
"Content-Encoding",
"br");
7256 if (req.ranges.empty())
7260 else if (req.ranges.size() == 1)
7262 auto content_range =
7264 res.set_header(
"Content-Range", content_range);
7267 auto offset = offsets.first;
7268 auto length = offsets.second;
7270 if (offset < res.body.size())
7272 res.body = res.body.substr(offset, length);
7285 res.body.swap(data);
7296 std::unique_ptr<detail::compressor> compressor;
7297 std::string content_encoding;
7301 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7302 compressor = detail::make_unique<detail::gzip_compressor>();
7303 content_encoding =
"gzip";
7308 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
7309 compressor = detail::make_unique<detail::brotli_compressor>();
7310 content_encoding =
"br";
7316 std::string compressed;
7317 if (compressor->compress(res.body.data(), res.body.size(),
true,
7318 [&](
const char* data,
size_t data_len)
7320 compressed.append(data, data_len);
7324 res.body.swap(compressed);
7325 res.set_header(
"Content-Encoding", content_encoding);
7331 res.set_header(
"Content-Length", length);
7335 inline bool Server::dispatch_request_for_content_reader(
Request& req, Response& res,
7336 ContentReader content_reader,
7337 const HandlersForContentReader& handlers)
7339 for (
const auto& x : handlers)
7341 const auto& matcher = x.first;
7342 const auto& handler = x.second;
7344 if (matcher->match(req))
7346 handler(req, res, content_reader);
7354 const std::function<
void(
Request&)>& setup_request)
7356 std::array<char, 2048> buf{};
7370 res.
headers = default_headers_;
7375 #ifndef CPPHTTPLIB_USE_POLL
7377 if (strm.
socket() >= FD_SETSIZE)
7382 return write_response(strm, close_connection, req, res);
7393 return write_response(strm, close_connection, req, res);
7400 return write_response(strm, close_connection, req, res);
7405 connection_closed =
true;
7410 connection_closed =
true;
7427 return write_response(strm, close_connection, req, res);
7439 if (expect_100_continue_handler_)
7441 status = expect_100_continue_handler_(req, res);
7449 default:
return write_response(strm, close_connection, req, res);
7454 auto routed =
false;
7455 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
7456 routed = routing(req, res, strm);
7460 routed = routing(req, res, strm);
7462 catch (std::exception&
e)
7464 if (exception_handler_)
7466 auto ep = std::current_exception();
7467 exception_handler_(req, res, ep);
7475 for (
size_t i = 0; s[i]; i++)
7479 case '\r': val +=
"\\r";
break;
7480 case '\n': val +=
"\\n";
break;
7481 default: val += s[i];
break;
7489 if (exception_handler_)
7491 auto ep = std::current_exception();
7492 exception_handler_(req, res, ep);
7509 return write_response_with_content(strm, close_connection, req, res);
7517 return write_response(strm, close_connection, req, res);
7523 inline bool Server::process_and_close_socket(
socket_t sock)
7528 [
this](
Stream& strm,
bool close_connection,
bool& connection_closed)
7529 {
return process_request(strm, close_connection, connection_closed,
nullptr); });
7538 :
ClientImpl(host, 80, std::string(), std::string())
7543 :
ClientImpl(host, port, std::string(), std::string())
7548 const std::string& client_cert_path,
7549 const std::string& client_key_path)
7552 host_and_port_(adjust_host_string(host) +
":" + std::
to_string(port)),
7553 client_cert_path_(client_cert_path),
7554 client_key_path_(client_key_path)
7579 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7580 digest_auth_username_ = rhs.digest_auth_username_;
7581 digest_auth_password_ = rhs.digest_auth_password_;
7597 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7598 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7599 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7601 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7602 ca_cert_file_path_ = rhs.ca_cert_file_path_;
7603 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7604 ca_cert_store_ = rhs.ca_cert_store_;
7606 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7607 server_certificate_verification_ = rhs.server_certificate_verification_;
7674 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7675 assert(
socket.ssl ==
nullptr);
7687 std::array<char, 2048> buf{};
7689 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7691 if (!line_reader.getline())
7696 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7697 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7699 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7703 if (!std::regex_match(line_reader.ptr(), m, re))
7705 return req.
method ==
"CONNECT";
7707 res.
version = std::string(m[1]);
7708 res.
status = std::stoi(std::string(m[2]));
7709 res.
reason = std::string(m[3]);
7712 while (res.
status == 100)
7714 if (!line_reader.getline())
7718 if (!line_reader.getline())
7723 if (!std::regex_match(line_reader.ptr(), m, re))
7727 res.
version = std::string(m[1]);
7728 res.
status = std::stoi(std::string(m[2]));
7729 res.
reason = std::string(m[3]);
7737 std::lock_guard<std::recursive_mutex> request_mutex_guard(
request_mutex_);
7738 auto ret = send_(req, res, error);
7742 ret = send_(req, res, error);
7756 auto is_alive =
false;
7766 const bool shutdown_gracefully =
false;
7780 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7784 auto& scli =
static_cast<SSLClient&
>(*this);
7787 auto success =
false;
7788 if (!scli.connect_with_proxy(
socket_, res, success, error))
7794 if (!scli.initialize_ssl(
socket_, error))
7824 auto se = detail::scope_exit(
7844 ret = process_socket(
socket_, [&](Stream& strm)
7845 {
return handle_request(strm, req, res, close_connection, error); });
7861 return send_(std::move(req2));
7866 auto res = detail::make_unique<Response>();
7868 auto ret =
send(req, *res, error);
7869 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
7872 inline bool ClientImpl::handle_request(Stream& strm,
Request& req, Response& res,
7873 bool close_connection,
Error& error)
7875 if (req.path.empty())
7881 auto req_save = req;
7891 req.path = req_save.path;
7903 if (res.get_header_value(
"Connection") ==
"close" ||
7904 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established"))
7925 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7926 if ((res.status == 401 || res.status == 407) && req.authorization_count_ < 5)
7928 auto is_proxy = res.status == 407;
7929 const auto& username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
7930 const auto& password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
7932 if (!username.empty() && !password.empty())
7934 std::map<std::string, std::string> auth;
7938 new_req.authorization_count_ += 1;
7939 new_req.headers.erase(is_proxy ?
"Proxy-Authorization" :
"Authorization");
7940 new_req.headers.insert(detail::make_digest_authentication_header(
7942 password, is_proxy));
7946 ret =
send(new_req, new_res, error);
7961 if (req.redirect_count_ == 0)
7967 auto location = res.get_header_value(
"location");
7968 if (location.empty())
7973 const static std::regex re(
7974 R
"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7977 if (!std::regex_match(location, m, re))
7982 auto scheme = is_ssl() ?
"https" :
"http";
7984 auto next_scheme = m[1].str();
7985 auto next_host = m[2].str();
7986 if (next_host.empty())
7988 next_host = m[3].str();
7990 auto port_str = m[4].str();
7991 auto next_path = m[5].str();
7992 auto next_query = m[6].str();
7994 auto next_port =
port_;
7995 if (!port_str.empty())
7997 next_port = std::stoi(port_str);
7999 else if (!next_scheme.empty())
8001 next_port = next_scheme ==
"https" ? 443 : 80;
8004 if (next_scheme.empty())
8008 if (next_host.empty())
8012 if (next_path.empty())
8025 if (next_scheme ==
"https")
8027 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8028 SSLClient cli(next_host.c_str(), next_port);
8029 cli.copy_settings(*
this);
8032 cli.set_ca_cert_store(ca_cert_store_);
8041 ClientImpl cli(next_host.c_str(), next_port);
8042 cli.copy_settings(*
this);
8050 auto is_shutting_down = []() {
return false; };
8055 std::unique_ptr<detail::compressor> compressor;
8056 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
8059 compressor = detail::make_unique<detail::gzip_compressor>();
8064 compressor = detail::make_unique<detail::nocompressor>();
8068 *compressor, error);
8073 is_shutting_down, error);
8077 inline bool ClientImpl::write_request(
Stream& strm,
Request& req,
bool close_connection,
8081 if (close_connection)
8120 #ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
8128 if (req.
body.empty())
8153 req.
set_header(
"Content-Type",
"text/plain");
8201 detail::BufferStream bstrm;
8204 bstrm.write_format(
"%s %s HTTP/1.1\r\n", req.
method.c_str(), path.c_str());
8209 auto& data = bstrm.get_buffer();
8218 if (req.
body.empty())
8232 inline std::unique_ptr<Response>
8233 ClientImpl::send_with_content_provider(
Request& req,
const char* body,
size_t content_length,
8236 const std::string& content_type,
Error& error)
8238 if (!content_type.empty())
8240 req.set_header(
"Content-Type", content_type);
8243 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
8246 req.set_header(
"Content-Encoding",
"gzip");
8250 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
8251 if (
compress_ && !content_provider_without_length)
8254 detail::gzip_compressor compressor;
8256 if (content_provider)
8262 data_sink.write = [&](
const char* data,
size_t data_len) ->
bool
8266 auto last = offset + data_len == content_length;
8268 auto ret = compressor.compress(
8269 data, data_len, last,
8270 [&](
const char* compressed_data,
size_t compressed_data_len)
8272 req.body.append(compressed_data, compressed_data_len);
8288 while (ok && offset < content_length)
8290 if (!content_provider(offset, content_length - offset, data_sink))
8299 if (!compressor.compress(body, content_length,
true,
8300 [&](
const char* data,
size_t data_len)
8302 req.body.append(data, data_len);
8314 if (content_provider)
8316 req.content_length_ = content_length;
8317 req.content_provider_ = std::move(content_provider);
8318 req.is_chunked_content_provider_ =
false;
8320 else if (content_provider_without_length)
8322 req.content_length_ = 0;
8323 req.content_provider_ =
8324 detail::ContentProviderAdapter(std::move(content_provider_without_length));
8325 req.is_chunked_content_provider_ =
true;
8326 req.set_header(
"Transfer-Encoding",
"chunked");
8330 req.body.assign(body, content_length);
8334 auto res = detail::make_unique<Response>();
8335 return send(req, *res, error) ? std::move(res) : nullptr;
8338 inline Result ClientImpl::send_with_content_provider(
8339 const std::string& method,
const std::string& path,
const Headers& headers,
const char* body,
8344 req.method = method;
8345 req.headers = headers;
8351 send_with_content_provider(req, body, content_length, std::move(content_provider),
8352 std::move(content_provider_without_length), content_type, error);
8354 return Result{std::move(res),
error, std::move(req.headers)};
8357 inline std::string ClientImpl::adjust_host_string(
const std::string&
host)
const
8359 if (
host.find(
':') != std::string::npos)
8361 return "[" +
host +
"]";
8367 bool close_connection,
Error& error)
8370 if (!write_request(strm, req, close_connection, error))
8375 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8379 if (!is_proxy_enabled)
8382 if (SSL_peek(
socket_.ssl, buf, 1) == 0 &&
8383 SSL_get_error(
socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN)
8415 [&](
const char* buf,
size_t n, uint64_t off, uint64_t len)
8429 [&](
const char* buf,
size_t n, uint64_t , uint64_t )
8431 if (res.
body.size() + n > res.
body.max_size())
8435 res.
body.append(buf, n);
8439 auto progress = [&](uint64_t current, uint64_t total)
8445 auto ret = req.
progress(current, total);
8455 std::move(progress), std::move(out),
decompress_))
8475 ClientImpl::get_multipart_content_provider(
const std::string& boundary,
8479 size_t cur_item = 0, cur_start = 0;
8482 return [&, cur_item, cur_start](
size_t offset,
DataSink& sink)
mutable ->
bool
8484 if (!offset && items.size())
8489 else if (cur_item < provider_items.size())
8494 provider_items[cur_item], boundary);
8495 offset += begin.size();
8501 auto has_data =
true;
8502 cur_sink.write = sink.write;
8503 cur_sink.done = [&]() { has_data =
false; };
8505 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
8525 inline bool ClientImpl::process_socket(
const Socket&
socket,
8526 std::function<
bool(Stream& strm)> callback)
8530 std::move(callback));
8533 inline bool ClientImpl::is_ssl()
const {
return false; }
8539 return Get(path,
Headers(), std::move(progress));
8553 req.
progress = std::move(progress);
8555 return send_(std::move(req));
8560 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
nullptr);
8566 return Get(path,
Headers(),
nullptr, std::move(content_receiver), std::move(progress));
8572 return Get(path, headers,
nullptr, std::move(content_receiver),
nullptr);
8578 return Get(path, headers,
nullptr, std::move(content_receiver), std::move(progress));
8584 return Get(path,
Headers(), std::move(response_handler), std::move(content_receiver),
nullptr);
8590 return Get(path, headers, std::move(response_handler), std::move(content_receiver),
nullptr);
8596 return Get(path,
Headers(), std::move(response_handler), std::move(content_receiver),
8597 std::move(progress));
8609 req.
content_receiver = [content_receiver](
const char* data,
size_t data_length,
8610 uint64_t , uint64_t )
8611 {
return content_receiver(data, data_length); };
8612 req.
progress = std::move(progress);
8614 return send_(std::move(req));
8622 return Get(path, headers);
8626 return Get(path_with_query.c_str(), headers, progress);
8632 return Get(path, params, headers,
nullptr, content_receiver, progress);
8641 return Get(path, headers, response_handler, content_receiver, progress);
8645 return Get(path_with_query.c_str(), headers, response_handler, content_receiver, progress);
8657 return send_(std::move(req));
8662 return Post(path, std::string(), std::string());
8667 return Post(path, headers,
nullptr, 0, std::string());
8671 const std::string& content_type)
8673 return Post(path,
Headers(), body, content_length, content_type);
8677 size_t content_length,
const std::string& content_type)
8679 return send_with_content_provider(
"POST", path, headers, body, content_length,
nullptr,
nullptr,
8684 const std::string& content_type)
8690 const std::string& body,
const std::string& content_type)
8692 return send_with_content_provider(
"POST", path, headers, body.data(), body.size(),
nullptr,
8693 nullptr, content_type);
8704 return Post(path,
Headers(), content_length, std::move(content_provider), content_type);
8709 const std::string& content_type)
8711 return Post(path,
Headers(), std::move(content_provider), content_type);
8716 const std::string& content_type)
8718 return send_with_content_provider(
"POST", path, headers,
nullptr, content_length,
8719 std::move(content_provider),
nullptr, content_type);
8724 const std::string& content_type)
8726 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr,
8727 std::move(content_provider), content_type);
8734 return Post(path, headers, query,
"application/x-www-form-urlencoded");
8748 return Post(path, headers, body, content_type.c_str());
8761 return Post(path, headers, body, content_type.c_str());
8770 return send_with_content_provider(
8771 "POST", path, headers,
nullptr, 0,
nullptr,
8772 get_multipart_content_provider(boundary, items, provider_items), content_type);
8777 return Put(path, std::string(), std::string());
8781 const std::string& content_type)
8783 return Put(path,
Headers(), body, content_length, content_type);
8787 size_t content_length,
const std::string& content_type)
8789 return send_with_content_provider(
"PUT", path, headers, body, content_length,
nullptr,
nullptr,
8794 const std::string& content_type)
8796 return Put(path,
Headers(), body, content_type);
8800 const std::string& body,
const std::string& content_type)
8802 return send_with_content_provider(
"PUT", path, headers, body.data(), body.size(),
nullptr,
8803 nullptr, content_type);
8809 return Put(path,
Headers(), content_length, std::move(content_provider), content_type);
8814 const std::string& content_type)
8816 return Put(path,
Headers(), std::move(content_provider), content_type);
8821 const std::string& content_type)
8823 return send_with_content_provider(
"PUT", path, headers,
nullptr, content_length,
8824 std::move(content_provider),
nullptr, content_type);
8829 const std::string& content_type)
8831 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr,
8832 std::move(content_provider), content_type);
8843 return Put(path, headers, query,
"application/x-www-form-urlencoded");
8857 return Put(path, headers, body, content_type);
8870 return Put(path, headers, body, content_type);
8879 return send_with_content_provider(
8880 "PUT", path, headers,
nullptr, 0,
nullptr,
8881 get_multipart_content_provider(boundary, items, provider_items), content_type);
8885 return Patch(path, std::string(), std::string());
8889 const std::string& content_type)
8891 return Patch(path,
Headers(), body, content_length, content_type);
8895 size_t content_length,
const std::string& content_type)
8897 return send_with_content_provider(
"PATCH", path, headers, body, content_length,
nullptr,
8898 nullptr, content_type);
8902 const std::string& content_type)
8908 const std::string& body,
const std::string& content_type)
8910 return send_with_content_provider(
"PATCH", path, headers, body.data(), body.size(),
nullptr,
8911 nullptr, content_type);
8917 return Patch(path,
Headers(), content_length, std::move(content_provider), content_type);
8922 const std::string& content_type)
8924 return Patch(path,
Headers(), std::move(content_provider), content_type);
8929 const std::string& content_type)
8931 return send_with_content_provider(
"PATCH", path, headers,
nullptr, content_length,
8932 std::move(content_provider),
nullptr, content_type);
8937 const std::string& content_type)
8939 return send_with_content_provider(
"PATCH", path, headers,
nullptr, 0,
nullptr,
8940 std::move(content_provider), content_type);
8945 return Delete(path,
Headers(), std::string(), std::string());
8950 return Delete(path, headers, std::string(), std::string());
8954 const std::string& content_type)
8956 return Delete(path,
Headers(), body, content_length, content_type);
8960 size_t content_length,
const std::string& content_type)
8967 if (!content_type.empty())
8969 req.
set_header(
"Content-Type", content_type);
8971 req.
body.assign(body, content_length);
8973 return send_(std::move(req));
8977 const std::string& content_type)
8979 return Delete(path,
Headers(), body.data(), body.size(), content_type);
8983 const std::string& body,
const std::string& content_type)
8985 return Delete(path, headers, body.data(), body.size(), content_type);
8997 return send_(std::move(req));
9066 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9067 inline void ClientImpl::set_digest_auth(
const std::string& username,
const std::string& password)
9069 digest_auth_username_ = username;
9070 digest_auth_password_ = password;
9112 const std::string& password)
9123 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9124 inline void ClientImpl::set_proxy_digest_auth(
const std::string& username,
9125 const std::string& password)
9127 proxy_digest_auth_username_ = username;
9128 proxy_digest_auth_password_ = password;
9131 inline void ClientImpl::set_ca_cert_path(
const std::string& ca_cert_file_path,
9132 const std::string& ca_cert_dir_path)
9134 ca_cert_file_path_ = ca_cert_file_path;
9135 ca_cert_dir_path_ = ca_cert_dir_path;
9138 inline void ClientImpl::set_ca_cert_store(X509_STORE* ca_cert_store)
9140 if (ca_cert_store && ca_cert_store != ca_cert_store_)
9142 ca_cert_store_ = ca_cert_store;
9146 inline X509_STORE* ClientImpl::create_ca_cert_store(
const char* ca_cert, std::size_t size)
9148 auto mem = BIO_new_mem_buf(ca_cert,
static_cast<int>(size));
9152 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
9159 auto cts = X509_STORE_new();
9162 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++)
9164 auto itmp = sk_X509_INFO_value(inf, i);
9172 X509_STORE_add_cert(cts, itmp->x509);
9176 X509_STORE_add_crl(cts, itmp->crl);
9181 sk_X509_INFO_pop_free(inf, X509_INFO_free);
9186 inline void ClientImpl::enable_server_certificate_verification(
bool enabled)
9188 server_certificate_verification_ = enabled;
9197 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9201 template <
typename U,
typename V>
9202 inline SSL* ssl_new(
socket_t sock, SSL_CTX* ctx,
std::mutex& ctx_mutex, U SSL_connect_or_accept,
9207 std::lock_guard<std::mutex> guard(ctx_mutex);
9214 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
9215 BIO_set_nbio(bio, 1);
9216 SSL_set_bio(ssl, bio, bio);
9218 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1)
9222 std::lock_guard<std::mutex> guard(ctx_mutex);
9228 BIO_set_nbio(bio, 0);
9235 inline void ssl_delete(
std::mutex& ctx_mutex, SSL* ssl,
bool shutdown_gracefully)
9241 if (shutdown_gracefully)
9246 std::lock_guard<std::mutex> guard(ctx_mutex);
9250 template <
typename U>
9251 bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL* ssl, U ssl_connect_or_accept,
9252 time_t timeout_sec, time_t timeout_usec)
9255 while ((res = ssl_connect_or_accept(ssl)) != 1)
9257 auto err = SSL_get_error(ssl, res);
9260 case SSL_ERROR_WANT_READ:
9261 if (
select_read(sock, timeout_sec, timeout_usec) > 0)
9266 case SSL_ERROR_WANT_WRITE:
9279 template <
typename T>
9280 inline bool process_server_socket_ssl(
const std::atomic<socket_t>& svr_sock, SSL* ssl,
9281 socket_t sock,
size_t keep_alive_max_count,
9282 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
9283 time_t read_timeout_usec, time_t write_timeout_sec,
9284 time_t write_timeout_usec, T callback)
9287 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
9288 [&](
bool close_connection,
bool& connection_closed)
9290 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec,
9291 write_timeout_usec);
9292 return callback(strm, close_connection, connection_closed);
9296 template <
typename T>
9297 inline bool process_client_socket_ssl(SSL* ssl,
socket_t sock, time_t read_timeout_sec,
9298 time_t read_timeout_usec, time_t write_timeout_sec,
9299 time_t write_timeout_usec, T callback)
9301 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec,
9302 write_timeout_usec);
9303 return callback(strm);
9311 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
9316 inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL* ssl, time_t read_timeout_sec,
9317 time_t read_timeout_usec, time_t write_timeout_sec,
9318 time_t write_timeout_usec)
9326 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
9329 inline SSLSocketStream::~SSLSocketStream() {}
9331 inline bool SSLSocketStream::is_readable()
const
9336 inline bool SSLSocketStream::is_writable()
const
9338 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9342 inline ssize_t SSLSocketStream::read(
char* ptr,
size_t size)
9344 if (SSL_pending(ssl_) > 0)
9346 return SSL_read(ssl_, ptr,
static_cast<int>(size));
9348 else if (is_readable())
9350 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
9353 auto err = SSL_get_error(ssl_, ret);
9356 while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
9357 (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)))
9360 while (--n >= 0 && err == SSL_ERROR_WANT_READ)
9363 if (SSL_pending(ssl_) > 0)
9365 return SSL_read(ssl_, ptr,
static_cast<int>(size));
9367 else if (is_readable())
9369 std::this_thread::sleep_for(std::chrono::milliseconds(1));
9370 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
9375 err = SSL_get_error(ssl_, ret);
9388 inline ssize_t SSLSocketStream::write(
const char* ptr,
size_t size)
9393 static_cast<int>(std::min<size_t>(size, (std::numeric_limits<int>::max)()));
9395 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
9398 auto err = SSL_get_error(ssl_, ret);
9401 while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
9402 (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)))
9405 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE)
9410 std::this_thread::sleep_for(std::chrono::milliseconds(1));
9411 ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
9416 err = SSL_get_error(ssl_, ret);
9439 inline socket_t SSLSocketStream::socket()
const {
return sock_; }
9441 static SSLInit sslinit_;
9446 inline SSLServer::SSLServer(
const char* cert_path,
const char* private_key_path,
9447 const char* client_ca_cert_file_path,
9448 const char* client_ca_cert_dir_path,
const char* private_key_password)
9450 ctx_ = SSL_CTX_new(TLS_server_method());
9454 SSL_CTX_set_options(ctx_,
9455 SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9457 SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
9460 if (private_key_password !=
nullptr && (private_key_password[0] !=
'\0'))
9462 SSL_CTX_set_default_passwd_cb_userdata(
9463 ctx_,
reinterpret_cast<void*
>(
const_cast<char*
>(private_key_password)));
9466 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
9467 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1)
9472 else if (client_ca_cert_file_path || client_ca_cert_dir_path)
9474 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path);
9476 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
9481 inline SSLServer::SSLServer(X509* cert, EVP_PKEY* private_key, X509_STORE* client_ca_cert_store)
9483 ctx_ = SSL_CTX_new(TLS_server_method());
9487 SSL_CTX_set_options(ctx_,
9488 SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9490 SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
9492 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
9493 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1)
9498 else if (client_ca_cert_store)
9500 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9502 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
9507 inline SSLServer::SSLServer(
const std::function<
bool(SSL_CTX& ssl_ctx)>& setup_ssl_ctx_callback)
9509 ctx_ = SSL_CTX_new(TLS_method());
9512 if (!setup_ssl_ctx_callback(*ctx_))
9520 inline SSLServer::~SSLServer()
9528 inline bool SSLServer::is_valid()
const {
return ctx_; }
9530 inline SSL_CTX* SSLServer::ssl_context()
const {
return ctx_; }
9532 inline bool SSLServer::process_and_close_socket(
socket_t sock)
9534 auto ssl = detail::ssl_new(
9535 sock, ctx_, ctx_mutex_,
9538 return detail::ssl_connect_or_accept_nonblocking(sock, ssl2, SSL_accept,
9539 read_timeout_sec_, read_timeout_usec_);
9541 [](SSL* ) {
return true; });
9546 ret = detail::process_server_socket_ssl(
9547 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
9548 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
9549 [
this, ssl](Stream& strm,
bool close_connection,
bool& connection_closed)
9551 return process_request(strm, close_connection, connection_closed,
9552 [&](
Request& req) { req.ssl = ssl; });
9557 const bool shutdown_gracefully = ret;
9558 detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
9567 inline SSLClient::SSLClient(
const std::string& host)
9572 inline SSLClient::SSLClient(
const std::string& host,
int port)
9577 inline SSLClient::SSLClient(
const std::string& host,
int port,
const std::string& client_cert_path,
9578 const std::string& client_key_path)
9579 : ClientImpl(host, port, client_cert_path, client_key_path)
9581 ctx_ = SSL_CTX_new(TLS_client_method());
9584 [&](
const char* b,
const char*
e)
9585 { host_components_.emplace_back(std::string(b, e)); });
9587 if (!client_cert_path.empty() && !client_key_path.empty())
9589 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 ||
9590 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1)
9598 inline SSLClient::SSLClient(
const std::string& host,
int port, X509* client_cert,
9599 EVP_PKEY* client_key)
9600 : ClientImpl(host, port)
9602 ctx_ = SSL_CTX_new(TLS_client_method());
9605 [&](
const char* b,
const char*
e)
9606 { host_components_.emplace_back(std::string(b, e)); });
9608 if (client_cert !=
nullptr && client_key !=
nullptr)
9610 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
9611 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1)
9619 inline SSLClient::~SSLClient()
9628 shutdown_ssl_impl(socket_,
true);
9631 inline bool SSLClient::is_valid()
const {
return ctx_; }
9633 inline void SSLClient::set_ca_cert_store(X509_STORE* ca_cert_store)
9639 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store)
9642 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
9647 X509_STORE_free(ca_cert_store);
9652 inline void SSLClient::load_ca_cert_store(
const char* ca_cert, std::size_t size)
9654 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
9657 inline long SSLClient::get_openssl_verify_result()
const {
return verify_result_; }
9659 inline SSL_CTX* SSLClient::ssl_context()
const {
return ctx_; }
9661 inline bool SSLClient::create_and_connect_socket(Socket& socket,
Error& error)
9667 inline bool SSLClient::connect_with_proxy(Socket& socket, Response& res,
bool& success,
9673 write_timeout_sec_, write_timeout_usec_,
9677 req2.method =
"CONNECT";
9678 req2.path = host_and_port_;
9679 return process_request(strm, req2, proxy_res, false,
9685 shutdown_ssl(socket,
true);
9692 if (proxy_res.status == 407)
9694 if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty())
9696 std::map<std::string, std::string> auth;
9699 proxy_res = Response();
9701 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9702 write_timeout_usec_,
9706 req3.method =
"CONNECT";
9707 req3.path = host_and_port_;
9708 req3.headers.insert(detail::make_digest_authentication_header(
9709 req3, auth, 1, detail::random_string(10),
9710 proxy_digest_auth_username_, proxy_digest_auth_password_, true));
9711 return process_request(strm, req3, proxy_res, false, error);
9716 shutdown_ssl(socket,
true);
9729 if (proxy_res.status != 200)
9732 res = std::move(proxy_res);
9735 shutdown_ssl(socket,
true);
9744 inline bool SSLClient::load_certs()
9752 std::lock_guard<std::mutex> guard(ctx_mutex_);
9753 if (!ca_cert_file_path_.empty())
9755 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
nullptr))
9760 else if (!ca_cert_dir_path_.empty())
9762 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr, ca_cert_dir_path_.c_str()))
9769 auto loaded =
false;
9771 loaded = detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9772 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9774 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
9775 #endif // TARGET_OS_OSX
9779 SSL_CTX_set_default_verify_paths(ctx_);
9787 inline bool SSLClient::initialize_ssl(Socket& socket,
Error& error)
9789 auto ssl = detail::ssl_new(
9790 socket.sock, ctx_, ctx_mutex_,
9793 if (server_certificate_verification_)
9797 error = Error::SSLLoadingCerts;
9800 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
9803 if (!detail::ssl_connect_or_accept_nonblocking(socket.sock, ssl2, SSL_connect,
9804 connection_timeout_sec_,
9805 connection_timeout_usec_))
9807 error = Error::SSLConnection;
9811 if (server_certificate_verification_)
9813 verify_result_ = SSL_get_verify_result(ssl2);
9815 if (verify_result_ != X509_V_OK)
9821 auto server_cert = SSL_get1_peer_certificate(ssl2);
9823 if (server_cert ==
nullptr)
9829 if (!verify_host(server_cert))
9831 X509_free(server_cert);
9835 X509_free(server_cert);
9846 SSL_set_tlsext_host_name(ssl2, host_.c_str());
9861 inline void SSLClient::shutdown_ssl(Socket& socket,
bool shutdown_gracefully)
9863 shutdown_ssl_impl(socket, shutdown_gracefully);
9866 inline void SSLClient::shutdown_ssl_impl(Socket& socket,
bool shutdown_gracefully)
9870 assert(socket.ssl ==
nullptr);
9875 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
9876 socket.ssl =
nullptr;
9878 assert(socket.ssl ==
nullptr);
9881 inline bool SSLClient::process_socket(
const Socket& socket,
9882 std::function<
bool(Stream& strm)> callback)
9885 return detail::process_client_socket_ssl(socket.ssl, socket.sock, read_timeout_sec_,
9886 read_timeout_usec_, write_timeout_sec_,
9887 write_timeout_usec_, std::move(callback));
9890 inline bool SSLClient::is_ssl()
const {
return true; }
9892 inline bool SSLClient::verify_host(X509* server_cert)
const
9915 return verify_host_with_subject_alt_name(server_cert) ||
9916 verify_host_with_common_name(server_cert);
9919 inline bool SSLClient::verify_host_with_subject_alt_name(X509* server_cert)
const
9923 auto type = GEN_DNS;
9925 struct in6_addr addr6;
9926 struct in_addr addr;
9927 size_t addr_len = 0;
9930 if (inet_pton(AF_INET6, host_.c_str(), &addr6))
9933 addr_len =
sizeof(
struct in6_addr);
9935 else if (inet_pton(AF_INET, host_.c_str(), &addr))
9938 addr_len =
sizeof(
struct in_addr);
9942 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME*
>(
9943 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
9947 auto dsn_matched =
false;
9948 auto ip_matched =
false;
9950 auto count = sk_GENERAL_NAME_num(alt_names);
9952 for (decltype(count) i = 0; i < count && !dsn_matched; i++)
9954 auto val = sk_GENERAL_NAME_value(alt_names, i);
9955 if (val->type ==
type)
9957 auto name =
reinterpret_cast<const char*
>(ASN1_STRING_get0_data(val->d.ia5));
9958 auto name_len =
static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
9962 case GEN_DNS: dsn_matched = check_host_name(name, name_len);
break;
9965 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len))
9974 if (dsn_matched || ip_matched)
9980 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME)*
>(
9981 reinterpret_cast<const STACK_OF(GENERAL_NAME)*
>(alt_names)));
9985 inline bool SSLClient::verify_host_with_common_name(X509* server_cert)
const
9987 const auto subject_name = X509_get_subject_name(server_cert);
9989 if (subject_name !=
nullptr)
9992 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name,
sizeof(name));
9996 return check_host_name(name,
static_cast<size_t>(name_len));
10003 inline bool SSLClient::check_host_name(
const char* pattern,
size_t pattern_len)
const
10005 if (host_.size() == pattern_len && host_ == pattern)
10012 std::vector<std::string> pattern_components;
10014 [&](
const char* b,
const char*
e)
10015 { pattern_components.emplace_back(std::string(b,
e)); });
10017 if (host_components_.size() != pattern_components.size())
10022 auto itr = pattern_components.begin();
10023 for (
const auto& h : host_components_)
10026 if (p != h && p !=
"*")
10028 auto partial_match =
10029 (p.size() > 0 && p[p.size() - 1] ==
'*' && !p.compare(0, p.size() - 1, h));
10030 if (!partial_match)
10044 :
Client(scheme_host_port, std::string(), std::string())
10048 inline Client::Client(
const std::string& scheme_host_port,
const std::string& client_cert_path,
10049 const std::string& client_key_path)
10051 const static std::regex re(R
"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
10054 if (std::regex_match(scheme_host_port, m, re))
10056 auto scheme = m[1].str();
10058 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10065 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
10066 std::string
msg =
"'" +
scheme +
"' scheme is not supported.";
10067 throw std::invalid_argument(
msg);
10072 auto is_ssl =
scheme ==
"https";
10074 auto host = m[2].str();
10080 auto port_str = m[4].str();
10081 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
10085 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10086 cli_ = detail::make_unique<SSLClient>(
host,
port, client_cert_path, client_key_path);
10092 cli_ = detail::make_unique<ClientImpl>(
host,
port, client_cert_path, client_key_path);
10097 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80, client_cert_path,
10107 inline Client::Client(
const std::string& host,
int port,
const std::string& client_cert_path,
10108 const std::string& client_key_path)
10120 return cli_->Get(path, headers);
10124 return cli_->Get(path, std::move(progress));
10128 return cli_->Get(path, headers, std::move(progress));
10132 return cli_->Get(path, std::move(content_receiver));
10137 return cli_->Get(path, headers, std::move(content_receiver));
10142 return cli_->Get(path, std::move(content_receiver), std::move(progress));
10147 return cli_->Get(path, headers, std::move(content_receiver), std::move(progress));
10152 return cli_->Get(path, std::move(response_handler), std::move(content_receiver));
10157 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver));
10162 return cli_->Get(path, std::move(response_handler), std::move(content_receiver),
10163 std::move(progress));
10169 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver),
10170 std::move(progress));
10175 return cli_->Get(path, params, headers, progress);
10180 return cli_->Get(path, params, headers, content_receiver, progress);
10186 return cli_->Get(path, params, headers, response_handler, content_receiver, progress);
10192 return cli_->Head(path, headers);
10198 return cli_->Post(path, headers);
10201 const std::string& content_type)
10203 return cli_->Post(path, body, content_length, content_type);
10206 size_t content_length,
const std::string& content_type)
10208 return cli_->Post(path, headers, body, content_length, content_type);
10211 const std::string& content_type)
10213 return cli_->Post(path, body, content_type);
10216 const std::string& content_type)
10218 return cli_->Post(path, headers, body, content_type);
10223 return cli_->Post(path, content_length, std::move(content_provider), content_type);
10226 const std::string& content_type)
10228 return cli_->Post(path, std::move(content_provider), content_type);
10233 return cli_->Post(path, headers, content_length, std::move(content_provider), content_type);
10237 const std::string& content_type)
10239 return cli_->Post(path, headers, std::move(content_provider), content_type);
10243 return cli_->Post(path, params);
10247 return cli_->Post(path, headers, params);
10251 return cli_->Post(path, items);
10256 return cli_->Post(path, headers, items);
10261 return cli_->Post(path, headers, items, boundary);
10267 return cli_->Post(path, headers, items, provider_items);
10271 const std::string& content_type)
10273 return cli_->Put(path, body, content_length, content_type);
10276 size_t content_length,
const std::string& content_type)
10278 return cli_->Put(path, headers, body, content_length, content_type);
10281 const std::string& content_type)
10283 return cli_->Put(path, body, content_type);
10286 const std::string& content_type)
10288 return cli_->Put(path, headers, body, content_type);
10293 return cli_->Put(path, content_length, std::move(content_provider), content_type);
10296 const std::string& content_type)
10298 return cli_->Put(path, std::move(content_provider), content_type);
10303 return cli_->Put(path, headers, content_length, std::move(content_provider), content_type);
10307 const std::string& content_type)
10309 return cli_->Put(path, headers, std::move(content_provider), content_type);
10313 return cli_->Put(path, params);
10317 return cli_->Put(path, headers, params);
10321 return cli_->Put(path, items);
10326 return cli_->Put(path, headers, items);
10331 return cli_->Put(path, headers, items, boundary);
10337 return cli_->Put(path, headers, items, provider_items);
10341 const std::string& content_type)
10343 return cli_->Patch(path, body, content_length, content_type);
10346 size_t content_length,
const std::string& content_type)
10348 return cli_->Patch(path, headers, body, content_length, content_type);
10351 const std::string& content_type)
10353 return cli_->Patch(path, body, content_type);
10356 const std::string& body,
const std::string& content_type)
10358 return cli_->Patch(path, headers, body, content_type);
10363 return cli_->Patch(path, content_length, std::move(content_provider), content_type);
10366 const std::string& content_type)
10368 return cli_->Patch(path, std::move(content_provider), content_type);
10373 return cli_->Patch(path, headers, content_length, std::move(content_provider), content_type);
10377 const std::string& content_type)
10379 return cli_->Patch(path, headers, std::move(content_provider), content_type);
10384 return cli_->Delete(path, headers);
10387 const std::string& content_type)
10389 return cli_->Delete(path, body, content_length, content_type);
10392 size_t content_length,
const std::string& content_type)
10394 return cli_->Delete(path, headers, body, content_length, content_type);
10397 const std::string& content_type)
10399 return cli_->Delete(path, body, content_type);
10402 const std::string& body,
const std::string& content_type)
10404 return cli_->Delete(path, headers, body, content_type);
10409 return cli_->Options(path, headers);
10414 return cli_->send(req, res, error);
10431 cli_->set_hostname_addr_map(std::move(addr_map));
10436 cli_->set_default_headers(std::move(headers));
10445 cli_->set_socket_options(std::move(socket_options));
10450 cli_->set_connection_timeout(sec, usec);
10457 cli_->set_write_timeout(sec, usec);
10462 cli_->set_basic_auth(username, password);
10466 cli_->set_bearer_token_auth(token);
10468 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10469 inline void Client::set_digest_auth(
const std::string& username,
const std::string& password)
10471 cli_->set_digest_auth(username, password);
10489 cli_->set_proxy_basic_auth(username, password);
10493 cli_->set_proxy_bearer_token_auth(token);
10495 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10496 inline void Client::set_proxy_digest_auth(
const std::string& username,
const std::string& password)
10498 cli_->set_proxy_digest_auth(username, password);
10502 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10503 inline void Client::enable_server_certificate_verification(
bool enabled)
10505 cli_->enable_server_certificate_verification(enabled);
10511 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10512 inline void Client::set_ca_cert_path(
const std::string& ca_cert_file_path,
10513 const std::string& ca_cert_dir_path)
10515 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
10518 inline void Client::set_ca_cert_store(X509_STORE* ca_cert_store)
10522 static_cast<SSLClient&
>(*cli_).set_ca_cert_store(ca_cert_store);
10526 cli_->set_ca_cert_store(ca_cert_store);
10530 inline void Client::load_ca_cert_store(
const char* ca_cert, std::size_t size)
10532 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
10535 inline long Client::get_openssl_verify_result()
const
10539 return static_cast<SSLClient&
>(*cli_).get_openssl_verify_result();
10544 inline SSL_CTX* Client::ssl_context()
const
10548 return static_cast<SSLClient&
>(*cli_).ssl_context();
10558 #if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
10562 #endif // CPPHTTPLIB_HTTPLIB_H