530template <
class T,
class... Args>
531typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
533 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
537typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
539 typedef typename std::remove_extent<T>::type RT;
540 return std::unique_ptr<T>(
new RT[n]);
546 const static unsigned char table[256] = {
547 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
548 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
549 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
550 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
551 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
552 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
553 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
554 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
555 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
556 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
557 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
558 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
559 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
560 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
561 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
562 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
563 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
566 return table[(
unsigned char)(
char)c];
570 std::string result = s;
572 result.begin(), result.end(), result.begin(),
573 [](
unsigned char c) { return static_cast<char>(to_lower(c)); });
577inline bool equal(
const std::string &a,
const std::string &b) {
578 return a.size() == b.size() &&
579 std::equal(a.begin(), a.end(), b.begin(), [](
char ca,
char cb) {
580 return to_lower(ca) == to_lower(cb);
585 bool operator()(
const std::string &a,
const std::string &b)
const {
592 return hash_core(key.data(), key.size(), 0);
595 size_t hash_core(
const char *s,
size_t l,
size_t h)
const {
600 (((std::numeric_limits<size_t>::max)() >> 6) &
602 static_cast<unsigned char>(
to_lower(*s)));
617 : exit_function(
std::move(
f)), execute_on_destruction{true} {}
620 : exit_function(std::move(rhs.exit_function)),
621 execute_on_destruction{rhs.execute_on_destruction} {
626 if (execute_on_destruction) { this->exit_function(); }
629 void release() { this->execute_on_destruction =
false; }
636 std::function<void(
void)> exit_function;
637 bool execute_on_destruction;
649 T &value,
int base = 10) {
651 const char *p = first;
652 bool negative =
false;
654 if (p != last && *p ==
'-') {
658 if (p == last) {
return {first, std::errc::invalid_argument}; }
661 for (; p != last; ++p) {
664 if (
'0' <= c && c <=
'9') {
666 }
else if (
'a' <= c && c <=
'z') {
667 digit = c -
'a' + 10;
668 }
else if (
'A' <= c && c <=
'Z') {
669 digit = c -
'A' + 10;
674 if (digit < 0 || digit >= base) {
break; }
675 if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
676 return {p, std::errc::result_out_of_range};
678 result = result * base + digit;
681 if (p == first || (negative && p == first + 1)) {
682 return {first, std::errc::invalid_argument};
685 value = negative ? -result : result;
686 return {p, std::errc{}};
692 std::string s(first, last);
693 char *endptr =
nullptr;
695 value = std::strtod(s.c_str(), &endptr);
696 if (endptr == s.c_str()) {
return {first, std::errc::invalid_argument}; }
697 if (errno == ERANGE) {
698 return {first + (endptr - s.c_str()), std::errc::result_out_of_range};
700 return {first + (endptr - s.c_str()), std::errc{}};
703inline bool parse_port(
const char *s,
size_t len,
int &port) {
706 if (r.ec != std::errc{} || val < 1 || val > 65535) {
return false; }
727 auto sep =
url.find(
"://");
728 if (sep != std::string::npos) {
732 if (uc.
scheme.empty()) {
return false; }
733 for (
auto c : uc.
scheme) {
734 if (c <
'a' || c >
'z') {
return false; }
738 }
else if (
url.compare(0, 2,
"//") == 0) {
742 auto has_authority_prefix = pos > 0;
743 auto has_authority = has_authority_prefix || (!
url.empty() &&
url[0] !=
'/' &&
744 url[0] !=
'?' &&
url[0] !=
'#');
746 if (pos <
url.size() &&
url[pos] ==
'[') {
747 auto close =
url.find(
']', pos);
748 if (close == std::string::npos) {
return false; }
749 uc.
host =
url.substr(pos + 1, close - pos - 1);
752 if (uc.
host.empty()) {
return false; }
753 for (
auto c : uc.
host) {
754 if (!((c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F') ||
755 (c >=
'0' && c <=
'9') || c ==
':')) {
762 auto end =
url.find_first_of(
":/?#", pos);
763 if (end == std::string::npos) { end =
url.size(); }
764 uc.
host =
url.substr(pos, end - pos);
768 if (pos <
url.size() &&
url[pos] ==
':') {
770 auto end =
url.find_first_of(
"/?#", pos);
771 if (end == std::string::npos) { end =
url.size(); }
772 uc.
port =
url.substr(pos, end - pos);
779 if (!has_authority_prefix && pos <
url.size()) {
786 if (pos <
url.size() &&
url[pos] !=
'?' &&
url[pos] !=
'#') {
787 auto end =
url.find_first_of(
"?#", pos);
788 if (end == std::string::npos) { end =
url.size(); }
789 uc.
path =
url.substr(pos, end - pos);
793 if (pos <
url.size() &&
url[pos] ==
'?') {
794 auto end =
url.find(
'#', pos);
795 if (end == std::string::npos) { end =
url.size(); }
796 uc.
query =
url.substr(pos, end - pos);
892using Params = std::multimap<std::string, std::string>;
908 static const char id = 0;
914 virtual std::unique_ptr<any_storage>
clone()
const = 0;
921 std::unique_ptr<any_storage>
clone()
const override {
936 for (
const auto &
e : o.entries_) {
937 if (e.second) { entries_[e.first] = e.second->clone(); }
944 for (
const auto &
e : o.entries_) {
945 if (
e.second) { entries_[
e.first] =
e.second->clone(); }
951 template <
typename T>
void set(
const std::string &key,
T &&value) {
952 using D =
typename std::decay<T>::type;
956 template <
typename T>
T *
get(
const std::string &key)
noexcept {
957 auto it = entries_.find(key);
958 if (it == entries_.end() || !it->second) {
return nullptr; }
963 template <
typename T>
const T *
get(
const std::string &key)
const noexcept {
964 auto it = entries_.find(key);
965 if (it == entries_.end() || !it->second) {
return nullptr; }
970 bool has(
const std::string &key)
const noexcept {
971 return entries_.find(key) != entries_.end();
974 void erase(
const std::string &key) { entries_.erase(key); }
976 void clear() noexcept { entries_.clear(); }
979 std::unordered_map<std::string, std::unique_ptr<detail::any_storage>>
1008 std::string
get_field(
const std::string &key,
size_t id = 0)
const;
1009 std::vector<std::string>
get_fields(
const std::string &key)
const;
1010 bool has_field(
const std::string &key)
const;
1015 std::vector<FormData>
get_files(
const std::string &key)
const;
1016 bool has_file(
const std::string &key)
const;
1037 std::function<bool(
const char *
data,
size_t data_len)>
write;
1044 class data_sink_streambuf final :
public std::streambuf {
1046 explicit data_sink_streambuf(
DataSink &sink) : sink_(sink) {}
1049 std::streamsize xsputn(
const char *s, std::streamsize n)
override {
1050 if (sink_.
write(s,
static_cast<size_t>(n))) {
return n; }
1058 data_sink_streambuf sb_;
1062 std::function<bool(
size_t offset,
size_t length,
DataSink &sink)>;
1065 std::function<bool(
size_t offset,
DataSink &sink)>;
1079 const std::string &filename = std::string(),
1080 const std::string &content_type = std::string()) {
1083 fdp.
filename = filename.empty() ? filepath : filename;
1086 std::ifstream
f(filepath, std::ios::binary);
1087 if (!
f) {
return false; }
1089 f.seekg(
static_cast<std::streamoff
>(offset));
1096 f.read(buf,
sizeof(buf));
1097 auto n =
static_cast<size_t>(
f.gcount());
1098 if (n > 0) {
return sink.write(buf, n); }
1105inline std::pair<size_t, ContentProvider>
1109 std::ifstream
f(filepath, std::ios::binary | std::ios::ate);
1111 size =
static_cast<size_t>(
f.tellg());
1116 std::ifstream
f(filepath, std::ios::binary);
1117 if (!
f) {
return false; }
1118 f.seekg(
static_cast<std::streamoff
>(offset));
1119 if (!
f.good()) {
return false; }
1121 while (length > 0) {
1122 auto to_read = (std::min)(
sizeof(buf), length);
1123 f.read(buf,
static_cast<std::streamsize
>(to_read));
1124 auto n =
static_cast<size_t>(
f.gcount());
1125 if (n == 0) {
break; }
1126 if (!sink.write(buf, n)) {
return false; }
1131 return {
size, std::move(provider)};
1135 const char *
data,
size_t data_length,
size_t offset,
size_t total_length)>;
1138 std::function<bool(
const char *
data,
size_t data_length)>;
1157 return reader_(std::move(receiver));
1164using Range = std::pair<ssize_t, ssize_t>;
1167#ifdef CPPHTTPLIB_SSL_ENABLED
1172using ctx_t =
void *;
1173using session_t =
void *;
1174using const_session_t =
const void *;
1175using cert_t =
void *;
1176using ca_store_t =
void *;
1185enum class SanType { DNS, IP, EMAIL, URI, OTHER };
1194struct VerifyContext {
1200 const char *error_string;
1203 std::string subject_cn()
const;
1204 std::string issuer_name()
const;
1205 bool check_hostname(
const char *hostname)
const;
1206 std::vector<SanEntry> sans()
const;
1207 bool validity(time_t ¬_before, time_t ¬_after)
const;
1208 std::string serial()
const;
1211using VerifyCallback = std::function<bool(
const VerifyContext &ctx)>;
1214enum class ErrorCode :
int {
1227 ErrorCode
code = ErrorCode::Fatal;
1228 uint64_t backend_code = 0;
1232 static std::string verify_error_to_string(
long error_code);
1239 PeerCert(PeerCert &&other)
noexcept;
1240 PeerCert &operator=(PeerCert &&other)
noexcept;
1243 PeerCert(
const PeerCert &) =
delete;
1244 PeerCert &operator=(
const PeerCert &) =
delete;
1246 explicit operator bool()
const;
1247 std::string subject_cn()
const;
1248 std::string issuer_name()
const;
1249 bool check_hostname(
const char *hostname)
const;
1250 std::vector<SanEntry> sans()
const;
1251 bool validity(time_t ¬_before, time_t ¬_after)
const;
1252 std::string serial()
const;
1255 explicit PeerCert(cert_t cert);
1256 cert_t cert_ =
nullptr;
1257 friend PeerCert get_peer_cert_from_session(const_session_t session);
1261using ContextSetupCallback = std::function<bool(ctx_t ctx)>;
1296 bool has_header(
const std::string &key)
const;
1298 size_t id = 0)
const;
1300 size_t id = 0)
const;
1302 void set_header(
const std::string &key,
const std::string &val);
1308 bool has_param(
const std::string &key)
const;
1309 std::string
get_param_value(
const std::string &key,
size_t id = 0)
const;
1323 (std::chrono::steady_clock::time_point::min)();
1325#ifdef CPPHTTPLIB_SSL_ENABLED
1326 tls::const_session_t ssl =
nullptr;
1327 tls::PeerCert peer_cert()
const;
1328 std::string sni()
const;
1345 bool has_header(
const std::string &key)
const;
1347 size_t id = 0)
const;
1349 size_t id = 0)
const;
1351 void set_header(
const std::string &key,
const std::string &val);
1358 void set_content(
const char *s,
size_t n,
const std::string &content_type);
1359 void set_content(
const std::string &s,
const std::string &content_type);
1360 void set_content(std::string &&s,
const std::string &content_type);
1363 size_t length,
const std::string &content_type,
ContentProvider provider,
1375 const std::string &content_type);
1465 ssize_t
write(
const char *ptr);
1466 ssize_t
write(
const std::string &s);
1479 virtual bool enqueue(std::function<
void()> fn) = 0;
1487 explicit ThreadPool(
size_t n,
size_t max_n = 0,
size_t mqr = 0);
1491 bool enqueue(std::function<
void()> fn)
override;
1495 void worker(
bool is_dynamic);
1496 void move_to_finished(std::thread::id
id);
1497 void cleanup_finished_threads();
1499 size_t base_thread_count_;
1500 size_t max_thread_count_;
1501 size_t max_queued_requests_;
1502 size_t idle_thread_count_;
1506 std::list<std::function<void()>> jobs_;
1507 std::vector<std::thread> threads_;
1508 std::list<std::thread> dynamic_threads_;
1509 std::vector<std::thread>
1512 std::condition_variable cond_;
1543 const std::string &
pattern()
const {
return pattern_; }
1549 std::string pattern_;
1580 static constexpr char separator =
'/';
1585 std::vector<std::string> static_fragments_;
1588 std::vector<std::string> param_names_;
1618 const std::string &boundary);
1622 const std::string &boundary);
1631 std::function<void(
const Request &,
Response &, std::exception_ptr ep)>;
1651 std::function<std::string(
const std::vector<std::string> &protocols)>;
1675 const std::string &mount_point = std::string());
1676 bool set_mount_point(
const std::string &mount_point,
const std::string &dir,
1680 const std::string &mime);
1684 template <
class ErrorHandlerFunc>
1686 return set_error_handler_core(
1687 std::forward<ErrorHandlerFunc>(handler),
1688 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1719 template <
class Rep,
class Period>
1724 template <
class Rep,
class Period>
1728 template <
class Rep,
class Period>
1732 template <
class Rep,
class Period>
1738 template <
class Rep,
class Period>
1740 const std::chrono::duration<Rep, Period> &duration);
1744 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
1748 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
1752 void stop() noexcept;
1759 int remote_port, const
std::
string &local_addr,
1760 int local_port,
bool close_connection,
1761 bool &connection_closed,
1763 bool *websocket_upgraded =
nullptr);
1784 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
Handler>>;
1785 using HandlersForContentReader =
1786 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1789 static std::unique_ptr<detail::MatcherBase>
1790 make_matcher(
const std::string &pattern);
1792 template <
typename H>
1794 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, H>> &handlers,
1795 const std::string &pattern, H handler) {
1796 handlers.emplace_back(make_matcher(pattern), std::move(handler));
1801 Server &set_error_handler_core(Handler handler, std::false_type);
1803 socket_t create_server_socket(
const std::string &host,
int port,
1806 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1807 bool listen_internal();
1812 const std::string &etag, time_t mtime)
const;
1813 bool check_if_range(
Request &req,
const std::string &etag,
1814 time_t mtime)
const;
1816 const Handlers &handlers)
const;
1817 bool dispatch_request_for_content_reader(
1819 const HandlersForContentReader &handlers)
const;
1821 bool parse_request_line(
const char *s,
Request &req)
const;
1823 std::string &content_type, std::string &boundary)
const;
1824 bool write_response(
Stream &strm,
bool close_connection,
Request &req,
1826 bool write_response_with_content(
Stream &strm,
bool close_connection,
1828 bool write_response_core(
Stream &strm,
bool close_connection,
1830 bool need_apply_ranges);
1831 bool write_content_with_provider(
Stream &strm,
const Request &req,
1832 Response &res,
const std::string &boundary,
1833 const std::string &content_type);
1835 bool read_content_with_content_receiver(
Stream &strm,
Request &req,
1845 virtual bool process_and_close_socket(
socket_t sock);
1848 void output_pre_compression_log(
const Request &req,
1850 void output_error_log(
const Error &err,
const Request *req)
const;
1852 std::atomic<bool> is_running_{
false};
1853 std::atomic<bool> is_decommissioned{
false};
1855 struct MountPointEntry {
1856 std::string mount_point;
1857 std::string base_dir;
1858 std::string resolved_base_dir;
1861 std::vector<MountPointEntry> base_dirs_;
1862 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1863 std::string default_file_mimetype_ =
"application/octet-stream";
1864 Handler file_request_handler_;
1866 Handlers get_handlers_;
1867 Handlers post_handlers_;
1868 HandlersForContentReader post_handlers_for_content_reader_;
1869 Handlers put_handlers_;
1870 HandlersForContentReader put_handlers_for_content_reader_;
1871 Handlers patch_handlers_;
1872 HandlersForContentReader patch_handlers_for_content_reader_;
1873 Handlers delete_handlers_;
1874 HandlersForContentReader delete_handlers_for_content_reader_;
1875 Handlers options_handlers_;
1877 struct WebSocketHandlerEntry {
1878 std::unique_ptr<detail::MatcherBase> matcher;
1882 using WebSocketHandlers = std::vector<WebSocketHandlerEntry>;
1883 WebSocketHandlers websocket_handlers_;
1888 Handler post_routing_handler_;
1893 mutable std::mutex logger_mutex_;
1895 Logger pre_compression_logger_;
1898 int address_family_ = AF_UNSPEC;
1904 std::function<ssize_t(Stream &,
Headers &)> header_writer_ =
1913 : res_(
std::move(res)), err_(err),
1914 request_headers_(
std::move(request_headers)) {}
1916 operator bool()
const {
return res_ !=
nullptr; }
1917 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
1918 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1932 const char *def =
"",
1933 size_t id = 0)
const;
1935 size_t id = 0)
const;
1939 std::unique_ptr<Response> res_;
1943#ifdef CPPHTTPLIB_SSL_ENABLED
1947 : res_(
std::move(res)), err_(err),
1948 request_headers_(
std::move(request_headers)), ssl_error_(ssl_error) {}
1950 int ssl_error, uint64_t ssl_backend_error)
1951 : res_(
std::move(res)), err_(err),
1952 request_headers_(
std::move(request_headers)), ssl_error_(ssl_error),
1953 ssl_backend_error_(ssl_backend_error) {}
1955 int ssl_error()
const {
return ssl_error_; }
1956 uint64_t ssl_backend_error()
const {
return ssl_backend_error_; }
1960 uint64_t ssl_backend_error_ = 0;
1978#ifdef CPPHTTPLIB_SSL_ENABLED
1980 session(other.session)
1984#ifdef CPPHTTPLIB_SSL_ENABLED
1985 other.session =
nullptr;
1990 if (
this != &other) {
1993#ifdef CPPHTTPLIB_SSL_ENABLED
1994 session = other.session;
1995 other.session =
nullptr;
2001#ifdef CPPHTTPLIB_SSL_ENABLED
2002 tls::session_t session =
nullptr;
2008struct ChunkedDecoder;
2021 ssize_t
read(
char *buf,
size_t len);
2028 return br.
read(buf, len);
2067 const std::string &client_cert_path,
2068 const std::string &client_key_path);
2089 ssize_t
read(
char *buf,
size_t len);
2099 ssize_t read_with_decompression(
char *buf,
size_t len);
2101 std::unique_ptr<ClientConnection> connection_;
2102 std::unique_ptr<Stream> socket_stream_;
2103 Stream *stream_ =
nullptr;
2106 std::unique_ptr<detail::decompressor> decompressor_;
2107 std::string decompress_buffer_;
2108 size_t decompress_offset_ = 0;
2109 size_t decompressed_bytes_read_ = 0;
2127 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2128 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2136 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2137 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2149 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2150 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2158 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2159 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2171 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2172 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2180 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2181 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2193 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
DownloadProgress progress =
nullptr);
2197 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
DownloadProgress progress =
nullptr);
2209 const Params ¶ms = {},
2211 const std::string &body = {},
2212 const std::string &content_type = {});
2215 Result
send(
const Request &req);
2219 std::string
host()
const;
2238 template <
class Rep,
class Period>
2243 template <
class Rep,
class Period>
2247 template <
class Rep,
class Period>
2251 template <
class Rep,
class Period>
2252 void set_max_timeout(
const std::chrono::duration<Rep, Period> &duration);
2254 void set_basic_auth(
const std::string &username,
const std::string &password);
2272 const std::string &password);
2274 void set_no_proxy(
const std::vector<std::string> &patterns);
2288#ifdef CPPHTTPLIB_SSL_ENABLED
2289 tls::session_t ssl =
nullptr;
2297 std::chrono::time_point<std::chrono::steady_clock> start_time,
2405 bool skip_100_continue =
true)
const;
2406 bool write_request(
Stream &strm,
Request &req,
bool close_connection,
2409 void prepare_default_headers(
Request &r,
bool for_stream,
2410 const std::string &ct);
2412 bool create_redirect_client(
const std::string &scheme,
2414 Response &res,
const std::string &path,
2416 template <
typename ClientType>
void setup_redirect_client(ClientType &client);
2419 std::unique_ptr<Response> send_with_content_provider_and_receiver(
2420 Request &req,
const char *body,
size_t content_length,
2425 Result send_with_content_provider_and_receiver(
2426 const std::string &method,
const std::string &path,
2427 const Headers &headers,
const char *body,
size_t content_length,
2438 std::chrono::time_point<std::chrono::steady_clock> start_time,
2439 std::function<
bool(
Stream &strm)> callback);
2440 virtual bool is_ssl()
const;
2442 void transfer_socket_ownership_to_handle(
StreamHandle &handle);
2444#ifdef CPPHTTPLIB_SSL_ENABLED
2446 void set_digest_auth(
const std::string &username,
2447 const std::string &password);
2448 void set_proxy_digest_auth(
const std::string &username,
2449 const std::string &password);
2450 void set_ca_cert_path(
const std::string &ca_cert_file_path,
2451 const std::string &ca_cert_dir_path = std::string());
2452 void enable_server_certificate_verification(
bool enabled);
2453 void enable_server_hostname_verification(
bool enabled);
2456 std::string digest_auth_username_;
2457 std::string digest_auth_password_;
2458 std::string proxy_digest_auth_username_;
2459 std::string proxy_digest_auth_password_;
2460 std::string ca_cert_file_path_;
2461 std::string ca_cert_dir_path_;
2462 bool server_certificate_verification_ =
true;
2463 bool server_hostname_verification_ =
true;
2464 std::string ca_cert_pem_;
2465 int last_ssl_error_ = 0;
2466 uint64_t last_backend_error_ = 0;
2473 explicit Client(
const std::string &scheme_host_port);
2475 explicit Client(
const std::string &scheme_host_port,
2476 const std::string &client_cert_path,
2477 const std::string &client_key_path);
2483 const std::string &client_cert_path,
2484 const std::string &client_key_path);
2508 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2509 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2517 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2518 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2530 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2531 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2539 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2540 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2552 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2553 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2561 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
UploadProgress progress =
nullptr);
2562 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
UploadProgress progress =
nullptr);
2574 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
DownloadProgress progress =
nullptr);
2578 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
const std::string &content_type,
DownloadProgress progress =
nullptr);
2590 const std::string &path,
2591 const Params ¶ms = {},
2593 const std::string &body = {},
2594 const std::string &content_type = {});
2597 Result
send(
const Request &req);
2601 std::string
host()
const;
2619 template <
class Rep,
class Period>
2624 template <
class Rep,
class Period>
2628 template <
class Rep,
class Period>
2632 template <
class Rep,
class Period>
2633 void set_max_timeout(
const std::chrono::duration<Rep, Period> &duration);
2635 void set_basic_auth(
const std::string &username,
const std::string &password);
2653 const std::string &password);
2655 void set_no_proxy(
const std::vector<std::string> &patterns);
2660 std::unique_ptr<ClientImpl> cli_;
2662#ifdef CPPHTTPLIB_SSL_ENABLED
2664 void set_digest_auth(
const std::string &username,
2665 const std::string &password);
2666 void set_proxy_digest_auth(
const std::string &username,
2667 const std::string &password);
2668 void enable_server_certificate_verification(
bool enabled);
2669 void enable_server_hostname_verification(
bool enabled);
2670 void set_ca_cert_path(
const std::string &ca_cert_file_path,
2671 const std::string &ca_cert_dir_path = std::string());
2673 void set_ca_cert_store(tls::ca_store_t ca_cert_store);
2674 void load_ca_cert_store(
const char *ca_cert, std::size_t
size);
2676 void set_server_certificate_verifier(tls::VerifyCallback verifier);
2678 void set_session_verifier(
2681 tls::ctx_t tls_context()
const;
2683#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2684 void enable_windows_certificate_verification(
bool enabled);
2688 bool is_ssl_ =
false;
2692#ifdef CPPHTTPLIB_SSL_ENABLED
2693class SSLServer :
public Server {
2695 SSLServer(
const char *cert_path,
const char *private_key_path,
2696 const char *client_ca_cert_file_path =
nullptr,
2697 const char *client_ca_cert_dir_path =
nullptr,
2698 const char *private_key_password =
nullptr);
2701 const char *cert_pem;
2702 size_t cert_pem_len;
2703 const char *key_pem;
2705 const char *client_ca_pem;
2706 size_t client_ca_pem_len;
2707 const char *private_key_password;
2709 explicit SSLServer(
const PemMemory &pem);
2714 explicit SSLServer(
const tls::ContextSetupCallback &setup_callback);
2716 ~SSLServer()
override;
2718 bool is_valid()
const override;
2720 bool update_certs_pem(
const char *cert_pem,
const char *key_pem,
2721 const char *client_ca_pem =
nullptr,
2722 const char *password =
nullptr);
2724 tls::ctx_t tls_context()
const {
return ctx_; }
2726 int ssl_last_error()
const {
return last_ssl_error_; }
2729 bool process_and_close_socket(
socket_t sock)
override;
2731 tls::ctx_t ctx_ =
nullptr;
2732 std::mutex ctx_mutex_;
2734 int last_ssl_error_ = 0;
2739 explicit SSLClient(
const std::string &host);
2741 explicit SSLClient(
const std::string &host,
int port);
2743 explicit SSLClient(
const std::string &host,
int port,
2744 const std::string &client_cert_path,
2745 const std::string &client_key_path,
2746 const std::string &private_key_password = std::string());
2749 const char *cert_pem;
2750 size_t cert_pem_len;
2751 const char *key_pem;
2753 const char *private_key_password;
2755 explicit SSLClient(
const std::string &host,
int port,
const PemMemory &pem);
2757 ~SSLClient()
override;
2759 bool is_valid()
const override;
2761 void set_ca_cert_store(tls::ca_store_t ca_cert_store);
2762 void load_ca_cert_store(
const char *ca_cert, std::size_t
size);
2764 void set_server_certificate_verifier(tls::VerifyCallback verifier);
2767 void set_session_verifier(
2770 tls::ctx_t tls_context()
const {
return ctx_; }
2772#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2773 void enable_windows_certificate_verification(
bool enabled);
2777 bool create_and_connect_socket(Socket &socket, Error &
error)
override;
2778 bool ensure_socket_connection(Socket &socket, Error &
error)
override;
2779 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
2780 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
2783 process_socket(
const Socket &socket,
2784 std::chrono::time_point<std::chrono::steady_clock> start_time,
2785 std::function<
bool(Stream &strm)> callback)
override;
2786 bool is_ssl()
const override;
2788 bool setup_proxy_connection(
2790 std::chrono::time_point<std::chrono::steady_clock> start_time,
2791 Response &res,
bool &success, Error &
error)
override;
2792 bool connect_with_proxy(
2794 std::chrono::time_point<std::chrono::steady_clock> start_time,
2795 Response &res,
bool &success, Error &
error);
2796 bool initialize_ssl(Socket &socket, Error &
error);
2799 void reset_ctx_on_error();
2803 tls::ctx_t ctx_ =
nullptr;
2804 std::mutex ctx_mutex_;
2805 std::once_flag initialize_cert_;
2807 long verify_result_ = 0;
2811#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2812 bool enable_windows_cert_verification_ =
true;
2815 friend class ClientImpl;
2817#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2819 bool verify_host(X509 *server_cert)
const;
2820 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
2821 bool verify_host_with_common_name(X509 *server_cert)
const;
2828template <
typename T,
typename U>
2830 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2831 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2832 duration - std::chrono::seconds(sec))
2834 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
2837template <
size_t N>
inline constexpr size_t str_len(
const char (&)[N]) {
2842 return !
str.empty() &&
2843 std::all_of(
str.cbegin(),
str.cend(),
2844 [](
unsigned char c) { return std::isdigit(c); });
2848 const std::string &key,
size_t def,
2849 size_t id,
bool &is_invalid_value) {
2850 is_invalid_value =
false;
2851 auto rng = headers.equal_range(key);
2852 auto it = rng.first;
2853 std::advance(it,
static_cast<ssize_t
>(
id));
2854 if (it != rng.second) {
2856 return static_cast<size_t>(std::strtoull(it->second.data(),
nullptr, 10));
2858 is_invalid_value =
true;
2865 const std::string &key,
size_t def,
2873template <
class Rep,
class Period>
2881template <
class Rep,
class Period>
2889template <
class Rep,
class Period>
2897template <
class Rep,
class Period>
2899 const std::chrono::duration<Rep, Period> &duration) {
2905template <
class Rep,
class Period>
2907 const std::chrono::duration<Rep, Period> &duration) {
2912template <
class Rep,
class Period>
2914 const std::chrono::duration<Rep, Period> &duration) {
2919template <
class Rep,
class Period>
2921 const std::chrono::duration<Rep, Period> &duration) {
2923 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2927template <
class Rep,
class Period>
2929 const std::chrono::duration<Rep, Period> &duration) {
2930 cli_->set_connection_timeout(duration);
2933template <
class Rep,
class Period>
2936 cli_->set_read_timeout(duration);
2939template <
class Rep,
class Period>
2942 cli_->set_write_timeout(duration);
2946 cli_->set_max_timeout(msec);
2949template <
class Rep,
class Period>
2952 cli_->set_max_timeout(duration);
2960std::string
hosted_at(
const std::string &hostname);
2962void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
2966std::string
encode_uri(
const std::string &value);
2968std::string
decode_uri(
const std::string &value);
2974 bool space_as_plus =
true);
2976 bool plus_as_space =
true);
2984std::pair<std::string, std::string>
2986 const std::string &password,
2987 bool is_proxy =
false);
2992inline std::wstring u8string_to_wstring(
const char *s) {
2993 if (!s) {
return std::wstring(); }
2995 auto len =
static_cast<int>(strlen(s));
2996 if (!len) {
return std::wstring(); }
2998 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len,
nullptr, 0);
2999 if (!wlen) {
return std::wstring(); }
3003 wlen = ::MultiByteToWideChar(
3005 const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
3006 if (wlen !=
static_cast<int>(ws.size())) { ws.clear(); }
3015 time_t
mtime()
const;
3016 size_t size()
const;
3030std::string
trim_copy(
const std::string &s);
3033 const char *
data, std::size_t
size,
char d,
3034 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
3038 const std::string &
str,
char d,
3039 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
3042void split(
const char *b,
const char *
e,
char d,
3043 std::function<
void(
const char *,
const char *)> fn);
3045void split(
const char *b,
const char *
e,
char d,
size_t m,
3046 std::function<
void(
const char *,
const char *)> fn);
3049 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3050 time_t write_timeout_sec, time_t write_timeout_usec,
3051 time_t max_timeout_msec,
3052 std::chrono::time_point<std::chrono::steady_clock> start_time,
3053 std::function<
bool(
Stream &)> callback);
3056 int port,
int address_family,
bool tcp_nodelay,
3058 time_t connection_timeout_sec,
3059 time_t connection_timeout_usec,
3060 time_t read_timeout_sec, time_t read_timeout_usec,
3061 time_t write_timeout_sec,
3062 time_t write_timeout_usec,
3066 const char *def,
size_t id);
3075 std::string &boundary);
3080 std::vector<std::string> &content_types);
3098 ssize_t
read(
char *ptr,
size_t size)
override;
3099 ssize_t
write(
const char *ptr,
size_t size)
override;
3109 size_t position = 0;
3136 bool compress(
const char *
data,
size_t data_length,
bool ,
3140#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3141class gzip_compressor final :
public compressor {
3144 ~gzip_compressor()
override;
3146 bool compress(
const char *
data,
size_t data_length,
bool last,
3147 Callback callback)
override;
3150 bool is_valid_ =
false;
3154class gzip_decompressor final :
public decompressor {
3156 gzip_decompressor();
3157 ~gzip_decompressor()
override;
3159 bool is_valid()
const override;
3161 bool decompress(
const char *
data,
size_t data_length,
3162 Callback callback)
override;
3165 bool is_valid_ =
false;
3170#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3171class brotli_compressor final :
public compressor {
3173 brotli_compressor();
3174 ~brotli_compressor();
3176 bool compress(
const char *
data,
size_t data_length,
bool last,
3177 Callback callback)
override;
3180 BrotliEncoderState *state_ =
nullptr;
3185 brotli_decompressor();
3186 ~brotli_decompressor();
3188 bool is_valid()
const override;
3190 bool decompress(
const char *
data,
size_t data_length,
3191 Callback callback)
override;
3194 BrotliDecoderResult decoder_r;
3195 BrotliDecoderState *decoder_s =
nullptr;
3199#ifdef CPPHTTPLIB_ZSTD_SUPPORT
3205 bool compress(
const char *
data,
size_t data_length,
bool last,
3206 Callback callback)
override;
3209 ZSTD_CCtx *ctx_ =
nullptr;
3214 zstd_decompressor();
3215 ~zstd_decompressor();
3217 bool is_valid()
const override;
3219 bool decompress(
const char *
data,
size_t data_length,
3220 Callback callback)
override;
3223 ZSTD_DCtx *ctx_ =
nullptr;
3232 size_t fixed_buffer_size);
3233 const char *
ptr()
const;
3234 size_t size()
const;
3239 void append(
char c);
3242 char *fixed_buffer_;
3243 const size_t fixed_buffer_size_;
3244 size_t fixed_buffer_used_size_ = 0;
3245 std::string growable_buffer_;
3261 ssize_t
read_payload(
char *buf,
size_t len,
size_t &out_chunk_offset,
3262 size_t &out_chunk_total);
3269 mmap(
const char *path);
3272 bool open(
const char *path);
3276 size_t size()
const;
3277 const char *
data()
const;
3281 HANDLE hFile_ = NULL;
3282 HANDLE hMapping_ = NULL;
3287 void *addr_ =
nullptr;
3288 bool is_open_empty_file =
false;
3295bool is_token(
const std::string &s);
3310#ifdef CPPHTTPLIB_SSL_ENABLED
3312#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
3319struct MbedTlsContext {
3320 mbedtls_ssl_config conf;
3321 mbedtls_entropy_context entropy;
3322 mbedtls_ctr_drbg_context ctr_drbg;
3323 mbedtls_x509_crt ca_chain;
3324 mbedtls_x509_crt own_cert;
3325 mbedtls_pk_context own_key;
3326 bool is_server =
false;
3327 bool verify_client =
false;
3328 bool has_verify_callback =
false;
3333 MbedTlsContext(
const MbedTlsContext &) =
delete;
3334 MbedTlsContext &operator=(
const MbedTlsContext &) =
delete;
3341#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
3348struct WolfSSLContext {
3349 WOLFSSL_CTX *ctx =
nullptr;
3350 bool is_server =
false;
3351 bool verify_client =
false;
3352 bool has_verify_callback =
false;
3353 std::string ca_pem_data_;
3358 WolfSSLContext(
const WolfSSLContext &) =
delete;
3359 WolfSSLContext &operator=(
const WolfSSLContext &) =
delete;
3363struct WolfSSLCAStore {
3364 std::string pem_data;
3386 explicit operator bool()
const;
3390 const char *def =
"")
const;
3391 bool has_header(
const std::string &key)
const;
3398 const char *
data()
const;
3399 size_t size()
const;
3404 std::string buffer_;
3405 size_t current_size_ = 0;
3407 bool finished_ =
false;
3411template <
typename ClientType>
3413 size_t chunk_size = 8192) {
3414 return Result{cli.open_stream(
"GET", path), chunk_size};
3417template <
typename ClientType>
3419 const Headers &headers,
size_t chunk_size = 8192) {
3420 return Result{cli.open_stream(
"GET", path, {}, headers), chunk_size};
3423template <
typename ClientType>
3425 const Params ¶ms,
size_t chunk_size = 8192) {
3426 return Result{cli.open_stream(
"GET", path, params), chunk_size};
3429template <
typename ClientType>
3432 size_t chunk_size = 8192) {
3433 return Result{cli.open_stream(
"GET", path, params, headers), chunk_size};
3437template <
typename ClientType>
3439 const std::string &body,
const std::string &content_type,
3440 size_t chunk_size = 8192) {
3441 return Result{cli.open_stream(
"POST", path, {}, {}, body, content_type),
3445template <
typename ClientType>
3447 const Headers &headers,
const std::string &body,
3448 const std::string &content_type,
size_t chunk_size = 8192) {
3449 return Result{cli.open_stream(
"POST", path, {}, headers, body, content_type),
3453template <
typename ClientType>
3455 const Params ¶ms,
const std::string &body,
3456 const std::string &content_type,
size_t chunk_size = 8192) {
3457 return Result{cli.open_stream(
"POST", path, params, {}, body, content_type),
3461template <
typename ClientType>
3464 const std::string &body,
const std::string &content_type,
3465 size_t chunk_size = 8192) {
3467 cli.open_stream(
"POST", path, params, headers, body, content_type),
3472template <
typename ClientType>
3474 const std::string &body,
const std::string &content_type,
3475 size_t chunk_size = 8192) {
3476 return Result{cli.open_stream(
"PUT", path, {}, {}, body, content_type),
3480template <
typename ClientType>
3482 const Headers &headers,
const std::string &body,
3483 const std::string &content_type,
size_t chunk_size = 8192) {
3484 return Result{cli.open_stream(
"PUT", path, {}, headers, body, content_type),
3488template <
typename ClientType>
3490 const Params ¶ms,
const std::string &body,
3491 const std::string &content_type,
size_t chunk_size = 8192) {
3492 return Result{cli.open_stream(
"PUT", path, params, {}, body, content_type),
3496template <
typename ClientType>
3499 const std::string &body,
const std::string &content_type,
3500 size_t chunk_size = 8192) {
3502 cli.open_stream(
"PUT", path, params, headers, body, content_type),
3507template <
typename ClientType>
3509 const std::string &body,
const std::string &content_type,
3510 size_t chunk_size = 8192) {
3511 return Result{cli.open_stream(
"PATCH", path, {}, {}, body, content_type),
3515template <
typename ClientType>
3517 const Headers &headers,
const std::string &body,
3518 const std::string &content_type,
size_t chunk_size = 8192) {
3519 return Result{cli.open_stream(
"PATCH", path, {}, headers, body, content_type),
3523template <
typename ClientType>
3525 const Params ¶ms,
const std::string &body,
3526 const std::string &content_type,
size_t chunk_size = 8192) {
3527 return Result{cli.open_stream(
"PATCH", path, params, {}, body, content_type),
3531template <
typename ClientType>
3534 const std::string &body,
const std::string &content_type,
3535 size_t chunk_size = 8192) {
3537 cli.open_stream(
"PATCH", path, params, headers, body, content_type),
3542template <
typename ClientType>
3544 size_t chunk_size = 8192) {
3545 return Result{cli.open_stream(
"DELETE", path), chunk_size};
3548template <
typename ClientType>
3550 const Headers &headers,
size_t chunk_size = 8192) {
3551 return Result{cli.open_stream(
"DELETE", path, {}, headers), chunk_size};
3554template <
typename ClientType>
3556 const std::string &body,
const std::string &content_type,
3557 size_t chunk_size = 8192) {
3558 return Result{cli.open_stream(
"DELETE", path, {}, {}, body, content_type),
3562template <
typename ClientType>
3564 const Headers &headers,
const std::string &body,
3565 const std::string &content_type,
3566 size_t chunk_size = 8192) {
3568 cli.open_stream(
"DELETE", path, {}, headers, body, content_type),
3572template <
typename ClientType>
3574 const Params ¶ms,
size_t chunk_size = 8192) {
3575 return Result{cli.open_stream(
"DELETE", path, params), chunk_size};
3578template <
typename ClientType>
3581 size_t chunk_size = 8192) {
3582 return Result{cli.open_stream(
"DELETE", path, params, headers), chunk_size};
3585template <
typename ClientType>
3587 const Params ¶ms,
const std::string &body,
3588 const std::string &content_type,
3589 size_t chunk_size = 8192) {
3590 return Result{cli.open_stream(
"DELETE", path, params, {}, body, content_type),
3594template <
typename ClientType>
3597 const std::string &body,
const std::string &content_type,
3598 size_t chunk_size = 8192) {
3600 cli.open_stream(
"DELETE", path, params, headers, body, content_type),
3605template <
typename ClientType>
3607 size_t chunk_size = 8192) {
3608 return Result{cli.open_stream(
"HEAD", path), chunk_size};
3611template <
typename ClientType>
3613 const Headers &headers,
size_t chunk_size = 8192) {
3614 return Result{cli.open_stream(
"HEAD", path, {}, headers), chunk_size};
3617template <
typename ClientType>
3619 const Params ¶ms,
size_t chunk_size = 8192) {
3620 return Result{cli.open_stream(
"HEAD", path, params), chunk_size};
3623template <
typename ClientType>
3626 size_t chunk_size = 8192) {
3627 return Result{cli.open_stream(
"HEAD", path, params, headers), chunk_size};
3631template <
typename ClientType>
3633 size_t chunk_size = 8192) {
3634 return Result{cli.open_stream(
"OPTIONS", path), chunk_size};
3637template <
typename ClientType>
3639 const Headers &headers,
size_t chunk_size = 8192) {
3640 return Result{cli.open_stream(
"OPTIONS", path, {}, headers), chunk_size};
3643template <
typename ClientType>
3645 const Params ¶ms,
size_t chunk_size = 8192) {
3646 return Result{cli.open_stream(
"OPTIONS", path, params), chunk_size};
3649template <
typename ClientType>
3652 size_t chunk_size = 8192) {
3653 return Result{cli.open_stream(
"OPTIONS", path, params, headers), chunk_size};
3707 bool parse_sse_line(
const std::string &line,
SSEMessage &msg,
int &retry_ms);
3708 void run_event_loop();
3710 bool should_reconnect(
int count)
const;
3711 void wait_for_reconnect();
3717 mutable std::mutex headers_mutex_;
3721 std::map<std::string, MessageHandler> event_handlers_;
3726 int reconnect_interval_ms_ = 3000;
3727 int max_reconnect_attempts_ = 0;
3730 std::atomic<bool> running_{
false};
3731 std::atomic<bool> connected_{
false};
3732 std::string last_event_id_;
3735 std::thread async_thread_;
3774 bool send(
const std::string &
data);
3775 bool send(
const char *
data,
size_t len);
3777 const std::string &reason =
"");
3789 : strm_(strm), req_(req), is_server_(is_server),
3790 ping_interval_sec_(ping_interval_sec),
3791 max_missed_pongs_(max_missed_pongs) {
3796 std::unique_ptr<Stream> &&owned_strm,
const Request &req,
bool is_server,
3799 : strm_(*owned_strm), owned_strm_(
std::move(owned_strm)), req_(req),
3800 is_server_(is_server), ping_interval_sec_(ping_interval_sec),
3801 max_missed_pongs_(max_missed_pongs) {
3805 void start_heartbeat();
3806 bool send_frame(
Opcode op,
const char *
data,
size_t len,
bool fin =
true);
3809 std::unique_ptr<Stream> owned_strm_;
3812 time_t ping_interval_sec_;
3813 int max_missed_pongs_;
3814 int unacked_pings_ = 0;
3815 std::atomic<bool> closed_{
false};
3816 std::mutex write_mutex_;
3817 std::thread ping_thread_;
3818 std::mutex ping_mutex_;
3819 std::condition_variable ping_cv_;
3835 bool send(
const std::string &
data);
3836 bool send(
const char *
data,
size_t len);
3838 const std::string &reason =
"");
3853#ifdef CPPHTTPLIB_SSL_ENABLED
3854 void set_ca_cert_path(
const std::string &path);
3855 void set_ca_cert_store(tls::ca_store_t
store);
3856 void enable_server_certificate_verification(
bool enabled);
3860 void shutdown_and_close();
3861 bool create_stream(std::unique_ptr<Stream> &strm);
3867 std::string subprotocol_;
3868 bool is_valid_ =
false;
3870 std::unique_ptr<WebSocket> ws_;
3872 time_t read_timeout_usec_ = 0;
3875 time_t websocket_ping_interval_sec_ =
3878 int address_family_ = AF_UNSPEC;
3884 std::string interface_;
3887 std::map<std::string, std::string> addr_map_;
3889#ifdef CPPHTTPLIB_SSL_ENABLED
3890 bool is_ssl_ =
false;
3891 tls::ctx_t tls_ctx_ =
nullptr;
3892 tls::session_t tls_session_ =
nullptr;
3893 std::string ca_cert_file_path_;
3894 tls::ca_store_t ca_cert_store_ =
nullptr;
3895 bool server_certificate_verification_ =
true;
3904 bool &fin,
bool expect_masked,
size_t max_len);
3922 : handle_(
std::move(handle)), chunk_size_(chunk_size) {}
3925 : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
3926 current_size_(other.current_size_), chunk_size_(other.chunk_size_),
3927 finished_(other.finished_) {
3928 other.current_size_ = 0;
3929 other.finished_ =
true;
3933 if (
this != &other) {
3934 handle_ = std::move(other.handle_);
3935 buffer_ = std::move(other.buffer_);
3936 current_size_ = other.current_size_;
3937 chunk_size_ = other.chunk_size_;
3938 finished_ = other.finished_;
3939 other.current_size_ = 0;
3940 other.finished_ =
true;
3946inline Result::operator bool()
const {
return is_valid(); }
3949 return handle_.response ? handle_.response->status : -1;
3953 static const Headers empty_headers;
3954 return handle_.response ? handle_.response->headers : empty_headers;
3958 const char *def)
const {
3959 return handle_.response ? handle_.response->get_header_value(key, def) : def;
3963 return handle_.response ? handle_.response->has_header(key) :
false;
3971 if (!handle_.is_valid() || finished_) {
return false; }
3973 if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }
3975 ssize_t n = handle_.read(&buffer_[0], chunk_size_);
3977 current_size_ =
static_cast<size_t>(n);
4012 : client_(client), path_(path) {}
4016 : client_(client), path_(path), headers_(headers) {}
4021 on_message_ = std::move(handler);
4027 event_handlers_[type] = std::move(handler);
4032 on_open_ = std::move(handler);
4037 on_error_ = std::move(handler);
4042 reconnect_interval_ms_ = ms;
4047 max_reconnect_attempts_ = n;
4052 std::lock_guard<std::mutex> lock(headers_mutex_);
4060 return last_event_id_;
4064 running_.store(
true);
4069 running_.store(
true);
4070 async_thread_ = std::thread([
this]() { run_event_loop(); });
4074 running_.store(
false);
4076 if (async_thread_.joinable()) { async_thread_.join(); }
4079inline bool SSEClient::parse_sse_line(
const std::string &line,
SSEMessage &msg,
4082 if (line.empty() || line ==
"\r") {
return true; }
4085 if (!line.empty() && line[0] ==
':') {
return false; }
4088 auto colon_pos = line.find(
':');
4089 if (colon_pos == std::string::npos) {
4094 auto field = line.substr(0, colon_pos);
4098 if (colon_pos + 1 < line.size()) {
4099 auto value_start = colon_pos + 1;
4100 if (line[value_start] ==
' ') { value_start++; }
4101 value = line.substr(value_start);
4103 if (!value.empty() && value.back() ==
'\r') { value.pop_back(); }
4107 if (field ==
"event") {
4109 }
else if (field ==
"data") {
4111 if (!msg.
data.empty()) { msg.
data +=
"\n"; }
4113 }
else if (field ==
"id") {
4116 }
else if (field ==
"retry") {
4122 if (res.ec == std::errc{}) { retry_ms = v; }
4130inline void SSEClient::run_event_loop() {
4131 auto reconnect_count = 0;
4133 while (running_.load()) {
4137 std::lock_guard<std::mutex> lock(headers_mutex_);
4138 request_headers = headers_;
4140 if (!last_event_id_.empty()) {
4141 request_headers.emplace(
"Last-Event-ID", last_event_id_);
4145 auto result =
stream::Get(client_, path_, request_headers);
4149 connected_.store(
false);
4150 if (on_error_) { on_error_(result.error()); }
4152 if (!should_reconnect(reconnect_count)) {
break; }
4153 wait_for_reconnect();
4159 connected_.store(
false);
4171 if (!should_reconnect(reconnect_count)) {
break; }
4172 wait_for_reconnect();
4178 connected_.store(
true);
4179 reconnect_count = 0;
4180 if (on_open_) { on_open_(); }
4184 SSEMessage current_msg;
4186 while (running_.load() && result.next()) {
4187 buffer.append(result.data(), result.size());
4190 size_t line_start = 0;
4193 while ((newline_pos = buffer.find(
'\n', line_start)) !=
4194 std::string::npos) {
4195 auto line = buffer.substr(line_start, newline_pos - line_start);
4196 line_start = newline_pos + 1;
4199 auto event_complete =
4200 parse_sse_line(line, current_msg, reconnect_interval_ms_);
4202 if (event_complete && !current_msg.data.empty()) {
4204 if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
4207 dispatch_event(current_msg);
4209 current_msg.clear();
4214 buffer.erase(0, line_start);
4218 connected_.store(
false);
4220 if (!running_.load()) {
break; }
4223 if (result.has_read_error()) {
4224 if (on_error_) { on_error_(result.read_error()); }
4227 if (!should_reconnect(reconnect_count)) {
break; }
4228 wait_for_reconnect();
4232 connected_.store(
false);
4235inline void SSEClient::dispatch_event(
const SSEMessage &msg) {
4237 auto it = event_handlers_.find(msg.event);
4238 if (it != event_handlers_.end()) {
4244 if (on_message_) { on_message_(msg); }
4247inline bool SSEClient::should_reconnect(
int count)
const {
4248 if (!running_.load()) {
return false; }
4249 if (max_reconnect_attempts_ == 0) {
return true; }
4250 return count < max_reconnect_attempts_;
4253inline void SSEClient::wait_for_reconnect() {
4256 while (running_.load() && waited < reconnect_interval_ms_) {
4257 std::this_thread::sleep_for(std::chrono::milliseconds(100));
4264#ifdef CPPHTTPLIB_SSL_ENABLED
4272ctx_t create_client_context();
4273void free_context(ctx_t ctx);
4274bool set_min_version(ctx_t ctx, Version version);
4275bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len);
4276bool load_ca_file(ctx_t ctx,
const char *file_path);
4277bool load_ca_dir(ctx_t ctx,
const char *dir_path);
4278bool load_system_certs(ctx_t ctx);
4279bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
4280 const char *password);
4281bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
4282 const char *key_path,
const char *password);
4285ctx_t create_server_context();
4286bool set_server_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
4287 const char *password);
4288bool set_server_cert_file(ctx_t ctx,
const char *cert_path,
4289 const char *key_path,
const char *password);
4290bool set_client_ca_file(ctx_t ctx,
const char *ca_file,
const char *ca_dir);
4291void set_verify_client(ctx_t ctx,
bool require);
4294session_t create_session(ctx_t ctx,
socket_t sock);
4295void free_session(session_t session);
4296bool set_sni(session_t session,
const char *hostname);
4297bool set_hostname(session_t session,
const char *hostname);
4300TlsError connect(session_t session);
4301TlsError accept(session_t session);
4304bool connect_nonblocking(session_t session,
socket_t sock, time_t timeout_sec,
4305 time_t timeout_usec, TlsError *err);
4306bool accept_nonblocking(session_t session,
socket_t sock, time_t timeout_sec,
4307 time_t timeout_usec, TlsError *err);
4310ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err);
4311ssize_t write(session_t session,
const void *buf,
size_t len, TlsError &err);
4312int pending(const_session_t session);
4313void shutdown(session_t session,
bool graceful);
4316bool is_peer_closed(session_t session,
socket_t sock);
4319cert_t get_peer_cert(const_session_t session);
4320void free_cert(cert_t cert);
4321bool verify_hostname(cert_t cert,
const char *hostname);
4322uint64_t hostname_mismatch_code();
4323long get_verify_result(const_session_t session);
4326std::string get_cert_subject_cn(cert_t cert);
4327std::string get_cert_issuer_name(cert_t cert);
4328bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans);
4329bool get_cert_validity(cert_t cert, time_t ¬_before, time_t ¬_after);
4330std::string get_cert_serial(cert_t cert);
4331bool get_cert_der(cert_t cert, std::vector<unsigned char> &der);
4332const char *get_sni(const_session_t session);
4335ca_store_t create_ca_store(
const char *pem,
size_t len);
4336void free_ca_store(ca_store_t
store);
4337bool set_ca_store(ctx_t ctx, ca_store_t
store);
4338size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs);
4339std::vector<std::string> get_ca_names(ctx_t ctx);
4342bool update_server_cert(ctx_t ctx,
const char *cert_pem,
const char *key_pem,
4343 const char *password);
4344bool update_server_client_ca(ctx_t ctx,
const char *ca_pem);
4347bool set_verify_callback(ctx_t ctx, VerifyCallback callback);
4348long get_verify_error(const_session_t session);
4349std::string verify_error_string(
long error_code);
4352uint64_t peek_error();
4353uint64_t get_error();
4354std::string error_string(uint64_t
code);
4366 const void *optval, socklen_t optlen) {
4367 return setsockopt(sock, level, optname,
4369 reinterpret_cast<const char *
>(optval),
4377 time_t sec, time_t usec) {
4379 auto timeout =
static_cast<uint32_t
>(sec * 1000 + usec / 1000);
4382 timeout.tv_sec =
static_cast<long>(sec);
4383 timeout.tv_usec =
static_cast<decltype(timeout.tv_usec)
>(usec);
4392 }
else if (
'A' <= c && c <=
'F') {
4395 }
else if (
'a' <= c && c <=
'f') {
4404 if (i >= s.size()) {
return false; }
4407 for (; cnt; i++, cnt--) {
4408 if (!s[i]) {
return false; }
4420 static const auto charset =
"0123456789abcdef";
4423 ret = charset[n & 15] +
ret;
4430 if (!fs.
is_file()) {
return std::string(); }
4436 auto mtime_raw = fs.
mtime();
4437 if (mtime_raw < 0) {
return std::string(); }
4439 auto mtime =
static_cast<size_t>(mtime_raw);
4450 if (mtime < 0) {
return std::string(); }
4454 if (gmtime_s(&tm_buf, &mtime) != 0) {
return std::string(); }
4456 if (gmtime_r(&mtime, &tm_buf) ==
nullptr) {
return std::string(); }
4459 if (strftime(buf,
sizeof(buf),
"%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {
4460 return std::string();
4463 return std::string(buf);
4471 const std::locale classic_locale = std::locale::classic();
4474 auto try_parse = [&](
const char *fmt) ->
bool {
4475 std::istringstream ss(date_str);
4476 ss.imbue(classic_locale);
4478 memset(&tm_buf, 0,
sizeof(tm_buf));
4479 ss >> std::get_time(&tm_buf, fmt);
4485 if (!try_parse(
"%a, %d %b %Y %H:%M:%S")) {
4487 if (!try_parse(
"%A, %d-%b-%y %H:%M:%S")) {
4489 if (!try_parse(
"%a %b %d %H:%M:%S %Y")) {
4490 return static_cast<time_t
>(-1);
4496 return _mkgmtime(&tm_buf);
4498 return mktime(&tm_buf);
4500 return timegm(&tm_buf);
4506 return s.size() > 3 && s[0] ==
'W' && s[1] ==
'/' && s[2] ==
'"';
4512 return s.size() >= 2 && s[0] ==
'"' && s.back() ==
'"';
4516 if (
code < 0x0080) {
4517 buff[0] =
static_cast<char>(
code & 0x7F);
4519 }
else if (
code < 0x0800) {
4520 buff[0] =
static_cast<char>(0xC0 | ((
code >> 6) & 0x1F));
4521 buff[1] =
static_cast<char>(0x80 | (
code & 0x3F));
4523 }
else if (
code < 0xD800) {
4524 buff[0] =
static_cast<char>(0xE0 | ((
code >> 12) & 0xF));
4525 buff[1] =
static_cast<char>(0x80 | ((
code >> 6) & 0x3F));
4526 buff[2] =
static_cast<char>(0x80 | (
code & 0x3F));
4528 }
else if (
code < 0xE000) {
4530 }
else if (
code < 0x10000) {
4531 buff[0] =
static_cast<char>(0xE0 | ((
code >> 12) & 0xF));
4532 buff[1] =
static_cast<char>(0x80 | ((
code >> 6) & 0x3F));
4533 buff[2] =
static_cast<char>(0x80 | (
code & 0x3F));
4535 }
else if (
code < 0x110000) {
4536 buff[0] =
static_cast<char>(0xF0 | ((
code >> 18) & 0x7));
4537 buff[1] =
static_cast<char>(0x80 | ((
code >> 12) & 0x3F));
4538 buff[2] =
static_cast<char>(0x80 | ((
code >> 6) & 0x3F));
4539 buff[3] =
static_cast<char>(0x80 | (
code & 0x3F));
4556 auto c =
static_cast<unsigned char>(s[i]);
4562 }
else if ((c & 0xE0) == 0xC0) {
4565 }
else if ((c & 0xF0) == 0xE0) {
4568 }
else if ((c & 0xF8) == 0xF0) {
4574 if (i + len > n) {
return false; }
4575 for (
size_t j = 1; j < len; j++) {
4576 auto b =
static_cast<unsigned char>(s[i + j]);
4577 if ((b & 0xC0) != 0x80) {
return false; }
4578 cp = (cp << 6) | (b & 0x3F);
4581 if (len == 2 && cp < 0x80) {
return false; }
4582 if (len == 3 && cp < 0x800) {
return false; }
4583 if (len == 4 && cp < 0x10000) {
return false; }
4585 if (cp >= 0xD800 && cp <= 0xDFFF) {
return false; }
4586 if (cp > 0x10FFFF) {
return false; }
4600 static const auto lookup =
4601 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4604 out.reserve(in.size());
4610 val = (val << 8) + static_cast<uint8_t>(c);
4613 out.push_back(lookup[(val >> valb) & 0x3F]);
4618 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
4620 while (out.size() % 4) {
4627inline std::string
sha1(
const std::string &input) {
4629 auto left_rotate = [](uint32_t x, uint32_t n) -> uint32_t {
4630 return (x << n) | (x >> (32 - n));
4633 uint32_t h0 = 0x67452301;
4634 uint32_t h1 = 0xEFCDAB89;
4635 uint32_t h2 = 0x98BADCFE;
4636 uint32_t h3 = 0x10325476;
4637 uint32_t h4 = 0xC3D2E1F0;
4640 std::string msg = input;
4641 uint64_t original_bit_len =
static_cast<uint64_t
>(msg.size()) * 8;
4642 msg.push_back(
static_cast<char>(0x80u));
4643 while (msg.size() % 64 != 56) {
4648 for (
int i = 56; i >= 0; i -= 8) {
4649 msg.push_back(
static_cast<char>((original_bit_len >> i) & 0xFF));
4653 for (
size_t offset = 0; offset < msg.size(); offset += 64) {
4656 for (
size_t i = 0; i < 16; i++) {
4658 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(msg[offset + i * 4]))
4660 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(msg[offset + i * 4 + 1]))
4662 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(msg[offset + i * 4 + 2]))
4664 (
static_cast<uint32_t
>(
4665 static_cast<uint8_t
>(msg[offset + i * 4 + 3])));
4668 for (
int i = 16; i < 80; i++) {
4669 w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
4672 uint32_t a = h0, b = h1, c = h2,
d = h3,
e = h4;
4674 for (
int i = 0; i < 80; i++) {
4677 f = (b & c) | ((~b) &
d);
4679 }
else if (i < 40) {
4682 }
else if (i < 60) {
4683 f = (b & c) | (b &
d) | (c &
d);
4690 uint32_t temp = left_rotate(a, 5) +
f +
e + k + w[i];
4693 c = left_rotate(b, 30);
4706 std::string hash(20,
'\0');
4707 for (
size_t i = 0; i < 4; i++) {
4708 hash[i] =
static_cast<char>((h0 >> (24 - i * 8)) & 0xFF);
4709 hash[4 + i] =
static_cast<char>((h1 >> (24 - i * 8)) & 0xFF);
4710 hash[8 + i] =
static_cast<char>((h2 >> (24 - i * 8)) & 0xFF);
4711 hash[12 + i] =
static_cast<char>((h3 >> (24 - i * 8)) & 0xFF);
4712 hash[16 + i] =
static_cast<char>((h4 >> (24 - i * 8)) & 0xFF);
4718 const std::string magic =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
4723 if (req.
method !=
"GET") {
return false; }
4726 auto upgrade_it = req.
headers.find(
"Upgrade");
4727 if (upgrade_it == req.
headers.end()) {
return false; }
4729 if (upgrade_val !=
"websocket") {
return false; }
4732 auto connection_it = req.
headers.find(
"Connection");
4733 if (connection_it == req.
headers.end()) {
return false; }
4735 if (connection_val.find(
"upgrade") == std::string::npos) {
return false; }
4740 if (ws_key.size() != 24 || ws_key[22] !=
'=' || ws_key[23] !=
'=') {
4743 static const std::string b64chars =
4744 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4745 for (
size_t i = 0; i < 22; i++) {
4746 if (b64chars.find(ws_key[i]) == std::string::npos) {
return false; }
4751 if (version !=
"13") {
return false; }
4757 const char *
data,
size_t len,
bool fin,
4761 header[0] =
static_cast<uint8_t
>((fin ? 0x80 : 0x00) |
4762 (
static_cast<uint8_t
>(opcode) & 0x0F));
4766 header[1] =
static_cast<uint8_t
>(len);
4767 if (mask) {
header[1] |= 0x80; }
4768 if (strm.
write(
reinterpret_cast<char *
>(
header), 2) < 0) {
return false; }
4769 }
else if (len <= 0xFFFF) {
4771 if (mask) {
header[1] |= 0x80; }
4772 if (strm.
write(
reinterpret_cast<char *
>(
header), 2) < 0) {
return false; }
4774 ext[0] =
static_cast<uint8_t
>((len >> 8) & 0xFF);
4775 ext[1] =
static_cast<uint8_t
>(len & 0xFF);
4776 if (strm.
write(
reinterpret_cast<char *
>(ext), 2) < 0) {
return false; }
4779 if (mask) {
header[1] |= 0x80; }
4780 if (strm.
write(
reinterpret_cast<char *
>(
header), 2) < 0) {
return false; }
4782 for (
int i = 7; i >= 0; i--) {
4784 static_cast<uint8_t
>((
static_cast<uint64_t
>(len) >> (i * 8)) & 0xFF);
4786 if (strm.
write(
reinterpret_cast<char *
>(ext), 8) < 0) {
return false; }
4791 thread_local std::mt19937 rng(std::random_device{}());
4792 uint8_t mask_key[4];
4794 std::memcpy(mask_key, &r, 4);
4795 if (strm.
write(
reinterpret_cast<char *
>(mask_key), 4) < 0) {
return false; }
4798 const size_t chunk_size = 4096;
4799 std::vector<char> buf((std::min)(len, chunk_size));
4800 for (
size_t offset = 0; offset < len; offset += chunk_size) {
4801 size_t n = (std::min)(chunk_size, len - offset);
4802 for (
size_t i = 0; i < n; i++) {
4804 data[offset + i] ^
static_cast<char>(mask_key[(offset + i) % 4]);
4806 if (strm.
write(buf.data(), n) < 0) {
return false; }
4810 if (strm.
write(
data, len) < 0) {
return false; }
4823 std::string &payload,
bool &fin,
4824 bool expect_masked,
size_t max_len) {
4827 if (strm.
read(
reinterpret_cast<char *
>(
header), 2) != 2) {
return false; }
4829 fin = (
header[0] & 0x80) != 0;
4832 if (
header[0] & 0x70) {
return false; }
4835 bool masked = (
header[1] & 0x80) != 0;
4836 uint64_t payload_len =
header[1] & 0x7F;
4840 bool is_control = (
static_cast<uint8_t
>(opcode) & 0x08) != 0;
4842 if (!fin) {
return false; }
4843 if (payload_len > 125) {
return false; }
4846 if (masked != expect_masked) {
return false; }
4849 if (payload_len == 126) {
4851 if (strm.
read(
reinterpret_cast<char *
>(ext), 2) != 2) {
return false; }
4852 payload_len = (
static_cast<uint64_t
>(ext[0]) << 8) | ext[1];
4853 }
else if (payload_len == 127) {
4855 if (strm.
read(
reinterpret_cast<char *
>(ext), 8) != 8) {
return false; }
4857 if (ext[0] & 0x80) {
return false; }
4859 for (
int i = 0; i < 8; i++) {
4860 payload_len = (payload_len << 8) | ext[i];
4864 if (payload_len > max_len) {
return false; }
4867 uint8_t mask_key[4] = {0};
4869 if (strm.
read(
reinterpret_cast<char *
>(mask_key), 4) != 4) {
return false; }
4873 payload.resize(
static_cast<size_t>(payload_len));
4874 if (payload_len > 0) {
4875 size_t total_read = 0;
4876 while (total_read < payload_len) {
4877 auto n = strm.
read(&payload[total_read],
4878 static_cast<size_t>(payload_len - total_read));
4879 if (n <= 0) {
return false; }
4880 total_read +=
static_cast<size_t>(n);
4886 for (
size_t i = 0; i < payload.size(); i++) {
4887 payload[i] ^=
static_cast<char>(mask_key[i % 4]);
4904 while (i < path.size() && path[i] ==
'/') {
4908 while (i < path.size()) {
4911 while (i < path.size() && path[i] !=
'/') {
4912 if (path[i] ==
'\0') {
4914 }
else if (path[i] ==
'\\') {
4923 if (!path.compare(beg, len,
".")) {
4925 }
else if (!path.compare(beg, len,
"..")) {
4926 if (level == 0) {
return false; }
4933 while (i < path.size() && path[i] ==
'/') {
4943 char buf[_MAX_PATH];
4944 if (_fullpath(buf, path, _MAX_PATH) ==
nullptr) {
return false; }
4946#elif defined(PATH_MAX)
4948 if (realpath(path, buf) ==
nullptr) {
return false; }
4951 auto buf = realpath(path,
nullptr);
4952 auto guard =
scope_exit([&]() { std::free(buf); });
4953 if (buf ==
nullptr) {
return false; }
4960 const std::string &resolved_base) {
4962 return _strnicmp(resolved_path.c_str(), resolved_base.c_str(),
4963 resolved_base.size()) == 0;
4965 return strncmp(resolved_path.c_str(), resolved_base.c_str(),
4966 resolved_base.size()) == 0;
4972 auto wpath = u8string_to_wstring(path.c_str());
4973 ret_ = _wstat(wpath.c_str(), &st_);
4975 ret_ = stat(path.c_str(), &st_);
4979 return ret_ >= 0 && S_ISREG(st_.st_mode);
4982 return ret_ >= 0 && S_ISDIR(st_.st_mode);
4986 return ret_ >= 0 ?
static_cast<time_t
>(st_.st_mtime)
4987 :
static_cast<time_t
>(-1);
4991 return ret_ >= 0 ?
static_cast<size_t>(st_.st_size) : 0;
4996 result.reserve(s.size());
4998 for (
size_t i = 0; s[i]; i++) {
5000 case ' ': result +=
"%20";
break;
5001 case '+': result +=
"%2B";
break;
5002 case '\r': result +=
"%0D";
break;
5003 case '\n': result +=
"%0A";
break;
5004 case '\'': result +=
"%27";
break;
5005 case ',': result +=
"%2C";
break;
5007 case ';': result +=
"%3B";
break;
5009 auto c =
static_cast<uint8_t
>(s[i]);
5013 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
5015 result.append(hex,
static_cast<size_t>(len));
5028 thread_local auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
5029 if (std::regex_search(path, m, re)) {
return m[1].str(); }
5030 return std::string();
5035template <
typename T>
5036inline bool parse_header(
const char *beg,
const char *end,
T fn);
5038template <
typename T>
5046 while (p < end && *p !=
':') {
5050 auto name = std::string(beg, p);
5053 if (p == end) {
return false; }
5057 if (*p++ !=
':') {
return false; }
5064 auto key_len = key_end - beg;
5065 if (!key_len) {
return false; }
5067 auto key = std::string(beg, key_end);
5068 auto val = std::string(p, end);
5101 "transfer-encoding",
5106 "proxy-authenticate",
5107 "proxy-authorization",
5130 if (trailer_header && std::strlen(trailer_header)) {
5131 auto len = std::strlen(trailer_header);
5132 split(trailer_header, trailer_header + len,
',',
5133 [&](
const char *b,
const char *
e) {
5134 const char *kbeg = b;
5135 const char *kend =
e;
5136 while (kbeg < kend && (*kbeg ==
' ' || *kbeg ==
'\t')) {
5139 while (kend > kbeg && (kend[-1] ==
' ' || kend[-1] ==
'\t')) {
5142 std::string key(kbeg,
static_cast<size_t>(kend - kbeg));
5144 prohibited_trailers.find(key) == prohibited_trailers.end()) {
5145 declared_trailers.insert(key);
5150 size_t trailer_header_count = 0;
5151 while (strcmp(line_reader.
ptr(),
"\r\n") != 0) {
5155 constexpr auto line_terminator_len = 2;
5156 auto line_beg = line_reader.
ptr();
5158 line_reader.
ptr() + line_reader.
size() - line_terminator_len;
5161 [&](
const std::string &key,
const std::string &val) {
5162 if (declared_trailers.find(key) !=
5163 declared_trailers.end()) {
5164 dest.emplace(key, val);
5165 trailer_header_count++;
5171 if (!line_reader.
getline()) {
return false; }
5177inline std::pair<size_t, size_t>
trim(
const char *b,
const char *
e,
size_t left,
5185 return std::make_pair(left, right);
5189 auto r =
trim(s.data(), s.data() + s.size(), 0, s.size());
5190 return s.substr(r.first, r.second - r.first);
5194 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"') {
5195 return s.substr(1, s.size() - 2);
5202 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
5205 const auto found =
static_cast<std::size_t
>(it !=
data +
size);
5206 const auto lhs_data =
data;
5207 const auto lhs_size =
static_cast<std::size_t
>(it -
data);
5208 const auto rhs_data = it + found;
5209 const auto rhs_size =
size - lhs_size - found;
5211 fn(lhs_data, lhs_size, rhs_data, rhs_size);
5216 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
5221inline void split(
const char *b,
const char *
e,
char d,
5222 std::function<
void(
const char *,
const char *)> fn) {
5223 return split(b,
e,
d, (std::numeric_limits<size_t>::max)(), std::move(fn));
5226inline void split(
const char *b,
const char *
e,
char d,
size_t m,
5227 std::function<
void(
const char *,
const char *)> fn) {
5232 while (
e ? (b + i <
e) : (b[i] !=
'\0')) {
5233 if (b[i] ==
d && count < m) {
5234 auto r =
trim(b,
e, beg, i);
5235 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
5243 auto r =
trim(b,
e, beg, i);
5244 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
5249 std::function<
bool(
const char *,
const char *)> fn) {
5254 while (
e ? (b + i <
e) : (b[i] !=
'\0')) {
5255 if (b[i] ==
d && count < m) {
5256 auto r =
trim(b,
e, beg, i);
5257 if (r.first < r.second) {
5258 auto found = fn(&b[r.first], &b[r.second]);
5259 if (found) {
return true; }
5268 auto r =
trim(b,
e, beg, i);
5269 if (r.first < r.second) {
5270 auto found = fn(&b[r.first], &b[r.second]);
5271 if (found) {
return true; }
5279 std::function<
bool(
const char *,
const char *)> fn) {
5280 return split_find(b,
e,
d, (std::numeric_limits<size_t>::max)(),
5285 size_t fixed_buffer_size)
5286 : strm_(strm), fixed_buffer_(fixed_buffer),
5287 fixed_buffer_size_(fixed_buffer_size) {}
5290 if (growable_buffer_.empty()) {
5291 return fixed_buffer_;
5293 return growable_buffer_.data();
5298 if (growable_buffer_.empty()) {
5299 return fixed_buffer_used_size_;
5301 return growable_buffer_.size();
5307 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
5311 fixed_buffer_used_size_ = 0;
5312 growable_buffer_.clear();
5314#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5318 for (
size_t i = 0;; i++) {
5325 auto n = strm_.read(&
byte, 1);
5329 }
else if (n == 0) {
5339#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5340 if (
byte ==
'\n') {
break; }
5342 if (prev_byte ==
'\r' &&
byte ==
'\n') {
break; }
5350inline void stream_line_reader::append(
char c) {
5351 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
5352 fixed_buffer_[fixed_buffer_used_size_++] = c;
5353 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
5355 if (growable_buffer_.empty()) {
5356 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
5357 growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
5359 growable_buffer_ += c;
5371 auto wpath = u8string_to_wstring(path);
5372 if (wpath.empty()) {
return false; }
5375 ::CreateFile2(wpath.c_str(), GENERIC_READ,
5376 FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, NULL);
5378 if (hFile_ == INVALID_HANDLE_VALUE) {
return false; }
5380 LARGE_INTEGER
size{};
5381 if (!::GetFileSizeEx(hFile_, &
size)) {
return false; }
5385 if (
static_cast<ULONGLONG
>(
size.QuadPart) >
5386 (std::numeric_limits<
decltype(size_)>::max)()) {
5390 size_ =
static_cast<size_t>(
size.QuadPart);
5393 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
5396 if (hMapping_ == NULL && size_ == 0) {
5398 is_open_empty_file =
true;
5402 if (hMapping_ == NULL) {
5407 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
5409 if (addr_ ==
nullptr) {
5414 fd_ =
::open(path, O_RDONLY);
5415 if (fd_ == -1) {
return false; }
5418 if (fstat(fd_, &sb) == -1) {
5422 size_ =
static_cast<size_t>(sb.st_size);
5424 addr_ =
::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
5427 if (addr_ == MAP_FAILED && size_ == 0) {
5429 is_open_empty_file =
true;
5438 return is_open_empty_file ? true : addr_ !=
nullptr;
5444 return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_);
5450 ::UnmapViewOfFile(addr_);
5455 ::CloseHandle(hMapping_);
5459 if (hFile_ != INVALID_HANDLE_VALUE) {
5460 ::CloseHandle(hFile_);
5461 hFile_ = INVALID_HANDLE_VALUE;
5464 is_open_empty_file =
false;
5466 if (addr_ !=
nullptr) {
5467 munmap(addr_, size_);
5480 return closesocket(sock);
5490 if (res < 0 && errno == EINTR) {
5491 std::this_thread::sleep_for(std::chrono::microseconds{1});
5503 static_cast<char *
>(ptr),
static_cast<int>(
size),
5516 static_cast<const char *
>(ptr),
static_cast<int>(
size),
5526 return ::WSAPoll(fds, nfds, timeout);
5528 return ::poll(fds, nfds, timeout);
5536 pfd.events = events;
5539 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
5554 struct pollfd pfd_read;
5556 pfd_read.events = POLLIN | POLLOUT;
5557 pfd_read.revents = 0;
5559 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
5566 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
5568 socklen_t len =
sizeof(
error);
5569 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5570 reinterpret_cast<char *
>(&
error), &len);
5571 auto successful = res >= 0 && !
error;
5582 }
else if (val < 0 && errno == EBADF) {
5592 time_t write_timeout_sec, time_t write_timeout_usec,
5593 time_t max_timeout_msec = 0,
5594 std::chrono::time_point<std::chrono::steady_clock> start_time =
5595 (std::chrono::steady_clock::time_point::min)());
5602 ssize_t
read(
char *ptr,
size_t size)
override;
5603 ssize_t
write(
const char *ptr,
size_t size)
override;
5612 time_t read_timeout_sec_;
5613 time_t read_timeout_usec_;
5614 time_t write_timeout_sec_;
5615 time_t write_timeout_usec_;
5616 time_t max_timeout_msec_;
5617 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5619 std::vector<char> read_buff_;
5620 size_t read_buff_off_ = 0;
5621 size_t read_buff_content_size_ = 0;
5623 static const size_t read_buff_size_ = 1024l * 4;
5627 time_t keep_alive_timeout_sec) {
5628 using namespace std::chrono;
5630 const auto interval_usec =
5634 if (
select_read(sock, 0, interval_usec) > 0) {
return true; }
5636 const auto start = steady_clock::now() - microseconds{interval_usec};
5637 const auto timeout = seconds{keep_alive_timeout_sec};
5647 }
else if (val == 0) {
5648 if (steady_clock::now() - start > timeout) {
5659template <
typename T>
5662 size_t keep_alive_max_count,
5663 time_t keep_alive_timeout_sec,
T callback) {
5664 assert(keep_alive_max_count > 0);
5666 auto count = keep_alive_max_count;
5667 while (count > 0 &&
keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
5668 auto close_connection = count == 1;
5669 auto connection_closed =
false;
5670 ret = callback(close_connection, connection_closed);
5671 if (!
ret || connection_closed) {
break; }
5677template <
typename T>
5680 size_t keep_alive_max_count,
5681 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
5682 time_t read_timeout_usec, time_t write_timeout_sec,
5683 time_t write_timeout_usec,
T callback) {
5685 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
5686 [&](
bool close_connection,
bool &connection_closed) {
5687 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5688 write_timeout_sec, write_timeout_usec);
5689 return callback(strm, close_connection, connection_closed);
5694 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5695 time_t write_timeout_sec, time_t write_timeout_usec,
5696 time_t max_timeout_msec,
5697 std::chrono::time_point<std::chrono::steady_clock> start_time,
5698 std::function<
bool(
Stream &)> callback) {
5699 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5700 write_timeout_sec, write_timeout_usec, max_timeout_msec,
5702 return callback(strm);
5707 return shutdown(sock, SD_BOTH);
5709 return shutdown(sock, SHUT_RDWR);
5714 if (s.size() > 1 && s[0] ==
'\0') {
5724 if (s.size() > 1 && s[0] ==
'@') {
5733 const struct addrinfo *hints,
5734 struct addrinfo **res, time_t timeout_sec) {
5735#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
5736 if (timeout_sec <= 0) {
5738 return getaddrinfo(node, service, hints, res);
5743 OVERLAPPED overlapped = {};
5744 HANDLE
event = CreateEventW(
nullptr, TRUE, FALSE,
nullptr);
5745 if (!event) {
return EAI_FAIL; }
5747 overlapped.hEvent = event;
5749 PADDRINFOEXW result_addrinfo =
nullptr;
5750 HANDLE cancel_handle =
nullptr;
5752 ADDRINFOEXW hints_ex = {};
5754 hints_ex.ai_flags = hints->ai_flags;
5755 hints_ex.ai_family = hints->ai_family;
5756 hints_ex.ai_socktype = hints->ai_socktype;
5757 hints_ex.ai_protocol = hints->ai_protocol;
5760 auto wnode = u8string_to_wstring(node);
5761 auto wservice = u8string_to_wstring(service);
5763 auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS,
nullptr,
5764 hints ? &hints_ex :
nullptr, &result_addrinfo,
5765 nullptr, &overlapped,
nullptr, &cancel_handle);
5767 if (
ret == WSA_IO_PENDING) {
5769 ::WaitForSingleObject(event,
static_cast<DWORD
>(timeout_sec * 1000));
5770 if (wait_result == WAIT_TIMEOUT) {
5771 if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
5772 ::CloseHandle(event);
5776 DWORD bytes_returned;
5778 &bytes_returned, FALSE)) {
5779 ::CloseHandle(event);
5780 return ::WSAGetLastError();
5784 ::CloseHandle(event);
5786 if (
ret == NO_ERROR ||
ret == WSA_IO_PENDING) {
5787 *res =
reinterpret_cast<struct addrinfo *
>(result_addrinfo);
5792#elif TARGET_OS_MAC && defined(__clang__)
5793 if (!node) {
return EAI_NONAME; }
5795 CFStringRef hostname_ref = CFStringCreateWithCString(
5796 kCFAllocatorDefault, node, kCFStringEncodingUTF8);
5797 if (!hostname_ref) {
return EAI_MEMORY; }
5799 CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
5800 CFRelease(hostname_ref);
5801 if (!host_ref) {
return EAI_MEMORY; }
5804 struct CFHostContext {
5805 bool completed =
false;
5806 bool success =
false;
5807 CFArrayRef addresses =
nullptr;
5809 std::condition_variable cv;
5812 CFHostClientContext client_context;
5813 memset(&client_context, 0,
sizeof(client_context));
5814 client_context.info = &context;
5817 auto callback = [](CFHostRef theHost, CFHostInfoType ,
5818 const CFStreamError *
error,
void *
info) {
5819 auto ctx =
static_cast<CFHostContext *
>(
info);
5820 std::lock_guard<std::mutex> lock(ctx->mutex);
5823 ctx->success =
false;
5825 Boolean hasBeenResolved;
5826 ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
5827 if (ctx->addresses && hasBeenResolved) {
5828 CFRetain(ctx->addresses);
5829 ctx->success =
true;
5831 ctx->success =
false;
5834 ctx->completed =
true;
5835 ctx->cv.notify_one();
5838 if (!CFHostSetClient(host_ref, callback, &client_context)) {
5839 CFRelease(host_ref);
5844 CFRunLoopRef run_loop = CFRunLoopGetCurrent();
5845 CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5848 CFStreamError stream_error;
5849 if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
5850 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5851 CFRelease(host_ref);
5857 std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
5858 bool timed_out =
false;
5861 std::unique_lock<std::mutex> lock(context.mutex);
5863 while (!context.completed) {
5864 auto now = std::chrono::steady_clock::now();
5865 if (
now >= timeout_time) {
5872 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1,
true);
5878 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5879 CFHostSetClient(host_ref,
nullptr,
nullptr);
5881 if (timed_out || !context.completed) {
5882 CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
5883 CFRelease(host_ref);
5887 if (!context.success || !context.addresses) {
5888 CFRelease(host_ref);
5893 CFIndex count = CFArrayGetCount(context.addresses);
5895 CFRelease(context.addresses);
5896 CFRelease(host_ref);
5900 struct addrinfo *result_addrinfo =
nullptr;
5901 struct addrinfo **current = &result_addrinfo;
5903 for (CFIndex i = 0; i < count; i++) {
5904 CFDataRef addr_data =
5905 static_cast<CFDataRef
>(CFArrayGetValueAtIndex(context.addresses, i));
5906 if (!addr_data)
continue;
5908 const struct sockaddr *sockaddr_ptr =
5909 reinterpret_cast<const struct sockaddr *
>(CFDataGetBytePtr(addr_data));
5910 socklen_t sockaddr_len =
static_cast<socklen_t
>(CFDataGetLength(addr_data));
5913 *current =
static_cast<struct addrinfo *
>(malloc(
sizeof(
struct addrinfo)));
5915 freeaddrinfo(result_addrinfo);
5916 CFRelease(context.addresses);
5917 CFRelease(host_ref);
5921 memset(*current, 0,
sizeof(
struct addrinfo));
5924 (*current)->ai_family = sockaddr_ptr->sa_family;
5925 (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
5926 (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
5927 (*current)->ai_addrlen = sockaddr_len;
5930 (*current)->ai_addr =
static_cast<struct sockaddr *
>(malloc(sockaddr_len));
5931 if (!(*current)->ai_addr) {
5932 freeaddrinfo(result_addrinfo);
5933 CFRelease(context.addresses);
5934 CFRelease(host_ref);
5937 memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
5940 if (service && *service) {
5942 if (
parse_port(service, strlen(service), port)) {
5943 if (sockaddr_ptr->sa_family == AF_INET) {
5944 reinterpret_cast<struct sockaddr_in *
>((*current)->ai_addr)
5945 ->sin_port = htons(
static_cast<uint16_t
>(port));
5946 }
else if (sockaddr_ptr->sa_family == AF_INET6) {
5947 reinterpret_cast<struct sockaddr_in6 *
>((*current)->ai_addr)
5948 ->sin6_port = htons(
static_cast<uint16_t
>(port));
5953 current = &((*current)->ai_next);
5956 CFRelease(context.addresses);
5957 CFRelease(host_ref);
5959 *res = result_addrinfo;
5961#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
5962 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
5969 struct gaicb request {};
5970 struct gaicb *requests[1] = {&request};
5971 struct sigevent sevp {};
5972 struct timespec timeout {
5976 request.ar_name = node;
5977 request.ar_service = service;
5978 request.ar_request = hints;
5979 sevp.sigev_notify = SIGEV_NONE;
5981 int rc = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
5982 if (rc != 0) {
return rc; }
5985 if (request.ar_result) { freeaddrinfo(request.ar_result); }
5988 int wait_result = gai_suspend(requests, 1, &timeout);
5990 if (wait_result == 0 || wait_result == EAI_ALLDONE) {
5991 int gai_result = gai_error(&request);
5992 if (gai_result == 0) {
5993 *res = request.ar_result;
5994 request.ar_result =
nullptr;
6000 gai_cancel(&request);
6001 while (gai_error(&request) == EAI_INPROGRESS) {
6002 gai_suspend(requests, 1,
nullptr);
6008 struct GetAddrInfoState {
6009 ~GetAddrInfoState() {
6014 std::condition_variable result_cv;
6015 bool completed =
false;
6016 int result = EAI_SYSTEM;
6018 std::string service;
6019 struct addrinfo hints;
6020 struct addrinfo *
info =
nullptr;
6024 auto state = std::make_shared<GetAddrInfoState>();
6025 if (node) { state->node = node; }
6026 state->service = service;
6027 state->hints = *hints;
6029 std::thread resolve_thread([state]() {
6030 auto thread_result =
6031 getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
6034 std::lock_guard<std::mutex> lock(state->mutex);
6035 state->result = thread_result;
6036 state->completed =
true;
6037 state->result_cv.notify_one();
6041 std::unique_lock<std::mutex> lock(state->mutex);
6043 state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
6044 [&] {
return state->completed; });
6048 resolve_thread.join();
6050 state->info =
nullptr;
6051 return state->result;
6054 resolve_thread.detach();
6059 (void)(timeout_sec);
6060 return getaddrinfo(node, service, hints, res);
6064template <
typename BindOrConnect>
6066 int address_family,
int socket_flags,
bool tcp_nodelay,
6068 BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
6070 const char *node =
nullptr;
6071 struct addrinfo hints;
6072 struct addrinfo *result;
6074 memset(&hints, 0,
sizeof(
struct addrinfo));
6075 hints.ai_socktype = SOCK_STREAM;
6076 hints.ai_protocol = IPPROTO_IP;
6081 hints.ai_family = AF_UNSPEC;
6082 hints.ai_flags = AI_NUMERICHOST;
6084 if (!host.empty()) { node = host.c_str(); }
6085 hints.ai_family = address_family;
6086 hints.ai_flags = socket_flags;
6089#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
6090 if (hints.ai_family == AF_UNIX) {
6091 const auto addrlen = host.length();
6092 if (addrlen >
sizeof(sockaddr_un::sun_path)) {
return INVALID_SOCKET; }
6095 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
6098 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
6103 addr.sun_family = AF_UNIX;
6106 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
6108 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
6109 hints.ai_addrlen =
static_cast<socklen_t
>(
6110 sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
6114 fcntl(sock, F_SETFD, FD_CLOEXEC);
6118 if (socket_options) { socket_options(sock); }
6127 if (!bind_or_connect(sock, hints, dummy)) {
6136 auto service = std::to_string(port);
6140#if defined __linux__ && !defined __ANDROID__
6147 for (
auto rp = result; rp; rp = rp->ai_next) {
6151 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
6152 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
6168 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
6174 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
6176 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
6182#if !defined _WIN32 && !defined SOCK_CLOEXEC
6183 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
6189 if (tcp_nodelay) {
set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
6191 if (rp->ai_family == AF_INET6) {
6192 set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
6195 if (socket_options) { socket_options(sock); }
6199 if (bind_or_connect(sock, *rp, quit)) {
return sock; }
6203 if (quit) {
break; }
6211 auto flags = nonblocking ? 1UL : 0UL;
6212 ioctlsocket(sock, FIONBIO, &flags);
6214 auto flags = fcntl(sock, F_GETFL, 0);
6215 fcntl(sock, F_SETFL,
6216 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
6222 return WSAGetLastError() != WSAEWOULDBLOCK;
6224 return errno != EINPROGRESS;
6229 struct addrinfo hints;
6230 struct addrinfo *result;
6232 memset(&hints, 0,
sizeof(
struct addrinfo));
6233 hints.ai_family = AF_UNSPEC;
6234 hints.ai_socktype = SOCK_STREAM;
6235 hints.ai_protocol = 0;
6244 for (
auto rp = result; rp; rp = rp->ai_next) {
6245 const auto &ai = *rp;
6246 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
6255#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
6260inline std::string
if2ip(
int address_family,
const std::string &ifn) {
6261 struct ifaddrs *ifap;
6265 std::string addr_candidate;
6266 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
6267 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
6268 (AF_UNSPEC == address_family ||
6269 ifa->ifa_addr->sa_family == address_family)) {
6270 if (ifa->ifa_addr->sa_family == AF_INET) {
6271 auto sa =
reinterpret_cast<struct sockaddr_in *
>(ifa->ifa_addr);
6272 char buf[INET_ADDRSTRLEN];
6273 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
6274 return std::string(buf, INET_ADDRSTRLEN);
6276 }
else if (ifa->ifa_addr->sa_family == AF_INET6) {
6277 auto sa =
reinterpret_cast<struct sockaddr_in6 *
>(ifa->ifa_addr);
6278 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
6279 char buf[INET6_ADDRSTRLEN] = {};
6280 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
6282 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
6283 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
6284 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
6286 return std::string(buf, INET6_ADDRSTRLEN);
6293 return addr_candidate;
6298 const std::string &host,
const std::string &ip,
int port,
6299 int address_family,
bool tcp_nodelay,
bool ipv6_v6only,
6300 SocketOptions socket_options, time_t connection_timeout_sec,
6301 time_t connection_timeout_usec, time_t read_timeout_sec,
6302 time_t read_timeout_usec, time_t write_timeout_sec,
6303 time_t write_timeout_usec,
const std::string &
intf,
Error &
error) {
6305 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
6306 std::move(socket_options),
6307 [&](
socket_t sock2,
struct addrinfo &ai,
bool &quit) ->
bool {
6308 if (!
intf.empty()) {
6310 auto ip_from_if =
if2ip(address_family,
intf);
6311 if (ip_from_if.empty()) { ip_from_if =
intf; }
6322 ::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
6330 connection_timeout_usec);
6341 write_timeout_usec);
6346 connection_timeout_sec);
6358 socklen_t addr_len, std::string &ip,
int &port) {
6359 if (addr.ss_family == AF_INET) {
6360 port = ntohs(
reinterpret_cast<const struct sockaddr_in *
>(&addr)->sin_port);
6361 }
else if (addr.ss_family == AF_INET6) {
6363 ntohs(
reinterpret_cast<const struct sockaddr_in6 *
>(&addr)->sin6_port);
6368 std::array<char, NI_MAXHOST> ipstr{};
6369 if (getnameinfo(
reinterpret_cast<const struct sockaddr *
>(&addr), addr_len,
6370 ipstr.data(),
static_cast<socklen_t
>(ipstr.size()),
nullptr,
6371 0, NI_NUMERICHOST)) {
6380 struct sockaddr_storage addr;
6381 socklen_t addr_len =
sizeof(addr);
6382 if (!getsockname(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
6389 struct sockaddr_storage addr;
6390 socklen_t addr_len =
sizeof(addr);
6392 if (!getpeername(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
6395 if (addr.ss_family == AF_UNIX) {
6396#if defined(__linux__)
6398 socklen_t len =
sizeof(ucred);
6399 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
6402#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
6404 socklen_t len =
sizeof(pid);
6405 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
6427 (((std::numeric_limits<unsigned int>::max)() >> 6) &
6429 static_cast<unsigned char>(*s));
6432inline unsigned int str2tag(
const std::string &s) {
6439 h = (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^
6440 static_cast<unsigned char>(c);
6447inline constexpr unsigned int operator""_t(
const char *s,
size_t l) {
6455 const std::map<std::string, std::string> &user_data,
6456 const std::string &default_content_type) {
6459 auto it = user_data.find(ext);
6460 if (it != user_data.end()) {
return it->second; }
6462 using udl::operator
""_t;
6465 default:
return default_content_type;
6467 case "css"_t:
return "text/css";
6468 case "csv"_t:
return "text/csv";
6470 case "html"_t:
return "text/html";
6472 case "mjs"_t:
return "text/javascript";
6473 case "txt"_t:
return "text/plain";
6474 case "vtt"_t:
return "text/vtt";
6476 case "apng"_t:
return "image/apng";
6477 case "avif"_t:
return "image/avif";
6478 case "bmp"_t:
return "image/bmp";
6479 case "gif"_t:
return "image/gif";
6480 case "png"_t:
return "image/png";
6481 case "svg"_t:
return "image/svg+xml";
6482 case "webp"_t:
return "image/webp";
6483 case "ico"_t:
return "image/x-icon";
6484 case "tif"_t:
return "image/tiff";
6485 case "tiff"_t:
return "image/tiff";
6487 case "jpeg"_t:
return "image/jpeg";
6489 case "mp4"_t:
return "video/mp4";
6490 case "mpeg"_t:
return "video/mpeg";
6491 case "webm"_t:
return "video/webm";
6493 case "mp3"_t:
return "audio/mp3";
6494 case "mpga"_t:
return "audio/mpeg";
6495 case "weba"_t:
return "audio/webm";
6496 case "wav"_t:
return "audio/wave";
6498 case "otf"_t:
return "font/otf";
6499 case "ttf"_t:
return "font/ttf";
6500 case "woff"_t:
return "font/woff";
6501 case "woff2"_t:
return "font/woff2";
6503 case "7z"_t:
return "application/x-7z-compressed";
6504 case "atom"_t:
return "application/atom+xml";
6505 case "pdf"_t:
return "application/pdf";
6506 case "json"_t:
return "application/json";
6507 case "rss"_t:
return "application/rss+xml";
6508 case "tar"_t:
return "application/x-tar";
6510 case "xhtml"_t:
return "application/xhtml+xml";
6511 case "xslt"_t:
return "application/xslt+xml";
6512 case "xml"_t:
return "application/xml";
6513 case "gz"_t:
return "application/gzip";
6514 case "zip"_t:
return "application/zip";
6515 case "wasm"_t:
return "application/wasm";
6521 std::map<std::string, std::string> *params =
nullptr) {
6524 auto media_type = content_type;
6525 auto semicolon_pos = media_type.find(
';');
6526 if (semicolon_pos != std::string::npos) {
6527 auto param_str = media_type.substr(semicolon_pos + 1);
6528 media_type = media_type.substr(0, semicolon_pos);
6532 split(param_str.data(), param_str.data() + param_str.size(),
';',
6533 [&](
const char *b,
const char *
e) {
6536 split(b, e,
'=', [&](const char *b2, const char *e2) {
6555 using udl::operator
""_t;
6561 case "image/svg+xml"_t:
6562 case "application/javascript"_t:
6563 case "application/x-javascript"_t:
6564 case "application/json"_t:
6565 case "application/ld+json"_t:
6566 case "application/xml"_t:
6567 case "application/xhtml+xml"_t:
6568 case "application/rss+xml"_t:
6569 case "application/atom+xml"_t:
6570 case "application/xslt+xml"_t:
6571 case "application/protobuf"_t:
return true;
6573 case "text/event-stream"_t:
return false;
6575 default:
return !mime_type.rfind(
"text/", 0);
6585 const char *params_b =
nullptr;
6586 std::size_t params_len = 0;
6589 b,
static_cast<std::size_t
>(
e - b),
';',
6590 [&](
const char *lb, std::size_t llen,
const char *rb, std::size_t rlen) {
6591 auto r =
trim(lb, lb + llen, 0, llen);
6592 if (r.first < r.second) { token.assign(lb + r.first, lb + r.second); }
6597 if (token.empty()) {
return false; }
6598 if (params_len == 0) {
return true; }
6601 bool invalid =
false;
6602 split_find(params_b, params_b + params_len,
';',
6603 (std::numeric_limits<size_t>::max)(),
6604 [&](
const char *pb,
const char *pe) ->
bool {
6606 auto len =
static_cast<size_t>(pe - pb);
6607 if (len < 2) {
return false; }
6608 if ((pb[0] !=
'q' && pb[0] !=
'Q') || pb[1] !=
'=') {
6613 auto r =
trim(pb, pe, 2, len);
6614 if (r.first >= r.second) {
6620 auto res =
from_chars(pb + r.first, pb + r.second, v);
6621 if (res.ec != std::errc{} || v < 0.0 || v > 1.0) {
6643 double best_q = 0.0;
6656 split(s.data(), s.data() + s.size(),
',', [&](
const char *b,
const char *
e) {
6657 double quality = 1.0;
6658 if (!parse_quality(b, e, name, quality)) { return; }
6659 if (quality <= 0.0) {
return; }
6662#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6665#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6670#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6679 if (quality > best_q ||
6680 (quality == best_q && priority(type) < priority(best))) {
6690#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6695#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6700#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6720 if (!data_length) {
return true; }
6721 return callback(
data, data_length);
6724#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6725inline gzip_compressor::gzip_compressor() {
6726 std::memset(&strm_, 0,
sizeof(strm_));
6735inline gzip_compressor::~gzip_compressor() {
deflateEnd(&strm_); }
6737inline bool gzip_compressor::compress(
const char *
data,
size_t data_length,
6738 bool last, Callback callback) {
6742 constexpr size_t max_avail_in =
6743 (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
6745 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
6746 (std::min)(data_length, max_avail_in));
6747 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(
data));
6749 data_length -= strm_.avail_in;
6750 data += strm_.avail_in;
6755 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6757 strm_.avail_out =
static_cast<uInt>(buff.size());
6758 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
6763 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
6766 }
while (strm_.avail_out == 0);
6770 assert(strm_.avail_in == 0);
6771 }
while (data_length > 0);
6776inline gzip_decompressor::gzip_decompressor() {
6777 std::memset(&strm_, 0,
sizeof(strm_));
6789inline gzip_decompressor::~gzip_decompressor() {
inflateEnd(&strm_); }
6791inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
6793inline bool gzip_decompressor::decompress(
const char *
data,
size_t data_length,
6794 Callback callback) {
6800 constexpr size_t max_avail_in =
6801 (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
6803 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
6804 (std::min)(data_length, max_avail_in));
6805 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(
data));
6807 data_length -= strm_.avail_in;
6808 data += strm_.avail_in;
6810 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6811 while (strm_.avail_in > 0 &&
ret ==
Z_OK) {
6812 strm_.avail_out = static_cast<uInt>(buff.size());
6813 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
6815 ret = inflate(&strm_, Z_NO_FLUSH);
6817 assert(ret != Z_STREAM_ERROR);
6821 case Z_MEM_ERROR: inflateEnd(&strm_); return false;
6824 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
6831 }
while (data_length > 0);
6837#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6838inline brotli_compressor::brotli_compressor() {
6839 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
6842inline brotli_compressor::~brotli_compressor() {
6843 BrotliEncoderDestroyInstance(state_);
6846inline bool brotli_compressor::compress(
const char *
data,
size_t data_length,
6847 bool last, Callback callback) {
6848 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6850 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
6851 auto available_in = data_length;
6852 auto next_in =
reinterpret_cast<const uint8_t *
>(
data);
6856 if (BrotliEncoderIsFinished(state_)) {
break; }
6858 if (!available_in) {
break; }
6861 auto available_out = buff.size();
6862 auto next_out = buff.data();
6864 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
6865 &available_out, &next_out,
nullptr)) {
6869 auto output_bytes = buff.size() - available_out;
6871 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
6878inline brotli_decompressor::brotli_decompressor() {
6879 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
6880 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
6881 : BROTLI_DECODER_RESULT_ERROR;
6884inline brotli_decompressor::~brotli_decompressor() {
6885 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
6888inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
6890inline bool brotli_decompressor::decompress(
const char *
data,
6892 Callback callback) {
6893 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6894 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
6898 auto next_in =
reinterpret_cast<const uint8_t *
>(
data);
6899 size_t avail_in = data_length;
6902 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
6904 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6905 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
6906 char *next_out = buff.data();
6907 size_t avail_out = buff.size();
6909 decoder_r = BrotliDecoderDecompressStream(
6910 decoder_s, &avail_in, &next_in, &avail_out,
6911 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
6913 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return false; }
6915 if (!callback(buff.data(), buff.size() - avail_out)) {
return false; }
6918 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6919 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
6923#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6924inline zstd_compressor::zstd_compressor() {
6925 ctx_ = ZSTD_createCCtx();
6926 ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
6929inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
6931inline bool zstd_compressor::compress(
const char *
data,
size_t data_length,
6932 bool last, Callback callback) {
6933 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6935 ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
6936 ZSTD_inBuffer input = {
data, data_length, 0};
6941 size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
6943 if (ZSTD_isError(remaining)) {
return false; }
6945 if (!callback(buff.data(), output.pos)) {
return false; }
6947 finished = last ? (remaining == 0) : (input.pos == input.
size);
6949 }
while (!finished);
6954inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
6956inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
6958inline bool zstd_decompressor::is_valid()
const {
return ctx_ !=
nullptr; }
6960inline bool zstd_decompressor::decompress(
const char *
data,
size_t data_length,
6961 Callback callback) {
6962 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6963 ZSTD_inBuffer input = {
data, data_length, 0};
6965 while (input.pos < input.size) {
6967 size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
6969 if (ZSTD_isError(remaining)) {
return false; }
6971 if (!callback(buff.data(), output.pos)) {
return false; }
6978inline std::unique_ptr<decompressor>
6982 if (encoding ==
"gzip" || encoding ==
"deflate") {
6983#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6986 }
else if (encoding.find(
"br") != std::string::npos) {
6987#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6990 }
else if (encoding ==
"zstd" || encoding.find(
"zstd") != std::string::npos) {
6991#ifdef CPPHTTPLIB_ZSTD_SUPPORT
7001inline std::pair<std::unique_ptr<compressor>,
const char *>
7003#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7005#elif defined(CPPHTTPLIB_ZLIB_SUPPORT)
7007#elif defined(CPPHTTPLIB_ZSTD_SUPPORT)
7010 return {
nullptr,
nullptr};
7015 using udl::operator
""_t;
7018 case "REMOTE_ADDR"_t:
7019 case "REMOTE_PORT"_t:
7020 case "LOCAL_ADDR"_t:
7021 case "LOCAL_PORT"_t:
return true;
7022 default:
return false;
7028 return headers.find(key) != headers.end();
7032 const std::string &key,
const char *def,
7035#ifndef CPPHTTPLIB_NO_EXCEPTIONS
7036 std::string msg =
"Prohibited header name '" + key +
"' is specified.";
7037 throw std::invalid_argument(msg);
7043 auto rng = headers.equal_range(key);
7044 auto it = rng.first;
7045 std::advance(it,
static_cast<ssize_t
>(
id));
7046 if (it != rng.second) {
return it->second.c_str(); }
7051 const std::string &key) {
7052 auto r = headers.equal_range(key);
7053 return static_cast<size_t>(std::distance(r.first, r.second));
7056template <
typename Map>
7057inline typename Map::mapped_type
7059 auto rng = m.equal_range(key);
7060 auto it = rng.first;
7061 std::advance(it,
static_cast<ssize_t
>(
id));
7062 if (it != rng.second) {
return it->second; }
7063 return typename Map::mapped_type();
7067 const std::string &val) {
7069 headers.emplace(key, val);
7074 const auto bufsiz = 2048;
7078 size_t header_count = 0;
7081 if (!line_reader.
getline()) {
return false; }
7084 auto line_terminator_len = 2;
7087 if (line_reader.
size() == 2) {
break; }
7089#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7091 if (line_reader.
size() == 1) {
break; }
7092 line_terminator_len = 1;
7104 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
7107 [&](
const std::string &key,
const std::string &val) {
7108 headers.emplace(key, val);
7118 auto cl_range = headers.equal_range(
"Content-Length");
7119 if (cl_range.first != cl_range.second) {
7120 const auto &first_val = cl_range.first->second;
7121 for (
auto it = std::next(cl_range.first); it != cl_range.second; ++it) {
7122 if (it->second != first_val) {
return false; }
7130 const std::string &expected_accept,
7131 std::string &selected_subprotocol) {
7133 const auto bufsiz = 2048;
7136 if (!line_reader.
getline()) {
return false; }
7139 auto line = std::string(line_reader.
ptr(), line_reader.
size());
7140 if (line.find(
"HTTP/1.1 101") == std::string::npos) {
return false; }
7147 auto upgrade_it = headers.find(
"Upgrade");
7148 if (upgrade_it == headers.end()) {
return false; }
7150 if (upgrade_val !=
"websocket") {
return false; }
7153 auto connection_it = headers.find(
"Connection");
7154 if (connection_it == headers.end()) {
return false; }
7156 if (connection_val.find(
"upgrade") == std::string::npos) {
return false; }
7159 auto it = headers.find(
"Sec-WebSocket-Accept");
7160 if (it == headers.end() || it->second != expected_accept) {
return false; }
7163 auto proto_it = headers.find(
"Sec-WebSocket-Protocol");
7164 if (proto_it != headers.end()) { selected_subprotocol = proto_it->second; }
7178 size_t payload_max_length = (std::numeric_limits<size_t>::max)()) {
7192 auto read_len =
static_cast<size_t>(len - r);
7203 if (!out(buf,
static_cast<size_t>(n), r, len)) {
7206 r +=
static_cast<size_t>(n);
7216inline ReadContentResult
7227 if (r > payload_max_length ||
7228 payload_max_length - r <
static_cast<size_t>(n)) {
7232 if (!out(buf,
static_cast<size_t>(n), r, 0)) {
7235 r +=
static_cast<size_t>(n);
7241template <
typename T>
7243 size_t payload_max_length,
7248 size_t total_len = 0;
7251 size_t chunk_offset = 0;
7252 size_t chunk_total = 0;
7253 auto n = dec.
read_payload(buf,
sizeof(buf), chunk_offset, chunk_total);
7263 if (total_len > payload_max_length ||
7264 payload_max_length - total_len <
static_cast<size_t>(n)) {
7268 if (!out(buf,
static_cast<size_t>(n), chunk_offset, chunk_total)) {
7272 total_len +=
static_cast<size_t>(n);
7281template <
typename T,
typename U>
7284 bool decompress,
size_t payload_max_length,
7285 bool &exceed_payload_max_length, U callback) {
7287 std::string encoding = x.get_header_value(
"Content-Encoding");
7290 if (!encoding.empty()) {
7301 size_t decompressed_size = 0;
7303 size_t off,
size_t len) {
7305 buf, n, [&](
const char *buf2,
size_t n2) {
7308 if (payload_max_length > 0 &&
7309 (decompressed_size >= payload_max_length ||
7310 n2 > payload_max_length - decompressed_size)) {
7311 exceed_payload_max_length =
true;
7314 decompressed_size += n2;
7315 return receiver(buf2, n2, off, len);
7318 return callback(std::move(out));
7328 return receiver(buf, n, off, len);
7330 return callback(std::move(out));
7333template <
typename T>
7337 bool exceed_payload_max_length =
false;
7339 x, status, std::move(receiver), decompress, payload_max_length,
7351 exceed_payload_max_length =
true;
7356 }
else if (!
has_header(x.headers,
"Content-Length")) {
7362 exceed_payload_max_length =
true;
7368 auto is_invalid_value =
false;
7370 (std::numeric_limits<size_t>::max)(),
7371 0, is_invalid_value);
7373 if (is_invalid_value) {
7375 }
else if (len > 0) {
7377 strm, len, std::move(progress), out, payload_max_length);
7380 exceed_payload_max_length =
true;
7394 const std::string &path) {
7395 std::string s = method;
7398 s +=
" HTTP/1.1\r\n";
7399 return strm.
write(s.data(), s.size());
7403 std::string s =
"HTTP/1.1 ";
7404 s += std::to_string(status);
7408 return strm.
write(s.data(), s.size());
7412 ssize_t write_len = 0;
7413 for (
const auto &x : headers) {
7420 auto len = strm.
write(s.data(), s.size());
7421 if (len < 0) {
return len; }
7424 auto len = strm.
write(
"\r\n");
7425 if (len < 0) {
return len; }
7432 while (offset < l) {
7433 auto length = strm.
write(
d + offset, l - offset);
7434 if (length < 0) {
return false; }
7435 offset +=
static_cast<size_t>(length);
7440template <
typename T>
7443 size_t offset,
size_t length,
7447 size_t end_offset = offset + length;
7448 size_t start_offset = offset;
7452 data_sink.
write = [&](
const char *
d,
size_t l) ->
bool {
7457 if (upload_progress && length > 0) {
7458 size_t current_written = offset - start_offset;
7459 if (!upload_progress(current_written, length)) {
7473 while (offset < end_offset && !is_shutting_down()) {
7477 }
else if (!content_provider(offset, end_offset - offset, data_sink)) {
7486 if (offset < end_offset) {
7495template <
typename T>
7497 size_t offset,
size_t length,
T is_shutting_down,
7500 is_shutting_down,
nullptr,
error);
7503template <
typename T>
7505 size_t offset,
size_t length,
7506 const T &is_shutting_down) {
7508 return write_content(strm, content_provider, offset, length, is_shutting_down,
7512template <
typename T>
7516 const T &is_shutting_down) {
7518 auto data_available =
true;
7522 data_sink.
write = [&](
const char *
d,
size_t l) ->
bool {
7532 data_sink.
done = [&](void) { data_available =
false; };
7534 while (data_available && !is_shutting_down()) {
7537 }
else if (!content_provider(offset, 0, data_sink)) {
7543 return !data_available;
7547template <
typename T,
typename U>
7552 auto data_available =
true;
7556 data_sink.
write = [&](
const char *
d,
size_t l) ->
bool {
7558 data_available = l > 0;
7561 std::string payload;
7563 [&](
const char *
data,
size_t data_len) {
7564 payload.append(data, data_len);
7567 if (!payload.empty()) {
7571 if (!
write_data(strm, chunk.data(), chunk.size())) { ok =
false; }
7582 auto done_with_trailer = [&](
const Headers *trailer) {
7583 if (!ok) {
return; }
7585 data_available =
false;
7587 std::string payload;
7589 [&](
const char *
data,
size_t data_len) {
7590 payload.append(data, data_len);
7597 if (!payload.empty()) {
7599 auto chunk =
from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
7600 if (!
write_data(strm, chunk.data(), chunk.size())) {
7606 constexpr const char done_marker[] =
"0\r\n";
7611 for (
const auto &kv : *trailer) {
7612 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
7613 if (!
write_data(strm, field_line.data(), field_line.size())) {
7619 constexpr const char crlf[] =
"\r\n";
7623 data_sink.
done = [&](void) { done_with_trailer(
nullptr); };
7626 done_with_trailer(&trailer);
7629 while (data_available && !is_shutting_down()) {
7633 }
else if (!content_provider(offset, 0, data_sink)) {
7642 if (data_available) {
7651template <
typename T,
typename U>
7660template <
typename T>
7662 const std::string &path,
const std::string &location,
7665 new_req.
path = path;
7671 new_req.
body.clear();
7677 auto ret = cli.send(new_req, new_res,
error);
7679 req = std::move(new_req);
7680 res = std::move(new_res);
7690 for (
auto it = params.begin(); it != params.end(); ++it) {
7691 if (it != params.begin()) { query +=
'&'; }
7701 std::set<std::string> cache;
7703 std::string kv(b,
e);
7704 if (cache.find(kv) != cache.end()) {
return; }
7705 cache.insert(std::move(kv));
7709 divide(b,
static_cast<std::size_t
>(
e - b),
'=',
7710 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data,
7711 std::size_t rhs_size) {
7712 key.assign(lhs_data, lhs_size);
7713 val.assign(rhs_data, rhs_size);
7732 split(query.data(), query.data() + query.size(),
'&',
7733 [&](
const char *b,
const char *
e) {
7736 divide(b, static_cast<std::size_t>(e - b),
'=',
7737 [&](const char *lhs_data, std::size_t lhs_size,
7738 const char *rhs_data, std::size_t rhs_size) {
7739 key.assign(lhs_data, lhs_size);
7740 val.assign(rhs_data, rhs_size);
7747 if (!result.empty()) { result +=
'&'; }
7749 if (!val.empty() || std::find(b,
e,
'=') !=
e) {
7759 std::string &boundary) {
7760 std::map<std::string, std::string> params;
7762 auto it = params.find(
"boundary");
7763 if (it == params.end()) {
return false; }
7764 boundary = it->second;
7765 return !boundary.empty();
7769 std::set<std::string> cache;
7770 split(s.data(), s.data() + s.size(),
';', [&](
const char *b,
const char *
e) {
7771 std::string kv(b, e);
7772 if (cache.find(kv) != cache.end()) { return; }
7777 split(b,
e,
'=', [&](
const char *b2,
const char *e2) {
7786 params.emplace(trim_double_quotes_copy((key)),
7787 trim_double_quotes_copy((val)));
7792#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7793inline bool parse_range_header(
const std::string &s, Ranges &ranges) {
7797 auto is_valid = [](
const std::string &
str) {
7798 return std::all_of(
str.cbegin(),
str.cend(),
7799 [](
unsigned char c) { return std::isdigit(c); });
7802 if (s.size() > 7 && s.compare(0, 6,
"bytes=") == 0) {
7803 const auto pos =
static_cast<size_t>(6);
7804 const auto len =
static_cast<size_t>(s.size() - 6);
7805 auto all_valid_ranges =
true;
7806 split(&s[pos], &s[pos + len],
',', [&](
const char *b,
const char *
e) {
7807 if (!all_valid_ranges) {
return; }
7809 const auto it = std::find(b,
e,
'-');
7811 all_valid_ranges =
false;
7815 const auto lhs = std::string(b, it);
7816 const auto rhs = std::string(it + 1,
e);
7817 if (!is_valid(lhs) || !is_valid(rhs)) {
7818 all_valid_ranges =
false;
7826 if (res.ec == std::errc{}) { first = v; }
7833 if (res.ec == std::errc{}) { last = v; }
7836 if ((first == -1 && last == -1) ||
7837 (first != -1 && last != -1 && first > last)) {
7838 all_valid_ranges =
false;
7842 ranges.emplace_back(first, last);
7844 return all_valid_ranges && !ranges.empty();
7847#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7850}
catch (...) {
return false; }
7854 std::vector<std::string> &content_types) {
7855 content_types.clear();
7858 if (s.empty()) {
return true; }
7861 if (s.front() ==
',' || s.back() ==
',' ||
7862 s.find(
",,") != std::string::npos) {
7866 struct AcceptEntry {
7867 std::string media_type;
7872 std::vector<AcceptEntry> entries;
7874 bool has_invalid_entry =
false;
7877 split(s.data(), s.data() + s.size(),
',', [&](
const char *b,
const char *
e) {
7878 std::string entry(b, e);
7879 entry = trim_copy(entry);
7881 if (entry.empty()) {
7882 has_invalid_entry = true;
7886 AcceptEntry accept_entry;
7887 accept_entry.order = order++;
7889 if (!
parse_quality(entry.data(), entry.data() + entry.size(),
7890 accept_entry.media_type, accept_entry.quality)) {
7891 has_invalid_entry = true;
7899 if (accept_entry.media_type.empty()) {
7900 has_invalid_entry = true;
7905 if (accept_entry.media_type !=
"*" &&
7906 accept_entry.media_type.find(
'/') == std::string::npos) {
7907 has_invalid_entry = true;
7911 entries.push_back(std::move(accept_entry));
7915 if (has_invalid_entry) {
return false; }
7918 std::sort(entries.begin(), entries.end(),
7919 [](
const AcceptEntry &a,
const AcceptEntry &b) {
7920 if (a.quality != b.quality) {
7921 return a.quality > b.quality;
7923 return a.order < b.order;
7927 content_types.reserve(entries.size());
7928 for (
auto &entry : entries) {
7929 content_types.push_back(std::move(entry.media_type));
7940 boundary_ = std::move(boundary);
7941 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
7942 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
7952 while (buf_size() > 0) {
7955 auto pos = buf_find(dash_boundary_crlf_);
7956 if (pos == buf_size()) {
return true; }
7957 buf_erase(pos + dash_boundary_crlf_.size());
7967 auto pos = buf_find(crlf_);
7969 while (pos < buf_size()) {
7972 if (!header_callback(file_)) {
7976 buf_erase(crlf_.size());
7981 const auto header = buf_head(pos);
7984 [&](
const std::string &,
const std::string &) {})) {
7992 [&](
const std::string &key,
const std::string &val) {
7993 file_.headers.emplace(key, val);
7999 constexpr const char header_content_type[] =
"Content-Type:";
8001 if (start_with_case_ignore(
header, header_content_type)) {
8002 file_.content_type =
8005 std::string disposition_params;
8006 if (parse_content_disposition(
header, disposition_params)) {
8010 auto it = params.find(
"name");
8011 if (it != params.end()) {
8012 file_.name = it->second;
8018 it = params.find(
"filename");
8019 if (it != params.end()) { file_.filename = it->second; }
8021 it = params.find(
"filename*");
8022 if (it != params.end()) {
8024 const auto &val = it->second;
8025 constexpr const char utf8_prefix[] =
"UTF-8''";
8026 constexpr size_t prefix_len =
str_len(utf8_prefix);
8027 if (val.size() > prefix_len &&
8028 start_with_case_ignore(val, utf8_prefix)) {
8030 val.substr(prefix_len));
8038 buf_erase(pos + crlf_.size());
8039 pos = buf_find(crlf_);
8041 if (state_ != 3) {
return true; }
8045 if (crlf_dash_boundary_.size() > buf_size()) {
return true; }
8046 auto pos = buf_find(crlf_dash_boundary_);
8047 if (pos < buf_size()) {
8048 if (!content_callback(buf_data(), pos)) {
8052 buf_erase(pos + crlf_dash_boundary_.size());
8055 auto len = buf_size() - crlf_dash_boundary_.size();
8057 if (!content_callback(buf_data(), len)) {
8068 if (crlf_.size() > buf_size()) {
return true; }
8069 if (buf_start_with(crlf_)) {
8070 buf_erase(crlf_.size());
8073 if (dash_.size() > buf_size()) {
return true; }
8074 if (buf_start_with(dash_)) {
8075 buf_erase(dash_.size());
8077 buf_erase(buf_size());
8091 void clear_file_info() {
8093 file_.filename.clear();
8094 file_.content_type.clear();
8095 file_.headers.clear();
8098 bool start_with_case_ignore(
const std::string &a,
const char *b,
8099 size_t offset = 0)
const {
8100 const auto b_len = strlen(b);
8101 if (a.size() < offset + b_len) {
return false; }
8102 for (
size_t i = 0; i < b_len; i++) {
8103 if (case_ignore::to_lower(a[offset + i]) != case_ignore::to_lower(b[i])) {
8112 bool parse_content_disposition(
const std::string &
header,
8113 std::string ¶ms_out)
const {
8114 constexpr const char prefix[] =
"Content-Disposition:";
8115 constexpr size_t prefix_len =
str_len(prefix);
8117 if (!start_with_case_ignore(
header, prefix)) {
return false; }
8120 auto pos = prefix_len;
8126 constexpr const char form_data[] =
"form-data;";
8127 constexpr size_t form_data_len =
str_len(form_data);
8128 if (!start_with_case_ignore(
header, form_data, pos)) {
return false; }
8129 pos += form_data_len;
8136 params_out =
header.substr(pos);
8140 const std::string dash_ =
"--";
8141 const std::string crlf_ =
"\r\n";
8142 std::string boundary_;
8143 std::string dash_boundary_crlf_;
8144 std::string crlf_dash_boundary_;
8147 bool is_valid_ =
false;
8151 bool start_with(
const std::string &a,
size_t spos,
size_t epos,
8152 const std::string &b)
const {
8153 if (epos - spos < b.size()) {
return false; }
8154 for (
size_t i = 0; i < b.size(); i++) {
8155 if (a[i + spos] != b[i]) {
return false; }
8160 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
8162 const char *buf_data()
const {
return &buf_[buf_spos_]; }
8164 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_, l); }
8166 bool buf_start_with(
const std::string &s)
const {
8167 return start_with(buf_, buf_spos_, buf_epos_, s);
8170 size_t buf_find(
const std::string &s)
const {
8173 size_t off = buf_spos_;
8174 while (off < buf_epos_) {
8177 if (pos == buf_epos_) {
return buf_size(); }
8178 if (buf_[pos] == c) {
break; }
8182 auto remaining_size = buf_epos_ - pos;
8183 if (s.size() > remaining_size) {
return buf_size(); }
8185 if (start_with(buf_, pos, buf_epos_, s)) {
return pos - buf_spos_; }
8193 void buf_append(
const char *
data,
size_t n) {
8194 auto remaining_size = buf_size();
8195 if (remaining_size > 0 && buf_spos_ > 0) {
8196 for (
size_t i = 0; i < remaining_size; i++) {
8197 buf_[i] = buf_[buf_spos_ + i];
8201 buf_epos_ = remaining_size;
8203 if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
8205 for (
size_t i = 0; i < n; i++) {
8206 buf_[buf_epos_ + i] =
data[i];
8211 void buf_erase(
size_t size) { buf_spos_ +=
size; }
8214 size_t buf_spos_ = 0;
8215 size_t buf_epos_ = 0;
8219 constexpr const char data[] =
8220 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8222 thread_local auto engine([]() {
8226 std::random_device seed_gen;
8228 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
8229 return std::mt19937(seed_sequence);
8233 for (
size_t i = 0; i < length; i++) {
8234 result +=
data[engine() % (
sizeof(
data) - 1)];
8245 for (
size_t i = 0; i < boundary.size(); i++) {
8246 auto c = boundary[i];
8247 if (!std::isalnum(c) && c !=
'-' && c !=
'_') {
8255template <
typename T>
8258 const std::string &boundary) {
8259 std::string body =
"--" + boundary +
"\r\n";
8260 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
8261 if (!item.filename.empty()) {
8262 body +=
"; filename=\"" + item.filename +
"\"";
8265 if (!item.content_type.empty()) {
8266 body +=
"Content-Type: " + item.content_type +
"\r\n";
8277 return "--" + boundary +
"--\r\n";
8282 return "multipart/form-data; boundary=" + boundary;
8287 const std::string &boundary,
bool finish =
true) {
8290 for (
const auto &item : items) {
8301 const std::string &boundary) {
8303 for (
const auto &item : items) {
8305 total += item.content.size();
8321 const std::string &boundary) {
8323 std::vector<std::string> owned;
8324 owned.reserve(items.size() + 1);
8325 for (
const auto &item : items)
8330 std::vector<MultipartSegment> segs;
8331 segs.reserve(items.size() * 3 + 1);
8332 static const char crlf[] =
"\r\n";
8333 for (
size_t i = 0; i < items.size(); i++) {
8334 segs.push_back({owned[i].data(), owned[i].size()});
8335 segs.push_back({items[i].content.data(), items[i].content.size()});
8336 segs.push_back({crlf, 2});
8338 segs.push_back({owned.back().data(), owned.back().size()});
8340 struct MultipartState {
8341 std::vector<std::string> owned;
8342 std::vector<MultipartSegment> segs;
8345 auto state = std::make_shared<MultipartState>();
8346 state->owned = std::move(owned);
8349 state->segs = std::move(segs);
8351 return [state](
size_t offset,
size_t length,
DataSink &sink) ->
bool {
8354 auto &buf = state->buf;
8355 auto buf_size = buf.size();
8357 size_t remaining = length;
8362 for (; seg_idx < state->segs.size(); seg_idx++) {
8363 const auto &seg = state->segs[seg_idx];
8364 if (seg.size > 0 && offset - pos < seg.size) {
break; }
8368 size_t seg_offset = (seg_idx < state->segs.size()) ? offset - pos : 0;
8370 for (; seg_idx < state->segs.size() && remaining > 0; seg_idx++) {
8371 const auto &seg = state->segs[seg_idx];
8372 size_t available = seg.size - seg_offset;
8373 size_t to_copy = (std::min)(available, remaining);
8374 const char *
src = seg.data + seg_offset;
8377 while (to_copy > 0) {
8378 size_t space = buf_size - buf_len;
8379 size_t chunk = (std::min)(to_copy, space);
8380 std::memcpy(buf.data() + buf_len,
src, chunk);
8386 if (buf_len == buf_size) {
8387 if (!sink.write(buf.data(), buf_len)) {
return false; }
8393 if (buf_len > 0) {
return sink.write(buf.data(), buf_len); }
8399 if (ranges.size() <= 1)
return;
8402 std::sort(ranges.begin(), ranges.end(),
8403 [](
const Range &a,
const Range &b) { return a.first < b.first; });
8406 coalesced.reserve(ranges.size());
8408 for (
auto &r : ranges) {
8409 auto first_pos = r.first;
8410 auto last_pos = r.second;
8413 if (first_pos == -1 && last_pos == -1) {
8415 last_pos =
static_cast<ssize_t
>(content_length);
8418 if (first_pos == -1) {
8419 first_pos =
static_cast<ssize_t
>(content_length) - last_pos;
8420 last_pos =
static_cast<ssize_t
>(content_length) - 1;
8423 if (last_pos == -1 || last_pos >=
static_cast<ssize_t
>(content_length)) {
8424 last_pos =
static_cast<ssize_t
>(content_length) - 1;
8428 if (!(0 <= first_pos && first_pos <= last_pos &&
8429 last_pos <
static_cast<ssize_t
>(content_length))) {
8435 if (!coalesced.empty()) {
8436 auto &prev = coalesced.back();
8439 if (first_pos <= prev.second + 1 &&
8440 !(first_pos == prev.first && last_pos == prev.second)) {
8442 prev.second = (std::max)(prev.second, last_pos);
8448 coalesced.emplace_back(first_pos, last_pos);
8451 ranges = std::move(coalesced);
8464 ssize_t content_len =
static_cast<ssize_t
>(
8467 std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
8468 size_t overwrapping_count = 0;
8477 for (
auto &r : req.
ranges) {
8478 auto &first_pos = r.first;
8479 auto &last_pos = r.second;
8481 if (first_pos == -1 && last_pos == -1) {
8483 last_pos = content_len;
8486 if (first_pos == -1) {
8487 first_pos = content_len - last_pos;
8488 last_pos = content_len - 1;
8500 if (last_pos == -1 || last_pos >= content_len) {
8501 last_pos = content_len - 1;
8505 if (!(0 <= first_pos && first_pos <= last_pos &&
8506 last_pos <= content_len - 1)) {
8511 for (
const auto &processed_range : processed_ranges) {
8512 if (!(last_pos < processed_range.first ||
8513 first_pos > processed_range.second)) {
8514 overwrapping_count++;
8515 if (overwrapping_count > 2) {
return true; }
8520 processed_ranges.emplace_back(first_pos, last_pos);
8530inline std::pair<size_t, size_t>
8532 assert(r.first != -1 && r.second != -1);
8533 assert(0 <= r.first && r.first <
static_cast<ssize_t
>(content_length));
8534 assert(r.first <= r.second &&
8535 r.second <
static_cast<ssize_t
>(content_length));
8536 (void)(content_length);
8537 return std::make_pair(
static_cast<size_t>(r.first),
8538 static_cast<size_t>(r.second - r.first) + 1);
8542 const std::pair<size_t, size_t> &offset_and_length,
size_t content_length) {
8543 auto st = offset_and_length.first;
8544 auto ed = st + offset_and_length.second - 1;
8546 std::string field =
"bytes ";
8547 field += std::to_string(st);
8549 field += std::to_string(ed);
8551 field += std::to_string(content_length);
8555template <
typename SToken,
typename CToken,
typename Content>
8557 const std::string &boundary,
8558 const std::string &content_type,
8559 size_t content_length, SToken stoken,
8560 CToken ctoken, Content content) {
8561 for (
size_t i = 0; i < req.
ranges.size(); i++) {
8565 if (!content_type.empty()) {
8566 ctoken(
"Content-Type: ");
8567 stoken(content_type);
8571 auto offset_and_length =
8574 ctoken(
"Content-Range: ");
8579 if (!content(offset_and_length.first, offset_and_length.second)) {
8593 const std::string &boundary,
8594 const std::string &content_type,
8595 size_t content_length,
8596 std::string &
data) {
8598 req, boundary, content_type, content_length,
8599 [&](
const std::string &token) {
data += token; },
8600 [&](
const std::string &token) {
data += token; },
8601 [&](
size_t offset,
size_t length) {
8602 assert(offset + length <= content_length);
8603 data += res.
body.substr(offset, length);
8609 const std::string &boundary,
8610 const std::string &content_type,
8611 size_t content_length) {
8612 size_t data_length = 0;
8615 req, boundary, content_type, content_length,
8616 [&](
const std::string &token) { data_length += token.size(); },
8617 [&](
const std::string &token) { data_length += token.size(); },
8618 [&](
size_t ,
size_t length) {
8619 data_length += length;
8626template <
typename T>
8629 const std::string &boundary,
8630 const std::string &content_type,
8631 size_t content_length,
const T &is_shutting_down) {
8633 req, boundary, content_type, content_length,
8634 [&](
const std::string &token) { strm.
write(token); },
8635 [&](
const std::string &token) { strm.
write(token); },
8636 [&](
size_t offset,
size_t length) {
8649 if (conn ==
"close") {
return false; }
8650 if (req.
version ==
"HTTP/1.0" && conn !=
"Keep-Alive") {
return false; }
8656 req.
method ==
"DELETE") {
8667 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ =
true;
8671 if (is_valid_) WSACleanup();
8674 bool is_valid_ =
false;
8677static WSInit wsinit_;
8681 std::map<std::string, std::string> &auth,
8683 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
8685 thread_local auto re =
8686 std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
8688 auto pos = s.find(
' ');
8689 if (pos != std::string::npos) {
8690 auto type = s.substr(0, pos);
8691 if (type ==
"Basic") {
8693 }
else if (type ==
"Digest") {
8694 s = s.substr(pos + 1);
8695 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
8696 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
8698 auto key = s.substr(
static_cast<size_t>(m.position(1)),
8699 static_cast<size_t>(m.length(1)));
8700 auto val = m.length(2) > 0
8701 ? s.substr(
static_cast<size_t>(m.position(2)),
8702 static_cast<size_t>(m.length(2)))
8703 : s.substr(
static_cast<size_t>(m.position(3)),
8704 static_cast<size_t>(m.length(3)));
8705 auth[std::move(key)] = std::move(val);
8718 : content_provider_(
std::move(content_provider)) {}
8721 return content_provider_(offset, sink);
8732 return std::isalnum(c) || c ==
'!' || c ==
'#' || c ==
'$' || c ==
'%' ||
8733 c ==
'&' || c ==
'\'' || c ==
'*' || c ==
'+' || c ==
'-' ||
8734 c ==
'.' || c ==
'^' || c ==
'_' || c ==
'`' || c ==
'|' || c ==
'~';
8738 if (s.empty()) {
return false; }
8747inline bool is_vchar(
char c) {
return c >= 33 && c <= 126; }
8749inline bool is_obs_text(
char c) {
return 128 <=
static_cast<unsigned char>(c); }
8754 if (s.empty()) {
return true; }
8756 if (s.size() == 1) {
8758 }
else if (s.size() == 2) {
8766 while (i < s.size() - 1) {
8783 int port,
const std::string &path,
8785 std::string &selected_subprotocol) {
8792 for (
const auto &h : headers) {
8799 thread_local std::mt19937 rng(std::random_device{}());
8800 std::string key_bytes(16,
'\0');
8801 for (
size_t i = 0; i < 16; i += 4) {
8803 std::memcpy(&key_bytes[i], &r, (std::min)(
size_t(4),
size_t(16 - i)));
8808 std::string req_str =
"GET " + path +
" HTTP/1.1\r\n";
8809 req_str +=
"Host: " + host +
":" + std::to_string(port) +
"\r\n";
8810 req_str +=
"Upgrade: websocket\r\n";
8811 req_str +=
"Connection: Upgrade\r\n";
8812 req_str +=
"Sec-WebSocket-Key: " + client_key +
"\r\n";
8813 req_str +=
"Sec-WebSocket-Version: 13\r\n";
8814 for (
const auto &h : headers) {
8815 req_str += h.first +
": " + h.second +
"\r\n";
8819 if (strm.
write(req_str.data(), req_str.size()) < 0) {
return false; }
8824 selected_subprotocol);
8833#ifdef CPPHTTPLIB_SSL_ENABLED
8836class SSLSocketStream final :
public Stream {
8839 socket_t sock, tls::session_t session, time_t read_timeout_sec,
8840 time_t read_timeout_usec, time_t write_timeout_sec,
8841 time_t write_timeout_usec, time_t max_timeout_msec = 0,
8842 std::chrono::time_point<std::chrono::steady_clock> start_time =
8843 (std::chrono::steady_clock::time_point::min)());
8844 ~SSLSocketStream()
override;
8846 bool is_readable()
const override;
8847 bool wait_readable()
const override;
8848 bool wait_writable()
const override;
8849 bool is_peer_alive()
const override;
8850 ssize_t read(
char *ptr,
size_t size)
override;
8851 ssize_t write(
const char *ptr,
size_t size)
override;
8852 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
8853 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
8855 time_t duration()
const override;
8856 void set_read_timeout(time_t sec, time_t usec = 0)
override;
8860 tls::session_t session_;
8861 time_t read_timeout_sec_;
8862 time_t read_timeout_usec_;
8863 time_t write_timeout_sec_;
8864 time_t write_timeout_usec_;
8865 time_t max_timeout_msec_;
8866 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
8869#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8870inline std::string message_digest(
const std::string &s,
const EVP_MD *algo) {
8871 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
8872 EVP_MD_CTX_new(), EVP_MD_CTX_free);
8874 unsigned int hash_length = 0;
8875 unsigned char hash[EVP_MAX_MD_SIZE];
8877 EVP_DigestInit_ex(context.get(), algo,
nullptr);
8878 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
8879 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
8881 std::stringstream ss;
8882 for (
auto i = 0u; i < hash_length; ++i) {
8883 ss << std::hex << std::setw(2) << std::setfill(
'0')
8884 <<
static_cast<unsigned int>(hash[i]);
8890inline std::string MD5(
const std::string &s) {
8891 return message_digest(s, EVP_md5());
8894inline std::string SHA_256(
const std::string &s) {
8895 return message_digest(s, EVP_sha256());
8898inline std::string SHA_512(
const std::string &s) {
8899 return message_digest(s, EVP_sha512());
8901#elif defined(CPPHTTPLIB_MBEDTLS_SUPPORT)
8904inline std::string hash_to_hex(
const unsigned char (&hash)[N]) {
8905 std::stringstream ss;
8906 for (
size_t i = 0; i < N; ++i) {
8907 ss << std::hex << std::setw(2) << std::setfill(
'0')
8908 <<
static_cast<unsigned int>(hash[i]);
8914inline std::string MD5(
const std::string &s) {
8915 unsigned char hash[16];
8916#ifdef CPPHTTPLIB_MBEDTLS_V3
8917 mbedtls_md5(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8920 mbedtls_md5_ret(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8923 return hash_to_hex(hash);
8926inline std::string SHA_256(
const std::string &s) {
8927 unsigned char hash[32];
8928#ifdef CPPHTTPLIB_MBEDTLS_V3
8929 mbedtls_sha256(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8932 mbedtls_sha256_ret(
reinterpret_cast<const unsigned char *
>(s.c_str()),
8935 return hash_to_hex(hash);
8938inline std::string SHA_512(
const std::string &s) {
8939 unsigned char hash[64];
8940#ifdef CPPHTTPLIB_MBEDTLS_V3
8941 mbedtls_sha512(
reinterpret_cast<const unsigned char *
>(s.c_str()), s.size(),
8944 mbedtls_sha512_ret(
reinterpret_cast<const unsigned char *
>(s.c_str()),
8947 return hash_to_hex(hash);
8949#elif defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
8952inline std::string hash_to_hex(
const unsigned char (&hash)[N]) {
8953 std::stringstream ss;
8954 for (
size_t i = 0; i < N; ++i) {
8955 ss << std::hex << std::setw(2) << std::setfill(
'0')
8956 <<
static_cast<unsigned int>(hash[i]);
8962inline std::string MD5(
const std::string &s) {
8963 unsigned char hash[WC_MD5_DIGEST_SIZE];
8964 wc_Md5Hash(
reinterpret_cast<const unsigned char *
>(s.c_str()),
8965 static_cast<word32
>(s.size()), hash);
8966 return hash_to_hex(hash);
8969inline std::string SHA_256(
const std::string &s) {
8970 unsigned char hash[WC_SHA256_DIGEST_SIZE];
8971 wc_Sha256Hash(
reinterpret_cast<const unsigned char *
>(s.c_str()),
8972 static_cast<word32
>(s.size()), hash);
8973 return hash_to_hex(hash);
8976inline std::string SHA_512(
const std::string &s) {
8977 unsigned char hash[WC_SHA512_DIGEST_SIZE];
8978 wc_Sha512Hash(
reinterpret_cast<const unsigned char *
>(s.c_str()),
8979 static_cast<word32
>(s.size()), hash);
8980 return hash_to_hex(hash);
8984inline bool is_ip_address(
const std::string &host) {
8985 struct in_addr addr4;
8986 struct in6_addr addr6;
8987 return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
8988 inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
8991template <
typename T>
8992inline bool process_server_socket_ssl(
8993 const std::atomic<socket_t> &svr_sock, tls::session_t session,
8994 socket_t sock,
size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
8995 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
8996 time_t write_timeout_usec, T callback) {
8998 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
8999 [&](
bool close_connection,
bool &connection_closed) {
9000 SSLSocketStream strm(sock, session, read_timeout_sec, read_timeout_usec,
9001 write_timeout_sec, write_timeout_usec);
9002 return callback(strm, close_connection, connection_closed);
9006template <
typename T>
9007inline bool process_client_socket_ssl(
9008 tls::session_t session,
socket_t sock, time_t read_timeout_sec,
9009 time_t read_timeout_usec, time_t write_timeout_sec,
9010 time_t write_timeout_usec, time_t max_timeout_msec,
9011 std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
9012 SSLSocketStream strm(sock, session, read_timeout_sec, read_timeout_usec,
9013 write_timeout_sec, write_timeout_usec, max_timeout_msec,
9015 return callback(strm);
9018inline std::pair<std::string, std::string> make_digest_authentication_header(
9019 const Request &req,
const std::map<std::string, std::string> &auth,
9020 size_t cnonce_count,
const std::string &cnonce,
const std::string &username,
9021 const std::string &password,
bool is_proxy =
false) {
9024 std::stringstream ss;
9025 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
9030 if (auth.find(
"qop") != auth.end()) {
9031 qop = auth.at(
"qop");
9032 if (qop.find(
"auth-int") != std::string::npos) {
9034 }
else if (qop.find(
"auth") != std::string::npos) {
9041 std::string algo =
"MD5";
9042 if (auth.find(
"algorithm") != auth.end()) { algo = auth.at(
"algorithm"); }
9044 std::string response;
9046 auto H = algo ==
"SHA-256" ? detail::SHA_256
9047 : algo ==
"SHA-512" ? detail::SHA_512
9050 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
9052 auto A2 = req.method +
":" + req.path;
9053 if (qop ==
"auth-int") { A2 +=
":" + H(req.body); }
9056 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
9058 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
9059 ":" + qop +
":" + H(A2));
9063 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
9065 auto field =
"Digest username=\"" + username +
"\", realm=\"" +
9066 auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
9067 "\", uri=\"" + req.path +
"\", algorithm=" + algo +
9068 (qop.empty() ?
", response=\""
9069 :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" +
9070 cnonce +
"\", response=\"") +
9072 (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
9074 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9075 return std::make_pair(key, field);
9078inline bool match_hostname(
const std::string &pattern,
9079 const std::string &hostname) {
9081 if (detail::case_ignore::equal(hostname, pattern)) {
return true; }
9084 std::vector<std::string> pattern_components;
9085 if (!pattern.empty()) {
9086 split(pattern.data(), pattern.data() + pattern.size(),
'.',
9087 [&](
const char *b,
const char *
e) {
9088 pattern_components.emplace_back(b, e);
9092 std::vector<std::string> host_components;
9093 if (!hostname.empty()) {
9094 split(hostname.data(), hostname.data() + hostname.size(),
'.',
9095 [&](
const char *b,
const char *
e) {
9096 host_components.emplace_back(b, e);
9101 if (host_components.size() != pattern_components.size()) {
return false; }
9106 auto itr = pattern_components.begin();
9107 for (
const auto &h : host_components) {
9109 if (!detail::case_ignore::equal(p, h) && p !=
"*") {
9110 bool partial_match =
false;
9111 if (!p.empty() && p[p.size() - 1] ==
'*') {
9112 const auto prefix_length = p.size() - 1;
9113 if (prefix_length == 0) {
9114 partial_match =
true;
9115 }
else if (h.size() >= prefix_length) {
9117 std::equal(p.begin(),
9118 p.begin() +
static_cast<std::string::difference_type
>(
9120 h.begin(), [](
const char ca,
const char cb) {
9121 return detail::case_ignore::to_lower(ca) ==
9122 detail::case_ignore::to_lower(cb);
9126 if (!partial_match) {
return false; }
9139verify_cert_with_windows_schannel(
const std::vector<unsigned char> &der_cert,
9140 const std::string &hostname,
9141 bool verify_hostname, uint64_t &out_error) {
9142 if (der_cert.empty()) {
return false; }
9147 auto cert_context = CertCreateCertificateContext(
9148 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
9149 static_cast<DWORD
>(der_cert.size()));
9151 if (!cert_context) {
9152 out_error = GetLastError();
9157 scope_exit([&] { CertFreeCertificateContext(cert_context); });
9160 CERT_CHAIN_PARA chain_para = {};
9161 chain_para.cbSize =
sizeof(chain_para);
9164 PCCERT_CHAIN_CONTEXT chain_context =
nullptr;
9165 auto chain_result = CertGetCertificateChain(
9166 nullptr, cert_context,
nullptr, cert_context->hCertStore, &chain_para,
9167 CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_END_CERT |
9168 CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
9169 nullptr, &chain_context);
9171 if (!chain_result || !chain_context) {
9172 out_error = GetLastError();
9177 scope_exit([&] { CertFreeCertificateChain(chain_context); });
9180 if (chain_context->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) {
9181 out_error = chain_context->TrustStatus.dwErrorStatus;
9186 SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para = {};
9187 extra_policy_para.cbSize =
sizeof(extra_policy_para);
9188#ifdef AUTHTYPE_SERVER
9189 extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
9193 if (verify_hostname) {
9194 whost = u8string_to_wstring(hostname.c_str());
9195 extra_policy_para.pwszServerName =
const_cast<wchar_t *
>(whost.c_str());
9198 CERT_CHAIN_POLICY_PARA policy_para = {};
9199 policy_para.cbSize =
sizeof(policy_para);
9200#ifdef CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS
9201 policy_para.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
9203 policy_para.dwFlags = 0;
9205 policy_para.pvExtraPolicyPara = &extra_policy_para;
9207 CERT_CHAIN_POLICY_STATUS policy_status = {};
9208 policy_status.cbSize =
sizeof(policy_status);
9210 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chain_context,
9211 &policy_para, &policy_status)) {
9212 out_error = GetLastError();
9216 if (policy_status.dwError != 0) {
9217 out_error = policy_status.dwError;
9225inline bool setup_client_tls_session(
const std::string &host, tls::ctx_t &ctx,
9226 tls::session_t &session,
socket_t sock,
9227 bool server_certificate_verification,
9228 const std::string &ca_cert_file_path,
9229 tls::ca_store_t ca_cert_store,
9230 time_t timeout_sec, time_t timeout_usec) {
9231 using namespace tls;
9233 ctx = create_client_context();
9234 if (!ctx) {
return false; }
9236 if (server_certificate_verification) {
9237 if (!ca_cert_file_path.empty()) {
9238 load_ca_file(ctx, ca_cert_file_path.c_str());
9240 if (ca_cert_store) { set_ca_store(ctx, ca_cert_store); }
9241 load_system_certs(ctx);
9244 bool is_ip = is_ip_address(host);
9246#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
9247 if (is_ip && server_certificate_verification) {
9248 set_verify_client(ctx,
false);
9250 set_verify_client(ctx, server_certificate_verification);
9254 session = create_session(ctx, sock);
9255 if (!session) {
return false; }
9258 if (!is_ip) { set_sni(session, host.c_str()); }
9259 if (server_certificate_verification) { set_hostname(session, host.c_str()); }
9261 if (!connect_nonblocking(session, sock, timeout_sec, timeout_usec,
nullptr)) {
9265 if (server_certificate_verification) {
9266 if (get_verify_result(session) != 0) {
return false; }
9298 .substr(bearer_header_prefix_len);
9313 return "Non-Authoritative Information";
9337 return "Proxy Authentication Required";
9358 return "Request Header Fields Too Large";
9360 return "Unavailable For Legal Reasons";
9366 return "HTTP Version Not Supported";
9372 return "Network Authentication Required";
9385 case Error::Read:
return "Failed to read connection";
9386 case Error::Write:
return "Failed to write connection";
9393 return "SSL server hostname verification failed";
9395 return "Unsupported HTTP multipart boundary characters";
9406 return "Exceeded maximum socket descriptor count";
9426 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
9431 std::vector<std::string> addrs;
9433 if (addrs.empty()) {
return std::string(); }
9438 std::vector<std::string> &addrs) {
9439 struct addrinfo hints;
9440 struct addrinfo *result;
9442 memset(&hints, 0,
sizeof(
struct addrinfo));
9443 hints.ai_family = AF_UNSPEC;
9444 hints.ai_socktype = SOCK_STREAM;
9445 hints.ai_protocol = 0;
9449#if defined __linux__ && !defined __ANDROID__
9456 for (
auto rp = result; rp; rp = rp->ai_next) {
9458 *
reinterpret_cast<struct sockaddr_storage *
>(rp->ai_addr);
9463 addrs.emplace_back(std::move(ip));
9469 std::ostringstream escaped;
9471 escaped << std::hex;
9473 for (
auto c : value) {
9474 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
9475 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
9479 escaped << std::uppercase;
9480 escaped <<
'%' << std::setw(2)
9481 <<
static_cast<int>(
static_cast<unsigned char>(c));
9482 escaped << std::nouppercase;
9486 return escaped.str();
9490 std::ostringstream escaped;
9492 escaped << std::hex;
9494 for (
auto c : value) {
9495 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
9496 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
9497 c ==
')' || c ==
';' || c ==
'/' || c ==
'?' || c ==
':' || c ==
'@' ||
9498 c ==
'&' || c ==
'=' || c ==
'+' || c ==
'$' || c ==
',' || c ==
'#') {
9501 escaped << std::uppercase;
9502 escaped <<
'%' << std::setw(2)
9503 <<
static_cast<int>(
static_cast<unsigned char>(c));
9504 escaped << std::nouppercase;
9508 return escaped.str();
9514 for (
size_t i = 0; i < value.size(); i++) {
9515 if (value[i] ==
'%' && i + 2 < value.size()) {
9518 result +=
static_cast<char>(val);
9534 for (
size_t i = 0; i < value.size(); i++) {
9535 if (value[i] ==
'%' && i + 2 < value.size()) {
9538 result +=
static_cast<char>(val);
9553 result.reserve(component.size() * 3);
9555 for (
size_t i = 0; i < component.size(); i++) {
9556 auto c =
static_cast<unsigned char>(component[i]);
9559 if (std::isalnum(c) || c ==
'-' || c ==
'.' || c ==
'_' || c ==
'~') {
9560 result +=
static_cast<char>(c);
9564 else if (c ==
'!' || c ==
'$' || c ==
'&' || c ==
'\'' || c ==
'(' ||
9565 c ==
')' || c ==
'*' || c ==
'+' || c ==
',' || c ==
';' ||
9567 result +=
static_cast<char>(c);
9570 else if (c ==
':') {
9571 result +=
static_cast<char>(c);
9574 else if (c ==
'@') {
9575 result +=
static_cast<char>(c);
9579 snprintf(hex,
sizeof(hex),
"%02X", c);
9580 result.append(hex, 2);
9588 result.reserve(component.size());
9590 for (
size_t i = 0; i < component.size(); i++) {
9591 if (component[i] ==
'%' && i + 1 < component.size()) {
9592 if (component[i + 1] ==
'u') {
9600 if (len > 0) { result.append(buff, len); }
9603 result += component[i];
9610 result +=
static_cast<char>(val);
9613 result += component[i];
9617 result += component[i];
9624 bool space_as_plus) {
9626 result.reserve(component.size() * 3);
9628 for (
size_t i = 0; i < component.size(); i++) {
9629 auto c =
static_cast<unsigned char>(component[i]);
9632 if (std::isalnum(c) || c ==
'-' || c ==
'.' || c ==
'_' || c ==
'~') {
9633 result +=
static_cast<char>(c);
9636 else if (c ==
' ') {
9637 if (space_as_plus) {
9644 else if (c ==
'+') {
9645 if (space_as_plus) {
9648 result +=
static_cast<char>(c);
9652 else if (c ==
'!' || c ==
'$' || c ==
'\'' || c ==
'(' || c ==
')' ||
9653 c ==
'*' || c ==
',' || c ==
';') {
9654 result +=
static_cast<char>(c);
9657 else if (c ==
':' || c ==
'@') {
9658 result +=
static_cast<char>(c);
9661 else if (c ==
'/') {
9662 result +=
static_cast<char>(c);
9665 else if (c ==
'?') {
9666 result +=
static_cast<char>(c);
9670 snprintf(hex,
sizeof(hex),
"%02X", c);
9671 result.append(hex, 2);
9678 bool plus_as_space) {
9680 result.reserve(component.size());
9682 for (
size_t i = 0; i < component.size(); i++) {
9683 if (component[i] ==
'%' && i + 2 < component.size()) {
9684 std::string hex = component.substr(i + 1, 2);
9686 unsigned long value = std::strtoul(hex.c_str(), &end, 16);
9687 if (end == hex.c_str() + 2) {
9688 result +=
static_cast<char>(value);
9691 result += component[i];
9693 }
else if (component[i] ==
'+' && plus_as_space) {
9696 result += component[i];
9704 auto pos = filename.find_last_of(
"/\\");
9706 (pos != std::string::npos) ? filename.substr(pos + 1) : filename;
9709 result.erase(std::remove(result.begin(), result.end(),
'\0'), result.end());
9713 auto start = result.find_first_not_of(
" \t");
9714 auto end = result.find_last_not_of(
" \t");
9715 result = (start == std::string::npos)
9717 : result.substr(start, end - start + 1);
9721 if (result ==
"." || result ==
"..") {
return ""; }
9728 std::string path_with_query = path;
9729 thread_local const std::regex re(
"[^?]+\\?.*");
9730 auto delm = std::regex_match(path, re) ?
'&' :
'?';
9732 return path_with_query;
9736inline std::pair<std::string, std::string>
9738 std::string field =
"bytes=";
9740 for (
const auto &r : ranges) {
9741 if (i != 0) { field +=
", "; }
9742 if (r.first != -1) { field += std::to_string(r.first); }
9744 if (r.second != -1) { field += std::to_string(r.second); }
9747 return std::make_pair(
"Range", std::move(field));
9750inline std::pair<std::string, std::string>
9752 const std::string &password,
bool is_proxy) {
9754 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9755 return std::make_pair(key, std::move(field));
9758inline std::pair<std::string, std::string>
9760 bool is_proxy =
false) {
9761 auto field =
"Bearer " + token;
9762 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9763 return std::make_pair(key, std::move(field));
9777 const char *def,
size_t id)
const {
9786 const std::string &val) {
9800 auto r =
trailers.equal_range(key);
9801 return static_cast<size_t>(std::distance(r.first, r.second));
9813inline std::vector<std::string>
9815 auto rng =
params.equal_range(key);
9816 std::vector<std::string> values;
9817 values.reserve(
static_cast<size_t>(std::distance(rng.first, rng.second)));
9818 for (
auto it = rng.first; it != rng.second; ++it) {
9819 values.push_back(it->second);
9825 auto r =
params.equal_range(key);
9826 return static_cast<size_t>(std::distance(r.first, r.second));
9837 auto rng =
fields.equal_range(key);
9838 auto it = rng.first;
9839 std::advance(it,
static_cast<ssize_t
>(
id));
9840 if (it != rng.second) {
return it->second.content; }
9841 return std::string();
9844inline std::vector<std::string>
9846 std::vector<std::string> values;
9847 auto rng =
fields.equal_range(key);
9848 for (
auto it = rng.first; it != rng.second; it++) {
9849 values.push_back(it->second.content);
9859 auto r =
fields.equal_range(key);
9860 return static_cast<size_t>(std::distance(r.first, r.second));
9868inline std::vector<FormData>
9870 std::vector<FormData> values;
9871 auto rng =
files.equal_range(key);
9872 for (
auto it = rng.first; it != rng.second; it++) {
9873 values.push_back(it->second);
9883 auto r =
files.equal_range(key);
9884 return static_cast<size_t>(std::distance(r.first, r.second));
9908 const std::string &val) {
9921 auto r =
trailers.equal_range(key);
9922 return static_cast<size_t>(std::distance(r.first, r.second));
9928 if (300 <= stat && stat < 400) {
9937 const std::string &content_type) {
9940 auto rng =
headers.equal_range(
"Content-Type");
9941 headers.erase(rng.first, rng.second);
9946 const std::string &content_type) {
9951 const std::string &content_type) {
9952 body = std::move(s);
9954 auto rng =
headers.equal_range(
"Content-Type");
9955 headers.erase(rng.first, rng.second);
9960 size_t in_length,
const std::string &content_type,
ContentProvider provider,
9990 const std::string &content_type) {
10007 return request_headers_.find(key) != request_headers_.end();
10018 auto r = request_headers_.equal_range(key);
10019 return static_cast<size_t>(std::distance(r.first, r.second));
10024 return write(ptr, strlen(ptr));
10028 return write(s.data(), s.size());
10037 if (
eof) {
return 0; }
10046 auto to_read = len;
10049 to_read = (std::min)(len, remaining);
10051 auto n =
stream->read(buf, to_read);
10080 size_t chunk_offset = 0;
10081 size_t chunk_total = 0;
10082 auto n =
chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
10107 : base_thread_count_(n), max_queued_requests_(mqr), idle_thread_count_(0),
10109#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10110 if (max_n != 0 && max_n < n) {
10111 std::string msg =
"max_threads must be >= base_threads";
10112 throw std::invalid_argument(msg);
10115 max_thread_count_ = max_n == 0 ? n : max_n;
10116 threads_.reserve(base_thread_count_);
10117#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10120 for (
size_t i = 0; i < base_thread_count_; i++) {
10121 threads_.emplace_back(std::thread([
this]() { worker(
false); }));
10123#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10130 std::unique_lock<std::mutex> lock(mutex_);
10133 cond_.notify_all();
10134 for (
auto &t : threads_) {
10135 if (t.joinable()) { t.join(); }
10144 std::unique_lock<std::mutex> lock(mutex_);
10145 if (shutdown_) {
return false; }
10146 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
10149 jobs_.push_back(std::move(fn));
10152 if (idle_thread_count_ == 0 &&
10153 threads_.size() + dynamic_threads_.size() < max_thread_count_) {
10154 cleanup_finished_threads();
10155 dynamic_threads_.emplace_back(std::thread([
this]() { worker(
true); }));
10159 cond_.notify_one();
10165 std::unique_lock<std::mutex> lock(mutex_);
10169 cond_.notify_all();
10171 for (
auto &t : threads_) {
10172 if (t.joinable()) { t.join(); }
10177 std::list<std::thread> remaining_dynamic;
10179 std::unique_lock<std::mutex> lock(mutex_);
10180 remaining_dynamic = std::move(dynamic_threads_);
10182 for (
auto &t : remaining_dynamic) {
10183 if (t.joinable()) { t.join(); }
10186 std::unique_lock<std::mutex> lock(mutex_);
10187 cleanup_finished_threads();
10190inline void ThreadPool::move_to_finished(std::thread::id
id) {
10192 for (
auto it = dynamic_threads_.begin(); it != dynamic_threads_.end(); ++it) {
10193 if (it->get_id() ==
id) {
10194 finished_threads_.push_back(std::move(*it));
10195 dynamic_threads_.erase(it);
10201inline void ThreadPool::cleanup_finished_threads() {
10203 for (
auto &t : finished_threads_) {
10204 if (t.joinable()) { t.join(); }
10206 finished_threads_.clear();
10209inline void ThreadPool::worker(
bool is_dynamic) {
10211 std::function<void()> fn;
10213 std::unique_lock<std::mutex> lock(mutex_);
10214 idle_thread_count_++;
10217 auto has_work = cond_.wait_for(
10219 [&] {
return !jobs_.empty() || shutdown_; });
10222 idle_thread_count_--;
10223 move_to_finished(std::this_thread::get_id());
10227 cond_.wait(lock, [&] {
return !jobs_.empty() || shutdown_; });
10230 idle_thread_count_--;
10232 if (shutdown_ && jobs_.empty()) {
break; }
10234 fn = std::move(jobs_.front());
10238 assert(
true ==
static_cast<bool>(fn));
10242#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
10243 !defined(LIBRESSL_VERSION_NUMBER)
10244 OPENSSL_thread_stop();
10255 time_t timeout_sec, time_t timeout_usec,
10256 time_t &actual_timeout_sec,
10257 time_t &actual_timeout_usec) {
10258 auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
10260 auto actual_timeout_msec =
10261 (std::min)(max_timeout_msec - duration_msec, timeout_msec);
10263 if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
10265 actual_timeout_sec = actual_timeout_msec / 1000;
10266 actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
10271 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
10272 time_t write_timeout_sec, time_t write_timeout_usec,
10273 time_t max_timeout_msec,
10274 std::chrono::time_point<std::chrono::steady_clock> start_time)
10275 : sock_(sock), read_timeout_sec_(read_timeout_sec),
10276 read_timeout_usec_(read_timeout_usec),
10277 write_timeout_sec_(write_timeout_sec),
10278 write_timeout_usec_(write_timeout_usec),
10279 max_timeout_msec_(max_timeout_msec), start_time_(start_time),
10280 read_buff_(read_buff_size_, 0) {}
10285 return read_buff_off_ < read_buff_content_size_;
10289 if (max_timeout_msec_ <= 0) {
10290 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10293 time_t read_timeout_sec;
10294 time_t read_timeout_usec;
10296 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10298 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10302 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
10312 (std::min)(
size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
10315 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
10318 if (read_buff_off_ < read_buff_content_size_) {
10319 auto remaining_size = read_buff_content_size_ - read_buff_off_;
10320 if (
size <= remaining_size) {
10321 memcpy(ptr, read_buff_.data() + read_buff_off_,
size);
10322 read_buff_off_ +=
size;
10323 return static_cast<ssize_t
>(
size);
10325 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
10326 read_buff_off_ += remaining_size;
10327 return static_cast<ssize_t
>(remaining_size);
10336 read_buff_off_ = 0;
10337 read_buff_content_size_ = 0;
10339 if (
size < read_buff_size_) {
10340 auto n =
read_socket(sock_, read_buff_.data(), read_buff_size_,
10349 }
else if (n <=
static_cast<ssize_t
>(
size)) {
10350 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
10353 memcpy(ptr, read_buff_.data(),
size);
10354 read_buff_off_ =
size;
10355 read_buff_content_size_ =
static_cast<size_t>(n);
10356 return static_cast<ssize_t
>(
size);
10374#if defined(_WIN32) && !defined(_WIN64)
10376 (std::min)(
size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
10395 return std::chrono::duration_cast<std::chrono::milliseconds>(
10396 std::chrono::steady_clock::now() - start_time_)
10401 read_timeout_sec_ = sec;
10402 read_timeout_usec_ = usec;
10413#if defined(_MSC_VER) && _MSC_VER < 1910
10414 auto len_read = buffer._Copy_s(ptr,
size,
size, position);
10416 auto len_read = buffer.copy(ptr,
size, position);
10418 position +=
static_cast<size_t>(len_read);
10419 return static_cast<ssize_t
>(len_read);
10423 buffer.append(ptr,
size);
10424 return static_cast<ssize_t
>(
size);
10441 constexpr const char marker[] =
"/:";
10444 std::size_t last_param_end = 0;
10446#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10451 std::unordered_set<std::string> param_name_set;
10455 const auto marker_pos =
pattern.find(
10456 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
10457 if (marker_pos == std::string::npos) {
break; }
10459 static_fragments_.push_back(
10460 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
10462 const auto param_name_start = marker_pos +
str_len(marker);
10464 auto sep_pos =
pattern.find(separator, param_name_start);
10465 if (sep_pos == std::string::npos) { sep_pos =
pattern.length(); }
10468 pattern.substr(param_name_start, sep_pos - param_name_start);
10470#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10471 if (param_name_set.find(param_name) != param_name_set.cend()) {
10472 std::string msg =
"Encountered path parameter '" + param_name +
10473 "' multiple times in route pattern '" +
pattern +
"'.";
10474 throw std::invalid_argument(msg);
10478 param_names_.push_back(std::move(param_name));
10480 last_param_end = sep_pos + 1;
10483 if (last_param_end <
pattern.length()) {
10484 static_fragments_.push_back(pattern.substr(last_param_end));
10489 request.
matches = std::smatch();
10491 request.
path_params.reserve(param_names_.size());
10494 std::size_t starting_pos = 0;
10495 for (
size_t i = 0; i < static_fragments_.size(); ++i) {
10496 const auto &fragment = static_fragments_[i];
10498 if (starting_pos + fragment.length() > request.
path.length()) {
10504 if (std::strncmp(request.
path.c_str() + starting_pos, fragment.c_str(),
10505 fragment.length()) != 0) {
10509 starting_pos += fragment.length();
10514 if (i >= param_names_.size()) {
continue; }
10516 auto sep_pos = request.
path.find(separator, starting_pos);
10517 if (sep_pos == std::string::npos) { sep_pos = request.
path.length(); }
10519 const auto ¶m_name = param_names_[i];
10522 param_name, request.
path.substr(starting_pos, sep_pos - starting_pos));
10525 starting_pos = sep_pos + 1;
10528 return starting_pos >= request.
path.length();
10533 return std::regex_match(request.
path, request.
matches, regex_);
10539 if (host.find(
':') == std::string::npos ||
10540 (!host.empty() && host[0] ==
'[')) {
10545 return "[" + host +
"]";
10554 if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {
10557 result +=
":" + std::to_string(port);
10573 const std::vector<NoProxyEntry> &entries);
10576 if (prefix_bits < 0 || prefix_bits > 128) {
return false; }
10577 if (prefix_bits == 0) {
return true; }
10578 int full_bytes = prefix_bits / 8;
10579 int rem_bits = prefix_bits % 8;
10580 if (full_bytes > 0 && std::memcmp(ip.data(), net.data(),
10581 static_cast<size_t>(full_bytes)) != 0) {
10584 if (rem_bits == 0) {
return true; }
10585 auto i =
static_cast<size_t>(full_bytes);
10586 auto mask =
static_cast<uint8_t
>(0xFFu << (8 - rem_bits));
10587 return (ip[i] & mask) == (net[i] & mask);
10591 if (token.empty()) {
return false; }
10593 if (token ==
"*") {
10598 auto slash = token.find(
'/');
10599 std::string addr_part =
10600 (slash == std::string::npos) ? token : token.substr(0, slash);
10601 std::string prefix_part =
10602 (slash == std::string::npos) ? std::string() : token.substr(slash + 1);
10606 if (slash != std::string::npos && prefix_part.empty()) {
return false; }
10611 bool bracketed = addr_part.size() >= 2 && addr_part.front() ==
'[' &&
10612 addr_part.back() ==
']';
10613 if (bracketed) { addr_part = addr_part.substr(1, addr_part.size() - 2); }
10617 if (inet_pton(AF_INET, addr_part.c_str(), &v4) == 1) {
10619 if (!prefix_part.empty()) {
10621 prefix_part.data() + prefix_part.size(), prefix);
10622 if (r.ec != std::errc{} ||
10623 r.ptr != prefix_part.data() + prefix_part.size()) {
10626 if (prefix < 0 || prefix > 32) {
return false; }
10629 std::memcpy(out.
net.data(), &v4,
sizeof(v4));
10635 struct in6_addr v6;
10636 if (inet_pton(AF_INET6, addr_part.c_str(), &v6) == 1) {
10638 if (!prefix_part.empty()) {
10640 prefix_part.data() + prefix_part.size(), prefix);
10641 if (r.ec != std::errc{} ||
10642 r.ptr != prefix_part.data() + prefix_part.size()) {
10645 if (prefix < 0 || prefix > 128) {
return false; }
10648 std::memcpy(out.
net.data(), &v6,
sizeof(v6));
10655 if (bracketed) {
return false; }
10658 if (slash != std::string::npos) {
return false; }
10660 if (token.find(
':') != std::string::npos) {
return false; }
10663 while (!hostname.empty() && hostname.front() ==
'.') {
10664 hostname.erase(hostname.begin());
10666 while (!hostname.empty() && hostname.back() ==
'.') {
10667 hostname.pop_back();
10669 if (hostname.empty()) {
return false; }
10678 std::string h = host;
10680 if (h.size() >= 2 && h.front() ==
'[' && h.back() ==
']') {
10681 h = h.substr(1, h.size() - 2);
10686 if (!h.empty() && h.back() ==
'.') { h.pop_back(); }
10692 struct in6_addr v6;
10693 if (inet_pton(AF_INET, t.
hostname.c_str(), &v4) == 1) {
10695 std::memcpy(t.
ip.data(), &v4,
sizeof(v4));
10696 }
else if (inet_pton(AF_INET6, t.
hostname.c_str(), &v6) == 1) {
10698 std::memcpy(t.
ip.data(), &v6,
sizeof(v6));
10705 const std::vector<NoProxyEntry> &entries) {
10706 if (target.hostname.empty()) {
return false; }
10707 for (
const auto &
e : entries) {
10711 if (target.is_ipv4 &&
ip_in_cidr(target.ip,
e.net,
e.prefix_bits)) {
10716 if (target.is_ipv6 &&
ip_in_cidr(target.ip,
e.net,
e.prefix_bits)) {
10721 if (target.is_ipv4 || target.is_ipv6) {
break; }
10722 if (target.hostname ==
e.hostname_pattern) {
return true; }
10725 if (target.hostname.size() >
e.hostname_pattern.size() + 1) {
10726 auto offset = target.hostname.size() -
e.hostname_pattern.size();
10727 if (target.hostname[offset - 1] ==
'.' &&
10728 target.hostname.compare(offset,
e.hostname_pattern.size(),
10729 e.hostname_pattern) == 0) {
10739template <
typename T>
10742 for (
const auto &h : headers) {
10749 if (header_writer(strm, headers) <= 0) {
10762#ifdef CPPHTTPLIB_SSL_ENABLED
10766inline SSLSocketStream::SSLSocketStream(
10767 socket_t sock, tls::session_t session, time_t read_timeout_sec,
10768 time_t read_timeout_usec, time_t write_timeout_sec,
10769 time_t write_timeout_usec, time_t max_timeout_msec,
10770 std::chrono::time_point<std::chrono::steady_clock> start_time)
10771 : sock_(sock), session_(session), read_timeout_sec_(read_timeout_sec),
10772 read_timeout_usec_(read_timeout_usec),
10773 write_timeout_sec_(write_timeout_sec),
10774 write_timeout_usec_(write_timeout_usec),
10775 max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
10776#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10782 SSL_clear_mode(
static_cast<SSL *
>(session), SSL_MODE_AUTO_RETRY);
10786inline SSLSocketStream::~SSLSocketStream() =
default;
10788inline bool SSLSocketStream::is_readable()
const {
10789 return tls::pending(session_) > 0;
10792inline bool SSLSocketStream::wait_readable()
const {
10793 if (max_timeout_msec_ <= 0) {
10794 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10797 time_t read_timeout_sec;
10798 time_t read_timeout_usec;
10800 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10802 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10805inline bool SSLSocketStream::wait_writable()
const {
10806 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
10807 !tls::is_peer_closed(session_, sock_);
10810inline bool SSLSocketStream::is_peer_alive()
const {
10811 return !tls::is_peer_closed(session_, sock_);
10814inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size) {
10815 if (tls::pending(session_) > 0) {
10817 auto ret = tls::read(session_, ptr,
size, err);
10818 if (
ret == 0 || err.code == tls::ErrorCode::PeerClosed) {
10822 }
else if (wait_readable()) {
10824 auto ret = tls::read(session_, ptr,
size, err);
10828 while (--n >= 0 && (err.code == tls::ErrorCode::WantRead ||
10829 (err.code == tls::ErrorCode::SyscallError &&
10830 WSAGetLastError() == WSAETIMEDOUT))) {
10832 while (--n >= 0 && err.code == tls::ErrorCode::WantRead) {
10834 if (tls::pending(session_) > 0) {
10835 return tls::read(session_, ptr,
size, err);
10836 }
else if (wait_readable()) {
10837 std::this_thread::sleep_for(std::chrono::microseconds{10});
10838 ret = tls::read(session_, ptr,
size, err);
10839 if (
ret >= 0) {
return ret; }
10845 }
else if (
ret == 0 || err.code == tls::ErrorCode::PeerClosed) {
10855inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size) {
10856 if (wait_writable()) {
10858 std::min<size_t>(
size, (std::numeric_limits<int>::max)());
10861 auto ret = tls::write(session_, ptr, handle_size, err);
10865 while (--n >= 0 && (err.code == tls::ErrorCode::WantWrite ||
10866 (err.code == tls::ErrorCode::SyscallError &&
10867 WSAGetLastError() == WSAETIMEDOUT))) {
10869 while (--n >= 0 && err.code == tls::ErrorCode::WantWrite) {
10871 if (wait_writable()) {
10872 std::this_thread::sleep_for(std::chrono::microseconds{10});
10873 ret = tls::write(session_, ptr, handle_size, err);
10874 if (
ret >= 0) {
return ret; }
10886inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
10891inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
10896inline socket_t SSLSocketStream::socket()
const {
return sock_; }
10898inline time_t SSLSocketStream::duration()
const {
10899 return std::chrono::duration_cast<std::chrono::milliseconds>(
10900 std::chrono::steady_clock::now() - start_time_)
10904inline void SSLSocketStream::set_read_timeout(time_t sec, time_t usec) {
10905 read_timeout_sec_ = sec;
10906 read_timeout_usec_ = usec;
10923 signal(SIGPIPE, SIG_IGN);
10929inline std::unique_ptr<detail::MatcherBase>
10930Server::make_matcher(
const std::string &pattern) {
10931 if (pattern.find(
"/:") != std::string::npos) {
10939 return add_handler(get_handlers_, pattern, std::move(handler));
10943 return add_handler(post_handlers_, pattern, std::move(handler));
10948 return add_handler(post_handlers_for_content_reader_, pattern,
10949 std::move(handler));
10953 return add_handler(put_handlers_, pattern, std::move(handler));
10958 return add_handler(put_handlers_for_content_reader_, pattern,
10959 std::move(handler));
10963 return add_handler(patch_handlers_, pattern, std::move(handler));
10968 return add_handler(patch_handlers_for_content_reader_, pattern,
10969 std::move(handler));
10973 return add_handler(delete_handlers_, pattern, std::move(handler));
10978 return add_handler(delete_handlers_for_content_reader_, pattern,
10979 std::move(handler));
10983 return add_handler(options_handlers_, pattern, std::move(handler));
10988 websocket_handlers_.push_back(
10989 {make_matcher(pattern), std::move(handler),
nullptr});
10996 websocket_handlers_.push_back({make_matcher(pattern), std::move(handler),
10997 std::move(sub_protocol_selector)});
11002 const std::string &mount_point) {
11007 const std::string &dir,
Headers headers) {
11010 std::string mnt = !mount_point.empty() ? mount_point :
"/";
11011 if (!mnt.empty() && mnt[0] ==
'/') {
11012 std::string resolved_base;
11015 if (resolved_base.back() !=
'\\' && resolved_base.back() !=
'/') {
11016 resolved_base +=
'\\';
11019 if (resolved_base.back() !=
'/') { resolved_base +=
'/'; }
11022 base_dirs_.push_back(
11023 {std::move(mnt), dir, std::move(resolved_base), std::move(headers)});
11031 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
11032 if (it->mount_point == mount_point) {
11033 base_dirs_.erase(it);
11042 const std::string &mime) {
11043 file_extension_and_mimetype_map_[ext] = mime;
11048 default_file_mimetype_ = mime;
11053 file_request_handler_ = std::move(handler);
11057inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
11059 error_handler_ = std::move(handler);
11063inline Server &Server::set_error_handler_core(Handler handler,
11065 error_handler_ = [handler](
const Request &req, Response &res) {
11073 exception_handler_ = std::move(handler);
11078 pre_routing_handler_ = std::move(handler);
11083 post_routing_handler_ = std::move(handler);
11088 pre_request_handler_ = std::move(handler);
11093 logger_ = std::move(logger);
11098 error_logger_ = std::move(error_logger);
11103 pre_compression_logger_ = std::move(logger);
11109 expect_100_continue_handler_ = std::move(handler);
11114 start_handler_ = std::move(handler);
11119 address_family_ = family;
11134 socket_options_ = std::move(socket_options);
11139 default_headers_ = std::move(headers);
11144 std::function<ssize_t(
Stream &,
Headers &)>
const &writer) {
11145 header_writer_ = writer;
11165template <
class Rep,
class Period>
11167 const std::chrono::duration<Rep, Period> &duration) {
11207template <
class Rep,
class Period>
11209 const std::chrono::duration<Rep, Period> &duration) {
11217 int socket_flags) {
11218 auto ret = bind_internal(host, port, socket_flags);
11219 if (
ret == -1) { is_decommissioned =
true; }
11223 auto ret = bind_internal(host, 0, socket_flags);
11224 if (
ret == -1) { is_decommissioned =
true; }
11231 int socket_flags) {
11232 return bind_to_port(host, port, socket_flags) && listen_internal();
11238 while (!is_running_ && !is_decommissioned) {
11239 std::this_thread::sleep_for(std::chrono::milliseconds{1});
11250 is_decommissioned =
false;
11255inline bool Server::parse_request_line(
const char *s,
Request &req)
const {
11256 auto len = strlen(s);
11257 if (len < 2 || s[len - 2] !=
'\r' || s[len - 1] !=
'\n') {
return false; }
11263 detail::split(s, s + len,
' ', [&](
const char *b,
const char *
e) {
11265 case 0: req.
method = std::string(b,
e);
break;
11266 case 1: req.
target = std::string(b,
e);
break;
11267 case 2: req.
version = std::string(b,
e);
break;
11273 if (count != 3) {
return false; }
11276 thread_local const std::set<std::string> methods{
11277 "GET",
"HEAD",
"POST",
"PUT",
"DELETE",
11278 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
11280 if (methods.find(req.
method) == methods.end()) {
11292 for (
size_t i = 0; i < req.
target.size(); i++) {
11293 if (req.
target[i] ==
'#') {
11300 [&](
const char *lhs_data, std::size_t lhs_size,
11301 const char *rhs_data, std::size_t rhs_size) {
11303 decode_path_component(std::string(lhs_data, lhs_size));
11304 detail::parse_query_text(rhs_data, rhs_size, req.params);
11311inline bool Server::write_response(
Stream &strm,
bool close_connection,
11315 req.ranges.clear();
11316 return write_response_core(strm, close_connection, req, res,
false);
11319inline bool Server::write_response_with_content(
Stream &strm,
11320 bool close_connection,
11323 return write_response_core(strm, close_connection, req, res,
true);
11326inline bool Server::write_response_core(
Stream &strm,
bool close_connection,
11328 bool need_apply_ranges) {
11329 assert(res.status != -1);
11331 if (400 <= res.status && error_handler_ &&
11333 need_apply_ranges =
true;
11336 std::string content_type;
11337 std::string boundary;
11338 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
11341 if (close_connection || req.get_header_value(
"Connection") ==
"close" ||
11342 400 <= res.status) {
11343 res.set_header(
"Connection",
"close");
11345 std::string s =
"timeout=";
11349 res.set_header(
"Keep-Alive", s);
11352 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
11353 !res.has_header(
"Content-Type")) {
11354 res.set_header(
"Content-Type",
"text/plain");
11357 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
11358 !res.has_header(
"Content-Length")) {
11359 res.set_header(
"Content-Length",
"0");
11362 if (req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges")) {
11363 res.set_header(
"Accept-Ranges",
"bytes");
11366 if (post_routing_handler_) { post_routing_handler_(req, res); }
11369 detail::BufferStream bstrm;
11371 if (header_writer_(bstrm, res.headers) <= 0) {
return false; }
11374 if (req.method !=
"HEAD" && !res.body.empty() && !res.content_provider_) {
11375 bstrm.write(res.body.data(), res.body.size());
11380 output_log(req, res);
11383 auto &
data = bstrm.get_buffer();
11388 if (req.method !=
"HEAD" && res.content_provider_) {
11389 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
11390 res.content_provider_success_ =
true;
11400Server::write_content_with_provider(
Stream &strm,
const Request &req,
11401 Response &res,
const std::string &boundary,
11402 const std::string &content_type) {
11403 auto is_shutting_down = [
this]() {
11407 if (res.content_length_ > 0) {
11408 if (req.ranges.empty()) {
11410 res.content_length_, is_shutting_down);
11411 }
else if (req.ranges.size() == 1) {
11413 req.ranges[0], res.content_length_);
11416 offset_and_length.first,
11417 offset_and_length.second, is_shutting_down);
11420 strm, req, res, boundary, content_type, res.content_length_,
11424 if (res.is_chunked_content_provider_) {
11433 is_shutting_down, *compressor);
11442 FormFields::iterator cur_field;
11443 FormFiles::iterator cur_file;
11444 auto is_text_field =
false;
11446 if (read_content_core(
11449 [&](
const char *buf,
size_t n) {
11456 if (req.body.size() >= req.body.max_size() ||
11457 n > req.body.max_size() - req.body.size()) {
11470 req.body.append(buf, n);
11474 [&](
const FormData &file) {
11480 if (file.filename.empty()) {
11481 cur_field = req.form.fields.emplace(
11482 file.name, FormField{file.name, file.content, file.headers});
11483 is_text_field =
true;
11485 cur_file = req.form.files.emplace(file.name, file);
11486 is_text_field =
false;
11490 [&](
const char *buf,
size_t n) {
11491 if (is_text_field) {
11492 auto &content = cur_field->second.content;
11493 if (content.size() + n > content.max_size()) {
return false; }
11494 content.append(buf, n);
11496 auto &content = cur_file->second.content;
11497 if (content.size() + n > content.max_size()) {
return false; }
11498 content.append(buf, n);
11502 const auto &content_type = req.get_header_value(
"Content-Type");
11504 "application/x-www-form-urlencoded") {
11517inline bool Server::read_content_with_content_receiver(
11520 return read_content_core(strm, req, res, std::move(receiver),
11521 std::move(multipart_header),
11522 std::move(multipart_receiver));
11525inline bool Server::read_content_core(
11528 detail::FormDataParser multipart_form_data_parser;
11531 if (req.is_multipart_form_data()) {
11532 const auto &content_type = req.get_header_value(
"Content-Type");
11533 std::string boundary;
11540 multipart_form_data_parser.set_boundary(std::move(boundary));
11541 out = [&](
const char *buf,
size_t n,
size_t ,
size_t ) {
11542 return multipart_form_data_parser.parse(buf, n, multipart_header,
11543 multipart_receiver);
11546 out = [receiver](
const char *buf,
size_t n,
size_t ,
11547 size_t ) {
return receiver(buf, n); };
11554#if !defined(CPPHTTPLIB_SSL_ENABLED)
11555 if (!req.has_header(
"Content-Length") &&
11559 auto has_data = strm.is_readable();
11561 auto s = strm.socket();
11581 if (!req.has_header(
"Content-Length") &&
11592 req.body_consumed_ =
true;
11594 if (req.is_multipart_form_data()) {
11595 if (!multipart_form_data_parser.is_valid()) {
11605inline bool Server::handle_file_request(
Request &req,
Response &res) {
11606 for (
const auto &entry : base_dirs_) {
11608 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
11609 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
11611 auto path = entry.base_dir + sub_path;
11612 if (path.back() ==
'/') { path +=
"index.html"; }
11616 if (!entry.resolved_base_dir.empty()) {
11617 std::string resolved_path;
11620 entry.resolved_base_dir)) {
11626 detail::FileStat stat(path);
11628 if (stat.is_dir()) {
11633 if (stat.is_file()) {
11634 for (
const auto &kv : entry.headers) {
11635 res.set_header(kv.first, kv.second);
11639 if (!etag.empty()) { res.set_header(
"ETag", etag); }
11641 auto mtime = stat.mtime();
11644 if (!last_modified.empty()) {
11645 res.set_header(
"Last-Modified", last_modified);
11648 if (check_if_not_modified(req, res, etag, mtime)) {
return true; }
11650 check_if_range(req, etag, mtime);
11652 auto mm = std::make_shared<detail::mmap>(path.c_str());
11653 if (!mm->is_open()) {
11658 res.set_content_provider(
11661 default_file_mimetype_),
11662 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
11663 sink.write(mm->data() + offset, length);
11667 if (req.method !=
"HEAD" && file_request_handler_) {
11668 file_request_handler_(req, res);
11681inline bool Server::check_if_not_modified(
const Request &req,
Response &res,
11682 const std::string &etag,
11683 time_t mtime)
const {
11687 if (req.has_header(
"If-None-Match")) {
11688 if (!etag.empty()) {
11689 auto val = req.get_header_value(
"If-None-Match");
11698 [&](
const char *b,
const char *
e) {
11699 auto seg_len = static_cast<size_t>(e - b);
11700 return (seg_len == 1 && *b ==
'*') ||
11701 (seg_len == etag.size() &&
11702 std::equal(b, e, etag.begin()));
11710 }
else if (req.has_header(
"If-Modified-Since")) {
11711 auto val = req.get_header_value(
"If-Modified-Since");
11714 if (t !=
static_cast<time_t
>(-1) && mtime <= t) {
11722inline bool Server::check_if_range(
Request &req,
const std::string &etag,
11723 time_t mtime)
const {
11728 if (!req.ranges.empty() && req.has_header(
"If-Range")) {
11729 auto val = req.get_header_value(
"If-Range");
11731 auto is_valid_range = [&]() {
11735 return (!etag.empty() && val == etag);
11742 return (t !=
static_cast<time_t
>(-1) && mtime <= t);
11746 if (!is_valid_range()) {
11748 req.ranges.clear();
11757Server::create_server_socket(
const std::string &host,
int port,
11761 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
11762 ipv6_v6only_, std::move(socket_options),
11763 [&](
socket_t sock,
struct addrinfo &ai,
bool & ) ->
bool {
11764 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
11776inline int Server::bind_internal(
const std::string &host,
int port,
11777 int socket_flags) {
11778 if (is_decommissioned) {
return -1; }
11782 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
11786 struct sockaddr_storage addr;
11787 socklen_t addr_len =
sizeof(addr);
11788 if (getsockname(
svr_sock_,
reinterpret_cast<struct sockaddr *
>(&addr),
11789 &addr_len) == -1) {
11793 if (addr.ss_family == AF_INET) {
11794 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&addr)->sin_port);
11795 }
else if (addr.ss_family == AF_INET6) {
11796 return ntohs(
reinterpret_cast<struct sockaddr_in6 *
>(&addr)->sin6_port);
11806inline bool Server::listen_internal() {
11807 if (is_decommissioned) {
return false; }
11810 is_running_ =
true;
11811 auto se = detail::scope_exit([&]() { is_running_ =
false; });
11813 if (start_handler_) { start_handler_(); }
11825 task_queue->on_idle();
11836#elif defined SOCK_CLOEXEC
11843 if (errno == EMFILE) {
11846 std::this_thread::sleep_for(std::chrono::microseconds{1});
11848 }
else if (errno == EINTR || errno == EAGAIN) {
11866 if (tcp_nodelay_) {
set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
11868 if (!task_queue->enqueue(
11869 [
this, sock]() { process_and_close_socket(sock); })) {
11876 task_queue->shutdown();
11879 is_decommissioned = !
ret;
11884 if (pre_routing_handler_ &&
11890 if ((req.method ==
"GET" || req.method ==
"HEAD") &&
11891 handle_file_request(req, res)) {
11902 bool content_reader_payload_too_large =
false;
11904 ContentReader reader(
11906 auto result = read_content_with_content_receiver(
11907 strm, req, res, std::move(receiver),
nullptr,
nullptr);
11911 content_reader_payload_too_large =
true;
11917 auto result = read_content_with_content_receiver(
11918 strm, req, res,
nullptr, std::move(
header),
11919 std::move(receiver));
11923 content_reader_payload_too_large =
true;
11929 bool dispatched =
false;
11930 if (req.method ==
"POST") {
11931 dispatched = dispatch_request_for_content_reader(
11932 req, res, std::move(reader), post_handlers_for_content_reader_);
11933 }
else if (req.method ==
"PUT") {
11934 dispatched = dispatch_request_for_content_reader(
11935 req, res, std::move(reader), put_handlers_for_content_reader_);
11936 }
else if (req.method ==
"PATCH") {
11937 dispatched = dispatch_request_for_content_reader(
11938 req, res, std::move(reader), patch_handlers_for_content_reader_);
11939 }
else if (req.method ==
"DELETE") {
11940 dispatched = dispatch_request_for_content_reader(
11941 req, res, std::move(reader), delete_handlers_for_content_reader_);
11945 if (content_reader_payload_too_large) {
11950 res.content_length_ = 0;
11951 res.content_provider_ =
nullptr;
11959 if (!read_content(strm, req, res)) {
11966 if (req.method ==
"GET" || req.method ==
"HEAD") {
11967 return dispatch_request(req, res, get_handlers_);
11968 }
else if (req.method ==
"POST") {
11969 return dispatch_request(req, res, post_handlers_);
11970 }
else if (req.method ==
"PUT") {
11971 return dispatch_request(req, res, put_handlers_);
11972 }
else if (req.method ==
"DELETE") {
11973 return dispatch_request(req, res, delete_handlers_);
11974 }
else if (req.method ==
"OPTIONS") {
11975 return dispatch_request(req, res, options_handlers_);
11976 }
else if (req.method ==
"PATCH") {
11977 return dispatch_request(req, res, patch_handlers_);
11985 const Handlers &handlers)
const {
11986 for (
const auto &x : handlers) {
11987 const auto &matcher = x.first;
11988 const auto &handler = x.second;
11990 if (matcher->match(req)) {
11991 req.matched_route = matcher->pattern();
11992 if (!pre_request_handler_ ||
12003 std::string &content_type,
12004 std::string &boundary)
const {
12006 auto it = res.headers.find(
"Content-Type");
12007 if (it != res.headers.end()) {
12008 content_type = it->second;
12009 res.headers.erase(it);
12014 res.set_header(
"Content-Type",
12015 "multipart/byteranges; boundary=" + boundary);
12020 if (res.body.empty()) {
12021 if (res.content_length_ > 0) {
12024 length = res.content_length_;
12025 }
else if (req.ranges.size() == 1) {
12027 req.ranges[0], res.content_length_);
12029 length = offset_and_length.second;
12032 offset_and_length, res.content_length_);
12033 res.set_header(
"Content-Range", content_range);
12036 req, boundary, content_type, res.content_length_);
12038 res.set_header(
"Content-Length", std::to_string(length));
12040 if (res.content_provider_) {
12041 if (res.is_chunked_content_provider_) {
12042 res.set_header(
"Transfer-Encoding",
"chunked");
12045 res.set_header(
"Vary",
"Accept-Encoding");
12053 }
else if (req.ranges.size() == 1) {
12054 auto offset_and_length =
12056 auto offset = offset_and_length.first;
12057 auto length = offset_and_length.second;
12060 offset_and_length, res.body.size());
12061 res.set_header(
"Content-Range", content_range);
12063 assert(offset + length <= res.body.size());
12064 res.body = res.body.substr(offset, length);
12068 res.body.size(),
data);
12069 res.body.swap(
data);
12073 output_pre_compression_log(req, res);
12076 std::string compressed;
12077 if (compressor->compress(res.body.data(), res.body.size(),
true,
12078 [&](
const char *
data,
size_t data_len) {
12079 compressed.append(data, data_len);
12082 res.body.swap(compressed);
12084 res.set_header(
"Vary",
"Accept-Encoding");
12089 auto length = std::to_string(res.body.size());
12090 res.set_header(
"Content-Length", length);
12094inline bool Server::dispatch_request_for_content_reader(
12096 const HandlersForContentReader &handlers)
const {
12097 for (
const auto &x : handlers) {
12098 const auto &matcher = x.first;
12099 const auto &handler = x.second;
12101 if (matcher->match(req)) {
12102 req.matched_route = matcher->pattern();
12103 if (!pre_request_handler_ ||
12105 handler(req, res, content_reader);
12115 const std::vector<std::string> &trusted_proxies) {
12117 std::vector<std::string> ip_list;
12119 x_forwarded_for.data() + x_forwarded_for.size(),
',',
12120 [&](
const char *b,
const char *
e) {
12121 auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
12122 ip_list.emplace_back(std::string(b + r.first, b + r.second));
12128 if (ip_list.empty()) {
return std::string(); }
12130 for (
size_t i = 0; i < ip_list.size(); ++i) {
12131 auto ip = ip_list[i];
12133 auto is_trusted_proxy =
12134 std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
12135 [&](
const std::string &proxy) { return ip == proxy; });
12137 if (is_trusted_proxy) {
12143 return ip_list[i - 1];
12149 return ip_list.front();
12154 int remote_port,
const std::string &local_addr,
12155 int local_port,
bool close_connection,
12156 bool &connection_closed,
12157 const std::function<
void(
Request &)> &setup_request,
12158 bool *websocket_upgraded) {
12159 std::array<char, 2048> buf{};
12164 if (!line_reader.
getline()) {
return false; }
12167 req.
start_time_ = std::chrono::steady_clock::now();
12175 res.
headers = default_headers_;
12178 if (!parse_request_line(line_reader.
ptr(), req)) {
12181 return write_response(strm, close_connection, req, res);
12188 return write_response(strm, close_connection, req, res);
12196 connection_closed =
true;
12198 return write_response(strm, close_connection, req, res);
12203 connection_closed =
true;
12206 return write_response(strm, close_connection, req, res);
12210 connection_closed =
true;
12213 if (req.
version ==
"HTTP/1.0" &&
12215 connection_closed =
true;
12221 req.
remote_addr = derived.empty() ? remote_addr : derived;
12233 connection_closed =
true;
12236 return write_response(strm, close_connection, req, res);
12243 connection_closed =
true;
12246 return write_response(strm, close_connection, req, res);
12250 if (setup_request) { setup_request(req); }
12254 if (expect_100_continue_handler_) {
12255 status = expect_100_continue_handler_(req, res);
12261 strm.
write(
"\r\n");
12264 connection_closed =
true;
12265 return write_response(strm,
true, req, res);
12270 auto sock = strm.
socket();
12280 if (pre_routing_handler_ &&
12283 return write_response(strm, close_connection, req, res);
12286 for (
const auto &entry : websocket_handlers_) {
12287 if (entry.matcher->match(req)) {
12293 std::string selected_subprotocol;
12294 if (entry.sub_protocol_selector) {
12296 if (!protocol_header.empty()) {
12297 std::vector<std::string> protocols;
12298 std::istringstream iss(protocol_header);
12300 while (std::getline(iss, token,
',')) {
12302 auto start = token.find_first_not_of(
' ');
12303 auto end = token.find_last_not_of(
' ');
12304 if (start != std::string::npos) {
12305 protocols.push_back(token.substr(start, end - start + 1));
12308 selected_subprotocol = entry.sub_protocol_selector(protocols);
12313 std::string handshake_response =
"HTTP/1.1 101 Switching Protocols\r\n"
12314 "Upgrade: websocket\r\n"
12315 "Connection: Upgrade\r\n"
12316 "Sec-WebSocket-Accept: " +
12317 accept_key +
"\r\n";
12318 if (!selected_subprotocol.empty()) {
12322 handshake_response +=
12323 "Sec-WebSocket-Protocol: " + selected_subprotocol +
"\r\n";
12325 handshake_response +=
"\r\n";
12326 if (strm.
write(handshake_response.data(), handshake_response.size()) <
12331 connection_closed =
true;
12332 if (websocket_upgraded) { *websocket_upgraded =
true; }
12339 entry.handler(req,
ws);
12348 auto routed =
false;
12349#ifdef CPPHTTPLIB_NO_EXCEPTIONS
12350 routed = routing(req, res, strm);
12353 routed = routing(req, res, strm);
12354 }
catch (std::exception &) {
12355 if (exception_handler_) {
12356 auto ep = std::current_exception();
12357 exception_handler_(req, res, ep);
12363 if (exception_handler_) {
12364 auto ep = std::current_exception();
12365 exception_handler_(req, res, ep);
12380 auto file_open_error =
false;
12383 auto mm = std::make_shared<detail::mmap>(path.c_str());
12384 if (!mm->is_open()) {
12390 file_open_error =
true;
12393 if (content_type.empty()) {
12395 path, file_extension_and_mimetype_map_, default_file_mimetype_);
12399 mm->size(), content_type,
12400 [mm](
size_t offset,
size_t length,
DataSink &sink) ->
bool {
12401 sink.write(mm->data() + offset, length);
12407 if (file_open_error) {
12408 ret = write_response(strm, close_connection, req, res);
12414 ret = write_response(strm, close_connection, req, res);
12416 ret = write_response_with_content(strm, close_connection, req, res);
12420 ret = write_response(strm, close_connection, req, res);
12430 [](
const char *,
size_t,
size_t,
size_t) {
return true; },
false)) {
12431 connection_closed =
true;
12440inline bool Server::process_and_close_socket(
socket_t sock) {
12441 std::string remote_addr;
12442 int remote_port = 0;
12445 std::string local_addr;
12446 int local_port = 0;
12449 bool websocket_upgraded =
false;
12454 [&](
Stream &strm,
bool close_connection,
bool &connection_closed) {
12456 local_port, close_connection, connection_closed,
12457 nullptr, &websocket_upgraded);
12465inline void Server::output_log(
const Request &req,
const Response &res)
const {
12467 std::lock_guard<std::mutex> guard(logger_mutex_);
12472inline void Server::output_pre_compression_log(
const Request &req,
12474 if (pre_compression_logger_) {
12475 std::lock_guard<std::mutex> guard(logger_mutex_);
12476 pre_compression_logger_(req, res);
12480inline void Server::output_error_log(
const Error &err,
12482 if (error_logger_) {
12483 std::lock_guard<std::mutex> guard(logger_mutex_);
12484 error_logger_(err, req);
12499 const std::string &client_cert_path,
12500 const std::string &client_key_path)
12506 size_t retry_count = 10;
12507 while (retry_count-- > 0) {
12512 std::this_thread::sleep_for(std::chrono::milliseconds{1});
12555#ifdef CPPHTTPLIB_SSL_ENABLED
12556 digest_auth_username_ = rhs.digest_auth_username_;
12557 digest_auth_password_ = rhs.digest_auth_password_;
12558 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
12559 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
12560 ca_cert_file_path_ = rhs.ca_cert_file_path_;
12561 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
12562 server_certificate_verification_ = rhs.server_certificate_verification_;
12563 server_hostname_verification_ = rhs.server_hostname_verification_;
12596 if (it !=
addr_map_.end()) { ip = it->second; }
12607 auto sock = create_client_socket(
error);
12619 std::chrono::time_point<std::chrono::steady_clock> ,
12648#ifdef CPPHTTPLIB_SSL_ENABLED
12649 assert(
socket.ssl ==
nullptr);
12663inline bool ClientImpl::read_response_line(
Stream &strm,
const Request &req,
12665 bool skip_100_continue)
const {
12666 std::array<char, 2048> buf{};
12668 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12670 if (!line_reader.getline()) {
return false; }
12672#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
12673 thread_local const std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
12675 thread_local const std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
12679 if (!std::regex_match(line_reader.ptr(), m, re)) {
12680 return req.
method ==
"CONNECT";
12682 res.
version = std::string(m[1]);
12683 res.
status = std::stoi(std::string(m[2]));
12684 res.
reason = std::string(m[3]);
12688 if (!line_reader.getline()) {
return false; }
12689 if (!line_reader.getline()) {
return false; }
12691 if (!std::regex_match(line_reader.ptr(), m, re)) {
return false; }
12692 res.
version = std::string(m[1]);
12693 res.
status = std::stoi(std::string(m[2]));
12694 res.
reason = std::string(m[3]);
12701 std::lock_guard<std::recursive_mutex> request_mutex_guard(
request_mutex_);
12702 auto ret = send_(req, res,
error);
12721 auto is_alive =
false;
12725#ifdef CPPHTTPLIB_SSL_ENABLED
12726 if (is_alive && is_ssl()) {
12746 auto success =
true;
12774 auto se = detail::scope_exit([&]() {
12790 return handle_request(strm, req, res, close_connection, error);
12805 return send_(std::move(req2));
12812#ifdef CPPHTTPLIB_SSL_ENABLED
12814 last_ssl_error_, last_backend_error_};
12820inline void ClientImpl::prepare_default_headers(Request &r,
bool for_stream,
12821 const std::string &ct) {
12824 if (!r.has_header(
header.first)) { r.headers.insert(
header); }
12827 if (!r.has_header(
"Host")) {
12829 r.headers.emplace(
"Host",
"localhost");
12836 if (!r.has_header(
"Accept")) { r.headers.emplace(
"Accept",
"*/*"); }
12838 if (!r.content_receiver) {
12839 if (!r.has_header(
"Accept-Encoding")) {
12840 std::string accept_encoding;
12841#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12842 accept_encoding =
"br";
12844#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12845 if (!accept_encoding.empty()) { accept_encoding +=
", "; }
12846 accept_encoding +=
"gzip, deflate";
12848#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12849 if (!accept_encoding.empty()) { accept_encoding +=
", "; }
12850 accept_encoding +=
"zstd";
12852 r.set_header(
"Accept-Encoding", accept_encoding);
12855#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
12856 if (!r.has_header(
"User-Agent")) {
12858 r.set_header(
"User-Agent", agent);
12863 if (!r.body.empty()) {
12864 if (!ct.empty() && !r.has_header(
"Content-Type")) {
12865 r.headers.emplace(
"Content-Type", ct);
12867 if (!r.has_header(
"Content-Length")) {
12868 r.headers.emplace(
"Content-Length", std::to_string(r.body.size()));
12873inline ClientImpl::StreamHandle
12876 const std::string &body,
12877 const std::string &content_type) {
12888 auto is_alive =
false;
12891#ifdef CPPHTTPLIB_SSL_ENABLED
12892 if (is_alive && is_ssl()) {
12908 auto success =
true;
12909 auto start_time = std::chrono::steady_clock::now();
12911 success, handle.
error)) {
12912 if (!success) { handle.
response.reset(); }
12918 transfer_socket_ownership_to_handle(handle);
12921#ifdef CPPHTTPLIB_SSL_ENABLED
12922 if (is_ssl() && handle.connection_->session) {
12924 handle.connection_->sock, handle.connection_->session,
12937 handle.stream_ = handle.socket_stream_.get();
12941 req.
path = query_path;
12945 prepare_default_headers(req,
true, content_type);
12947 auto &strm = *handle.stream_;
12960 if (!body.empty()) {
12961 if (strm.
write(body.data(), body.size()) < 0) {
12968 if (!read_response_line(strm, req, *handle.
response) ||
12975 handle.body_reader_.
stream = handle.stream_;
12978 if (handle.
response->has_header(
"Content-Length")) {
12979 bool is_invalid =
false;
12981 handle.
response->headers,
"Content-Length", 0, 0, is_invalid);
12991 auto transfer_encoding =
12992 handle.
response->get_header_value(
"Transfer-Encoding");
12993 handle.body_reader_.
chunked = (transfer_encoding ==
"chunked");
12995 auto content_encoding = handle.
response->get_header_value(
"Content-Encoding");
12996 if (!content_encoding.empty()) {
13006 if (decompressor_) {
return read_with_decompression(buf, len); }
13011 if (body_reader_.chunked_decoder) {
13012 if (!body_reader_.chunked_decoder->parse_trailers_into(
13027inline ssize_t ClientImpl::StreamHandle::read_with_decompression(
char *buf,
13029 if (decompress_offset_ < decompress_buffer_.size()) {
13030 auto available = decompress_buffer_.size() - decompress_offset_;
13031 auto to_copy = (std::min)(len, available);
13032 std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
13033 decompress_offset_ += to_copy;
13034 decompressed_bytes_read_ += to_copy;
13035 return static_cast<ssize_t
>(to_copy);
13038 decompress_buffer_.clear();
13039 decompress_offset_ = 0;
13041 constexpr size_t kDecompressionBufferSize = 8192;
13042 char compressed_buf[kDecompressionBufferSize];
13046 sizeof(compressed_buf));
13048 if (n <= 0) {
return n; }
13050 bool decompress_ok = decompressor_->decompress(
13051 compressed_buf,
static_cast<size_t>(n),
13052 [
this](
const char *
data,
size_t data_len) {
13053 decompress_buffer_.append(
data, data_len);
13054 auto limit = body_reader_.payload_max_length;
13055 if (decompressed_bytes_read_ + decompress_buffer_.size() > limit) {
13061 if (!decompress_ok) {
13066 if (!decompress_buffer_.empty()) {
break; }
13069 auto to_copy = (std::min)(len, decompress_buffer_.size());
13070 std::memcpy(buf, decompress_buffer_.data(), to_copy);
13071 decompress_offset_ = to_copy;
13072 decompressed_bytes_read_ += to_copy;
13073 return static_cast<ssize_t
>(to_copy);
13083 const auto bufsiz = 128;
13084 char line_buf[bufsiz];
13087 if (!line_reader.
getline()) {
return; }
13100 size_t &out_chunk_offset,
13101 size_t &out_chunk_total) {
13106 if (!lr.
getline()) {
return -1; }
13109 const char *p = lr.
ptr();
13111 if (!
is_hex(*p, v)) {
return -1; }
13113 size_t chunk_len = 0;
13114 constexpr size_t chunk_len_max = (std::numeric_limits<size_t>::max)();
13115 for (;
is_hex(*p, v); ++p) {
13116 if (chunk_len > (chunk_len_max >> 4)) {
return -1; }
13117 chunk_len = (chunk_len << 4) | static_cast<size_t>(v);
13123 if (*p !=
'\0' && *p !=
';' && *p !=
'\r' && *p !=
'\n') {
return -1; }
13125 if (chunk_len == 0) {
13128 out_chunk_offset = 0;
13129 out_chunk_total = 0;
13139 auto n =
strm.read(buf, to_read);
13140 if (n <= 0) {
return -1; }
13146 out_chunk_offset = offset_before;
13151 if (!lr.
getline()) {
return -1; }
13152 if (std::strcmp(lr.
ptr(),
"\r\n") != 0) {
return -1; }
13159 const Headers &src_headers) {
13161 if (!lr.
getline()) {
return false; }
13168ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
13170#ifdef CPPHTTPLIB_SSL_ENABLED
13171 handle.connection_->session =
socket_.ssl;
13177inline bool ClientImpl::handle_request(Stream &strm, Request &req,
13178 Response &res,
bool close_connection,
13180 if (req.path.empty()) {
13186 auto req_save = req;
13192 req2.path =
"http://" +
13196 req = std::move(req2);
13197 req.path = req_save.path;
13202 if (!
ret) {
return false; }
13204 if (res.get_header_value(
"Connection") ==
"close" ||
13205 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
13218 req = std::move(req_save);
13222#ifdef CPPHTTPLIB_SSL_ENABLED
13225 req.authorization_count_ < 5) {
13235 const auto &username =
13236 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
13237 const auto &password =
13238 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
13240 if (!username.empty() && !password.empty()) {
13241 std::map<std::string, std::string> auth;
13243 Request new_req = req;
13244 new_req.authorization_count_ += 1;
13245 new_req.headers.erase(is_proxy ?
"Proxy-Authorization"
13246 :
"Authorization");
13247 new_req.headers.insert(detail::make_digest_authentication_header(
13249 username, password, is_proxy));
13254 if (
ret) { res = std::move(new_res); }
13264 if (req.redirect_count_ == 0) {
13270 auto location = res.get_header_value(
"location");
13271 if (location.empty()) {
return false; }
13273 detail::UrlComponents uc;
13277 if (!uc.scheme.empty() && uc.scheme !=
"http" && uc.scheme !=
"https") {
13281 auto scheme = is_ssl() ?
"https" :
"http";
13283 auto next_scheme = std::move(uc.scheme);
13284 auto next_host = std::move(uc.host);
13285 auto port_str = std::move(uc.port);
13286 auto next_path = std::move(uc.path);
13287 auto next_query = std::move(uc.query);
13289 auto next_port =
port_;
13290 if (!port_str.empty()) {
13292 }
else if (!next_scheme.empty()) {
13293 next_port = next_scheme ==
"https" ? 443 : 80;
13296 if (next_scheme.empty()) { next_scheme = scheme; }
13297 if (next_host.empty()) { next_host =
host_; }
13298 if (next_path.empty()) { next_path =
"/"; }
13303 if (next_scheme == scheme && next_host ==
host_ && next_port ==
port_) {
13308 return create_redirect_client(next_scheme, next_host, next_port, req, res,
13309 path, location,
error);
13313inline bool ClientImpl::create_redirect_client(
13314 const std::string &scheme,
const std::string &host,
int port,
Request &req,
13315 Response &res,
const std::string &path,
const std::string &location,
13318 auto need_ssl = (scheme ==
"https");
13322 auto headers_to_remove =
13323 std::vector<std::string>{
"Host",
"Proxy-Authorization",
"Authorization"};
13325 for (
const auto &header_name : headers_to_remove) {
13326 auto it = req.headers.find(header_name);
13327 while (it != req.headers.end()) {
13328 it = req.headers.erase(it);
13329 it = req.headers.find(header_name);
13335#ifdef CPPHTTPLIB_SSL_ENABLED
13337 SSLClient redirect_client(
host,
port);
13340 setup_redirect_client(redirect_client);
13342 redirect_client.enable_server_certificate_verification(
13343 server_certificate_verification_);
13344 redirect_client.enable_server_hostname_verification(
13345 server_hostname_verification_);
13348 if (!ca_cert_pem_.empty()) {
13349 redirect_client.load_ca_cert_store(ca_cert_pem_.c_str(),
13350 ca_cert_pem_.size());
13352 if (!ca_cert_file_path_.empty()) {
13353 redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
13374 setup_redirect_client(redirect_client);
13383template <
typename ClientType>
13384inline void ClientImpl::setup_redirect_client(ClientType &client) {
13390 client.set_follow_location(
13416#ifdef CPPHTTPLIB_SSL_ENABLED
13417 if (!proxy_digest_auth_username_.empty()) {
13418 client.set_proxy_digest_auth(proxy_digest_auth_username_,
13419 proxy_digest_auth_password_);
13442 auto is_shutting_down = []() {
return false; };
13446 : std::unique_ptr<detail::compressor>();
13452 is_shutting_down, *compressor,
error);
13460inline bool ClientImpl::write_request(
Stream &strm,
Request &req,
13464 if (close_connection) {
13470 std::string ct_for_defaults;
13472 ct_for_defaults =
"text/plain";
13474 prepare_default_headers(req,
false, ct_for_defaults);
13476 if (req.
body.empty()) {
13486 req.
method ==
"PATCH") {
13525 detail::BufferStream bstrm;
13528 std::string path_part, query_part;
13529 auto query_pos = req.
path.find(
'?');
13530 if (query_pos != std::string::npos) {
13531 path_part = req.
path.substr(0, query_pos);
13532 query_part = req.
path.substr(query_pos + 1);
13534 path_part = req.
path;
13545 auto path_with_query =
13548 if (!query_part.empty()) {
13552 if (!normalized.empty()) { path_with_query +=
'?' + normalized; }
13561 if (!req.
params.empty()) {
13575 auto &
data = bstrm.get_buffer();
13598 auto start = std::chrono::high_resolution_clock::now();
13604 auto sock = strm.
socket();
13613 if (!is_ssl() && strm.
is_readable()) {
return false; }
13615 auto now = std::chrono::high_resolution_clock::now();
13617 std::chrono::duration_cast<std::chrono::milliseconds>(
now - start)
13623 std::this_thread::sleep_for(std::chrono::milliseconds(1));
13629 if (skip_body) {
return true; }
13631 return write_request_body(strm, req,
error);
13634inline bool ClientImpl::write_request_body(
Stream &strm,
Request &req,
13636 if (req.body.empty()) {
13640 if (req.upload_progress) {
13641 auto body_size = req.body.size();
13642 size_t written = 0;
13643 auto data = req.body.data();
13645 while (written < body_size) {
13652 written += to_write;
13654 if (!req.upload_progress(written, body_size)) {
13671inline std::unique_ptr<Response>
13672ClientImpl::send_with_content_provider_and_receiver(
13673 Request &req,
const char *body,
size_t content_length,
13678 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
13682 : std::pair<std::unique_ptr<detail::compressor>, const char *>(
13685 if (enc.second) { req.set_header(
"Content-Encoding", enc.second); }
13687 if (enc.first && !content_provider_without_length) {
13688 auto &compressor = enc.first;
13690 if (content_provider) {
13693 DataSink data_sink;
13695 data_sink.write = [&](const char *data, size_t data_len) -> bool {
13697 auto last = offset + data_len == content_length;
13699 auto ret = compressor->compress(
13700 data, data_len, last,
13701 [&](const char *compressed_data, size_t compressed_data_len) {
13702 req.body.append(compressed_data, compressed_data_len);
13707 offset += data_len;
13715 while (ok && offset < content_length) {
13716 if (!content_provider(offset, content_length - offset, data_sink)) {
13717 error = Error::Canceled;
13718 output_error_log(error, &req);
13723 if (!compressor->compress(body, content_length, true,
13724 [&](const char *data, size_t data_len) {
13725 req.body.append(data, data_len);
13734 if (content_provider) {
13735 req.content_length_ = content_length;
13736 req.content_provider_ = std::move(content_provider);
13737 req.is_chunked_content_provider_ =
false;
13738 }
else if (content_provider_without_length) {
13739 req.content_length_ = 0;
13741 std::move(content_provider_without_length));
13742 req.is_chunked_content_provider_ =
true;
13743 req.set_header(
"Transfer-Encoding",
"chunked");
13745 req.body.assign(body, content_length);
13749 if (content_receiver) {
13750 req.content_receiver =
13751 [content_receiver](
const char *
data,
size_t data_length,
13752 size_t ,
size_t ) {
13753 return content_receiver(
data, data_length);
13758 return send(req, *res,
error) ? std::move(res) : nullptr;
13761inline Result ClientImpl::send_with_content_provider_and_receiver(
13762 const std::string &method,
const std::string &path,
const Headers &headers,
13763 const char *body,
size_t content_length, ContentProvider content_provider,
13764 ContentProviderWithoutLength content_provider_without_length,
13765 const std::string &content_type, ContentReceiver content_receiver,
13766 UploadProgress progress) {
13768 req.method = method;
13769 req.headers = headers;
13771 req.upload_progress = std::move(progress);
13772 if (max_timeout_msec_ > 0) {
13773 req.start_time_ = std::chrono::steady_clock::now();
13776 auto error = Error::Success;
13778 auto res = send_with_content_provider_and_receiver(
13779 req, body, content_length, std::move(content_provider),
13780 std::move(content_provider_without_length), content_type,
13781 std::move(content_receiver),
error);
13783#ifdef CPPHTTPLIB_SSL_ENABLED
13784 return Result{std::move(res),
error, std::move(req.headers), last_ssl_error_,
13785 last_backend_error_};
13787 return Result{std::move(res),
error, std::move(req.headers)};
13808 Response &res,
bool close_connection,
13819 auto expect_100_continue = req.
get_header_value(
"Expect") ==
"100-continue";
13822 auto write_request_success =
13823 write_request(strm, req, close_connection,
error, expect_100_continue);
13825#ifdef CPPHTTPLIB_SSL_ENABLED
13826 if (is_ssl() && !expect_100_continue) {
13828 if (!is_proxy_enabled) {
13847 auto status_line_read =
false;
13848 if (expect_100_continue && write_request_success) {
13853 status_line_read = read_response_line(strm, req, res,
false);
13857 if (!status_line_read) {
13860 if (!write_request_body(strm, req,
error)) {
return false; }
13861 expect_100_continue =
false;
13867 if ((!status_line_read &&
13868 !read_response_line(strm, req, res, !expect_100_continue)) ||
13875 if (!write_request_success) {
return false; }
13878 if (expect_100_continue) {
13881 if (!write_request_body(strm, req,
error)) {
return false; }
13886 if (!read_response_line(strm, req, res) ||
13898 req.
method !=
"CONNECT") {
13899 auto redirect = 300 < res.
status && res.
status < 400 &&
13914 [&](
const char *buf,
size_t n,
size_t off,
size_t len) {
13915 if (redirect) {
return true; }
13924 [&](
const char *buf,
size_t n,
size_t ,
13926 assert(res.
body.size() + n <= res.
body.max_size());
13932 res.
body.append(buf, n);
13936 auto progress = [&](
size_t current,
size_t total) {
13949 if (len > res.
body.max_size()) {
13958 auto reserve_len =
static_cast<size_t>(len);
13962 res.
body.reserve(reserve_len);
13969 ? (std::numeric_limits<size_t>::max)()
13972 std::move(progress), std::move(out),
13990 size_t cur_item = 0;
13991 size_t cur_start = 0;
13994 return [&, cur_item, cur_start](
size_t offset,
13996 if (!offset && !items.empty()) {
13999 }
else if (cur_item < provider_items.size()) {
14001 const auto &begin = detail::serialize_multipart_formdata_item_begin(
14002 provider_items[cur_item], boundary);
14003 offset += begin.size();
14004 cur_start = offset;
14009 auto has_data =
true;
14010 cur_sink.write = sink.write;
14011 cur_sink.done = [&]() { has_data =
false; };
14013 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
14018 sink.os << detail::serialize_multipart_formdata_item_end();
14024 sink.os << detail::serialize_multipart_formdata_finish(boundary);
14031inline bool ClientImpl::process_socket(
14032 const Socket &socket,
14033 std::chrono::time_point<std::chrono::steady_clock> start_time,
14034 std::function<
bool(Stream &strm)> callback) {
14035 return detail::process_client_socket(
14036 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
14037 write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
14040inline bool ClientImpl::is_ssl()
const {
return false; }
14044 return Get(path,
Headers(), std::move(progress));
14050 if (params.empty()) {
return Get(path, headers); }
14053 return Get(path_with_query, headers, std::move(progress));
14064 req.
start_time_ = std::chrono::steady_clock::now();
14067 return send_(std::move(req));
14073 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
14074 std::move(progress));
14080 return Get(path, headers,
nullptr, std::move(content_receiver),
14081 std::move(progress));
14088 return Get(path,
Headers(), std::move(response_handler),
14089 std::move(content_receiver), std::move(progress));
14102 [content_receiver](
const char *
data,
size_t data_length,
14103 size_t ,
size_t ) {
14104 return content_receiver(
data, data_length);
14108 req.
start_time_ = std::chrono::steady_clock::now();
14111 return send_(std::move(req));
14118 return Get(path, params, headers,
nullptr, std::move(content_receiver),
14119 std::move(progress));
14127 if (params.empty()) {
14128 return Get(path, headers, std::move(response_handler),
14129 std::move(content_receiver), std::move(progress));
14133 return Get(path_with_query, headers, std::move(response_handler),
14134 std::move(content_receiver), std::move(progress));
14148 req.
start_time_ = std::chrono::steady_clock::now();
14151 return send_(std::move(req));
14155 return Post(path, std::string(), std::string());
14160 return Post(path, headers,
nullptr, 0, std::string());
14164 size_t content_length,
14165 const std::string &content_type,
14167 return Post(path,
Headers(), body, content_length, content_type, progress);
14171 const std::string &content_type,
14173 return Post(path,
Headers(), body, content_type, progress);
14182 const std::string &content_type,
14184 return Post(path,
Headers(), content_length, std::move(content_provider),
14185 content_type, progress);
14190 const std::string &content_type,
14193 return Post(path,
Headers(), content_length, std::move(content_provider),
14194 content_type, std::move(content_receiver), progress);
14199 const std::string &content_type,
14201 return Post(path,
Headers(), std::move(content_provider), content_type,
14207 const std::string &content_type,
14210 return Post(path,
Headers(), std::move(content_provider), content_type,
14211 std::move(content_receiver), progress);
14217 return Post(path, headers, query,
"application/x-www-form-urlencoded");
14230 const auto &content_type =
14233 return Post(path, headers, content_length,
14235 content_type, progress);
14240 const std::string &boundary,
14246 const auto &content_type =
14249 return Post(path, headers, content_length,
14251 content_type, progress);
14255 const char *body,
size_t content_length,
14256 const std::string &content_type,
14258 return send_with_content_provider_and_receiver(
14259 "POST", path, headers, body, content_length,
nullptr,
nullptr,
14260 content_type,
nullptr, progress);
14264 const std::string &body,
14265 const std::string &content_type,
14267 return send_with_content_provider_and_receiver(
14268 "POST", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14269 content_type,
nullptr, progress);
14273 size_t content_length,
14275 const std::string &content_type,
14277 return send_with_content_provider_and_receiver(
14278 "POST", path, headers,
nullptr, content_length,
14279 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14283 size_t content_length,
14285 const std::string &content_type,
14288 return send_with_content_provider_and_receiver(
14289 "POST", path, headers,
nullptr, content_length,
14290 std::move(content_provider),
nullptr, content_type,
14291 std::move(content_receiver), std::move(progress));
14296 const std::string &content_type,
14298 return send_with_content_provider_and_receiver(
14299 "POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14300 content_type,
nullptr, progress);
14305 const std::string &content_type,
14308 return send_with_content_provider_and_receiver(
14309 "POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14310 content_type, std::move(content_receiver), std::move(progress));
14318 const auto &content_type =
14320 return send_with_content_provider_and_receiver(
14321 "POST", path, headers,
nullptr, 0,
nullptr,
14322 get_multipart_content_provider(boundary, items, provider_items),
14323 content_type,
nullptr, progress);
14327 const std::string &body,
14328 const std::string &content_type,
14337 [content_receiver](
const char *
data,
size_t data_length,
14338 size_t ,
size_t ) {
14339 return content_receiver(
data, data_length);
14344 req.
start_time_ = std::chrono::steady_clock::now();
14347 if (!content_type.empty()) { req.
set_header(
"Content-Type", content_type); }
14349 return send_(std::move(req));
14353 return Put(path, std::string(), std::string());
14357 return Put(path, headers,
nullptr, 0, std::string());
14361 size_t content_length,
14362 const std::string &content_type,
14364 return Put(path,
Headers(), body, content_length, content_type, progress);
14368 const std::string &content_type,
14370 return Put(path,
Headers(), body, content_type, progress);
14379 const std::string &content_type,
14381 return Put(path,
Headers(), content_length, std::move(content_provider),
14382 content_type, progress);
14387 const std::string &content_type,
14390 return Put(path,
Headers(), content_length, std::move(content_provider),
14391 content_type, std::move(content_receiver), progress);
14396 const std::string &content_type,
14398 return Put(path,
Headers(), std::move(content_provider), content_type,
14404 const std::string &content_type,
14407 return Put(path,
Headers(), std::move(content_provider), content_type,
14408 std::move(content_receiver), progress);
14414 return Put(path, headers, query,
"application/x-www-form-urlencoded");
14420 return Put(path,
Headers(), items, progress);
14427 const auto &content_type =
14430 return Put(path, headers, content_length,
14432 content_type, progress);
14437 const std::string &boundary,
14443 const auto &content_type =
14446 return Put(path, headers, content_length,
14448 content_type, progress);
14452 const char *body,
size_t content_length,
14453 const std::string &content_type,
14455 return send_with_content_provider_and_receiver(
14456 "PUT", path, headers, body, content_length,
nullptr,
nullptr,
14457 content_type,
nullptr, progress);
14461 const std::string &body,
14462 const std::string &content_type,
14464 return send_with_content_provider_and_receiver(
14465 "PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14466 content_type,
nullptr, progress);
14470 size_t content_length,
14472 const std::string &content_type,
14474 return send_with_content_provider_and_receiver(
14475 "PUT", path, headers,
nullptr, content_length,
14476 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14480 size_t content_length,
14482 const std::string &content_type,
14485 return send_with_content_provider_and_receiver(
14486 "PUT", path, headers,
nullptr, content_length,
14487 std::move(content_provider),
nullptr, content_type,
14488 std::move(content_receiver), progress);
14493 const std::string &content_type,
14495 return send_with_content_provider_and_receiver(
14496 "PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14497 content_type,
nullptr, progress);
14502 const std::string &content_type,
14505 return send_with_content_provider_and_receiver(
14506 "PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14507 content_type, std::move(content_receiver), progress);
14515 const auto &content_type =
14517 return send_with_content_provider_and_receiver(
14518 "PUT", path, headers,
nullptr, 0,
nullptr,
14519 get_multipart_content_provider(boundary, items, provider_items),
14520 content_type,
nullptr, progress);
14524 const std::string &body,
14525 const std::string &content_type,
14534 [content_receiver](
const char *
data,
size_t data_length,
14535 size_t ,
size_t ) {
14536 return content_receiver(
data, data_length);
14541 req.
start_time_ = std::chrono::steady_clock::now();
14544 if (!content_type.empty()) { req.
set_header(
"Content-Type", content_type); }
14546 return send_(std::move(req));
14550 return Patch(path, std::string(), std::string());
14555 return Patch(path, headers,
nullptr, 0, std::string(), progress);
14559 size_t content_length,
14560 const std::string &content_type,
14562 return Patch(path,
Headers(), body, content_length, content_type, progress);
14566 const std::string &body,
14567 const std::string &content_type,
14569 return Patch(path,
Headers(), body, content_type, progress);
14578 const std::string &content_type,
14580 return Patch(path,
Headers(), content_length, std::move(content_provider),
14581 content_type, progress);
14586 const std::string &content_type,
14589 return Patch(path,
Headers(), content_length, std::move(content_provider),
14590 content_type, std::move(content_receiver), progress);
14595 const std::string &content_type,
14597 return Patch(path,
Headers(), std::move(content_provider), content_type,
14603 const std::string &content_type,
14606 return Patch(path,
Headers(), std::move(content_provider), content_type,
14607 std::move(content_receiver), progress);
14613 return Patch(path, headers, query,
"application/x-www-form-urlencoded");
14626 const auto &content_type =
14629 return Patch(path, headers, content_length,
14631 content_type, progress);
14636 const std::string &boundary,
14642 const auto &content_type =
14645 return Patch(path, headers, content_length,
14647 content_type, progress);
14651 const char *body,
size_t content_length,
14652 const std::string &content_type,
14654 return send_with_content_provider_and_receiver(
14655 "PATCH", path, headers, body, content_length,
nullptr,
nullptr,
14656 content_type,
nullptr, progress);
14660 const std::string &body,
14661 const std::string &content_type,
14663 return send_with_content_provider_and_receiver(
14664 "PATCH", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14665 content_type,
nullptr, progress);
14669 size_t content_length,
14671 const std::string &content_type,
14673 return send_with_content_provider_and_receiver(
14674 "PATCH", path, headers,
nullptr, content_length,
14675 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14679 size_t content_length,
14681 const std::string &content_type,
14684 return send_with_content_provider_and_receiver(
14685 "PATCH", path, headers,
nullptr, content_length,
14686 std::move(content_provider),
nullptr, content_type,
14687 std::move(content_receiver), progress);
14692 const std::string &content_type,
14694 return send_with_content_provider_and_receiver(
14695 "PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14696 content_type,
nullptr, progress);
14701 const std::string &content_type,
14704 return send_with_content_provider_and_receiver(
14705 "PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14706 content_type, std::move(content_receiver), progress);
14714 const auto &content_type =
14716 return send_with_content_provider_and_receiver(
14717 "PATCH", path, headers,
nullptr, 0,
nullptr,
14718 get_multipart_content_provider(boundary, items, provider_items),
14719 content_type,
nullptr, progress);
14723 const std::string &body,
14724 const std::string &content_type,
14733 [content_receiver](
const char *
data,
size_t data_length,
14734 size_t ,
size_t ) {
14735 return content_receiver(
data, data_length);
14740 req.
start_time_ = std::chrono::steady_clock::now();
14743 if (!content_type.empty()) { req.
set_header(
"Content-Type", content_type); }
14745 return send_(std::move(req));
14750 return Delete(path,
Headers(), std::string(), std::string(), progress);
14756 return Delete(path, headers, std::string(), std::string(), progress);
14760 size_t content_length,
14761 const std::string &content_type,
14763 return Delete(path,
Headers(), body, content_length, content_type, progress);
14767 const std::string &body,
14768 const std::string &content_type,
14770 return Delete(path,
Headers(), body.data(), body.size(), content_type,
14776 const std::string &body,
14777 const std::string &content_type,
14779 return Delete(path, headers, body.data(), body.size(), content_type,
14792 return Delete(path, headers, query,
"application/x-www-form-urlencoded",
14797 const Headers &headers,
const char *body,
14798 size_t content_length,
14799 const std::string &content_type,
14807 req.
start_time_ = std::chrono::steady_clock::now();
14810 if (!content_type.empty()) { req.
set_header(
"Content-Type", content_type); }
14811 req.
body.assign(body, content_length);
14813 return send_(std::move(req));
14827 req.
start_time_ = std::chrono::steady_clock::now();
14830 return send_(std::move(req));
14884 const std::string &password) {
14909 std::function<ssize_t(
Stream &,
Headers &)>
const &writer) {
14946 const std::string &password) {
14956 std::vector<detail::NoProxyEntry> parsed;
14957 parsed.reserve(patterns.size());
14958 for (
const auto &p : patterns) {
14960 if (trimmed.empty()) {
continue; }
14963 parsed.push_back(std::move(entry));
14971#ifdef CPPHTTPLIB_SSL_ENABLED
14972inline void ClientImpl::set_digest_auth(
const std::string &username,
14973 const std::string &password) {
14974 digest_auth_username_ = username;
14975 digest_auth_password_ = password;
14978inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
14979 const std::string &ca_cert_dir_path) {
14980 ca_cert_file_path_ = ca_cert_file_path;
14981 ca_cert_dir_path_ = ca_cert_dir_path;
14984inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
14985 const std::string &password) {
14986 proxy_digest_auth_username_ = username;
14987 proxy_digest_auth_password_ = password;
14990inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
14991 server_certificate_verification_ = enabled;
14994inline void ClientImpl::enable_server_hostname_verification(
bool enabled) {
14995 server_hostname_verification_ = enabled;
15012#ifdef CPPHTTPLIB_SSL_ENABLED
15014 tls::shutdown(session,
true);
15015 tls::free_session(session);
15028 :
Client(scheme_host_port,
std::string(),
std::string()) {}
15031 const std::string &client_cert_path,
15032 const std::string &client_key_path) {
15035 auto &scheme = uc.
scheme;
15037#ifdef CPPHTTPLIB_SSL_ENABLED
15038 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
15040 if (!scheme.empty() && scheme !=
"http") {
15042#ifndef CPPHTTPLIB_NO_EXCEPTIONS
15043 std::string msg =
"'" + scheme +
"' scheme is not supported.";
15044 throw std::invalid_argument(msg);
15049 auto is_ssl = scheme ==
"https";
15053 auto port = is_ssl ? 443 : 80;
15057#ifdef CPPHTTPLIB_SSL_ENABLED
15070 client_cert_path, client_key_path);
15078 const std::string &client_cert_path,
15079 const std::string &client_key_path)
15081 client_key_path)) {}
15086 return cli_ !=
nullptr && cli_->is_valid();
15090 return cli_->Get(path, std::move(progress));
15094 return cli_->Get(path, headers, std::move(progress));
15099 return cli_->Get(path, std::move(content_receiver), std::move(progress));
15104 return cli_->Get(path, headers, std::move(content_receiver),
15105 std::move(progress));
15111 return cli_->Get(path, std::move(response_handler),
15112 std::move(content_receiver), std::move(progress));
15118 return cli_->Get(path, headers, std::move(response_handler),
15119 std::move(content_receiver), std::move(progress));
15123 return cli_->Get(path, params, headers, std::move(progress));
15129 return cli_->Get(path, params, headers, std::move(content_receiver),
15130 std::move(progress));
15137 return cli_->Get(path, params, headers, std::move(response_handler),
15138 std::move(content_receiver), std::move(progress));
15143 return cli_->Head(path, headers);
15148 return cli_->Post(path, headers);
15151 size_t content_length,
15152 const std::string &content_type,
15154 return cli_->Post(path, body, content_length, content_type, progress);
15157 const char *body,
size_t content_length,
15158 const std::string &content_type,
15160 return cli_->Post(path, headers, body, content_length, content_type,
15164 const std::string &content_type,
15166 return cli_->Post(path, body, content_type, progress);
15169 const std::string &body,
15170 const std::string &content_type,
15172 return cli_->Post(path, headers, body, content_type, progress);
15176 const std::string &content_type,
15178 return cli_->Post(path, content_length, std::move(content_provider),
15179 content_type, progress);
15183 const std::string &content_type,
15186 return cli_->Post(path, content_length, std::move(content_provider),
15187 content_type, std::move(content_receiver), progress);
15191 const std::string &content_type,
15193 return cli_->Post(path, std::move(content_provider), content_type, progress);
15197 const std::string &content_type,
15200 return cli_->Post(path, std::move(content_provider), content_type,
15201 std::move(content_receiver), progress);
15204 size_t content_length,
15206 const std::string &content_type,
15208 return cli_->Post(path, headers, content_length, std::move(content_provider),
15209 content_type, progress);
15212 size_t content_length,
15214 const std::string &content_type,
15217 return cli_->Post(path, headers, content_length, std::move(content_provider),
15218 content_type, std::move(content_receiver), progress);
15222 const std::string &content_type,
15224 return cli_->Post(path, headers, std::move(content_provider), content_type,
15229 const std::string &content_type,
15232 return cli_->Post(path, headers, std::move(content_provider), content_type,
15233 std::move(content_receiver), progress);
15236 return cli_->Post(path, params);
15240 return cli_->Post(path, headers, params);
15245 return cli_->Post(path, items, progress);
15250 return cli_->Post(path, headers, items, progress);
15254 const std::string &boundary,
15256 return cli_->Post(path, headers, items, boundary, progress);
15262 return cli_->Post(path, headers, items, provider_items, progress);
15265 const std::string &body,
15266 const std::string &content_type,
15269 return cli_->Post(path, headers, body, content_type,
15270 std::move(content_receiver), progress);
15275 return cli_->Put(path, headers);
15278 size_t content_length,
15279 const std::string &content_type,
15281 return cli_->Put(path, body, content_length, content_type, progress);
15284 const char *body,
size_t content_length,
15285 const std::string &content_type,
15287 return cli_->Put(path, headers, body, content_length, content_type, progress);
15290 const std::string &content_type,
15292 return cli_->Put(path, body, content_type, progress);
15295 const std::string &body,
15296 const std::string &content_type,
15298 return cli_->Put(path, headers, body, content_type, progress);
15302 const std::string &content_type,
15304 return cli_->Put(path, content_length, std::move(content_provider),
15305 content_type, progress);
15309 const std::string &content_type,
15312 return cli_->Put(path, content_length, std::move(content_provider),
15313 content_type, std::move(content_receiver), progress);
15317 const std::string &content_type,
15319 return cli_->Put(path, std::move(content_provider), content_type, progress);
15323 const std::string &content_type,
15326 return cli_->Put(path, std::move(content_provider), content_type,
15327 std::move(content_receiver), progress);
15330 size_t content_length,
15332 const std::string &content_type,
15334 return cli_->Put(path, headers, content_length, std::move(content_provider),
15335 content_type, progress);
15338 size_t content_length,
15340 const std::string &content_type,
15343 return cli_->Put(path, headers, content_length, std::move(content_provider),
15344 content_type, std::move(content_receiver), progress);
15348 const std::string &content_type,
15350 return cli_->Put(path, headers, std::move(content_provider), content_type,
15355 const std::string &content_type,
15358 return cli_->Put(path, headers, std::move(content_provider), content_type,
15359 std::move(content_receiver), progress);
15362 return cli_->Put(path, params);
15366 return cli_->Put(path, headers, params);
15371 return cli_->Put(path, items, progress);
15376 return cli_->Put(path, headers, items, progress);
15380 const std::string &boundary,
15382 return cli_->Put(path, headers, items, boundary, progress);
15388 return cli_->Put(path, headers, items, provider_items, progress);
15391 const std::string &body,
15392 const std::string &content_type,
15395 return cli_->Put(path, headers, body, content_type, content_receiver,
15400 return cli_->Patch(path);
15403 return cli_->Patch(path, headers);
15406 size_t content_length,
15407 const std::string &content_type,
15409 return cli_->Patch(path, body, content_length, content_type, progress);
15412 const char *body,
size_t content_length,
15413 const std::string &content_type,
15415 return cli_->Patch(path, headers, body, content_length, content_type,
15419 const std::string &content_type,
15421 return cli_->Patch(path, body, content_type, progress);
15424 const std::string &body,
15425 const std::string &content_type,
15427 return cli_->Patch(path, headers, body, content_type, progress);
15431 const std::string &content_type,
15433 return cli_->Patch(path, content_length, std::move(content_provider),
15434 content_type, progress);
15438 const std::string &content_type,
15441 return cli_->Patch(path, content_length, std::move(content_provider),
15442 content_type, std::move(content_receiver), progress);
15446 const std::string &content_type,
15448 return cli_->Patch(path, std::move(content_provider), content_type, progress);
15452 const std::string &content_type,
15455 return cli_->Patch(path, std::move(content_provider), content_type,
15456 std::move(content_receiver), progress);
15459 size_t content_length,
15461 const std::string &content_type,
15463 return cli_->Patch(path, headers, content_length, std::move(content_provider),
15464 content_type, progress);
15467 size_t content_length,
15469 const std::string &content_type,
15472 return cli_->Patch(path, headers, content_length, std::move(content_provider),
15473 content_type, std::move(content_receiver), progress);
15477 const std::string &content_type,
15479 return cli_->Patch(path, headers, std::move(content_provider), content_type,
15484 const std::string &content_type,
15487 return cli_->Patch(path, headers, std::move(content_provider), content_type,
15488 std::move(content_receiver), progress);
15491 return cli_->Patch(path, params);
15495 return cli_->Patch(path, headers, params);
15500 return cli_->Patch(path, items, progress);
15505 return cli_->Patch(path, headers, items, progress);
15509 const std::string &boundary,
15511 return cli_->Patch(path, headers, items, boundary, progress);
15517 return cli_->Patch(path, headers, items, provider_items, progress);
15520 const std::string &body,
15521 const std::string &content_type,
15524 return cli_->Patch(path, headers, body, content_type, content_receiver,
15530 return cli_->Delete(path, progress);
15534 return cli_->Delete(path, headers, progress);
15537 size_t content_length,
15538 const std::string &content_type,
15540 return cli_->Delete(path, body, content_length, content_type, progress);
15543 const char *body,
size_t content_length,
15544 const std::string &content_type,
15546 return cli_->Delete(path, headers, body, content_length, content_type,
15550 const std::string &content_type,
15552 return cli_->Delete(path, body, content_type, progress);
15555 const std::string &body,
15556 const std::string &content_type,
15558 return cli_->Delete(path, headers, body, content_type, progress);
15562 return cli_->Delete(path, params, progress);
15566 return cli_->Delete(path, headers, params, progress);
15570 return cli_->Options(path);
15573 return cli_->Options(path, headers);
15579 const std::string &body,
const std::string &content_type) {
15580 return cli_->open_stream(method, path, params, headers, body, content_type);
15584 return cli_->send(req, res,
error);
15601 cli_->set_hostname_addr_map(std::move(addr_map));
15605 cli_->set_default_headers(std::move(headers));
15609 std::function<ssize_t(
Stream &,
Headers &)>
const &writer) {
15610 cli_->set_header_writer(writer);
15614 cli_->set_address_family(family);
15620 cli_->set_socket_options(std::move(socket_options));
15624 cli_->set_connection_timeout(sec, usec);
15628 cli_->set_read_timeout(sec, usec);
15632 cli_->set_write_timeout(sec, usec);
15636 const std::string &password) {
15637 cli_->set_basic_auth(username, password);
15640 cli_->set_bearer_token_auth(token);
15645 cli_->set_follow_location(on);
15655 cli_->set_payload_max_length(length);
15659 cli_->set_interface(
intf);
15666 const std::string &password) {
15667 cli_->set_proxy_basic_auth(username, password);
15670 cli_->set_proxy_bearer_token_auth(token);
15673 cli_->set_no_proxy(patterns);
15677 cli_->set_logger(std::move(logger));
15681 cli_->set_error_logger(std::move(error_logger));
15688#ifdef CPPHTTPLIB_SSL_ENABLED
15691inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
15692 const char *client_ca_cert_file_path,
15693 const char *client_ca_cert_dir_path,
15694 const char *private_key_password) {
15695 using namespace tls;
15697 ctx_ = create_server_context();
15698 if (!ctx_) {
return; }
15701 if (!set_server_cert_file(ctx_, cert_path, private_key_path,
15702 private_key_password)) {
15703 last_ssl_error_ =
static_cast<int>(get_error());
15704 free_context(ctx_);
15710 if (client_ca_cert_file_path || client_ca_cert_dir_path) {
15711 if (!set_client_ca_file(ctx_, client_ca_cert_file_path,
15712 client_ca_cert_dir_path)) {
15713 last_ssl_error_ =
static_cast<int>(get_error());
15714 free_context(ctx_);
15719 set_verify_client(ctx_,
true);
15723inline SSLServer::SSLServer(
const PemMemory &pem) {
15724 using namespace tls;
15725 ctx_ = create_server_context();
15727 if (!set_server_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
15728 pem.private_key_password)) {
15729 last_ssl_error_ =
static_cast<int>(get_error());
15730 free_context(ctx_);
15732 }
else if (pem.client_ca_pem && pem.client_ca_pem_len > 0) {
15733 if (!load_ca_pem(ctx_, pem.client_ca_pem, pem.client_ca_pem_len)) {
15734 last_ssl_error_ =
static_cast<int>(get_error());
15735 free_context(ctx_);
15738 set_verify_client(ctx_,
true);
15744inline SSLServer::SSLServer(
const tls::ContextSetupCallback &setup_callback) {
15745 using namespace tls;
15746 ctx_ = create_server_context();
15748 if (!setup_callback(ctx_)) {
15749 free_context(ctx_);
15755inline SSLServer::~SSLServer() {
15756 if (ctx_) { tls::free_context(ctx_); }
15759inline bool SSLServer::is_valid()
const {
return ctx_ !=
nullptr; }
15761inline bool SSLServer::process_and_close_socket(
socket_t sock) {
15762 using namespace tls;
15765 session_t session =
nullptr;
15767 std::lock_guard<std::mutex> guard(ctx_mutex_);
15768 session = create_session(
static_cast<ctx_t
>(ctx_), sock);
15772 last_ssl_error_ =
static_cast<int>(get_error());
15779 bool handshake_done =
false;
15781 bool websocket_upgraded =
false;
15783 if (handshake_done) { shutdown(session, !websocket_upgraded &&
ret); }
15784 free_session(session);
15791 if (!accept_nonblocking(session, sock, read_timeout_sec_, read_timeout_usec_,
15793#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15795 if (tls_err.code == ErrorCode::WantRead) {
15796 last_ssl_error_ = SSL_ERROR_WANT_READ;
15797 }
else if (tls_err.code == ErrorCode::WantWrite) {
15798 last_ssl_error_ = SSL_ERROR_WANT_WRITE;
15800 last_ssl_error_ = SSL_ERROR_SSL;
15803 last_ssl_error_ =
static_cast<int>(get_error());
15808 handshake_done =
true;
15810 std::string remote_addr;
15811 int remote_port = 0;
15814 std::string local_addr;
15815 int local_port = 0;
15818 ret = detail::process_server_socket_ssl(
15819 svr_sock_, session, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
15820 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
15821 write_timeout_usec_,
15822 [&](
Stream &strm,
bool close_connection,
bool &connection_closed) {
15823 return process_request(
15824 strm, remote_addr, remote_port, local_addr, local_port,
15825 close_connection, connection_closed,
15826 [&](
Request &req) { req.ssl = session; }, &websocket_upgraded);
15832inline bool SSLServer::update_certs_pem(
const char *cert_pem,
15833 const char *key_pem,
15834 const char *client_ca_pem,
15835 const char *password) {
15836 if (!ctx_) {
return false; }
15837 std::lock_guard<std::mutex> guard(ctx_mutex_);
15838 if (!tls::update_server_cert(ctx_, cert_pem, key_pem, password)) {
15841 if (client_ca_pem) {
15842 return tls::update_server_client_ca(ctx_, client_ca_pem);
15848inline SSLClient::~SSLClient() {
15849 if (ctx_) { tls::free_context(ctx_); }
15853 shutdown_ssl_impl(socket_,
true);
15856inline bool SSLClient::is_valid()
const {
return ctx_ !=
nullptr; }
15858inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
15859 shutdown_ssl_impl(socket, shutdown_gracefully);
15862inline void SSLClient::shutdown_ssl_impl(Socket &socket,
15863 bool shutdown_gracefully) {
15865 assert(socket.ssl ==
nullptr);
15869 tls::shutdown(socket.ssl, shutdown_gracefully);
15871 std::lock_guard<std::mutex> guard(ctx_mutex_);
15872 tls::free_session(socket.ssl);
15874 socket.ssl =
nullptr;
15876 assert(socket.ssl ==
nullptr);
15879inline bool SSLClient::process_socket(
15880 const Socket &socket,
15881 std::chrono::time_point<std::chrono::steady_clock> start_time,
15882 std::function<
bool(
Stream &strm)> callback) {
15883 assert(socket.ssl);
15884 return detail::process_client_socket_ssl(
15885 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
15886 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
15887 std::move(callback));
15890inline bool SSLClient::is_ssl()
const {
return true; }
15892inline bool SSLClient::create_and_connect_socket(Socket &socket,
Error &
error) {
15900inline bool SSLClient::setup_proxy_connection(
15902 std::chrono::time_point<std::chrono::steady_clock> start_time,
15904 if (!is_proxy_enabled_for_host(host_)) {
return true; }
15906 if (!connect_with_proxy(socket, start_time, res, success,
error)) {
15910 if (!initialize_ssl(socket,
error)) {
15920inline bool SSLClient::connect_with_proxy(
15922 std::chrono::time_point<std::chrono::steady_clock> start_time,
15927 socket.sock, read_timeout_sec_, read_timeout_usec_,
15928 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
15929 start_time, [&](
Stream &strm) {
15931 req2.method =
"CONNECT";
15933 detail::make_host_and_port_string_always_port(host_, port_);
15934 if (max_timeout_msec_ > 0) {
15935 req2.start_time_ = std::chrono::steady_clock::now();
15937 return process_request(strm, req2, proxy_res,
false,
error);
15941 shutdown_ssl(socket,
true);
15949 if (!proxy_digest_auth_username_.empty() &&
15950 !proxy_digest_auth_password_.empty()) {
15951 std::map<std::string, std::string> auth;
15955 shutdown_ssl(socket,
true);
15960 if (!ensure_socket_connection(socket,
error)) {
15962 output_error_log(
error,
nullptr);
15968 socket.sock, read_timeout_sec_, read_timeout_usec_,
15969 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
15970 start_time, [&](
Stream &strm) {
15972 req3.method =
"CONNECT";
15973 req3.path = detail::make_host_and_port_string_always_port(
15975 req3.headers.insert(detail::make_digest_authentication_header(
15976 req3, auth, 1, detail::random_string(10),
15977 proxy_digest_auth_username_, proxy_digest_auth_password_,
15979 if (max_timeout_msec_ > 0) {
15980 req3.start_time_ = std::chrono::steady_clock::now();
15982 return process_request(strm, req3, proxy_res,
false,
error);
15986 shutdown_ssl(socket,
true);
15999 if (proxy_res.status != StatusCode::OK_200) {
16000 error = Error::ProxyConnection;
16001 output_error_log(
error,
nullptr);
16002 res = std::move(proxy_res);
16005 shutdown_ssl(socket,
true);
16014inline bool SSLClient::ensure_socket_connection(Socket &socket, Error &
error) {
16015 if (!ClientImpl::ensure_socket_connection(socket,
error)) {
return false; }
16017 if (is_proxy_enabled_for_host(host_)) {
return true; }
16019 if (!initialize_ssl(socket,
error)) {
16029inline SSLClient::SSLClient(
const std::string &host)
16032inline SSLClient::SSLClient(
const std::string &host,
int port)
16033 : SSLClient(host, port, std::
string(), std::
string()) {}
16035inline void SSLClient::init_ctx() {
16036 ctx_ = tls::create_client_context();
16037 if (ctx_) { tls::set_min_version(ctx_, tls::Version::TLS1_2); }
16040inline void SSLClient::reset_ctx_on_error() {
16041 last_backend_error_ = tls::get_error();
16042 tls::free_context(ctx_);
16046inline SSLClient::SSLClient(
const std::string &host,
int port,
16047 const std::string &client_cert_path,
16048 const std::string &client_key_path,
16049 const std::string &private_key_password)
16050 :
ClientImpl(host, port, client_cert_path, client_key_path) {
16052 if (!ctx_) {
return; }
16054 if (!client_cert_path.empty() && !client_key_path.empty()) {
16055 const char *password =
16056 private_key_password.empty() ? nullptr : private_key_password.c_str();
16057 if (!tls::set_client_cert_file(ctx_, client_cert_path.c_str(),
16058 client_key_path.c_str(), password)) {
16059 reset_ctx_on_error();
16064inline SSLClient::SSLClient(
const std::string &host,
int port,
16065 const PemMemory &pem)
16068 if (!ctx_) {
return; }
16070 if (pem.cert_pem && pem.key_pem) {
16071 if (!tls::set_client_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
16072 pem.private_key_password)) {
16073 reset_ctx_on_error();
16078inline void SSLClient::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
16079 if (ca_cert_store && ctx_) {
16081 tls::set_ca_store(ctx_, ca_cert_store);
16082 }
else if (ca_cert_store) {
16083 tls::free_ca_store(ca_cert_store);
16088SSLClient::set_server_certificate_verifier(tls::VerifyCallback verifier) {
16089 if (!ctx_) {
return; }
16090 tls::set_verify_callback(ctx_, verifier);
16093inline void SSLClient::set_session_verifier(
16095 session_verifier_ = std::move(verifier);
16098#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16099inline void SSLClient::enable_windows_certificate_verification(
bool enabled) {
16100 enable_windows_cert_verification_ = enabled;
16104inline void SSLClient::load_ca_cert_store(
const char *ca_cert,
16105 std::size_t
size) {
16106 if (ctx_ && ca_cert &&
size > 0) {
16107 ca_cert_pem_.assign(ca_cert,
size);
16108 tls::load_ca_pem(ctx_, ca_cert,
size);
16112inline bool SSLClient::load_certs() {
16115 std::call_once(initialize_cert_, [&]() {
16116 std::lock_guard<std::mutex> guard(ctx_mutex_);
16118 if (!ca_cert_file_path_.empty()) {
16119 if (!tls::load_ca_file(ctx_, ca_cert_file_path_.c_str())) {
16120 last_backend_error_ = tls::get_error();
16123 }
else if (!ca_cert_dir_path_.empty()) {
16124 if (!tls::load_ca_dir(ctx_, ca_cert_dir_path_.c_str())) {
16125 last_backend_error_ = tls::get_error();
16128 }
else if (ca_cert_pem_.empty()) {
16129 if (!tls::load_system_certs(ctx_)) {
16130 last_backend_error_ = tls::get_error();
16138inline bool SSLClient::initialize_ssl(Socket &socket,
Error &
error) {
16139 using namespace tls;
16142 if (server_certificate_verification_) {
16143 if (!load_certs()) {
16145 output_error_log(
error,
nullptr);
16150 bool is_ip = detail::is_ip_address(host_);
16152#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
16157 if (is_ip && server_certificate_verification_) {
16158 set_verify_client(ctx_,
false);
16160 set_verify_client(ctx_, server_certificate_verification_);
16165 session_t session =
nullptr;
16167 std::lock_guard<std::mutex> guard(ctx_mutex_);
16168 session = create_session(ctx_, socket.sock);
16173 last_backend_error_ = get_error();
16178 bool success =
false;
16180 if (!success) { free_session(session); }
16187 if (!set_sni(session, host_.c_str())) {
16189 last_backend_error_ = get_error();
16196 if (!connect_nonblocking(session, socket.sock, connection_timeout_sec_,
16197 connection_timeout_usec_, &tls_err)) {
16198 last_ssl_error_ =
static_cast<int>(tls_err.code);
16199 last_backend_error_ = tls_err.backend_code;
16200 if (tls_err.code == ErrorCode::CertVerifyFailed) {
16202 }
else if (tls_err.code == ErrorCode::HostnameMismatch) {
16207 output_error_log(
error,
nullptr);
16213 if (session_verifier_) { verification_status = session_verifier_(session); }
16216 last_backend_error_ = get_error();
16218 output_error_log(
error,
nullptr);
16224 server_certificate_verification_) {
16225 verify_result_ = tls::get_verify_result(session);
16226 if (verify_result_ != 0) {
16227 last_backend_error_ =
static_cast<uint64_t
>(verify_result_);
16229 output_error_log(
error,
nullptr);
16233 auto server_cert = get_peer_cert(session);
16234 if (!server_cert) {
16235 last_backend_error_ = get_error();
16237 output_error_log(
error,
nullptr);
16247 if (server_hostname_verification_) {
16248 if (!verify_hostname(server_cert, host_.c_str())) {
16249 last_backend_error_ = hostname_mismatch_code();
16251 output_error_log(
error,
nullptr);
16256#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16262 if (enable_windows_cert_verification_ && ca_cert_file_path_.empty() &&
16263 ca_cert_dir_path_.empty() && ca_cert_pem_.empty()) {
16264 std::vector<unsigned char> der;
16265 if (get_cert_der(server_cert, der)) {
16266 uint64_t wincrypt_error = 0;
16267 if (!detail::verify_cert_with_windows_schannel(
16268 der, host_, server_hostname_verification_, wincrypt_error)) {
16269 last_backend_error_ = wincrypt_error;
16271 output_error_log(
error,
nullptr);
16280 socket.ssl = session;
16284inline void Client::set_digest_auth(
const std::string &username,
16285 const std::string &password) {
16286 cli_->set_digest_auth(username, password);
16289inline void Client::set_proxy_digest_auth(
const std::string &username,
16290 const std::string &password) {
16291 cli_->set_proxy_digest_auth(username, password);
16294inline void Client::enable_server_certificate_verification(
bool enabled) {
16295 cli_->enable_server_certificate_verification(enabled);
16298inline void Client::enable_server_hostname_verification(
bool enabled) {
16299 cli_->enable_server_hostname_verification(enabled);
16302#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16303inline void Client::enable_windows_certificate_verification(
bool enabled) {
16305 static_cast<SSLClient &
>(*cli_).enable_windows_certificate_verification(
16311inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
16312 const std::string &ca_cert_dir_path) {
16313 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
16316inline void Client::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
16318 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
16319 }
else if (ca_cert_store) {
16320 tls::free_ca_store(ca_cert_store);
16324inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t
size) {
16325 set_ca_cert_store(tls::create_ca_store(ca_cert,
size));
16329Client::set_server_certificate_verifier(tls::VerifyCallback verifier) {
16331 static_cast<SSLClient &
>(*cli_).set_server_certificate_verifier(
16332 std::move(verifier));
16336inline void Client::set_session_verifier(
16339 static_cast<SSLClient &
>(*cli_).set_session_verifier(std::move(verifier));
16343inline tls::ctx_t Client::tls_context()
const {
16344 if (is_ssl_) {
return static_cast<SSLClient &
>(*cli_).tls_context(); }
16354#ifdef CPPHTTPLIB_SSL_ENABLED
16359inline PeerCert get_peer_cert_from_session(const_session_t session) {
16360 return PeerCert(get_peer_cert(session));
16365inline VerifyCallback &get_verify_callback() {
16366 static thread_local VerifyCallback callback;
16370inline VerifyCallback &get_mbedtls_verify_callback() {
16371 static thread_local VerifyCallback callback;
16376inline bool is_ipv4_address(
const std::string &
str) {
16378 for (
char c :
str) {
16381 }
else if (!isdigit(
static_cast<unsigned char>(c))) {
16389inline bool parse_ipv4(
const std::string &
str,
unsigned char *out) {
16390 const char *p =
str.c_str();
16391 for (
int i = 0; i < 4; i++) {
16393 if (*p !=
'.') {
return false; }
16398 while (*p >=
'0' && *p <=
'9') {
16399 val = val * 10 + (*p -
'0');
16400 if (val > 255) {
return false; }
16404 if (digits == 0) {
return false; }
16406 if (digits > 1 && *(p - digits) ==
'0') {
return false; }
16407 out[i] =
static_cast<unsigned char>(val);
16414template <
typename Callback>
16415inline bool enumerate_windows_system_certs(Callback cb) {
16416 bool loaded =
false;
16417 static const wchar_t *store_names[] = {L
"ROOT", L
"CA"};
16418 for (
auto store_name : store_names) {
16419 HCERTSTORE hStore = CertOpenSystemStoreW(0, store_name);
16421 PCCERT_CONTEXT pContext =
nullptr;
16422 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
16424 if (cb(pContext->pbCertEncoded, pContext->cbCertEncoded)) {
16428 CertCloseStore(hStore, 0);
16435#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
16437template <
typename Callback>
16438inline bool enumerate_macos_keychain_certs(Callback cb) {
16439 bool loaded =
false;
16440 const SecTrustSettingsDomain domains[] = {
16441 kSecTrustSettingsDomainSystem,
16442 kSecTrustSettingsDomainAdmin,
16443 kSecTrustSettingsDomainUser,
16445 for (
auto domain : domains) {
16446 CFArrayRef certs =
nullptr;
16447 OSStatus status = SecTrustSettingsCopyCertificates(domain, &certs);
16448 if (status != errSecSuccess || !certs) {
16449 if (certs) CFRelease(certs);
16452 CFIndex
count = CFArrayGetCount(certs);
16453 for (CFIndex i = 0; i <
count; i++) {
16454 SecCertificateRef cert =
16455 (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
16456 CFDataRef
data = SecCertificateCopyData(cert);
16458 if (cb(CFDataGetBytePtr(
data),
16459 static_cast<size_t>(CFDataGetLength(
data)))) {
16471#if !defined(_WIN32) && !(defined(__APPLE__) && \
16472 defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN))
16474inline const char **system_ca_paths() {
16475 static const char *paths[] = {
16476 "/etc/ssl/certs/ca-certificates.crt",
16477 "/etc/pki/tls/certs/ca-bundle.crt",
16478 "/etc/ssl/ca-bundle.pem",
16479 "/etc/pki/tls/cacert.pem",
16480 "/etc/ssl/cert.pem",
16486inline const char **system_ca_dirs() {
16487 static const char *dirs[] = {
"/etc/ssl/certs",
16488 "/etc/pki/tls/certs",
16489 "/usr/share/ca-certificates",
16497inline bool set_client_ca_file(ctx_t ctx,
const char *ca_file,
16498 const char *ca_dir) {
16499 if (!ctx) {
return false; }
16501 bool success =
true;
16502 if (ca_file && *ca_file) {
16503 if (!load_ca_file(ctx, ca_file)) { success =
false; }
16505 if (ca_dir && *ca_dir) {
16506 if (!load_ca_dir(ctx, ca_dir)) { success =
false; }
16509#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16511 if (ca_file && *ca_file) {
16512 auto list = SSL_load_client_CA_file(ca_file);
16513 if (list) { SSL_CTX_set_client_CA_list(
static_cast<SSL_CTX *
>(ctx), list); }
16520inline bool set_server_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
16521 const char *password) {
16522 return set_client_cert_pem(ctx, cert, key, password);
16525inline bool set_server_cert_file(ctx_t ctx,
const char *cert_path,
16526 const char *key_path,
const char *password) {
16527 return set_client_cert_file(ctx, cert_path, key_path, password);
16531inline PeerCert::PeerCert() =
default;
16533inline PeerCert::PeerCert(cert_t cert) : cert_(cert) {}
16535inline PeerCert::PeerCert(PeerCert &&other)
noexcept : cert_(other.cert_) {
16536 other.cert_ =
nullptr;
16539inline PeerCert &PeerCert::operator=(PeerCert &&other)
noexcept {
16540 if (
this != &other) {
16541 if (cert_) { free_cert(cert_); }
16542 cert_ = other.cert_;
16543 other.cert_ =
nullptr;
16548inline PeerCert::~PeerCert() {
16549 if (cert_) { free_cert(cert_); }
16552inline PeerCert::operator bool()
const {
return cert_ !=
nullptr; }
16554inline std::string PeerCert::subject_cn()
const {
16555 return cert_ ? get_cert_subject_cn(cert_) : std::
string();
16558inline std::string PeerCert::issuer_name()
const {
16559 return cert_ ? get_cert_issuer_name(cert_) : std::
string();
16562inline bool PeerCert::check_hostname(
const char *hostname)
const {
16563 return cert_ ? verify_hostname(cert_, hostname) : false;
16566inline std::vector<SanEntry> PeerCert::sans()
const {
16567 std::vector<SanEntry> result;
16568 if (cert_) { get_cert_sans(cert_, result); }
16572inline bool PeerCert::validity(time_t ¬_before, time_t ¬_after)
const {
16573 return cert_ ? get_cert_validity(cert_, not_before, not_after) : false;
16576inline std::string PeerCert::serial()
const {
16577 return cert_ ? get_cert_serial(cert_) : std::
string();
16581inline std::string VerifyContext::subject_cn()
const {
16582 return cert ? get_cert_subject_cn(cert) : std::
string();
16585inline std::string VerifyContext::issuer_name()
const {
16586 return cert ? get_cert_issuer_name(cert) : std::
string();
16589inline bool VerifyContext::check_hostname(
const char *hostname)
const {
16590 return cert ? verify_hostname(cert, hostname) : false;
16593inline std::vector<SanEntry> VerifyContext::sans()
const {
16594 std::vector<SanEntry> result;
16595 if (cert) { get_cert_sans(cert, result); }
16599inline bool VerifyContext::validity(time_t ¬_before,
16600 time_t ¬_after)
const {
16601 return cert ? get_cert_validity(cert, not_before, not_after) : false;
16604inline std::string VerifyContext::serial()
const {
16605 return cert ? get_cert_serial(cert) : std::
string();
16609inline std::string TlsError::verify_error_to_string(
long error_code) {
16610 return verify_error_string(error_code);
16616inline tls::PeerCert Request::peer_cert()
const {
16617 return tls::get_peer_cert_from_session(ssl);
16621inline std::string Request::sni()
const {
16622 if (!ssl) {
return std::string(); }
16623 const char *s = tls::get_sni(ssl);
16624 return s ? std::string(s) :
std::
string();
16637#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16643inline ErrorCode map_ssl_error(
int ssl_error,
int &out_errno) {
16644 switch (ssl_error) {
16645 case SSL_ERROR_NONE:
return ErrorCode::Success;
16646 case SSL_ERROR_WANT_READ:
return ErrorCode::WantRead;
16647 case SSL_ERROR_WANT_WRITE:
return ErrorCode::WantWrite;
16648 case SSL_ERROR_ZERO_RETURN:
return ErrorCode::PeerClosed;
16649 case SSL_ERROR_SYSCALL: out_errno = errno;
return ErrorCode::SyscallError;
16650 case SSL_ERROR_SSL:
16651 default:
return ErrorCode::Fatal;
16658inline STACK_OF(X509_NAME) *
16659 create_client_ca_list_from_pem(
const char *ca_pem) {
16660 if (!ca_pem) {
return nullptr; }
16662 auto ca_list = sk_X509_NAME_new_null();
16663 if (!ca_list) {
return nullptr; }
16665 BIO *bio = BIO_new_mem_buf(ca_pem, -1);
16667 sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
16671 X509 *cert =
nullptr;
16672 while ((cert = PEM_read_bio_X509(bio,
nullptr,
nullptr,
nullptr)) !=
16674 const X509_NAME *
name = X509_get_subject_name(cert);
16676 sk_X509_NAME_push(ca_list, X509_NAME_dup(
const_cast<X509_NAME *
>(
name)));
16686inline int openssl_verify_callback(
int preverify_ok, X509_STORE_CTX *ctx) {
16687 auto &callback = get_verify_callback();
16688 if (!callback) {
return preverify_ok; }
16691 auto ssl =
static_cast<SSL *
>(
16692 X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
16693 if (!ssl) {
return preverify_ok; }
16696 auto cert = X509_STORE_CTX_get_current_cert(ctx);
16697 int depth = X509_STORE_CTX_get_error_depth(ctx);
16698 int error = X509_STORE_CTX_get_error(ctx);
16701 VerifyContext verify_ctx;
16702 verify_ctx.session =
static_cast<session_t
>(ssl);
16703 verify_ctx.cert =
static_cast<cert_t
>(cert);
16704 verify_ctx.depth = depth;
16705 verify_ctx.preverify_ok = (preverify_ok != 0);
16706 verify_ctx.error_code =
error;
16707 verify_ctx.error_string =
16708 (
error != X509_V_OK) ? X509_verify_cert_error_string(
error) :
nullptr;
16710 return callback(verify_ctx) ? 1 : 0;
16715inline ctx_t create_client_context() {
16716 SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
16719 SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
16721 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
16723 return static_cast<ctx_t
>(ctx);
16726inline void free_context(ctx_t ctx) {
16727 if (ctx) { SSL_CTX_free(
static_cast<SSL_CTX *
>(ctx)); }
16730inline bool set_min_version(ctx_t ctx, Version version) {
16731 if (!ctx)
return false;
16732 return SSL_CTX_set_min_proto_version(
static_cast<SSL_CTX *
>(ctx),
16733 static_cast<int>(version)) == 1;
16736inline bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len) {
16737 if (!ctx || !pem || len == 0)
return false;
16739 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16740 auto store = SSL_CTX_get_cert_store(ssl_ctx);
16741 if (!
store)
return false;
16743 auto bio = BIO_new_mem_buf(pem,
static_cast<int>(len));
16744 if (!bio)
return false;
16747 X509 *cert =
nullptr;
16748 while ((cert = PEM_read_bio_X509(bio,
nullptr,
nullptr,
nullptr)) !=
16750 if (X509_STORE_add_cert(
store, cert) != 1) {
16752 auto err = ERR_peek_last_error();
16753 if (ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
16767inline bool load_ca_file(ctx_t ctx,
const char *file_path) {
16768 if (!ctx || !file_path)
return false;
16769 return SSL_CTX_load_verify_locations(
static_cast<SSL_CTX *
>(ctx), file_path,
16773inline bool load_ca_dir(ctx_t ctx,
const char *dir_path) {
16774 if (!ctx || !dir_path)
return false;
16775 return SSL_CTX_load_verify_locations(
static_cast<SSL_CTX *
>(ctx),
nullptr,
16779inline bool load_system_certs(ctx_t ctx) {
16780 if (!ctx)
return false;
16781 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16785 auto store = SSL_CTX_get_cert_store(ssl_ctx);
16786 if (!
store)
return false;
16788 bool loaded_any =
false;
16789 static const wchar_t *store_names[] = {L
"ROOT", L
"CA"};
16790 for (
auto store_name : store_names) {
16791 auto hStore = CertOpenSystemStoreW(NULL, store_name);
16792 if (!hStore)
continue;
16794 PCCERT_CONTEXT pContext =
nullptr;
16795 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
16797 const unsigned char *
data = pContext->pbCertEncoded;
16798 auto x509 = d2i_X509(
nullptr, &
data, pContext->cbCertEncoded);
16800 if (X509_STORE_add_cert(
store, x509) == 1) { loaded_any =
true; }
16804 CertCloseStore(hStore, 0);
16808#elif defined(__APPLE__)
16809#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
16811 auto store = SSL_CTX_get_cert_store(ssl_ctx);
16812 if (!
store)
return false;
16814 bool loaded_any =
false;
16815 const SecTrustSettingsDomain domains[] = {
16816 kSecTrustSettingsDomainSystem,
16817 kSecTrustSettingsDomainAdmin,
16818 kSecTrustSettingsDomainUser,
16820 for (
auto domain : domains) {
16821 CFArrayRef certs =
nullptr;
16822 if (SecTrustSettingsCopyCertificates(domain, &certs) != errSecSuccess ||
16824 if (certs) CFRelease(certs);
16827 auto count = CFArrayGetCount(certs);
16828 for (CFIndex i = 0; i <
count; i++) {
16829 auto cert =
reinterpret_cast<SecCertificateRef
>(
16830 const_cast<void *
>(CFArrayGetValueAtIndex(certs, i)));
16831 CFDataRef der = SecCertificateCopyData(cert);
16833 const unsigned char *
data = CFDataGetBytePtr(der);
16834 auto x509 = d2i_X509(
nullptr, &
data, CFDataGetLength(der));
16836 if (X509_STORE_add_cert(
store, x509) == 1) { loaded_any =
true; }
16844 return loaded_any || SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16846 return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16851 return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16855inline bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
16856 const char *password) {
16857 if (!ctx || !cert || !key)
return false;
16859 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16862 auto cert_bio = BIO_new_mem_buf(cert, -1);
16863 if (!cert_bio)
return false;
16865 auto x509 = PEM_read_bio_X509(cert_bio,
nullptr,
nullptr,
nullptr);
16866 BIO_free(cert_bio);
16867 if (!x509)
return false;
16869 auto cert_ok = SSL_CTX_use_certificate(ssl_ctx, x509) == 1;
16871 if (!cert_ok)
return false;
16874 auto key_bio = BIO_new_mem_buf(key, -1);
16875 if (!key_bio)
return false;
16877 auto pkey = PEM_read_bio_PrivateKey(key_bio,
nullptr,
nullptr,
16878 password ?
const_cast<char *
>(password)
16881 if (!pkey)
return false;
16883 auto key_ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey) == 1;
16884 EVP_PKEY_free(pkey);
16886 return key_ok && SSL_CTX_check_private_key(ssl_ctx) == 1;
16889inline bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
16890 const char *key_path,
const char *password) {
16891 if (!ctx || !cert_path || !key_path)
return false;
16893 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16895 if (password && password[0] !=
'\0') {
16896 SSL_CTX_set_default_passwd_cb_userdata(
16897 ssl_ctx,
reinterpret_cast<void *
>(
const_cast<char *
>(password)));
16900 return SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_path) == 1 &&
16901 SSL_CTX_use_PrivateKey_file(ssl_ctx, key_path, SSL_FILETYPE_PEM) == 1;
16904inline ctx_t create_server_context() {
16905 SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
16907 SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION |
16908 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
16909 SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
16911 return static_cast<ctx_t
>(ctx);
16914inline void set_verify_client(ctx_t ctx,
bool require) {
16916 SSL_CTX_set_verify(
static_cast<SSL_CTX *
>(ctx),
16918 ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
16923inline session_t create_session(ctx_t ctx,
socket_t sock) {
16926 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
16927 SSL *ssl = SSL_new(ssl_ctx);
16928 if (!ssl)
return nullptr;
16931 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
16933 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
16939 SSL_set_bio(ssl, bio, bio);
16940 return static_cast<session_t
>(ssl);
16943inline void free_session(session_t session) {
16944 if (session) { SSL_free(
static_cast<SSL *
>(session)); }
16947inline bool set_sni(session_t session,
const char *hostname) {
16948 if (!session || !hostname)
return false;
16950 auto ssl =
static_cast<SSL *
>(session);
16953#if defined(OPENSSL_IS_BORINGSSL)
16954 return SSL_set_tlsext_host_name(ssl, hostname) == 1;
16957 return SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
16958 static_cast<void *
>(
const_cast<char *
>(hostname))) == 1;
16962inline bool set_hostname(session_t session,
const char *hostname) {
16963 if (!session || !hostname)
return false;
16965 auto ssl =
static_cast<SSL *
>(session);
16968 if (!set_sni(session, hostname)) {
return false; }
16971 auto param = SSL_get0_param(ssl);
16972 if (!param)
return false;
16974 X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
16975 if (X509_VERIFY_PARAM_set1_host(param, hostname, 0) != 1) {
return false; }
16977 SSL_set_verify(ssl, SSL_VERIFY_PEER,
nullptr);
16981inline TlsError connect(session_t session) {
16982 if (!session) {
return TlsError(); }
16984 auto ssl =
static_cast<SSL *
>(session);
16985 auto ret = SSL_connect(ssl);
16989 err.code = ErrorCode::Success;
16991 auto ssl_err = SSL_get_error(ssl,
ret);
16992 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
16993 err.backend_code = ERR_get_error();
16998inline TlsError accept(session_t session) {
16999 if (!session) {
return TlsError(); }
17001 auto ssl =
static_cast<SSL *
>(session);
17002 auto ret = SSL_accept(ssl);
17006 err.code = ErrorCode::Success;
17008 auto ssl_err = SSL_get_error(ssl,
ret);
17009 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17010 err.backend_code = ERR_get_error();
17015inline bool connect_nonblocking(session_t session,
socket_t sock,
17016 time_t timeout_sec, time_t timeout_usec,
17019 if (err) { err->code = ErrorCode::Fatal; }
17023 auto ssl =
static_cast<SSL *
>(session);
17024 auto bio = SSL_get_rbio(ssl);
17027 detail::set_nonblocking(sock,
true);
17028 if (bio) { BIO_set_nbio(bio, 1); }
17030 auto cleanup = detail::scope_exit([&]() {
17032 if (bio) { BIO_set_nbio(bio, 0); }
17033 detail::set_nonblocking(sock,
false);
17037 while ((res = SSL_connect(ssl)) != 1) {
17038 auto ssl_err = SSL_get_error(ssl, res);
17040 case SSL_ERROR_WANT_READ:
17041 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
17045 case SSL_ERROR_WANT_WRITE:
17046 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
17053 err->code = impl::map_ssl_error(ssl_err, err->sys_errno);
17054 err->backend_code = ERR_get_error();
17058 if (err) { err->code = ErrorCode::Success; }
17062inline bool accept_nonblocking(session_t session,
socket_t sock,
17063 time_t timeout_sec, time_t timeout_usec,
17066 if (err) { err->code = ErrorCode::Fatal; }
17070 auto ssl =
static_cast<SSL *
>(session);
17071 auto bio = SSL_get_rbio(ssl);
17074 detail::set_nonblocking(sock,
true);
17075 if (bio) { BIO_set_nbio(bio, 1); }
17077 auto cleanup = detail::scope_exit([&]() {
17079 if (bio) { BIO_set_nbio(bio, 0); }
17080 detail::set_nonblocking(sock,
false);
17084 while ((res = SSL_accept(ssl)) != 1) {
17085 auto ssl_err = SSL_get_error(ssl, res);
17087 case SSL_ERROR_WANT_READ:
17088 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
17092 case SSL_ERROR_WANT_WRITE:
17093 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
17100 err->code = impl::map_ssl_error(ssl_err, err->sys_errno);
17101 err->backend_code = ERR_get_error();
17105 if (err) { err->code = ErrorCode::Success; }
17109inline ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err) {
17110 if (!session || !buf) {
17111 err.code = ErrorCode::Fatal;
17115 auto ssl =
static_cast<SSL *
>(session);
17116 constexpr auto max_len =
17117 static_cast<size_t>((std::numeric_limits<int>::max)());
17118 if (len > max_len) { len = max_len; }
17119 auto ret = SSL_read(ssl, buf,
static_cast<int>(len));
17122 err.code = ErrorCode::Success;
17126 auto ssl_err = SSL_get_error(ssl,
ret);
17127 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17128 if (err.code == ErrorCode::PeerClosed) {
17131 if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
17135inline ssize_t write(session_t session,
const void *buf,
size_t len,
17137 if (!session || !buf) {
17138 err.code = ErrorCode::Fatal;
17142 auto ssl =
static_cast<SSL *
>(session);
17143 auto ret = SSL_write(ssl, buf,
static_cast<int>(len));
17146 err.code = ErrorCode::Success;
17150 auto ssl_err = SSL_get_error(ssl,
ret);
17151 err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17152 if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
17156inline int pending(const_session_t session) {
17157 if (!session)
return 0;
17158 return SSL_pending(
static_cast<SSL *
>(
const_cast<void *
>(session)));
17161inline void shutdown(session_t session,
bool graceful) {
17162 if (!session)
return;
17164 auto ssl =
static_cast<SSL *
>(session);
17167 if (SSL_shutdown(ssl) == 0) {
17174inline bool is_peer_closed(session_t session,
socket_t sock) {
17175 if (!session)
return true;
17178 detail::set_nonblocking(sock,
true);
17179 auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
17181 auto ssl =
static_cast<SSL *
>(session);
17183 auto ret = SSL_peek(ssl, &buf, 1);
17184 if (
ret > 0)
return false;
17186 auto err = SSL_get_error(ssl,
ret);
17187 return err == SSL_ERROR_ZERO_RETURN;
17190inline cert_t get_peer_cert(const_session_t session) {
17191 if (!session)
return nullptr;
17192 return static_cast<cert_t
>(SSL_get1_peer_certificate(
17193 static_cast<SSL *
>(
const_cast<void *
>(session))));
17196inline void free_cert(cert_t cert) {
17197 if (cert) { X509_free(
static_cast<X509 *
>(cert)); }
17200inline bool verify_hostname(cert_t cert,
const char *hostname) {
17201 if (!cert || !hostname)
return false;
17203 auto x509 =
static_cast<X509 *
>(cert);
17206 if (detail::is_ip_address(hostname)) {
17207 return X509_check_ip_asc(x509, hostname, 0) == 1;
17209 return X509_check_host(x509, hostname, strlen(hostname), 0,
nullptr) == 1;
17212inline uint64_t hostname_mismatch_code() {
17213 return static_cast<uint64_t
>(X509_V_ERR_HOSTNAME_MISMATCH);
17216inline long get_verify_result(const_session_t session) {
17217 if (!session)
return X509_V_ERR_UNSPECIFIED;
17218 return SSL_get_verify_result(
static_cast<SSL *
>(
const_cast<void *
>(session)));
17221inline std::string get_cert_subject_cn(cert_t cert) {
17222 if (!cert)
return "";
17223 auto x509 =
static_cast<X509 *
>(cert);
17224 auto subject_name = X509_get_subject_name(x509);
17225 if (!subject_name)
return "";
17229 X509_NAME_get_text_by_NID(subject_name, NID_commonName, buf,
sizeof(buf));
17230 if (len < 0)
return "";
17231 return std::string(buf,
static_cast<size_t>(len));
17234inline std::string get_cert_issuer_name(cert_t cert) {
17235 if (!cert)
return "";
17236 auto x509 =
static_cast<X509 *
>(cert);
17237 auto issuer_name = X509_get_issuer_name(x509);
17238 if (!issuer_name)
return "";
17241 X509_NAME_oneline(issuer_name, buf,
sizeof(buf));
17242 return std::string(buf);
17245inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
17247 if (!cert)
return false;
17248 auto x509 =
static_cast<X509 *
>(cert);
17250 auto names =
static_cast<GENERAL_NAMES *
>(
17251 X509_get_ext_d2i(x509, NID_subject_alt_name,
nullptr,
nullptr));
17252 if (!names)
return true;
17254 auto count = sk_GENERAL_NAME_num(names);
17255 for (
decltype(count) i = 0; i <
count; i++) {
17256 auto gen = sk_GENERAL_NAME_value(names, i);
17257 if (!gen)
continue;
17260 switch (gen->type) {
17262 entry.type = SanType::DNS;
17263 if (gen->d.dNSName) {
17264 entry.value = std::string(
17265 reinterpret_cast<const char *
>(
17266 ASN1_STRING_get0_data(gen->d.dNSName)),
17267 static_cast<size_t>(ASN1_STRING_length(gen->d.dNSName)));
17271 entry.type = SanType::IP;
17272 if (gen->d.iPAddress) {
17273 auto data = ASN1_STRING_get0_data(gen->d.iPAddress);
17274 auto len = ASN1_STRING_length(gen->d.iPAddress);
17277 char buf[INET_ADDRSTRLEN];
17278 inet_ntop(AF_INET,
data, buf,
sizeof(buf));
17280 }
else if (len == 16) {
17282 char buf[INET6_ADDRSTRLEN];
17283 inet_ntop(AF_INET6,
data, buf,
sizeof(buf));
17289 entry.type = SanType::EMAIL;
17290 if (gen->d.rfc822Name) {
17291 entry.value = std::string(
17292 reinterpret_cast<const char *
>(
17293 ASN1_STRING_get0_data(gen->d.rfc822Name)),
17294 static_cast<size_t>(ASN1_STRING_length(gen->d.rfc822Name)));
17298 entry.type = SanType::URI;
17299 if (gen->d.uniformResourceIdentifier) {
17300 entry.value = std::string(
17301 reinterpret_cast<const char *
>(
17302 ASN1_STRING_get0_data(gen->d.uniformResourceIdentifier)),
17303 static_cast<size_t>(
17304 ASN1_STRING_length(gen->d.uniformResourceIdentifier)));
17307 default: entry.type = SanType::OTHER;
break;
17310 if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
17313 GENERAL_NAMES_free(names);
17317inline bool get_cert_validity(cert_t cert, time_t ¬_before,
17318 time_t ¬_after) {
17319 if (!cert)
return false;
17320 auto x509 =
static_cast<X509 *
>(cert);
17322 auto nb = X509_get0_notBefore(x509);
17323 auto na = X509_get0_notAfter(x509);
17324 if (!nb || !na)
return false;
17326 ASN1_TIME *epoch = ASN1_TIME_new();
17327 if (!epoch)
return false;
17328 auto se = detail::scope_exit([&] { ASN1_TIME_free(epoch); });
17330 if (!ASN1_TIME_set(epoch, 0))
return false;
17334 if (!ASN1_TIME_diff(&pday, &psec, epoch, nb))
return false;
17335 not_before = 86400 * (time_t)pday + psec;
17337 if (!ASN1_TIME_diff(&pday, &psec, epoch, na))
return false;
17338 not_after = 86400 * (time_t)pday + psec;
17343inline std::string get_cert_serial(cert_t cert) {
17344 if (!cert)
return "";
17345 auto x509 =
static_cast<X509 *
>(cert);
17347 auto serial = X509_get_serialNumber(x509);
17348 if (!serial)
return "";
17350 auto bn = ASN1_INTEGER_to_BN(serial,
nullptr);
17351 if (!bn)
return "";
17353 auto hex = BN_bn2hex(bn);
17355 if (!hex)
return "";
17357 std::string result(hex);
17362inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
17363 if (!cert)
return false;
17364 auto x509 =
static_cast<X509 *
>(cert);
17365 auto len = i2d_X509(x509,
nullptr);
17366 if (len < 0)
return false;
17367 der.resize(
static_cast<size_t>(len));
17368 auto p = der.data();
17369 i2d_X509(x509, &p);
17373inline const char *get_sni(const_session_t session) {
17374 if (!session)
return nullptr;
17375 auto ssl =
static_cast<SSL *
>(
const_cast<void *
>(session));
17376 return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
17379inline uint64_t peek_error() {
return ERR_peek_last_error(); }
17381inline uint64_t get_error() {
return ERR_get_error(); }
17383inline std::string error_string(uint64_t
code) {
17385 ERR_error_string_n(
static_cast<unsigned long>(
code), buf,
sizeof(buf));
17386 return std::string(buf);
17389inline ca_store_t create_ca_store(
const char *pem,
size_t len) {
17390 auto mem = BIO_new_mem_buf(pem,
static_cast<int>(len));
17391 if (!mem) {
return nullptr; }
17392 auto mem_guard = detail::scope_exit([&] { BIO_free_all(mem); });
17394 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
17395 if (!inf) {
return nullptr; }
17397 auto store = X509_STORE_new();
17399 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
17400 auto itmp = sk_X509_INFO_value(inf, i);
17401 if (!itmp) {
continue; }
17402 if (itmp->x509) { X509_STORE_add_cert(
store, itmp->x509); }
17403 if (itmp->crl) { X509_STORE_add_crl(
store, itmp->crl); }
17407 sk_X509_INFO_pop_free(inf, X509_INFO_free);
17408 return static_cast<ca_store_t
>(
store);
17411inline void free_ca_store(ca_store_t
store) {
17412 if (
store) { X509_STORE_free(
static_cast<X509_STORE *
>(
store)); }
17415inline bool set_ca_store(ctx_t ctx, ca_store_t
store) {
17416 if (!ctx || !
store) {
return false; }
17417 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17418 auto x509_store =
static_cast<X509_STORE *
>(
store);
17421 if (SSL_CTX_get_cert_store(ssl_ctx) == x509_store) {
return true; }
17424 SSL_CTX_set_cert_store(ssl_ctx, x509_store);
17428inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
17430 if (!ctx) {
return 0; }
17431 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17433 auto store = SSL_CTX_get_cert_store(ssl_ctx);
17434 if (!
store) {
return 0; }
17436 auto objs = X509_STORE_get0_objects(
store);
17437 if (!objs) {
return 0; }
17439 auto count = sk_X509_OBJECT_num(objs);
17440 for (
decltype(count) i = 0; i <
count; i++) {
17441 auto obj = sk_X509_OBJECT_value(objs, i);
17442 if (!obj) {
continue; }
17443 if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
17444 auto x509 = X509_OBJECT_get0_X509(obj);
17448 certs.push_back(
static_cast<cert_t
>(x509));
17452 return certs.size();
17455inline std::vector<std::string> get_ca_names(ctx_t ctx) {
17456 std::vector<std::string> names;
17457 if (!ctx) {
return names; }
17458 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17460 auto store = SSL_CTX_get_cert_store(ssl_ctx);
17461 if (!
store) {
return names; }
17463 auto objs = X509_STORE_get0_objects(
store);
17464 if (!objs) {
return names; }
17466 auto count = sk_X509_OBJECT_num(objs);
17467 for (
decltype(count) i = 0; i <
count; i++) {
17468 auto obj = sk_X509_OBJECT_value(objs, i);
17469 if (!obj) {
continue; }
17470 if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
17471 auto x509 = X509_OBJECT_get0_X509(obj);
17473 auto subject = X509_get_subject_name(x509);
17476 X509_NAME_oneline(subject, buf,
sizeof(buf));
17477 names.push_back(buf);
17485inline bool update_server_cert(ctx_t ctx,
const char *cert_pem,
17486 const char *key_pem,
const char *password) {
17487 if (!ctx || !cert_pem || !key_pem) {
return false; }
17488 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17491 auto cert_bio = BIO_new_mem_buf(cert_pem, -1);
17492 if (!cert_bio) {
return false; }
17493 auto cert = PEM_read_bio_X509(cert_bio,
nullptr,
nullptr,
nullptr);
17494 BIO_free(cert_bio);
17495 if (!cert) {
return false; }
17498 auto key_bio = BIO_new_mem_buf(key_pem, -1);
17503 auto key = PEM_read_bio_PrivateKey(key_bio,
nullptr,
nullptr,
17504 password ?
const_cast<char *
>(password)
17513 auto ret = SSL_CTX_use_certificate(ssl_ctx, cert) == 1 &&
17514 SSL_CTX_use_PrivateKey(ssl_ctx, key) == 1;
17517 EVP_PKEY_free(key);
17521inline bool update_server_client_ca(ctx_t ctx,
const char *ca_pem) {
17522 if (!ctx || !ca_pem) {
return false; }
17523 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17526 auto store = create_ca_store(ca_pem, strlen(ca_pem));
17527 if (!
store) {
return false; }
17530 SSL_CTX_set_cert_store(ssl_ctx,
static_cast<X509_STORE *
>(
store));
17533 auto ca_list = impl::create_client_ca_list_from_pem(ca_pem);
17536 SSL_CTX_set_client_CA_list(ssl_ctx, ca_list);
17542inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
17543 if (!ctx) {
return false; }
17544 auto ssl_ctx =
static_cast<SSL_CTX *
>(ctx);
17546 impl::get_verify_callback() = std::move(callback);
17548 if (impl::get_verify_callback()) {
17549 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, impl::openssl_verify_callback);
17551 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER,
nullptr);
17556inline long get_verify_error(const_session_t session) {
17557 if (!session) {
return -1; }
17558 auto ssl =
static_cast<SSL *
>(
const_cast<void *
>(session));
17559 return SSL_get_verify_result(ssl);
17562inline std::string verify_error_string(
long error_code) {
17563 if (error_code == X509_V_OK) {
return ""; }
17564 const char *
str = X509_verify_cert_error_string(
static_cast<int>(error_code));
17565 return str ?
str :
"unknown error";
17570inline bool SSLClient::verify_host(X509 *server_cert)
const {
17592 return verify_host_with_subject_alt_name(server_cert) ||
17593 verify_host_with_common_name(server_cert);
17597SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
17600 auto type = GEN_DNS;
17602 struct in6_addr addr6 = {};
17603 struct in_addr addr = {};
17604 size_t addr_len = 0;
17607 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
17609 addr_len =
sizeof(
struct in6_addr);
17610 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
17612 addr_len =
sizeof(
struct in_addr);
17616 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
17617 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
17620 auto dsn_matched =
false;
17621 auto ip_matched =
false;
17623 auto count = sk_GENERAL_NAME_num(alt_names);
17625 for (
decltype(count) i = 0; i <
count && !dsn_matched; i++) {
17626 auto val = sk_GENERAL_NAME_value(alt_names, i);
17627 if (!val || val->type != type) {
continue; }
17630 reinterpret_cast<const char *
>(ASN1_STRING_get0_data(val->d.ia5));
17631 if (
name ==
nullptr) {
continue; }
17633 auto name_len =
static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
17638 detail::match_hostname(std::string(
name, name_len), host_);
17642 if (!memcmp(&addr6,
name, addr_len) || !memcmp(&addr,
name, addr_len)) {
17649 if (dsn_matched || ip_matched) {
ret =
true; }
17652 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME) *
>(
17653 reinterpret_cast<const STACK_OF(GENERAL_NAME) *
>(alt_names)));
17657inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
17658 const auto subject_name = X509_get_subject_name(server_cert);
17660 if (subject_name !=
nullptr) {
17662 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
17665 if (name_len != -1) {
17666 return detail::match_hostname(
17667 std::string(
name,
static_cast<size_t>(name_len)), host_);
17684#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
17690struct MbedTlsSession {
17691 mbedtls_ssl_context ssl;
17693 std::string hostname;
17694 std::string sni_hostname;
17696 MbedTlsSession() { mbedtls_ssl_init(&ssl); }
17698 ~MbedTlsSession() { mbedtls_ssl_free(&ssl); }
17700 MbedTlsSession(
const MbedTlsSession &) =
delete;
17701 MbedTlsSession &operator=(
const MbedTlsSession &) =
delete;
17706inline int &mbedtls_last_error() {
17707 static thread_local int err = 0;
17712inline ErrorCode map_mbedtls_error(
int ret,
int &out_errno) {
17713 if (
ret == 0) {
return ErrorCode::Success; }
17714 if (
ret == MBEDTLS_ERR_SSL_WANT_READ) {
return ErrorCode::WantRead; }
17715 if (
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
return ErrorCode::WantWrite; }
17716 if (
ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
17717 return ErrorCode::PeerClosed;
17719 if (
ret == MBEDTLS_ERR_NET_CONN_RESET ||
ret == MBEDTLS_ERR_NET_SEND_FAILED ||
17720 ret == MBEDTLS_ERR_NET_RECV_FAILED) {
17722 return ErrorCode::SyscallError;
17724 if (
ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
17725 return ErrorCode::CertVerifyFailed;
17727 return ErrorCode::Fatal;
17731inline int mbedtls_net_send_cb(
void *ctx,
const unsigned char *buf,
17733 auto sock = *
static_cast<socket_t *
>(ctx);
17736 send(sock,
reinterpret_cast<const char *
>(buf),
static_cast<int>(len), 0);
17737 if (
ret == SOCKET_ERROR) {
17738 int err = WSAGetLastError();
17739 if (err == WSAEWOULDBLOCK) {
return MBEDTLS_ERR_SSL_WANT_WRITE; }
17740 return MBEDTLS_ERR_NET_SEND_FAILED;
17743 auto ret = send(sock, buf, len, 0);
17745 if (errno == EAGAIN || errno == EWOULDBLOCK) {
17746 return MBEDTLS_ERR_SSL_WANT_WRITE;
17748 return MBEDTLS_ERR_NET_SEND_FAILED;
17751 return static_cast<int>(
ret);
17755inline int mbedtls_net_recv_cb(
void *ctx,
unsigned char *buf,
size_t len) {
17756 auto sock = *
static_cast<socket_t *
>(ctx);
17759 recv(sock,
reinterpret_cast<char *
>(buf),
static_cast<int>(len), 0);
17760 if (
ret == SOCKET_ERROR) {
17761 int err = WSAGetLastError();
17762 if (err == WSAEWOULDBLOCK) {
return MBEDTLS_ERR_SSL_WANT_READ; }
17763 return MBEDTLS_ERR_NET_RECV_FAILED;
17766 auto ret = recv(sock, buf, len, 0);
17768 if (errno == EAGAIN || errno == EWOULDBLOCK) {
17769 return MBEDTLS_ERR_SSL_WANT_READ;
17771 return MBEDTLS_ERR_NET_RECV_FAILED;
17774 if (
ret == 0) {
return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY; }
17775 return static_cast<int>(
ret);
17779inline MbedTlsContext::MbedTlsContext() {
17780 mbedtls_ssl_config_init(&conf);
17781 mbedtls_entropy_init(&entropy);
17782 mbedtls_ctr_drbg_init(&ctr_drbg);
17783 mbedtls_x509_crt_init(&ca_chain);
17784 mbedtls_x509_crt_init(&own_cert);
17785 mbedtls_pk_init(&own_key);
17788inline MbedTlsContext::~MbedTlsContext() {
17789 mbedtls_pk_free(&own_key);
17790 mbedtls_x509_crt_free(&own_cert);
17791 mbedtls_x509_crt_free(&ca_chain);
17792 mbedtls_ctr_drbg_free(&ctr_drbg);
17793 mbedtls_entropy_free(&entropy);
17794 mbedtls_ssl_config_free(&conf);
17800inline std::string &mbedpending_sni() {
17801 static thread_local std::string sni;
17806inline int mbedtls_sni_callback(
void *p_ctx, mbedtls_ssl_context *ssl,
17807 const unsigned char *
name,
size_t name_len) {
17813 if (
name && name_len > 0) {
17814 mbedpending_sni().assign(
reinterpret_cast<const char *
>(
name), name_len);
17816 mbedpending_sni().clear();
17821inline int mbedtls_verify_callback(
void *
data, mbedtls_x509_crt *crt,
17822 int cert_depth, uint32_t *flags);
17825inline int mbedtls_verify_callback(
void *
data, mbedtls_x509_crt *crt,
17826 int cert_depth, uint32_t *flags) {
17827 auto &callback = get_verify_callback();
17828 if (!callback) {
return 0; }
17831 auto *session =
static_cast<MbedTlsSession *
>(
data);
17834 VerifyContext verify_ctx;
17835 verify_ctx.session =
static_cast<session_t
>(session);
17836 verify_ctx.cert =
static_cast<cert_t
>(crt);
17837 verify_ctx.depth = cert_depth;
17838 verify_ctx.preverify_ok = (*flags == 0);
17839 verify_ctx.error_code =
static_cast<long>(*flags);
17842 static thread_local char error_buf[256];
17844 mbedtls_x509_crt_verify_info(error_buf,
sizeof(error_buf),
"", *flags);
17845 verify_ctx.error_string = error_buf;
17847 verify_ctx.error_string =
nullptr;
17850 bool accepted = callback(verify_ctx);
17856 return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
17861inline ctx_t create_client_context() {
17862 auto ctx =
new (std::nothrow) impl::MbedTlsContext();
17863 if (!ctx) {
return nullptr; }
17865 ctx->is_server =
false;
17868 const char *pers =
"httplib_client";
17869 int ret = mbedtls_ctr_drbg_seed(
17870 &ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy,
17871 reinterpret_cast<const unsigned char *
>(pers), strlen(pers));
17873 impl::mbedtls_last_error() =
ret;
17879 ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_CLIENT,
17880 MBEDTLS_SSL_TRANSPORT_STREAM,
17881 MBEDTLS_SSL_PRESET_DEFAULT);
17883 impl::mbedtls_last_error() =
ret;
17889 mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
17892 mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
17895#ifdef CPPHTTPLIB_MBEDTLS_V3
17896 mbedtls_ssl_conf_min_tls_version(&ctx->conf, MBEDTLS_SSL_VERSION_TLS1_2);
17898 mbedtls_ssl_conf_min_version(&ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
17899 MBEDTLS_SSL_MINOR_VERSION_3);
17902 return static_cast<ctx_t
>(ctx);
17905inline ctx_t create_server_context() {
17906 auto ctx =
new (std::nothrow) impl::MbedTlsContext();
17907 if (!ctx) {
return nullptr; }
17909 ctx->is_server =
true;
17912 const char *pers =
"httplib_server";
17913 int ret = mbedtls_ctr_drbg_seed(
17914 &ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy,
17915 reinterpret_cast<const unsigned char *
>(pers), strlen(pers));
17917 impl::mbedtls_last_error() =
ret;
17923 ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_SERVER,
17924 MBEDTLS_SSL_TRANSPORT_STREAM,
17925 MBEDTLS_SSL_PRESET_DEFAULT);
17927 impl::mbedtls_last_error() =
ret;
17933 mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
17936 mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
17939#ifdef CPPHTTPLIB_MBEDTLS_V3
17940 mbedtls_ssl_conf_min_tls_version(&ctx->conf, MBEDTLS_SSL_VERSION_TLS1_2);
17942 mbedtls_ssl_conf_min_version(&ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
17943 MBEDTLS_SSL_MINOR_VERSION_3);
17947 mbedtls_ssl_conf_sni(&ctx->conf, impl::mbedtls_sni_callback,
nullptr);
17949 return static_cast<ctx_t
>(ctx);
17952inline void free_context(ctx_t ctx) {
17953 if (ctx) {
delete static_cast<impl::MbedTlsContext *
>(ctx); }
17956inline bool set_min_version(ctx_t ctx, Version version) {
17957 if (!ctx) {
return false; }
17958 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
17960#ifdef CPPHTTPLIB_MBEDTLS_V3
17962 mbedtls_ssl_protocol_version min_ver = MBEDTLS_SSL_VERSION_TLS1_2;
17963 if (version >= Version::TLS1_3) {
17964#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
17965 min_ver = MBEDTLS_SSL_VERSION_TLS1_3;
17968 mbedtls_ssl_conf_min_tls_version(&mctx->conf, min_ver);
17971 int major = MBEDTLS_SSL_MAJOR_VERSION_3;
17972 int minor = MBEDTLS_SSL_MINOR_VERSION_3;
17973 if (version >= Version::TLS1_3) {
17974#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
17975 minor = MBEDTLS_SSL_MINOR_VERSION_4;
17977 minor = MBEDTLS_SSL_MINOR_VERSION_3;
17980 mbedtls_ssl_conf_min_version(&mctx->conf, major, minor);
17985inline bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len) {
17986 if (!ctx || !pem) {
return false; }
17987 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
17991 std::string pem_str(pem, len);
17992 int ret = mbedtls_x509_crt_parse(
17993 &mctx->ca_chain,
reinterpret_cast<const unsigned char *
>(pem_str.c_str()),
17994 pem_str.size() + 1);
17996 impl::mbedtls_last_error() =
ret;
18000 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18004inline bool load_ca_file(ctx_t ctx,
const char *file_path) {
18005 if (!ctx || !file_path) {
return false; }
18006 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18008 int ret = mbedtls_x509_crt_parse_file(&mctx->ca_chain, file_path);
18010 impl::mbedtls_last_error() =
ret;
18014 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18018inline bool load_ca_dir(ctx_t ctx,
const char *dir_path) {
18019 if (!ctx || !dir_path) {
return false; }
18020 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18022 int ret = mbedtls_x509_crt_parse_path(&mctx->ca_chain, dir_path);
18024 impl::mbedtls_last_error() =
ret;
18028 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18032inline bool load_system_certs(ctx_t ctx) {
18033 if (!ctx) {
return false; }
18034 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18035 bool loaded =
false;
18038 loaded = impl::enumerate_windows_system_certs(
18039 [&](
const unsigned char *
data,
size_t len) {
18040 return mbedtls_x509_crt_parse_der(&mctx->ca_chain,
data, len) == 0;
18042#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
18043 loaded = impl::enumerate_macos_keychain_certs(
18044 [&](
const unsigned char *
data,
size_t len) {
18045 return mbedtls_x509_crt_parse_der(&mctx->ca_chain,
data, len) == 0;
18048 for (
auto path = impl::system_ca_paths(); *path; ++path) {
18049 if (mbedtls_x509_crt_parse_file(&mctx->ca_chain, *path) >= 0) {
18056 for (
auto dir = impl::system_ca_dirs(); *dir; ++dir) {
18057 if (mbedtls_x509_crt_parse_path(&mctx->ca_chain, *dir) >= 0) {
18066 mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain,
nullptr);
18071inline bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
18072 const char *password) {
18073 if (!ctx || !cert || !key) {
return false; }
18074 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18077 std::string cert_str(cert);
18078 int ret = mbedtls_x509_crt_parse(
18080 reinterpret_cast<const unsigned char *
>(cert_str.c_str()),
18081 cert_str.size() + 1);
18083 impl::mbedtls_last_error() =
ret;
18088 std::string key_str(key);
18089 const unsigned char *pwd =
18090 password ?
reinterpret_cast<const unsigned char *
>(password) : nullptr;
18091 size_t pwd_len = password ? strlen(password) : 0;
18093#ifdef CPPHTTPLIB_MBEDTLS_V3
18094 ret = mbedtls_pk_parse_key(
18095 &mctx->own_key,
reinterpret_cast<const unsigned char *
>(key_str.c_str()),
18096 key_str.size() + 1, pwd, pwd_len, mbedtls_ctr_drbg_random,
18099 ret = mbedtls_pk_parse_key(
18100 &mctx->own_key,
reinterpret_cast<const unsigned char *
>(key_str.c_str()),
18101 key_str.size() + 1, pwd, pwd_len);
18104 impl::mbedtls_last_error() =
ret;
18109#ifdef CPPHTTPLIB_MBEDTLS_V3
18110 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key,
18111 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18113 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key);
18116 impl::mbedtls_last_error() =
ret;
18120 ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key);
18122 impl::mbedtls_last_error() =
ret;
18129inline bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
18130 const char *key_path,
const char *password) {
18131 if (!ctx || !cert_path || !key_path) {
return false; }
18132 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18135 int ret = mbedtls_x509_crt_parse_file(&mctx->own_cert, cert_path);
18137 impl::mbedtls_last_error() =
ret;
18142#ifdef CPPHTTPLIB_MBEDTLS_V3
18143 ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password,
18144 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18146 ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password);
18149 impl::mbedtls_last_error() =
ret;
18154#ifdef CPPHTTPLIB_MBEDTLS_V3
18155 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key,
18156 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18158 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key);
18161 impl::mbedtls_last_error() =
ret;
18165 ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key);
18167 impl::mbedtls_last_error() =
ret;
18174inline void set_verify_client(ctx_t ctx,
bool require) {
18175 if (!ctx) {
return; }
18176 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18177 mctx->verify_client = require;
18179 mbedtls_ssl_conf_authmode(&mctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
18183 mbedtls_ssl_conf_authmode(&mctx->conf, mctx->has_verify_callback
18184 ? MBEDTLS_SSL_VERIFY_OPTIONAL
18185 : MBEDTLS_SSL_VERIFY_NONE);
18189inline session_t create_session(ctx_t ctx,
socket_t sock) {
18191 auto mctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18193 auto session =
new (std::nothrow) impl::MbedTlsSession();
18194 if (!session) {
return nullptr; }
18196 session->sock = sock;
18198 int ret = mbedtls_ssl_setup(&session->ssl, &mctx->conf);
18200 impl::mbedtls_last_error() =
ret;
18206 mbedtls_ssl_set_bio(&session->ssl, &session->sock, impl::mbedtls_net_send_cb,
18207 impl::mbedtls_net_recv_cb,
nullptr);
18211 if (mctx->has_verify_callback) {
18212 mbedtls_ssl_set_verify(&session->ssl, impl::mbedtls_verify_callback,
18216 return static_cast<session_t
>(session);
18219inline void free_session(session_t session) {
18220 if (session) {
delete static_cast<impl::MbedTlsSession *
>(session); }
18223inline bool set_sni(session_t session,
const char *hostname) {
18224 if (!session || !hostname) {
return false; }
18225 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18227 int ret = mbedtls_ssl_set_hostname(&msession->ssl, hostname);
18229 impl::mbedtls_last_error() =
ret;
18233 msession->hostname = hostname;
18237inline bool set_hostname(session_t session,
const char *hostname) {
18239 return set_sni(session, hostname);
18242inline TlsError connect(session_t session) {
18245 err.code = ErrorCode::Fatal;
18249 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18250 int ret = mbedtls_ssl_handshake(&msession->ssl);
18253 err.code = ErrorCode::Success;
18255 err.code = impl::map_mbedtls_error(
ret, err.sys_errno);
18256 err.backend_code =
static_cast<uint64_t
>(-
ret);
18257 impl::mbedtls_last_error() =
ret;
18263inline TlsError accept(session_t session) {
18265 auto result = connect(session);
18268 if (result.code == ErrorCode::Success && session) {
18269 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18270 msession->sni_hostname = std::move(impl::mbedpending_sni());
18271 impl::mbedpending_sni().clear();
18277inline bool connect_nonblocking(session_t session,
socket_t sock,
18278 time_t timeout_sec, time_t timeout_usec,
18281 if (err) { err->code = ErrorCode::Fatal; }
18285 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18288 detail::set_nonblocking(sock,
true);
18290 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
18293 while ((
ret = mbedtls_ssl_handshake(&msession->ssl)) != 0) {
18294 if (
ret == MBEDTLS_ERR_SSL_WANT_READ) {
18295 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
18298 }
else if (
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
18299 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
18306 err->code = impl::map_mbedtls_error(
ret, err->sys_errno);
18307 err->backend_code =
static_cast<uint64_t
>(-
ret);
18309 impl::mbedtls_last_error() =
ret;
18313 if (err) { err->code = ErrorCode::Success; }
18317inline bool accept_nonblocking(session_t session,
socket_t sock,
18318 time_t timeout_sec, time_t timeout_usec,
18322 connect_nonblocking(session, sock, timeout_sec, timeout_usec, err);
18325 if (result && session) {
18326 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18327 msession->sni_hostname = std::move(impl::mbedpending_sni());
18328 impl::mbedpending_sni().clear();
18334inline ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err) {
18335 if (!session || !buf) {
18336 err.code = ErrorCode::Fatal;
18340 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18342 mbedtls_ssl_read(&msession->ssl,
static_cast<unsigned char *
>(buf), len);
18345 err.code = ErrorCode::Success;
18346 return static_cast<ssize_t
>(
ret);
18350 err.code = ErrorCode::PeerClosed;
18354 err.code = impl::map_mbedtls_error(
ret, err.sys_errno);
18355 err.backend_code =
static_cast<uint64_t
>(-
ret);
18356 impl::mbedtls_last_error() =
ret;
18359 if (err.code == ErrorCode::PeerClosed) {
return 0; }
18363inline ssize_t write(session_t session,
const void *buf,
size_t len,
18365 if (!session || !buf) {
18366 err.code = ErrorCode::Fatal;
18370 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18371 int ret = mbedtls_ssl_write(&msession->ssl,
18372 static_cast<const unsigned char *
>(buf), len);
18375 err.code = ErrorCode::Success;
18376 return static_cast<ssize_t
>(
ret);
18380 err.code = ErrorCode::PeerClosed;
18384 err.code = impl::map_mbedtls_error(
ret, err.sys_errno);
18385 err.backend_code =
static_cast<uint64_t
>(-
ret);
18386 impl::mbedtls_last_error() =
ret;
18390inline int pending(const_session_t session) {
18391 if (!session) {
return 0; }
18393 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18394 return static_cast<int>(mbedtls_ssl_get_bytes_avail(&msession->ssl));
18397inline void shutdown(session_t session,
bool graceful) {
18398 if (!session) {
return; }
18399 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18405 while ((
ret = mbedtls_ssl_close_notify(&msession->ssl)) != 0 &&
18407 if (
ret != MBEDTLS_ERR_SSL_WANT_READ &&
18408 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
18416inline bool is_peer_closed(session_t session,
socket_t sock) {
18418 auto msession =
static_cast<impl::MbedTlsSession *
>(session);
18422 if (mbedtls_ssl_get_bytes_avail(&msession->ssl) > 0) {
return false; }
18425 detail::set_nonblocking(sock,
true);
18427 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
18434 int ret = mbedtls_ssl_read(&msession->ssl, &buf, 1);
18437 if (
ret > 0 ||
ret == MBEDTLS_ERR_SSL_WANT_READ) {
return false; }
18440 return ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ||
18441 ret == MBEDTLS_ERR_NET_CONN_RESET ||
ret == 0;
18444inline cert_t get_peer_cert(const_session_t session) {
18445 if (!session) {
return nullptr; }
18447 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18452 const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&msession->ssl);
18453 return const_cast<mbedtls_x509_crt *
>(cert);
18456inline void free_cert(cert_t cert) {
18463inline bool verify_hostname(cert_t cert,
const char *hostname) {
18464 if (!cert || !hostname) {
return false; }
18465 auto mcert =
static_cast<const mbedtls_x509_crt *
>(cert);
18466 std::string host_str(hostname);
18469 bool is_ip = impl::is_ipv4_address(host_str);
18470 unsigned char ip_bytes[4];
18471 if (is_ip) { impl::parse_ipv4(host_str, ip_bytes); }
18477 const mbedtls_x509_sequence *san = &mcert->subject_alt_names;
18478 while (san !=
nullptr && san->buf.p !=
nullptr && san->buf.len > 0) {
18479 const unsigned char *p = san->buf.p;
18480 size_t len = san->buf.len;
18484 if (len == 4 && memcmp(p, ip_bytes, 4) == 0) {
return true; }
18488 bool is_dns = len > 0;
18489 for (
size_t i = 0; i < len && is_dns; i++) {
18490 if (p[i] < 32 || p[i] > 126) { is_dns =
false; }
18493 std::string san_name(
reinterpret_cast<const char *
>(p), len);
18494 if (detail::match_hostname(san_name, host_str)) {
return true; }
18502 int ret = mbedtls_x509_dn_gets(cn,
sizeof(cn), &mcert->subject);
18504 std::string cn_str(cn);
18507 size_t cn_pos = cn_str.find(
"CN=");
18508 if (cn_pos != std::string::npos) {
18509 size_t start = cn_pos + 3;
18510 size_t end = cn_str.find(
',', start);
18511 std::string cn_value =
18512 cn_str.substr(start, end == std::string::npos ? end : end - start);
18514 if (detail::match_hostname(cn_value, host_str)) {
return true; }
18521inline uint64_t hostname_mismatch_code() {
18522 return static_cast<uint64_t
>(MBEDTLS_X509_BADCERT_CN_MISMATCH);
18525inline long get_verify_result(const_session_t session) {
18526 if (!session) {
return -1; }
18528 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18529 uint32_t flags = mbedtls_ssl_get_verify_result(&msession->ssl);
18531 return flags == 0 ? 0 :
static_cast<long>(flags);
18534inline std::string get_cert_subject_cn(cert_t cert) {
18535 if (!cert)
return "";
18536 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18539 const mbedtls_x509_name *
name = &x509->subject;
18540 while (
name !=
nullptr) {
18541 if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &
name->oid) == 0) {
18542 return std::string(
reinterpret_cast<const char *
>(
name->val.p),
18550inline std::string get_cert_issuer_name(cert_t cert) {
18551 if (!cert)
return "";
18552 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18556 int ret = mbedtls_x509_dn_gets(buf,
sizeof(buf), &x509->issuer);
18557 if (
ret < 0)
return "";
18558 return std::string(buf);
18561inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
18563 if (!cert)
return false;
18564 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18567 const mbedtls_x509_sequence *cur = &x509->subject_alt_names;
18568 while (cur !=
nullptr) {
18569 if (cur->buf.len > 0) {
18572 const unsigned char *p = cur->buf.p;
18573 size_t len = cur->buf.len;
18576 unsigned char tag = *p;
18581 if (len > 0 && *p < 0x80) {
18582 size_t value_len = *p;
18586 if (value_len <= len) {
18589 switch (
tag & 0x1F) {
18591 entry.type = SanType::DNS;
18593 std::string(
reinterpret_cast<const char *
>(p), value_len);
18596 entry.type = SanType::IP;
18597 if (value_len == 4) {
18600 snprintf(buf,
sizeof(buf),
"%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
18602 }
else if (value_len == 16) {
18605 snprintf(buf,
sizeof(buf),
18606 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
18607 "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
18608 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
18609 p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
18614 entry.type = SanType::EMAIL;
18616 std::string(
reinterpret_cast<const char *
>(p), value_len);
18619 entry.type = SanType::URI;
18621 std::string(
reinterpret_cast<const char *
>(p), value_len);
18623 default: entry.type = SanType::OTHER;
break;
18626 if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
18635inline bool get_cert_validity(cert_t cert, time_t ¬_before,
18636 time_t ¬_after) {
18637 if (!cert)
return false;
18638 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18641 auto to_time_t = [](
const mbedtls_x509_time &t) -> time_t {
18642 struct tm tm_time = {};
18643 tm_time.tm_year = t.year - 1900;
18644 tm_time.tm_mon = t.mon - 1;
18645 tm_time.tm_mday = t.day;
18646 tm_time.tm_hour = t.hour;
18647 tm_time.tm_min = t.min;
18648 tm_time.tm_sec = t.sec;
18650 return _mkgmtime(&tm_time);
18652 return timegm(&tm_time);
18656 not_before = to_time_t(x509->valid_from);
18657 not_after = to_time_t(x509->valid_to);
18661inline std::string get_cert_serial(cert_t cert) {
18662 if (!cert)
return "";
18663 auto x509 =
static_cast<mbedtls_x509_crt *
>(cert);
18666 std::string result;
18667 result.reserve(x509->serial.len * 2);
18668 for (
size_t i = 0; i < x509->serial.len; i++) {
18670 snprintf(hex,
sizeof(hex),
"%02X", x509->serial.p[i]);
18676inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
18677 if (!cert)
return false;
18678 auto crt =
static_cast<mbedtls_x509_crt *
>(cert);
18679 if (!crt->raw.p || crt->raw.len == 0)
return false;
18680 der.assign(crt->raw.p, crt->raw.p + crt->raw.len);
18684inline const char *get_sni(const_session_t session) {
18685 if (!session)
return nullptr;
18686 auto msession =
static_cast<const impl::MbedTlsSession *
>(session);
18689 if (!msession->sni_hostname.empty()) {
18690 return msession->sni_hostname.c_str();
18694 if (!msession->hostname.empty()) {
return msession->hostname.c_str(); }
18699inline uint64_t peek_error() {
18701 return static_cast<uint64_t
>(-impl::mbedtls_last_error());
18704inline uint64_t get_error() {
18706 uint64_t err =
static_cast<uint64_t
>(-impl::mbedtls_last_error());
18707 impl::mbedtls_last_error() = 0;
18711inline std::string error_string(uint64_t
code) {
18713 mbedtls_strerror(-
static_cast<int>(
code), buf,
sizeof(buf));
18714 return std::string(buf);
18717inline ca_store_t create_ca_store(
const char *pem,
size_t len) {
18718 auto *ca_chain =
new (std::nothrow) mbedtls_x509_crt;
18719 if (!ca_chain) {
return nullptr; }
18721 mbedtls_x509_crt_init(ca_chain);
18724 int ret = mbedtls_x509_crt_parse(ca_chain,
18725 reinterpret_cast<const unsigned char *
>(pem),
18729 ret = mbedtls_x509_crt_parse(
18730 ca_chain,
reinterpret_cast<const unsigned char *
>(pem), len);
18732 mbedtls_x509_crt_free(ca_chain);
18738 return static_cast<ca_store_t
>(ca_chain);
18741inline void free_ca_store(ca_store_t
store) {
18743 auto *ca_chain =
static_cast<mbedtls_x509_crt *
>(
store);
18744 mbedtls_x509_crt_free(ca_chain);
18749inline bool set_ca_store(ctx_t ctx, ca_store_t
store) {
18750 if (!ctx || !
store) {
return false; }
18751 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18752 auto *ca_chain =
static_cast<mbedtls_x509_crt *
>(
store);
18755 mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
18756 mbedtls_x509_crt_init(&mbed_ctx->ca_chain);
18760 mbedtls_x509_crt *
src = ca_chain;
18761 while (
src !=
nullptr) {
18762 int ret = mbedtls_x509_crt_parse_der(&mbed_ctx->ca_chain,
src->raw.p,
18764 if (
ret != 0) {
return false; }
18769 mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->ca_chain,
nullptr);
18773inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
18775 if (!ctx) {
return 0; }
18776 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18779 mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
18780 while (cert !=
nullptr && cert->raw.len > 0) {
18782 auto *copy =
new mbedtls_x509_crt;
18783 mbedtls_x509_crt_init(copy);
18784 int ret = mbedtls_x509_crt_parse_der(copy, cert->raw.p, cert->raw.len);
18786 certs.push_back(
static_cast<cert_t
>(copy));
18788 mbedtls_x509_crt_free(copy);
18793 return certs.size();
18796inline std::vector<std::string> get_ca_names(ctx_t ctx) {
18797 std::vector<std::string> names;
18798 if (!ctx) {
return names; }
18799 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18802 mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
18803 while (cert !=
nullptr && cert->raw.len > 0) {
18805 int ret = mbedtls_x509_dn_gets(buf,
sizeof(buf), &cert->subject);
18806 if (
ret > 0) { names.push_back(buf); }
18812inline bool update_server_cert(ctx_t ctx,
const char *cert_pem,
18813 const char *key_pem,
const char *password) {
18814 if (!ctx || !cert_pem || !key_pem) {
return false; }
18815 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18818 mbedtls_x509_crt_free(&mbed_ctx->own_cert);
18819 mbedtls_pk_free(&mbed_ctx->own_key);
18820 mbedtls_x509_crt_init(&mbed_ctx->own_cert);
18821 mbedtls_pk_init(&mbed_ctx->own_key);
18824 int ret = mbedtls_x509_crt_parse(
18825 &mbed_ctx->own_cert,
reinterpret_cast<const unsigned char *
>(cert_pem),
18826 strlen(cert_pem) + 1);
18828 impl::mbedtls_last_error() =
ret;
18833#ifdef CPPHTTPLIB_MBEDTLS_V3
18834 ret = mbedtls_pk_parse_key(
18835 &mbed_ctx->own_key,
reinterpret_cast<const unsigned char *
>(key_pem),
18836 strlen(key_pem) + 1,
18837 password ?
reinterpret_cast<const unsigned char *
>(password) :
nullptr,
18838 password ? strlen(password) : 0, mbedtls_ctr_drbg_random,
18839 &mbed_ctx->ctr_drbg);
18841 ret = mbedtls_pk_parse_key(
18842 &mbed_ctx->own_key,
reinterpret_cast<const unsigned char *
>(key_pem),
18843 strlen(key_pem) + 1,
18844 password ?
reinterpret_cast<const unsigned char *
>(password) :
nullptr,
18845 password ? strlen(password) : 0);
18848 impl::mbedtls_last_error() =
ret;
18853 ret = mbedtls_ssl_conf_own_cert(&mbed_ctx->conf, &mbed_ctx->own_cert,
18854 &mbed_ctx->own_key);
18856 impl::mbedtls_last_error() =
ret;
18863inline bool update_server_client_ca(ctx_t ctx,
const char *ca_pem) {
18864 if (!ctx || !ca_pem) {
return false; }
18865 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18868 mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
18869 mbedtls_x509_crt_init(&mbed_ctx->ca_chain);
18872 int ret = mbedtls_x509_crt_parse(
18873 &mbed_ctx->ca_chain,
reinterpret_cast<const unsigned char *
>(ca_pem),
18874 strlen(ca_pem) + 1);
18876 impl::mbedtls_last_error() =
ret;
18881 mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->ca_chain,
nullptr);
18885inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
18886 if (!ctx) {
return false; }
18887 auto *mbed_ctx =
static_cast<impl::MbedTlsContext *
>(ctx);
18889 impl::get_verify_callback() = std::move(callback);
18890 mbed_ctx->has_verify_callback =
18891 static_cast<bool>(impl::get_verify_callback());
18893 if (mbed_ctx->has_verify_callback) {
18896 mbedtls_ssl_conf_authmode(&mbed_ctx->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
18897 mbedtls_ssl_conf_verify(&mbed_ctx->conf, impl::mbedtls_verify_callback,
18900 mbedtls_ssl_conf_verify(&mbed_ctx->conf,
nullptr,
nullptr);
18905inline long get_verify_error(const_session_t session) {
18906 if (!session) {
return -1; }
18908 static_cast<impl::MbedTlsSession *
>(
const_cast<void *
>(session));
18909 return static_cast<long>(mbedtls_ssl_get_verify_result(&msession->ssl));
18912inline std::string verify_error_string(
long error_code) {
18913 if (error_code == 0) {
return ""; }
18915 mbedtls_x509_crt_verify_info(buf,
sizeof(buf),
"",
18916 static_cast<uint32_t
>(error_code));
18918 std::string result(buf);
18919 while (!result.empty() && (result.back() ==
'\n' || result.back() ==
' ')) {
18937#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
18943struct WolfSSLSession {
18944 WOLFSSL *ssl =
nullptr;
18946 std::string hostname;
18947 std::string sni_hostname;
18949 WolfSSLSession() =
default;
18951 ~WolfSSLSession() {
18952 if (ssl) { wolfSSL_free(ssl); }
18955 WolfSSLSession(
const WolfSSLSession &) =
delete;
18956 WolfSSLSession &operator=(
const WolfSSLSession &) =
delete;
18960inline uint64_t &wolfssl_last_error() {
18961 static thread_local uint64_t err = 0;
18968inline ErrorCode map_wolfssl_error(WOLFSSL *ssl,
int ssl_error,
18970 switch (ssl_error) {
18971 case SSL_ERROR_NONE:
return ErrorCode::Success;
18972 case SSL_ERROR_WANT_READ:
return ErrorCode::WantRead;
18973 case SSL_ERROR_WANT_WRITE:
return ErrorCode::WantWrite;
18974 case SSL_ERROR_ZERO_RETURN:
return ErrorCode::PeerClosed;
18975 case SSL_ERROR_SYSCALL: out_errno = errno;
return ErrorCode::SyscallError;
18980 int low_err = ssl_error;
18981 if (low_err == DOMAIN_NAME_MISMATCH) {
18982 return ErrorCode::HostnameMismatch;
18986 long vr = wolfSSL_get_verify_result(ssl);
18987 if (vr != 0) {
return ErrorCode::CertVerifyFailed; }
18989 return ErrorCode::Fatal;
18994inline WolfSSLContext::WolfSSLContext() { wolfSSL_Init(); }
18996inline WolfSSLContext::~WolfSSLContext() {
18997 if (ctx) { wolfSSL_CTX_free(ctx); }
19001inline std::string &wolfssl_pending_sni() {
19002 static thread_local std::string sni;
19007inline int wolfssl_sni_callback(WOLFSSL *ssl,
int *
ret,
void *exArg) {
19011 void *name_data =
nullptr;
19012 unsigned short name_len =
19013 wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, &name_data);
19015 if (name_data && name_len > 0) {
19016 wolfssl_pending_sni().assign(
static_cast<const char *
>(name_data),
19019 wolfssl_pending_sni().clear();
19025inline int wolfssl_verify_callback(
int preverify_ok,
19026 WOLFSSL_X509_STORE_CTX *x509_ctx) {
19027 auto &callback = get_verify_callback();
19028 if (!callback) {
return preverify_ok; }
19030 WOLFSSL_X509 *cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
19031 int depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
19032 int err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
19035 WOLFSSL *ssl =
static_cast<WOLFSSL *
>(wolfSSL_X509_STORE_CTX_get_ex_data(
19036 x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx()));
19038 VerifyContext verify_ctx;
19039 verify_ctx.session =
static_cast<session_t
>(ssl);
19040 verify_ctx.cert =
static_cast<cert_t
>(cert);
19041 verify_ctx.depth = depth;
19042 verify_ctx.preverify_ok = (preverify_ok != 0);
19043 verify_ctx.error_code =
static_cast<long>(err);
19046 verify_ctx.error_string = wolfSSL_X509_verify_cert_error_string(err);
19048 verify_ctx.error_string =
nullptr;
19051 bool accepted = callback(verify_ctx);
19052 return accepted ? 1 : 0;
19055inline void set_wolfssl_password_cb(WOLFSSL_CTX *ctx,
const char *password) {
19056 wolfSSL_CTX_set_default_passwd_cb_userdata(ctx,
const_cast<char *
>(password));
19057 wolfSSL_CTX_set_default_passwd_cb(
19058 ctx, [](
char *buf,
int size,
int ,
void *userdata) ->
int {
19059 auto *pwd =
static_cast<const char *
>(userdata);
19060 if (!pwd)
return 0;
19061 auto len =
static_cast<int>(strlen(pwd));
19063 memcpy(buf, pwd,
static_cast<size_t>(len));
19070inline ctx_t create_client_context() {
19071 auto ctx =
new (std::nothrow) impl::WolfSSLContext();
19072 if (!ctx) {
return nullptr; }
19074 ctx->is_server =
false;
19076 WOLFSSL_METHOD *method = wolfTLSv1_2_client_method();
19082 ctx->ctx = wolfSSL_CTX_new(method);
19089 wolfSSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_PEER,
nullptr);
19091 return static_cast<ctx_t
>(ctx);
19094inline ctx_t create_server_context() {
19095 auto ctx =
new (std::nothrow) impl::WolfSSLContext();
19096 if (!ctx) {
return nullptr; }
19098 ctx->is_server =
true;
19100 WOLFSSL_METHOD *method = wolfTLSv1_2_server_method();
19106 ctx->ctx = wolfSSL_CTX_new(method);
19113 wolfSSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_NONE,
nullptr);
19116 wolfSSL_CTX_SNI_SetOptions(ctx->ctx, WOLFSSL_SNI_HOST_NAME,
19117 WOLFSSL_SNI_CONTINUE_ON_MISMATCH);
19118 wolfSSL_CTX_set_servername_callback(ctx->ctx, impl::wolfssl_sni_callback);
19120 return static_cast<ctx_t
>(ctx);
19123inline void free_context(ctx_t ctx) {
19124 if (ctx) {
delete static_cast<impl::WolfSSLContext *
>(ctx); }
19127inline bool set_min_version(ctx_t ctx, Version version) {
19128 if (!ctx) {
return false; }
19129 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19131 int min_ver = WOLFSSL_TLSV1_2;
19132 if (version >= Version::TLS1_3) { min_ver = WOLFSSL_TLSV1_3; }
19134 return wolfSSL_CTX_SetMinVersion(wctx->ctx, min_ver) == WOLFSSL_SUCCESS;
19137inline bool load_ca_pem(ctx_t ctx,
const char *pem,
size_t len) {
19138 if (!ctx || !pem) {
return false; }
19139 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19141 int ret = wolfSSL_CTX_load_verify_buffer(
19142 wctx->ctx,
reinterpret_cast<const unsigned char *
>(pem),
19143 static_cast<long>(len), SSL_FILETYPE_PEM);
19144 if (
ret != SSL_SUCCESS) {
19145 impl::wolfssl_last_error() =
19146 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19149 wctx->ca_pem_data_.append(pem, len);
19153inline bool load_ca_file(ctx_t ctx,
const char *file_path) {
19154 if (!ctx || !file_path) {
return false; }
19155 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19157 int ret = wolfSSL_CTX_load_verify_locations(wctx->ctx, file_path,
nullptr);
19158 if (
ret != SSL_SUCCESS) {
19159 impl::wolfssl_last_error() =
19160 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19166inline bool load_ca_dir(ctx_t ctx,
const char *dir_path) {
19167 if (!ctx || !dir_path) {
return false; }
19168 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19170 int ret = wolfSSL_CTX_load_verify_locations(wctx->ctx,
nullptr, dir_path);
19179inline bool load_system_certs(ctx_t ctx) {
19180 if (!ctx) {
return false; }
19181 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19182 bool loaded =
false;
19185 loaded = impl::enumerate_windows_system_certs(
19186 [&](
const unsigned char *
data,
size_t len) {
19187 return wolfSSL_CTX_load_verify_buffer(wctx->ctx,
data,
19188 static_cast<long>(len),
19189 SSL_FILETYPE_ASN1) == SSL_SUCCESS;
19191#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
19192 loaded = impl::enumerate_macos_keychain_certs(
19193 [&](
const unsigned char *
data,
size_t len) {
19194 return wolfSSL_CTX_load_verify_buffer(wctx->ctx,
data,
19195 static_cast<long>(len),
19196 SSL_FILETYPE_ASN1) == SSL_SUCCESS;
19199 for (
auto path = impl::system_ca_paths(); *path; ++path) {
19200 if (wolfSSL_CTX_load_verify_locations(wctx->ctx, *path,
nullptr) ==
19208 for (
auto dir = impl::system_ca_dirs(); *dir; ++dir) {
19209 if (wolfSSL_CTX_load_verify_locations(wctx->ctx,
nullptr, *dir) ==
19221inline bool set_client_cert_pem(ctx_t ctx,
const char *cert,
const char *key,
19222 const char *password) {
19223 if (!ctx || !cert || !key) {
return false; }
19224 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19227 int ret = wolfSSL_CTX_use_certificate_buffer(
19228 wctx->ctx,
reinterpret_cast<const unsigned char *
>(cert),
19229 static_cast<long>(strlen(cert)), SSL_FILETYPE_PEM);
19230 if (
ret != SSL_SUCCESS) {
19231 impl::wolfssl_last_error() =
19232 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19237 if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19240 ret = wolfSSL_CTX_use_PrivateKey_buffer(
19241 wctx->ctx,
reinterpret_cast<const unsigned char *
>(key),
19242 static_cast<long>(strlen(key)), SSL_FILETYPE_PEM);
19243 if (
ret != SSL_SUCCESS) {
19244 impl::wolfssl_last_error() =
19245 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19250 return wolfSSL_CTX_check_private_key(wctx->ctx) == SSL_SUCCESS;
19253inline bool set_client_cert_file(ctx_t ctx,
const char *cert_path,
19254 const char *key_path,
const char *password) {
19255 if (!ctx || !cert_path || !key_path) {
return false; }
19256 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19260 wolfSSL_CTX_use_certificate_file(wctx->ctx, cert_path, SSL_FILETYPE_PEM);
19261 if (
ret != SSL_SUCCESS) {
19262 impl::wolfssl_last_error() =
19263 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19268 if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19271 ret = wolfSSL_CTX_use_PrivateKey_file(wctx->ctx, key_path, SSL_FILETYPE_PEM);
19272 if (
ret != SSL_SUCCESS) {
19273 impl::wolfssl_last_error() =
19274 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19279 return wolfSSL_CTX_check_private_key(wctx->ctx) == SSL_SUCCESS;
19282inline void set_verify_client(ctx_t ctx,
bool require) {
19283 if (!ctx) {
return; }
19284 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19285 wctx->verify_client = require;
19287 wolfSSL_CTX_set_verify(
19288 wctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
19289 wctx->has_verify_callback ? impl::wolfssl_verify_callback :
nullptr);
19291 if (wctx->has_verify_callback) {
19292 wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_PEER,
19293 impl::wolfssl_verify_callback);
19295 wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_NONE,
nullptr);
19300inline session_t create_session(ctx_t ctx,
socket_t sock) {
19302 auto wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19304 auto session =
new (std::nothrow) impl::WolfSSLSession();
19305 if (!session) {
return nullptr; }
19307 session->sock = sock;
19308 session->ssl = wolfSSL_new(wctx->ctx);
19309 if (!session->ssl) {
19310 impl::wolfssl_last_error() =
19311 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19316 wolfSSL_set_fd(session->ssl,
static_cast<int>(sock));
19318 return static_cast<session_t
>(session);
19321inline void free_session(session_t session) {
19322 if (session) {
delete static_cast<impl::WolfSSLSession *
>(session); }
19325inline bool set_sni(session_t session,
const char *hostname) {
19326 if (!session || !hostname) {
return false; }
19327 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19329 int ret = wolfSSL_UseSNI(wsession->ssl, WOLFSSL_SNI_HOST_NAME, hostname,
19330 static_cast<word16
>(strlen(hostname)));
19331 if (
ret != WOLFSSL_SUCCESS) {
19332 impl::wolfssl_last_error() =
19333 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19338 wolfSSL_check_domain_name(wsession->ssl, hostname);
19340 wsession->hostname = hostname;
19344inline bool set_hostname(session_t session,
const char *hostname) {
19346 return set_sni(session, hostname);
19349inline TlsError connect(session_t session) {
19352 err.code = ErrorCode::Fatal;
19356 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19357 int ret = wolfSSL_connect(wsession->ssl);
19359 if (
ret == SSL_SUCCESS) {
19360 err.code = ErrorCode::Success;
19362 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19363 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19364 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19365 impl::wolfssl_last_error() = err.backend_code;
19371inline TlsError accept(session_t session) {
19374 err.code = ErrorCode::Fatal;
19378 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19379 int ret = wolfSSL_accept(wsession->ssl);
19381 if (
ret == SSL_SUCCESS) {
19382 err.code = ErrorCode::Success;
19384 wsession->sni_hostname = std::move(impl::wolfssl_pending_sni());
19385 impl::wolfssl_pending_sni().clear();
19387 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19388 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19389 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19390 impl::wolfssl_last_error() = err.backend_code;
19396inline bool connect_nonblocking(session_t session,
socket_t sock,
19397 time_t timeout_sec, time_t timeout_usec,
19400 if (err) { err->code = ErrorCode::Fatal; }
19404 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19407 detail::set_nonblocking(sock,
true);
19409 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
19412 while ((
ret = wolfSSL_connect(wsession->ssl)) != SSL_SUCCESS) {
19413 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19414 if (ssl_error == SSL_ERROR_WANT_READ) {
19415 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
19418 }
else if (ssl_error == SSL_ERROR_WANT_WRITE) {
19419 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
19427 impl::map_wolfssl_error(wsession->ssl, ssl_error, err->sys_errno);
19428 err->backend_code =
static_cast<uint64_t
>(ssl_error);
19430 impl::wolfssl_last_error() =
static_cast<uint64_t
>(ssl_error);
19434 if (err) { err->code = ErrorCode::Success; }
19438inline bool accept_nonblocking(session_t session,
socket_t sock,
19439 time_t timeout_sec, time_t timeout_usec,
19442 if (err) { err->code = ErrorCode::Fatal; }
19446 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19449 detail::set_nonblocking(sock,
true);
19451 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
19454 while ((
ret = wolfSSL_accept(wsession->ssl)) != SSL_SUCCESS) {
19455 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19456 if (ssl_error == SSL_ERROR_WANT_READ) {
19457 if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
19460 }
else if (ssl_error == SSL_ERROR_WANT_WRITE) {
19461 if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
19469 impl::map_wolfssl_error(wsession->ssl, ssl_error, err->sys_errno);
19470 err->backend_code =
static_cast<uint64_t
>(ssl_error);
19472 impl::wolfssl_last_error() =
static_cast<uint64_t
>(ssl_error);
19476 if (err) { err->code = ErrorCode::Success; }
19479 wsession->sni_hostname = std::move(impl::wolfssl_pending_sni());
19480 impl::wolfssl_pending_sni().clear();
19485inline ssize_t read(session_t session,
void *buf,
size_t len, TlsError &err) {
19486 if (!session || !buf) {
19487 err.code = ErrorCode::Fatal;
19491 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19492 int ret = wolfSSL_read(wsession->ssl, buf,
static_cast<int>(len));
19495 err.code = ErrorCode::Success;
19496 return static_cast<ssize_t
>(
ret);
19500 err.code = ErrorCode::PeerClosed;
19504 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19505 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19506 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19507 impl::wolfssl_last_error() = err.backend_code;
19511inline ssize_t write(session_t session,
const void *buf,
size_t len,
19513 if (!session || !buf) {
19514 err.code = ErrorCode::Fatal;
19518 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19519 int ret = wolfSSL_write(wsession->ssl, buf,
static_cast<int>(len));
19522 err.code = ErrorCode::Success;
19523 return static_cast<ssize_t
>(
ret);
19530 err.code = ErrorCode::PeerClosed;
19534 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19535 err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19536 err.backend_code =
static_cast<uint64_t
>(ssl_error);
19537 impl::wolfssl_last_error() = err.backend_code;
19541inline int pending(const_session_t session) {
19542 if (!session) {
return 0; }
19544 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
19545 return wolfSSL_pending(wsession->ssl);
19548inline void shutdown(session_t session,
bool graceful) {
19549 if (!session) {
return; }
19550 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19555 while ((
ret = wolfSSL_shutdown(wsession->ssl)) != SSL_SUCCESS &&
19557 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19558 if (ssl_error != SSL_ERROR_WANT_READ &&
19559 ssl_error != SSL_ERROR_WANT_WRITE) {
19565 wolfSSL_shutdown(wsession->ssl);
19569inline bool is_peer_closed(session_t session,
socket_t sock) {
19571 auto wsession =
static_cast<impl::WolfSSLSession *
>(session);
19574 if (wolfSSL_pending(wsession->ssl) > 0) {
return false; }
19577 detail::set_nonblocking(sock,
true);
19579 detail::scope_exit([&]() { detail::set_nonblocking(sock,
false); });
19583 int ret = wolfSSL_peek(wsession->ssl, &buf, 1);
19586 if (
ret > 0) {
return false; }
19588 int ssl_error = wolfSSL_get_error(wsession->ssl,
ret);
19589 if (ssl_error == SSL_ERROR_WANT_READ) {
return false; }
19591 return ssl_error == SSL_ERROR_ZERO_RETURN || ssl_error == SSL_ERROR_SYSCALL ||
19595inline cert_t get_peer_cert(const_session_t session) {
19596 if (!session) {
return nullptr; }
19598 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
19600 WOLFSSL_X509 *cert = wolfSSL_get_peer_certificate(wsession->ssl);
19601 return static_cast<cert_t
>(cert);
19604inline void free_cert(cert_t cert) {
19605 if (cert) { wolfSSL_X509_free(
static_cast<WOLFSSL_X509 *
>(cert)); }
19608inline bool verify_hostname(cert_t cert,
const char *hostname) {
19609 if (!cert || !hostname) {
return false; }
19610 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19611 std::string host_str(hostname);
19614 bool is_ip = impl::is_ipv4_address(host_str);
19615 unsigned char ip_bytes[4];
19616 if (is_ip) { impl::parse_ipv4(host_str, ip_bytes); }
19619 auto *san_names =
static_cast<WOLF_STACK_OF(WOLFSSL_GENERAL_NAME) *
>(
19620 wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name,
nullptr,
nullptr));
19623 int san_count = wolfSSL_sk_num(san_names);
19624 for (
int i = 0; i < san_count; i++) {
19626 static_cast<WOLFSSL_GENERAL_NAME *
>(wolfSSL_sk_value(san_names, i));
19627 if (!names)
continue;
19629 if (!is_ip && names->type == WOLFSSL_GEN_DNS) {
19631 unsigned char *dns_name =
nullptr;
19632 int dns_len = wolfSSL_ASN1_STRING_to_UTF8(&dns_name, names->d.dNSName);
19633 if (dns_name && dns_len > 0) {
19634 std::string san_name(
reinterpret_cast<char *
>(dns_name),
19635 static_cast<size_t>(dns_len));
19636 XFREE(dns_name,
nullptr, DYNAMIC_TYPE_OPENSSL);
19637 if (detail::match_hostname(san_name, host_str)) {
19638 wolfSSL_sk_free(san_names);
19642 }
else if (is_ip && names->type == WOLFSSL_GEN_IPADD) {
19644 unsigned char *ip_data = wolfSSL_ASN1_STRING_data(names->d.iPAddress);
19645 int ip_len = wolfSSL_ASN1_STRING_length(names->d.iPAddress);
19646 if (ip_data && ip_len == 4 && memcmp(ip_data, ip_bytes, 4) == 0) {
19647 wolfSSL_sk_free(san_names);
19652 wolfSSL_sk_free(san_names);
19656 WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19659 int cn_len = wolfSSL_X509_NAME_get_text_by_NID(subject, NID_commonName, cn,
19662 std::string cn_str(cn,
static_cast<size_t>(cn_len));
19663 if (detail::match_hostname(cn_str, host_str)) {
return true; }
19670inline uint64_t hostname_mismatch_code() {
19671 return static_cast<uint64_t
>(DOMAIN_NAME_MISMATCH);
19674inline long get_verify_result(const_session_t session) {
19675 if (!session) {
return -1; }
19677 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
19678 long result = wolfSSL_get_verify_result(wsession->ssl);
19682inline std::string get_cert_subject_cn(cert_t cert) {
19683 if (!cert)
return "";
19684 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19686 WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19687 if (!subject)
return "";
19690 int cn_len = wolfSSL_X509_NAME_get_text_by_NID(subject, NID_commonName, cn,
19692 if (cn_len <= 0)
return "";
19693 return std::string(cn,
static_cast<size_t>(cn_len));
19696inline std::string get_cert_issuer_name(cert_t cert) {
19697 if (!cert)
return "";
19698 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19700 WOLFSSL_X509_NAME *issuer = wolfSSL_X509_get_issuer_name(x509);
19701 if (!issuer)
return "";
19703 char *name_str = wolfSSL_X509_NAME_oneline(issuer,
nullptr, 0);
19704 if (!name_str)
return "";
19706 std::string result(name_str);
19707 XFREE(name_str,
nullptr, DYNAMIC_TYPE_OPENSSL);
19711inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
19713 if (!cert)
return false;
19714 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19716 auto *san_names =
static_cast<WOLF_STACK_OF(WOLFSSL_GENERAL_NAME) *
>(
19717 wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name,
nullptr,
nullptr));
19718 if (!san_names)
return true;
19720 int count = wolfSSL_sk_num(san_names);
19721 for (
int i = 0; i <
count; i++) {
19723 static_cast<WOLFSSL_GENERAL_NAME *
>(wolfSSL_sk_value(san_names, i));
19724 if (!
name)
continue;
19727 switch (
name->type) {
19728 case WOLFSSL_GEN_DNS: {
19729 entry.type = SanType::DNS;
19730 unsigned char *dns_name =
nullptr;
19731 int dns_len = wolfSSL_ASN1_STRING_to_UTF8(&dns_name,
name->d.dNSName);
19732 if (dns_name && dns_len > 0) {
19733 entry.value = std::string(
reinterpret_cast<char *
>(dns_name),
19734 static_cast<size_t>(dns_len));
19735 XFREE(dns_name,
nullptr, DYNAMIC_TYPE_OPENSSL);
19739 case WOLFSSL_GEN_IPADD: {
19740 entry.type = SanType::IP;
19741 unsigned char *ip_data = wolfSSL_ASN1_STRING_data(
name->d.iPAddress);
19742 int ip_len = wolfSSL_ASN1_STRING_length(
name->d.iPAddress);
19743 if (ip_data && ip_len == 4) {
19745 snprintf(buf,
sizeof(buf),
"%d.%d.%d.%d", ip_data[0], ip_data[1],
19746 ip_data[2], ip_data[3]);
19748 }
else if (ip_data && ip_len == 16) {
19750 snprintf(buf,
sizeof(buf),
19751 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
19752 "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
19753 ip_data[0], ip_data[1], ip_data[2], ip_data[3], ip_data[4],
19754 ip_data[5], ip_data[6], ip_data[7], ip_data[8], ip_data[9],
19755 ip_data[10], ip_data[11], ip_data[12], ip_data[13],
19756 ip_data[14], ip_data[15]);
19761 case WOLFSSL_GEN_EMAIL:
19762 entry.type = SanType::EMAIL;
19764 unsigned char *email =
nullptr;
19765 int email_len = wolfSSL_ASN1_STRING_to_UTF8(&email,
name->d.rfc822Name);
19766 if (email && email_len > 0) {
19767 entry.value = std::string(
reinterpret_cast<char *
>(email),
19768 static_cast<size_t>(email_len));
19769 XFREE(email,
nullptr, DYNAMIC_TYPE_OPENSSL);
19773 case WOLFSSL_GEN_URI:
19774 entry.type = SanType::URI;
19776 unsigned char *uri =
nullptr;
19777 int uri_len = wolfSSL_ASN1_STRING_to_UTF8(
19778 &uri,
name->d.uniformResourceIdentifier);
19779 if (uri && uri_len > 0) {
19780 entry.value = std::string(
reinterpret_cast<char *
>(uri),
19781 static_cast<size_t>(uri_len));
19782 XFREE(uri,
nullptr, DYNAMIC_TYPE_OPENSSL);
19786 default: entry.type = SanType::OTHER;
break;
19789 if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
19791 wolfSSL_sk_free(san_names);
19795inline bool get_cert_validity(cert_t cert, time_t ¬_before,
19796 time_t ¬_after) {
19797 if (!cert)
return false;
19798 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19800 const WOLFSSL_ASN1_TIME *nb = wolfSSL_X509_get_notBefore(x509);
19801 const WOLFSSL_ASN1_TIME *na = wolfSSL_X509_get_notAfter(x509);
19803 if (!nb || !na)
return false;
19806 struct tm tm_nb = {}, tm_na = {};
19807 if (wolfSSL_ASN1_TIME_to_tm(nb, &tm_nb) != WOLFSSL_SUCCESS)
return false;
19808 if (wolfSSL_ASN1_TIME_to_tm(na, &tm_na) != WOLFSSL_SUCCESS)
return false;
19811 not_before = _mkgmtime(&tm_nb);
19812 not_after = _mkgmtime(&tm_na);
19814 not_before = timegm(&tm_nb);
19815 not_after = timegm(&tm_na);
19820inline std::string get_cert_serial(cert_t cert) {
19821 if (!cert)
return "";
19822 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19824 WOLFSSL_ASN1_INTEGER *serial_asn1 = wolfSSL_X509_get_serialNumber(x509);
19825 if (!serial_asn1)
return "";
19828 int len = serial_asn1->length;
19829 unsigned char *
data = serial_asn1->data;
19830 if (!
data || len <= 0)
return "";
19832 std::string result;
19833 result.reserve(
static_cast<size_t>(len) * 2);
19834 for (
int i = 0; i < len; i++) {
19836 snprintf(hex,
sizeof(hex),
"%02X",
data[i]);
19842inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
19843 if (!cert)
return false;
19844 auto x509 =
static_cast<WOLFSSL_X509 *
>(cert);
19847 const unsigned char *der_data = wolfSSL_X509_get_der(x509, &der_len);
19848 if (!der_data || der_len <= 0)
return false;
19850 der.assign(der_data, der_data + der_len);
19854inline const char *get_sni(const_session_t session) {
19855 if (!session)
return nullptr;
19856 auto wsession =
static_cast<const impl::WolfSSLSession *
>(session);
19859 if (!wsession->sni_hostname.empty()) {
19860 return wsession->sni_hostname.c_str();
19864 if (!wsession->hostname.empty()) {
return wsession->hostname.c_str(); }
19869inline uint64_t peek_error() {
19870 return static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19873inline uint64_t get_error() {
19874 uint64_t err = impl::wolfssl_last_error();
19875 impl::wolfssl_last_error() = 0;
19879inline std::string error_string(uint64_t
code) {
19881 wolfSSL_ERR_error_string(
static_cast<unsigned long>(
code), buf);
19882 return std::string(buf);
19885inline ca_store_t create_ca_store(
const char *pem,
size_t len) {
19886 if (!pem || len == 0) {
return nullptr; }
19888 WOLFSSL_CTX *tmp_ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
19889 if (!tmp_ctx) {
return nullptr; }
19890 int ret = wolfSSL_CTX_load_verify_buffer(
19891 tmp_ctx,
reinterpret_cast<const unsigned char *
>(pem),
19892 static_cast<long>(len), SSL_FILETYPE_PEM);
19893 wolfSSL_CTX_free(tmp_ctx);
19894 if (
ret != SSL_SUCCESS) {
return nullptr; }
19895 return static_cast<ca_store_t
>(
19896 new impl::WolfSSLCAStore{std::string(pem, len)});
19899inline void free_ca_store(ca_store_t
store) {
19900 delete static_cast<impl::WolfSSLCAStore *
>(
store);
19903inline bool set_ca_store(ctx_t ctx, ca_store_t
store) {
19904 if (!ctx || !
store) {
return false; }
19905 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19906 auto *ca =
static_cast<impl::WolfSSLCAStore *
>(
store);
19907 int ret = wolfSSL_CTX_load_verify_buffer(
19908 wctx->ctx,
reinterpret_cast<const unsigned char *
>(ca->pem_data.data()),
19909 static_cast<long>(ca->pem_data.size()), SSL_FILETYPE_PEM);
19910 if (
ret == SSL_SUCCESS) { wctx->ca_pem_data_ += ca->pem_data; }
19911 return ret == SSL_SUCCESS;
19914inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
19916 if (!ctx) {
return 0; }
19917 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19918 if (wctx->ca_pem_data_.empty()) {
return 0; }
19920 const std::string &pem = wctx->ca_pem_data_;
19921 const std::string begin_marker =
"-----BEGIN CERTIFICATE-----";
19922 const std::string end_marker =
"-----END CERTIFICATE-----";
19924 while ((pos = pem.find(begin_marker, pos)) != std::string::npos) {
19925 size_t end_pos = pem.find(end_marker, pos);
19926 if (end_pos == std::string::npos) {
break; }
19927 end_pos += end_marker.size();
19928 std::string cert_pem = pem.substr(pos, end_pos - pos);
19929 WOLFSSL_X509 *x509 = wolfSSL_X509_load_certificate_buffer(
19930 reinterpret_cast<const unsigned char *
>(cert_pem.data()),
19931 static_cast<int>(cert_pem.size()), WOLFSSL_FILETYPE_PEM);
19932 if (x509) { certs.push_back(
static_cast<cert_t
>(x509)); }
19935 return certs.size();
19938inline std::vector<std::string> get_ca_names(ctx_t ctx) {
19939 std::vector<std::string> names;
19940 if (!ctx) {
return names; }
19941 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19942 if (wctx->ca_pem_data_.empty()) {
return names; }
19944 const std::string &pem = wctx->ca_pem_data_;
19945 const std::string begin_marker =
"-----BEGIN CERTIFICATE-----";
19946 const std::string end_marker =
"-----END CERTIFICATE-----";
19948 while ((pos = pem.find(begin_marker, pos)) != std::string::npos) {
19949 size_t end_pos = pem.find(end_marker, pos);
19950 if (end_pos == std::string::npos) {
break; }
19951 end_pos += end_marker.size();
19952 std::string cert_pem = pem.substr(pos, end_pos - pos);
19953 WOLFSSL_X509 *x509 = wolfSSL_X509_load_certificate_buffer(
19954 reinterpret_cast<const unsigned char *
>(cert_pem.data()),
19955 static_cast<int>(cert_pem.size()), WOLFSSL_FILETYPE_PEM);
19957 WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19959 char *name_str = wolfSSL_X509_NAME_oneline(subject,
nullptr, 0);
19961 names.push_back(name_str);
19962 XFREE(name_str,
nullptr, DYNAMIC_TYPE_OPENSSL);
19965 wolfSSL_X509_free(x509);
19972inline bool update_server_cert(ctx_t ctx,
const char *cert_pem,
19973 const char *key_pem,
const char *password) {
19974 if (!ctx || !cert_pem || !key_pem) {
return false; }
19975 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
19978 int ret = wolfSSL_CTX_use_certificate_buffer(
19979 wctx->ctx,
reinterpret_cast<const unsigned char *
>(cert_pem),
19980 static_cast<long>(strlen(cert_pem)), SSL_FILETYPE_PEM);
19981 if (
ret != SSL_SUCCESS) {
19982 impl::wolfssl_last_error() =
19983 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
19988 if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19991 ret = wolfSSL_CTX_use_PrivateKey_buffer(
19992 wctx->ctx,
reinterpret_cast<const unsigned char *
>(key_pem),
19993 static_cast<long>(strlen(key_pem)), SSL_FILETYPE_PEM);
19994 if (
ret != SSL_SUCCESS) {
19995 impl::wolfssl_last_error() =
19996 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
20003inline bool update_server_client_ca(ctx_t ctx,
const char *ca_pem) {
20004 if (!ctx || !ca_pem) {
return false; }
20005 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20007 int ret = wolfSSL_CTX_load_verify_buffer(
20008 wctx->ctx,
reinterpret_cast<const unsigned char *
>(ca_pem),
20009 static_cast<long>(strlen(ca_pem)), SSL_FILETYPE_PEM);
20010 if (
ret != SSL_SUCCESS) {
20011 impl::wolfssl_last_error() =
20012 static_cast<uint64_t
>(wolfSSL_ERR_peek_last_error());
20018inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
20019 if (!ctx) {
return false; }
20020 auto *wctx =
static_cast<impl::WolfSSLContext *
>(ctx);
20022 impl::get_verify_callback() = std::move(callback);
20023 wctx->has_verify_callback =
static_cast<bool>(impl::get_verify_callback());
20025 if (wctx->has_verify_callback) {
20026 wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_PEER,
20027 impl::wolfssl_verify_callback);
20029 wolfSSL_CTX_set_verify(
20031 wctx->verify_client
20032 ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
20039inline long get_verify_error(const_session_t session) {
20040 if (!session) {
return -1; }
20042 static_cast<impl::WolfSSLSession *
>(
const_cast<void *
>(session));
20043 return wolfSSL_get_verify_result(wsession->ssl);
20046inline std::string verify_error_string(
long error_code) {
20047 if (error_code == 0) {
return ""; }
20049 wolfSSL_X509_verify_cert_error_string(
static_cast<int>(error_code));
20060inline bool WebSocket::send_frame(Opcode op,
const char *
data,
size_t len,
20062 std::lock_guard<std::mutex> lock(write_mutex_);
20063 if (closed_) {
return false; }
20064 return detail::write_websocket_frame(strm_, op,
data, len, fin, !is_server_);
20070 std::string payload;
20081 std::lock_guard<std::mutex> lock(write_mutex_);
20083 payload.size(),
true, !is_server_);
20087 std::lock_guard<std::mutex> lock(ping_mutex_);
20088 unacked_pings_ = 0;
20092 if (!closed_.exchange(
true)) {
20094 std::lock_guard<std::mutex> lock(write_mutex_);
20096 payload.size(),
true, !is_server_);
20103 msg = std::move(payload);
20109 std::string cont_payload;
20112 strm_, cont_opcode, cont_payload, cont_fin, is_server_,
20118 std::lock_guard<std::mutex> lock(write_mutex_);
20120 strm_,
Opcode::Pong, cont_payload.data(), cont_payload.size(),
20121 true, !is_server_);
20125 std::lock_guard<std::mutex> lock(ping_mutex_);
20126 unacked_pings_ = 0;
20130 if (!closed_.exchange(
true)) {
20131 std::lock_guard<std::mutex> lock(write_mutex_);
20134 cont_payload.size(),
true, !is_server_);
20143 msg += cont_payload;
20148 if (cont_fin) {
break; }
20158 default: closed_ =
true;
return Fail;
20173 if (closed_.exchange(
true)) {
return; }
20174 ping_cv_.notify_all();
20175 std::string payload;
20176 auto code =
static_cast<uint16_t
>(status);
20177 payload.push_back(
static_cast<char>((
code >> 8) & 0xFF));
20178 payload.push_back(
static_cast<char>(
code & 0xFF));
20181 payload += reason.substr(0, 123);
20183 std::lock_guard<std::mutex> lock(write_mutex_);
20185 payload.size(),
true, !is_server_);
20202 std::lock_guard<std::mutex> lock(ping_mutex_);
20205 ping_cv_.notify_all();
20206 if (ping_thread_.joinable()) { ping_thread_.join(); }
20209inline void WebSocket::start_heartbeat() {
20210 if (ping_interval_sec_ == 0) {
return; }
20211 ping_thread_ = std::thread([
this]() {
20212 std::unique_lock<std::mutex> lock(ping_mutex_);
20214 ping_cv_.wait_for(lock, std::chrono::seconds(ping_interval_sec_));
20215 if (closed_) {
break; }
20219 if (max_missed_pongs_ > 0 && unacked_pings_ >= max_missed_pongs_) {
20221 close(CloseStatus::GoingAway,
"pong timeout");
20225 if (!send_frame(Opcode::Ping,
nullptr, 0)) {
20242 const std::string &scheme_host_port_path,
const Headers &headers)
20243 : headers_(headers) {
20246 !uc.
host.empty() && !uc.
path.empty()) {
20247 auto &scheme = uc.scheme;
20249#ifdef CPPHTTPLIB_SSL_ENABLED
20250 if (scheme !=
"ws" && scheme !=
"wss") {
20252 if (scheme !=
"ws") {
20254#ifndef CPPHTTPLIB_NO_EXCEPTIONS
20255 std::string msg =
"'" + scheme +
"' scheme is not supported.";
20256 throw std::invalid_argument(msg);
20261 auto is_ssl = scheme ==
"wss";
20263 host_ = std::move(uc.host);
20265 port_ = is_ssl ? 443 : 80;
20266 if (!uc.port.empty() && !detail::parse_port(uc.port, port_)) { return; }
20268 path_ = std::move(uc.path);
20269 if (!uc.query.empty()) { path_ += uc.query; }
20271#ifdef CPPHTTPLIB_SSL_ENABLED
20274 if (is_ssl) { return; }
20285inline void WebSocketClient::shutdown_and_close() {
20286#ifdef CPPHTTPLIB_SSL_ENABLED
20288 if (tls_session_) {
20289 tls::shutdown(tls_session_,
true);
20290 tls::free_session(tls_session_);
20291 tls_session_ =
nullptr;
20294 tls::free_context(tls_ctx_);
20295 tls_ctx_ =
nullptr;
20299 if (ws_ && ws_->is_open()) { ws_->close(); }
20308inline bool WebSocketClient::create_stream(std::unique_ptr<Stream> &strm) {
20309#ifdef CPPHTTPLIB_SSL_ENABLED
20311 if (!detail::setup_client_tls_session(
20312 host_, tls_ctx_, tls_session_, sock_,
20313 server_certificate_verification_, ca_cert_file_path_,
20314 ca_cert_store_, read_timeout_sec_, read_timeout_usec_)) {
20318 strm = std::unique_ptr<Stream>(
new detail::SSLSocketStream(
20319 sock_, tls_session_, read_timeout_sec_, read_timeout_usec_,
20320 write_timeout_sec_, write_timeout_usec_));
20324 strm = std::unique_ptr<Stream>(
20325 new detail::SocketStream(sock_, read_timeout_sec_, read_timeout_usec_,
20326 write_timeout_sec_, write_timeout_usec_));
20331 if (!is_valid_) {
return false; }
20332 shutdown_and_close();
20336 auto it = addr_map_.find(host_);
20337 if (it != addr_map_.end()) { ip = it->second; }
20341 host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
20342 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
20343 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
20344 write_timeout_usec_, interface_,
error);
20348 std::unique_ptr<Stream> strm;
20349 if (!create_stream(strm)) {
20350 shutdown_and_close();
20354 std::string selected_subprotocol;
20356 selected_subprotocol)) {
20357 shutdown_and_close();
20360 subprotocol_ = std::move(selected_subprotocol);
20365 ws_ = std::unique_ptr<WebSocket>(
new WebSocket(std::move(strm), req,
false,
20366 websocket_ping_interval_sec_,
20367 websocket_max_missed_pongs_));
20372 if (!ws_) {
return Fail; }
20373 return ws_->read(msg);
20377 if (!ws_) {
return false; }
20378 return ws_->send(
data);
20382 if (!ws_) {
return false; }
20383 return ws_->send(
data, len);
20387 const std::string &reason) {
20388 if (ws_) { ws_->close(status, reason); }
20394 return subprotocol_;
20398 read_timeout_sec_ = sec;
20399 read_timeout_usec_ = usec;
20403 write_timeout_sec_ = sec;
20404 write_timeout_usec_ = usec;
20408 websocket_ping_interval_sec_ = sec;
20412 websocket_max_missed_pongs_ = count;
20418 address_family_ = family;
20424 socket_options_ = std::move(socket_options);
20428 connection_timeout_sec_ = sec;
20429 connection_timeout_usec_ = usec;
20437 std::map<std::string, std::string> addr_map) {
20438 addr_map_ = std::move(addr_map);
20441#ifdef CPPHTTPLIB_SSL_ENABLED
20443inline void WebSocketClient::set_ca_cert_path(
const std::string &path) {
20444 ca_cert_file_path_ = path;
20447inline void WebSocketClient::set_ca_cert_store(tls::ca_store_t
store) {
20448 ca_cert_store_ =
store;
20452WebSocketClient::enable_server_certificate_verification(
bool enabled) {
20453 server_certificate_verification_ = enabled;