394template <
class T,
class... Args>
395typename std::enable_if < !std::is_array<T>::value, std::unique_ptr<T >>
::type
398 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
402typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>
::type
405 typedef typename std::remove_extent<T>::type RT;
406 return std::unique_ptr<T>(
new RT[n]);
414 const static unsigned char table[256] =
416 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
417 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
418 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
419 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
420 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
421 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
422 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
423 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
424 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
425 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
426 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
427 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
428 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
429 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
430 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
431 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
432 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
435 return table[(
unsigned char)(
char)c];
438inline bool equal(
const std::string &a,
const std::string &b)
440 return a.size() == b.size() &&
441 std::equal(a.begin(), a.end(), b.begin(), [](
char ca,
char cb)
443 return to_lower(ca) == to_lower(cb);
449 bool operator()(
const std::string &a,
const std::string &b)
const
459 return hash_core(key.data(), key.size(), 0);
462 size_t hash_core(
const char *s,
size_t l,
size_t h)
const
468 (((std::numeric_limits<size_t>::max)() >> 6) &
470 static_cast<unsigned char>(
to_lower(*s)));
486 : exit_function(
std::move(f)), execute_on_destruction{true} {}
489 : exit_function(std::move(rhs.exit_function)),
490 execute_on_destruction{rhs.execute_on_destruction}
497 if (execute_on_destruction)
499 this->exit_function();
505 this->execute_on_destruction =
false;
513 std::function<void(
void)> exit_function;
514 bool execute_on_destruction;
609using Params = std::multimap<std::string, std::string>;
643 std::string
get_field(
const std::string &key,
size_t id = 0)
const;
644 std::vector<std::string>
get_fields(
const std::string &key)
const;
645 bool has_field(
const std::string &key)
const;
650 std::vector<FormData>
get_files(
const std::string &key)
const;
651 bool has_file(
const std::string &key)
const;
674 std::function<bool(
const char *data,
size_t data_len)>
write;
681 class data_sink_streambuf final :
public std::streambuf
684 explicit data_sink_streambuf(
DataSink &sink) : sink_(sink) {}
687 std::streamsize xsputn(
const char *s, std::streamsize n)
override
689 sink_.write(s,
static_cast<size_t>(n));
697 data_sink_streambuf sb_;
701 std::function<bool(
size_t offset,
size_t length,
DataSink &sink)>;
704 std::function<bool(
size_t offset,
DataSink &sink)>;
718 const char *data,
size_t data_length,
size_t offset,
size_t total_length)>;
721 std::function<bool(
const char *data,
size_t data_length)>;
750using Range = std::pair<ssize_t, ssize_t>;
786#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
787 const SSL *ssl =
nullptr;
790 bool has_header(
const std::string &key)
const;
792 size_t id = 0)
const;
794 size_t id = 0)
const;
796 void set_header(
const std::string &key,
const std::string &val);
802 bool has_param(
const std::string &key)
const;
803 std::string
get_param_value(
const std::string &key,
size_t id = 0)
const;
815 (std::chrono::steady_clock::time_point::min)();
828 bool has_header(
const std::string &key)
const;
830 size_t id = 0)
const;
832 size_t id = 0)
const;
834 void set_header(
const std::string &key,
const std::string &val);
841 void set_content(
const char *s,
size_t n,
const std::string &content_type);
842 void set_content(
const std::string &s,
const std::string &content_type);
843 void set_content(std::string &&s,
const std::string &content_type);
846 size_t length,
const std::string &content_type,
ContentProvider provider,
858 const std::string &content_type);
938 virtual ssize_t
read(
char *ptr,
size_t size) = 0;
939 virtual ssize_t
write(
const char *ptr,
size_t size) = 0;
946 ssize_t
write(
const char *ptr);
947 ssize_t
write(
const std::string &s);
964 virtual bool enqueue(std::function<
void()> fn) = 0;
974 : shutdown_(false), max_queued_requests_(mqr)
979 threads_.emplace_back(
worker(*
this));
987 bool enqueue(std::function<
void()> fn)
override
990 std::unique_lock<std::mutex> lock(mutex_);
991 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_)
995 jobs_.push_back(std::move(fn));
1006 std::unique_lock<std::mutex> lock(mutex_);
1013 for (
auto &t : threads_)
1022 explicit worker(
ThreadPool &pool) : pool_(pool) {}
1028 std::function<void()> fn;
1030 std::unique_lock<std::mutex> lock(pool_.mutex_);
1033 lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
1035 if (pool_.shutdown_ && pool_.jobs_.empty())
1040 fn = pool_.jobs_.front();
1041 pool_.jobs_.pop_front();
1044 assert(
true ==
static_cast<bool>(fn));
1048#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
1049 !defined(LIBRESSL_VERSION_NUMBER)
1050 OPENSSL_thread_stop();
1058 std::vector<std::thread> threads_;
1059 std::list<std::function<void()>> jobs_;
1062 size_t max_queued_requests_ = 0;
1064 std::condition_variable cond_;
1104 std::string pattern_;
1136 static constexpr char separator =
'/';
1141 std::vector<std::string> static_fragments_;
1144 std::vector<std::string> param_names_;
1179 std::function<void(
const Request &,
Response &, std::exception_ptr ep)>;
1213 const std::string &mount_point = std::string());
1214 bool set_mount_point(
const std::string &mount_point,
const std::string &dir,
1218 const std::string &mime);
1222 template <
class ErrorHandlerFunc>
1225 return set_error_handler_core(
1226 std::forward<ErrorHandlerFunc>(handler),
1227 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse> {});
1257 template <
class Rep,
class Period>
1261 template <
class Rep,
class Period>
1265 template <
class Rep,
class Period>
1270 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
1274 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
1285 int remote_port,
const std::string &local_addr,
1286 int local_port,
bool close_connection,
1287 bool &connection_closed,
1288 const std::function<
void(
Request &)> &setup_request);
1306 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
Handler>>;
1307 using HandlersForContentReader =
1308 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1311 static std::unique_ptr<detail::MatcherBase>
1312 make_matcher(
const std::string &pattern);
1315 Server &set_error_handler_core(
Handler handler, std::false_type);
1317 socket_t create_server_socket(
const std::string &host,
int port,
1320 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1321 bool listen_internal();
1326 const std::string &etag, time_t mtime)
const;
1327 bool check_if_range(
Request &req,
const std::string &etag,
1328 time_t mtime)
const;
1330 const Handlers &handlers)
const;
1331 bool dispatch_request_for_content_reader(
1333 const HandlersForContentReader &handlers)
const;
1335 bool parse_request_line(
const char *s,
Request &req)
const;
1337 std::string &content_type, std::string &boundary)
const;
1338 bool write_response(
Stream &strm,
bool close_connection,
Request &req,
1340 bool write_response_with_content(
Stream &strm,
bool close_connection,
1342 bool write_response_core(
Stream &strm,
bool close_connection,
1344 bool need_apply_ranges);
1345 bool write_content_with_provider(
Stream &strm,
const Request &req,
1346 Response &res,
const std::string &boundary,
1347 const std::string &content_type);
1349 bool read_content_with_content_receiver(
Stream &strm,
Request &req,
1359 virtual bool process_and_close_socket(
socket_t sock);
1362 void output_pre_compression_log(
const Request &req,
1364 void output_error_log(
const Error &err,
const Request *req)
const;
1366 std::atomic<bool> is_running_{
false};
1367 std::atomic<bool> is_decommissioned{
false};
1369 struct MountPointEntry
1371 std::string mount_point;
1372 std::string base_dir;
1375 std::vector<MountPointEntry> base_dirs_;
1376 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1377 std::string default_file_mimetype_ =
"application/octet-stream";
1378 Handler file_request_handler_;
1380 Handlers get_handlers_;
1381 Handlers post_handlers_;
1382 HandlersForContentReader post_handlers_for_content_reader_;
1383 Handlers put_handlers_;
1384 HandlersForContentReader put_handlers_for_content_reader_;
1385 Handlers patch_handlers_;
1386 HandlersForContentReader patch_handlers_for_content_reader_;
1387 Handlers delete_handlers_;
1388 HandlersForContentReader delete_handlers_for_content_reader_;
1389 Handlers options_handlers_;
1394 Handler post_routing_handler_;
1398 mutable std::mutex logger_mutex_;
1400 Logger pre_compression_logger_;
1403 int address_family_ = AF_UNSPEC;
1409 std::function<ssize_t(Stream &,
Headers &)> header_writer_ =
1419 : res_(
std::move(res)), err_(err),
1420 request_headers_(
std::move(request_headers)) {}
1421#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1424 : res_(
std::move(res)), err_(err),
1425 request_headers_(
std::move(request_headers)), ssl_error_(ssl_error) {}
1427 int ssl_error,
unsigned long ssl_openssl_error)
1428 : res_(
std::move(res)), err_(err),
1429 request_headers_(
std::move(request_headers)), ssl_error_(ssl_error),
1430 ssl_openssl_error_(ssl_openssl_error) {}
1433 operator bool()
const
1435 return res_ !=
nullptr;
1439 return res_ ==
nullptr;
1443 return res_ !=
nullptr;
1476#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1478 int ssl_error()
const
1483 unsigned long ssl_openssl_error()
const
1485 return ssl_openssl_error_;
1492 const char *def =
"",
1493 size_t id = 0)
const;
1495 size_t id = 0)
const;
1499 std::unique_ptr<Response> res_;
1502#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1504 unsigned long ssl_openssl_error_ = 0;
1511#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1524#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1543#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1549#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1550 other.ssl =
nullptr;
1559#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1563#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1564 other.ssl =
nullptr;
1574struct ChunkedDecoder;
1586 ssize_t
read(
char *buf,
size_t len);
1597 return br.
read(buf, len);
1612 const std::string &client_cert_path,
1613 const std::string &client_key_path);
1636 ssize_t
read(
char *buf,
size_t len);
1652 ssize_t read_with_decompression(
char *buf,
size_t len);
1654 std::unique_ptr<ClientConnection> connection_;
1655 std::unique_ptr<Stream> socket_stream_;
1656 Stream *stream_ =
nullptr;
1659 std::unique_ptr<detail::decompressor> decompressor_;
1660 std::string decompress_buffer_;
1661 size_t decompress_offset_ = 0;
1684 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1686 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
1689 const std::string &content_type,
UploadProgress progress =
nullptr);
1699 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1700 const std::string &content_type,
UploadProgress progress =
nullptr);
1701 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1704 const std::string &content_type,
UploadProgress progress =
nullptr);
1708 const std::string &content_type,
UploadProgress progress =
nullptr);
1718 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1722 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1724 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
1727 const std::string &content_type,
UploadProgress progress =
nullptr);
1737 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1738 const std::string &content_type,
UploadProgress progress =
nullptr);
1739 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1742 const std::string &content_type,
UploadProgress progress =
nullptr);
1746 const std::string &content_type,
UploadProgress progress =
nullptr);
1756 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1760 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1762 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
1765 const std::string &content_type,
UploadProgress progress =
nullptr);
1775 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1776 const std::string &content_type,
UploadProgress progress =
nullptr);
1777 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1780 const std::string &content_type,
UploadProgress progress =
nullptr);
1784 const std::string &content_type,
UploadProgress progress =
nullptr);
1794 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1798 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
1800 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type,
1804 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
1806 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
1818 const Params ¶ms = {},
1820 const std::string &body = {},
1821 const std::string &content_type = {});
1823 bool send(Request &req, Response &res,
Error &error);
1824 Result
send(
const Request &req);
1828 std::string
host()
const;
1847 template <
class Rep,
class Period>
1852 template <
class Rep,
class Period>
1856 template <
class Rep,
class Period>
1860 template <
class Rep,
class Period>
1861 void set_max_timeout(
const std::chrono::duration<Rep, Period> &duration);
1863 void set_basic_auth(
const std::string &username,
const std::string &password);
1865#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1866 void set_digest_auth(
const std::string &username,
1867 const std::string &password);
1883 const std::string &password);
1885#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1886 void set_proxy_digest_auth(
const std::string &username,
1887 const std::string &password);
1890#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1891 void set_ca_cert_path(
const std::string &ca_cert_file_path,
1892 const std::string &ca_cert_dir_path = std::string());
1893 void set_ca_cert_store(X509_STORE *ca_cert_store);
1894 X509_STORE *create_ca_cert_store(
const char *ca_cert, std::size_t size)
const;
1897#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1898 void enable_server_certificate_verification(
bool enabled);
1899 void enable_server_hostname_verification(
bool enabled);
1900 void set_server_certificate_verifier(
1911#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1936 bool close_connection,
Error &error);
1939 Error &error)
const;
1985#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1986 std::string digest_auth_username_;
1987 std::string digest_auth_password_;
2011#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2012 std::string proxy_digest_auth_username_;
2013 std::string proxy_digest_auth_password_;
2016#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2017 std::string ca_cert_file_path_;
2018 std::string ca_cert_dir_path_;
2020 X509_STORE *ca_cert_store_ =
nullptr;
2023#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2024 bool server_certificate_verification_ =
true;
2025 bool server_hostname_verification_ =
true;
2033#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2034 int last_ssl_error_ = 0;
2035 unsigned long last_openssl_error_ = 0;
2045 bool write_request(
Stream &strm,
Request &req,
bool close_connection,
2047 void prepare_default_headers(
Request &r,
bool for_stream,
2048 const std::string &ct);
2050 bool create_redirect_client(
const std::string &scheme,
2052 Response &res,
const std::string &path,
2053 const std::string &location,
Error &error);
2054 template <
typename ClientType>
void setup_redirect_client(ClientType &client);
2056 bool close_connection,
Error &error);
2057 std::unique_ptr<Response> send_with_content_provider_and_receiver(
2058 Request &req,
const char *body,
size_t content_length,
2063 Result send_with_content_provider_and_receiver(
2064 const std::string &method,
const std::string &path,
2065 const Headers &headers,
const char *body,
size_t content_length,
2076 std::chrono::time_point<std::chrono::steady_clock> start_time,
2077 std::function<
bool(
Stream &strm)> callback);
2078 virtual bool is_ssl()
const;
2080 void transfer_socket_ownership_to_handle(
StreamHandle &handle);
2087 explicit Client(
const std::string &scheme_host_port);
2089 explicit Client(
const std::string &scheme_host_port,
2090 const std::string &client_cert_path,
2091 const std::string &client_key_path);
2097 const std::string &client_cert_path,
2098 const std::string &client_key_path);
2127 Result Post(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
2129 Result Post(
const std::string &path,
const std::string &body,
const std::string &content_type,
2132 const std::string &content_type,
UploadProgress progress =
nullptr);
2142 Result Post(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
2143 const std::string &content_type,
UploadProgress progress =
nullptr);
2144 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2147 const std::string &content_type,
UploadProgress progress =
nullptr);
2151 const std::string &content_type,
UploadProgress progress =
nullptr);
2161 Result Post(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2165 Result Put(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
2167 Result Put(
const std::string &path,
const std::string &body,
const std::string &content_type,
2170 const std::string &content_type,
UploadProgress progress =
nullptr);
2180 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
2181 const std::string &content_type,
UploadProgress progress =
nullptr);
2182 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2185 const std::string &content_type,
UploadProgress progress =
nullptr);
2189 const std::string &content_type,
UploadProgress progress =
nullptr);
2199 Result Put(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2203 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
2205 Result Patch(
const std::string &path,
const std::string &body,
const std::string &content_type,
2208 const std::string &content_type,
UploadProgress progress =
nullptr);
2218 Result Patch(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
2219 const std::string &content_type,
UploadProgress progress =
nullptr);
2220 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2223 const std::string &content_type,
UploadProgress progress =
nullptr);
2227 const std::string &content_type,
UploadProgress progress =
nullptr);
2237 Result Patch(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2241 Result Delete(
const std::string &path,
const char *body,
size_t content_length,
const std::string &content_type,
2243 Result Delete(
const std::string &path,
const std::string &body,
const std::string &content_type,
2247 Result Delete(
const std::string &path,
const Headers &headers,
const char *body,
size_t content_length,
2249 Result Delete(
const std::string &path,
const Headers &headers,
const std::string &body,
const std::string &content_type,
2261 const std::string &path,
2262 const Params ¶ms = {},
2264 const std::string &body = {},
2265 const std::string &content_type = {});
2267 bool send(Request &req, Response &res,
Error &error);
2268 Result
send(
const Request &req);
2272 std::string
host()
const;
2290 template <
class Rep,
class Period>
2295 template <
class Rep,
class Period>
2299 template <
class Rep,
class Period>
2303 template <
class Rep,
class Period>
2304 void set_max_timeout(
const std::chrono::duration<Rep, Period> &duration);
2306 void set_basic_auth(
const std::string &username,
const std::string &password);
2308#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2309 void set_digest_auth(
const std::string &username,
2310 const std::string &password);
2327 const std::string &password);
2329#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2330 void set_proxy_digest_auth(
const std::string &username,
2331 const std::string &password);
2334#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2335 void enable_server_certificate_verification(
bool enabled);
2336 void enable_server_hostname_verification(
bool enabled);
2337 void set_server_certificate_verifier(
2345#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2346 void set_ca_cert_path(
const std::string &ca_cert_file_path,
2347 const std::string &ca_cert_dir_path = std::string());
2349 void set_ca_cert_store(X509_STORE *ca_cert_store);
2350 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
2352 long get_openssl_verify_result()
const;
2354 SSL_CTX *ssl_context()
const;
2358 std::unique_ptr<ClientImpl> cli_;
2360#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2361 bool is_ssl_ =
false;
2365#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2366class SSLServer :
public Server
2369 SSLServer(
const char *cert_path,
const char *private_key_path,
2370 const char *client_ca_cert_file_path =
nullptr,
2371 const char *client_ca_cert_dir_path =
nullptr,
2372 const char *private_key_password =
nullptr);
2374 SSLServer(X509 *cert, EVP_PKEY *private_key,
2375 X509_STORE *client_ca_cert_store =
nullptr);
2378 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
2380 ~SSLServer()
override;
2382 bool is_valid()
const override;
2384 SSL_CTX *ssl_context()
const;
2386 void update_certs(X509 *cert, EVP_PKEY *private_key,
2387 X509_STORE *client_ca_cert_store =
nullptr);
2389 int ssl_last_error()
const
2391 return last_ssl_error_;
2395 bool process_and_close_socket(
socket_t sock)
override;
2397 STACK_OF(X509_NAME) * extract_ca_names_from_x509_store(X509_STORE *store);
2400 std::mutex ctx_mutex_;
2402 int last_ssl_error_ = 0;
2405class SSLClient final :
public ClientImpl
2408 explicit SSLClient(
const std::string &host);
2410 explicit SSLClient(
const std::string &host,
int port);
2412 explicit SSLClient(
const std::string &host,
int port,
2413 const std::string &client_cert_path,
2414 const std::string &client_key_path,
2415 const std::string &private_key_password = std::string());
2417 explicit SSLClient(
const std::string &host,
int port, X509 *client_cert,
2418 EVP_PKEY *client_key,
2419 const std::string &private_key_password = std::string());
2421 ~SSLClient()
override;
2423 bool is_valid()
const override;
2425 void set_ca_cert_store(X509_STORE *ca_cert_store);
2426 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
2428 long get_openssl_verify_result()
const;
2430 SSL_CTX *ssl_context()
const;
2433 bool create_and_connect_socket(Socket &socket, Error &error)
override;
2434 bool ensure_socket_connection(Socket &socket, Error &error)
override;
2435 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
2436 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
2439 process_socket(
const Socket &socket,
2440 std::chrono::time_point<std::chrono::steady_clock> start_time,
2441 std::function<
bool(Stream &strm)> callback)
override;
2442 bool is_ssl()
const override;
2444 bool connect_with_proxy(
2446 std::chrono::time_point<std::chrono::steady_clock> start_time,
2447 Response &res,
bool &success, Error &error);
2448 bool initialize_ssl(Socket &socket, Error &error);
2452 bool verify_host(X509 *server_cert)
const;
2453 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
2454 bool verify_host_with_common_name(X509 *server_cert)
const;
2455 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
2458 std::mutex ctx_mutex_;
2459 std::once_flag initialize_cert_;
2461 std::vector<std::string> host_components_;
2463 long verify_result_ = 0;
2465 friend class ClientImpl;
2476template <
typename T,
typename U>
2477inline void duration_to_sec_and_usec(
const T &duration, U callback)
2479 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2480 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2481 duration - std::chrono::seconds(sec))
2483 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
2486template <
size_t N>
inline constexpr size_t str_len(
const char (&)[N])
2491inline bool is_numeric(
const std::string &str)
2493 return !str.empty() &&
2494 std::all_of(str.cbegin(), str.cend(),
2497 return std::isdigit(c);
2501inline size_t get_header_value_u64(
const Headers &headers,
2502 const std::string &key,
size_t def,
2503 size_t id,
bool &is_invalid_value)
2505 is_invalid_value =
false;
2506 auto rng = headers.equal_range(key);
2507 auto it = rng.first;
2508 std::advance(it,
static_cast<ssize_t
>(
id));
2509 if (it != rng.second)
2511 if (is_numeric(it->second))
2513 return std::strtoull(it->second.data(),
nullptr, 10);
2517 is_invalid_value =
true;
2523inline size_t get_header_value_u64(
const Headers &headers,
2524 const std::string &key,
size_t def,
2528 return get_header_value_u64(headers, key, def,
id, dummy);
2536 return detail::get_header_value_u64(
headers, key, def,
id);
2542 return detail::get_header_value_u64(
headers, key, def,
id);
2548inline bool set_socket_opt_impl(
socket_t sock,
int level,
int optname,
2549 const void *optval, socklen_t optlen)
2551 return setsockopt(sock, level, optname,
2553 reinterpret_cast<const char *
>(optval),
2560inline bool set_socket_opt(
socket_t sock,
int level,
int optname,
int optval)
2562 return set_socket_opt_impl(sock, level, optname, &optval,
sizeof(optval));
2565inline bool set_socket_opt_time(
socket_t sock,
int level,
int optname,
2566 time_t sec, time_t usec)
2569 auto timeout =
static_cast<uint32_t
>(sec * 1000 + usec / 1000);
2572 timeout.tv_sec =
static_cast<long>(sec);
2573 timeout.tv_usec =
static_cast<decltype(timeout.tv_usec)
>(usec);
2575 return set_socket_opt_impl(sock, level, optname, &timeout,
sizeof(timeout));
2582 detail::set_socket_opt(sock, SOL_SOCKET,
2595 constexpr auto bearer_header_prefix_len = detail::str_len(
"Bearer ");
2597 .substr(bearer_header_prefix_len);
2602template <
class Rep,
class Period>
2606 detail::duration_to_sec_and_usec(
2607 duration, [&](time_t sec, time_t usec)
2614template <
class Rep,
class Period>
2618 detail::duration_to_sec_and_usec(
2619 duration, [&](time_t sec, time_t usec)
2626template <
class Rep,
class Period>
2630 detail::duration_to_sec_and_usec(
2631 duration, [&](time_t sec, time_t usec)
2642 return detail::get_header_value_u64(request_headers_, key, def,
id);
2645template <
class Rep,
class Period>
2647 const std::chrono::duration<Rep, Period> &duration)
2649 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec)
2655template <
class Rep,
class Period>
2657 const std::chrono::duration<Rep, Period> &duration)
2659 detail::duration_to_sec_and_usec(
2660 duration, [&](time_t sec, time_t usec)
2666template <
class Rep,
class Period>
2668 const std::chrono::duration<Rep, Period> &duration)
2670 detail::duration_to_sec_and_usec(
2671 duration, [&](time_t sec, time_t usec)
2677template <
class Rep,
class Period>
2679 const std::chrono::duration<Rep, Period> &duration)
2682 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2686template <
class Rep,
class Period>
2688 const std::chrono::duration<Rep, Period> &duration)
2690 cli_->set_connection_timeout(duration);
2693template <
class Rep,
class Period>
2697 cli_->set_read_timeout(duration);
2700template <
class Rep,
class Period>
2704 cli_->set_write_timeout(duration);
2709 cli_->set_max_timeout(msec);
2712template <
class Rep,
class Period>
2716 cli_->set_max_timeout(duration);
2724std::string
hosted_at(
const std::string &hostname);
2726void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
2730std::string
encode_uri(
const std::string &value);
2732std::string
decode_uri(
const std::string &value);
2738 bool space_as_plus =
true);
2740 bool plus_as_space =
true);
2746std::pair<std::string, std::string>
2748 const std::string &password,
2749 bool is_proxy =
false);
2755inline std::wstring u8string_to_wstring(
const char *s)
2758 auto len =
static_cast<int>(strlen(s));
2759 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len,
nullptr, 0);
2763 wlen = ::MultiByteToWideChar(
2765 const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
2766 if (wlen !=
static_cast<int>(ws.size()))
2777 FileStat(
const std::string &path);
2778 bool is_file()
const;
2779 bool is_dir()
const;
2780 time_t mtime()
const;
2781 size_t size()
const;
2792std::string make_host_and_port_string(
const std::string &host,
int port,
2795std::string trim_copy(
const std::string &s);
2798 const char *data, std::size_t size,
char d,
2799 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2803 const std::string &str,
char d,
2804 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2807void split(
const char *b,
const char *e,
char d,
2808 std::function<
void(
const char *,
const char *)> fn);
2810void split(
const char *b,
const char *e,
char d,
size_t m,
2811 std::function<
void(
const char *,
const char *)> fn);
2813bool process_client_socket(
2814 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2815 time_t write_timeout_sec, time_t write_timeout_usec,
2816 time_t max_timeout_msec,
2817 std::chrono::time_point<std::chrono::steady_clock> start_time,
2818 std::function<
bool(Stream &)> callback);
2820socket_t create_client_socket(
const std::string &host,
const std::string &ip,
2821 int port,
int address_family,
bool tcp_nodelay,
2823 time_t connection_timeout_sec,
2824 time_t connection_timeout_usec,
2825 time_t read_timeout_sec, time_t read_timeout_usec,
2826 time_t write_timeout_sec,
2827 time_t write_timeout_usec,
2828 const std::string &intf, Error &error);
2830const char *get_header_value(
const Headers &headers,
const std::string &key,
2831 const char *def,
size_t id);
2833std::string params_to_query_str(
const Params ¶ms);
2835void parse_query_text(
const char *data, std::size_t size,
Params ¶ms);
2837void parse_query_text(
const std::string &s,
Params ¶ms);
2839bool parse_multipart_boundary(
const std::string &content_type,
2840 std::string &boundary);
2842bool parse_range_header(
const std::string &s,
Ranges &ranges);
2844bool parse_accept_header(
const std::string &s,
2845 std::vector<std::string> &content_types);
2849ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
int flags);
2851ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags);
2853enum class EncodingType {
None = 0, Gzip, Brotli, Zstd };
2855EncodingType encoding_type(
const Request &req,
const Response &res);
2857class BufferStream final :
public Stream
2860 BufferStream() =
default;
2861 ~BufferStream()
override =
default;
2863 bool is_readable()
const override;
2864 bool wait_readable()
const override;
2865 bool wait_writable()
const override;
2866 ssize_t read(
char *ptr,
size_t size)
override;
2867 ssize_t
write(
const char *ptr,
size_t size)
override;
2868 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
2869 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
2871 time_t duration()
const override;
2873 const std::string &get_buffer()
const;
2877 size_t position = 0;
2883 virtual ~compressor() =
default;
2885 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
2886 virtual bool compress(
const char *data,
size_t data_length,
bool last,
2887 Callback callback) = 0;
2893 virtual ~decompressor() =
default;
2895 virtual bool is_valid()
const = 0;
2897 typedef std::function<bool(
const char *data,
size_t data_len)> Callback;
2898 virtual bool decompress(
const char *data,
size_t data_length,
2899 Callback callback) = 0;
2902class nocompressor final :
public compressor
2905 ~nocompressor()
override =
default;
2907 bool compress(
const char *data,
size_t data_length,
bool ,
2908 Callback callback)
override;
2911#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2912class gzip_compressor final :
public compressor
2916 ~gzip_compressor()
override;
2918 bool compress(
const char *data,
size_t data_length,
bool last,
2919 Callback callback)
override;
2922 bool is_valid_ =
false;
2926class gzip_decompressor final :
public decompressor
2929 gzip_decompressor();
2930 ~gzip_decompressor()
override;
2932 bool is_valid()
const override;
2934 bool decompress(
const char *data,
size_t data_length,
2935 Callback callback)
override;
2938 bool is_valid_ =
false;
2943#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2944class brotli_compressor final :
public compressor
2947 brotli_compressor();
2948 ~brotli_compressor();
2950 bool compress(
const char *data,
size_t data_length,
bool last,
2951 Callback callback)
override;
2954 BrotliEncoderState *state_ =
nullptr;
2957class brotli_decompressor final :
public decompressor
2960 brotli_decompressor();
2961 ~brotli_decompressor();
2963 bool is_valid()
const override;
2965 bool decompress(
const char *data,
size_t data_length,
2966 Callback callback)
override;
2969 BrotliDecoderResult decoder_r;
2970 BrotliDecoderState *decoder_s =
nullptr;
2974#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2975class zstd_compressor :
public compressor
2981 bool compress(
const char *data,
size_t data_length,
bool last,
2982 Callback callback)
override;
2985 ZSTD_CCtx *ctx_ =
nullptr;
2988class zstd_decompressor :
public decompressor
2991 zstd_decompressor();
2992 ~zstd_decompressor();
2994 bool is_valid()
const override;
2996 bool decompress(
const char *data,
size_t data_length,
2997 Callback callback)
override;
3000 ZSTD_DCtx *ctx_ =
nullptr;
3006class stream_line_reader
3009 stream_line_reader(Stream &strm,
char *fixed_buffer,
3010 size_t fixed_buffer_size);
3011 const char *ptr()
const;
3012 size_t size()
const;
3013 bool end_with_crlf()
const;
3017 void append(
char c);
3020 char *fixed_buffer_;
3021 const size_t fixed_buffer_size_;
3022 size_t fixed_buffer_used_size_ = 0;
3023 std::string growable_buffer_;
3026bool parse_trailers(stream_line_reader &line_reader,
Headers &dest,
3029struct ChunkedDecoder
3032 size_t chunk_remaining = 0;
3033 bool finished =
false;
3035 size_t last_chunk_total = 0;
3036 size_t last_chunk_offset = 0;
3038 explicit ChunkedDecoder(Stream &s);
3040 ssize_t read_payload(
char *buf,
size_t len,
size_t &out_chunk_offset,
3041 size_t &out_chunk_total);
3043 bool parse_trailers_into(
Headers &dest,
const Headers &src_headers);
3049 mmap(
const char *path);
3052 bool open(
const char *path);
3055 bool is_open()
const;
3056 size_t size()
const;
3057 const char *data()
const;
3061 HANDLE hFile_ = NULL;
3062 HANDLE hMapping_ = NULL;
3067 void *addr_ =
nullptr;
3068 bool is_open_empty_file =
false;
3075bool is_token_char(
char c);
3076bool is_token(
const std::string &s);
3077bool is_field_name(
const std::string &s);
3078bool is_vchar(
char c);
3079bool is_obs_text(
char c);
3080bool is_field_vchar(
char c);
3081bool is_field_content(
const std::string &s);
3082bool is_field_value(
const std::string &s);
3094 Result() : chunk_size_(8192) {}
3097 : handle_(std::move(handle)), chunk_size_(chunk_size) {}
3099 Result(Result &&other) noexcept
3100 : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
3101 current_size_(other.current_size_), chunk_size_(other.chunk_size_),
3102 finished_(other.finished_)
3104 other.current_size_ = 0;
3105 other.finished_ =
true;
3108 Result &operator=(Result &&other)
noexcept
3112 handle_ = std::move(other.handle_);
3113 buffer_ = std::move(other.buffer_);
3114 current_size_ = other.current_size_;
3115 chunk_size_ = other.chunk_size_;
3116 finished_ = other.finished_;
3117 other.current_size_ = 0;
3118 other.finished_ =
true;
3123 Result(
const Result &) =
delete;
3124 Result &operator=(
const Result &) =
delete;
3127 bool is_valid()
const
3129 return handle_.is_valid();
3131 explicit operator bool()
const
3139 return handle_.response ? handle_.response->status : -1;
3143 const Headers &headers()
const
3145 static const Headers empty_headers;
3146 return handle_.response ? handle_.response->headers : empty_headers;
3149 std::string get_header_value(
const std::string &key,
3150 const char *def =
"")
const
3152 return handle_.response ? handle_.response->get_header_value(key, def)
3156 bool has_header(
const std::string &key)
const
3158 return handle_.response ? handle_.response->has_header(key) :
false;
3164 return handle_.error;
3166 Error read_error()
const
3168 return handle_.get_read_error();
3170 bool has_read_error()
const
3172 return handle_.has_read_error();
3180 if (!handle_.is_valid() || finished_)
3185 if (buffer_.size() < chunk_size_)
3187 buffer_.resize(chunk_size_);
3190 ssize_t n = handle_.read(&buffer_[0], chunk_size_);
3193 current_size_ =
static_cast<size_t>(n);
3203 const char *data()
const
3205 return buffer_.data();
3211 return current_size_;
3215 std::string read_all()
3220 result.append(data(), size());
3227 std::string buffer_;
3228 size_t current_size_ = 0;
3230 bool finished_ =
false;
3234template <
typename ClientType>
3235inline Result Get(ClientType &cli,
const std::string &path,
3236 size_t chunk_size = 8192)
3238 return Result{cli.open_stream(
"GET", path), chunk_size};
3241template <
typename ClientType>
3242inline Result Get(ClientType &cli,
const std::string &path,
3243 const Headers &headers,
size_t chunk_size = 8192)
3245 return Result{cli.open_stream(
"GET", path, {}, headers), chunk_size};
3248template <
typename ClientType>
3249inline Result Get(ClientType &cli,
const std::string &path,
3250 const Params ¶ms,
size_t chunk_size = 8192)
3252 return Result{cli.open_stream(
"GET", path, params), chunk_size};
3255template <
typename ClientType>
3256inline Result Get(ClientType &cli,
const std::string &path,
3258 size_t chunk_size = 8192)
3260 return Result{cli.open_stream(
"GET", path, params, headers), chunk_size};
3264template <
typename ClientType>
3265inline Result Post(ClientType &cli,
const std::string &path,
3266 const std::string &body,
const std::string &content_type,
3267 size_t chunk_size = 8192)
3269 return Result{cli.open_stream(
"POST", path, {}, {}, body, content_type),
3273template <
typename ClientType>
3274inline Result Post(ClientType &cli,
const std::string &path,
3275 const Headers &headers,
const std::string &body,
3276 const std::string &content_type,
size_t chunk_size = 8192)
3278 return Result{cli.open_stream(
"POST", path, {}, headers, body, content_type),
3282template <
typename ClientType>
3283inline Result Post(ClientType &cli,
const std::string &path,
3284 const Params ¶ms,
const std::string &body,
3285 const std::string &content_type,
size_t chunk_size = 8192)
3287 return Result{cli.open_stream(
"POST", path, params, {}, body, content_type),
3291template <
typename ClientType>
3292inline Result Post(ClientType &cli,
const std::string &path,
3294 const std::string &body,
const std::string &content_type,
3295 size_t chunk_size = 8192)
3299 cli.open_stream(
"POST", path, params, headers, body, content_type),
3304template <
typename ClientType>
3305inline Result Put(ClientType &cli,
const std::string &path,
3306 const std::string &body,
const std::string &content_type,
3307 size_t chunk_size = 8192)
3309 return Result{cli.open_stream(
"PUT", path, {}, {}, body, content_type),
3313template <
typename ClientType>
3314inline Result Put(ClientType &cli,
const std::string &path,
3315 const Headers &headers,
const std::string &body,
3316 const std::string &content_type,
size_t chunk_size = 8192)
3318 return Result{cli.open_stream(
"PUT", path, {}, headers, body, content_type),
3322template <
typename ClientType>
3323inline Result Put(ClientType &cli,
const std::string &path,
3324 const Params ¶ms,
const std::string &body,
3325 const std::string &content_type,
size_t chunk_size = 8192)
3327 return Result{cli.open_stream(
"PUT", path, params, {}, body, content_type),
3331template <
typename ClientType>
3332inline Result Put(ClientType &cli,
const std::string &path,
3334 const std::string &body,
const std::string &content_type,
3335 size_t chunk_size = 8192)
3339 cli.open_stream(
"PUT", path, params, headers, body, content_type),
3344template <
typename ClientType>
3345inline Result Patch(ClientType &cli,
const std::string &path,
3346 const std::string &body,
const std::string &content_type,
3347 size_t chunk_size = 8192)
3349 return Result{cli.open_stream(
"PATCH", path, {}, {}, body, content_type),
3353template <
typename ClientType>
3354inline Result Patch(ClientType &cli,
const std::string &path,
3355 const Headers &headers,
const std::string &body,
3356 const std::string &content_type,
size_t chunk_size = 8192)
3358 return Result{cli.open_stream(
"PATCH", path, {}, headers, body, content_type),
3362template <
typename ClientType>
3363inline Result Patch(ClientType &cli,
const std::string &path,
3364 const Params ¶ms,
const std::string &body,
3365 const std::string &content_type,
size_t chunk_size = 8192)
3367 return Result{cli.open_stream(
"PATCH", path, params, {}, body, content_type),
3371template <
typename ClientType>
3372inline Result Patch(ClientType &cli,
const std::string &path,
3374 const std::string &body,
const std::string &content_type,
3375 size_t chunk_size = 8192)
3379 cli.open_stream(
"PATCH", path, params, headers, body, content_type),
3384template <
typename ClientType>
3385inline Result Delete(ClientType &cli,
const std::string &path,
3386 size_t chunk_size = 8192)
3388 return Result{cli.open_stream(
"DELETE", path), chunk_size};
3391template <
typename ClientType>
3392inline Result Delete(ClientType &cli,
const std::string &path,
3393 const Headers &headers,
size_t chunk_size = 8192)
3395 return Result{cli.open_stream(
"DELETE", path, {}, headers), chunk_size};
3398template <
typename ClientType>
3399inline Result Delete(ClientType &cli,
const std::string &path,
3400 const std::string &body,
const std::string &content_type,
3401 size_t chunk_size = 8192)
3403 return Result{cli.open_stream(
"DELETE", path, {}, {}, body, content_type),
3407template <
typename ClientType>
3408inline Result Delete(ClientType &cli,
const std::string &path,
3409 const Headers &headers,
const std::string &body,
3410 const std::string &content_type,
3411 size_t chunk_size = 8192)
3415 cli.open_stream(
"DELETE", path, {}, headers, body, content_type),
3419template <
typename ClientType>
3420inline Result Delete(ClientType &cli,
const std::string &path,
3421 const Params ¶ms,
size_t chunk_size = 8192)
3423 return Result{cli.open_stream(
"DELETE", path, params), chunk_size};
3426template <
typename ClientType>
3427inline Result Delete(ClientType &cli,
const std::string &path,
3429 size_t chunk_size = 8192)
3431 return Result{cli.open_stream(
"DELETE", path, params, headers), chunk_size};
3434template <
typename ClientType>
3435inline Result Delete(ClientType &cli,
const std::string &path,
3436 const Params ¶ms,
const std::string &body,
3437 const std::string &content_type,
3438 size_t chunk_size = 8192)
3440 return Result{cli.open_stream(
"DELETE", path, params, {}, body, content_type),
3444template <
typename ClientType>
3445inline Result Delete(ClientType &cli,
const std::string &path,
3447 const std::string &body,
const std::string &content_type,
3448 size_t chunk_size = 8192)
3452 cli.open_stream(
"DELETE", path, params, headers, body, content_type),
3457template <
typename ClientType>
3458inline Result Head(ClientType &cli,
const std::string &path,
3459 size_t chunk_size = 8192)
3461 return Result{cli.open_stream(
"HEAD", path), chunk_size};
3464template <
typename ClientType>
3465inline Result Head(ClientType &cli,
const std::string &path,
3466 const Headers &headers,
size_t chunk_size = 8192)
3468 return Result{cli.open_stream(
"HEAD", path, {}, headers), chunk_size};
3471template <
typename ClientType>
3472inline Result Head(ClientType &cli,
const std::string &path,
3473 const Params ¶ms,
size_t chunk_size = 8192)
3475 return Result{cli.open_stream(
"HEAD", path, params), chunk_size};
3478template <
typename ClientType>
3479inline Result Head(ClientType &cli,
const std::string &path,
3481 size_t chunk_size = 8192)
3483 return Result{cli.open_stream(
"HEAD", path, params, headers), chunk_size};
3487template <
typename ClientType>
3488inline Result
Options(ClientType &cli,
const std::string &path,
3489 size_t chunk_size = 8192)
3491 return Result{cli.open_stream(
"OPTIONS", path), chunk_size};
3494template <
typename ClientType>
3495inline Result
Options(ClientType &cli,
const std::string &path,
3496 const Headers &headers,
size_t chunk_size = 8192)
3498 return Result{cli.open_stream(
"OPTIONS", path, {}, headers), chunk_size};
3501template <
typename ClientType>
3502inline Result
Options(ClientType &cli,
const std::string &path,
3503 const Params ¶ms,
size_t chunk_size = 8192)
3505 return Result{cli.open_stream(
"OPTIONS", path, params), chunk_size};
3508template <
typename ClientType>
3509inline Result
Options(ClientType &cli,
const std::string &path,
3511 size_t chunk_size = 8192)
3513 return Result{cli.open_stream(
"OPTIONS", path, params, headers), chunk_size};
3527 SSEMessage() : event(
"message") {}
3540 using MessageHandler = std::function<void(
const SSEMessage &)>;
3541 using ErrorHandler = std::function<void(
Error)>;
3542 using OpenHandler = std::function<void()>;
3544 SSEClient(
Client &client,
const std::string &path)
3545 : client_(client), path_(path) {}
3547 SSEClient(
Client &client,
const std::string &path,
const Headers &headers)
3548 : client_(client), path_(path), headers_(headers) {}
3555 SSEClient(
const SSEClient &) =
delete;
3556 SSEClient &operator=(
const SSEClient &) =
delete;
3559 SSEClient &on_message(MessageHandler handler)
3561 on_message_ = std::move(handler);
3565 SSEClient &on_event(
const std::string &
type, MessageHandler handler)
3567 event_handlers_[
type] = std::move(handler);
3571 SSEClient &on_open(OpenHandler handler)
3573 on_open_ = std::move(handler);
3577 SSEClient &on_error(ErrorHandler handler)
3579 on_error_ = std::move(handler);
3583 SSEClient &set_reconnect_interval(
int ms)
3585 reconnect_interval_ms_ = ms;
3589 SSEClient &set_max_reconnect_attempts(
int n)
3591 max_reconnect_attempts_ = n;
3596 bool is_connected()
const
3598 return connected_.load();
3600 const std::string &last_event_id()
const
3602 return last_event_id_;
3608 running_.store(
true);
3615 running_.store(
true);
3616 async_thread_ = std::thread([
this]()
3625 running_.store(
false);
3627 if (async_thread_.joinable())
3629 async_thread_.join();
3636 bool parse_sse_line(
const std::string &line, SSEMessage &msg,
int &retry_ms)
3639 if (line.empty() || line ==
"\r")
3645 if (!line.empty() && line[0] ==
':')
3651 auto colon_pos = line.find(
':');
3652 if (colon_pos == std::string::npos)
3658 auto field = line.substr(0, colon_pos);
3662 if (colon_pos + 1 < line.size())
3664 auto value_start = colon_pos + 1;
3665 if (line[value_start] ==
' ')
3669 value = line.substr(value_start);
3671 if (!value.empty() && value.back() ==
'\r')
3678 if (field ==
"event")
3682 else if (field ==
"data")
3685 if (!msg.data.empty())
3691 else if (field ==
"id")
3696 else if (field ==
"retry")
3701 retry_ms = std::stoi(value);
3714 void run_event_loop()
3716 auto reconnect_count = 0;
3718 while (running_.load())
3721 auto request_headers = headers_;
3722 if (!last_event_id_.empty())
3724 request_headers.emplace(
"Last-Event-ID", last_event_id_);
3728 auto result = stream::Get(client_, path_, request_headers);
3733 connected_.store(
false);
3736 on_error_(result.error());
3739 if (!should_reconnect(reconnect_count))
3743 wait_for_reconnect();
3748 if (result.status() != 200)
3750 connected_.store(
false);
3752 if (result.status() == 204 ||
3753 result.status() == 404 ||
3754 result.status() == 401 ||
3755 result.status() == 403)
3769 if (!should_reconnect(reconnect_count))
3773 wait_for_reconnect();
3779 connected_.store(
true);
3780 reconnect_count = 0;
3788 SSEMessage current_msg;
3790 while (running_.load() && result.next())
3792 buffer.append(result.data(), result.size());
3795 size_t line_start = 0;
3798 while ((newline_pos = buffer.find(
'\n', line_start)) !=
3801 auto line = buffer.substr(line_start, newline_pos - line_start);
3802 line_start = newline_pos + 1;
3805 auto event_complete =
3806 parse_sse_line(line, current_msg, reconnect_interval_ms_);
3808 if (event_complete && !current_msg.data.empty())
3811 if (!current_msg.id.empty())
3813 last_event_id_ = current_msg.id;
3817 dispatch_event(current_msg);
3819 current_msg.clear();
3824 buffer.erase(0, line_start);
3828 connected_.store(
false);
3830 if (!running_.load())
3836 if (result.has_read_error())
3840 on_error_(result.read_error());
3844 if (!should_reconnect(reconnect_count))
3848 wait_for_reconnect();
3852 connected_.store(
false);
3856 void dispatch_event(
const SSEMessage &msg)
3859 auto it = event_handlers_.find(msg.event);
3860 if (it != event_handlers_.end())
3874 bool should_reconnect(
int count)
const
3876 if (!running_.load())
3880 if (max_reconnect_attempts_ == 0)
3884 return count < max_reconnect_attempts_;
3888 void wait_for_reconnect()
3892 while (running_.load() && waited < reconnect_interval_ms_)
3894 std::this_thread::sleep_for(std::chrono::milliseconds(100));
3905 MessageHandler on_message_;
3906 std::map<std::string, MessageHandler> event_handlers_;
3907 OpenHandler on_open_;
3908 ErrorHandler on_error_;
3911 int reconnect_interval_ms_ = 3000;
3912 int max_reconnect_attempts_ = 0;
3915 std::atomic<bool> running_{
false};
3916 std::atomic<bool> connected_{
false};
3917 std::string last_event_id_;
3920 std::thread async_thread_;
3934inline bool is_hex(
char c,
int &v)
3941 else if (
'A' <= c && c <=
'F')
3946 else if (
'a' <= c && c <=
'f')
3954inline bool from_hex_to_i(
const std::string &s,
size_t i,
size_t cnt,
3963 for (; cnt; i++, cnt--)
3970 if (is_hex(s[i], v))
3982inline std::string from_i_to_hex(
size_t n)
3984 static const auto charset =
"0123456789abcdef";
3988 ret = charset[n & 15] + ret;
3995inline std::string compute_etag(
const FileStat &fs)
3999 return std::string();
4006 auto mtime_raw = fs.mtime();
4009 return std::string();
4012 auto mtime =
static_cast<size_t>(mtime_raw);
4013 auto size = fs.size();
4015 return std::string(
"W/\"") + from_i_to_hex(mtime) +
"-" +
4016 from_i_to_hex(size) +
"\"";
4022inline std::string file_mtime_to_http_date(time_t mtime)
4026 return std::string();
4031 if (gmtime_s(&tm_buf, &mtime) != 0)
4033 return std::string();
4036 if (gmtime_r(&mtime, &tm_buf) ==
nullptr)
4038 return std::string();
4042 if (strftime(buf,
sizeof(buf),
"%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0)
4044 return std::string();
4047 return std::string(buf);
4051inline time_t parse_http_date(
const std::string &date_str)
4056 const std::locale classic_locale = std::locale::classic();
4059 auto try_parse = [&](
const char *
fmt) ->
bool
4061 std::istringstream ss(date_str);
4062 ss.imbue(classic_locale);
4064 memset(&tm_buf, 0,
sizeof(tm_buf));
4065 ss >> std::get_time(&tm_buf,
fmt);
4071 if (!try_parse(
"%a, %d %b %Y %H:%M:%S"))
4074 if (!try_parse(
"%A, %d-%b-%y %H:%M:%S"))
4077 if (!try_parse(
"%a %b %d %H:%M:%S %Y"))
4079 return static_cast<time_t
>(-1);
4085 return _mkgmtime(&tm_buf);
4087 return timegm(&tm_buf);
4091inline bool is_weak_etag(
const std::string &s)
4094 return s.size() > 3 && s[0] ==
'W' && s[1] ==
'/' && s[2] ==
'"';
4097inline bool is_strong_etag(
const std::string &s)
4101 return s.size() >= 2 && s[0] ==
'"' && s.back() ==
'"';
4104inline size_t to_utf8(
int code,
char *buff)
4108 buff[0] =
static_cast<char>(code & 0x7F);
4111 else if (code < 0x0800)
4113 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
4114 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
4117 else if (code < 0xD800)
4119 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4120 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4121 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
4124 else if (code < 0xE000)
4128 else if (code < 0x10000)
4130 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4131 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4132 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
4135 else if (code < 0x110000)
4137 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
4138 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
4139 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4140 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
4150inline std::string base64_encode(
const std::string &in)
4152 static const auto lookup =
4153 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4156 out.reserve(in.size());
4163 val = (val << 8) + static_cast<uint8_t>(c);
4167 out.push_back(lookup[(val >> valb) & 0x3F]);
4174 out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]);
4177 while (out.size() % 4)
4185inline bool is_valid_path(
const std::string &path)
4191 while (i < path.size() && path[i] ==
'/')
4196 while (i < path.size())
4200 while (i < path.size() && path[i] !=
'/')
4202 if (path[i] ==
'\0')
4206 else if (path[i] ==
'\\')
4216 if (!path.compare(beg, len,
"."))
4220 else if (!path.compare(beg, len,
".."))
4234 while (i < path.size() && path[i] ==
'/')
4243inline FileStat::FileStat(
const std::string &path)
4246 auto wpath = u8string_to_wstring(path.c_str());
4247 ret_ = _wstat(wpath.c_str(), &st_);
4249 ret_ = stat(path.c_str(), &st_);
4252inline bool FileStat::is_file()
const
4254 return ret_ >= 0 && S_ISREG(st_.st_mode);
4256inline bool FileStat::is_dir()
const
4258 return ret_ >= 0 && S_ISDIR(st_.st_mode);
4261inline time_t FileStat::mtime()
const
4263 return ret_ >= 0 ?
static_cast<time_t
>(st_.st_mtime)
4264 : static_cast<time_t>(-1);
4267inline size_t FileStat::size()
const
4269 return ret_ >= 0 ?
static_cast<size_t>(st_.st_size) : 0;
4272inline std::string encode_path(
const std::string &s)
4275 result.reserve(s.size());
4277 for (
size_t i = 0; s[i]; i++)
4304 auto c =
static_cast<uint8_t
>(s[i]);
4309 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
4311 result.append(hex,
static_cast<size_t>(len));
4324inline std::string file_extension(
const std::string &path)
4327 thread_local auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
4328 if (std::regex_search(path, m, re))
4332 return std::string();
4335inline bool is_space_or_tab(
char c)
4337 return c ==
' ' || c ==
'\t';
4340template <
typename T>
4341inline bool parse_header(
const char *beg,
const char *end, T fn);
4343template <
typename T>
4344inline bool parse_header(
const char *beg,
const char *end, T fn)
4347 while (beg < end && is_space_or_tab(end[-1]))
4353 while (p < end && *p !=
':')
4358 auto name = std::string(beg, p);
4359 if (!detail::fields::is_field_name(name))
4376 while (p < end && is_space_or_tab(*p))
4383 auto key_len = key_end - beg;
4389 auto key = std::string(beg, key_end);
4390 auto val = std::string(p, end);
4392 if (!detail::fields::is_field_value(val))
4413inline bool parse_trailers(stream_line_reader &line_reader,
Headers &dest,
4430 thread_local case_ignore::unordered_set<std::string> prohibited_trailers =
4432 "transfer-encoding",
4437 "proxy-authenticate",
4438 "proxy-authorization",
4460 case_ignore::unordered_set<std::string> declared_trailers;
4461 auto trailer_header = get_header_value(src_headers,
"Trailer",
"", 0);
4462 if (trailer_header && std::strlen(trailer_header))
4464 auto len = std::strlen(trailer_header);
4465 split(trailer_header, trailer_header + len,
',',
4466 [&](
const char *b,
const char *e)
4468 const char *kbeg = b;
4469 const char *kend = e;
4470 while (kbeg < kend && (*kbeg ==
' ' || *kbeg ==
'\t'))
4474 while (kend > kbeg && (kend[-1] ==
' ' || kend[-1] ==
'\t'))
4478 std::string key(kbeg,
static_cast<size_t>(kend - kbeg));
4480 prohibited_trailers.find(key) == prohibited_trailers.end())
4482 declared_trailers.insert(key);
4487 size_t trailer_header_count = 0;
4488 while (strcmp(line_reader.ptr(),
"\r\n") != 0)
4499 constexpr auto line_terminator_len = 2;
4500 auto line_beg = line_reader.ptr();
4502 line_reader.ptr() + line_reader.size() - line_terminator_len;
4504 if (!parse_header(line_beg, line_end,
4505 [&](
const std::string & key,
const std::string & val)
4507 if (declared_trailers.find(key) !=
4508 declared_trailers.end())
4510 dest.emplace(key, val);
4511 trailer_header_count++;
4518 if (!line_reader.getline())
4527inline std::pair<size_t, size_t> trim(
const char *b,
const char *e,
size_t left,
4530 while (b + left < e && is_space_or_tab(b[left]))
4534 while (right > 0 && is_space_or_tab(b[right - 1]))
4538 return std::make_pair(left, right);
4541inline std::string trim_copy(
const std::string &s)
4543 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
4544 return s.substr(r.first, r.second - r.first);
4547inline std::string trim_double_quotes_copy(
const std::string &s)
4549 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"')
4551 return s.substr(1, s.size() - 2);
4557divide(
const char *data, std::size_t size,
char d,
4558 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
4561 const auto it = std::find(data, data + size, d);
4562 const auto found =
static_cast<std::size_t
>(it != data + size);
4563 const auto lhs_data = data;
4564 const auto lhs_size =
static_cast<std::size_t
>(it - data);
4565 const auto rhs_data = it + found;
4566 const auto rhs_size = size - lhs_size - found;
4568 fn(lhs_data, lhs_size, rhs_data, rhs_size);
4572divide(
const std::string &str,
char d,
4573 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
4576 divide(str.data(), str.size(), d, std::move(fn));
4579inline void split(
const char *b,
const char *e,
char d,
4580 std::function<
void(
const char *,
const char *)> fn)
4582 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
4585inline void split(
const char *b,
const char *e,
char d,
size_t m,
4586 std::function<
void(
const char *,
const char *)> fn)
4592 while (e ? (b + i < e) : (b[i] !=
'\0'))
4594 if (b[i] == d && count < m)
4596 auto r = trim(b, e, beg, i);
4597 if (r.first < r.second)
4599 fn(&b[r.first], &b[r.second]);
4609 auto r = trim(b, e, beg, i);
4610 if (r.first < r.second)
4612 fn(&b[r.first], &b[r.second]);
4617inline bool split_find(
const char *b,
const char *e,
char d,
size_t m,
4618 std::function<
bool(
const char *,
const char *)> fn)
4624 while (e ? (b + i < e) : (b[i] !=
'\0'))
4626 if (b[i] == d && count < m)
4628 auto r = trim(b, e, beg, i);
4629 if (r.first < r.second)
4631 auto found = fn(&b[r.first], &b[r.second]);
4645 auto r = trim(b, e, beg, i);
4646 if (r.first < r.second)
4648 auto found = fn(&b[r.first], &b[r.second]);
4659inline bool split_find(
const char *b,
const char *e,
char d,
4660 std::function<
bool(
const char *,
const char *)> fn)
4662 return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
4666inline stream_line_reader::stream_line_reader(Stream &strm,
char *fixed_buffer,
4667 size_t fixed_buffer_size)
4668 : strm_(strm), fixed_buffer_(fixed_buffer),
4669 fixed_buffer_size_(fixed_buffer_size) {}
4671inline const char *stream_line_reader::ptr()
const
4673 if (growable_buffer_.empty())
4675 return fixed_buffer_;
4679 return growable_buffer_.data();
4683inline size_t stream_line_reader::size()
const
4685 if (growable_buffer_.empty())
4687 return fixed_buffer_used_size_;
4691 return growable_buffer_.size();
4695inline bool stream_line_reader::end_with_crlf()
const
4697 auto end = ptr() + size();
4698 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
4701inline bool stream_line_reader::getline()
4703 fixed_buffer_used_size_ = 0;
4704 growable_buffer_.clear();
4706#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4710 for (
size_t i = 0;; i++)
4719 auto n = strm_.read(&
byte, 1);
4739#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4745 if (prev_byte ==
'\r' &&
byte ==
'\n')
4756inline void stream_line_reader::append(
char c)
4758 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1)
4760 fixed_buffer_[fixed_buffer_used_size_++] = c;
4761 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
4765 if (growable_buffer_.empty())
4767 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
4768 growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
4770 growable_buffer_ += c;
4774inline mmap::mmap(
const char *path)
4784inline bool mmap::open(
const char *path)
4789 auto wpath = u8string_to_wstring(path);
4795 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
4796 OPEN_EXISTING, NULL);
4798 if (hFile_ == INVALID_HANDLE_VALUE)
4803 LARGE_INTEGER size{};
4804 if (!::GetFileSizeEx(hFile_, &size))
4811 if (
static_cast<ULONGLONG
>(size.QuadPart) >
4812 (std::numeric_limits<
decltype(size_)>
::max)())
4817 size_ =
static_cast<size_t>(size.QuadPart);
4820 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
4823 if (hMapping_ == NULL && size_ == 0)
4826 is_open_empty_file =
true;
4830 if (hMapping_ == NULL)
4836 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
4838 if (addr_ ==
nullptr)
4844 fd_ = ::open(path, O_RDONLY);
4851 if (fstat(fd_, &sb) == -1)
4856 size_ =
static_cast<size_t>(sb.st_size);
4858 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
4861 if (addr_ == MAP_FAILED && size_ == 0)
4864 is_open_empty_file =
true;
4872inline bool mmap::is_open()
const
4874 return is_open_empty_file ? true : addr_ !=
nullptr;
4877inline size_t mmap::size()
const
4882inline const char *mmap::data()
const
4884 return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_);
4887inline void mmap::close()
4892 ::UnmapViewOfFile(addr_);
4898 ::CloseHandle(hMapping_);
4902 if (hFile_ != INVALID_HANDLE_VALUE)
4904 ::CloseHandle(hFile_);
4905 hFile_ = INVALID_HANDLE_VALUE;
4908 is_open_empty_file =
false;
4910 if (addr_ !=
nullptr)
4912 munmap(addr_, size_);
4927 return closesocket(sock);
4933template <
typename T>
inline ssize_t handle_EINTR(T fn)
4939 if (res < 0 &&
errno == EINTR)
4941 std::this_thread::sleep_for(std::chrono::microseconds{1});
4949inline ssize_t read_socket(
socket_t sock,
void *ptr,
size_t size,
int flags)
4951 return handle_EINTR([&]()
4955 static_cast<char *
>(ptr),
static_cast<int>(size),
4963inline ssize_t send_socket(
socket_t sock,
const void *ptr,
size_t size,
4966 return handle_EINTR([&]()
4970 static_cast<const char *
>(ptr),
static_cast<int>(size),
4978inline int poll_wrapper(
struct pollfd *fds, nfds_t nfds,
int timeout)
4981 return ::WSAPoll(fds, nfds, timeout);
4983 return ::poll(fds, nfds, timeout);
4988inline ssize_t select_impl(
socket_t sock, time_t sec, time_t usec)
4991 if (sock >= FD_SETSIZE)
4996 fd_set fds, *rfds, *wfds;
4999 rfds = (
Read ? &fds :
nullptr);
5000 wfds = (
Read ? nullptr : &fds);
5003 tv.tv_sec =
static_cast<long>(sec);
5004 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
5006 return handle_EINTR([&]()
5008 return select(
static_cast<int>(sock + 1), rfds, wfds,
nullptr, &tv);
5013 pfd.events = (
Read ? POLLIN : POLLOUT);
5015 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
5017 return handle_EINTR([&]()
5019 return poll_wrapper(&pfd, 1, timeout);
5024inline ssize_t select_read(
socket_t sock, time_t sec, time_t usec)
5026 return select_impl<true>(sock, sec, usec);
5029inline ssize_t select_write(
socket_t sock, time_t sec, time_t usec)
5031 return select_impl<false>(sock, sec, usec);
5034inline Error wait_until_socket_is_ready(
socket_t sock, time_t sec,
5038 if (sock >= FD_SETSIZE)
5046 FD_SET(sock, &fdsr);
5047 FD_SET(sock, &fdsw);
5050 tv.tv_sec =
static_cast<long>(sec);
5051 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
5053 auto ret = handle_EINTR([&]()
5055 return select(
static_cast<int>(sock + 1), &fdsr, &fdsw,
nullptr, &tv);
5063 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)))
5066 socklen_t len =
sizeof(
error);
5067 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5068 reinterpret_cast<char *
>(&error), &len);
5069 auto successful = res >= 0 && !
error;
5075 struct pollfd pfd_read;
5077 pfd_read.events = POLLIN | POLLOUT;
5079 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
5084 return poll_wrapper(&pfd_read, 1, timeout);
5092 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT))
5095 socklen_t len =
sizeof(
error);
5096 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5097 reinterpret_cast<char *
>(&error), &len);
5098 auto successful = res >= 0 && !
error;
5106inline bool is_socket_alive(
socket_t sock)
5108 const auto val = detail::select_read(sock, 0, 0);
5113 else if (val < 0 &&
errno == EBADF)
5118 return detail::read_socket(sock, &buf[0],
sizeof(buf), MSG_PEEK) > 0;
5121class SocketStream final :
public Stream
5124 SocketStream(
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5125 time_t write_timeout_sec, time_t write_timeout_usec,
5126 time_t max_timeout_msec = 0,
5127 std::chrono::time_point<std::chrono::steady_clock> start_time =
5128 (std::chrono::steady_clock::time_point::min)());
5129 ~SocketStream()
override;
5131 bool is_readable()
const override;
5132 bool wait_readable()
const override;
5133 bool wait_writable()
const override;
5134 ssize_t read(
char *ptr,
size_t size)
override;
5135 ssize_t write(
const char *ptr,
size_t size)
override;
5136 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
5137 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
5139 time_t duration()
const override;
5143 time_t read_timeout_sec_;
5144 time_t read_timeout_usec_;
5145 time_t write_timeout_sec_;
5146 time_t write_timeout_usec_;
5147 time_t max_timeout_msec_;
5148 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5150 std::vector<char> read_buff_;
5151 size_t read_buff_off_ = 0;
5152 size_t read_buff_content_size_ = 0;
5154 static const size_t read_buff_size_ = 1024l * 4;
5157#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5158class SSLSocketStream final :
public Stream
5162 socket_t sock, SSL *ssl, time_t read_timeout_sec,
5163 time_t read_timeout_usec, time_t write_timeout_sec,
5164 time_t write_timeout_usec, time_t max_timeout_msec = 0,
5165 std::chrono::time_point<std::chrono::steady_clock> start_time =
5166 (std::chrono::steady_clock::time_point::min)());
5167 ~SSLSocketStream()
override;
5169 bool is_readable()
const override;
5170 bool wait_readable()
const override;
5171 bool wait_writable()
const override;
5172 ssize_t read(
char *ptr,
size_t size)
override;
5173 ssize_t write(
const char *ptr,
size_t size)
override;
5174 void get_remote_ip_and_port(std::string &ip,
int &port)
const override;
5175 void get_local_ip_and_port(std::string &ip,
int &port)
const override;
5177 time_t duration()
const override;
5182 time_t read_timeout_sec_;
5183 time_t read_timeout_usec_;
5184 time_t write_timeout_sec_;
5185 time_t write_timeout_usec_;
5186 time_t max_timeout_msec_;
5187 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5191inline bool keep_alive(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
5192 time_t keep_alive_timeout_sec)
5194 using namespace std::chrono;
5196 const auto interval_usec =
5200 if (select_read(sock, 0, interval_usec) > 0)
5205 const auto start = steady_clock::now() - microseconds{interval_usec};
5206 const auto timeout = seconds{keep_alive_timeout_sec};
5215 auto val = select_read(sock, 0, interval_usec);
5222 if (steady_clock::now() - start > timeout)
5236template <
typename T>
5238process_server_socket_core(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
5239 size_t keep_alive_max_count,
5240 time_t keep_alive_timeout_sec, T callback)
5242 assert(keep_alive_max_count > 0);
5244 auto count = keep_alive_max_count;
5245 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec))
5247 auto close_connection = count == 1;
5248 auto connection_closed =
false;
5249 ret = callback(close_connection, connection_closed);
5250 if (!ret || connection_closed)
5259template <
typename T>
5261process_server_socket(
const std::atomic<socket_t> &svr_sock,
socket_t sock,
5262 size_t keep_alive_max_count,
5263 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
5264 time_t read_timeout_usec, time_t write_timeout_sec,
5265 time_t write_timeout_usec, T callback)
5267 return process_server_socket_core(
5268 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
5269 [&](
bool close_connection,
bool & connection_closed)
5271 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5272 write_timeout_sec, write_timeout_usec);
5273 return callback(strm, close_connection, connection_closed);
5277inline bool process_client_socket(
5278 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5279 time_t write_timeout_sec, time_t write_timeout_usec,
5280 time_t max_timeout_msec,
5281 std::chrono::time_point<std::chrono::steady_clock> start_time,
5282 std::function<
bool(Stream &)> callback)
5284 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5285 write_timeout_sec, write_timeout_usec, max_timeout_msec,
5287 return callback(strm);
5290inline int shutdown_socket(
socket_t sock)
5293 return shutdown(sock, SD_BOTH);
5295 return shutdown(sock, SHUT_RDWR);
5299inline std::string escape_abstract_namespace_unix_domain(
const std::string &s)
5301 if (s.size() > 1 && s[0] ==
'\0')
5311unescape_abstract_namespace_unix_domain(
const std::string &s)
5313 if (s.size() > 1 && s[0] ==
'@')
5322inline int getaddrinfo_with_timeout(
const char *node,
const char *service,
5323 const struct addrinfo *hints,
5324 struct addrinfo **res, time_t timeout_sec)
5326#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
5327 if (timeout_sec <= 0)
5330 return getaddrinfo(node, service, hints, res);
5335 OVERLAPPED overlapped = {0};
5336 HANDLE
event = CreateEventW(
nullptr,
TRUE,
FALSE,
nullptr);
5342 overlapped.hEvent = event;
5344 PADDRINFOEXW result_addrinfo =
nullptr;
5345 HANDLE cancel_handle =
nullptr;
5347 ADDRINFOEXW hints_ex = {0};
5350 hints_ex.ai_flags = hints->ai_flags;
5351 hints_ex.ai_family = hints->ai_family;
5352 hints_ex.ai_socktype = hints->ai_socktype;
5353 hints_ex.ai_protocol = hints->ai_protocol;
5356 auto wnode = u8string_to_wstring(node);
5357 auto wservice = u8string_to_wstring(service);
5359 auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS,
nullptr,
5360 hints ? &hints_ex : nullptr, &result_addrinfo,
5361 nullptr, &overlapped, nullptr, &cancel_handle);
5363 if (ret == WSA_IO_PENDING)
5366 ::WaitForSingleObject(event,
static_cast<DWORD
>(timeout_sec * 1000));
5367 if (wait_result == WAIT_TIMEOUT)
5371 ::GetAddrInfoExCancel(&cancel_handle);
5373 ::CloseHandle(event);
5377 DWORD bytes_returned;
5379 &bytes_returned,
FALSE))
5381 ::CloseHandle(event);
5382 return ::WSAGetLastError();
5386 ::CloseHandle(event);
5388 if (ret == NO_ERROR || ret == WSA_IO_PENDING)
5390 *res =
reinterpret_cast<struct addrinfo *
>(result_addrinfo);
5397 CFStringRef hostname_ref = CFStringCreateWithCString(
5398 kCFAllocatorDefault, node, kCFStringEncodingUTF8);
5404 CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
5405 CFRelease(hostname_ref);
5412 struct CFHostContext
5414 bool completed =
false;
5415 bool success =
false;
5416 CFArrayRef addresses =
nullptr;
5418 std::condition_variable cv;
5421 CFHostClientContext client_context;
5422 memset(&client_context, 0,
sizeof(client_context));
5423 client_context.info = &context;
5426 auto callback = [](CFHostRef theHost, CFHostInfoType ,
5427 const CFStreamError *
error,
void *info)
5429 auto ctx =
static_cast<CFHostContext *
>(info);
5430 std::lock_guard<std::mutex> lock(ctx->mutex);
5432 if (error &&
error->error != 0)
5434 ctx->success =
false;
5438 Boolean hasBeenResolved;
5439 ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
5440 if (ctx->addresses && hasBeenResolved)
5442 CFRetain(ctx->addresses);
5443 ctx->success =
true;
5447 ctx->success =
false;
5450 ctx->completed =
true;
5451 ctx->cv.notify_one();
5454 if (!CFHostSetClient(host_ref, callback, &client_context))
5456 CFRelease(host_ref);
5461 CFRunLoopRef run_loop = CFRunLoopGetCurrent();
5462 CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5465 CFStreamError stream_error;
5466 if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error))
5468 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5469 CFRelease(host_ref);
5475 std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
5476 bool timed_out =
false;
5479 std::unique_lock<std::mutex> lock(context.mutex);
5481 while (!context.completed)
5483 auto now = std::chrono::steady_clock::now();
5484 if (now >= timeout_time)
5492 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1,
true);
5498 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5499 CFHostSetClient(host_ref,
nullptr,
nullptr);
5501 if (timed_out || !context.completed)
5503 CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
5504 CFRelease(host_ref);
5508 if (!context.success || !context.addresses)
5510 CFRelease(host_ref);
5515 CFIndex count = CFArrayGetCount(context.addresses);
5518 CFRelease(context.addresses);
5519 CFRelease(host_ref);
5523 struct addrinfo *result_addrinfo =
nullptr;
5524 struct addrinfo **current = &result_addrinfo;
5526 for (CFIndex i = 0; i < count; i++)
5528 CFDataRef addr_data =
5529 static_cast<CFDataRef
>(CFArrayGetValueAtIndex(context.addresses, i));
5530 if (!addr_data)
continue;
5532 const struct sockaddr *sockaddr_ptr =
5533 reinterpret_cast<const struct sockaddr *
>(CFDataGetBytePtr(addr_data));
5534 socklen_t sockaddr_len =
static_cast<socklen_t
>(CFDataGetLength(addr_data));
5537 *current =
static_cast<struct addrinfo *
>(malloc(
sizeof(
struct addrinfo)));
5540 freeaddrinfo(result_addrinfo);
5541 CFRelease(context.addresses);
5542 CFRelease(host_ref);
5546 memset(*current, 0,
sizeof(
struct addrinfo));
5549 (*current)->ai_family = sockaddr_ptr->sa_family;
5550 (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
5551 (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
5552 (*current)->ai_addrlen = sockaddr_len;
5555 (*current)->ai_addr =
static_cast<struct sockaddr *
>(malloc(sockaddr_len));
5556 if (!(*current)->ai_addr)
5558 freeaddrinfo(result_addrinfo);
5559 CFRelease(context.addresses);
5560 CFRelease(host_ref);
5563 memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
5566 if (service && strlen(service) > 0)
5568 int port = atoi(service);
5571 if (sockaddr_ptr->sa_family == AF_INET)
5573 reinterpret_cast<struct sockaddr_in *
>((*current)->ai_addr)
5574 ->sin_port = htons(
static_cast<uint16_t
>(port));
5576 else if (sockaddr_ptr->sa_family == AF_INET6)
5578 reinterpret_cast<struct sockaddr_in6 *
>((*current)->ai_addr)
5579 ->sin6_port = htons(
static_cast<uint16_t
>(port));
5584 current = &((*current)->ai_next);
5587 CFRelease(context.addresses);
5588 CFRelease(host_ref);
5590 *res = result_addrinfo;
5592#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
5593 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
5595 struct gaicb request;
5596 struct gaicb *requests[1] = {&request};
5597 struct sigevent sevp;
5598 struct timespec timeout;
5601 memset(&request, 0,
sizeof(request));
5602 request.ar_name = node;
5603 request.ar_service = service;
5604 request.ar_request = hints;
5607 timeout.tv_sec = timeout_sec;
5608 timeout.tv_nsec = 0;
5611 memset(&sevp, 0,
sizeof(sevp));
5612 sevp.sigev_notify = SIGEV_NONE;
5615 int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
5616 if (start_result != 0)
5618 return start_result;
5623 gai_suspend((
const struct gaicb *
const *)requests, 1, &timeout);
5625 if (wait_result == 0 || wait_result == EAI_ALLDONE)
5628 int gai_result = gai_error(&request);
5629 if (gai_result == 0)
5631 *res = request.ar_result;
5637 if (request.ar_result)
5639 freeaddrinfo(request.ar_result);
5644 else if (wait_result == EAI_AGAIN)
5647 gai_cancel(&request);
5653 gai_cancel(&request);
5659 struct GetAddrInfoState
5670 std::condition_variable result_cv;
5671 bool completed =
false;
5672 int result = EAI_SYSTEM;
5674 std::string service;
5675 struct addrinfo hints;
5676 struct addrinfo *info =
nullptr;
5680 auto state = std::make_shared<GetAddrInfoState>();
5682 state->service = service;
5683 state->hints = *hints;
5685 std::thread resolve_thread([state]()
5687 auto thread_result =
5688 getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
5691 std::lock_guard<std::mutex> lock(state->mutex);
5692 state->result = thread_result;
5693 state->completed =
true;
5694 state->result_cv.notify_one();
5698 std::unique_lock<std::mutex> lock(state->mutex);
5700 state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
5701 [&] {
return state->completed; });
5706 resolve_thread.join();
5708 state->info =
nullptr;
5709 return state->result;
5714 resolve_thread.detach();
5719 (void)(timeout_sec);
5720 return getaddrinfo(node, service, hints, res);
5724template <
typename BindOrConnect>
5725socket_t create_socket(
const std::string &host,
const std::string &ip,
int port,
5726 int address_family,
int socket_flags,
bool tcp_nodelay,
5728 BindOrConnect bind_or_connect, time_t timeout_sec = 0)
5731 const char *node =
nullptr;
5732 struct addrinfo hints;
5733 struct addrinfo *result;
5735 memset(&hints, 0,
sizeof(
struct addrinfo));
5736 hints.ai_socktype = SOCK_STREAM;
5737 hints.ai_protocol = IPPROTO_IP;
5743 hints.ai_family = AF_UNSPEC;
5744 hints.ai_flags = AI_NUMERICHOST;
5750 node = host.c_str();
5752 hints.ai_family = address_family;
5753 hints.ai_flags = socket_flags;
5756#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
5757 if (hints.ai_family == AF_UNIX)
5759 const auto addrlen = host.length();
5760 if (addrlen >
sizeof(sockaddr_un::sun_path))
5766 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
5769 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
5775 addr.sun_family = AF_UNIX;
5777 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
5778 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
5780 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
5781 hints.ai_addrlen =
static_cast<socklen_t
>(
5782 sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
5786 fcntl(sock, F_SETFD, FD_CLOEXEC);
5792 socket_options(sock);
5798 detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
5802 if (!bind_or_connect(sock, hints, dummy))
5812 auto service = std::to_string(port);
5814 if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
5817#if defined __linux__ && !defined __ANDROID__
5822 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5824 for (
auto rp = result; rp; rp = rp->ai_next)
5829 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
5830 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
5847 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
5853 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
5855 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
5864#if !defined _WIN32 && !defined SOCK_CLOEXEC
5865 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1)
5874 set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
5877 if (rp->ai_family == AF_INET6)
5879 set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
5884 socket_options(sock);
5889 if (bind_or_connect(sock, *rp, quit))
5905inline void set_nonblocking(
socket_t sock,
bool nonblocking)
5908 auto flags = nonblocking ? 1UL : 0UL;
5909 ioctlsocket(sock, FIONBIO, &flags);
5911 auto flags = fcntl(sock, F_GETFL, 0);
5912 fcntl(sock, F_SETFL,
5913 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
5917inline bool is_connection_error()
5920 return WSAGetLastError() != WSAEWOULDBLOCK;
5922 return errno != EINPROGRESS;
5926inline bool bind_ip_address(
socket_t sock,
const std::string &host)
5928 struct addrinfo hints;
5929 struct addrinfo *result;
5931 memset(&hints, 0,
sizeof(
struct addrinfo));
5932 hints.ai_family = AF_UNSPEC;
5933 hints.ai_socktype = SOCK_STREAM;
5934 hints.ai_protocol = 0;
5936 if (getaddrinfo_with_timeout(host.c_str(),
"0", &hints, &result, 0))
5941 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5944 for (
auto rp = result; rp; rp = rp->ai_next)
5946 const auto &ai = *rp;
5947 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen)))
5957#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
5962inline std::string if2ip(
int address_family,
const std::string &ifn)
5964 struct ifaddrs *ifap;
5966 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
5968 std::string addr_candidate;
5969 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next)
5971 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
5972 (AF_UNSPEC == address_family ||
5973 ifa->ifa_addr->sa_family == address_family))
5975 if (ifa->ifa_addr->sa_family == AF_INET)
5977 auto sa =
reinterpret_cast<struct sockaddr_in *
>(ifa->ifa_addr);
5978 char buf[INET_ADDRSTRLEN];
5979 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN))
5981 return std::string(buf, INET_ADDRSTRLEN);
5984 else if (ifa->ifa_addr->sa_family == AF_INET6)
5986 auto sa =
reinterpret_cast<struct sockaddr_in6 *
>(ifa->ifa_addr);
5987 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
5989 char buf[INET6_ADDRSTRLEN] = {};
5990 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN))
5993 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
5994 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd)
5996 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
6000 return std::string(buf, INET6_ADDRSTRLEN);
6007 return addr_candidate;
6011inline socket_t create_client_socket(
6012 const std::string &host,
const std::string &ip,
int port,
6013 int address_family,
bool tcp_nodelay,
bool ipv6_v6only,
6014 SocketOptions socket_options, time_t connection_timeout_sec,
6015 time_t connection_timeout_usec, time_t read_timeout_sec,
6016 time_t read_timeout_usec, time_t write_timeout_sec,
6017 time_t write_timeout_usec,
const std::string &intf,
Error &error)
6019 auto sock = create_socket(
6020 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
6021 std::move(socket_options),
6022 [&](
socket_t sock2,
struct addrinfo & ai,
bool & quit) ->
bool
6027 auto ip_from_if = if2ip(address_family, intf);
6028 if (ip_from_if.empty())
6032 if (!bind_ip_address(sock2, ip_from_if))
6040 set_nonblocking(sock2,
true);
6043 ::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
6047 if (is_connection_error())
6052 error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
6053 connection_timeout_usec);
6064 set_nonblocking(sock2,
false);
6065 set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
6067 set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
6068 write_timeout_usec);
6073 connection_timeout_sec);
6090inline bool get_ip_and_port(
const struct sockaddr_storage &addr,
6091 socklen_t addr_len, std::string &ip,
int &port)
6093 if (addr.ss_family == AF_INET)
6095 port = ntohs(
reinterpret_cast<const struct sockaddr_in *
>(&addr)->sin_port);
6097 else if (addr.ss_family == AF_INET6)
6100 ntohs(
reinterpret_cast<const struct sockaddr_in6 *
>(&addr)->sin6_port);
6107 std::array<char, NI_MAXHOST> ipstr{};
6108 if (getnameinfo(
reinterpret_cast<const struct sockaddr *
>(&addr), addr_len,
6109 ipstr.data(),
static_cast<socklen_t
>(ipstr.size()),
nullptr,
6119inline void get_local_ip_and_port(
socket_t sock, std::string &ip,
int &port)
6121 struct sockaddr_storage addr;
6122 socklen_t addr_len =
sizeof(addr);
6123 if (!getsockname(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
6126 get_ip_and_port(addr, addr_len, ip, port);
6130inline void get_remote_ip_and_port(
socket_t sock, std::string &ip,
int &port)
6132 struct sockaddr_storage addr;
6133 socklen_t addr_len =
sizeof(addr);
6135 if (!getpeername(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
6139 if (addr.ss_family == AF_UNIX)
6141#if defined(__linux__)
6143 socklen_t len =
sizeof(ucred);
6144 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0)
6148#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
6150 socklen_t len =
sizeof(pid);
6151 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0)
6159 get_ip_and_port(addr, addr_len, ip, port);
6163inline constexpr unsigned int str2tag_core(
const char *s,
size_t l,
6171 (((std::numeric_limits<unsigned int>::max)() >> 6) &
6173 static_cast<unsigned char>(*s));
6176inline unsigned int str2tag(
const std::string &s)
6178 return str2tag_core(s.data(), s.size(), 0);
6184inline constexpr unsigned int operator""_t(
const char *s,
size_t l)
6186 return str2tag_core(s, l, 0);
6192find_content_type(
const std::string &path,
6193 const std::map<std::string, std::string> &user_data,
6194 const std::string &default_content_type)
6196 auto ext = file_extension(path);
6198 auto it = user_data.find(ext);
6199 if (it != user_data.end())
6204 using udl::operator
""_t;
6206 switch (str2tag(ext))
6209 return default_content_type;
6220 return "text/javascript";
6222 return "text/plain";
6227 return "image/apng";
6229 return "image/avif";
6237 return "image/svg+xml";
6239 return "image/webp";
6241 return "image/x-icon";
6243 return "image/tiff";
6245 return "image/tiff";
6248 return "image/jpeg";
6253 return "video/mpeg";
6255 return "video/webm";
6260 return "audio/mpeg";
6262 return "audio/webm";
6264 return "audio/wave";
6273 return "font/woff2";
6276 return "application/x-7z-compressed";
6278 return "application/atom+xml";
6280 return "application/pdf";
6282 return "application/json";
6284 return "application/rss+xml";
6286 return "application/x-tar";
6289 return "application/xhtml+xml";
6291 return "application/xslt+xml";
6293 return "application/xml";
6295 return "application/gzip";
6297 return "application/zip";
6299 return "application/wasm";
6303inline bool can_compress_content_type(
const std::string &content_type)
6305 using udl::operator
""_t;
6307 auto tag = str2tag(content_type);
6311 case "image/svg+xml"_t:
6312 case "application/javascript"_t:
6313 case "application/json"_t:
6314 case "application/xml"_t:
6315 case "application/protobuf"_t:
6316 case "application/xhtml+xml"_t:
6319 case "text/event-stream"_t:
6323 return !content_type.rfind(
"text/", 0);
6327inline EncodingType encoding_type(
const Request &req,
const Response &res)
6330 detail::can_compress_content_type(res.get_header_value(
"Content-Type"));
6333 return EncodingType::None;
6336 const auto &s = req.get_header_value(
"Accept-Encoding");
6339#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6341 ret = s.find(
"br") != std::string::npos;
6344 return EncodingType::Brotli;
6348#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6350 ret = s.find(
"gzip") != std::string::npos;
6353 return EncodingType::Gzip;
6357#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6359 ret = s.find(
"zstd") != std::string::npos;
6362 return EncodingType::Zstd;
6366 return EncodingType::None;
6369inline bool nocompressor::compress(
const char *data,
size_t data_length,
6370 bool , Callback callback)
6376 return callback(data, data_length);
6379#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6380inline gzip_compressor::gzip_compressor()
6382 std::memset(&strm_, 0,
sizeof(strm_));
6383 strm_.zalloc = Z_NULL;
6384 strm_.zfree = Z_NULL;
6385 strm_.opaque = Z_NULL;
6387 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
6388 Z_DEFAULT_STRATEGY) == Z_OK;
6391inline gzip_compressor::~gzip_compressor()
6396inline bool gzip_compressor::compress(
const char *data,
size_t data_length,
6397 bool last, Callback callback)
6403 constexpr size_t max_avail_in =
6404 (std::numeric_limits<
decltype(strm_.avail_in)>
::max)();
6406 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
6407 (std::min)(data_length, max_avail_in));
6408 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
6410 data_length -= strm_.avail_in;
6411 data += strm_.avail_in;
6413 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
6416 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6419 strm_.avail_out =
static_cast<uInt
>(buff.size());
6420 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
6422 ret = deflate(&strm_, flush);
6423 if (ret == Z_STREAM_ERROR)
6428 if (!callback(buff.data(), buff.size() - strm_.avail_out))
6433 while (strm_.avail_out == 0);
6435 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
6436 (flush == Z_NO_FLUSH && ret == Z_OK));
6437 assert(strm_.avail_in == 0);
6439 while (data_length > 0);
6444inline gzip_decompressor::gzip_decompressor()
6446 std::memset(&strm_, 0,
sizeof(strm_));
6447 strm_.zalloc = Z_NULL;
6448 strm_.zfree = Z_NULL;
6449 strm_.opaque = Z_NULL;
6455 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
6458inline gzip_decompressor::~gzip_decompressor()
6463inline bool gzip_decompressor::is_valid()
const
6468inline bool gzip_decompressor::decompress(
const char *data,
size_t data_length,
6477 constexpr size_t max_avail_in =
6478 (std::numeric_limits<
decltype(strm_.avail_in)>
::max)();
6480 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
6481 (std::min)(data_length, max_avail_in));
6482 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
6484 data_length -= strm_.avail_in;
6485 data += strm_.avail_in;
6487 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6488 while (strm_.avail_in > 0 && ret == Z_OK)
6490 strm_.avail_out =
static_cast<uInt
>(buff.size());
6491 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
6493 ret = inflate(&strm_, Z_NO_FLUSH);
6495 assert(ret != Z_STREAM_ERROR);
6505 if (!callback(buff.data(), buff.size() - strm_.avail_out))
6511 if (ret != Z_OK && ret != Z_STREAM_END)
6517 while (data_length > 0);
6523#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6524inline brotli_compressor::brotli_compressor()
6526 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
6529inline brotli_compressor::~brotli_compressor()
6531 BrotliEncoderDestroyInstance(state_);
6534inline bool brotli_compressor::compress(
const char *data,
size_t data_length,
6535 bool last, Callback callback)
6537 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6539 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
6540 auto available_in = data_length;
6541 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
6547 if (BrotliEncoderIsFinished(state_))
6560 auto available_out = buff.size();
6561 auto next_out = buff.data();
6563 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
6564 &available_out, &next_out,
nullptr))
6569 auto output_bytes = buff.size() - available_out;
6572 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
6579inline brotli_decompressor::brotli_decompressor()
6581 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
6582 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
6583 : BROTLI_DECODER_RESULT_ERROR;
6586inline brotli_decompressor::~brotli_decompressor()
6590 BrotliDecoderDestroyInstance(decoder_s);
6594inline bool brotli_decompressor::is_valid()
const
6599inline bool brotli_decompressor::decompress(
const char *data,
6603 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6604 decoder_r == BROTLI_DECODER_RESULT_ERROR)
6609 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
6610 size_t avail_in = data_length;
6613 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
6615 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6616 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT)
6618 char *next_out = buff.data();
6619 size_t avail_out = buff.size();
6621 decoder_r = BrotliDecoderDecompressStream(
6622 decoder_s, &avail_in, &next_in, &avail_out,
6623 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
6625 if (decoder_r == BROTLI_DECODER_RESULT_ERROR)
6630 if (!callback(buff.data(), buff.size() - avail_out))
6636 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6637 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
6641#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6642inline zstd_compressor::zstd_compressor()
6644 ctx_ = ZSTD_createCCtx();
6645 ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
6648inline zstd_compressor::~zstd_compressor()
6650 ZSTD_freeCCtx(ctx_);
6653inline bool zstd_compressor::compress(
const char *data,
size_t data_length,
6654 bool last, Callback callback)
6656 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6658 ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
6659 ZSTD_inBuffer input = {data, data_length, 0};
6665 size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
6667 if (ZSTD_isError(remaining))
6672 if (!callback(buff.data(), output.pos))
6677 finished = last ? (remaining == 0) : (input.pos == input.size);
6685inline zstd_decompressor::zstd_decompressor()
6687 ctx_ = ZSTD_createDCtx();
6690inline zstd_decompressor::~zstd_decompressor()
6692 ZSTD_freeDCtx(ctx_);
6695inline bool zstd_decompressor::is_valid()
const
6697 return ctx_ !=
nullptr;
6700inline bool zstd_decompressor::decompress(
const char *data,
size_t data_length,
6703 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6704 ZSTD_inBuffer input = {data, data_length, 0};
6706 while (input.pos < input.size)
6709 size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
6711 if (ZSTD_isError(remaining))
6716 if (!callback(buff.data(), output.pos))
6726inline std::unique_ptr<decompressor>
6727create_decompressor(
const std::string &encoding)
6729 std::unique_ptr<decompressor> decompressor;
6731 if (encoding ==
"gzip" || encoding ==
"deflate")
6733#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6734 decompressor = detail::make_unique<gzip_decompressor>();
6737 else if (encoding.find(
"br") != std::string::npos)
6739#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6740 decompressor = detail::make_unique<brotli_decompressor>();
6743 else if (encoding ==
"zstd" || encoding.find(
"zstd") != std::string::npos)
6745#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6746 decompressor = detail::make_unique<zstd_decompressor>();
6750 return decompressor;
6753inline bool is_prohibited_header_name(
const std::string &name)
6755 using udl::operator
""_t;
6757 switch (str2tag(name))
6759 case "REMOTE_ADDR"_t:
6760 case "REMOTE_PORT"_t:
6761 case "LOCAL_ADDR"_t:
6762 case "LOCAL_PORT"_t:
6769inline bool has_header(
const Headers &headers,
const std::string &key)
6771 if (is_prohibited_header_name(key))
6775 return headers.find(key) != headers.end();
6778inline const char *get_header_value(
const Headers &headers,
6779 const std::string &key,
const char *def,
6782 if (is_prohibited_header_name(key))
6784#ifndef CPPHTTPLIB_NO_EXCEPTIONS
6785 std::string msg =
"Prohibited header name '" + key +
"' is specified.";
6786 throw std::invalid_argument(msg);
6792 auto rng = headers.equal_range(key);
6793 auto it = rng.first;
6794 std::advance(it,
static_cast<ssize_t
>(
id));
6795 if (it != rng.second)
6797 return it->second.c_str();
6802inline bool read_headers(Stream &strm, Headers &headers)
6804 const auto bufsiz = 2048;
6806 stream_line_reader line_reader(strm, buf, bufsiz);
6808 size_t header_count = 0;
6812 if (!line_reader.getline())
6818 auto line_terminator_len = 2;
6819 if (line_reader.end_with_crlf())
6822 if (line_reader.size() == 2)
6829#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
6831 if (line_reader.size() == 1)
6835 line_terminator_len = 1;
6853 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
6855 if (!parse_header(line_reader.ptr(), end,
6856 [&](
const std::string & key,
const std::string & val)
6858 headers.emplace(key, val);
6870inline bool read_content_with_length(Stream &strm,
size_t len,
6871 DownloadProgress progress,
6872 ContentReceiverWithProgress out)
6876 detail::BodyReader br;
6878 br.content_length = len;
6881 br.last_error = Error::Success;
6886 auto read_len =
static_cast<size_t>(len - r);
6888 auto n = detail::read_body_content(&strm, br, buf, to_read);
6894 if (!out(buf,
static_cast<size_t>(n), r, len))
6898 r +=
static_cast<size_t>(n);
6902 if (!progress(r, len))
6912inline void skip_content_with_length(Stream &strm,
size_t len)
6918 auto read_len =
static_cast<size_t>(len - r);
6924 r +=
static_cast<size_t>(n);
6928enum class ReadContentResult
6935inline ReadContentResult
6936read_content_without_length(Stream &strm,
size_t payload_max_length,
6937 ContentReceiverWithProgress out)
6946 return ReadContentResult::Success;
6950 return ReadContentResult::Error;
6954 if (r > payload_max_length ||
6955 payload_max_length - r <
static_cast<size_t>(n))
6957 return ReadContentResult::PayloadTooLarge;
6960 if (!out(buf,
static_cast<size_t>(n), r, 0))
6962 return ReadContentResult::Error;
6964 r +=
static_cast<size_t>(n);
6967 return ReadContentResult::Success;
6970template <
typename T>
6971inline ReadContentResult read_content_chunked(Stream &strm, T &x,
6972 size_t payload_max_length,
6973 ContentReceiverWithProgress out)
6975 detail::ChunkedDecoder
dec(strm);
6978 size_t total_len = 0;
6982 size_t chunk_offset = 0;
6983 size_t chunk_total = 0;
6984 auto n =
dec.read_payload(buf,
sizeof(buf), chunk_offset, chunk_total);
6987 return ReadContentResult::Error;
6992 if (!
dec.parse_trailers_into(x.trailers, x.headers))
6994 return ReadContentResult::Error;
6996 return ReadContentResult::Success;
6999 if (total_len > payload_max_length ||
7000 payload_max_length - total_len <
static_cast<size_t>(n))
7002 return ReadContentResult::PayloadTooLarge;
7005 if (!out(buf,
static_cast<size_t>(n), chunk_offset, chunk_total))
7007 return ReadContentResult::Error;
7010 total_len +=
static_cast<size_t>(n);
7014inline bool is_chunked_transfer_encoding(
const Headers &headers)
7016 return case_ignore::equal(
7017 get_header_value(headers,
"Transfer-Encoding",
"", 0),
"chunked");
7020template <
typename T,
typename U>
7021bool prepare_content_receiver(T &x,
int &status,
7022 ContentReceiverWithProgress
receiver,
7023 bool decompress, U callback)
7027 std::string encoding = x.get_header_value(
"Content-Encoding");
7028 std::unique_ptr<decompressor> decompressor;
7030 if (!encoding.empty())
7032 decompressor = detail::create_decompressor(encoding);
7036 status = StatusCode::UnsupportedMediaType_415;
7043 if (decompressor->is_valid())
7046 size_t off,
size_t len)
7048 return decompressor->decompress(buf, n,
7049 [&](
const char *buf2,
size_t n2)
7051 return receiver(buf2, n2, off, len);
7054 return callback(std::move(out));
7058 status = StatusCode::InternalServerError_500;
7069 return callback(std::move(out));
7072template <
typename T>
7073bool read_content(Stream &strm, T &x,
size_t payload_max_length,
int &status,
7074 DownloadProgress progress,
7075 ContentReceiverWithProgress
receiver,
bool decompress)
7077 return prepare_content_receiver(
7078 x, status, std::move(
receiver), decompress,
7079 [&](
const ContentReceiverWithProgress & out)
7082 auto exceed_payload_max_length =
false;
7084 if (is_chunked_transfer_encoding(x.headers))
7086 auto result = read_content_chunked(strm, x, payload_max_length, out);
7087 if (result == ReadContentResult::Success)
7091 else if (result == ReadContentResult::PayloadTooLarge)
7093 exceed_payload_max_length =
true;
7101 else if (!has_header(x.headers,
"Content-Length"))
7104 read_content_without_length(strm, payload_max_length, out);
7105 if (result == ReadContentResult::Success)
7109 else if (result == ReadContentResult::PayloadTooLarge)
7111 exceed_payload_max_length =
true;
7121 auto is_invalid_value =
false;
7122 auto len = get_header_value_u64(x.headers,
"Content-Length",
7123 (std::numeric_limits<size_t>::max)(),
7124 0, is_invalid_value);
7126 if (is_invalid_value)
7130 else if (len > payload_max_length)
7132 exceed_payload_max_length =
true;
7133 skip_content_with_length(strm, len);
7138 ret = read_content_with_length(strm, len, std::move(progress), out);
7144 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
7145 : StatusCode::BadRequest_400;
7151inline ssize_t write_request_line(Stream &strm,
const std::string &method,
7152 const std::string &path)
7154 std::string s = method;
7157 s +=
" HTTP/1.1\r\n";
7158 return strm.write(s.data(), s.size());
7161inline ssize_t write_response_line(Stream &strm,
int status)
7163 std::string s =
"HTTP/1.1 ";
7164 s += std::to_string(status);
7168 return strm.write(s.data(), s.size());
7173 ssize_t write_len = 0;
7174 for (
const auto &x : headers)
7182 auto len = strm.
write(s.data(), s.size());
7189 auto len = strm.
write(
"\r\n");
7198inline bool write_data(
Stream &strm,
const char *d,
size_t l)
7203 auto length = strm.
write(d + offset, l - offset);
7208 offset +=
static_cast<size_t>(length);
7213template <
typename T>
7214inline bool write_content_with_progress(Stream &strm,
7215 const ContentProvider &content_provider,
7216 size_t offset,
size_t length,
7218 const UploadProgress &upload_progress,
7221 size_t end_offset = offset + length;
7222 size_t start_offset = offset;
7226 data_sink.write = [&](
const char *d,
size_t l) ->
bool
7230 if (write_data(strm, d, l))
7234 if (upload_progress && length > 0)
7236 size_t current_written = offset - start_offset;
7237 if (!upload_progress(current_written, length))
7252 data_sink.is_writable = [&]() ->
bool {
return strm.wait_writable(); };
7254 while (offset < end_offset && !is_shutting_down())
7256 if (!strm.wait_writable())
7258 error = Error::Write;
7261 else if (!content_provider(offset, end_offset - offset, data_sink))
7263 error = Error::Canceled;
7268 error = Error::Write;
7273 error = Error::Success;
7277template <
typename T>
7278inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
7279 size_t offset,
size_t length, T is_shutting_down,
7282 return write_content_with_progress<T>(strm, content_provider, offset, length,
7283 is_shutting_down,
nullptr, error);
7286template <
typename T>
7287inline bool write_content(Stream &strm,
const ContentProvider &content_provider,
7288 size_t offset,
size_t length,
7289 const T &is_shutting_down)
7291 auto error = Error::Success;
7292 return write_content(strm, content_provider, offset, length, is_shutting_down,
7296template <
typename T>
7298write_content_without_length(Stream &strm,
7299 const ContentProvider &content_provider,
7300 const T &is_shutting_down)
7303 auto data_available =
true;
7307 data_sink.write = [&](
const char *d,
size_t l) ->
bool
7312 if (!write_data(strm, d, l))
7320 data_sink.is_writable = [&]() ->
bool {
return strm.wait_writable(); };
7322 data_sink.done = [&](void)
7324 data_available =
false;
7327 while (data_available && !is_shutting_down())
7329 if (!strm.wait_writable())
7333 else if (!content_provider(offset, 0, data_sink))
7345template <
typename T,
typename U>
7347write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
7348 const T &is_shutting_down, U &compressor, Error &error)
7351 auto data_available =
true;
7355 data_sink.write = [&](
const char *d,
size_t l) ->
bool
7359 data_available = l > 0;
7362 std::string payload;
7363 if (compressor.compress(d, l,
false,
7364 [&](
const char *data,
size_t data_len)
7366 payload.append(data, data_len);
7370 if (!payload.empty())
7374 from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
7375 if (!write_data(strm, chunk.data(), chunk.size()))
7389 data_sink.is_writable = [&]() ->
bool {
return strm.wait_writable(); };
7391 auto done_with_trailer = [&](
const Headers * trailer)
7398 data_available =
false;
7400 std::string payload;
7401 if (!compressor.compress(
nullptr, 0,
true,
7402 [&](
const char *data,
size_t data_len)
7404 payload.append(data, data_len);
7412 if (!payload.empty())
7415 auto chunk = from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
7416 if (!write_data(strm, chunk.data(), chunk.size()))
7423 constexpr const char done_marker[] =
"0\r\n";
7424 if (!write_data(strm, done_marker, str_len(done_marker)))
7432 for (
const auto &kv : *trailer)
7434 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
7435 if (!write_data(strm, field_line.data(), field_line.size()))
7442 constexpr const char crlf[] =
"\r\n";
7443 if (!write_data(strm, crlf, str_len(crlf)))
7449 data_sink.done = [&](void)
7451 done_with_trailer(
nullptr);
7454 data_sink.done_with_trailer = [&](
const Headers & trailer)
7456 done_with_trailer(&trailer);
7459 while (data_available && !is_shutting_down())
7461 if (!strm.wait_writable())
7463 error = Error::Write;
7466 else if (!content_provider(offset, 0, data_sink))
7468 error = Error::Canceled;
7473 error = Error::Write;
7478 error = Error::Success;
7482template <
typename T,
typename U>
7483inline bool write_content_chunked(Stream &strm,
7484 const ContentProvider &content_provider,
7485 const T &is_shutting_down, U &compressor)
7487 auto error = Error::Success;
7488 return write_content_chunked(strm, content_provider, is_shutting_down,
7492template <
typename T>
7493inline bool redirect(T &cli, Request &req, Response &res,
7494 const std::string &path,
const std::string &location,
7497 Request new_req = req;
7498 new_req.path = path;
7499 new_req.redirect_count_ -= 1;
7501 if (res.status == StatusCode::SeeOther_303 &&
7502 (req.method !=
"GET" && req.method !=
"HEAD"))
7504 new_req.method =
"GET";
7505 new_req.body.clear();
7506 new_req.headers.clear();
7511 auto ret = cli.send(new_req, new_res, error);
7514 req = std::move(new_req);
7515 res = std::move(new_res);
7517 if (res.location.empty())
7519 res.location = location;
7525inline std::string params_to_query_str(
const Params ¶ms)
7529 for (
auto it = params.begin(); it != params.end(); ++it)
7531 if (it != params.begin())
7542inline void parse_query_text(
const char *data, std::size_t size,
7545 std::set<std::string> cache;
7546 split(data, data + size,
'&', [&](
const char *b,
const char *e)
7548 std::string kv(b, e);
7549 if (cache.find(kv) != cache.end())
7553 cache.insert(std::move(kv));
7557 divide(b,
static_cast<std::size_t
>(e - b),
'=',
7558 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data,
7559 std::size_t rhs_size)
7561 key.assign(lhs_data, lhs_size);
7562 val.assign(rhs_data, rhs_size);
7572inline void parse_query_text(
const std::string &s, Params ¶ms)
7574 parse_query_text(s.data(), s.size(), params);
7581inline std::string normalize_query_string(
const std::string &query)
7584 split(query.data(), query.data() + query.size(),
'&',
7585 [&](
const char *b,
const char *e)
7589 divide(b, static_cast<std::size_t>(e - b),
'=',
7590 [&](const char *lhs_data, std::size_t lhs_size,
7591 const char *rhs_data, std::size_t rhs_size)
7593 key.assign(lhs_data, lhs_size);
7594 val.assign(rhs_data, rhs_size);
7602 if (!result.empty())
7607 if (!val.empty() || std::find(b, e,
'=') != e)
7617inline bool parse_multipart_boundary(
const std::string &content_type,
7618 std::string &boundary)
7620 auto boundary_keyword =
"boundary=";
7621 auto pos = content_type.find(boundary_keyword);
7622 if (pos == std::string::npos)
7626 auto end = content_type.find(
';', pos);
7627 auto beg = pos + strlen(boundary_keyword);
7628 boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
7629 return !boundary.empty();
7632inline void parse_disposition_params(
const std::string &s, Params ¶ms)
7634 std::set<std::string> cache;
7635 split(s.data(), s.data() + s.size(),
';', [&](
const char *b,
const char *e)
7637 std::string kv(b, e);
7638 if (cache.find(kv) != cache.end())
7646 split(b, e,
'=', [&](
const char *b2,
const char *e2)
7660 params.emplace(trim_double_quotes_copy((key)),
7661 trim_double_quotes_copy((val)));
7666#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7667inline bool parse_range_header(
const std::string &s, Ranges &ranges)
7670inline bool parse_range_header(
const std::string &s, Ranges &ranges)
try
7673 auto is_valid = [](
const std::string & str)
7675 return std::all_of(str.cbegin(), str.cend(),
7678 return std::isdigit(c);
7682 if (s.size() > 7 && s.compare(0, 6,
"bytes=") == 0)
7684 const auto pos =
static_cast<size_t>(6);
7685 const auto len =
static_cast<size_t>(s.size() - 6);
7686 auto all_valid_ranges =
true;
7687 split(&s[pos], &s[pos + len],
',', [&](
const char *b,
const char *e)
7689 if (!all_valid_ranges)
7694 const auto it = std::find(b, e,
'-');
7697 all_valid_ranges =
false;
7701 const auto lhs = std::string(b, it);
7702 const auto rhs = std::string(it + 1, e);
7703 if (!is_valid(lhs) || !is_valid(rhs))
7705 all_valid_ranges =
false;
7710 static_cast<ssize_t
>(lhs.empty() ? -1 : std::stoll(lhs));
7712 static_cast<ssize_t
>(rhs.empty() ? -1 : std::stoll(rhs));
7713 if ((first == -1 && last == -1) ||
7714 (first != -1 && last != -1 && first > last))
7716 all_valid_ranges =
false;
7720 ranges.emplace_back(first, last);
7722 return all_valid_ranges && !ranges.empty();
7725#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7735inline bool parse_accept_header(
const std::string &s,
7736 std::vector<std::string> &content_types)
7738 content_types.clear();
7747 if (s.front() ==
',' || s.back() ==
',' ||
7748 s.find(
",,") != std::string::npos)
7755 std::string media_type;
7760 std::vector<AcceptEntry> entries;
7762 bool has_invalid_entry =
false;
7765 split(s.data(), s.data() + s.size(),
',', [&](
const char *b,
const char *e)
7767 std::string entry(b, e);
7768 entry = trim_copy(entry);
7772 has_invalid_entry = true;
7776 AcceptEntry accept_entry;
7777 accept_entry.quality = 1.0;
7778 accept_entry.order = order++;
7781 auto q_pos = entry.find(
";q=");
7782 if (q_pos == std::string::npos)
7784 q_pos = entry.find(
"; q=");
7787 if (q_pos != std::string::npos)
7790 accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
7793 auto q_start = entry.find(
'=', q_pos) + 1;
7794 auto q_end = entry.find(
';', q_start);
7795 if (q_end == std::string::npos)
7797 q_end = entry.length();
7800 std::string quality_str =
7801 trim_copy(entry.substr(q_start, q_end - q_start));
7802 if (quality_str.empty())
7804 has_invalid_entry =
true;
7808#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7810 std::istringstream iss(quality_str);
7811 iss >> accept_entry.quality;
7814 if (iss.fail() || !iss.eof())
7816 has_invalid_entry =
true;
7823 accept_entry.quality = std::stod(quality_str);
7827 has_invalid_entry =
true;
7832 if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0)
7834 has_invalid_entry =
true;
7841 accept_entry.media_type = entry;
7845 auto param_pos = accept_entry.media_type.find(
';');
7846 if (param_pos != std::string::npos)
7848 accept_entry.media_type =
7849 trim_copy(accept_entry.media_type.substr(0, param_pos));
7853 if (accept_entry.media_type.empty())
7855 has_invalid_entry =
true;
7860 if (accept_entry.media_type !=
"*" &&
7861 accept_entry.media_type.find(
'/') == std::string::npos)
7863 has_invalid_entry =
true;
7867 entries.push_back(std::move(accept_entry));
7871 if (has_invalid_entry)
7877 std::sort(entries.begin(), entries.end(),
7878 [](
const AcceptEntry & a,
const AcceptEntry & b)
7880 if (a.quality != b.quality)
7882 return a.quality > b.quality;
7884 return a.order < b.order;
7888 content_types.reserve(entries.size());
7889 for (
auto &entry : entries)
7891 content_types.push_back(std::move(entry.media_type));
7900 FormDataParser() =
default;
7902 void set_boundary(std::string &&boundary)
7904 boundary_ = std::move(boundary);
7905 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
7906 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
7909 bool is_valid()
const
7914 bool parse(
const char *buf,
size_t n,
const FormDataHeader &header_callback,
7915 const ContentReceiver &content_callback)
7920 while (buf_size() > 0)
7926 auto pos = buf_find(dash_boundary_crlf_);
7927 if (pos == buf_size())
7931 buf_erase(pos + dash_boundary_crlf_.size());
7943 auto pos = buf_find(crlf_);
7948 while (pos < buf_size())
7953 if (!header_callback(file_))
7958 buf_erase(crlf_.size());
7963 const auto header = buf_head(pos);
7965 if (!parse_header(header.data(), header.data() + header.size(),
7966 [&](
const std::string &,
const std::string &) {}))
7974 header.data(), header.data() + header.size(),
7975 [&](
const std::string & key,
const std::string & val)
7977 file_.headers.emplace(key, val);
7984 constexpr const char header_content_type[] =
"Content-Type:";
7986 if (start_with_case_ignore(header, header_content_type))
7988 file_.content_type =
7989 trim_copy(header.substr(str_len(header_content_type)));
7993 thread_local const std::regex re_content_disposition(
7994 R
"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
7995 std::regex_constants::icase);
7998 if (std::regex_match(header, m, re_content_disposition))
8001 parse_disposition_params(m[1], params);
8003 auto it = params.find(
"name");
8004 if (it != params.end())
8006 file_.name = it->second;
8014 it = params.find(
"filename");
8015 if (it != params.end())
8017 file_.filename = it->second;
8020 it = params.find(
"filename*");
8021 if (it != params.end())
8024 thread_local const std::regex re_rfc5987_encoding(
8025 R
"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
8028 if (std::regex_match(it->second, m2, re_rfc5987_encoding))
8040 buf_erase(pos + crlf_.size());
8041 pos = buf_find(crlf_);
8051 if (crlf_dash_boundary_.size() > buf_size())
8055 auto pos = buf_find(crlf_dash_boundary_);
8056 if (pos < buf_size())
8058 if (!content_callback(buf_data(), pos))
8063 buf_erase(pos + crlf_dash_boundary_.size());
8068 auto len = buf_size() - crlf_dash_boundary_.size();
8071 if (!content_callback(buf_data(), len))
8084 if (crlf_.size() > buf_size())
8088 if (buf_start_with(crlf_))
8090 buf_erase(crlf_.size());
8095 if (dash_.size() > buf_size())
8099 if (buf_start_with(dash_))
8101 buf_erase(dash_.size());
8103 buf_erase(buf_size());
8119 void clear_file_info()
8122 file_.filename.clear();
8123 file_.content_type.clear();
8124 file_.headers.clear();
8127 bool start_with_case_ignore(
const std::string &a,
const char *b)
const
8129 const auto b_len = strlen(b);
8130 if (a.size() < b_len)
8134 for (
size_t i = 0; i < b_len; i++)
8136 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i]))
8144 const std::string dash_ =
"--";
8145 const std::string crlf_ =
"\r\n";
8146 std::string boundary_;
8147 std::string dash_boundary_crlf_;
8148 std::string crlf_dash_boundary_;
8151 bool is_valid_ =
false;
8155 bool start_with(
const std::string &a,
size_t spos,
size_t epos,
8156 const std::string &b)
const
8158 if (epos - spos < b.size())
8162 for (
size_t i = 0; i < b.size(); i++)
8164 if (a[i + spos] != b[i])
8172 size_t buf_size()
const
8174 return buf_epos_ - buf_spos_;
8177 const char *buf_data()
const
8179 return &buf_[buf_spos_];
8182 std::string buf_head(
size_t l)
const
8184 return buf_.substr(buf_spos_, l);
8187 bool buf_start_with(
const std::string &s)
const
8189 return start_with(buf_, buf_spos_, buf_epos_, s);
8192 size_t buf_find(
const std::string &s)
const
8196 size_t off = buf_spos_;
8197 while (off < buf_epos_)
8202 if (pos == buf_epos_)
8213 auto remaining_size = buf_epos_ - pos;
8214 if (s.size() > remaining_size)
8219 if (start_with(buf_, pos, buf_epos_, s))
8221 return pos - buf_spos_;
8230 void buf_append(
const char *data,
size_t n)
8232 auto remaining_size = buf_size();
8233 if (remaining_size > 0 && buf_spos_ > 0)
8235 for (
size_t i = 0; i < remaining_size; i++)
8237 buf_[i] = buf_[buf_spos_ + i];
8241 buf_epos_ = remaining_size;
8243 if (remaining_size + n > buf_.size())
8245 buf_.resize(remaining_size + n);
8248 for (
size_t i = 0; i < n; i++)
8250 buf_[buf_epos_ + i] = data[i];
8255 void buf_erase(
size_t size)
8261 size_t buf_spos_ = 0;
8262 size_t buf_epos_ = 0;
8265inline std::string random_string(
size_t length)
8267 constexpr const char data[] =
8268 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8270 thread_local auto engine([]()
8275 std::random_device seed_gen;
8277 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
8278 return std::mt19937(seed_sequence);
8283 for (
size_t i = 0; i < length; i++)
8285 result += data[engine() % (
sizeof(data) - 1)];
8290inline std::string make_multipart_data_boundary()
8292 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
8295inline bool is_multipart_boundary_chars_valid(
const std::string &boundary)
8298 for (
size_t i = 0; i < boundary.size(); i++)
8300 auto c = boundary[i];
8301 if (!std::isalnum(c) && c !=
'-' && c !=
'_')
8310template <
typename T>
8312serialize_multipart_formdata_item_begin(
const T &item,
8313 const std::string &boundary)
8315 std::string body =
"--" + boundary +
"\r\n";
8316 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
8317 if (!item.filename.empty())
8319 body +=
"; filename=\"" + item.filename +
"\"";
8322 if (!item.content_type.empty())
8324 body +=
"Content-Type: " + item.content_type +
"\r\n";
8331inline std::string serialize_multipart_formdata_item_end()
8337serialize_multipart_formdata_finish(
const std::string &boundary)
8339 return "--" + boundary +
"--\r\n";
8343serialize_multipart_formdata_get_content_type(
const std::string &boundary)
8345 return "multipart/form-data; boundary=" + boundary;
8349serialize_multipart_formdata(
const UploadFormDataItems &items,
8350 const std::string &boundary,
bool finish =
true)
8354 for (
const auto &item : items)
8356 body += serialize_multipart_formdata_item_begin(item, boundary);
8357 body += item.content + serialize_multipart_formdata_item_end();
8362 body += serialize_multipart_formdata_finish(boundary);
8368inline void coalesce_ranges(Ranges &ranges,
size_t content_length)
8370 if (ranges.size() <= 1)
return;
8373 std::sort(ranges.begin(), ranges.end(),
8374 [](
const Range & a,
const Range & b)
8376 return a.first < b.first;
8380 coalesced.reserve(ranges.size());
8382 for (
auto &r : ranges)
8384 auto first_pos = r.first;
8385 auto last_pos = r.second;
8388 if (first_pos == -1 && last_pos == -1)
8391 last_pos =
static_cast<ssize_t
>(content_length);
8394 if (first_pos == -1)
8396 first_pos =
static_cast<ssize_t
>(content_length) - last_pos;
8397 last_pos =
static_cast<ssize_t
>(content_length) - 1;
8400 if (last_pos == -1 || last_pos >=
static_cast<ssize_t
>(content_length))
8402 last_pos =
static_cast<ssize_t
>(content_length) - 1;
8406 if (!(0 <= first_pos && first_pos <= last_pos &&
8407 last_pos <
static_cast<ssize_t
>(content_length)))
8414 if (!coalesced.empty())
8416 auto &prev = coalesced.back();
8419 if (first_pos <= prev.second + 1 &&
8420 !(first_pos == prev.first && last_pos == prev.second))
8423 prev.second = (std::max)(prev.second, last_pos);
8429 coalesced.emplace_back(first_pos, last_pos);
8432 ranges = std::move(coalesced);
8435inline bool range_error(Request &req, Response &res)
8437 if (!req.ranges.empty() && 200 <= res.status && res.status < 300)
8439 ssize_t content_len =
static_cast<ssize_t
>(
8440 res.content_length_ ? res.content_length_ : res.body.size());
8442 std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
8443 size_t overwrapping_count = 0;
8455 for (
auto &r : req.ranges)
8457 auto &first_pos = r.first;
8458 auto &last_pos = r.second;
8460 if (first_pos == -1 && last_pos == -1)
8463 last_pos = content_len;
8466 if (first_pos == -1)
8468 first_pos = content_len - last_pos;
8469 last_pos = content_len - 1;
8481 if (last_pos == -1 || last_pos >= content_len)
8483 last_pos = content_len - 1;
8487 if (!(0 <= first_pos && first_pos <= last_pos &&
8488 last_pos <= content_len - 1))
8494 for (
const auto &processed_range : processed_ranges)
8496 if (!(last_pos < processed_range.first ||
8497 first_pos > processed_range.second))
8499 overwrapping_count++;
8500 if (overwrapping_count > 2)
8508 processed_ranges.emplace_back(first_pos, last_pos);
8512 coalesce_ranges(req.ranges,
static_cast<size_t>(content_len));
8518inline std::pair<size_t, size_t>
8519get_range_offset_and_length(Range r,
size_t content_length)
8521 assert(r.first != -1 && r.second != -1);
8522 assert(0 <= r.first && r.first <
static_cast<ssize_t
>(content_length));
8523 assert(r.first <= r.second &&
8524 r.second <
static_cast<ssize_t
>(content_length));
8525 (void)(content_length);
8526 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
8529inline std::string make_content_range_header_field(
8530 const std::pair<size_t, size_t> &offset_and_length,
size_t content_length)
8532 auto st = offset_and_length.first;
8533 auto ed = st + offset_and_length.second - 1;
8535 std::string field =
"bytes ";
8536 field += std::to_string(st);
8538 field += std::to_string(ed);
8540 field += std::to_string(content_length);
8544template <
typename SToken,
typename CToken,
typename Content>
8545bool process_multipart_ranges_data(
const Request &req,
8546 const std::string &boundary,
8547 const std::string &content_type,
8548 size_t content_length, SToken stoken,
8549 CToken ctoken, Content content)
8551 for (
size_t i = 0; i < req.ranges.size(); i++)
8556 if (!content_type.empty())
8558 ctoken(
"Content-Type: ");
8559 stoken(content_type);
8563 auto offset_and_length =
8564 get_range_offset_and_length(req.ranges[i], content_length);
8566 ctoken(
"Content-Range: ");
8567 stoken(make_content_range_header_field(offset_and_length, content_length));
8571 if (!content(offset_and_length.first, offset_and_length.second))
8585inline void make_multipart_ranges_data(
const Request &req, Response &res,
8586 const std::string &boundary,
8587 const std::string &content_type,
8588 size_t content_length,
8591 process_multipart_ranges_data(
8592 req, boundary, content_type, content_length,
8593 [&](
const std::string & token)
8597 [&](
const std::string & token)
8601 [&](
size_t offset,
size_t length)
8603 assert(offset + length <= content_length);
8604 data += res.body.substr(offset, length);
8609inline size_t get_multipart_ranges_data_length(
const Request &req,
8610 const std::string &boundary,
8611 const std::string &content_type,
8612 size_t content_length)
8614 size_t data_length = 0;
8616 process_multipart_ranges_data(
8617 req, boundary, content_type, content_length,
8618 [&](
const std::string & token)
8620 data_length += token.size();
8622 [&](
const std::string & token)
8624 data_length += token.size();
8626 [&](
size_t ,
size_t length)
8628 data_length += length;
8635template <
typename T>
8637write_multipart_ranges_data(Stream &strm,
const Request &req, Response &res,
8638 const std::string &boundary,
8639 const std::string &content_type,
8640 size_t content_length,
const T &is_shutting_down)
8642 return process_multipart_ranges_data(
8643 req, boundary, content_type, content_length,
8644 [&](
const std::string & token)
8648 [&](
const std::string & token)
8652 [&](
size_t offset,
size_t length)
8654 return write_content(strm, res.content_provider_, offset, length,
8659inline bool expect_content(
const Request &req)
8661 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" ||
8662 req.method ==
"DELETE")
8666 if (req.has_header(
"Content-Length") &&
8667 req.get_header_value_u64(
"Content-Length") > 0)
8671 if (is_chunked_transfer_encoding(req.headers))
8678inline bool has_crlf(
const std::string &s)
8683 if (*p ==
'\r' || *p ==
'\n')
8692#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8693inline std::string message_digest(
const std::string &s,
const EVP_MD *algo)
8695 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
8696 EVP_MD_CTX_new(), EVP_MD_CTX_free);
8698 unsigned int hash_length = 0;
8699 unsigned char hash[EVP_MAX_MD_SIZE];
8701 EVP_DigestInit_ex(context.get(), algo,
nullptr);
8702 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
8703 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
8705 std::stringstream ss;
8706 for (
auto i = 0u; i < hash_length; ++i)
8708 ss << std::hex << std::setw(2) << std::setfill(
'0')
8709 <<
static_cast<unsigned int>(hash[i]);
8715inline std::string MD5(
const std::string &s)
8717 return message_digest(s, EVP_md5());
8720inline std::string SHA_256(
const std::string &s)
8722 return message_digest(s, EVP_sha256());
8725inline std::string SHA_512(
const std::string &s)
8727 return message_digest(s, EVP_sha512());
8730inline std::pair<std::string, std::string> make_digest_authentication_header(
8731 const Request &req,
const std::map<std::string, std::string> &auth,
8732 size_t cnonce_count,
const std::string &cnonce,
const std::string &username,
8733 const std::string &password,
bool is_proxy =
false)
8737 std::stringstream ss;
8738 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
8743 if (auth.find(
"qop") != auth.end())
8745 qop = auth.at(
"qop");
8746 if (qop.find(
"auth-int") != std::string::npos)
8750 else if (qop.find(
"auth") != std::string::npos)
8760 std::string algo =
"MD5";
8761 if (auth.find(
"algorithm") != auth.end())
8763 algo = auth.at(
"algorithm");
8766 std::string response;
8768 auto H = algo ==
"SHA-256" ? detail::SHA_256
8769 : algo ==
"SHA-512" ? detail::SHA_512
8772 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
8774 auto A2 = req.method +
":" + req.path;
8775 if (qop ==
"auth-int")
8777 A2 +=
":" + H(req.body);
8782 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
8786 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
8787 ":" + qop +
":" + H(A2));
8791 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
8793 auto field =
"Digest username=\"" + username +
"\", realm=\"" +
8794 auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
8795 "\", uri=\"" + req.path +
"\", algorithm=" + algo +
8796 (qop.empty() ?
", response=\""
8797 :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" +
8798 cnonce +
"\", response=\"") +
8800 (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
8802 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
8803 return std::make_pair(key, field);
8806inline bool is_ssl_peer_could_be_closed(SSL *ssl,
socket_t sock)
8808 detail::set_nonblocking(sock,
true);
8809 auto se = detail::scope_exit([&]()
8811 detail::set_nonblocking(sock,
false);
8815 return !SSL_peek(ssl, buf, 1) &&
8816 SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
8822inline bool load_system_certs_on_windows(X509_STORE *store)
8824 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L
"ROOT");
8830 auto result =
false;
8831 PCCERT_CONTEXT pContext = NULL;
8832 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
8836 static_cast<const unsigned char *
>(pContext->pbCertEncoded);
8838 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
8841 X509_STORE_add_cert(store, x509);
8847 CertFreeCertificateContext(pContext);
8848 CertCloseStore(hStore, 0);
8852#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
8853template <
typename T>
8855 std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
8857inline void cf_object_ptr_deleter(CFTypeRef obj)
8865inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs)
8867 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
8868 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
8872 CFObjectPtr<CFDictionaryRef> query(
8873 CFDictionaryCreate(
nullptr,
reinterpret_cast<const void **
>(keys), values,
8874 sizeof(keys) /
sizeof(keys[0]),
8875 &kCFTypeDictionaryKeyCallBacks,
8876 &kCFTypeDictionaryValueCallBacks),
8877 cf_object_ptr_deleter);
8884 CFTypeRef security_items =
nullptr;
8885 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
8886 CFArrayGetTypeID() != CFGetTypeID(security_items))
8891 certs.reset(
reinterpret_cast<CFArrayRef
>(security_items));
8895inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs)
8897 CFArrayRef root_security_items =
nullptr;
8898 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess)
8903 certs.reset(root_security_items);
8907inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store)
8909 auto result =
false;
8910 for (
auto i = 0; i < CFArrayGetCount(certs); ++i)
8912 const auto cert =
reinterpret_cast<const __SecCertificate *
>(
8913 CFArrayGetValueAtIndex(certs, i));
8915 if (SecCertificateGetTypeID() != CFGetTypeID(cert))
8920 CFDataRef cert_data =
nullptr;
8921 if (SecItemExport(cert, kSecFormatX509Cert, 0,
nullptr, &cert_data) !=
8927 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
8929 auto encoded_cert =
static_cast<const unsigned char *
>(
8930 CFDataGetBytePtr(cert_data_ptr.get()));
8933 d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
8937 X509_STORE_add_cert(store, x509);
8946inline bool load_system_certs_on_macos(X509_STORE *store)
8948 auto result =
false;
8949 CFObjectPtr<CFArrayRef> certs(
nullptr, cf_object_ptr_deleter);
8950 if (retrieve_certs_from_keychain(certs) && certs)
8952 result = add_certs_to_x509_store(certs.get(), store);
8955 if (retrieve_root_certs_from_keychain(certs) && certs)
8957 result = add_certs_to_x509_store(certs.get(), store) || result;
8972 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ =
true;
8977 if (is_valid_) WSACleanup();
8980 bool is_valid_ =
false;
8983static WSInit wsinit_;
8986inline bool parse_www_authenticate(
const Response &res,
8987 std::map<std::string, std::string> &auth,
8990 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
8991 if (res.has_header(auth_key))
8993 thread_local auto re =
8994 std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
8995 auto s = res.get_header_value(auth_key);
8996 auto pos = s.find(
' ');
8997 if (pos != std::string::npos)
8999 auto type = s.substr(0, pos);
9000 if (
type ==
"Basic")
9004 else if (
type ==
"Digest")
9006 s = s.substr(pos + 1);
9007 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
9008 for (
auto i = beg; i != std::sregex_iterator(); ++i)
9011 auto key = s.substr(
static_cast<size_t>(m.position(1)),
9012 static_cast<size_t>(m.length(1)));
9013 auto val = m.length(2) > 0
9014 ? s.substr(
static_cast<size_t>(m.position(2)),
9015 static_cast<size_t>(m.length(2)))
9016 : s.substr(
static_cast<size_t>(m.position(3)),
9017 static_cast<size_t>(m.length(3)));
9018 auth[std::move(key)] = std::move(val);
9027class ContentProviderAdapter
9030 explicit ContentProviderAdapter(
9031 ContentProviderWithoutLength &&content_provider)
9032 : content_provider_(
std::move(content_provider)) {}
9034 bool operator()(
size_t offset,
size_t, DataSink &sink)
9036 return content_provider_(offset, sink);
9047inline bool is_token_char(
char c)
9049 return std::isalnum(c) || c ==
'!' || c ==
'#' || c ==
'$' || c ==
'%' ||
9050 c ==
'&' || c ==
'\'' || c ==
'*' || c ==
'+' || c ==
'-' ||
9051 c ==
'.' || c ==
'^' || c ==
'_' || c ==
'`' || c ==
'|' || c ==
'~';
9054inline bool is_token(
const std::string &s)
9062 if (!is_token_char(c))
9070inline bool is_field_name(
const std::string &s)
9075inline bool is_vchar(
char c)
9077 return c >= 33 && c <= 126;
9080inline bool is_obs_text(
char c)
9082 return 128 <=
static_cast<unsigned char>(c);
9085inline bool is_field_vchar(
char c)
9087 return is_vchar(c) || is_obs_text(c);
9090inline bool is_field_content(
const std::string &s)
9099 return is_field_vchar(s[0]);
9101 else if (s.size() == 2)
9103 return is_field_vchar(s[0]) && is_field_vchar(s[1]);
9109 if (!is_field_vchar(s[i]))
9115 while (i < s.size() - 1)
9118 if (c ==
' ' || c ==
'\t' || is_field_vchar(c))
9127 return is_field_vchar(s[i]);
9131inline bool is_field_value(
const std::string &s)
9133 return is_field_content(s);
9144 case StatusCode::Continue_100:
9146 case StatusCode::SwitchingProtocol_101:
9147 return "Switching Protocol";
9148 case StatusCode::Processing_102:
9149 return "Processing";
9150 case StatusCode::EarlyHints_103:
9151 return "Early Hints";
9152 case StatusCode::OK_200:
9154 case StatusCode::Created_201:
9156 case StatusCode::Accepted_202:
9158 case StatusCode::NonAuthoritativeInformation_203:
9159 return "Non-Authoritative Information";
9160 case StatusCode::NoContent_204:
9161 return "No Content";
9162 case StatusCode::ResetContent_205:
9163 return "Reset Content";
9164 case StatusCode::PartialContent_206:
9165 return "Partial Content";
9166 case StatusCode::MultiStatus_207:
9167 return "Multi-Status";
9168 case StatusCode::AlreadyReported_208:
9169 return "Already Reported";
9170 case StatusCode::IMUsed_226:
9172 case StatusCode::MultipleChoices_300:
9173 return "Multiple Choices";
9174 case StatusCode::MovedPermanently_301:
9175 return "Moved Permanently";
9176 case StatusCode::Found_302:
9178 case StatusCode::SeeOther_303:
9180 case StatusCode::NotModified_304:
9181 return "Not Modified";
9182 case StatusCode::UseProxy_305:
9184 case StatusCode::unused_306:
9186 case StatusCode::TemporaryRedirect_307:
9187 return "Temporary Redirect";
9188 case StatusCode::PermanentRedirect_308:
9189 return "Permanent Redirect";
9190 case StatusCode::BadRequest_400:
9191 return "Bad Request";
9192 case StatusCode::Unauthorized_401:
9193 return "Unauthorized";
9194 case StatusCode::PaymentRequired_402:
9195 return "Payment Required";
9196 case StatusCode::Forbidden_403:
9198 case StatusCode::NotFound_404:
9200 case StatusCode::MethodNotAllowed_405:
9201 return "Method Not Allowed";
9202 case StatusCode::NotAcceptable_406:
9203 return "Not Acceptable";
9204 case StatusCode::ProxyAuthenticationRequired_407:
9205 return "Proxy Authentication Required";
9206 case StatusCode::RequestTimeout_408:
9207 return "Request Timeout";
9208 case StatusCode::Conflict_409:
9210 case StatusCode::Gone_410:
9212 case StatusCode::LengthRequired_411:
9213 return "Length Required";
9214 case StatusCode::PreconditionFailed_412:
9215 return "Precondition Failed";
9216 case StatusCode::PayloadTooLarge_413:
9217 return "Payload Too Large";
9218 case StatusCode::UriTooLong_414:
9219 return "URI Too Long";
9220 case StatusCode::UnsupportedMediaType_415:
9221 return "Unsupported Media Type";
9222 case StatusCode::RangeNotSatisfiable_416:
9223 return "Range Not Satisfiable";
9224 case StatusCode::ExpectationFailed_417:
9225 return "Expectation Failed";
9226 case StatusCode::ImATeapot_418:
9227 return "I'm a teapot";
9228 case StatusCode::MisdirectedRequest_421:
9229 return "Misdirected Request";
9230 case StatusCode::UnprocessableContent_422:
9231 return "Unprocessable Content";
9232 case StatusCode::Locked_423:
9234 case StatusCode::FailedDependency_424:
9235 return "Failed Dependency";
9236 case StatusCode::TooEarly_425:
9238 case StatusCode::UpgradeRequired_426:
9239 return "Upgrade Required";
9240 case StatusCode::PreconditionRequired_428:
9241 return "Precondition Required";
9242 case StatusCode::TooManyRequests_429:
9243 return "Too Many Requests";
9244 case StatusCode::RequestHeaderFieldsTooLarge_431:
9245 return "Request Header Fields Too Large";
9246 case StatusCode::UnavailableForLegalReasons_451:
9247 return "Unavailable For Legal Reasons";
9248 case StatusCode::NotImplemented_501:
9249 return "Not Implemented";
9250 case StatusCode::BadGateway_502:
9251 return "Bad Gateway";
9252 case StatusCode::ServiceUnavailable_503:
9253 return "Service Unavailable";
9254 case StatusCode::GatewayTimeout_504:
9255 return "Gateway Timeout";
9256 case StatusCode::HttpVersionNotSupported_505:
9257 return "HTTP Version Not Supported";
9258 case StatusCode::VariantAlsoNegotiates_506:
9259 return "Variant Also Negotiates";
9260 case StatusCode::InsufficientStorage_507:
9261 return "Insufficient Storage";
9262 case StatusCode::LoopDetected_508:
9263 return "Loop Detected";
9264 case StatusCode::NotExtended_510:
9265 return "Not Extended";
9266 case StatusCode::NetworkAuthenticationRequired_511:
9267 return "Network Authentication Required";
9270 case StatusCode::InternalServerError_500:
9271 return "Internal Server Error";
9279 case Error::Success:
9280 return "Success (no error)";
9281 case Error::Unknown:
9283 case Error::Connection:
9284 return "Could not establish connection";
9285 case Error::BindIPAddress:
9286 return "Failed to bind IP address";
9288 return "Failed to read connection";
9290 return "Failed to write connection";
9291 case Error::ExceedRedirectCount:
9292 return "Maximum redirect count exceeded";
9293 case Error::Canceled:
9294 return "Connection handling canceled";
9295 case Error::SSLConnection:
9296 return "SSL connection failed";
9297 case Error::SSLLoadingCerts:
9298 return "SSL certificate loading failed";
9299 case Error::SSLServerVerification:
9300 return "SSL server verification failed";
9301 case Error::SSLServerHostnameVerification:
9302 return "SSL server hostname verification failed";
9303 case Error::UnsupportedMultipartBoundaryChars:
9304 return "Unsupported HTTP multipart boundary characters";
9305 case Error::Compression:
9306 return "Compression failed";
9307 case Error::ConnectionTimeout:
9308 return "Connection timed out";
9309 case Error::ProxyConnection:
9310 return "Proxy connection failed";
9311 case Error::ConnectionClosed:
9312 return "Connection closed by server";
9313 case Error::Timeout:
9314 return "Read timeout";
9315 case Error::ResourceExhaustion:
9316 return "Resource exhaustion";
9317 case Error::TooManyFormDataFiles:
9318 return "Too many form data files";
9319 case Error::ExceedMaxPayloadSize:
9320 return "Exceeded maximum payload size";
9321 case Error::ExceedUriMaxLength:
9322 return "Exceeded maximum URI length";
9323 case Error::ExceedMaxSocketDescriptorCount:
9324 return "Exceeded maximum socket descriptor count";
9325 case Error::InvalidRequestLine:
9326 return "Invalid request line";
9327 case Error::InvalidHTTPMethod:
9328 return "Invalid HTTP method";
9329 case Error::InvalidHTTPVersion:
9330 return "Invalid HTTP version";
9331 case Error::InvalidHeaders:
9332 return "Invalid headers";
9333 case Error::MultipartParsing:
9334 return "Multipart parsing failed";
9335 case Error::OpenFile:
9336 return "Failed to open file";
9338 return "Failed to listen on socket";
9339 case Error::GetSockName:
9340 return "Failed to get socket name";
9341 case Error::UnsupportedAddressFamily:
9342 return "Unsupported address family";
9343 case Error::HTTPParsing:
9344 return "HTTP parsing failed";
9345 case Error::InvalidRangeHeader:
9346 return "Invalid Range header";
9357 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
9363 std::vector<std::string> addrs;
9367 return std::string();
9373 std::vector<std::string> &addrs)
9375 struct addrinfo hints;
9376 struct addrinfo *result;
9378 memset(&hints, 0,
sizeof(
struct addrinfo));
9379 hints.ai_family = AF_UNSPEC;
9380 hints.ai_socktype = SOCK_STREAM;
9381 hints.ai_protocol = 0;
9383 if (detail::getaddrinfo_with_timeout(hostname.c_str(),
nullptr, &hints,
9386#if defined __linux__ && !defined __ANDROID__
9393 for (
auto rp = result; rp; rp = rp->ai_next)
9396 *
reinterpret_cast<struct sockaddr_storage *
>(rp->ai_addr);
9399 if (detail::get_ip_and_port(addr,
sizeof(
struct sockaddr_storage), ip,
9402 addrs.emplace_back(std::move(ip));
9409 std::ostringstream escaped;
9411 escaped << std::hex;
9413 for (
auto c : value)
9415 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
9416 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
9423 escaped << std::uppercase;
9424 escaped <<
'%' << std::setw(2)
9425 <<
static_cast<int>(
static_cast<unsigned char>(c));
9426 escaped << std::nouppercase;
9430 return escaped.str();
9435 std::ostringstream escaped;
9437 escaped << std::hex;
9439 for (
auto c : value)
9441 if (std::isalnum(
static_cast<uint8_t
>(c)) || c ==
'-' || c ==
'_' ||
9442 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
9443 c ==
')' || c ==
';' || c ==
'/' || c ==
'?' || c ==
':' || c ==
'@' ||
9444 c ==
'&' || c ==
'=' || c ==
'+' || c ==
'$' || c ==
',' || c ==
'#')
9450 escaped << std::uppercase;
9451 escaped <<
'%' << std::setw(2)
9452 <<
static_cast<int>(
static_cast<unsigned char>(c));
9453 escaped << std::nouppercase;
9457 return escaped.str();
9464 for (
size_t i = 0; i < value.size(); i++)
9466 if (value[i] ==
'%' && i + 2 < value.size())
9469 if (detail::from_hex_to_i(value, i + 1, 2, val))
9471 result +=
static_cast<char>(val);
9492 for (
size_t i = 0; i < value.size(); i++)
9494 if (value[i] ==
'%' && i + 2 < value.size())
9497 if (detail::from_hex_to_i(value, i + 1, 2, val))
9499 result +=
static_cast<char>(val);
9519 result.reserve(component.size() * 3);
9521 for (
size_t i = 0; i < component.size(); i++)
9523 auto c =
static_cast<unsigned char>(component[i]);
9526 if (std::isalnum(c) || c ==
'-' || c ==
'.' || c ==
'_' || c ==
'~')
9528 result +=
static_cast<char>(c);
9532 else if (c ==
'!' || c ==
'$' || c ==
'&' || c ==
'\'' || c ==
'(' ||
9533 c ==
')' || c ==
'*' || c ==
'+' || c ==
',' || c ==
';' ||
9536 result +=
static_cast<char>(c);
9541 result +=
static_cast<char>(c);
9546 result +=
static_cast<char>(c);
9552 snprintf(hex,
sizeof(hex),
"%02X", c);
9553 result.append(hex, 2);
9562 result.reserve(component.size());
9564 for (
size_t i = 0; i < component.size(); i++)
9566 if (component[i] ==
'%' && i + 1 < component.size())
9568 if (component[i + 1] ==
'u')
9572 if (detail::from_hex_to_i(component, i + 2, 4, val))
9576 size_t len = detail::to_utf8(val, buff);
9579 result.append(buff, len);
9585 result += component[i];
9592 if (detail::from_hex_to_i(component, i + 1, 2, val))
9595 result +=
static_cast<char>(val);
9600 result += component[i];
9606 result += component[i];
9616 result.reserve(component.size() * 3);
9618 for (
size_t i = 0; i < component.size(); i++)
9620 auto c =
static_cast<unsigned char>(component[i]);
9623 if (std::isalnum(c) || c ==
'-' || c ==
'.' || c ==
'_' || c ==
'~')
9625 result +=
static_cast<char>(c);
9648 result +=
static_cast<char>(c);
9652 else if (c ==
'!' || c ==
'$' || c ==
'\'' || c ==
'(' || c ==
')' ||
9653 c ==
'*' || c ==
',' || c ==
';')
9655 result +=
static_cast<char>(c);
9658 else if (c ==
':' || c ==
'@')
9660 result +=
static_cast<char>(c);
9665 result +=
static_cast<char>(c);
9670 result +=
static_cast<char>(c);
9676 snprintf(hex,
sizeof(hex),
"%02X", c);
9677 result.append(hex, 2);
9687 result.reserve(component.size());
9689 for (
size_t i = 0; i < component.size(); i++)
9691 if (component[i] ==
'%' && i + 2 < component.size())
9693 std::string hex = component.substr(i + 1, 2);
9695 unsigned long value = std::strtoul(hex.c_str(), &end, 16);
9696 if (end == hex.c_str() + 2)
9698 result +=
static_cast<char>(value);
9703 result += component[i];
9706 else if (component[i] ==
'+' && plus_as_space)
9712 result += component[i];
9721 std::string path_with_query = path;
9722 thread_local const std::regex re(
"[^?]+\\?.*");
9723 auto delm = std::regex_match(path, re) ?
'&' :
'?';
9724 path_with_query += delm + detail::params_to_query_str(params);
9725 return path_with_query;
9729inline std::pair<std::string, std::string>
9732 std::string field =
"bytes=";
9734 for (
const auto &r : ranges)
9742 field += std::to_string(r.first);
9747 field += std::to_string(r.second);
9751 return std::make_pair(
"Range", std::move(field));
9754inline std::pair<std::string, std::string>
9756 const std::string &password,
bool is_proxy)
9758 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
9759 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9760 return std::make_pair(key, std::move(field));
9763inline std::pair<std::string, std::string>
9765 bool is_proxy =
false)
9767 auto field =
"Bearer " + token;
9768 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
9769 return std::make_pair(key, std::move(field));
9773inline bool Request::has_header(
const std::string &key)
const
9775 return detail::has_header(headers, key);
9778inline std::string Request::get_header_value(
const std::string &key,
9779 const char *def,
size_t id)
const
9781 return detail::get_header_value(headers, key, def,
id);
9784inline size_t Request::get_header_value_count(
const std::string &key)
const
9786 auto r = headers.equal_range(key);
9787 return static_cast<size_t>(std::distance(r.first, r.second));
9790inline void Request::set_header(
const std::string &key,
9791 const std::string &val)
9793 if (detail::fields::is_field_name(key) &&
9794 detail::fields::is_field_value(val))
9796 headers.emplace(key, val);
9800inline bool Request::has_trailer(
const std::string &key)
const
9802 return trailers.find(key) != trailers.end();
9805inline std::string Request::get_trailer_value(
const std::string &key,
9808 auto rng = trailers.equal_range(key);
9809 auto it = rng.first;
9810 std::advance(it,
static_cast<ssize_t
>(
id));
9811 if (it != rng.second)
9815 return std::string();
9818inline size_t Request::get_trailer_value_count(
const std::string &key)
const
9820 auto r = trailers.equal_range(key);
9821 return static_cast<size_t>(std::distance(r.first, r.second));
9824inline bool Request::has_param(
const std::string &key)
const
9826 return params.find(key) != params.end();
9829inline std::string Request::get_param_value(
const std::string &key,
9832 auto rng = params.equal_range(key);
9833 auto it = rng.first;
9834 std::advance(it,
static_cast<ssize_t
>(
id));
9835 if (it != rng.second)
9839 return std::string();
9842inline size_t Request::get_param_value_count(
const std::string &key)
const
9844 auto r = params.equal_range(key);
9845 return static_cast<size_t>(std::distance(r.first, r.second));
9848inline bool Request::is_multipart_form_data()
const
9850 const auto &content_type = get_header_value(
"Content-Type");
9851 return !content_type.rfind(
"multipart/form-data", 0);
9855inline std::string MultipartFormData::get_field(
const std::string &key,
9858 auto rng = fields.equal_range(key);
9859 auto it = rng.first;
9860 std::advance(it,
static_cast<ssize_t
>(
id));
9861 if (it != rng.second)
9863 return it->second.content;
9865 return std::string();
9868inline std::vector<std::string>
9869MultipartFormData::get_fields(
const std::string &key)
const
9871 std::vector<std::string> values;
9872 auto rng = fields.equal_range(key);
9873 for (
auto it = rng.first; it != rng.second; it++)
9875 values.push_back(it->second.content);
9880inline bool MultipartFormData::has_field(
const std::string &key)
const
9882 return fields.find(key) != fields.end();
9885inline size_t MultipartFormData::get_field_count(
const std::string &key)
const
9887 auto r = fields.equal_range(key);
9888 return static_cast<size_t>(std::distance(r.first, r.second));
9891inline FormData MultipartFormData::get_file(
const std::string &key,
9894 auto rng = files.equal_range(key);
9895 auto it = rng.first;
9896 std::advance(it,
static_cast<ssize_t
>(
id));
9897 if (it != rng.second)
9904inline std::vector<FormData>
9905MultipartFormData::get_files(
const std::string &key)
const
9907 std::vector<FormData> values;
9908 auto rng = files.equal_range(key);
9909 for (
auto it = rng.first; it != rng.second; it++)
9911 values.push_back(it->second);
9916inline bool MultipartFormData::has_file(
const std::string &key)
const
9918 return files.find(key) != files.end();
9921inline size_t MultipartFormData::get_file_count(
const std::string &key)
const
9923 auto r = files.equal_range(key);
9924 return static_cast<size_t>(std::distance(r.first, r.second));
9928inline bool Response::has_header(
const std::string &key)
const
9930 return headers.find(key) != headers.end();
9933inline std::string Response::get_header_value(
const std::string &key,
9937 return detail::get_header_value(headers, key, def,
id);
9940inline size_t Response::get_header_value_count(
const std::string &key)
const
9942 auto r = headers.equal_range(key);
9943 return static_cast<size_t>(std::distance(r.first, r.second));
9946inline void Response::set_header(
const std::string &key,
9947 const std::string &val)
9949 if (detail::fields::is_field_name(key) &&
9950 detail::fields::is_field_value(val))
9952 headers.emplace(key, val);
9955inline bool Response::has_trailer(
const std::string &key)
const
9957 return trailers.find(key) != trailers.end();
9960inline std::string Response::get_trailer_value(
const std::string &key,
9963 auto rng = trailers.equal_range(key);
9964 auto it = rng.first;
9965 std::advance(it,
static_cast<ssize_t
>(
id));
9966 if (it != rng.second)
9970 return std::string();
9973inline size_t Response::get_trailer_value_count(
const std::string &key)
const
9975 auto r = trailers.equal_range(key);
9976 return static_cast<size_t>(std::distance(r.first, r.second));
9979inline void Response::set_redirect(
const std::string &url,
int stat)
9981 if (detail::fields::is_field_value(url))
9983 set_header(
"Location", url);
9984 if (300 <= stat && stat < 400)
9986 this->status = stat;
9990 this->status = StatusCode::Found_302;
9995inline void Response::set_content(
const char *s,
size_t n,
9996 const std::string &content_type)
10000 auto rng = headers.equal_range(
"Content-Type");
10001 headers.erase(rng.first, rng.second);
10002 set_header(
"Content-Type", content_type);
10005inline void Response::set_content(
const std::string &s,
10006 const std::string &content_type)
10008 set_content(s.data(), s.size(), content_type);
10011inline void Response::set_content(std::string &&s,
10012 const std::string &content_type)
10014 body = std::move(s);
10016 auto rng = headers.equal_range(
"Content-Type");
10017 headers.erase(rng.first, rng.second);
10018 set_header(
"Content-Type", content_type);
10021inline void Response::set_content_provider(
10022 size_t in_length,
const std::string &content_type,
ContentProvider provider,
10025 set_header(
"Content-Type", content_type);
10026 content_length_ = in_length;
10029 content_provider_ = std::move(provider);
10031 content_provider_resource_releaser_ = std::move(resource_releaser);
10032 is_chunked_content_provider_ =
false;
10035inline void Response::set_content_provider(
10039 set_header(
"Content-Type", content_type);
10040 content_length_ = 0;
10041 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
10042 content_provider_resource_releaser_ = std::move(resource_releaser);
10043 is_chunked_content_provider_ =
false;
10046inline void Response::set_chunked_content_provider(
10050 set_header(
"Content-Type", content_type);
10051 content_length_ = 0;
10052 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
10053 content_provider_resource_releaser_ = std::move(resource_releaser);
10054 is_chunked_content_provider_ =
true;
10057inline void Response::set_file_content(
const std::string &path,
10058 const std::string &content_type)
10060 file_content_path_ = path;
10061 file_content_content_type_ = content_type;
10064inline void Response::set_file_content(
const std::string &path)
10066 file_content_path_ = path;
10070inline bool Result::has_request_header(
const std::string &key)
const
10072 return request_headers_.find(key) != request_headers_.end();
10075inline std::string Result::get_request_header_value(
const std::string &key,
10079 return detail::get_header_value(request_headers_, key, def,
id);
10083Result::get_request_header_value_count(
const std::string &key)
const
10085 auto r = request_headers_.equal_range(key);
10086 return static_cast<size_t>(std::distance(r.first, r.second));
10090inline ssize_t Stream::write(
const char *ptr)
10092 return write(ptr, strlen(ptr));
10095inline ssize_t Stream::write(
const std::string &s)
10097 return write(s.data(), s.size());
10101inline ssize_t detail::BodyReader::read(
char *buf,
size_t len)
10105 last_error = Error::Connection;
10116 if (bytes_read >= content_length)
10122 auto remaining = content_length - bytes_read;
10123 auto to_read = (std::min)(len, remaining);
10124 auto n = stream->read(buf, to_read);
10128 last_error = stream->get_error();
10129 if (last_error == Error::Success)
10131 last_error = Error::Read;
10139 last_error = stream->get_error();
10140 if (last_error == Error::Success)
10142 last_error = Error::Read;
10148 bytes_read +=
static_cast<size_t>(n);
10149 if (bytes_read >= content_length)
10157 if (!chunked_decoder)
10159 chunked_decoder.reset(
new ChunkedDecoder(*stream));
10162 size_t chunk_offset = 0;
10163 size_t chunk_total = 0;
10164 auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
10167 last_error = stream->get_error();
10168 if (last_error == Error::Success)
10170 last_error = Error::Read;
10183 bytes_read +=
static_cast<size_t>(n);
10190inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
10191 time_t timeout_sec, time_t timeout_usec,
10192 time_t &actual_timeout_sec,
10193 time_t &actual_timeout_usec)
10195 auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
10197 auto actual_timeout_msec =
10198 (std::min)(max_timeout_msec - duration_msec, timeout_msec);
10200 if (actual_timeout_msec < 0)
10202 actual_timeout_msec = 0;
10205 actual_timeout_sec = actual_timeout_msec / 1000;
10206 actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
10210inline SocketStream::SocketStream(
10211 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
10212 time_t write_timeout_sec, time_t write_timeout_usec,
10213 time_t max_timeout_msec,
10214 std::chrono::time_point<std::chrono::steady_clock> start_time)
10215 : sock_(sock), read_timeout_sec_(read_timeout_sec),
10216 read_timeout_usec_(read_timeout_usec),
10217 write_timeout_sec_(write_timeout_sec),
10218 write_timeout_usec_(write_timeout_usec),
10219 max_timeout_msec_(max_timeout_msec), start_time_(start_time),
10220 read_buff_(read_buff_size_, 0) {}
10222inline SocketStream::~SocketStream() =
default;
10224inline bool SocketStream::is_readable()
const
10226 return read_buff_off_ < read_buff_content_size_;
10229inline bool SocketStream::wait_readable()
const
10231 if (max_timeout_msec_ <= 0)
10233 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10236 time_t read_timeout_sec;
10237 time_t read_timeout_usec;
10238 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10239 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10241 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10244inline bool SocketStream::wait_writable()
const
10246 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
10247 is_socket_alive(sock_);
10250inline ssize_t SocketStream::read(
char *ptr,
size_t size)
10254 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
10256 size = (std::min)(size,
10257 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
10260 if (read_buff_off_ < read_buff_content_size_)
10262 auto remaining_size = read_buff_content_size_ - read_buff_off_;
10263 if (size <= remaining_size)
10265 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
10266 read_buff_off_ += size;
10267 return static_cast<ssize_t
>(size);
10271 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
10272 read_buff_off_ += remaining_size;
10273 return static_cast<ssize_t
>(remaining_size);
10277 if (!wait_readable())
10283 read_buff_off_ = 0;
10284 read_buff_content_size_ = 0;
10286 if (size < read_buff_size_)
10288 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
10302 else if (n <=
static_cast<ssize_t
>(size))
10304 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
10309 memcpy(ptr, read_buff_.data(), size);
10310 read_buff_off_ = size;
10311 read_buff_content_size_ =
static_cast<size_t>(n);
10312 return static_cast<ssize_t
>(size);
10333inline ssize_t SocketStream::write(
const char *ptr,
size_t size)
10335 if (!wait_writable())
10340#if defined(_WIN32) && !defined(_WIN64)
10342 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
10348inline void SocketStream::get_remote_ip_and_port(std::string &ip,
10351 return detail::get_remote_ip_and_port(sock_, ip, port);
10354inline void SocketStream::get_local_ip_and_port(std::string &ip,
10357 return detail::get_local_ip_and_port(sock_, ip, port);
10360inline socket_t SocketStream::socket()
const
10365inline time_t SocketStream::duration()
const
10367 return std::chrono::duration_cast<std::chrono::milliseconds>(
10368 std::chrono::steady_clock::now() - start_time_)
10373inline bool BufferStream::is_readable()
const
10378inline bool BufferStream::wait_readable()
const
10383inline bool BufferStream::wait_writable()
const
10388inline ssize_t BufferStream::read(
char *ptr,
size_t size)
10390#if defined(_MSC_VER) && _MSC_VER < 1910
10391 auto len_read =
buffer._Copy_s(ptr, size, size, position);
10393 auto len_read =
buffer.copy(ptr, size, position);
10395 position +=
static_cast<size_t>(len_read);
10396 return static_cast<ssize_t
>(len_read);
10399inline ssize_t BufferStream::write(
const char *ptr,
size_t size)
10401 buffer.append(ptr, size);
10402 return static_cast<ssize_t
>(size);
10405inline void BufferStream::get_remote_ip_and_port(std::string & ,
10408inline void BufferStream::get_local_ip_and_port(std::string & ,
10411inline socket_t BufferStream::socket()
const
10416inline time_t BufferStream::duration()
const
10421inline const std::string &BufferStream::get_buffer()
const
10429 constexpr const char marker[] =
"/:";
10432 std::size_t last_param_end = 0;
10434#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10439 std::unordered_set<std::string> param_name_set;
10444 const auto marker_pos =
pattern.find(
10445 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
10446 if (marker_pos == std::string::npos)
10451 static_fragments_.push_back(
10452 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
10454 const auto param_name_start = marker_pos + str_len(marker);
10456 auto sep_pos =
pattern.find(separator, param_name_start);
10457 if (sep_pos == std::string::npos)
10463 pattern.substr(param_name_start, sep_pos - param_name_start);
10465#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10466 if (param_name_set.find(param_name) != param_name_set.cend())
10468 std::string msg =
"Encountered path parameter '" + param_name +
10469 "' multiple times in route pattern '" +
pattern +
"'.";
10470 throw std::invalid_argument(msg);
10474 param_names_.push_back(std::move(param_name));
10476 last_param_end = sep_pos + 1;
10479 if (last_param_end <
pattern.length())
10481 static_fragments_.push_back(
pattern.substr(last_param_end));
10487 request.
matches = std::smatch();
10489 request.
path_params.reserve(param_names_.size());
10492 std::size_t starting_pos = 0;
10493 for (
size_t i = 0; i < static_fragments_.size(); ++i)
10495 const auto &fragment = static_fragments_[i];
10497 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)
10510 starting_pos += fragment.length();
10515 if (i >= param_names_.size())
10520 auto sep_pos = request.
path.find(separator, starting_pos);
10521 if (sep_pos == std::string::npos)
10523 sep_pos = request.
path.length();
10526 const auto ¶m_name = param_names_[i];
10529 param_name, request.
path.substr(starting_pos, sep_pos - starting_pos));
10532 starting_pos = sep_pos + 1;
10535 return starting_pos >= request.
path.length();
10541 return std::regex_match(request.
path, request.
matches, regex_);
10545inline std::string prepare_host_string(
const std::string &host)
10548 if (host.find(
':') == std::string::npos ||
10549 (!host.empty() && host[0] ==
'['))
10557 return "[" + host +
"]";
10561inline std::string make_host_and_port_string(
const std::string &host,
int port,
10564 auto result = prepare_host_string(host);
10567 if ((!is_ssl && port == 80) || (is_ssl && port == 443))
10573 result +=
":" + std::to_string(port);
10581make_host_and_port_string_always_port(
const std::string &host,
int port)
10583 return prepare_host_string(host) +
":" + std::to_string(port);
10597 signal(SIGPIPE, SIG_IGN);
10603inline std::unique_ptr<detail::MatcherBase>
10604Server::make_matcher(
const std::string &pattern)
10606 if (pattern.find(
"/:") != std::string::npos)
10608 return detail::make_unique<detail::PathParamsMatcher>(pattern);
10612 return detail::make_unique<detail::RegexMatcher>(pattern);
10618 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10624 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10631 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10632 std::move(handler));
10638 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10645 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10646 std::move(handler));
10652 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10659 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10660 std::move(handler));
10666 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10673 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10674 std::move(handler));
10680 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10685 const std::string &mount_point)
10691 const std::string &dir,
Headers headers)
10693 detail::FileStat stat(dir);
10696 std::string mnt = !mount_point.empty() ? mount_point :
"/";
10697 if (!mnt.empty() && mnt[0] ==
'/')
10699 base_dirs_.push_back({std::move(mnt), dir, std::move(headers)});
10708 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it)
10710 if (it->mount_point == mount_point)
10712 base_dirs_.erase(it);
10721 const std::string &mime)
10723 file_extension_and_mimetype_map_[ext] = mime;
10729 default_file_mimetype_ = mime;
10735 file_request_handler_ = std::move(handler);
10739inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
10742 error_handler_ = std::move(handler);
10746inline Server &Server::set_error_handler_core(Handler handler,
10749 error_handler_ = [handler](
const Request & req, Response & res)
10759 exception_handler_ = std::move(handler);
10765 pre_routing_handler_ = std::move(handler);
10771 post_routing_handler_ = std::move(handler);
10777 pre_request_handler_ = std::move(handler);
10783 logger_ = std::move(logger);
10789 error_logger_ = std::move(error_logger);
10795 pre_compression_logger_ = std::move(logger);
10802 expect_100_continue_handler_ = std::move(handler);
10808 address_family_ = family;
10826 socket_options_ = std::move(socket_options);
10832 default_headers_ = std::move(headers);
10839 header_writer_ = writer;
10892 auto ret = bind_internal(host, port, socket_flags);
10895 is_decommissioned =
true;
10901 auto ret = bind_internal(host, 0, socket_flags);
10904 is_decommissioned =
true;
10911 return listen_internal();
10917 return bind_to_port(host, port, socket_flags) && listen_internal();
10922 return is_running_;
10927 while (!is_running_ && !is_decommissioned)
10929 std::this_thread::sleep_for(std::chrono::milliseconds{1});
10939 detail::shutdown_socket(sock);
10942 is_decommissioned =
false;
10947 is_decommissioned =
true;
10950inline bool Server::parse_request_line(
const char *s,
Request &req)
const
10952 auto len = strlen(s);
10953 if (len < 2 || s[len - 2] !=
'\r' || s[len - 1] !=
'\n')
10962 detail::split(s, s + len,
' ', [&](
const char *b,
const char *e)
10967 req.
method = std::string(b, e);
10970 req.
target = std::string(b, e);
10973 req.
version = std::string(b, e);
10987 thread_local const std::set<std::string> methods
10989 "GET",
"HEAD",
"POST",
"PUT",
"DELETE",
10990 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
10992 if (methods.find(req.
method) == methods.end())
11006 for (
size_t i = 0; i < req.
target.size(); i++)
11008 if (req.
target[i] ==
'#')
11015 detail::divide(req.
target,
'?',
11016 [&](
const char *lhs_data, std::size_t lhs_size,
11017 const char *rhs_data, std::size_t rhs_size)
11020 decode_path_component(std::string(lhs_data, lhs_size));
11021 detail::parse_query_text(rhs_data, rhs_size, req.params);
11028inline bool Server::write_response(Stream &strm,
bool close_connection,
11029 Request &req, Response &res)
11033 req.ranges.clear();
11034 return write_response_core(strm, close_connection, req, res,
false);
11037inline bool Server::write_response_with_content(Stream &strm,
11038 bool close_connection,
11039 const Request &req,
11042 return write_response_core(strm, close_connection, req, res,
true);
11045inline bool Server::write_response_core(Stream &strm,
bool close_connection,
11046 const Request &req, Response &res,
11047 bool need_apply_ranges)
11049 assert(res.status != -1);
11051 if (400 <= res.status && error_handler_ &&
11054 need_apply_ranges =
true;
11057 std::string content_type;
11058 std::string boundary;
11059 if (need_apply_ranges)
11061 apply_ranges(req, res, content_type, boundary);
11065 if (close_connection || req.get_header_value(
"Connection") ==
"close" ||
11068 res.set_header(
"Connection",
"close");
11072 std::string s =
"timeout=";
11076 res.set_header(
"Keep-Alive", s);
11079 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
11080 !res.has_header(
"Content-Type"))
11082 res.set_header(
"Content-Type",
"text/plain");
11085 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
11086 !res.has_header(
"Content-Length"))
11088 res.set_header(
"Content-Length",
"0");
11091 if (req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges"))
11093 res.set_header(
"Accept-Ranges",
"bytes");
11096 if (post_routing_handler_)
11098 post_routing_handler_(req, res);
11103 detail::BufferStream bstrm;
11104 if (!detail::write_response_line(bstrm, res.status))
11108 if (!header_writer_(bstrm, res.headers))
11114 auto &data = bstrm.get_buffer();
11115 detail::write_data(strm, data.data(), data.size());
11120 if (req.method !=
"HEAD")
11122 if (!res.body.empty())
11124 if (!detail::write_data(strm, res.body.data(), res.body.size()))
11129 else if (res.content_provider_)
11131 if (write_content_with_provider(strm, req, res, boundary, content_type))
11133 res.content_provider_success_ =
true;
11143 output_log(req, res);
11149Server::write_content_with_provider(Stream &strm,
const Request &req,
11150 Response &res,
const std::string &boundary,
11151 const std::string &content_type)
11153 auto is_shutting_down = [
this]()
11158 if (res.content_length_ > 0)
11160 if (req.ranges.empty())
11162 return detail::write_content(strm, res.content_provider_, 0,
11163 res.content_length_, is_shutting_down);
11165 else if (req.ranges.size() == 1)
11167 auto offset_and_length = detail::get_range_offset_and_length(
11168 req.ranges[0], res.content_length_);
11170 return detail::write_content(strm, res.content_provider_,
11171 offset_and_length.first,
11172 offset_and_length.second, is_shutting_down);
11176 return detail::write_multipart_ranges_data(
11177 strm, req, res, boundary, content_type, res.content_length_,
11183 if (res.is_chunked_content_provider_)
11185 auto type = detail::encoding_type(req, res);
11187 std::unique_ptr<detail::compressor> compressor;
11188 if (
type == detail::EncodingType::Gzip)
11190#ifdef CPPHTTPLIB_ZLIB_SUPPORT
11191 compressor = detail::make_unique<detail::gzip_compressor>();
11194 else if (
type == detail::EncodingType::Brotli)
11196#ifdef CPPHTTPLIB_BROTLI_SUPPORT
11197 compressor = detail::make_unique<detail::brotli_compressor>();
11200 else if (
type == detail::EncodingType::Zstd)
11202#ifdef CPPHTTPLIB_ZSTD_SUPPORT
11203 compressor = detail::make_unique<detail::zstd_compressor>();
11208 compressor = detail::make_unique<detail::nocompressor>();
11210 assert(compressor !=
nullptr);
11212 return detail::write_content_chunked(strm, res.content_provider_,
11213 is_shutting_down, *compressor);
11217 return detail::write_content_without_length(strm, res.content_provider_,
11223inline bool Server::read_content(Stream &strm, Request &req, Response &res)
11225 FormFields::iterator cur_field;
11226 FormFiles::iterator cur_file;
11227 auto is_text_field =
false;
11229 if (read_content_core(
11232 [&](
const char *buf,
size_t n)
11240 if (req.body.size() >= req.body.max_size() ||
11241 n > req.body.max_size() - req.body.size())
11245 req.body.append(buf, n);
11249 [&](
const FormData & file)
11257 if (file.filename.empty())
11259 cur_field = req.form.fields.emplace(
11260 file.name, FormField{file.name, file.content, file.headers});
11261 is_text_field =
true;
11265 cur_file = req.form.files.emplace(file.name, file);
11266 is_text_field =
false;
11270 [&](
const char *buf,
size_t n)
11274 auto &content = cur_field->second.content;
11275 if (content.size() + n > content.max_size())
11279 content.append(buf, n);
11283 auto &content = cur_file->second.content;
11284 if (content.size() + n > content.max_size())
11288 content.append(buf, n);
11293 const auto &content_type = req.get_header_value(
"Content-Type");
11294 if (!content_type.find(
"application/x-www-form-urlencoded"))
11302 detail::parse_query_text(req.body, req.params);
11309inline bool Server::read_content_with_content_receiver(
11313 return read_content_core(strm, req, res, std::move(
receiver),
11314 std::move(multipart_header),
11315 std::move(multipart_receiver));
11318inline bool Server::read_content_core(
11322 detail::FormDataParser multipart_form_data_parser;
11325 if (req.is_multipart_form_data())
11327 const auto &content_type = req.get_header_value(
"Content-Type");
11328 std::string boundary;
11329 if (!detail::parse_multipart_boundary(content_type, boundary))
11336 multipart_form_data_parser.set_boundary(std::move(boundary));
11337 out = [&](
const char *buf,
size_t n,
size_t ,
size_t )
11339 return multipart_form_data_parser.parse(buf, n, multipart_header,
11340 multipart_receiver);
11345 out = [
receiver](
const char *buf,
size_t n,
size_t ,
11362#if !defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(_WIN32)
11363 if (!req.has_header(
"Content-Length") &&
11364 !detail::is_chunked_transfer_encoding(req.headers))
11375 std::vector<char> peekbuf(to_peek);
11376 ssize_t n = ::recv(s, peekbuf.data(), to_peek, MSG_PEEK);
11386 if (!req.has_header(
"Content-Length") &&
11387 !detail::is_chunked_transfer_encoding(req.headers))
11399 if (req.is_multipart_form_data())
11401 if (!multipart_form_data_parser.is_valid())
11412inline bool Server::handle_file_request(Request &req, Response &res)
11414 for (
const auto &entry : base_dirs_)
11417 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point))
11419 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
11420 if (detail::is_valid_path(sub_path))
11422 auto path = entry.base_dir + sub_path;
11423 if (path.back() ==
'/')
11425 path +=
"index.html";
11428 detail::FileStat stat(path);
11436 if (stat.is_file())
11438 for (
const auto &kv : entry.headers)
11440 res.set_header(kv.first, kv.second);
11443 auto etag = detail::compute_etag(stat);
11446 res.set_header(
"ETag", etag);
11449 auto mtime = stat.mtime();
11451 auto last_modified = detail::file_mtime_to_http_date(mtime);
11452 if (!last_modified.empty())
11454 res.set_header(
"Last-Modified", last_modified);
11457 if (check_if_not_modified(req, res, etag, mtime))
11462 check_if_range(req, etag, mtime);
11464 auto mm = std::make_shared<detail::mmap>(path.c_str());
11465 if (!mm->is_open())
11471 res.set_content_provider(
11473 detail::find_content_type(path, file_extension_and_mimetype_map_,
11474 default_file_mimetype_),
11475 [mm](
size_t offset,
size_t length, DataSink & sink) ->
bool
11477 sink.write(mm->data() + offset, length);
11481 if (req.method !=
"HEAD" && file_request_handler_)
11483 file_request_handler_(req, res);
11498inline bool Server::check_if_not_modified(
const Request &req, Response &res,
11499 const std::string &etag,
11500 time_t mtime)
const
11505 if (req.has_header(
"If-None-Match"))
11509 auto val = req.get_header_value(
"If-None-Match");
11517 auto ret = detail::split_find(val.data(), val.data() + val.size(),
',',
11518 [&](
const char *b,
const char *e)
11520 return std::equal(b, e,
"*") ||
11521 std::equal(b, e, etag.begin());
11531 else if (req.has_header(
"If-Modified-Since"))
11533 auto val = req.get_header_value(
"If-Modified-Since");
11534 auto t = detail::parse_http_date(val);
11536 if (t !=
static_cast<time_t
>(-1) && mtime <= t)
11545inline bool Server::check_if_range(Request &req,
const std::string &etag,
11546 time_t mtime)
const
11552 if (!req.ranges.empty() && req.has_header(
"If-Range"))
11554 auto val = req.get_header_value(
"If-Range");
11556 auto is_valid_range = [&]()
11558 if (detail::is_strong_etag(val))
11562 return (!etag.empty() && val == etag);
11564 else if (detail::is_weak_etag(val))
11572 auto t = detail::parse_http_date(val);
11573 return (t !=
static_cast<time_t
>(-1) && mtime <= t);
11577 if (!is_valid_range())
11580 req.ranges.clear();
11589Server::create_server_socket(
const std::string &host,
int port,
11593 return detail::create_socket(
11594 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
11595 ipv6_v6only_, std::move(socket_options),
11596 [&](
socket_t sock,
struct addrinfo & ai,
bool & ) ->
bool
11598 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen)))
11600 output_error_log(Error::BindIPAddress, nullptr);
11612inline int Server::bind_internal(
const std::string &host,
int port,
11615 if (is_decommissioned)
11625 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
11633 struct sockaddr_storage addr;
11634 socklen_t addr_len =
sizeof(addr);
11635 if (getsockname(
svr_sock_,
reinterpret_cast<struct sockaddr *
>(&addr),
11641 if (addr.ss_family == AF_INET)
11643 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&addr)->sin_port);
11645 else if (addr.ss_family == AF_INET6)
11647 return ntohs(
reinterpret_cast<struct sockaddr_in6 *
>(&addr)->sin6_port);
11661inline bool Server::listen_internal()
11663 if (is_decommissioned)
11669 is_running_ =
true;
11670 auto se = detail::scope_exit([&]()
11672 is_running_ =
false;
11688 task_queue->on_idle();
11699#elif defined SOCK_CLOEXEC
11707 if (
errno == EMFILE)
11711 std::this_thread::sleep_for(std::chrono::microseconds{1});
11731 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
11733 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
11736 if (!task_queue->enqueue(
11739 process_and_close_socket(sock);
11743 detail::shutdown_socket(sock);
11748 task_queue->shutdown();
11751 is_decommissioned = !ret;
11755inline bool Server::routing(Request &req, Response &res, Stream &strm)
11757 if (pre_routing_handler_ &&
11764 if ((req.method ==
"GET" || req.method ==
"HEAD") &&
11765 handle_file_request(req, res))
11770 if (detail::expect_content(req))
11774 ContentReader reader(
11777 auto result = read_content_with_content_receiver(
11778 strm, req, res, std::move(
receiver),
nullptr,
nullptr);
11787 auto result = read_content_with_content_receiver(
11788 strm, req, res,
nullptr, std::move(header),
11797 if (req.method ==
"POST")
11799 if (dispatch_request_for_content_reader(
11800 req, res, std::move(reader),
11801 post_handlers_for_content_reader_))
11806 else if (req.method ==
"PUT")
11808 if (dispatch_request_for_content_reader(
11809 req, res, std::move(reader),
11810 put_handlers_for_content_reader_))
11815 else if (req.method ==
"PATCH")
11817 if (dispatch_request_for_content_reader(
11818 req, res, std::move(reader),
11819 patch_handlers_for_content_reader_))
11824 else if (req.method ==
"DELETE")
11826 if (dispatch_request_for_content_reader(
11827 req, res, std::move(reader),
11828 delete_handlers_for_content_reader_))
11836 if (!read_content(strm, req, res))
11844 if (req.method ==
"GET" || req.method ==
"HEAD")
11846 return dispatch_request(req, res, get_handlers_);
11848 else if (req.method ==
"POST")
11850 return dispatch_request(req, res, post_handlers_);
11852 else if (req.method ==
"PUT")
11854 return dispatch_request(req, res, put_handlers_);
11856 else if (req.method ==
"DELETE")
11858 return dispatch_request(req, res, delete_handlers_);
11860 else if (req.method ==
"OPTIONS")
11862 return dispatch_request(req, res, options_handlers_);
11864 else if (req.method ==
"PATCH")
11866 return dispatch_request(req, res, patch_handlers_);
11873inline bool Server::dispatch_request(Request &req, Response &res,
11874 const Handlers &handlers)
const
11876 for (
const auto &x : handlers)
11878 const auto &matcher = x.first;
11879 const auto &handler = x.second;
11881 if (matcher->match(req))
11883 req.matched_route = matcher->pattern();
11884 if (!pre_request_handler_ ||
11895inline void Server::apply_ranges(
const Request &req, Response &res,
11896 std::string &content_type,
11897 std::string &boundary)
const
11901 auto it = res.headers.find(
"Content-Type");
11902 if (it != res.headers.end())
11904 content_type = it->second;
11905 res.headers.erase(it);
11908 boundary = detail::make_multipart_data_boundary();
11910 res.set_header(
"Content-Type",
11911 "multipart/byteranges; boundary=" + boundary);
11914 auto type = detail::encoding_type(req, res);
11916 if (res.body.empty())
11918 if (res.content_length_ > 0)
11923 length = res.content_length_;
11925 else if (req.ranges.size() == 1)
11927 auto offset_and_length = detail::get_range_offset_and_length(
11928 req.ranges[0], res.content_length_);
11930 length = offset_and_length.second;
11932 auto content_range = detail::make_content_range_header_field(
11933 offset_and_length, res.content_length_);
11934 res.set_header(
"Content-Range", content_range);
11938 length = detail::get_multipart_ranges_data_length(
11939 req, boundary, content_type, res.content_length_);
11941 res.set_header(
"Content-Length", std::to_string(length));
11945 if (res.content_provider_)
11947 if (res.is_chunked_content_provider_)
11949 res.set_header(
"Transfer-Encoding",
"chunked");
11950 if (
type == detail::EncodingType::Gzip)
11952 res.set_header(
"Content-Encoding",
"gzip");
11953 res.set_header(
"Vary",
"Accept-Encoding");
11955 else if (
type == detail::EncodingType::Brotli)
11957 res.set_header(
"Content-Encoding",
"br");
11958 res.set_header(
"Vary",
"Accept-Encoding");
11960 else if (
type == detail::EncodingType::Zstd)
11962 res.set_header(
"Content-Encoding",
"zstd");
11963 res.set_header(
"Vary",
"Accept-Encoding");
11975 else if (req.ranges.size() == 1)
11977 auto offset_and_length =
11978 detail::get_range_offset_and_length(req.ranges[0], res.body.size());
11979 auto offset = offset_and_length.first;
11980 auto length = offset_and_length.second;
11982 auto content_range = detail::make_content_range_header_field(
11983 offset_and_length, res.body.size());
11984 res.set_header(
"Content-Range", content_range);
11986 assert(offset + length <= res.body.size());
11987 res.body = res.body.substr(offset, length);
11992 detail::make_multipart_ranges_data(req, res, boundary, content_type,
11993 res.body.size(), data);
11994 res.body.swap(data);
11997 if (
type != detail::EncodingType::None)
11999 output_pre_compression_log(req, res);
12001 std::unique_ptr<detail::compressor> compressor;
12002 std::string content_encoding;
12004 if (
type == detail::EncodingType::Gzip)
12006#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12007 compressor = detail::make_unique<detail::gzip_compressor>();
12008 content_encoding =
"gzip";
12011 else if (
type == detail::EncodingType::Brotli)
12013#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12014 compressor = detail::make_unique<detail::brotli_compressor>();
12015 content_encoding =
"br";
12018 else if (
type == detail::EncodingType::Zstd)
12020#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12021 compressor = detail::make_unique<detail::zstd_compressor>();
12022 content_encoding =
"zstd";
12028 std::string compressed;
12029 if (compressor->compress(res.body.data(), res.body.size(),
true,
12030 [&](
const char *data,
size_t data_len)
12032 compressed.append(data, data_len);
12036 res.body.swap(compressed);
12037 res.set_header(
"Content-Encoding", content_encoding);
12038 res.set_header(
"Vary",
"Accept-Encoding");
12043 auto length = std::to_string(res.body.size());
12044 res.set_header(
"Content-Length", length);
12048inline bool Server::dispatch_request_for_content_reader(
12049 Request &req, Response &res, ContentReader content_reader,
12050 const HandlersForContentReader &handlers)
const
12052 for (
const auto &x : handlers)
12054 const auto &matcher = x.first;
12055 const auto &handler = x.second;
12057 if (matcher->match(req))
12059 req.matched_route = matcher->pattern();
12060 if (!pre_request_handler_ ||
12063 handler(req, res, content_reader);
12073 const std::vector<std::string> &trusted_proxies)
12076 std::vector<std::string> ip_list;
12077 detail::split(x_forwarded_for.data(),
12078 x_forwarded_for.data() + x_forwarded_for.size(),
',',
12079 [&](
const char *b,
const char *e)
12081 auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
12082 ip_list.emplace_back(std::string(b + r.first, b + r.second));
12085 for (
size_t i = 0; i < ip_list.size(); ++i)
12087 auto ip = ip_list[i];
12089 auto is_trusted_proxy =
12090 std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
12091 [&](
const std::string & proxy)
12093 return ip == proxy;
12096 if (is_trusted_proxy)
12106 return ip_list[i - 1];
12112 return ip_list.front();
12117 int remote_port,
const std::string &local_addr,
12118 int local_port,
bool close_connection,
12119 bool &connection_closed,
12120 const std::function<
void(
Request &)> &setup_request)
12122 std::array<char, 2048> buf{};
12124 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12127 if (!line_reader.getline())
12133 req.
start_time_ = std::chrono::steady_clock::now();
12141 res.
headers = default_headers_;
12145 if (strm.
socket() >= FD_SETSIZE)
12148 detail::read_headers(strm, dummy);
12151 return write_response(strm, close_connection, req, res);
12156 if (!parse_request_line(line_reader.ptr(), req))
12160 return write_response(strm, close_connection, req, res);
12164 if (!detail::read_headers(strm, req.
headers))
12168 return write_response(strm, close_connection, req, res);
12176 return write_response(strm, close_connection, req, res);
12181 connection_closed =
true;
12184 if (req.
version ==
"HTTP/1.0" &&
12187 connection_closed =
true;
12211 return write_response(strm, close_connection, req, res);
12218 if (!detail::parse_range_header(range_header_value, req.
ranges))
12222 return write_response(strm, close_connection, req, res);
12228 setup_request(req);
12234 if (expect_100_continue_handler_)
12236 status = expect_100_continue_handler_(req, res);
12242 detail::write_response_line(strm, status);
12243 strm.
write(
"\r\n");
12246 connection_closed =
true;
12247 return write_response(strm,
true, req, res);
12252 auto sock = strm.
socket();
12255 return !detail::is_socket_alive(sock);
12259 auto routed =
false;
12260#ifdef CPPHTTPLIB_NO_EXCEPTIONS
12261 routed = routing(req, res, strm);
12265 routed = routing(req, res, strm);
12267 catch (std::exception &e)
12269 if (exception_handler_)
12271 auto ep = std::current_exception();
12272 exception_handler_(req, res, ep);
12280 for (
size_t i = 0; s[i]; i++)
12300 if (exception_handler_)
12302 auto ep = std::current_exception();
12303 exception_handler_(req, res, ep);
12309 res.
set_header(
"EXCEPTION_WHAT",
"UNKNOWN");
12325 auto mm = std::make_shared<detail::mmap>(path.c_str());
12326 if (!mm->is_open())
12333 return write_response(strm, close_connection, req, res);
12337 if (content_type.empty())
12339 content_type = detail::find_content_type(
12340 path, file_extension_and_mimetype_map_, default_file_mimetype_);
12344 mm->size(), content_type,
12345 [mm](
size_t offset,
size_t length,
DataSink & sink) ->
bool
12347 sink.write(mm->data() + offset, length);
12352 if (detail::range_error(req, res))
12358 return write_response(strm, close_connection, req, res);
12361 return write_response_with_content(strm, close_connection, req, res);
12370 return write_response(strm, close_connection, req, res);
12379inline bool Server::process_and_close_socket(
socket_t sock)
12381 std::string remote_addr;
12382 int remote_port = 0;
12383 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
12385 std::string local_addr;
12386 int local_port = 0;
12387 detail::get_local_ip_and_port(sock, local_addr, local_port);
12389 auto ret = detail::process_server_socket(
12393 [&](
Stream & strm,
bool close_connection,
bool & connection_closed)
12396 local_port, close_connection, connection_closed,
12400 detail::shutdown_socket(sock);
12405inline void Server::output_log(
const Request &req,
const Response &res)
const
12409 std::lock_guard<std::mutex> guard(logger_mutex_);
12414inline void Server::output_pre_compression_log(
const Request &req,
12415 const Response &res)
const
12417 if (pre_compression_logger_)
12419 std::lock_guard<std::mutex> guard(logger_mutex_);
12420 pre_compression_logger_(req, res);
12424inline void Server::output_error_log(
const Error &err,
12425 const Request *req)
const
12429 std::lock_guard<std::mutex> guard(logger_mutex_);
12430 error_logger_(err, req);
12442 const std::string &client_cert_path,
12443 const std::string &client_key_path)
12444 : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
12445 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
12450 size_t retry_count = 10;
12451 while (retry_count-- > 0)
12460 std::this_thread::sleep_for(std::chrono::milliseconds{1});
12486#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12487 digest_auth_username_ = rhs.digest_auth_username_;
12488 digest_auth_password_ = rhs.digest_auth_password_;
12505#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12506 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
12507 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
12509#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12510 ca_cert_file_path_ = rhs.ca_cert_file_path_;
12511 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
12512 ca_cert_store_ = rhs.ca_cert_store_;
12514#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12515 server_certificate_verification_ = rhs.server_certificate_verification_;
12516 server_hostname_verification_ = rhs.server_hostname_verification_;
12517 server_certificate_verifier_ = rhs.server_certificate_verifier_;
12523inline socket_t ClientImpl::create_client_socket(
Error &error)
const
12527 return detail::create_client_socket(
12542 return detail::create_client_socket(
12552 auto sock = create_client_socket(error);
12566#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12567inline bool SSLClient::ensure_socket_connection(Socket &socket,
Error &error)
12574 if (!proxy_host_.empty() && proxy_port_ != -1)
12579 if (!initialize_ssl(socket, error))
12581 shutdown_socket(socket);
12605 detail::shutdown_socket(
socket.sock);
12620#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12621 assert(
socket.ssl ==
nullptr);
12631inline bool ClientImpl::read_response_line(
Stream &strm,
const Request &req,
12634 std::array<char, 2048> buf{};
12636 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12638 if (!line_reader.getline())
12643#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
12644 thread_local const std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
12646 thread_local const std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
12650 if (!std::regex_match(line_reader.ptr(), m, re))
12652 return req.
method ==
"CONNECT";
12654 res.
version = std::string(m[1]);
12655 res.
status = std::stoi(std::string(m[2]));
12656 res.
reason = std::string(m[3]);
12661 if (!line_reader.getline())
12665 if (!line_reader.getline())
12670 if (!std::regex_match(line_reader.ptr(), m, re))
12674 res.
version = std::string(m[1]);
12675 res.
status = std::stoi(std::string(m[2]));
12676 res.
reason = std::string(m[3]);
12684 std::lock_guard<std::recursive_mutex> request_mutex_guard(
request_mutex_);
12685 auto ret = send_(req, res, error);
12689 ret = send_(req, res, error);
12704 auto is_alive =
false;
12707 is_alive = detail::is_socket_alive(
socket_.
sock);
12709#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12710 if (is_alive && is_ssl())
12725 const bool shutdown_gracefully =
false;
12740#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12744 auto &scli =
static_cast<SSLClient &
>(*this);
12747 auto success =
false;
12761 if (!scli.initialize_ssl(
socket_, error))
12793 auto se = detail::scope_exit([&]()
12815 return handle_request(strm, req, res, close_connection, error);
12833 return send_(std::move(req2));
12838 auto res = detail::make_unique<Response>();
12840 auto ret =
send(req, *res, error);
12841#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12842 return Result {ret ? std::move(res) : nullptr, error,
std::move(req.headers),
12843 last_ssl_error_, last_openssl_error_
12846 return Result {ret ? std::move(res) : nullptr, error,
std::move(req.headers)};
12850inline void ClientImpl::prepare_default_headers(Request &r,
bool for_stream,
12851 const std::string &ct)
12856 if (!r.has_header(header.first))
12858 r.headers.insert(header);
12862 if (!r.has_header(
"Host"))
12866 r.headers.emplace(
"Host",
"localhost");
12871 "Host", detail::make_host_and_port_string(
host_,
port_, is_ssl()));
12875 if (!r.has_header(
"Accept"))
12877 r.headers.emplace(
"Accept",
"*/*");
12880 if (!r.content_receiver)
12882 if (!r.has_header(
"Accept-Encoding"))
12884 std::string accept_encoding;
12885#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12886 accept_encoding =
"br";
12888#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12889 if (!accept_encoding.empty())
12891 accept_encoding +=
", ";
12893 accept_encoding +=
"gzip, deflate";
12895#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12896 if (!accept_encoding.empty())
12898 accept_encoding +=
", ";
12900 accept_encoding +=
"zstd";
12902 r.set_header(
"Accept-Encoding", accept_encoding);
12905#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
12906 if (!r.has_header(
"User-Agent"))
12909 r.set_header(
"User-Agent", agent);
12914 if (!r.body.empty())
12916 if (!ct.empty() && !r.has_header(
"Content-Type"))
12918 r.headers.emplace(
"Content-Type", ct);
12920 if (!r.has_header(
"Content-Length"))
12922 r.headers.emplace(
"Content-Length", std::to_string(r.body.size()));
12927inline ClientImpl::StreamHandle
12930 const std::string &body,
12931 const std::string &content_type)
12934 handle.
response = detail::make_unique<Response>();
12938 handle.connection_ = detail::make_unique<ClientConnection>();
12943 auto is_alive =
false;
12946 is_alive = detail::is_socket_alive(
socket_.
sock);
12947#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12948 if (is_alive && is_ssl())
12972#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12975 auto &scli =
static_cast<SSLClient &
>(*this);
12988 transfer_socket_ownership_to_handle(handle);
12991#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12992 if (is_ssl() && handle.connection_->ssl)
12994 handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
13000 handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
13005 handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
13009 handle.stream_ = handle.socket_stream_.get();
13013 req.
path = query_path;
13017 prepare_default_headers(req,
true, content_type);
13019 auto &strm = *handle.stream_;
13020 if (detail::write_request_line(strm, req.
method, req.
path) < 0)
13036 if (strm.
write(body.data(), body.size()) < 0)
13044 if (!read_response_line(strm, req, *handle.
response) ||
13045 !detail::read_headers(strm, handle.
response->headers))
13052 handle.body_reader_.
stream = handle.stream_;
13054 auto content_length_str = handle.
response->get_header_value(
"Content-Length");
13055 if (!content_length_str.empty())
13058 static_cast<size_t>(std::stoull(content_length_str));
13061 auto transfer_encoding =
13062 handle.
response->get_header_value(
"Transfer-Encoding");
13063 handle.body_reader_.
chunked = (transfer_encoding ==
"chunked");
13065 auto content_encoding = handle.
response->get_header_value(
"Content-Encoding");
13066 if (!content_encoding.empty())
13068 handle.decompressor_ = detail::create_decompressor(content_encoding);
13083 return read_with_decompression(buf, len);
13087 if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_)
13089 trailers_parsed_ =
true;
13090 if (body_reader_.chunked_decoder)
13092 if (!body_reader_.chunked_decoder->parse_trailers_into(
13093 response->trailers, response->headers))
13100 detail::ChunkedDecoder
dec(*stream_);
13101 if (!
dec.parse_trailers_into(response->trailers, response->headers))
13111inline ssize_t ClientImpl::StreamHandle::read_with_decompression(
char *buf,
13114 if (decompress_offset_ < decompress_buffer_.size())
13116 auto available = decompress_buffer_.size() - decompress_offset_;
13117 auto to_copy = (std::min)(len, available);
13118 std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
13119 decompress_offset_ += to_copy;
13120 return static_cast<ssize_t
>(to_copy);
13123 decompress_buffer_.clear();
13124 decompress_offset_ = 0;
13126 constexpr size_t kDecompressionBufferSize = 8192;
13127 char compressed_buf[kDecompressionBufferSize];
13132 sizeof(compressed_buf));
13139 bool decompress_ok =
13140 decompressor_->decompress(compressed_buf,
static_cast<size_t>(n),
13141 [
this](
const char *data,
size_t data_len)
13143 decompress_buffer_.append(data, data_len);
13147 if (!decompress_ok)
13153 if (!decompress_buffer_.empty())
13159 auto to_copy = (std::min)(len, decompress_buffer_.size());
13160 std::memcpy(buf, decompress_buffer_.data(), to_copy);
13161 decompress_offset_ = to_copy;
13162 return static_cast<ssize_t
>(to_copy);
13167 if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_)
13172 trailers_parsed_ =
true;
13174 const auto bufsiz = 128;
13175 char line_buf[bufsiz];
13176 detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
13178 if (!line_reader.getline())
13183 if (!detail::parse_trailers(line_reader, response->trailers,
13184 response->headers))
13194inline ChunkedDecoder::ChunkedDecoder(
Stream &s) : strm(s) {}
13196inline ssize_t ChunkedDecoder::read_payload(
char *buf,
size_t len,
13197 size_t &out_chunk_offset,
13198 size_t &out_chunk_total)
13205 if (chunk_remaining == 0)
13207 stream_line_reader lr(strm, line_buf,
sizeof(line_buf));
13213 char *endptr =
nullptr;
13214 unsigned long chunk_len = std::strtoul(lr.ptr(), &endptr, 16);
13215 if (endptr == lr.ptr())
13219 if (chunk_len == ULONG_MAX)
13224 if (chunk_len == 0)
13226 chunk_remaining = 0;
13228 out_chunk_offset = 0;
13229 out_chunk_total = 0;
13233 chunk_remaining =
static_cast<size_t>(chunk_len);
13234 last_chunk_total = chunk_remaining;
13235 last_chunk_offset = 0;
13238 auto to_read = (std::min)(chunk_remaining, len);
13239 auto n = strm.
read(buf, to_read);
13245 auto offset_before = last_chunk_offset;
13246 last_chunk_offset +=
static_cast<size_t>(n);
13247 chunk_remaining -=
static_cast<size_t>(n);
13249 out_chunk_offset = offset_before;
13250 out_chunk_total = last_chunk_total;
13252 if (chunk_remaining == 0)
13254 stream_line_reader lr(strm, line_buf,
sizeof(line_buf));
13259 if (std::strcmp(lr.ptr(),
"\r\n") != 0)
13268inline bool ChunkedDecoder::parse_trailers_into(
Headers &dest,
13271 stream_line_reader lr(strm, line_buf,
sizeof(line_buf));
13276 return parse_trailers(lr, dest, src_headers);
13282ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle)
13285#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13286 handle.connection_->ssl =
socket_.ssl;
13292inline bool ClientImpl::handle_request(Stream &strm, Request &req,
13293 Response &res,
bool close_connection,
13296 if (req.path.empty())
13303 auto req_save = req;
13310 req2.path =
"http://" +
13311 detail::make_host_and_port_string(
host_,
port_,
false) +
13314 req = std::move(req2);
13315 req.path = req_save.path;
13327 if (res.get_header_value(
"Connection") ==
"close" ||
13328 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established"))
13345 req = std::move(req_save);
13346 ret = redirect(req, res, error);
13349#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13352 req.authorization_count_ < 5)
13355 const auto &username =
13356 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
13357 const auto &password =
13358 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
13360 if (!username.empty() && !password.empty())
13362 std::map<std::string, std::string> auth;
13363 if (detail::parse_www_authenticate(res, auth, is_proxy))
13365 Request new_req = req;
13366 new_req.authorization_count_ += 1;
13367 new_req.headers.erase(is_proxy ?
"Proxy-Authorization"
13368 :
"Authorization");
13369 new_req.headers.insert(detail::make_digest_authentication_header(
13370 req, auth, new_req.authorization_count_, detail::random_string(10),
13371 username, password, is_proxy));
13375 ret =
send(new_req, new_res, error);
13378 res = std::move(new_res);
13388inline bool ClientImpl::redirect(Request &req, Response &res,
Error &error)
13390 if (req.redirect_count_ == 0)
13397 auto location = res.get_header_value(
"location");
13398 if (location.empty())
13403 thread_local const std::regex re(
13404 R
"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
13407 if (!std::regex_match(location, m, re))
13412 auto scheme = is_ssl() ?
"https" :
"http";
13414 auto next_scheme = m[1].str();
13415 auto next_host = m[2].str();
13416 if (next_host.empty())
13418 next_host = m[3].str();
13420 auto port_str = m[4].str();
13421 auto next_path = m[5].str();
13422 auto next_query = m[6].str();
13424 auto next_port =
port_;
13425 if (!port_str.empty())
13427 next_port = std::stoi(port_str);
13429 else if (!next_scheme.empty())
13431 next_port = next_scheme ==
"https" ? 443 : 80;
13434 if (next_scheme.empty())
13436 next_scheme = scheme;
13438 if (next_host.empty())
13442 if (next_path.empty())
13450 if (next_scheme == scheme && next_host ==
host_ && next_port ==
port_)
13452 return detail::redirect(*
this, req, res, path, location, error);
13456 return create_redirect_client(next_scheme, next_host, next_port, req, res,
13457 path, location, error);
13461inline bool ClientImpl::create_redirect_client(
13462 const std::string &scheme,
const std::string &host,
int port, Request &req,
13463 Response &res,
const std::string &path,
const std::string &location,
13467 auto need_ssl = (scheme ==
"https");
13471 auto headers_to_remove =
13472 std::vector<std::string> {
"Host",
"Proxy-Authorization",
"Authorization"};
13474 for (
const auto &header_name : headers_to_remove)
13476 auto it = req.headers.find(header_name);
13477 while (it != req.headers.end())
13479 it = req.headers.erase(it);
13480 it = req.headers.find(header_name);
13487#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13489 SSLClient redirect_client(
host,
port);
13492 setup_redirect_client(redirect_client);
13498 redirect_client.enable_server_certificate_verification(
false);
13499 redirect_client.enable_server_hostname_verification(
false);
13504 redirect_client.enable_server_certificate_verification(
13505 server_certificate_verification_);
13506 redirect_client.enable_server_hostname_verification(
13507 server_hostname_verification_);
13511 if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_))
13513 redirect_client.set_ca_cert_store(ca_cert_store_);
13515 if (!ca_cert_file_path_.empty())
13517 redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
13526 return detail::redirect(redirect_client, req, res, path, location, error);
13540 setup_redirect_client(redirect_client);
13543 return detail::redirect(redirect_client, req, res, path, location, error);
13549template <
typename ClientType>
13550inline void ClientImpl::setup_redirect_client(ClientType &client)
13557 client.set_follow_location(
13572#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13573 if (!digest_auth_username_.empty())
13575 client.set_digest_auth(digest_auth_username_, digest_auth_password_);
13596#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13597 if (!proxy_digest_auth_username_.empty())
13599 client.set_proxy_digest_auth(proxy_digest_auth_username_,
13600 proxy_digest_auth_password_);
13634 Error &error)
const
13636 auto is_shutting_down = []()
13644 std::unique_ptr<detail::compressor> compressor;
13645#ifdef CPPHTTPLIB_ZLIB_SUPPORT
13648 compressor = detail::make_unique<detail::gzip_compressor>();
13653 compressor = detail::make_unique<detail::nocompressor>();
13657 is_shutting_down, *compressor, error);
13661 return detail::write_content_with_progress(
13667inline bool ClientImpl::write_request(
Stream &strm,
Request &req,
13668 bool close_connection,
Error &error)
13671 if (close_connection)
13679 std::string ct_for_defaults;
13682 ct_for_defaults =
"text/plain";
13684 prepare_default_headers(req,
false, ct_for_defaults);
13686 if (req.
body.empty())
13748 detail::BufferStream bstrm;
13751 std::string path_part, query_part;
13752 auto query_pos = req.
path.find(
'?');
13753 if (query_pos != std::string::npos)
13755 path_part = req.
path.substr(0, query_pos);
13756 query_part = req.
path.substr(query_pos + 1);
13760 path_part = req.
path;
13771 auto path_with_query =
13772 path_encode_ ? detail::encode_path(path_part) : path_part;
13774 if (!query_part.empty())
13778 auto normalized = detail::normalize_query_string(query_part);
13779 if (!normalized.empty())
13781 path_with_query +=
'?' + normalized;
13785 detail::parse_query_text(query_part, req.
params);
13792 detail::parse_query_text(query_part, req.
params);
13793 if (!req.
params.empty())
13800 detail::write_request_line(bstrm, req.
method, path_with_query);
13804 auto &data = bstrm.get_buffer();
13805 if (!detail::write_data(strm, data.data(), data.size()))
13814 if (req.
body.empty())
13821 auto body_size = req.
body.size();
13822 size_t written = 0;
13823 auto data = req.
body.data();
13825 while (written < body_size)
13828 if (!detail::write_data(strm, data + written, to_write))
13834 written += to_write;
13846 if (!detail::write_data(strm, req.
body.data(), req.
body.size()))
13857inline std::unique_ptr<Response>
13858ClientImpl::send_with_content_provider_and_receiver(
13859 Request &req,
const char *body,
size_t content_length,
13865 if (!content_type.empty())
13867 req.set_header(
"Content-Type", content_type);
13870#ifdef CPPHTTPLIB_ZLIB_SUPPORT
13873 req.set_header(
"Content-Encoding",
"gzip");
13877#ifdef CPPHTTPLIB_ZLIB_SUPPORT
13878 if (
compress_ && !content_provider_without_length)
13881 detail::gzip_compressor compressor;
13883 if (content_provider)
13887 DataSink data_sink;
13889 data_sink.write = [&](
const char *data,
size_t data_len) ->
bool
13893 auto last = offset + data_len == content_length;
13895 auto ret = compressor.compress(
13896 data, data_len, last,
13897 [&](
const char *compressed_data,
size_t compressed_data_len)
13899 req.body.append(compressed_data, compressed_data_len);
13905 offset += data_len;
13915 while (ok && offset < content_length)
13917 if (!content_provider(offset, content_length - offset, data_sink))
13927 if (!compressor.compress(body, content_length,
true,
13928 [&](
const char *data,
size_t data_len)
13930 req.body.append(data, data_len);
13943 if (content_provider)
13945 req.content_length_ = content_length;
13946 req.content_provider_ = std::move(content_provider);
13947 req.is_chunked_content_provider_ =
false;
13949 else if (content_provider_without_length)
13951 req.content_length_ = 0;
13952 req.content_provider_ = detail::ContentProviderAdapter(
13953 std::move(content_provider_without_length));
13954 req.is_chunked_content_provider_ =
true;
13955 req.set_header(
"Transfer-Encoding",
"chunked");
13959 req.body.assign(body, content_length);
13963 if (content_receiver)
13965 req.content_receiver =
13966 [content_receiver](
const char *data,
size_t data_length,
13969 return content_receiver(data, data_length);
13973 auto res = detail::make_unique<Response>();
13974 return send(req, *res, error) ? std::move(res) : nullptr;
13977inline Result ClientImpl::send_with_content_provider_and_receiver(
13978 const std::string &method,
const std::string &path,
const Headers &headers,
13979 const char *body,
size_t content_length,
ContentProvider content_provider,
13985 req.method = method;
13986 req.headers = headers;
13988 req.upload_progress = std::move(progress);
13991 req.start_time_ = std::chrono::steady_clock::now();
13996 auto res = send_with_content_provider_and_receiver(
13997 req, body, content_length, std::move(content_provider),
13998 std::move(content_provider_without_length), content_type,
13999 std::move(content_receiver), error);
14001#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14002 return Result {std::move(res),
error, std::move(req.headers), last_ssl_error_,
14003 last_openssl_error_
14006 return Result {std::move(res),
error, std::move(req.headers)};
14031 Response &res,
bool close_connection,
14035 if (!write_request(strm, req, close_connection, error))
14040#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14044 if (!is_proxy_enabled)
14057 if (!read_response_line(strm, req, res) ||
14058 !detail::read_headers(strm, res.
headers))
14067 req.
method !=
"CONNECT")
14069 auto redirect = 300 < res.
status && res.
status < 400 &&
14086 [&](
const char *buf,
size_t n,
size_t off,
size_t len)
14101 [&](
const char *buf,
size_t n,
size_t ,
14104 assert(res.
body.size() + n <= res.
body.max_size());
14105 res.
body.append(buf, n);
14109 auto progress = [&](
size_t current,
size_t total)
14129 if (len > res.
body.max_size())
14135 res.
body.reserve(
static_cast<size_t>(len));
14142 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
14143 dummy_status, std::move(progress),
14166 size_t cur_item = 0;
14167 size_t cur_start = 0;
14170 return [ &, cur_item, cur_start](
size_t offset,
14173 if (!offset && !items.empty())
14175 sink.os << detail::serialize_multipart_formdata(items, boundary,
false);
14178 else if (cur_item < provider_items.size())
14182 const auto &begin = detail::serialize_multipart_formdata_item_begin(
14183 provider_items[cur_item], boundary);
14184 offset += begin.size();
14185 cur_start = offset;
14190 auto has_data =
true;
14191 cur_sink.write = sink.write;
14192 cur_sink.done = [&]()
14197 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
14204 sink.os << detail::serialize_multipart_formdata_item_end();
14212 sink.os << detail::serialize_multipart_formdata_finish(boundary);
14219inline bool ClientImpl::process_socket(
14220 const Socket &socket,
14221 std::chrono::time_point<std::chrono::steady_clock> start_time,
14222 std::function<
bool(Stream &strm)> callback)
14224 return detail::process_client_socket(
14229inline bool ClientImpl::is_ssl()
const
14237 return Get(path,
Headers(), std::move(progress));
14244 if (params.empty())
14246 return Get(path, headers);
14250 return Get(path_with_query, headers, std::move(progress));
14263 req.
start_time_ = std::chrono::steady_clock::now();
14266 return send_(std::move(req));
14273 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
14274 std::move(progress));
14281 return Get(path, headers,
nullptr, std::move(content_receiver),
14282 std::move(progress));
14290 return Get(path,
Headers(), std::move(response_handler),
14291 std::move(content_receiver), std::move(progress));
14305 [content_receiver](
const char *data,
size_t data_length,
14308 return content_receiver(data, data_length);
14313 req.
start_time_ = std::chrono::steady_clock::now();
14316 return send_(std::move(req));
14324 return Get(path, params, headers,
nullptr, std::move(content_receiver),
14325 std::move(progress));
14334 if (params.empty())
14336 return Get(path, headers, std::move(response_handler),
14337 std::move(content_receiver), std::move(progress));
14341 return Get(path_with_query, headers, std::move(response_handler),
14342 std::move(content_receiver), std::move(progress));
14359 req.
start_time_ = std::chrono::steady_clock::now();
14362 return send_(std::move(req));
14367 return Post(path, std::string(), std::string());
14373 return Post(path, headers,
nullptr, 0, std::string());
14377 size_t content_length,
14378 const std::string &content_type,
14381 return Post(path,
Headers(), body, content_length, content_type, progress);
14385 const std::string &content_type,
14388 return Post(path,
Headers(), body, content_type, progress);
14398 const std::string &content_type,
14401 return Post(path,
Headers(), content_length, std::move(content_provider),
14402 content_type, progress);
14407 const std::string &content_type,
14411 return Post(path,
Headers(), content_length, std::move(content_provider),
14412 content_type, std::move(content_receiver), progress);
14417 const std::string &content_type,
14420 return Post(path,
Headers(), std::move(content_provider), content_type,
14426 const std::string &content_type,
14430 return Post(path,
Headers(), std::move(content_provider), content_type,
14431 std::move(content_receiver), progress);
14437 auto query = detail::params_to_query_str(params);
14438 return Post(path, headers, query,
"application/x-www-form-urlencoded");
14452 const auto &boundary = detail::make_multipart_data_boundary();
14453 const auto &content_type =
14454 detail::serialize_multipart_formdata_get_content_type(boundary);
14455 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14456 return Post(path, headers, body, content_type, progress);
14461 const std::string &boundary,
14464 if (!detail::is_multipart_boundary_chars_valid(boundary))
14469 const auto &content_type =
14470 detail::serialize_multipart_formdata_get_content_type(boundary);
14471 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14472 return Post(path, headers, body, content_type, progress);
14476 const char *body,
size_t content_length,
14477 const std::string &content_type,
14480 return send_with_content_provider_and_receiver(
14481 "POST", path, headers, body, content_length,
nullptr,
nullptr,
14482 content_type,
nullptr, progress);
14486 const std::string &body,
14487 const std::string &content_type,
14490 return send_with_content_provider_and_receiver(
14491 "POST", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14492 content_type,
nullptr, progress);
14496 size_t content_length,
14498 const std::string &content_type,
14501 return send_with_content_provider_and_receiver(
14502 "POST", path, headers,
nullptr, content_length,
14503 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14507 size_t content_length,
14509 const std::string &content_type,
14513 return send_with_content_provider_and_receiver(
14514 "POST", path, headers,
nullptr, content_length,
14515 std::move(content_provider),
nullptr, content_type,
14516 std::move(content_receiver), std::move(progress));
14521 const std::string &content_type,
14524 return send_with_content_provider_and_receiver(
14525 "POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14526 content_type,
nullptr, progress);
14531 const std::string &content_type,
14535 return send_with_content_provider_and_receiver(
14536 "POST", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14537 content_type, std::move(content_receiver), std::move(progress));
14545 const auto &boundary = detail::make_multipart_data_boundary();
14546 const auto &content_type =
14547 detail::serialize_multipart_formdata_get_content_type(boundary);
14548 return send_with_content_provider_and_receiver(
14549 "POST", path, headers,
nullptr, 0,
nullptr,
14550 get_multipart_content_provider(boundary, items, provider_items),
14551 content_type,
nullptr, progress);
14555 const std::string &body,
14556 const std::string &content_type,
14566 [content_receiver](
const char *data,
size_t data_length,
14569 return content_receiver(data, data_length);
14575 req.
start_time_ = std::chrono::steady_clock::now();
14578 if (!content_type.empty())
14580 req.
set_header(
"Content-Type", content_type);
14583 return send_(std::move(req));
14588 return Put(path, std::string(), std::string());
14593 return Put(path, headers,
nullptr, 0, std::string());
14597 size_t content_length,
14598 const std::string &content_type,
14601 return Put(path,
Headers(), body, content_length, content_type, progress);
14605 const std::string &content_type,
14608 return Put(path,
Headers(), body, content_type, progress);
14618 const std::string &content_type,
14621 return Put(path,
Headers(), content_length, std::move(content_provider),
14622 content_type, progress);
14627 const std::string &content_type,
14631 return Put(path,
Headers(), content_length, std::move(content_provider),
14632 content_type, std::move(content_receiver), progress);
14637 const std::string &content_type,
14640 return Put(path,
Headers(), std::move(content_provider), content_type,
14646 const std::string &content_type,
14650 return Put(path,
Headers(), std::move(content_provider), content_type,
14651 std::move(content_receiver), progress);
14657 auto query = detail::params_to_query_str(params);
14658 return Put(path, headers, query,
"application/x-www-form-urlencoded");
14665 return Put(path,
Headers(), items, progress);
14672 const auto &boundary = detail::make_multipart_data_boundary();
14673 const auto &content_type =
14674 detail::serialize_multipart_formdata_get_content_type(boundary);
14675 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14676 return Put(path, headers, body, content_type, progress);
14681 const std::string &boundary,
14684 if (!detail::is_multipart_boundary_chars_valid(boundary))
14689 const auto &content_type =
14690 detail::serialize_multipart_formdata_get_content_type(boundary);
14691 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14692 return Put(path, headers, body, content_type, progress);
14696 const char *body,
size_t content_length,
14697 const std::string &content_type,
14700 return send_with_content_provider_and_receiver(
14701 "PUT", path, headers, body, content_length,
nullptr,
nullptr,
14702 content_type,
nullptr, progress);
14706 const std::string &body,
14707 const std::string &content_type,
14710 return send_with_content_provider_and_receiver(
14711 "PUT", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14712 content_type,
nullptr, progress);
14716 size_t content_length,
14718 const std::string &content_type,
14721 return send_with_content_provider_and_receiver(
14722 "PUT", path, headers,
nullptr, content_length,
14723 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14727 size_t content_length,
14729 const std::string &content_type,
14733 return send_with_content_provider_and_receiver(
14734 "PUT", path, headers,
nullptr, content_length,
14735 std::move(content_provider),
nullptr, content_type,
14736 std::move(content_receiver), progress);
14741 const std::string &content_type,
14744 return send_with_content_provider_and_receiver(
14745 "PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14746 content_type,
nullptr, progress);
14751 const std::string &content_type,
14755 return send_with_content_provider_and_receiver(
14756 "PUT", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14757 content_type, std::move(content_receiver), progress);
14765 const auto &boundary = detail::make_multipart_data_boundary();
14766 const auto &content_type =
14767 detail::serialize_multipart_formdata_get_content_type(boundary);
14768 return send_with_content_provider_and_receiver(
14769 "PUT", path, headers,
nullptr, 0,
nullptr,
14770 get_multipart_content_provider(boundary, items, provider_items),
14771 content_type,
nullptr, progress);
14775 const std::string &body,
14776 const std::string &content_type,
14786 [content_receiver](
const char *data,
size_t data_length,
14789 return content_receiver(data, data_length);
14795 req.
start_time_ = std::chrono::steady_clock::now();
14798 if (!content_type.empty())
14800 req.
set_header(
"Content-Type", content_type);
14803 return send_(std::move(req));
14808 return Patch(path, std::string(), std::string());
14814 return Patch(path, headers,
nullptr, 0, std::string(), progress);
14818 size_t content_length,
14819 const std::string &content_type,
14822 return Patch(path,
Headers(), body, content_length, content_type, progress);
14826 const std::string &body,
14827 const std::string &content_type,
14830 return Patch(path,
Headers(), body, content_type, progress);
14840 const std::string &content_type,
14843 return Patch(path,
Headers(), content_length, std::move(content_provider),
14844 content_type, progress);
14849 const std::string &content_type,
14853 return Patch(path,
Headers(), content_length, std::move(content_provider),
14854 content_type, std::move(content_receiver), progress);
14859 const std::string &content_type,
14862 return Patch(path,
Headers(), std::move(content_provider), content_type,
14868 const std::string &content_type,
14872 return Patch(path,
Headers(), std::move(content_provider), content_type,
14873 std::move(content_receiver), progress);
14879 auto query = detail::params_to_query_str(params);
14880 return Patch(path, headers, query,
"application/x-www-form-urlencoded");
14894 const auto &boundary = detail::make_multipart_data_boundary();
14895 const auto &content_type =
14896 detail::serialize_multipart_formdata_get_content_type(boundary);
14897 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14898 return Patch(path, headers, body, content_type, progress);
14903 const std::string &boundary,
14906 if (!detail::is_multipart_boundary_chars_valid(boundary))
14911 const auto &content_type =
14912 detail::serialize_multipart_formdata_get_content_type(boundary);
14913 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14914 return Patch(path, headers, body, content_type, progress);
14918 const char *body,
size_t content_length,
14919 const std::string &content_type,
14922 return send_with_content_provider_and_receiver(
14923 "PATCH", path, headers, body, content_length,
nullptr,
nullptr,
14924 content_type,
nullptr, progress);
14928 const std::string &body,
14929 const std::string &content_type,
14932 return send_with_content_provider_and_receiver(
14933 "PATCH", path, headers, body.data(), body.size(),
nullptr,
nullptr,
14934 content_type,
nullptr, progress);
14938 size_t content_length,
14940 const std::string &content_type,
14943 return send_with_content_provider_and_receiver(
14944 "PATCH", path, headers,
nullptr, content_length,
14945 std::move(content_provider),
nullptr, content_type,
nullptr, progress);
14949 size_t content_length,
14951 const std::string &content_type,
14955 return send_with_content_provider_and_receiver(
14956 "PATCH", path, headers,
nullptr, content_length,
14957 std::move(content_provider),
nullptr, content_type,
14958 std::move(content_receiver), progress);
14963 const std::string &content_type,
14966 return send_with_content_provider_and_receiver(
14967 "PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14968 content_type,
nullptr, progress);
14973 const std::string &content_type,
14977 return send_with_content_provider_and_receiver(
14978 "PATCH", path, headers,
nullptr, 0,
nullptr, std::move(content_provider),
14979 content_type, std::move(content_receiver), progress);
14987 const auto &boundary = detail::make_multipart_data_boundary();
14988 const auto &content_type =
14989 detail::serialize_multipart_formdata_get_content_type(boundary);
14990 return send_with_content_provider_and_receiver(
14991 "PATCH", path, headers,
nullptr, 0,
nullptr,
14992 get_multipart_content_provider(boundary, items, provider_items),
14993 content_type,
nullptr, progress);
14997 const std::string &body,
14998 const std::string &content_type,
15008 [content_receiver](
const char *data,
size_t data_length,
15011 return content_receiver(data, data_length);
15017 req.
start_time_ = std::chrono::steady_clock::now();
15020 if (!content_type.empty())
15022 req.
set_header(
"Content-Type", content_type);
15025 return send_(std::move(req));
15031 return Delete(path,
Headers(), std::string(), std::string(), progress);
15038 return Delete(path, headers, std::string(), std::string(), progress);
15042 size_t content_length,
15043 const std::string &content_type,
15046 return Delete(path,
Headers(), body, content_length, content_type, progress);
15050 const std::string &body,
15051 const std::string &content_type,
15054 return Delete(path,
Headers(), body.data(), body.size(), content_type,
15060 const std::string &body,
15061 const std::string &content_type,
15064 return Delete(path, headers, body.data(), body.size(), content_type,
15078 auto query = detail::params_to_query_str(params);
15079 return Delete(path, headers, query,
"application/x-www-form-urlencoded",
15084 const Headers &headers,
const char *body,
15085 size_t content_length,
15086 const std::string &content_type,
15096 req.
start_time_ = std::chrono::steady_clock::now();
15099 if (!content_type.empty())
15101 req.
set_header(
"Content-Type", content_type);
15103 req.
body.assign(body, content_length);
15105 return send_(std::move(req));
15122 req.
start_time_ = std::chrono::steady_clock::now();
15125 return send_(std::move(req));
15198 const std::string &password)
15209#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15210inline void ClientImpl::set_digest_auth(
const std::string &username,
15211 const std::string &password)
15213 digest_auth_username_ = username;
15214 digest_auth_password_ = password;
15292 const std::string &password)
15303#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15304inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
15305 const std::string &password)
15307 proxy_digest_auth_username_ = username;
15308 proxy_digest_auth_password_ = password;
15311inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
15312 const std::string &ca_cert_dir_path)
15314 ca_cert_file_path_ = ca_cert_file_path;
15315 ca_cert_dir_path_ = ca_cert_dir_path;
15318inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store)
15320 if (ca_cert_store && ca_cert_store != ca_cert_store_)
15322 ca_cert_store_ = ca_cert_store;
15326inline X509_STORE *ClientImpl::create_ca_cert_store(
const char *ca_cert,
15327 std::size_t size)
const
15329 auto mem = BIO_new_mem_buf(ca_cert,
static_cast<int>(size));
15330 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
15336 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
15342 auto cts = X509_STORE_new();
15345 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++)
15347 auto itmp = sk_X509_INFO_value(inf, i);
15355 X509_STORE_add_cert(cts, itmp->x509);
15359 X509_STORE_add_crl(cts, itmp->crl);
15364 sk_X509_INFO_pop_free(inf, X509_INFO_free);
15368inline void ClientImpl::enable_server_certificate_verification(
bool enabled)
15370 server_certificate_verification_ = enabled;
15373inline void ClientImpl::enable_server_hostname_verification(
bool enabled)
15375 server_hostname_verification_ = enabled;
15378inline void ClientImpl::set_server_certificate_verifier(
15381 server_certificate_verifier_ = verifier;
15398#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15402inline bool is_ip_address(
const std::string &host)
15404 struct in_addr addr4;
15405 struct in6_addr addr6;
15406 return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
15407 inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
15410template <
typename U,
typename V>
15411inline SSL *ssl_new(
socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
15412 U SSL_connect_or_accept, V setup)
15414 SSL *ssl =
nullptr;
15416 std::lock_guard<std::mutex> guard(ctx_mutex);
15417 ssl = SSL_new(ctx);
15422 set_nonblocking(sock,
true);
15423 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
15424 BIO_set_nbio(bio, 1);
15425 SSL_set_bio(ssl, bio, bio);
15427 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1)
15431 std::lock_guard<std::mutex> guard(ctx_mutex);
15434 set_nonblocking(sock,
false);
15437 BIO_set_nbio(bio, 0);
15438 set_nonblocking(sock,
false);
15444inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
socket_t sock,
15445 bool shutdown_gracefully)
15451 if (shutdown_gracefully)
15456 if (SSL_shutdown(ssl) == 0)
15463 std::lock_guard<std::mutex> guard(ctx_mutex);
15467template <
typename U>
15468bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL *ssl,
15469 U ssl_connect_or_accept,
15470 time_t timeout_sec, time_t timeout_usec,
15474 while ((res = ssl_connect_or_accept(ssl)) != 1)
15476 auto err = SSL_get_error(ssl, res);
15479 case SSL_ERROR_WANT_READ:
15480 if (select_read(sock, timeout_sec, timeout_usec) > 0)
15485 case SSL_ERROR_WANT_WRITE:
15486 if (select_write(sock, timeout_sec, timeout_usec) > 0)
15503template <
typename T>
15504inline bool process_server_socket_ssl(
15505 const std::atomic<socket_t> &svr_sock, SSL *ssl,
socket_t sock,
15506 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
15507 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
15508 time_t write_timeout_usec, T callback)
15510 return process_server_socket_core(
15511 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
15512 [&](
bool close_connection,
bool & connection_closed)
15514 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
15515 write_timeout_sec, write_timeout_usec);
15516 return callback(strm, close_connection, connection_closed);
15520template <
typename T>
15521inline bool process_client_socket_ssl(
15522 SSL *ssl,
socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
15523 time_t write_timeout_sec, time_t write_timeout_usec,
15524 time_t max_timeout_msec,
15525 std::chrono::time_point<std::chrono::steady_clock> start_time, T callback)
15527 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
15528 write_timeout_sec, write_timeout_usec, max_timeout_msec,
15530 return callback(strm);
15534inline SSLSocketStream::SSLSocketStream(
15535 socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
15536 time_t write_timeout_sec, time_t write_timeout_usec,
15537 time_t max_timeout_msec,
15538 std::chrono::time_point<std::chrono::steady_clock> start_time)
15539 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
15540 read_timeout_usec_(read_timeout_usec),
15541 write_timeout_sec_(write_timeout_sec),
15542 write_timeout_usec_(write_timeout_usec),
15543 max_timeout_msec_(max_timeout_msec), start_time_(start_time)
15545 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
15548inline SSLSocketStream::~SSLSocketStream() =
default;
15550inline bool SSLSocketStream::is_readable()
const
15552 return SSL_pending(ssl_) > 0;
15555inline bool SSLSocketStream::wait_readable()
const
15557 if (max_timeout_msec_ <= 0)
15559 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
15562 time_t read_timeout_sec;
15563 time_t read_timeout_usec;
15564 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
15565 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
15567 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
15570inline bool SSLSocketStream::wait_writable()
const
15572 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
15573 is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
15576inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size)
15578 if (SSL_pending(ssl_) > 0)
15580 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
15587 else if (wait_readable())
15589 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
15592 auto err = SSL_get_error(ssl_, ret);
15595 while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
15596 (err == SSL_ERROR_SYSCALL &&
15597 WSAGetLastError() == WSAETIMEDOUT)))
15600 while (--n >= 0 && err == SSL_ERROR_WANT_READ)
15603 if (SSL_pending(ssl_) > 0)
15605 return SSL_read(ssl_, ptr,
static_cast<int>(size));
15607 else if (wait_readable())
15609 std::this_thread::sleep_for(std::chrono::microseconds{10});
15610 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
15615 err = SSL_get_error(ssl_, ret);
15637inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size)
15639 if (wait_writable())
15641 auto handle_size =
static_cast<int>(
15642 std::min<size_t>(size, (std::numeric_limits<int>::max)()));
15644 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
15647 auto err = SSL_get_error(ssl_, ret);
15650 while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
15651 (err == SSL_ERROR_SYSCALL &&
15652 WSAGetLastError() == WSAETIMEDOUT)))
15655 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE)
15658 if (wait_writable())
15660 std::this_thread::sleep_for(std::chrono::microseconds{10});
15661 ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
15666 err = SSL_get_error(ssl_, ret);
15680inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
15683 detail::get_remote_ip_and_port(sock_, ip, port);
15686inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
15689 detail::get_local_ip_and_port(sock_, ip, port);
15692inline socket_t SSLSocketStream::socket()
const
15697inline time_t SSLSocketStream::duration()
const
15699 return std::chrono::duration_cast<std::chrono::milliseconds>(
15700 std::chrono::steady_clock::now() - start_time_)
15707inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
15708 const char *client_ca_cert_file_path,
15709 const char *client_ca_cert_dir_path,
15710 const char *private_key_password)
15712 ctx_ = SSL_CTX_new(TLS_server_method());
15716 SSL_CTX_set_options(ctx_,
15717 SSL_OP_NO_COMPRESSION |
15718 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
15720 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
15722 if (private_key_password !=
nullptr && (private_key_password[0] !=
'\0'))
15724 SSL_CTX_set_default_passwd_cb_userdata(
15726 reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password)));
15729 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
15730 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
15732 SSL_CTX_check_private_key(ctx_) != 1)
15734 last_ssl_error_ =
static_cast<int>(ERR_get_error());
15735 SSL_CTX_free(ctx_);
15738 else if (client_ca_cert_file_path || client_ca_cert_dir_path)
15740 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
15741 client_ca_cert_dir_path);
15744 if (client_ca_cert_file_path)
15746 auto ca_list = SSL_load_client_CA_file(client_ca_cert_file_path);
15747 if (ca_list !=
nullptr)
15749 SSL_CTX_set_client_CA_list(ctx_, ca_list);
15756 last_ssl_error_ =
static_cast<int>(ERR_get_error());
15760 SSL_CTX_set_verify(
15761 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
15766inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
15767 X509_STORE *client_ca_cert_store)
15769 ctx_ = SSL_CTX_new(TLS_server_method());
15773 SSL_CTX_set_options(ctx_,
15774 SSL_OP_NO_COMPRESSION |
15775 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
15777 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
15779 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
15780 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1)
15782 SSL_CTX_free(ctx_);
15785 else if (client_ca_cert_store)
15787 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
15790 auto ca_list = extract_ca_names_from_x509_store(client_ca_cert_store);
15793 SSL_CTX_set_client_CA_list(ctx_, ca_list);
15798 last_ssl_error_ =
static_cast<int>(ERR_get_error());
15801 SSL_CTX_set_verify(
15802 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
15807inline SSLServer::SSLServer(
15808 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback)
15810 ctx_ = SSL_CTX_new(TLS_method());
15813 if (!setup_ssl_ctx_callback(*ctx_))
15815 SSL_CTX_free(ctx_);
15821inline SSLServer::~SSLServer()
15825 SSL_CTX_free(ctx_);
15829inline bool SSLServer::is_valid()
const
15834inline SSL_CTX *SSLServer::ssl_context()
const
15839inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
15840 X509_STORE *client_ca_cert_store)
15843 std::lock_guard<std::mutex> guard(ctx_mutex_);
15845 SSL_CTX_use_certificate(ctx_, cert);
15846 SSL_CTX_use_PrivateKey(ctx_, private_key);
15848 if (client_ca_cert_store !=
nullptr)
15850 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
15854inline bool SSLServer::process_and_close_socket(
socket_t sock)
15856 auto ssl = detail::ssl_new(
15857 sock, ctx_, ctx_mutex_,
15860 return detail::ssl_connect_or_accept_nonblocking(
15861 sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
15872 std::string remote_addr;
15873 int remote_port = 0;
15874 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
15876 std::string local_addr;
15877 int local_port = 0;
15878 detail::get_local_ip_and_port(sock, local_addr, local_port);
15880 ret = detail::process_server_socket_ssl(
15881 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
15882 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
15883 write_timeout_usec_,
15884 [&](Stream & strm,
bool close_connection,
bool & connection_closed)
15886 return process_request(strm, remote_addr, remote_port, local_addr,
15887 local_port, close_connection,
15897 const bool shutdown_gracefully = ret;
15898 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
15901 detail::shutdown_socket(sock);
15906inline STACK_OF(X509_NAME) * SSLServer::extract_ca_names_from_x509_store(
15914 auto ca_list = sk_X509_NAME_new_null();
15921 auto objs = X509_STORE_get0_objects(store);
15924 sk_X509_NAME_free(ca_list);
15929 for (
int i = 0; i < sk_X509_OBJECT_num(objs); i++)
15931 auto obj = sk_X509_OBJECT_value(objs, i);
15932 if (X509_OBJECT_get_type(obj) == X509_LU_X509)
15934 auto cert = X509_OBJECT_get0_X509(obj);
15937 auto subject = X509_get_subject_name(cert);
15940 auto name_dup = X509_NAME_dup(subject);
15943 sk_X509_NAME_push(ca_list, name_dup);
15951 if (sk_X509_NAME_num(ca_list) == 0)
15953 sk_X509_NAME_free(ca_list);
15961inline SSLClient::SSLClient(
const std::string &host)
15964inline SSLClient::SSLClient(
const std::string &host,
int port)
15967inline SSLClient::SSLClient(
const std::string &host,
int port,
15968 const std::string &client_cert_path,
15969 const std::string &client_key_path,
15970 const std::string &private_key_password)
15971 : ClientImpl(host, port, client_cert_path, client_key_path)
15973 ctx_ = SSL_CTX_new(TLS_client_method());
15975 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
15978 [&](
const char *b,
const char *e)
15980 host_components_.emplace_back(b, e);
15983 if (!client_cert_path.empty() && !client_key_path.empty())
15985 if (!private_key_password.empty())
15987 SSL_CTX_set_default_passwd_cb_userdata(
15988 ctx_,
reinterpret_cast<void *
>(
15989 const_cast<char *
>(private_key_password.c_str())));
15992 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
15993 SSL_FILETYPE_PEM) != 1 ||
15994 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
15995 SSL_FILETYPE_PEM) != 1)
15997 last_openssl_error_ = ERR_get_error();
15998 SSL_CTX_free(ctx_);
16004inline SSLClient::SSLClient(
const std::string &host,
int port,
16005 X509 *client_cert, EVP_PKEY *client_key,
16006 const std::string &private_key_password)
16007 : ClientImpl(host, port)
16009 ctx_ = SSL_CTX_new(TLS_client_method());
16012 [&](
const char *b,
const char *e)
16014 host_components_.emplace_back(b, e);
16017 if (client_cert !=
nullptr && client_key !=
nullptr)
16019 if (!private_key_password.empty())
16021 SSL_CTX_set_default_passwd_cb_userdata(
16022 ctx_,
reinterpret_cast<void *
>(
16023 const_cast<char *
>(private_key_password.c_str())));
16026 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
16027 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1)
16029 last_openssl_error_ = ERR_get_error();
16030 SSL_CTX_free(ctx_);
16036inline SSLClient::~SSLClient()
16040 SSL_CTX_free(ctx_);
16045 shutdown_ssl_impl(socket_,
true);
16048inline bool SSLClient::is_valid()
const
16053inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store)
16059 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store)
16063 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
16064 ca_cert_store_ = ca_cert_store;
16069 X509_STORE_free(ca_cert_store);
16074inline void SSLClient::load_ca_cert_store(
const char *ca_cert,
16077 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
16080inline long SSLClient::get_openssl_verify_result()
const
16082 return verify_result_;
16085inline SSL_CTX *SSLClient::ssl_context()
const
16090inline bool SSLClient::create_and_connect_socket(Socket &socket,
Error &error)
16102inline bool SSLClient::connect_with_proxy(
16104 std::chrono::time_point<std::chrono::steady_clock> start_time,
16105 Response &res,
bool &success,
Error &error)
16108 Response proxy_res;
16109 if (!detail::process_client_socket(
16110 socket.sock, read_timeout_sec_, read_timeout_usec_,
16111 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
16112 start_time, [&](Stream & strm)
16115 req2.method =
"CONNECT";
16117 detail::make_host_and_port_string_always_port(host_, port_);
16118 if (max_timeout_msec_ > 0)
16120 req2.start_time_ = std::chrono::steady_clock::now();
16122 return process_request(strm, req2, proxy_res,
false, error);
16127 shutdown_ssl(socket,
true);
16128 shutdown_socket(socket);
16136 if (!proxy_digest_auth_username_.empty() &&
16137 !proxy_digest_auth_password_.empty())
16139 std::map<std::string, std::string> auth;
16140 if (detail::parse_www_authenticate(proxy_res, auth,
true))
16144 shutdown_ssl(socket,
true);
16145 shutdown_socket(socket);
16149 if (!ensure_socket_connection(socket, error))
16152 output_error_log(error,
nullptr);
16156 proxy_res = Response();
16157 if (!detail::process_client_socket(
16158 socket.sock, read_timeout_sec_, read_timeout_usec_,
16159 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
16160 start_time, [&](Stream & strm)
16163 req3.method =
"CONNECT";
16164 req3.path = detail::make_host_and_port_string_always_port(
16166 req3.headers.insert(detail::make_digest_authentication_header(
16167 req3, auth, 1, detail::random_string(10),
16168 proxy_digest_auth_username_, proxy_digest_auth_password_,
16170 if (max_timeout_msec_ > 0)
16172 req3.start_time_ = std::chrono::steady_clock::now();
16174 return process_request(strm, req3, proxy_res,
false, error);
16179 shutdown_ssl(socket,
true);
16180 shutdown_socket(socket);
16195 output_error_log(error,
nullptr);
16196 res = std::move(proxy_res);
16199 shutdown_ssl(socket,
true);
16200 shutdown_socket(socket);
16208inline bool SSLClient::load_certs()
16212 std::call_once(initialize_cert_, [&]()
16214 std::lock_guard<std::mutex> guard(ctx_mutex_);
16215 if (!ca_cert_file_path_.empty())
16217 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
16220 last_openssl_error_ = ERR_get_error();
16224 else if (!ca_cert_dir_path_.empty())
16226 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr,
16227 ca_cert_dir_path_.c_str()))
16229 last_openssl_error_ = ERR_get_error();
16235 auto loaded =
false;
16238 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
16239#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
16240 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
16244 SSL_CTX_set_default_verify_paths(ctx_);
16252inline bool SSLClient::initialize_ssl(Socket &socket, Error &error)
16254 auto ssl = detail::ssl_new(
16255 socket.sock, ctx_, ctx_mutex_,
16258 if (server_certificate_verification_)
16262 error = Error::SSLLoadingCerts;
16263 output_error_log(error, nullptr);
16266 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
16269 if (!detail::ssl_connect_or_accept_nonblocking(
16270 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
16271 connection_timeout_usec_, &last_ssl_error_))
16273 error = Error::SSLConnection;
16274 output_error_log(error, nullptr);
16278 if (server_certificate_verification_)
16280 auto verification_status = SSLVerifierResponse::NoDecisionMade;
16282 if (server_certificate_verifier_)
16284 verification_status = server_certificate_verifier_(ssl2);
16287 if (verification_status == SSLVerifierResponse::CertificateRejected)
16289 last_openssl_error_ = ERR_get_error();
16290 error = Error::SSLServerVerification;
16291 output_error_log(error,
nullptr);
16295 if (verification_status == SSLVerifierResponse::NoDecisionMade)
16297 verify_result_ = SSL_get_verify_result(ssl2);
16299 if (verify_result_ != X509_V_OK)
16301 last_openssl_error_ =
static_cast<unsigned long>(verify_result_);
16302 error = Error::SSLServerVerification;
16303 output_error_log(error,
nullptr);
16307 auto server_cert = SSL_get1_peer_certificate(ssl2);
16308 auto se = detail::scope_exit([&] { X509_free(server_cert); });
16310 if (server_cert ==
nullptr)
16312 last_openssl_error_ = ERR_get_error();
16313 error = Error::SSLServerVerification;
16314 output_error_log(error,
nullptr);
16318 if (server_hostname_verification_)
16320 if (!verify_host(server_cert))
16322 last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
16323 error = Error::SSLServerHostnameVerification;
16324 output_error_log(error,
nullptr);
16336 if (!detail::is_ip_address(host_))
16338#if defined(OPENSSL_IS_BORINGSSL)
16339 SSL_set_tlsext_host_name(ssl2, host_.c_str());
16343 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,
16344 TLSEXT_NAMETYPE_host_name,
16345 static_cast<void *
>(
const_cast<char *
>(host_.c_str())));
16357 if (ctx_ ==
nullptr)
16359 error = Error::SSLConnection;
16360 last_openssl_error_ = ERR_get_error();
16363 shutdown_socket(socket);
16368inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
16370 shutdown_ssl_impl(socket, shutdown_gracefully);
16373inline void SSLClient::shutdown_ssl_impl(Socket &socket,
16374 bool shutdown_gracefully)
16378 assert(socket.ssl ==
nullptr);
16383 detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
16384 shutdown_gracefully);
16385 socket.ssl =
nullptr;
16387 assert(socket.ssl ==
nullptr);
16390inline bool SSLClient::process_socket(
16391 const Socket &socket,
16392 std::chrono::time_point<std::chrono::steady_clock> start_time,
16393 std::function<
bool(Stream &strm)> callback)
16395 assert(socket.ssl);
16396 return detail::process_client_socket_ssl(
16397 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
16398 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
16399 std::move(callback));
16402inline bool SSLClient::is_ssl()
const
16407inline bool SSLClient::verify_host(X509 *server_cert)
const
16430 return verify_host_with_subject_alt_name(server_cert) ||
16431 verify_host_with_common_name(server_cert);
16435SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const
16439 auto type = GEN_DNS;
16441 struct in6_addr addr6 = {};
16442 struct in_addr addr = {};
16443 size_t addr_len = 0;
16446 if (inet_pton(AF_INET6, host_.c_str(), &addr6))
16449 addr_len =
sizeof(
struct in6_addr);
16451 else if (inet_pton(AF_INET, host_.c_str(), &addr))
16454 addr_len =
sizeof(
struct in_addr);
16458 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
16459 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
16463 auto dsn_matched =
false;
16464 auto ip_matched =
false;
16466 auto count = sk_GENERAL_NAME_num(alt_names);
16468 for (
decltype(count) i = 0; i < count && !dsn_matched; i++)
16470 auto val = sk_GENERAL_NAME_value(alt_names, i);
16471 if (!val || val->type !=
type)
16477 reinterpret_cast<const char *
>(ASN1_STRING_get0_data(val->d.ia5));
16478 if (name ==
nullptr)
16483 auto name_len =
static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
16488 dsn_matched = check_host_name(name, name_len);
16492 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len))
16500 if (dsn_matched || ip_matched)
16506 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME) *
>(
16507 reinterpret_cast<const STACK_OF(GENERAL_NAME) *
>(alt_names)));
16511inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const
16513 const auto subject_name = X509_get_subject_name(server_cert);
16515 if (subject_name !=
nullptr)
16518 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
16519 name,
sizeof(name));
16521 if (name_len != -1)
16523 return check_host_name(name,
static_cast<size_t>(name_len));
16530inline bool SSLClient::check_host_name(
const char *pattern,
16531 size_t pattern_len)
const
16533 if (host_.size() == pattern_len && host_ == pattern)
16540 std::vector<std::string> pattern_components;
16541 detail::split(&pattern[0], &pattern[pattern_len],
'.',
16542 [&](
const char *b,
const char *e)
16544 pattern_components.emplace_back(b, e);
16547 if (host_components_.size() != pattern_components.size())
16552 auto itr = pattern_components.begin();
16553 for (
const auto &h : host_components_)
16556 if (p != h && p !=
"*")
16558 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' &&
16559 !p.compare(0, p.size() - 1, h));
16560 if (!partial_match)
16573inline Client::Client(
const std::string &scheme_host_port)
16574 :
Client(scheme_host_port,
std::string(),
std::string()) {}
16577 const std::string &client_cert_path,
16578 const std::string &client_key_path)
16580 const static std::regex re(
16581 R
"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
16584 if (std::regex_match(scheme_host_port, m, re))
16586 auto scheme = m[1].str();
16588#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16589 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https"))
16592 if (!scheme.empty() && scheme !=
"http")
16595#ifndef CPPHTTPLIB_NO_EXCEPTIONS
16596 std::string msg =
"'" + scheme +
"' scheme is not supported.";
16597 throw std::invalid_argument(msg);
16602 auto is_ssl = scheme ==
"https";
16604 auto host = m[2].str();
16610 auto port_str = m[4].str();
16611 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
16615#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16616 cli_ = detail::make_unique<SSLClient>(
host,
port, client_cert_path,
16623 cli_ = detail::make_unique<ClientImpl>(
host,
port, client_cert_path,
16631 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
16632 client_cert_path, client_key_path);
16640 const std::string &client_cert_path,
16641 const std::string &client_key_path)
16642 : cli_(detail::make_unique<
ClientImpl>(host, port, client_cert_path,
16643 client_key_path)) {}
16649 return cli_ !=
nullptr && cli_->is_valid();
16654 return cli_->Get(path, std::move(progress));
16659 return cli_->Get(path, headers, std::move(progress));
16665 return cli_->Get(path, std::move(content_receiver), std::move(progress));
16671 return cli_->Get(path, headers, std::move(content_receiver),
16672 std::move(progress));
16679 return cli_->Get(path, std::move(response_handler),
16680 std::move(content_receiver), std::move(progress));
16687 return cli_->Get(path, headers, std::move(response_handler),
16688 std::move(content_receiver), std::move(progress));
16693 return cli_->Get(path, params, headers, std::move(progress));
16700 return cli_->Get(path, params, headers, std::move(content_receiver),
16701 std::move(progress));
16709 return cli_->Get(path, params, headers, std::move(response_handler),
16710 std::move(content_receiver), std::move(progress));
16715 return cli_->Head(path);
16719 return cli_->Head(path, headers);
16724 return cli_->Post(path);
16728 return cli_->Post(path, headers);
16731 size_t content_length,
16732 const std::string &content_type,
16735 return cli_->Post(path, body, content_length, content_type, progress);
16738 const char *body,
size_t content_length,
16739 const std::string &content_type,
16742 return cli_->Post(path, headers, body, content_length, content_type,
16746 const std::string &content_type,
16749 return cli_->Post(path, body, content_type, progress);
16752 const std::string &body,
16753 const std::string &content_type,
16756 return cli_->Post(path, headers, body, content_type, progress);
16760 const std::string &content_type,
16763 return cli_->Post(path, content_length, std::move(content_provider),
16764 content_type, progress);
16768 const std::string &content_type,
16772 return cli_->Post(path, content_length, std::move(content_provider),
16773 content_type, std::move(content_receiver), progress);
16777 const std::string &content_type,
16780 return cli_->Post(path, std::move(content_provider), content_type, progress);
16784 const std::string &content_type,
16788 return cli_->Post(path, std::move(content_provider), content_type,
16789 std::move(content_receiver), progress);
16792 size_t content_length,
16794 const std::string &content_type,
16797 return cli_->Post(path, headers, content_length, std::move(content_provider),
16798 content_type, progress);
16801 size_t content_length,
16803 const std::string &content_type,
16807 return cli_->Post(path, headers, content_length, std::move(content_provider),
16808 content_type, std::move(content_receiver), progress);
16812 const std::string &content_type,
16815 return cli_->Post(path, headers, std::move(content_provider), content_type,
16820 const std::string &content_type,
16824 return cli_->Post(path, headers, std::move(content_provider), content_type,
16825 std::move(content_receiver), progress);
16829 return cli_->Post(path, params);
16834 return cli_->Post(path, headers, params);
16840 return cli_->Post(path, items, progress);
16846 return cli_->Post(path, headers, items, progress);
16850 const std::string &boundary,
16853 return cli_->Post(path, headers, items, boundary, progress);
16860 return cli_->Post(path, headers, items, provider_items, progress);
16863 const std::string &body,
16864 const std::string &content_type,
16868 return cli_->Post(path, headers, body, content_type,
16869 std::move(content_receiver), progress);
16874 return cli_->Put(path);
16878 return cli_->Put(path, headers);
16881 size_t content_length,
16882 const std::string &content_type,
16885 return cli_->Put(path, body, content_length, content_type, progress);
16888 const char *body,
size_t content_length,
16889 const std::string &content_type,
16892 return cli_->Put(path, headers, body, content_length, content_type, progress);
16895 const std::string &content_type,
16898 return cli_->Put(path, body, content_type, progress);
16901 const std::string &body,
16902 const std::string &content_type,
16905 return cli_->Put(path, headers, body, content_type, progress);
16909 const std::string &content_type,
16912 return cli_->Put(path, content_length, std::move(content_provider),
16913 content_type, progress);
16917 const std::string &content_type,
16921 return cli_->Put(path, content_length, std::move(content_provider),
16922 content_type, std::move(content_receiver), progress);
16926 const std::string &content_type,
16929 return cli_->Put(path, std::move(content_provider), content_type, progress);
16933 const std::string &content_type,
16937 return cli_->Put(path, std::move(content_provider), content_type,
16938 std::move(content_receiver), progress);
16941 size_t content_length,
16943 const std::string &content_type,
16946 return cli_->Put(path, headers, content_length, std::move(content_provider),
16947 content_type, progress);
16950 size_t content_length,
16952 const std::string &content_type,
16956 return cli_->Put(path, headers, content_length, std::move(content_provider),
16957 content_type, std::move(content_receiver), progress);
16961 const std::string &content_type,
16964 return cli_->Put(path, headers, std::move(content_provider), content_type,
16969 const std::string &content_type,
16973 return cli_->Put(path, headers, std::move(content_provider), content_type,
16974 std::move(content_receiver), progress);
16978 return cli_->Put(path, params);
16983 return cli_->Put(path, headers, params);
16989 return cli_->Put(path, items, progress);
16995 return cli_->Put(path, headers, items, progress);
16999 const std::string &boundary,
17002 return cli_->Put(path, headers, items, boundary, progress);
17009 return cli_->Put(path, headers, items, provider_items, progress);
17012 const std::string &body,
17013 const std::string &content_type,
17017 return cli_->Put(path, headers, body, content_type, content_receiver,
17023 return cli_->Patch(path);
17027 return cli_->Patch(path, headers);
17030 size_t content_length,
17031 const std::string &content_type,
17034 return cli_->Patch(path, body, content_length, content_type, progress);
17037 const char *body,
size_t content_length,
17038 const std::string &content_type,
17041 return cli_->Patch(path, headers, body, content_length, content_type,
17045 const std::string &content_type,
17048 return cli_->Patch(path, body, content_type, progress);
17051 const std::string &body,
17052 const std::string &content_type,
17055 return cli_->Patch(path, headers, body, content_type, progress);
17059 const std::string &content_type,
17062 return cli_->Patch(path, content_length, std::move(content_provider),
17063 content_type, progress);
17067 const std::string &content_type,
17071 return cli_->Patch(path, content_length, std::move(content_provider),
17072 content_type, std::move(content_receiver), progress);
17076 const std::string &content_type,
17079 return cli_->Patch(path, std::move(content_provider), content_type, progress);
17083 const std::string &content_type,
17087 return cli_->Patch(path, std::move(content_provider), content_type,
17088 std::move(content_receiver), progress);
17091 size_t content_length,
17093 const std::string &content_type,
17096 return cli_->Patch(path, headers, content_length, std::move(content_provider),
17097 content_type, progress);
17100 size_t content_length,
17102 const std::string &content_type,
17106 return cli_->Patch(path, headers, content_length, std::move(content_provider),
17107 content_type, std::move(content_receiver), progress);
17111 const std::string &content_type,
17114 return cli_->Patch(path, headers, std::move(content_provider), content_type,
17119 const std::string &content_type,
17123 return cli_->Patch(path, headers, std::move(content_provider), content_type,
17124 std::move(content_receiver), progress);
17128 return cli_->Patch(path, params);
17133 return cli_->Patch(path, headers, params);
17139 return cli_->Patch(path, items, progress);
17145 return cli_->Patch(path, headers, items, progress);
17149 const std::string &boundary,
17152 return cli_->Patch(path, headers, items, boundary, progress);
17159 return cli_->Patch(path, headers, items, provider_items, progress);
17162 const std::string &body,
17163 const std::string &content_type,
17167 return cli_->Patch(path, headers, body, content_type, content_receiver,
17174 return cli_->Delete(path, progress);
17179 return cli_->Delete(path, headers, progress);
17182 size_t content_length,
17183 const std::string &content_type,
17186 return cli_->Delete(path, body, content_length, content_type, progress);
17189 const char *body,
size_t content_length,
17190 const std::string &content_type,
17193 return cli_->Delete(path, headers, body, content_length, content_type,
17197 const std::string &content_type,
17200 return cli_->Delete(path, body, content_type, progress);
17203 const std::string &body,
17204 const std::string &content_type,
17207 return cli_->Delete(path, headers, body, content_type, progress);
17212 return cli_->Delete(path, params, progress);
17217 return cli_->Delete(path, headers, params, progress);
17222 return cli_->Options(path);
17226 return cli_->Options(path, headers);
17232 const std::string &body,
const std::string &content_type)
17234 return cli_->open_stream(method, path, params, headers, body, content_type);
17239 return cli_->send(req, res, error);
17244 return cli_->send(req);
17254 return cli_->host();
17259 return cli_->port();
17264 return cli_->is_socket_open();
17269 return cli_->socket();
17275 cli_->set_hostname_addr_map(std::move(addr_map));
17280 cli_->set_default_headers(std::move(headers));
17286 cli_->set_header_writer(writer);
17291 cli_->set_address_family(family);
17296 cli_->set_tcp_nodelay(on);
17301 cli_->set_socket_options(std::move(socket_options));
17306 cli_->set_connection_timeout(sec, usec);
17311 cli_->set_read_timeout(sec, usec);
17316 cli_->set_write_timeout(sec, usec);
17320 const std::string &password)
17322 cli_->set_basic_auth(username, password);
17326 cli_->set_bearer_token_auth(token);
17328#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17329inline void Client::set_digest_auth(
const std::string &username,
17330 const std::string &password)
17332 cli_->set_digest_auth(username, password);
17338 cli_->set_keep_alive(on);
17342 cli_->set_follow_location(on);
17347 cli_->set_path_encode(on);
17350[[deprecated(
"Use set_path_encode instead")]]
17353 cli_->set_path_encode(on);
17358 cli_->set_compress(on);
17363 cli_->set_decompress(on);
17368 cli_->set_interface(intf);
17376 const std::string &password)
17378 cli_->set_proxy_basic_auth(username, password);
17382 cli_->set_proxy_bearer_token_auth(token);
17384#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17385inline void Client::set_proxy_digest_auth(
const std::string &username,
17386 const std::string &password)
17388 cli_->set_proxy_digest_auth(username, password);
17392#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17393inline void Client::enable_server_certificate_verification(
bool enabled)
17395 cli_->enable_server_certificate_verification(enabled);
17398inline void Client::enable_server_hostname_verification(
bool enabled)
17400 cli_->enable_server_hostname_verification(enabled);
17403inline void Client::set_server_certificate_verifier(
17406 cli_->set_server_certificate_verifier(verifier);
17412 cli_->set_logger(std::move(logger));
17417 cli_->set_error_logger(std::move(error_logger));
17420#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17421inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
17422 const std::string &ca_cert_dir_path)
17424 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
17427inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store)
17431 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
17435 cli_->set_ca_cert_store(ca_cert_store);
17439inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t size)
17441 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
17444inline long Client::get_openssl_verify_result()
const
17448 return static_cast<SSLClient &
>(*cli_).get_openssl_verify_result();
17453inline SSL_CTX *Client::ssl_context()
const
17457 return static_cast<SSLClient &
>(*cli_).ssl_context();