306template <
class T,
class... Args>
307typename 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)...));
314typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
317 typedef typename std::remove_extent<T>::type RT;
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;
369using Headers = std::multimap<std::string, std::string, detail::ci>;
371using Params = std::multimap<std::string, std::string>;
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)>;
469using 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(); }
650 explicit worker(
ThreadPool& pool) : pool_(pool) {}
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;
979std::ostream& operator<<(std::ostream& os,
const Error& obj);
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
1568class 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;
1590 std::mutex ctx_mutex_;
1593class 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;
1619 void shutdown_ssl_impl(Socket& socket,
bool shutdown_socket);
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;
1635 std::mutex ctx_mutex_;
1636 std::once_flag initialize_cert_;
1638 std::vector<std::string> host_components_;
1640 long verify_result_ = 0;
1642 friend class ClientImpl;
1653template <
typename T,
typename U>
1654inline void duration_to_sec_and_usec(
const T& duration, U callback)
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));
1663inline uint64_t get_header_value_u64(
const Headers& headers,
const std::string& key,
size_t id,
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);
1680 return detail::get_header_value_u64(
headers, key,
id, 0);
1685 return detail::get_header_value_u64(
headers, key,
id, 0);
1688template <
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";
1808template <
class Rep,
class Period>
1811 detail::duration_to_sec_and_usec(duration,
1816template <
class Rep,
class Period>
1819 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec)
1824template <
class Rep,
class Period>
1827 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec)
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";
1858inline std::ostream& operator<<(std::ostream& os,
const Error& obj)
1861 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
1867 return detail::get_header_value_u64(request_headers_, key,
id, 0);
1870template <
class Rep,
class Period>
1873 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec)
1877template <
class Rep,
class Period>
1880 detail::duration_to_sec_and_usec(duration,
1884template <
class Rep,
class Period>
1887 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec)
1891template <
class Rep,
class Period>
1894 cli_->set_connection_timeout(duration);
1897template <
class Rep,
class Period>
1900 cli_->set_read_timeout(duration);
1903template <
class Rep,
class Period>
1906 cli_->set_write_timeout(duration);
1914std::string
hosted_at(
const std::string& hostname);
1916void hosted_at(
const std::string& hostname, std::vector<std::string>& addrs);
1923 const std::string& password,
1924 bool is_proxy =
false);
1929std::string encode_query_param(
const std::string& value);
1931std::string decode_url(
const std::string& s,
bool convert_plus_to_space);
1933void read_file(
const std::string& path, std::string& out);
1935std::string trim_copy(
const std::string& s);
1937void split(
const char* b,
const char* e,
char d, std::function<
void(
const char*,
const char*)> fn);
1939bool process_client_socket(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
1940 time_t write_timeout_sec, time_t write_timeout_usec,
1941 std::function<
bool(
Stream&)> callback);
1943socket_t create_client_socket(
const std::string& host,
const std::string& ip,
int port,
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);
1950const char* get_header_value(
const Headers& headers,
const std::string& key,
size_t id = 0,
1951 const char* def =
nullptr);
1953std::string params_to_query_str(
const Params& params);
1955void parse_query_text(
const std::string& s,
Params& params);
1957bool parse_multipart_boundary(
const std::string& content_type, std::string& boundary);
1959bool parse_range_header(
const std::string& s,
Ranges& ranges);
1963ssize_t send_socket(
socket_t sock,
const void* ptr,
size_t size,
int flags);
1965ssize_t read_socket(
socket_t sock,
void* ptr,
size_t size,
int flags);
1967enum class EncodingType
1974EncodingType encoding_type(
const Request& req,
const Response& res);
1976class BufferStream :
public Stream
1979 BufferStream() =
default;
1980 ~BufferStream()
override =
default;
1982 bool is_readable()
const override;
1983 bool is_writable()
const override;
1984 ssize_t read(
char* ptr,
size_t size)
override;
1985 ssize_t write(
const char* ptr,
size_t size)
override;
1986 void get_remote_ip_and_port(std::string& ip,
int& port)
const override;
1987 void get_local_ip_and_port(std::string& ip,
int& port)
const override;
1990 const std::string& get_buffer()
const;
1994 size_t position = 0;
2000 virtual ~compressor() =
default;
2002 typedef std::function<
bool(
const char* data,
size_t data_len)> Callback;
2003 virtual bool compress(
const char* data,
size_t data_length,
bool last, Callback callback) = 0;
2009 virtual ~decompressor() =
default;
2011 virtual bool is_valid()
const = 0;
2013 typedef std::function<
bool(
const char* data,
size_t data_len)> Callback;
2014 virtual bool decompress(
const char* data,
size_t data_length, Callback callback) = 0;
2017class nocompressor :
public compressor
2020 virtual ~nocompressor() =
default;
2022 bool compress(
const char* data,
size_t data_length,
bool , Callback callback)
override;
2025#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2026class gzip_compressor :
public compressor
2032 bool compress(
const char* data,
size_t data_length,
bool last, Callback callback)
override;
2035 bool is_valid_ =
false;
2039class 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
2056class 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;
2068class 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;
2086class stream_line_reader
2089 stream_line_reader(Stream& strm,
char* fixed_buffer,
size_t fixed_buffer_size);
2090 const char* ptr()
const;
2091 size_t size()
const;
2092 bool end_with_crlf()
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);
2114 bool is_open()
const;
2115 size_t size()
const;
2116 const char* data()
const;
2140inline bool is_hex(
char c,
int& v)
2142 if (0x20 <= c && isdigit(c))
2147 else if (
'A' <= c && c <=
'F')
2152 else if (
'a' <= c && c <=
'f')
2160inline bool from_hex_to_i(
const std::string& s,
size_t i,
size_t cnt,
int& val)
2168 for (; cnt; i++, cnt--)
2175 if (is_hex(s[i], v))
2187inline std::string from_i_to_hex(
size_t n)
2189 static const auto charset =
"0123456789abcdef";
2192 ret = charset[n & 15] + ret;
2198inline size_t to_utf8(
int code,
char* buff)
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));
2244inline std::string base64_encode(
const std::string& in)
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(
'='); }
2275inline bool is_file(
const std::string& path)
2278 return _access_s(path.c_str(), 0) == 0;
2281 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
2285inline bool is_dir(
const std::string& path)
2288 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
2291inline bool is_valid_path(
const std::string& path)
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++; }
2332inline std::string encode_query_param(
const std::string& value)
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();
2356inline std::string encode_url(
const std::string& s)
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));
2394inline 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')
2405 if (from_hex_to_i(s, i + 2, 4, val))
2409 size_t len = to_utf8(val, buff);
2412 result.append(buff, len);
2424 if (from_hex_to_i(s, i + 1, 2, val))
2427 result +=
static_cast<char>(val);
2436 else if (convert_plus_to_space && s[i] ==
'+')
2449inline 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));
2459inline std::string file_extension(
const std::string& path)
2462 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2463 if (std::regex_search(path, m, re))
2467 return std::string();
2470inline bool is_space_or_tab(
char c) {
return c ==
' ' || c ==
'\t'; }
2472inline std::pair<size_t, size_t> trim(
const char* b,
const char* e,
size_t left,
size_t right)
2474 while (b + left < e && is_space_or_tab(b[left])) { left++; }
2475 while (right > 0 && is_space_or_tab(b[right - 1])) { right--; }
2476 return std::make_pair(left, right);
2479inline std::string trim_copy(
const std::string& s)
2481 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2482 return s.substr(r.first, r.second - r.first);
2485inline std::string trim_double_quotes_copy(
const std::string& s)
2487 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"')
2489 return s.substr(1, s.size() - 2);
2494inline 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]);
2524inline stream_line_reader::stream_line_reader(Stream& strm,
char* fixed_buffer,
2525 size_t fixed_buffer_size)
2526 : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size)
2530inline const char* stream_line_reader::ptr()
const
2532 if (glowable_buffer_.empty())
2534 return fixed_buffer_;
2538 return glowable_buffer_.data();
2542inline size_t stream_line_reader::size()
const
2544 if (glowable_buffer_.empty())
2546 return fixed_buffer_used_size_;
2550 return glowable_buffer_.size();
2554inline bool stream_line_reader::end_with_crlf()
const
2556 auto end = ptr() + size();
2557 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
2560inline bool stream_line_reader::getline()
2562 fixed_buffer_used_size_ = 0;
2563 glowable_buffer_.clear();
2565 for (
size_t i = 0;; i++)
2568 auto n = strm_.read(&
byte, 1);
2597inline 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;
2615inline mmap::mmap(
const char* path)
2628 std::runtime_error(
"");
2632inline mmap::~mmap() { close(); }
2634inline bool mmap::open(
const char* path)
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)
2685inline bool mmap::is_open()
const {
return addr_ !=
nullptr; }
2687inline size_t mmap::size()
const {
return size_; }
2689inline const char* mmap::data()
const {
return (
const char*)addr_; }
2691inline void mmap::close()
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_);
2726inline int close_socket(
socket_t sock)
2729 return closesocket(sock);
2735template <
typename T>
inline ssize_t handle_EINTR(T fn)
2741 if (res < 0 && errno == EINTR)
2750inline ssize_t read_socket(
socket_t sock,
void* ptr,
size_t size,
int flags)
2752 return handle_EINTR(
2757 static_cast<char*
>(ptr),
static_cast<int>(size),
2765inline ssize_t send_socket(
socket_t sock,
const void* ptr,
size_t size,
int flags)
2767 return handle_EINTR(
2772 static_cast<const char*
>(ptr),
static_cast<int>(size),
2780inline ssize_t select_read(
socket_t sock, time_t sec, time_t usec)
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);
2806 return handle_EINTR(
2807 [&]() {
return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv); });
2811inline ssize_t select_write(
socket_t sock, time_t sec, time_t usec)
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);
2837 return handle_EINTR(
2838 [&]() {
return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv); });
2842inline Error wait_until_socket_is_ready(
socket_t sock, time_t sec, time_t usec)
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);
2887 auto ret = handle_EINTR(
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;
2907inline bool is_socket_alive(
socket_t sock)
2909 const auto val = detail::select_read(sock, 0, 0);
2914 else if (val < 0 && errno == EBADF)
2919 return detail::read_socket(sock, &buf[0],
sizeof(buf), MSG_PEEK) > 0;
2922class SocketStream :
public Stream
2925 SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2926 time_t write_timeout_sec, time_t write_timeout_usec);
2927 ~SocketStream()
override;
2929 bool is_readable()
const override;
2930 bool is_writable()
const override;
2931 ssize_t read(
char* ptr,
size_t size)
override;
2932 ssize_t write(
const char* ptr,
size_t size)
override;
2933 void get_remote_ip_and_port(std::string& ip,
int& port)
const override;
2934 void get_local_ip_and_port(std::string& ip,
int& port)
const 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
2952class 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;
2963 void get_remote_ip_and_port(std::string& ip,
int& port)
const override;
2964 void get_local_ip_and_port(std::string& ip,
int& port)
const 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_;
2977inline bool keep_alive(
socket_t sock, time_t keep_alive_timeout_sec)
2979 using namespace std::chrono;
2980 auto start = steady_clock::now();
2983 auto val = select_read(sock, 0, 10000);
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));
3006template <
typename T>
3007inline bool process_server_socket_core(
const std::atomic<socket_t>& svr_sock,
socket_t sock,
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;
3014 while (svr_sock !=
INVALID_SOCKET && count > 0 && keep_alive(sock, keep_alive_timeout_sec))
3016 auto close_connection = count == 1;
3017 auto connection_closed =
false;
3018 ret = callback(close_connection, connection_closed);
3019 if (!ret || connection_closed)
3028template <
typename T>
3029inline bool process_server_socket(
const std::atomic<socket_t>& svr_sock,
socket_t sock,
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)
3034 return process_server_socket_core(
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);
3044inline bool process_client_socket(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
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);
3053inline int shutdown_socket(
socket_t sock)
3056 return shutdown(sock, SD_BOTH);
3058 return shutdown(sock, SHUT_RDWR);
3062template <
typename BindOrConnect>
3063socket_t create_socket(
const std::string& host,
const std::string& ip,
int port,
int address_family,
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))
3127 auto service = std::to_string(port);
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);
3220inline void set_nonblocking(
socket_t sock,
bool nonblocking)
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)));
3231inline bool is_connection_error()
3234 return WSAGetLastError() != WSAEWOULDBLOCK;
3236 return errno != EINPROGRESS;
3240inline bool bind_ip_address(
socket_t sock,
const std::string& host)
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__
3275inline 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;
3324inline socket_t create_client_socket(
const std::string& host,
const std::string& ip,
int port,
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,
3332 auto sock = create_socket(
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())
3344 if (!bind_ip_address(sock2, ip_from_if.c_str()))
3346 error = Error::BindIPAddress;
3352 set_nonblocking(sock2,
true);
3354 auto ret = ::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
3358 if (is_connection_error())
3363 error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
3364 connection_timeout_usec);
3371 set_nonblocking(sock2,
false);
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),
3422inline bool get_ip_and_port(
const struct sockaddr_storage& addr, socklen_t addr_len,
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))
3449inline void get_local_ip_and_port(
socket_t sock, std::string& ip,
int& port)
3451 struct sockaddr_storage addr;
3452 socklen_t addr_len =
sizeof(addr);
3453 if (!getsockname(sock,
reinterpret_cast<struct sockaddr*
>(&addr), &addr_len))
3455 get_ip_and_port(addr, addr_len, ip, port);
3459inline void get_remote_ip_and_port(
socket_t sock, std::string& ip,
int& port)
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)
3478 socklen_t len =
sizeof(pid);
3479 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0)
3487 get_ip_and_port(addr, addr_len, ip, port);
3491inline constexpr unsigned int str2tag_core(
const char* s,
size_t l,
unsigned int h)
3494 : str2tag_core(s + 1, l - 1,
3496 (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^
3497 static_cast<unsigned char>(*s));
3500inline unsigned int str2tag(
const std::string& s) {
return str2tag_core(s.data(), s.size(), 0); }
3505inline constexpr unsigned int operator"" _t(
const char* s,
size_t l)
3507 return str2tag_core(s, l, 0);
3512inline std::string find_content_type(
const std::string& path,
3513 const std::map<std::string, std::string>& user_data,
3514 const std::string& default_content_type)
3516 auto ext = file_extension(path);
3518 auto it = user_data.find(ext);
3519 if (it != user_data.end())
3521 return it->second.c_str();
3524 using udl::operator
""_t;
3526 switch (str2tag(ext))
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";
3582inline bool can_compress_content_type(
const std::string& content_type)
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;
3601inline EncodingType encoding_type(
const Request& req,
const Response& res)
3603 auto ret = detail::can_compress_content_type(res.get_header_value(
"Content-Type"));
3606 return EncodingType::None;
3609 const auto& s = req.get_header_value(
"Accept-Encoding");
3612#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3614 ret = s.find(
"br") != std::string::npos;
3617 return EncodingType::Brotli;
3621#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3623 ret = s.find(
"gzip") != std::string::npos;
3626 return EncodingType::Gzip;
3630 return EncodingType::None;
3633inline bool nocompressor::compress(
const char* data,
size_t data_length,
bool ,
3640 return callback(data, data_length);
3643#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3644inline 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;
3655inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3657inline 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);
3699inline 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;
3713inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3715inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
3717inline 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
3765inline brotli_compressor::brotli_compressor()
3767 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
3770inline brotli_compressor::~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
3772inline 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);
3817inline 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;
3823inline brotli_decompressor::~brotli_decompressor()
3827 BrotliDecoderDestroyInstance(decoder_s);
3831inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
3833inline 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;
3872inline bool has_header(
const Headers& headers,
const std::string& key)
3874 return headers.find(key) != headers.end();
3877inline const char* get_header_value(
const Headers& headers,
const std::string& key,
size_t id,
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();
3890inline bool compare_case_ignore(
const std::string& a,
const std::string& b)
3892 if (a.size() != b.size())
3896 for (
size_t i = 0; i < b.size(); i++)
3898 if (::tolower(a[i]) != ::tolower(b[i]))
3906template <
typename T>
inline bool parse_header(
const char* beg,
const char* end, T fn)
3909 while (beg < end && is_space_or_tab(end[-1])) { end--; }
3912 while (p < end && *p !=
':') { p++; }
3926 while (p < end && is_space_or_tab(*p)) { p++; }
3930 auto key = std::string(beg, key_end);
3931 auto val = compare_case_ignore(key,
"Location") ? std::string(p, end)
3932 : decode_url(
std::
string(p, end), false);
3933 fn(std::move(key), std::move(val));
3940inline bool read_headers(Stream& strm, Headers& headers)
3942 const auto bufsiz = 2048;
3944 stream_line_reader line_reader(strm, buf, bufsiz);
3948 if (!line_reader.getline())
3954 auto line_terminator_len = 2;
3955 if (line_reader.end_with_crlf())
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;
3989 parse_header(line_reader.ptr(), end,
3990 [&](std::string&& key, std::string&& val)
3991 { headers.emplace(std::move(key), std::move(val)); });
3997inline bool read_content_with_length(Stream& strm, uint64_t len, Progress progress,
3998 ContentReceiverWithProgress out)
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))
4030inline void skip_content_with_length(Stream& strm, uint64_t len)
4036 auto read_len =
static_cast<size_t>(len - r);
4042 r +=
static_cast<uint64_t
>(n);
4046inline bool read_content_without_length(Stream& strm, ContentReceiverWithProgress out)
4062 if (!out(buf,
static_cast<size_t>(n), r, 0))
4066 r +=
static_cast<uint64_t
>(n);
4072template <
typename T>
4073inline bool read_content_chunked(Stream& strm, T& x, ContentReceiverWithProgress out)
4075 const auto bufsiz = 16;
4078 stream_line_reader line_reader(strm, buf, bufsiz);
4080 if (!line_reader.getline())
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)
4106 if (!read_content_with_length(strm, chunk_len,
nullptr, out))
4111 if (!line_reader.getline())
4116 if (strcmp(line_reader.ptr(),
"\r\n"))
4121 if (!line_reader.getline())
4127 assert(chunk_len == 0);
4130 if (!line_reader.getline())
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;
4146 parse_header(line_reader.ptr(), end,
4147 [&](std::string&& key, std::string&& val)
4148 { x.headers.emplace(std::move(key), std::move(val)); });
4150 if (!line_reader.getline())
4159inline bool is_chunked_transfer_encoding(
const Headers& headers)
4161 return !strcasecmp(get_header_value(headers,
"Transfer-Encoding", 0,
""),
"chunked");
4164template <
typename T,
typename U>
4165bool prepare_content_receiver(T& x,
int& status, ContentReceiverWithProgress receiver,
4166 bool decompress, U callback)
4170 std::string encoding = x.get_header_value(
"Content-Encoding");
4171 std::unique_ptr<decompressor> decompressor;
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>();
4194 if (decompressor->is_valid())
4197 [&](
const char* buf,
size_t n, uint64_t off, uint64_t len)
4199 return decompressor->decompress(buf, n,
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));
4218template <
typename T>
4219bool read_content(Stream& strm, T& x,
size_t payload_max_length,
int& status, Progress progress,
4220 ContentReceiverWithProgress receiver,
bool decompress)
4222 return prepare_content_receiver(
4223 x, status, std::move(receiver), decompress,
4224 [&](
const ContentReceiverWithProgress& out)
4227 auto exceed_payload_max_length =
false;
4229 if (is_chunked_transfer_encoding(x.headers))
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);
4248 ret = read_content_with_length(strm, len, std::move(progress), out);
4254 status = exceed_payload_max_length ? 413 : 400;
4260inline ssize_t write_headers(Stream& strm,
const Headers& headers)
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");
4281inline bool write_data(Stream& strm,
const char* d,
size_t l)
4286 auto length = strm.write(d + offset, l - offset);
4291 offset +=
static_cast<size_t>(length);
4296template <
typename T>
4297inline bool write_content(Stream& strm,
const ContentProvider& content_provider,
size_t offset,
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
4308 if (strm.is_writable() && write_data(strm, d, l))
4320 while (offset < end_offset && !is_shutting_down())
4322 if (!strm.is_writable())
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;
4343template <
typename T>
4344inline bool write_content(Stream& strm,
const ContentProvider& content_provider,
size_t offset,
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);
4351template <
typename T>
4352inline bool write_content_without_length(Stream& strm,
const ContentProvider& content_provider,
4353 const T& is_shutting_down)
4356 auto data_available =
true;
4360 data_sink.write = [&](
const char* d,
size_t l) ->
bool
4365 if (!strm.is_writable() || !write_data(strm, d, l))
4373 data_sink.done = [&](void) { data_available =
false; };
4375 while (data_available && !is_shutting_down())
4377 if (!strm.is_writable())
4381 else if (!content_provider(offset, 0, data_sink))
4393template <
typename T,
typename U>
4394inline bool write_content_chunked(Stream& strm,
const ContentProvider& content_provider,
4395 const T& is_shutting_down, U& compressor, Error& error)
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;
4410 if (compressor.compress(d, l,
false,
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";
4421 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size()))
4435 auto done_with_trailer = [&](
const Headers* trailer)
4442 data_available =
false;
4444 std::string payload;
4445 if (!compressor.compress(
nullptr, 0,
true,
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";
4460 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size()))
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); };
4495 data_sink.done_with_trailer = [&](
const Headers& trailer) { done_with_trailer(&trailer); };
4497 while (data_available && !is_shutting_down())
4499 if (!strm.is_writable())
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;
4520template <
typename T,
typename U>
4521inline bool write_content_chunked(Stream& strm,
const ContentProvider& content_provider,
4522 const T& is_shutting_down, U& compressor)
4524 auto error = Error::Success;
4525 return write_content_chunked(strm, content_provider, is_shutting_down, compressor, error);
4528template <
typename T>
4529inline bool redirect(T& cli, Request& req, Response& res,
const std::string& path,
4530 const std::string& location, Error& error)
4533 new_req.path = path;
4534 new_req.redirect_count_ -= 1;
4536 if (res.status == 303 && (req.method !=
"GET" && req.method !=
"HEAD"))
4538 new_req.method =
"GET";
4539 new_req.body.clear();
4540 new_req.headers.clear();
4545 auto ret = cli.send(new_req, new_res, error);
4551 if (res.location.empty())
4552 res.location = location;
4557inline std::string params_to_query_str(
const Params& params)
4561 for (
auto it = params.begin(); it != params.end(); ++it)
4563 if (it != params.begin())
4569 query += encode_query_param(it->second);
4574inline void parse_query_text(
const std::string& s, Params& params)
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));
4609inline bool parse_multipart_boundary(
const std::string& content_type, std::string& boundary)
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);
4619 boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
4620 return !boundary.empty();
4623inline void parse_disposition_params(
const std::string& s, Params& params)
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
4659inline bool parse_range_header(
const std::string& s, Ranges& ranges)
4662inline bool parse_range_header(
const std::string& s, Ranges& ranges)
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
4715class MultipartFormDataParser
4718 MultipartFormDataParser() =
default;
4720 void set_boundary(std::string&& boundary)
4722 boundary_ = boundary;
4723 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4724 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4727 bool is_valid()
const {
return is_valid_; }
4729 bool parse(
const char* buf,
size_t n,
const ContentReceiver& content_callback,
4730 const MultipartContentHeader& header_callback)
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))
4797 parse_disposition_params(m[1], params);
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))
4826 file_.filename = decode_url(m2[1],
false);
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;
5045inline std::string to_lower(
const char* beg,
const char* end)
5051 out +=
static_cast<char>(::tolower(*it));
5057inline std::string make_multipart_data_boundary()
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)]; }
5077inline bool is_multipart_boundary_chars_valid(
const std::string& boundary)
5080 for (
size_t i = 0; i < boundary.size(); i++)
5082 auto c = boundary[i];
5083 if (!std::isalnum(c) && c !=
'-' && c !=
'_')
5092template <
typename T>
5093inline std::string serialize_multipart_formdata_item_begin(
const T& item,
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";
5112inline std::string serialize_multipart_formdata_item_end() {
return "\r\n"; }
5114inline std::string serialize_multipart_formdata_finish(
const std::string& boundary)
5116 return "--" + boundary +
"--\r\n";
5119inline std::string serialize_multipart_formdata_get_content_type(
const std::string& boundary)
5121 return "multipart/form-data; boundary=" + boundary;
5124inline std::string serialize_multipart_formdata(
const MultipartFormDataItems& items,
5125 const std::string& boundary,
bool finish =
true)
5129 for (
const auto& item : items)
5131 body += serialize_multipart_formdata_item_begin(item, boundary);
5132 body += item.content + serialize_multipart_formdata_item_end();
5136 body += serialize_multipart_formdata_finish(boundary);
5141inline std::pair<size_t, size_t> get_range_offset_and_length(
const Request& req,
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);
5166inline std::string make_content_range_header_field(
const std::pair<ssize_t, ssize_t>& range,
5167 size_t content_length)
5169 std::string
field =
"bytes ";
5170 if (range.first != -1)
5172 field += std::to_string(range.first);
5175 if (range.second != -1)
5177 field += std::to_string(range.second);
5180 field += std::to_string(content_length);
5184template <
typename SToken,
typename CToken,
typename Content>
5185bool process_multipart_ranges_data(
const Request& req, Response& res,
const std::string& boundary,
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];
5203 stoken(make_content_range_header_field(range, res.content_length_));
5207 auto offsets = get_range_offset_and_length(req, res.content_length_, i);
5208 auto offset = offsets.first;
5209 auto length = offsets.second;
5210 if (!content(offset, length))
5224inline bool make_multipart_ranges_data(
const Request& req, Response& res,
5225 const std::string& boundary,
const std::string& content_type,
5228 return process_multipart_ranges_data(
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);
5242inline size_t get_multipart_ranges_data_length(
const Request& req, Response& res,
5243 const std::string& boundary,
5244 const std::string& content_type)
5246 size_t data_length = 0;
5248 process_multipart_ranges_data(
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;
5261template <
typename T>
5262inline bool write_multipart_ranges_data(Stream& strm,
const Request& req, Response& res,
5263 const std::string& boundary,
5264 const std::string& content_type,
const T& is_shutting_down)
5266 return process_multipart_ranges_data(
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)
5270 {
return write_content(strm, res.content_provider_, offset, length, is_shutting_down); });
5273inline std::pair<size_t, size_t> get_range_offset_and_length(
const Request& req,
5274 const Response& res,
size_t index)
5276 auto r = req.ranges[index];
5280 r.second =
static_cast<ssize_t
>(res.content_length_) - 1;
5283 return std::make_pair(r.first, r.second - r.first + 1);
5286inline bool expect_content(
const Request& req)
5288 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" ||
5289 req.method ==
"PRI" || req.method ==
"DELETE")
5297inline bool has_crlf(
const std::string& s)
5302 if (*p ==
'\r' || *p ==
'\n')
5311#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5312inline 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]);
5333inline std::string MD5(
const std::string& s) {
return message_digest(s, EVP_md5()); }
5335inline std::string SHA_256(
const std::string& s) {
return message_digest(s, EVP_sha256()); }
5337inline std::string SHA_512(
const std::string& s) {
return message_digest(s, EVP_sha512()); }
5340#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5344inline 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__)
5374template <
typename T>
5375using CFObjectPtr = std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
5377inline void cf_object_ptr_deleter(CFTypeRef obj)
5385inline 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));
5412inline 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);
5424inline 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);
5461inline 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;
5488 if (WSAStartup(0x0002, &wsaData) == 0)
5498 bool is_valid_ =
false;
5501static WSInit wsinit_;
5504#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5505inline 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);
5580inline bool parse_www_authenticate(
const Response& res, std::map<std::string, std::string>& auth,
5583 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
5584 if (res.has_header(auth_key))
5586 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5587 auto s = res.get_header_value(auth_key);
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)));
5619inline std::string random_string(
size_t length)
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);
5634class ContentProviderAdapter
5637 explicit ContentProviderAdapter(ContentProviderWithoutLength&& content_provider)
5638 : content_provider_(content_provider)
5642 bool operator()(
size_t offset,
size_t, DataSink& sink)
5644 return content_provider_(offset, sink);
5655 std::vector<std::string> addrs;
5659 return std::string();
5664inline void hosted_at(
const std::string& hostname, std::vector<std::string>& addrs)
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);
5687 if (detail::get_ip_and_port(addr,
sizeof(
struct sockaddr_storage), ip, dummy))
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) ?
'&' :
'?';
5701 path_with_query += delm + detail::params_to_query_str(params);
5702 return path_with_query;
5708 std::string field =
"bytes=";
5710 for (
auto r : ranges)
5718 field += std::to_string(r.first);
5723 field += std::to_string(r.second);
5727 return std::make_pair(
"Range", std::move(field));
5730inline std::pair<std::string, std::string>
5734 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
5735 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5736 return std::make_pair(key, std::move(field));
5739inline 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));
5748inline bool Request::has_header(
const std::string& key)
const
5750 return detail::has_header(headers, key);
5753inline std::string Request::get_header_value(
const std::string& key,
size_t id)
const
5755 return detail::get_header_value(headers, key,
id,
"");
5758inline 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));
5764inline void Request::set_header(
const std::string& key,
const std::string& val)
5766 if (!detail::has_crlf(key) && !detail::has_crlf(val))
5768 headers.emplace(key, val);
5772inline bool Request::has_param(
const std::string& key)
const
5774 return params.find(key) != params.end();
5777inline 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();
5789inline 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));
5795inline bool Request::is_multipart_form_data()
const
5797 const auto& content_type = get_header_value(
"Content-Type");
5798 return !content_type.rfind(
"multipart/form-data", 0);
5801inline 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())
5816inline 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); }
5825inline bool Response::has_header(
const std::string& key)
const
5827 return headers.find(key) != headers.end();
5830inline std::string Response::get_header_value(
const std::string& key,
size_t id)
const
5832 return detail::get_header_value(headers, key,
id,
"");
5835inline 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));
5841inline void Response::set_header(
const std::string& key,
const std::string& val)
5843 if (!detail::has_crlf(key) && !detail::has_crlf(val))
5845 headers.emplace(key, val);
5849inline void Response::set_redirect(
const std::string& url,
int stat)
5851 if (!detail::has_crlf(url))
5853 set_header(
"Location", url);
5854 if (300 <= stat && stat < 400)
5856 this->status = stat;
5865inline 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);
5874inline void Response::set_content(
const std::string& s,
const std::string& content_type)
5876 set_content(s.data(), s.size(), content_type);
5879inline 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;
5893inline void Response::set_content_provider(
const std::string& content_type,
5897 set_header(
"Content-Type", content_type);
5898 content_length_ = 0;
5899 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5900 content_provider_resource_releaser_ = resource_releaser;
5901 is_chunked_content_provider_ =
false;
5905Response::set_chunked_content_provider(
const std::string& content_type,
5909 set_header(
"Content-Type", content_type);
5910 content_length_ = 0;
5911 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5912 content_provider_resource_releaser_ = resource_releaser;
5913 is_chunked_content_provider_ =
true;
5917inline bool Result::has_request_header(
const std::string& key)
const
5919 return request_headers_.find(key) != request_headers_.end();
5922inline std::string Result::get_request_header_value(
const std::string& key,
size_t id)
const
5924 return detail::get_header_value(request_headers_, key,
id,
"");
5927inline 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));
5934inline ssize_t Stream::write(
const char* ptr) {
return write(ptr, strlen(ptr)); }
5936inline ssize_t Stream::write(
const std::string& s) {
return write(s.data(), s.size()); }
5942inline 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)
5953inline SocketStream::~SocketStream() {}
5955inline bool SocketStream::is_readable()
const
5957 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
5960inline bool SocketStream::is_writable()
const
5962 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
5963 is_socket_alive(sock_);
5966inline ssize_t SocketStream::read(
char* ptr,
size_t size)
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);
6025inline ssize_t SocketStream::write(
const char* ptr,
size_t size)
6032#if defined(_WIN32) && !defined(_WIN64)
6033 size = (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
6039inline void SocketStream::get_remote_ip_and_port(std::string& ip,
int& port)
const
6041 return detail::get_remote_ip_and_port(sock_, ip, port);
6044inline void SocketStream::get_local_ip_and_port(std::string& ip,
int& port)
const
6046 return detail::get_local_ip_and_port(sock_, ip, port);
6049inline socket_t SocketStream::socket()
const {
return sock_; }
6052inline bool BufferStream::is_readable()
const {
return true; }
6054inline bool BufferStream::is_writable()
const {
return true; }
6056inline ssize_t BufferStream::read(
char* ptr,
size_t size)
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);
6067inline ssize_t BufferStream::write(
const char* ptr,
size_t size)
6069 buffer.append(ptr, size);
6070 return static_cast<ssize_t
>(size);
6073inline void BufferStream::get_remote_ip_and_port(std::string& ,
int& )
const {}
6075inline void BufferStream::get_local_ip_and_port(std::string& ,
int& )
const {}
6077inline socket_t BufferStream::socket()
const {
return 0; }
6079inline const std::string& BufferStream::get_buffer()
const {
return buffer; }
6081inline PathParamsMatcher::PathParamsMatcher(
const std::string& pattern)
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));
6134inline bool PathParamsMatcher::match(
Request& request)
const
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();
6187inline bool RegexMatcher::match(
Request& request)
const
6190 return std::regex_match(request.
path, request.
matches, regex_);
6196inline Server::Server()
6200 signal(SIGPIPE, SIG_IGN);
6206inline 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)));
6290 if (detail::is_dir(dir))
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}); }
6479 detail::shutdown_socket(sock);
6480 detail::close_socket(sock);
6484inline 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')
6496 detail::split(s, s + len,
' ',
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);
6571inline bool Server::write_response(Stream& strm,
bool close_connection,
const Request& req,
6574 return write_response_core(strm, close_connection, req, res,
false);
6577inline 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);
6583inline 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)))
6643 if (!detail::write_headers(bstrm, res.headers))
6649 auto& data = bstrm.get_buffer();
6650 detail::write_data(strm, data.data(), data.size());
6655 if (req.method !=
"HEAD")
6657 if (!res.body.empty())
6659 if (!detail::write_data(strm, res.body.data(), res.body.size()))
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;
6687inline 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())
6697 return detail::write_content(strm, res.content_provider_, 0, res.content_length_,
6700 else if (req.ranges.size() == 1)
6702 auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0);
6703 auto offset = offsets.first;
6704 auto length = offsets.second;
6705 return detail::write_content(strm, res.content_provider_, offset, length,
6710 return detail::write_multipart_ranges_data(strm, req, res, boundary, content_type,
6716 if (res.is_chunked_content_provider_)
6718 auto type = detail::encoding_type(req, res);
6720 std::unique_ptr<detail::compressor> compressor;
6721 if (type == detail::EncodingType::Gzip)
6723#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6724 compressor = detail::make_unique<detail::gzip_compressor>();
6727 else if (type == detail::EncodingType::Brotli)
6729#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6730 compressor = detail::make_unique<detail::brotli_compressor>();
6735 compressor = detail::make_unique<detail::nocompressor>();
6737 assert(compressor !=
nullptr);
6739 return detail::write_content_chunked(strm, res.content_provider_, is_shutting_down,
6744 return detail::write_content_without_length(strm, res.content_provider_,
6750inline 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"))
6795 detail::parse_query_text(req.body, req.params);
6802inline 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));
6811inline 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;
6823 if (!detail::parse_multipart_boundary(content_type, 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"))
6857 if (!detail::read_content(strm, req,
payload_max_length_, res.status,
nullptr, out,
true))
6862 if (req.is_multipart_form_data())
6864 if (!multipart_form_data_parser.is_valid())
6874inline 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());
6882 if (detail::is_valid_path(sub_path))
6884 auto path = entry.base_dir + sub_path;
6885 if (path.back() ==
'/')
6887 path +=
"index.html";
6890 if (detail::is_file(path))
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(
6905 detail::find_content_type(path, file_extension_and_mimetype_map_,
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);
6926inline socket_t Server::create_server_socket(
const std::string& host,
int port,
int socket_flags,
6929 return detail::create_socket(
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)))
6946inline 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);
6986inline 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();
7075inline 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))
7089 if (detail::expect_content(req))
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_))
7140 if (!read_content(strm, req, res))
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_);
7176inline 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))
7192inline void Server::apply_ranges(
const Request& req, Response& res, std::string& content_type,
7193 std::string& boundary)
7195 if (req.ranges.size() > 1)
7197 boundary = detail::make_multipart_data_boundary();
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);
7209 auto type = detail::encoding_type(req, res);
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)
7222 auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0);
7223 length = offsets.second;
7225 auto content_range =
7226 detail::make_content_range_header_field(req.ranges[0], res.content_length_);
7227 res.set_header(
"Content-Range", content_range);
7231 length = detail::get_multipart_ranges_data_length(req, res, boundary, content_type);
7233 res.set_header(
"Content-Length", std::to_string(length));
7237 if (res.content_provider_)
7239 if (res.is_chunked_content_provider_)
7241 res.set_header(
"Transfer-Encoding",
"chunked");
7242 if (type == detail::EncodingType::Gzip)
7244 res.set_header(
"Content-Encoding",
"gzip");
7246 else if (type == detail::EncodingType::Brotli)
7248 res.set_header(
"Content-Encoding",
"br");
7256 if (req.ranges.empty())
7260 else if (req.ranges.size() == 1)
7262 auto content_range =
7263 detail::make_content_range_header_field(req.ranges[0], res.body.size());
7264 res.set_header(
"Content-Range", content_range);
7266 auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0);
7267 auto offset = offsets.first;
7268 auto length = offsets.second;
7270 if (offset < res.body.size())
7272 res.body = res.body.substr(offset, length);
7283 if (detail::make_multipart_ranges_data(req, res, boundary, content_type, data))
7285 res.body.swap(data);
7294 if (type != detail::EncodingType::None)
7296 std::unique_ptr<detail::compressor> compressor;
7297 std::string content_encoding;
7299 if (type == detail::EncodingType::Gzip)
7301#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7302 compressor = detail::make_unique<detail::gzip_compressor>();
7303 content_encoding =
"gzip";
7306 else if (type == detail::EncodingType::Brotli)
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);
7330 auto length = std::to_string(res.body.size());
7331 res.set_header(
"Content-Length", length);
7335inline 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{};
7358 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7361 if (!line_reader.getline())
7370 res.
headers = default_headers_;
7375#ifndef CPPHTTPLIB_USE_POLL
7377 if (strm.
socket() >= FD_SETSIZE)
7380 detail::read_headers(strm, dummy);
7382 return write_response(strm, close_connection, req, res);
7391 detail::read_headers(strm, dummy);
7393 return write_response(strm, close_connection, req, res);
7397 if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.
headers))
7400 return write_response(strm, close_connection, req, res);
7405 connection_closed =
true;
7410 connection_closed =
true;
7424 if (!detail::parse_range_header(range_header_value, req.
ranges))
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);
7523inline bool Server::process_and_close_socket(
socket_t sock)
7525 auto ret = detail::process_server_socket(
7528 [
this](
Stream& strm,
bool close_connection,
bool& connection_closed)
7529 {
return process_request(strm, close_connection, connection_closed,
nullptr); });
7531 detail::shutdown_socket(sock);
7532 detail::close_socket(sock);
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_;
7612inline socket_t ClientImpl::create_client_socket(
Error& error)
const
7616 return detail::create_client_socket(
7628 return detail::create_client_socket(
7636 auto sock = create_client_socket(error);
7659 detail::shutdown_socket(
socket.sock);
7674#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7675 assert(
socket.ssl ==
nullptr);
7681 detail::close_socket(
socket.sock);
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)};
7872inline 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"))
7922 ret = redirect(req, res, error);
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;
7935 if (detail::parse_www_authenticate(res, auth, is_proxy))
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(
7941 req, auth, new_req.authorization_count_, detail::random_string(10), username,
7942 password, is_proxy));
7946 ret =
send(new_req, new_res, error);
7959inline bool ClientImpl::redirect(Request& req, Response& res,
Error& 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())
8017 auto path = detail::decode_url(next_path,
true) + next_query;
8019 if (next_scheme == scheme && next_host ==
host_ && next_port ==
port_)
8021 return detail::redirect(*
this, req, res, path, location, error);
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_);
8034 return detail::redirect(cli, req, res, path, location, error);
8041 ClientImpl cli(next_host.c_str(), next_port);
8042 cli.copy_settings(*
this);
8043 return detail::redirect(cli, req, res, path, location, error);
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>();
8067 return detail::write_content_chunked(strm, req.
content_provider_, is_shutting_down,
8068 *compressor, error);
8073 is_shutting_down, error);
8077inline 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");
8158 auto length = std::to_string(req.
body.size());
8201 detail::BufferStream bstrm;
8203 const auto& path =
url_encode_ ? detail::encode_url(req.
path) : req.path;
8204 bstrm.write_format(
"%s %s HTTP/1.1\r\n", req.
method.c_str(), path.c_str());
8206 detail::write_headers(bstrm, req.
headers);
8209 auto& data = bstrm.get_buffer();
8210 if (!detail::write_data(strm, data.data(), data.size()))
8218 if (req.
body.empty())
8223 if (!detail::write_data(strm, req.
body.data(), req.
body.size()))
8232inline std::unique_ptr<Response>
8233ClientImpl::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;
8338inline 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)};
8357inline 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)
8393 if (!read_response_line(strm, req, res) || !detail::read_headers(strm, res.
headers))
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);
8454 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), dummy_status,
8455 std::move(progress), std::move(out),
decompress_))
8475ClientImpl::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())
8486 sink.os << detail::serialize_multipart_formdata(items, boundary,
false);
8489 else if (cur_item < provider_items.size())
8493 const auto& begin = detail::serialize_multipart_formdata_item_begin(
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))
8510 sink.os << detail::serialize_multipart_formdata_item_end();
8518 sink.os << detail::serialize_multipart_formdata_finish(boundary);
8525inline bool ClientImpl::process_socket(
const Socket&
socket,
8526 std::function<
bool(Stream& strm)> callback)
8530 std::move(callback));
8533inline 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);
8733 auto query = detail::params_to_query_str(params);
8734 return Post(path, headers, query,
"application/x-www-form-urlencoded");
8745 const auto& boundary = detail::make_multipart_data_boundary();
8746 const auto& content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8747 const auto& body = detail::serialize_multipart_formdata(items, boundary);
8748 return Post(path, headers, body, content_type.c_str());
8754 if (!detail::is_multipart_boundary_chars_valid(boundary))
8759 const auto& content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8760 const auto& body = detail::serialize_multipart_formdata(items, boundary);
8761 return Post(path, headers, body, content_type.c_str());
8768 const auto& boundary = detail::make_multipart_data_boundary();
8769 const auto& content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
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);
8842 auto query = detail::params_to_query_str(params);
8843 return Put(path, headers, query,
"application/x-www-form-urlencoded");
8854 const auto& boundary = detail::make_multipart_data_boundary();
8855 const auto& content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8856 const auto& body = detail::serialize_multipart_formdata(items, boundary);
8857 return Put(path, headers, body, content_type);
8863 if (!detail::is_multipart_boundary_chars_valid(boundary))
8868 const auto& content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8869 const auto& body = detail::serialize_multipart_formdata(items, boundary);
8870 return Put(path, headers, body, content_type);
8877 const auto& boundary = detail::make_multipart_data_boundary();
8878 const auto& content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
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
9067inline 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
9124inline 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;
9131inline 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;
9138inline 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;
9146inline 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);
9186inline void ClientImpl::enable_server_certificate_verification(
bool enabled)
9188 server_certificate_verification_ = enabled;
9197#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9201template <
typename U,
typename V>
9202inline 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);
9213 set_nonblocking(sock,
true);
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);
9225 set_nonblocking(sock,
false);
9228 BIO_set_nbio(bio, 0);
9229 set_nonblocking(sock,
false);
9235inline 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);
9250template <
typename U>
9251bool 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:
9267 if (select_write(sock, timeout_sec, timeout_usec) > 0)
9279template <
typename T>
9280inline 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)
9286 return process_server_socket_core(
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);
9296template <
typename T>
9297inline 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);
9316inline 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)
9321 read_timeout_sec_(read_timeout_sec),
9322 read_timeout_usec_(read_timeout_usec),
9323 write_timeout_sec_(write_timeout_sec),
9324 write_timeout_usec_(write_timeout_usec)
9326 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
9329inline SSLSocketStream::~SSLSocketStream() {}
9331inline bool SSLSocketStream::is_readable()
const
9333 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
9336inline bool SSLSocketStream::is_writable()
const
9338 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9339 is_socket_alive(sock_);
9342inline 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);
9388inline 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);
9429inline void SSLSocketStream::get_remote_ip_and_port(std::string& ip,
int& port)
const
9431 detail::get_remote_ip_and_port(sock_, ip, port);
9434inline void SSLSocketStream::get_local_ip_and_port(std::string& ip,
int& port)
const
9436 detail::get_local_ip_and_port(sock_, ip, port);
9439inline socket_t SSLSocketStream::socket()
const {
return sock_; }
9441static SSLInit sslinit_;
9446inline 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);
9481inline 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);
9507inline 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_))
9520inline SSLServer::~SSLServer()
9528inline bool SSLServer::is_valid()
const {
return ctx_; }
9530inline SSL_CTX* SSLServer::ssl_context()
const {
return ctx_; }
9532inline 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);
9561 detail::shutdown_socket(sock);
9562 detail::close_socket(sock);
9567inline SSLClient::SSLClient(
const std::string& host)
9572inline SSLClient::SSLClient(
const std::string& host,
int port)
9577inline 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)
9598inline 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)
9619inline SSLClient::~SSLClient()
9628 shutdown_ssl_impl(socket_,
true);
9631inline bool SSLClient::is_valid()
const {
return ctx_; }
9633inline 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);
9652inline 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));
9657inline long SSLClient::get_openssl_verify_result()
const {
return verify_result_; }
9659inline SSL_CTX* SSLClient::ssl_context()
const {
return ctx_; }
9661inline bool SSLClient::create_and_connect_socket(Socket& socket,
Error& error)
9667inline bool SSLClient::connect_with_proxy(Socket& socket, Response& res,
bool& success,
9672 if (!detail::process_client_socket(socket.sock, read_timeout_sec_, read_timeout_usec_,
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);
9686 shutdown_socket(socket);
9687 close_socket(socket);
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;
9697 if (detail::parse_www_authenticate(proxy_res, auth,
true))
9699 proxy_res = Response();
9700 if (!detail::process_client_socket(
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);
9717 shutdown_socket(socket);
9718 close_socket(socket);
9729 if (proxy_res.status != 200)
9732 res = std::move(proxy_res);
9735 shutdown_ssl(socket,
true);
9736 shutdown_socket(socket);
9737 close_socket(socket);
9744inline 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_));
9779 SSL_CTX_set_default_verify_paths(ctx_);
9787inline 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());
9856 shutdown_socket(socket);
9857 close_socket(socket);
9861inline void SSLClient::shutdown_ssl(Socket& socket,
bool shutdown_gracefully)
9863 shutdown_ssl_impl(socket, shutdown_gracefully);
9866inline 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);
9881inline 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));
9890inline bool SSLClient::is_ssl()
const {
return true; }
9892inline 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);
9919inline 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)));
9985inline 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));
10003inline 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;
10013 detail::split(&pattern[0], &pattern[pattern_len],
'.',
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())
10048inline 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
10059 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https"))
10062 if (!scheme.empty() && scheme !=
"http")
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,
10107inline Client::Client(
const std::string& host,
int port,
const std::string& client_cert_path,
10108 const std::string& client_key_path)
10109 : cli_(
detail::make_unique<
ClientImpl>(host, port, client_cert_path, 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
10469inline 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
10496inline 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
10503inline void Client::enable_server_certificate_verification(
bool enabled)
10505 cli_->enable_server_certificate_verification(enabled);
10511#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10512inline 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);
10518inline 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);
10530inline 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));
10535inline long Client::get_openssl_verify_result()
const
10539 return static_cast<SSLClient&
>(*cli_).get_openssl_verify_result();
10544inline SSL_CTX* Client::ssl_context()
const
10548 return static_cast<SSLClient&
>(*cli_).ssl_context();