8#ifndef CPPHTTPLIB_HTTPLIB_H
9#define CPPHTTPLIB_HTTPLIB_H
11#define CPPHTTPLIB_VERSION "0.18.1"
17#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
18#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
21#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
22#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
25#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
26#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
29#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
30#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
33#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
34#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
37#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
38#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
41#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
42#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
45#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
46#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
49#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
50#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
53#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
54#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
57#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
58#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
61#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
62#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
65#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
66#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
69#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
70#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
73#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
75#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
77#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
81#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
82#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
85#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
86#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
89#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
90#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
93#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
94#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
97#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
98#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
101#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
102#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
105#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
106#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
109#ifndef CPPHTTPLIB_TCP_NODELAY
110#define CPPHTTPLIB_TCP_NODELAY false
113#ifndef CPPHTTPLIB_IPV6_V6ONLY
114#define CPPHTTPLIB_IPV6_V6ONLY false
117#ifndef CPPHTTPLIB_RECV_BUFSIZ
118#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
121#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
122#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
125#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
126#define CPPHTTPLIB_THREAD_POOL_COUNT \
127 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
128 ? std::thread::hardware_concurrency() - 1 \
132#ifndef CPPHTTPLIB_RECV_FLAGS
133#define CPPHTTPLIB_RECV_FLAGS 0
136#ifndef CPPHTTPLIB_SEND_FLAGS
137#define CPPHTTPLIB_SEND_FLAGS 0
140#ifndef CPPHTTPLIB_LISTEN_BACKLOG
141#define CPPHTTPLIB_LISTEN_BACKLOG 5
144#define CPPHTTPLIB_NO_EXCEPTION
151#ifndef _CRT_SECURE_NO_WARNINGS
152#define _CRT_SECURE_NO_WARNINGS
155#ifndef _CRT_NONSTDC_NO_DEPRECATE
156#define _CRT_NONSTDC_NO_DEPRECATE
161#error Sorry, Visual Studio versions prior to 2015 are not supported
164#pragma comment(lib, "ws2_32.lib")
174#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
178#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
189#ifndef WSA_FLAG_NO_HANDLE_INHERIT
190#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
194#ifdef CPPHTTPLIB_USE_POLL
195#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
200#include <arpa/inet.h>
201#if !defined(_AIX) && !defined(__MVS__)
207#define NI_MAXHOST 1025
212#include <netinet/in.h>
216#include <netinet/tcp.h>
217#ifdef CPPHTTPLIB_USE_POLL
223#include <sys/select.h>
224#include <sys/socket.h>
229#ifndef INVALID_SOCKET
230#define INVALID_SOCKET (-1)
240#include <condition_variable>
260#include <unordered_map>
261#include <unordered_set>
264#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
272#undef X509_EXTENSIONS
273#undef PKCS7_SIGNER_INFO
276#pragma comment(lib, "crypt32.lib")
278#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
279#include <TargetConditionals.h>
281#include <CoreFoundation/CoreFoundation.h>
282#include <Security/Security.h>
286#include <openssl/err.h>
287#include <openssl/evp.h>
288#include <openssl/ssl.h>
289#include <openssl/x509v3.h>
291#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
292#include <openssl/applink.c>
298#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
299#if OPENSSL_VERSION_NUMBER < 0x1010107f
300#error Please use OpenSSL or a current version of BoringSSL
302#define SSL_get1_peer_certificate SSL_get_peer_certificate
303#elif OPENSSL_VERSION_NUMBER < 0x30000000L
304#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
309#ifdef CPPHTTPLIB_ZLIB_SUPPORT
313#ifdef CPPHTTPLIB_BROTLI_SUPPORT
314#include <brotli/decode.h>
315#include <brotli/encode.h>
333template <
class T,
class... Args>
334typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
336 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
340typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
342 typedef typename std::remove_extent<T>::type RT;
343 return std::unique_ptr<T>(
new RT[n]);
346namespace case_ignore {
349 const static unsigned char table[256] = {
350 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
351 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
352 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
353 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
354 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
355 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
356 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
357 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
358 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
359 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
360 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
361 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
362 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
363 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
364 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
365 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
366 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
369 return table[(
unsigned char)(
char)c];
372inline bool equal(
const std::string &a,
const std::string &b) {
373 return a.size() == b.size() &&
374 std::equal(a.begin(), a.end(), b.begin(), [](
char ca,
char cb) {
375 return to_lower(ca) == to_lower(cb);
380 bool operator()(
const std::string &a,
const std::string &b)
const {
387 return hash_core(key.data(), key.size(), 0);
390 size_t hash_core(
const char *s,
size_t l,
size_t h)
const {
395 (((std::numeric_limits<size_t>::max)() >> 6) &
397 static_cast<unsigned char>(
to_lower(*s)));
408 : exit_function(
std::move(f)), execute_on_destruction{true} {}
411 : exit_function(std::move(rhs.exit_function)),
412 execute_on_destruction{rhs.execute_on_destruction} {
417 if (execute_on_destruction) { this->exit_function(); }
420 void release() { this->execute_on_destruction =
false; }
427 std::function<void(
void)> exit_function;
428 bool execute_on_destruction;
512using Params = std::multimap<std::string, std::string>;
538 std::function<bool(
const char *data,
size_t data_len)>
write;
545 class data_sink_streambuf final :
public std::streambuf {
547 explicit data_sink_streambuf(
DataSink &sink) : sink_(sink) {}
550 std::streamsize xsputn(
const char *s, std::streamsize n)
override {
551 sink_.write(s,
static_cast<size_t>(n));
559 data_sink_streambuf sb_;
563 std::function<bool(
size_t offset,
size_t length,
DataSink &sink)>;
566 std::function<bool(
size_t offset,
DataSink &sink)>;
579 std::function<bool(
const char *data,
size_t data_length,
uint64_t offset,
583 std::function<bool(
const char *data,
size_t data_length)>;
604 return reader_(std::move(receiver));
611using Range = std::pair<ssize_t, ssize_t>;
638#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
639 const SSL *ssl =
nullptr;
642 bool has_header(
const std::string &key)
const;
644 size_t id = 0)
const;
646 size_t id = 0)
const;
648 void set_header(
const std::string &key,
const std::string &val);
650 bool has_param(
const std::string &key)
const;
651 std::string
get_param_value(
const std::string &key,
size_t id = 0)
const;
656 bool has_file(
const std::string &key)
const;
658 std::vector<MultipartFormData>
get_file_values(
const std::string &key)
const;
676 bool has_header(
const std::string &key)
const;
678 size_t id = 0)
const;
680 size_t id = 0)
const;
682 void set_header(
const std::string &key,
const std::string &val);
685 void set_content(
const char *s,
size_t n,
const std::string &content_type);
686 void set_content(
const std::string &s,
const std::string &content_type);
687 void set_content(std::string &&s,
const std::string &content_type);
690 size_t length,
const std::string &content_type,
ContentProvider provider,
702 const std::string &content_type);
748 virtual bool enqueue(std::function<
void()> fn) = 0;
757 : shutdown_(false), max_queued_requests_(mqr) {
759 threads_.emplace_back(
worker(*
this));
767 bool enqueue(std::function<
void()> fn)
override {
769 std::unique_lock<std::mutex> lock(mutex_);
770 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
773 jobs_.push_back(std::move(fn));
783 std::unique_lock<std::mutex> lock(mutex_);
790 for (
auto &t : threads_) {
797 explicit worker(
ThreadPool &pool) : pool_(pool) {}
801 std::function<void()> fn;
803 std::unique_lock<std::mutex> lock(pool_.mutex_);
806 lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
808 if (pool_.shutdown_ && pool_.jobs_.empty()) {
break; }
810 fn = pool_.jobs_.front();
811 pool_.jobs_.pop_front();
814 assert(
true ==
static_cast<bool>(fn));
818#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
819 !defined(LIBRESSL_VERSION_NUMBER)
820 OPENSSL_thread_stop();
828 std::vector<std::thread> threads_;
829 std::list<std::function<void()>> jobs_;
832 size_t max_queued_requests_ = 0;
834 std::condition_variable cond_;
886 static constexpr char separator =
'/';
891 std::vector<std::string> static_fragments_;
894 std::vector<std::string> param_names_;
924 std::function<void(
const Request &,
Response &, std::exception_ptr ep)>;
957 const std::string &mount_point = std::string());
958 bool set_mount_point(
const std::string &mount_point,
const std::string &dir,
962 const std::string &mime);
966 template <
class ErrorHandlerFunc>
968 return set_error_handler_core(
969 std::forward<ErrorHandlerFunc>(handler),
970 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
993 template <
class Rep,
class Period>
997 template <
class Rep,
class Period>
1001 template <
class Rep,
class Period>
1006 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
1010 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
1012 bool start_server(
const std::string& host,
int port,
int socket_flags = 0);
1023 int remote_port,
const std::string &local_addr,
1024 int local_port,
bool close_connection,
1025 bool &connection_closed,
1026 const std::function<
void(
Request &)> &setup_request);
1041 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
Handler>>;
1042 using HandlersForContentReader =
1043 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1046 static std::unique_ptr<detail::MatcherBase>
1047 make_matcher(
const std::string &pattern);
1050 Server &set_error_handler_core(
Handler handler, std::false_type);
1052 socket_t create_server_socket(
const std::string &host,
int port,
1055 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1056 bool listen_internal();
1058 bool run_cycle_once();
1065 const Handlers &handlers)
const;
1066 bool dispatch_request_for_content_reader(
1068 const HandlersForContentReader &handlers)
const;
1070 bool parse_request_line(
const char *s,
Request &req)
const;
1072 std::string &content_type, std::string &boundary)
const;
1073 bool write_response(
Stream &strm,
bool close_connection,
Request &req,
1075 bool write_response_with_content(
Stream &strm,
bool close_connection,
1077 bool write_response_core(
Stream &strm,
bool close_connection,
1079 bool need_apply_ranges);
1080 bool write_content_with_provider(
Stream &strm,
const Request &req,
1081 Response &res,
const std::string &boundary,
1082 const std::string &content_type);
1094 virtual bool process_and_close_socket(
socket_t sock);
1096 std::atomic<bool> is_running_{
false};
1097 std::atomic<bool> is_decommisioned{
false};
1099 struct MountPointEntry {
1100 std::string mount_point;
1101 std::string base_dir;
1104 std::vector<MountPointEntry> base_dirs_;
1105 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1106 std::string default_file_mimetype_ =
"application/octet-stream";
1107 Handler file_request_handler_;
1109 Handlers get_handlers_;
1110 Handlers post_handlers_;
1111 HandlersForContentReader post_handlers_for_content_reader_;
1112 Handlers put_handlers_;
1113 HandlersForContentReader put_handlers_for_content_reader_;
1114 Handlers patch_handlers_;
1115 HandlersForContentReader patch_handlers_for_content_reader_;
1116 Handlers delete_handlers_;
1117 HandlersForContentReader delete_handlers_for_content_reader_;
1118 Handlers options_handlers_;
1123 Handler post_routing_handler_;
1128 int address_family_ = AF_UNSPEC;
1169 : res_(std::move(res)), err_(err),
1170 request_headers_(std::move(request_headers)) {}
1172 operator bool()
const {
return res_ !=
nullptr; }
1173 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
1174 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1188 const char *def =
"",
1189 size_t id = 0)
const;
1191 uint64_t def = 0,
size_t id = 0)
const;
1195 std::unique_ptr<Response> res_;
1207 const std::string &client_cert_path,
1208 const std::string &client_key_path);
1251 Result Post(
const std::string &path,
const char *
body,
size_t content_length,
1252 const std::string &content_type);
1254 size_t content_length,
const std::string &content_type);
1256 size_t content_length,
const std::string &content_type,
1259 const std::string &content_type);
1261 const std::string &content_type,
Progress progress);
1263 const std::string &
body,
const std::string &content_type);
1265 const std::string &
body,
const std::string &content_type,
1267 Result Post(
const std::string &path,
size_t content_length,
1269 const std::string &content_type);
1272 const std::string &content_type);
1275 const std::string &content_type);
1278 const std::string &content_type);
1294 Result Put(
const std::string &path,
const char *
body,
size_t content_length,
1295 const std::string &content_type);
1297 size_t content_length,
const std::string &content_type);
1299 size_t content_length,
const std::string &content_type,
1301 Result Put(
const std::string &path,
const std::string &
body,
1302 const std::string &content_type);
1303 Result Put(
const std::string &path,
const std::string &
body,
1304 const std::string &content_type,
Progress progress);
1306 const std::string &
body,
const std::string &content_type);
1308 const std::string &
body,
const std::string &content_type,
1310 Result Put(
const std::string &path,
size_t content_length,
1314 const std::string &content_type);
1317 const std::string &content_type);
1320 const std::string &content_type);
1336 Result Patch(
const std::string &path,
const char *
body,
size_t content_length,
1337 const std::string &content_type);
1338 Result Patch(
const std::string &path,
const char *
body,
size_t content_length,
1339 const std::string &content_type,
Progress progress);
1341 const char *
body,
size_t content_length,
1342 const std::string &content_type);
1344 const char *
body,
size_t content_length,
1345 const std::string &content_type,
Progress progress);
1347 const std::string &content_type);
1349 const std::string &content_type,
Progress progress);
1351 const std::string &
body,
const std::string &content_type);
1353 const std::string &
body,
const std::string &content_type,
1355 Result Patch(
const std::string &path,
size_t content_length,
1357 const std::string &content_type);
1360 const std::string &content_type);
1363 const std::string &content_type);
1366 const std::string &content_type);
1371 size_t content_length,
const std::string &content_type);
1373 size_t content_length,
const std::string &content_type,
1376 const char *
body,
size_t content_length,
1377 const std::string &content_type);
1379 const char *
body,
size_t content_length,
1380 const std::string &content_type,
Progress progress);
1382 const std::string &content_type);
1384 const std::string &content_type,
Progress progress);
1386 const std::string &
body,
const std::string &content_type);
1388 const std::string &
body,
const std::string &content_type,
1399 std::string
host()
const;
1418 template <
class Rep,
class Period>
1423 template <
class Rep,
class Period>
1427 template <
class Rep,
class Period>
1430 void set_basic_auth(
const std::string &username,
const std::string &password);
1432#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1433 void set_digest_auth(
const std::string &username,
1434 const std::string &password);
1450 const std::string &password);
1452#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1453 void set_proxy_digest_auth(
const std::string &username,
1454 const std::string &password);
1457#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1458 void set_ca_cert_path(
const std::string &ca_cert_file_path,
1459 const std::string &ca_cert_dir_path = std::string());
1460 void set_ca_cert_store(X509_STORE *ca_cert_store);
1461 X509_STORE *create_ca_cert_store(
const char *ca_cert, std::size_t size)
const;
1464#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1465 void enable_server_certificate_verification(
bool enabled);
1466 void enable_server_hostname_verification(
bool enabled);
1467 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1475#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1496 bool close_connection,
Error &error);
1499 Error &error)
const;
1542#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1543 std::string digest_auth_username_;
1544 std::string digest_auth_password_;
1568#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1569 std::string proxy_digest_auth_username_;
1570 std::string proxy_digest_auth_password_;
1573#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1574 std::string ca_cert_file_path_;
1575 std::string ca_cert_dir_path_;
1577 X509_STORE *ca_cert_store_ =
nullptr;
1580#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1581 bool server_certificate_verification_ =
true;
1582 bool server_hostname_verification_ =
true;
1583 std::function<bool(SSL *ssl)> server_certificate_verifier_;
1592#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1593 bool is_ssl_peer_could_be_closed(SSL *ssl)
const;
1598 bool write_request(
Stream &strm,
Request &req,
bool close_connection,
1602 bool close_connection,
Error &error);
1603 std::unique_ptr<Response> send_with_content_provider(
1604 Request &req,
const char *
body,
size_t content_length,
1607 const std::string &content_type,
Error &error);
1608 Result send_with_content_provider(
1609 const std::string &method,
const std::string &path,
1610 const Headers &headers,
const char *
body,
size_t content_length,
1613 const std::string &content_type,
Progress progress);
1618 std::string adjust_host_string(
const std::string &
host)
const;
1621 std::function<
bool(
Stream &strm)> callback);
1622 virtual bool is_ssl()
const;
1628 explicit Client(
const std::string &scheme_host_port);
1630 explicit Client(
const std::string &scheme_host_port,
1631 const std::string &client_cert_path,
1632 const std::string &client_key_path);
1638 const std::string &client_cert_path,
1639 const std::string &client_key_path);
1685 Result Post(
const std::string &path,
const char *
body,
size_t content_length,
1686 const std::string &content_type);
1688 size_t content_length,
const std::string &content_type);
1690 size_t content_length,
const std::string &content_type,
1693 const std::string &content_type);
1695 const std::string &content_type,
Progress progress);
1697 const std::string &
body,
const std::string &content_type);
1699 const std::string &
body,
const std::string &content_type,
1701 Result Post(
const std::string &path,
size_t content_length,
1703 const std::string &content_type);
1706 const std::string &content_type);
1709 const std::string &content_type);
1712 const std::string &content_type);
1728 Result Put(
const std::string &path,
const char *
body,
size_t content_length,
1729 const std::string &content_type);
1731 size_t content_length,
const std::string &content_type);
1733 size_t content_length,
const std::string &content_type,
1735 Result Put(
const std::string &path,
const std::string &
body,
1736 const std::string &content_type);
1737 Result Put(
const std::string &path,
const std::string &
body,
1738 const std::string &content_type,
Progress progress);
1740 const std::string &
body,
const std::string &content_type);
1742 const std::string &
body,
const std::string &content_type,
1744 Result Put(
const std::string &path,
size_t content_length,
1748 const std::string &content_type);
1751 const std::string &content_type);
1754 const std::string &content_type);
1770 Result Patch(
const std::string &path,
const char *
body,
size_t content_length,
1771 const std::string &content_type);
1772 Result Patch(
const std::string &path,
const char *
body,
size_t content_length,
1773 const std::string &content_type,
Progress progress);
1775 const char *
body,
size_t content_length,
1776 const std::string &content_type);
1778 const char *
body,
size_t content_length,
1779 const std::string &content_type,
Progress progress);
1781 const std::string &content_type);
1783 const std::string &content_type,
Progress progress);
1785 const std::string &
body,
const std::string &content_type);
1787 const std::string &
body,
const std::string &content_type,
1789 Result Patch(
const std::string &path,
size_t content_length,
1791 const std::string &content_type);
1794 const std::string &content_type);
1797 const std::string &content_type);
1800 const std::string &content_type);
1805 size_t content_length,
const std::string &content_type);
1807 size_t content_length,
const std::string &content_type,
1810 const char *
body,
size_t content_length,
1811 const std::string &content_type);
1813 const char *
body,
size_t content_length,
1814 const std::string &content_type,
Progress progress);
1816 const std::string &content_type);
1818 const std::string &content_type,
Progress progress);
1820 const std::string &
body,
const std::string &content_type);
1822 const std::string &
body,
const std::string &content_type,
1833 std::string
host()
const;
1851 template <
class Rep,
class Period>
1856 template <
class Rep,
class Period>
1860 template <
class Rep,
class Period>
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 enable_server_certificate_verification(
bool enabled);
1892 void enable_server_hostname_verification(
bool enabled);
1893 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1899#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1900 void set_ca_cert_path(
const std::string &ca_cert_file_path,
1901 const std::string &ca_cert_dir_path = std::string());
1903 void set_ca_cert_store(X509_STORE *ca_cert_store);
1904 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1906 long get_openssl_verify_result()
const;
1908 SSL_CTX *ssl_context()
const;
1912 std::unique_ptr<ClientImpl> cli_;
1914#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1915 bool is_ssl_ =
false;
1919#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1920class SSLServer :
public Server {
1922 SSLServer(
const char *cert_path,
const char *private_key_path,
1923 const char *client_ca_cert_file_path =
nullptr,
1924 const char *client_ca_cert_dir_path =
nullptr,
1925 const char *private_key_password =
nullptr);
1927 SSLServer(X509 *cert, EVP_PKEY *private_key,
1928 X509_STORE *client_ca_cert_store =
nullptr);
1931 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1933 ~SSLServer()
override;
1935 bool is_valid()
const override;
1937 SSL_CTX *ssl_context()
const;
1939 void update_certs(X509 *cert, EVP_PKEY *private_key,
1940 X509_STORE *client_ca_cert_store =
nullptr);
1943 bool process_and_close_socket(
socket_t sock)
override;
1946 std::mutex ctx_mutex_;
1949class SSLClient final :
public ClientImpl {
1951 explicit SSLClient(
const std::string &host);
1953 explicit SSLClient(
const std::string &host,
int port);
1955 explicit SSLClient(
const std::string &host,
int port,
1956 const std::string &client_cert_path,
1957 const std::string &client_key_path,
1958 const std::string &private_key_password = std::string());
1960 explicit SSLClient(
const std::string &host,
int port, X509 *client_cert,
1961 EVP_PKEY *client_key,
1962 const std::string &private_key_password = std::string());
1964 ~SSLClient()
override;
1966 bool is_valid()
const override;
1968 void set_ca_cert_store(X509_STORE *ca_cert_store);
1969 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1971 long get_openssl_verify_result()
const;
1973 SSL_CTX *ssl_context()
const;
1976 bool create_and_connect_socket(Socket &socket, Error &error)
override;
1977 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
1978 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
1980 bool process_socket(
const Socket &socket,
1981 std::function<
bool(Stream &strm)> callback)
override;
1982 bool is_ssl()
const override;
1984 bool connect_with_proxy(Socket &sock, Response &res,
bool &success,
1986 bool initialize_ssl(Socket &socket, Error &error);
1990 bool verify_host(X509 *server_cert)
const;
1991 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
1992 bool verify_host_with_common_name(X509 *server_cert)
const;
1993 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
1996 std::mutex ctx_mutex_;
1997 std::once_flag initialize_cert_;
1999 std::vector<std::string> host_components_;
2001 long verify_result_ = 0;
2003 friend class ClientImpl;
2013template <
typename T,
typename U>
2015 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2016 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2017 duration - std::chrono::seconds(sec))
2019 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
2023 const std::string &key,
uint64_t def,
2025 auto rng = headers.equal_range(key);
2026 auto it =
rng.first;
2027 std::advance(it,
static_cast<ssize_t>(
id));
2028 if (it !=
rng.second) {
2029 return std::strtoull(it->second.data(),
nullptr, 10);
2049 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2050 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
2051 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
2052 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
2055 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
2056 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
2058 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2059 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
2074 return "Non-Authoritative Information";
2098 return "Proxy Authentication Required";
2119 return "Request Header Fields Too Large";
2121 return "Unavailable For Legal Reasons";
2127 return "HTTP Version Not Supported";
2133 return "Network Authentication Required";
2141 if (req.has_header(
"Authorization")) {
2142 static std::string BearerHeaderPrefix =
"Bearer ";
2143 return req.get_header_value(
"Authorization")
2144 .substr(BearerHeaderPrefix.length());
2149template <
class Rep,
class Period>
2157template <
class Rep,
class Period>
2165template <
class Rep,
class Period>
2178 case Error::Read:
return "Failed to read connection";
2179 case Error::Write:
return "Failed to write connection";
2186 return "SSL server hostname verification failed";
2188 return "Unsupported HTTP multipart boundary characters";
2201 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
2211template <
class Rep,
class Period>
2213 const std::chrono::duration<Rep, Period> &duration) {
2219template <
class Rep,
class Period>
2221 const std::chrono::duration<Rep, Period> &duration) {
2226template <
class Rep,
class Period>
2228 const std::chrono::duration<Rep, Period> &duration) {
2233template <
class Rep,
class Period>
2235 const std::chrono::duration<Rep, Period> &duration) {
2236 cli_->set_connection_timeout(duration);
2239template <
class Rep,
class Period>
2242 cli_->set_read_timeout(duration);
2245template <
class Rep,
class Period>
2248 cli_->set_write_timeout(duration);
2256std::string
hosted_at(
const std::string &hostname);
2258void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
2264std::pair<std::string, std::string>
2266 const std::string &password,
2267 bool is_proxy =
false);
2272inline std::wstring u8string_to_wstring(
const char *s) {
2274 auto len =
static_cast<int>(strlen(s));
2275 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len,
nullptr, 0);
2278 wlen = ::MultiByteToWideChar(
2280 const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
2281 if (
size_t(wlen) != ws.size()) { ws.clear(); }
2303std::string
decode_url(
const std::string &s,
bool convert_plus_to_space);
2305void read_file(
const std::string &path, std::string &out);
2307std::string
trim_copy(
const std::string &s);
2310 const char *data, std::size_t size,
char d,
2311 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2315 const std::string &str,
char d,
2316 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2319void split(
const char *b,
const char *e,
char d,
2320 std::function<
void(
const char *,
const char *)> fn);
2322void split(
const char *b,
const char *e,
char d,
size_t m,
2323 std::function<
void(
const char *,
const char *)> fn);
2326 time_t read_timeout_usec, time_t write_timeout_sec,
2327 time_t write_timeout_usec,
2328 std::function<
bool(
Stream &)> callback);
2331 int port,
int address_family,
bool tcp_nodelay,
2333 time_t connection_timeout_sec,
2334 time_t connection_timeout_usec,
2335 time_t read_timeout_sec, time_t read_timeout_usec,
2336 time_t write_timeout_sec,
2337 time_t write_timeout_usec,
2338 const std::string &intf,
Error &error);
2341 const char *def,
size_t id);
2350 std::string &boundary);
2381 size_t position = 0;
2388 typedef std::function<bool(
const char *data,
size_t data_len)>
Callback;
2389 virtual bool compress(
const char *data,
size_t data_length,
bool last,
2399 typedef std::function<bool(
const char *data,
size_t data_len)>
Callback;
2408 bool compress(
const char *data,
size_t data_length,
bool ,
2412#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2413class gzip_compressor final :
public compressor {
2416 ~gzip_compressor()
override;
2418 bool compress(
const char *data,
size_t data_length,
bool last,
2419 Callback callback)
override;
2422 bool is_valid_ =
false;
2426class gzip_decompressor final :
public decompressor {
2428 gzip_decompressor();
2429 ~gzip_decompressor()
override;
2431 bool is_valid()
const override;
2433 bool decompress(
const char *data,
size_t data_length,
2434 Callback callback)
override;
2437 bool is_valid_ =
false;
2442#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2443class brotli_compressor final :
public compressor {
2445 brotli_compressor();
2446 ~brotli_compressor();
2448 bool compress(
const char *data,
size_t data_length,
bool last,
2449 Callback callback)
override;
2452 BrotliEncoderState *state_ =
nullptr;
2455class brotli_decompressor final :
public decompressor {
2457 brotli_decompressor();
2458 ~brotli_decompressor();
2460 bool is_valid()
const override;
2462 bool decompress(
const char *data,
size_t data_length,
2463 Callback callback)
override;
2466 BrotliDecoderResult decoder_r;
2467 BrotliDecoderState *decoder_s =
nullptr;
2476 size_t fixed_buffer_size);
2477 const char *
ptr()
const;
2478 size_t size()
const;
2483 void append(
char c);
2486 char *fixed_buffer_;
2487 const size_t fixed_buffer_size_;
2488 size_t fixed_buffer_used_size_ = 0;
2489 std::string glowable_buffer_;
2494 mmap(
const char *path);
2497 bool open(
const char *path);
2501 size_t size()
const;
2502 const char *
data()
const;
2506 HANDLE hFile_ = NULL;
2507 HANDLE hMapping_ = NULL;
2512 void *addr_ =
nullptr;
2513 bool is_open_empty_file =
false;
2527 if (0x20 <= c && isdigit(c)) {
2530 }
else if (
'A' <= c && c <=
'F') {
2533 }
else if (
'a' <= c && c <=
'f') {
2542 if (i >= s.size()) {
return false; }
2545 for (; cnt; i++, cnt--) {
2546 if (!s[i]) {
return false; }
2558 static const auto charset =
"0123456789abcdef";
2561 ret = charset[n & 15] + ret;
2568 if (code < 0x0080) {
2569 buff[0] =
static_cast<char>(code & 0x7F);
2571 }
else if (code < 0x0800) {
2572 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2573 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
2575 }
else if (code < 0xD800) {
2576 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2577 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2578 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2580 }
else if (code < 0xE000) {
2582 }
else if (code < 0x10000) {
2583 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2584 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2585 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2587 }
else if (code < 0x110000) {
2588 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2589 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2590 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2591 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
2602 static const auto lookup =
2603 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2606 out.reserve(in.size());
2612 val = (val << 8) + static_cast<uint8_t>(c);
2615 out.push_back(lookup[(val >> valb) & 0x3F]);
2620 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
2622 while (out.size() % 4) {
2634 while (i < path.size() && path[i] ==
'/') {
2638 while (i < path.size()) {
2641 while (i < path.size() && path[i] !=
'/') {
2642 if (path[i] ==
'\0') {
2644 }
else if (path[i] ==
'\\') {
2653 if (!path.compare(beg, len,
".")) {
2655 }
else if (!path.compare(beg, len,
"..")) {
2656 if (level == 0) {
return false; }
2663 while (i < path.size() && path[i] ==
'/') {
2673 auto wpath = u8string_to_wstring(path.c_str());
2674 ret_ = _wstat(wpath.c_str(), &st_);
2676 ret_ = stat(path.c_str(), &st_);
2680 return ret_ >= 0 && S_ISREG(st_.st_mode);
2683 return ret_ >= 0 && S_ISDIR(st_.st_mode);
2687 std::ostringstream escaped;
2689 escaped << std::hex;
2691 for (
auto c : value) {
2692 if (std::isalnum(
static_cast<uint8_t>(c)) || c ==
'-' || c ==
'_' ||
2693 c ==
'.' || c ==
'!' || c ==
'~' || c ==
'*' || c ==
'\'' || c ==
'(' ||
2697 escaped << std::uppercase;
2698 escaped <<
'%' << std::setw(2)
2699 <<
static_cast<int>(
static_cast<unsigned char>(c));
2700 escaped << std::nouppercase;
2704 return escaped.str();
2709 result.reserve(s.size());
2711 for (
size_t i = 0; s[i]; i++) {
2713 case ' ': result +=
"%20";
break;
2714 case '+': result +=
"%2B";
break;
2715 case '\r': result +=
"%0D";
break;
2716 case '\n': result +=
"%0A";
break;
2717 case '\'': result +=
"%27";
break;
2718 case ',': result +=
"%2C";
break;
2720 case ';': result +=
"%3B";
break;
2722 auto c =
static_cast<uint8_t>(s[i]);
2726 auto len =
snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
2728 result.append(hex,
static_cast<size_t>(len));
2740 bool convert_plus_to_space) {
2743 for (
size_t i = 0; i < s.size(); i++) {
2744 if (s[i] ==
'%' && i + 1 < s.size()) {
2745 if (s[i + 1] ==
'u') {
2750 size_t len =
to_utf8(val, buff);
2751 if (len > 0) { result.append(buff, len); }
2760 result +=
static_cast<char>(val);
2766 }
else if (convert_plus_to_space && s[i] ==
'+') {
2776inline void read_file(
const std::string &path, std::string &out) {
2777 std::ifstream fs(path, std::ios_base::binary);
2778 fs.seekg(0, std::ios_base::end);
2779 auto size = fs.tellg();
2781 out.resize(
static_cast<size_t>(size));
2782 fs.read(&out[0],
static_cast<std::streamsize
>(size));
2787 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2788 if (std::regex_search(path, m, re)) {
return m[1].str(); }
2789 return std::string();
2794inline std::pair<size_t, size_t>
trim(
const char *b,
const char *e,
size_t left,
2802 return std::make_pair(left, right);
2806 auto r =
trim(s.data(), s.data() + s.size(), 0, s.size());
2807 return s.substr(r.first, r.second - r.first);
2811 if (s.length() >= 2 && s.front() ==
'"' && s.back() ==
'"') {
2812 return s.substr(1, s.size() - 2);
2818divide(
const char *data, std::size_t size,
char d,
2819 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2821 const auto it = std::find(data, data + size, d);
2822 const auto found =
static_cast<std::size_t
>(it != data + size);
2823 const auto lhs_data = data;
2824 const auto lhs_size =
static_cast<std::size_t
>(it - data);
2825 const auto rhs_data = it + found;
2826 const auto rhs_size = size - lhs_size - found;
2828 fn(lhs_data, lhs_size, rhs_data, rhs_size);
2833 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2835 divide(str.data(), str.size(), d, std::move(fn));
2838inline void split(
const char *b,
const char *e,
char d,
2839 std::function<
void(
const char *,
const char *)> fn) {
2840 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
2843inline void split(
const char *b,
const char *e,
char d,
size_t m,
2844 std::function<
void(
const char *,
const char *)> fn) {
2849 while (e ? (b + i < e) : (b[i] !=
'\0')) {
2850 if (b[i] == d && count < m) {
2851 auto r =
trim(b, e, beg, i);
2852 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2860 auto r =
trim(b, e, beg, i);
2861 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2866 size_t fixed_buffer_size)
2867 : strm_(strm), fixed_buffer_(fixed_buffer),
2868 fixed_buffer_size_(fixed_buffer_size) {}
2871 if (glowable_buffer_.empty()) {
2872 return fixed_buffer_;
2874 return glowable_buffer_.data();
2879 if (glowable_buffer_.empty()) {
2880 return fixed_buffer_used_size_;
2882 return glowable_buffer_.size();
2888 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
2892 fixed_buffer_used_size_ = 0;
2893 glowable_buffer_.clear();
2895#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2899 for (
size_t i = 0;; i++) {
2901 auto n = strm_.
read(&
byte, 1);
2905 }
else if (n == 0) {
2915#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2916 if (
byte ==
'\n') {
break; }
2918 if (prev_byte ==
'\r' &&
byte ==
'\n') {
break; }
2926inline void stream_line_reader::append(
char c) {
2927 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2928 fixed_buffer_[fixed_buffer_used_size_++] = c;
2929 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
2931 if (glowable_buffer_.empty()) {
2932 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
2933 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2935 glowable_buffer_ += c;
2947 auto wpath = u8string_to_wstring(path);
2948 if (wpath.empty()) {
return false; }
2950#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2951 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
2952 OPEN_EXISTING, NULL);
2954 hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
2955 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2958 if (hFile_ == INVALID_HANDLE_VALUE) {
return false; }
2960 LARGE_INTEGER
size{};
2961 if (!::GetFileSizeEx(hFile_, &
size)) {
return false; }
2965 size_ =
static_cast<size_t>(
size.QuadPart);
2967#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2969 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
2971 hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
2975 if (hMapping_ == NULL && size_ == 0) {
2977 is_open_empty_file =
true;
2981 if (hMapping_ == NULL) {
2986#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2987 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
2989 addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
2992 if (addr_ ==
nullptr) {
2997 fd_ =
::open(path, O_RDONLY);
2998 if (fd_ == -1) {
return false; }
3001 if (fstat(fd_, &sb) == -1) {
3005 size_ =
static_cast<size_t>(sb.st_size);
3007 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
3010 if (addr_ == MAP_FAILED && size_ == 0) {
3012 is_open_empty_file =
true;
3021 return is_open_empty_file ? true : addr_ !=
nullptr;
3027 return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_);
3033 ::UnmapViewOfFile(addr_);
3038 ::CloseHandle(hMapping_);
3042 if (hFile_ != INVALID_HANDLE_VALUE) {
3043 ::CloseHandle(hFile_);
3044 hFile_ = INVALID_HANDLE_VALUE;
3047 is_open_empty_file =
false;
3049 if (addr_ !=
nullptr) {
3050 munmap(addr_, size_);
3063 return closesocket(sock);
3073 if (res < 0 && errno == EINTR) {
3074 std::this_thread::sleep_for(std::chrono::microseconds{1});
3086 static_cast<char *
>(ptr),
static_cast<int>(size),
3099 static_cast<const char *
>(ptr),
static_cast<int>(size),
3108#ifdef CPPHTTPLIB_USE_POLL
3109 struct pollfd pfd_read;
3111 pfd_read.events = POLLIN;
3113 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
3115 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
3118 if (sock >= FD_SETSIZE) {
return -1; }
3126 tv.tv_sec =
static_cast<long>(sec);
3127 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3130 return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv);
3136#ifdef CPPHTTPLIB_USE_POLL
3137 struct pollfd pfd_read;
3139 pfd_read.events = POLLOUT;
3141 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
3143 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
3146 if (sock >= FD_SETSIZE) {
return -1; }
3154 tv.tv_sec =
static_cast<long>(sec);
3155 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3158 return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv);
3165#ifdef CPPHTTPLIB_USE_POLL
3166 struct pollfd pfd_read;
3168 pfd_read.events = POLLIN | POLLOUT;
3170 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
3172 auto poll_res =
handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
3176 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
3178 socklen_t len =
sizeof(error);
3179 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3180 reinterpret_cast<char *
>(&error), &len);
3181 auto successful = res >= 0 && !error;
3193 FD_SET(sock, &fdsr);
3199 tv.tv_sec =
static_cast<long>(sec);
3200 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3203 return select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
3208 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3210 socklen_t len =
sizeof(error);
3211 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3212 reinterpret_cast<char *
>(&error), &len);
3213 auto successful = res >= 0 && !error;
3224 }
else if (val < 0 && errno == EBADF) {
3234 time_t write_timeout_sec, time_t write_timeout_usec);
3247 time_t read_timeout_sec_;
3248 time_t read_timeout_usec_;
3249 time_t write_timeout_sec_;
3250 time_t write_timeout_usec_;
3252 std::vector<char> read_buff_;
3253 size_t read_buff_off_ = 0;
3254 size_t read_buff_content_size_ = 0;
3256 static const size_t read_buff_size_ = 1024l * 4;
3259#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3260class SSLSocketStream final :
public Stream {
3262 SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec,
3263 time_t read_timeout_usec, time_t write_timeout_sec,
3264 time_t write_timeout_usec);
3265 ~SSLSocketStream()
override;
3267 bool is_readable()
const override;
3268 bool is_writable()
const override;
3269 ssize_t read(
char *ptr,
size_t size)
override;
3270 ssize_t write(
const char *ptr,
size_t size)
override;
3278 time_t read_timeout_sec_;
3279 time_t read_timeout_usec_;
3280 time_t write_timeout_sec_;
3281 time_t write_timeout_usec_;
3286 time_t keep_alive_timeout_sec) {
3287 using namespace std::chrono;
3289 const auto interval_usec =
3293 if (
select_read(sock, 0, interval_usec) > 0) {
return true; }
3295 const auto start = steady_clock::now() - microseconds{interval_usec};
3296 const auto timeout = seconds{keep_alive_timeout_sec};
3306 }
else if (val == 0) {
3307 if (steady_clock::now() - start > timeout) {
3318template <
typename T>
3321 size_t keep_alive_max_count,
3322 time_t keep_alive_timeout_sec, T callback) {
3323 assert(keep_alive_max_count > 0);
3325 auto count = keep_alive_max_count;
3326 while (count > 0 &&
keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3327 auto close_connection = count == 1;
3328 auto connection_closed =
false;
3329 ret = callback(close_connection, connection_closed);
3330 if (!ret || connection_closed) {
break; }
3336template <
typename T>
3339 size_t keep_alive_max_count,
3340 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
3341 time_t read_timeout_usec, time_t write_timeout_sec,
3342 time_t write_timeout_usec, T callback) {
3344 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3345 [&](
bool close_connection,
bool &connection_closed) {
3346 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3347 write_timeout_sec, write_timeout_usec);
3348 return callback(strm, close_connection, connection_closed);
3353 time_t read_timeout_usec,
3354 time_t write_timeout_sec,
3355 time_t write_timeout_usec,
3356 std::function<
bool(
Stream &)> callback) {
3357 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3358 write_timeout_sec, write_timeout_usec);
3359 return callback(strm);
3364 return shutdown(sock, SD_BOTH);
3366 return shutdown(sock, SHUT_RDWR);
3371 if (s.size() > 1 && s[0] ==
'\0') {
3381 if (s.size() > 1 && s[0] ==
'@') {
3389template <
typename BindOrConnect>
3391 int address_family,
int socket_flags,
bool tcp_nodelay,
3393 BindOrConnect bind_or_connect) {
3395 const char *node =
nullptr;
3396 struct addrinfo hints;
3397 struct addrinfo *result;
3399 memset(&hints, 0,
sizeof(
struct addrinfo));
3400 hints.ai_socktype = SOCK_STREAM;
3401 hints.ai_protocol = IPPROTO_IP;
3406 hints.ai_family = AF_UNSPEC;
3407 hints.ai_flags = AI_NUMERICHOST;
3409 if (!host.empty()) { node = host.c_str(); }
3410 hints.ai_family = address_family;
3411 hints.ai_flags = socket_flags;
3415 if (hints.ai_family == AF_UNIX) {
3416 const auto addrlen = host.length();
3417 if (addrlen >
sizeof(sockaddr_un::sun_path)) {
return INVALID_SOCKET; }
3420 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
3423 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3428 addr.sun_family = AF_UNIX;
3431 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3433 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
3434 hints.ai_addrlen =
static_cast<socklen_t
>(
3435 sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
3438 fcntl(sock, F_SETFD, FD_CLOEXEC);
3441 if (socket_options) { socket_options(sock); }
3444 if (!bind_or_connect(sock, hints, dummy)) {
3453 auto service = std::to_string(port);
3455 if (getaddrinfo(node, service.c_str(), &hints, &result)) {
3456#if defined __linux__ && !defined __ANDROID__
3463 for (
auto rp = result; rp; rp = rp->ai_next) {
3467 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
3468 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3484 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3490 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3492 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3498#if !defined _WIN32 && !defined SOCK_CLOEXEC
3499 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3508 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3509 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3511 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3512 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3516 if (rp->ai_family == AF_INET6) {
3517 auto opt = ipv6_v6only ? 1 : 0;
3519 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3520 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3522 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3523 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3527 if (socket_options) { socket_options(sock); }
3531 if (bind_or_connect(sock, *rp, quit)) {
return sock; }
3535 if (quit) {
break; }
3543 auto flags = nonblocking ? 1UL : 0UL;
3544 ioctlsocket(sock, FIONBIO, &flags);
3546 auto flags = fcntl(sock, F_GETFL, 0);
3547 fcntl(sock, F_SETFL,
3548 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3554 return WSAGetLastError() != WSAEWOULDBLOCK;
3556 return errno != EINPROGRESS;
3561 struct addrinfo hints;
3562 struct addrinfo *result;
3564 memset(&hints, 0,
sizeof(
struct addrinfo));
3565 hints.ai_family = AF_UNSPEC;
3566 hints.ai_socktype = SOCK_STREAM;
3567 hints.ai_protocol = 0;
3569 if (getaddrinfo(host.c_str(),
"0", &hints, &result)) {
return false; }
3573 for (
auto rp = result; rp; rp = rp->ai_next) {
3574 const auto &
ai = *rp;
3575 if (!::bind(sock,
ai.ai_addr,
static_cast<socklen_t
>(
ai.ai_addrlen))) {
3584#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3589inline std::string
if2ip(
int address_family,
const std::string &ifn) {
3590 struct ifaddrs *ifap;
3594 std::string addr_candidate;
3595 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3596 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3597 (AF_UNSPEC == address_family ||
3598 ifa->ifa_addr->sa_family == address_family)) {
3599 if (ifa->ifa_addr->sa_family == AF_INET) {
3600 auto sa =
reinterpret_cast<struct sockaddr_in *
>(ifa->ifa_addr);
3601 char buf[INET_ADDRSTRLEN];
3602 if (
inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3603 return std::string(buf, INET_ADDRSTRLEN);
3605 }
else if (ifa->ifa_addr->sa_family == AF_INET6) {
3606 auto sa =
reinterpret_cast<struct sockaddr_in6 *
>(ifa->ifa_addr);
3607 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
3608 char buf[INET6_ADDRSTRLEN] = {};
3609 if (
inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
3611 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3612 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3613 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3615 return std::string(buf, INET6_ADDRSTRLEN);
3622 return addr_candidate;
3627 const std::string &host,
const std::string &ip,
int port,
3628 int address_family,
bool tcp_nodelay,
bool ipv6_v6only,
3629 SocketOptions socket_options, time_t connection_timeout_sec,
3630 time_t connection_timeout_usec, time_t read_timeout_sec,
3631 time_t read_timeout_usec, time_t write_timeout_sec,
3632 time_t write_timeout_usec,
const std::string &intf,
Error &error) {
3634 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
3635 std::move(socket_options),
3636 [&](
socket_t sock2,
struct addrinfo &
ai,
bool &quit) ->
bool {
3637 if (!intf.empty()) {
3639 auto ip_from_if = if2ip(address_family, intf);
3640 if (ip_from_if.empty()) { ip_from_if = intf; }
3651 ::connect(sock2,
ai.ai_addr,
static_cast<socklen_t
>(
ai.ai_addrlen));
3659 connection_timeout_usec);
3670 auto timeout =
static_cast<uint32_t>(read_timeout_sec * 1000 +
3671 read_timeout_usec / 1000);
3672 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
3673 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3676 tv.tv_sec =
static_cast<long>(read_timeout_sec);
3677 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec);
3678 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
3679 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3685 auto timeout =
static_cast<uint32_t>(write_timeout_sec * 1000 +
3686 write_timeout_usec / 1000);
3687 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
3688 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3691 tv.tv_sec =
static_cast<long>(write_timeout_sec);
3692 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec);
3693 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
3694 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3712 socklen_t addr_len, std::string &ip,
int &port) {
3713 if (addr.ss_family == AF_INET) {
3714 port = ntohs(
reinterpret_cast<const struct sockaddr_in *
>(&addr)->sin_port);
3715 }
else if (addr.ss_family == AF_INET6) {
3717 ntohs(
reinterpret_cast<const struct sockaddr_in6 *
>(&addr)->sin6_port);
3722 std::array<char, NI_MAXHOST> ipstr{};
3723 if (getnameinfo(
reinterpret_cast<const struct sockaddr *
>(&addr), addr_len,
3724 ipstr.data(),
static_cast<socklen_t
>(ipstr.size()),
nullptr,
3725 0, NI_NUMERICHOST)) {
3734 struct sockaddr_storage addr;
3735 socklen_t addr_len =
sizeof(addr);
3736 if (!getsockname(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
3743 struct sockaddr_storage addr;
3744 socklen_t addr_len =
sizeof(addr);
3746 if (!getpeername(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
3749 if (addr.ss_family == AF_UNIX) {
3750#if defined(__linux__)
3752 socklen_t len =
sizeof(ucred);
3753 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
3756#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
3758 socklen_t len =
sizeof(pid);
3759 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
3777 (((std::numeric_limits<unsigned int>::max)() >> 6) &
3779 static_cast<unsigned char>(*s));
3782inline unsigned int str2tag(
const std::string &s) {
3788inline constexpr unsigned int operator""_t(
const char *s,
size_t l) {
3796 const std::map<std::string, std::string> &user_data,
3797 const std::string &default_content_type) {
3800 auto it = user_data.find(ext);
3801 if (it != user_data.end()) {
return it->second; }
3803 using udl::operator
""_t;
3806 default:
return default_content_type;
3808 case "css"_t:
return "text/css";
3809 case "csv"_t:
return "text/csv";
3811 case "html"_t:
return "text/html";
3813 case "mjs"_t:
return "text/javascript";
3814 case "txt"_t:
return "text/plain";
3815 case "vtt"_t:
return "text/vtt";
3817 case "apng"_t:
return "image/apng";
3818 case "avif"_t:
return "image/avif";
3819 case "bmp"_t:
return "image/bmp";
3820 case "gif"_t:
return "image/gif";
3821 case "png"_t:
return "image/png";
3822 case "svg"_t:
return "image/svg+xml";
3823 case "webp"_t:
return "image/webp";
3824 case "ico"_t:
return "image/x-icon";
3825 case "tif"_t:
return "image/tiff";
3826 case "tiff"_t:
return "image/tiff";
3828 case "jpeg"_t:
return "image/jpeg";
3830 case "mp4"_t:
return "video/mp4";
3831 case "mpeg"_t:
return "video/mpeg";
3832 case "webm"_t:
return "video/webm";
3834 case "mp3"_t:
return "audio/mp3";
3835 case "mpga"_t:
return "audio/mpeg";
3836 case "weba"_t:
return "audio/webm";
3837 case "wav"_t:
return "audio/wave";
3839 case "otf"_t:
return "font/otf";
3840 case "ttf"_t:
return "font/ttf";
3841 case "woff"_t:
return "font/woff";
3842 case "woff2"_t:
return "font/woff2";
3844 case "7z"_t:
return "application/x-7z-compressed";
3845 case "atom"_t:
return "application/atom+xml";
3846 case "pdf"_t:
return "application/pdf";
3847 case "json"_t:
return "application/json";
3848 case "rss"_t:
return "application/rss+xml";
3849 case "tar"_t:
return "application/x-tar";
3851 case "xhtml"_t:
return "application/xhtml+xml";
3852 case "xslt"_t:
return "application/xslt+xml";
3853 case "xml"_t:
return "application/xml";
3854 case "gz"_t:
return "application/gzip";
3855 case "zip"_t:
return "application/zip";
3856 case "wasm"_t:
return "application/wasm";
3861 using udl::operator
""_t;
3863 auto tag =
str2tag(content_type);
3866 case "image/svg+xml"_t:
3867 case "application/javascript"_t:
3868 case "application/json"_t:
3869 case "application/xml"_t:
3870 case "application/protobuf"_t:
3871 case "application/xhtml+xml"_t:
return true;
3873 case "text/event-stream"_t:
return false;
3875 default:
return !content_type.rfind(
"text/", 0);
3882 if (!ret) {
return EncodingType::None; }
3884 const auto &s = req.get_header_value(
"Accept-Encoding");
3887#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3889 ret = s.find(
"br") != std::string::npos;
3890 if (ret) {
return EncodingType::Brotli; }
3893#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3895 ret = s.find(
"gzip") != std::string::npos;
3896 if (ret) {
return EncodingType::Gzip; }
3899 return EncodingType::None;
3902inline bool nocompressor::compress(
const char *data,
size_t data_length,
3904 if (!data_length) {
return true; }
3905 return callback(data, data_length);
3908#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3909inline gzip_compressor::gzip_compressor() {
3910 std::memset(&strm_, 0,
sizeof(strm_));
3911 strm_.zalloc = Z_NULL;
3912 strm_.zfree = Z_NULL;
3913 strm_.opaque = Z_NULL;
3915 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
3916 Z_DEFAULT_STRATEGY) == Z_OK;
3919inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3921inline bool gzip_compressor::compress(
const char *data,
size_t data_length,
3922 bool last, Callback callback) {
3926 constexpr size_t max_avail_in =
3927 (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
3929 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
3930 (std::min)(data_length, max_avail_in));
3931 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
3933 data_length -= strm_.avail_in;
3934 data += strm_.avail_in;
3936 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
3939 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3941 strm_.avail_out =
static_cast<uInt
>(buff.size());
3942 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
3944 ret = deflate(&strm_, flush);
3945 if (ret == Z_STREAM_ERROR) {
return false; }
3947 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3950 }
while (strm_.avail_out == 0);
3952 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
3953 (flush == Z_NO_FLUSH && ret == Z_OK));
3954 assert(strm_.avail_in == 0);
3955 }
while (data_length > 0);
3960inline gzip_decompressor::gzip_decompressor() {
3961 std::memset(&strm_, 0,
sizeof(strm_));
3962 strm_.zalloc = Z_NULL;
3963 strm_.zfree = Z_NULL;
3964 strm_.opaque = Z_NULL;
3970 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
3973inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3975inline bool gzip_decompressor::is_valid()
const {
return is_valid_; }
3977inline bool gzip_decompressor::decompress(
const char *data,
size_t data_length,
3978 Callback callback) {
3984 constexpr size_t max_avail_in =
3985 (std::numeric_limits<
decltype(strm_.avail_in)>::max)();
3987 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
3988 (std::min)(data_length, max_avail_in));
3989 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(data));
3991 data_length -= strm_.avail_in;
3992 data += strm_.avail_in;
3994 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3995 while (strm_.avail_in > 0 && ret == Z_OK) {
3996 strm_.avail_out =
static_cast<uInt
>(buff.size());
3997 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
3999 ret = inflate(&strm_, Z_NO_FLUSH);
4001 assert(ret != Z_STREAM_ERROR);
4005 case Z_MEM_ERROR: inflateEnd(&strm_);
return false;
4008 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
4013 if (ret != Z_OK && ret != Z_STREAM_END) {
return false; }
4015 }
while (data_length > 0);
4021#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4022inline brotli_compressor::brotli_compressor() {
4023 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
4026inline brotli_compressor::~brotli_compressor() {
4027 BrotliEncoderDestroyInstance(state_);
4030inline bool brotli_compressor::compress(
const char *data,
size_t data_length,
4031 bool last, Callback callback) {
4032 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4034 auto operation =
last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
4035 auto available_in = data_length;
4036 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
4040 if (BrotliEncoderIsFinished(state_)) {
break; }
4042 if (!available_in) {
break; }
4045 auto available_out = buff.size();
4046 auto next_out = buff.data();
4048 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
4049 &available_out, &next_out,
nullptr)) {
4053 auto output_bytes = buff.size() - available_out;
4055 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
4062inline brotli_decompressor::brotli_decompressor() {
4063 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
4064 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
4065 : BROTLI_DECODER_RESULT_ERROR;
4068inline brotli_decompressor::~brotli_decompressor() {
4069 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
4072inline bool brotli_decompressor::is_valid()
const {
return decoder_s; }
4074inline bool brotli_decompressor::decompress(
const char *data,
4076 Callback callback) {
4077 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4078 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
4082 auto next_in =
reinterpret_cast<const uint8_t *
>(data);
4083 size_t avail_in = data_length;
4086 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
4088 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4089 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
4090 char *next_out = buff.data();
4091 size_t avail_out = buff.size();
4093 decoder_r = BrotliDecoderDecompressStream(
4094 decoder_s, &avail_in, &next_in, &avail_out,
4095 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
4097 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return false; }
4099 if (!callback(buff.data(), buff.size() - avail_out)) {
return false; }
4102 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4103 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
4108 return headers.find(key) != headers.end();
4112 const std::string &key,
const char *def,
4114 auto rng = headers.equal_range(key);
4115 auto it =
rng.first;
4116 std::advance(it,
static_cast<ssize_t>(
id));
4117 if (it !=
rng.second) {
return it->second.c_str(); }
4121template <
typename T>
4129 while (p < end && *p !=
':') {
4133 if (p == end) {
return false; }
4137 if (*p++ !=
':') {
return false; }
4144 auto key_len = key_end - beg;
4145 if (!key_len) {
return false; }
4147 auto key = std::string(beg, key_end);
4148 auto val = case_ignore::equal(key,
"Location")
4149 ? std::string(p, end)
4160 static const std::string CR_LF_NUL(
"\r\n\0", 3);
4161 if (val.find_first_of(CR_LF_NUL) != std::string::npos) {
return false; }
4171 const auto bufsiz = 2048;
4176 if (!line_reader.
getline()) {
return false; }
4179 auto line_terminator_len = 2;
4182 if (line_reader.
size() == 2) {
break; }
4184#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4186 if (line_reader.
size() == 1) {
break; }
4187 line_terminator_len = 1;
4196 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
4199 [&](
const std::string &key, std::string &val) {
4200 headers.emplace(key, val);
4216 auto read_len =
static_cast<size_t>(len - r);
4218 if (n <= 0) {
return false; }
4220 if (!out(buf,
static_cast<size_t>(n), r, len)) {
return false; }
4224 if (!progress(r, len)) {
return false; }
4235 auto read_len =
static_cast<size_t>(len - r);
4237 if (n <= 0) {
return; }
4248 if (n <= 0) {
return true; }
4250 if (!out(buf,
static_cast<size_t>(n), r, 0)) {
return false; }
4257template <
typename T>
4260 const auto bufsiz = 16;
4265 if (!line_reader.
getline()) {
return false; }
4267 unsigned long chunk_len;
4271 chunk_len = std::strtoul(line_reader.
ptr(), &end_ptr, 16);
4273 if (end_ptr == line_reader.
ptr()) {
return false; }
4274 if (chunk_len == ULONG_MAX) {
return false; }
4276 if (chunk_len == 0) {
break; }
4282 if (!line_reader.
getline()) {
return false; }
4284 if (strcmp(line_reader.
ptr(),
"\r\n") != 0) {
return false; }
4286 if (!line_reader.
getline()) {
return false; }
4303 if (!line_reader.
getline()) {
return true; }
4305 while (strcmp(line_reader.
ptr(),
"\r\n") != 0) {
4309 constexpr auto line_terminator_len = 2;
4310 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
4313 [&](
const std::string &key,
const std::string &val) {
4314 x.headers.emplace(key, val);
4317 if (!line_reader.
getline()) {
return false; }
4324 return case_ignore::equal(
4328template <
typename T,
typename U>
4331 bool decompress, U callback) {
4333 std::string encoding = x.get_header_value(
"Content-Encoding");
4336 if (encoding ==
"gzip" || encoding ==
"deflate") {
4337#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4338 decompressor = detail::make_unique<gzip_decompressor>();
4343 }
else if (encoding.find(
"br") != std::string::npos) {
4344#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4345 decompressor = detail::make_unique<brotli_decompressor>();
4357 [&](
const char *buf2,
size_t n2) {
4358 return receiver(buf2, n2, off, len);
4361 return callback(std::move(out));
4371 return receiver(buf, n, off, len);
4373 return callback(std::move(out));
4376template <
typename T>
4381 x, status, std::move(receiver), decompress,
4384 auto exceed_payload_max_length =
false;
4387 ret = read_content_chunked(strm, x, out);
4388 }
else if (!
has_header(x.headers,
"Content-Length")) {
4389 ret = read_content_without_length(strm, out);
4391 auto len = get_header_value_u64(x.headers,
"Content-Length", 0, 0);
4392 if (len > payload_max_length) {
4393 exceed_payload_max_length = true;
4394 skip_content_with_length(strm, len);
4396 }
else if (len > 0) {
4410 const std::string &path) {
4411 std::string s = method;
4414 s +=
" HTTP/1.1\r\n";
4415 return strm.
write(s.data(), s.size());
4419 std::string s =
"HTTP/1.1 ";
4420 s += std::to_string(status);
4424 return strm.
write(s.data(), s.size());
4429 for (
const auto &x : headers) {
4436 auto len = strm.
write(s.data(), s.size());
4437 if (len < 0) {
return len; }
4440 auto len = strm.
write(
"\r\n");
4441 if (len < 0) {
return len; }
4448 while (offset < l) {
4449 auto length = strm.
write(d + offset, l - offset);
4450 if (length < 0) {
return false; }
4451 offset +=
static_cast<size_t>(length);
4456template <
typename T>
4458 size_t offset,
size_t length, T is_shutting_down,
4460 size_t end_offset = offset + length;
4464 data_sink.
write = [&](
const char *d,
size_t l) ->
bool {
4477 while (offset < end_offset && !is_shutting_down()) {
4479 error = Error::Write;
4481 }
else if (!content_provider(offset, end_offset - offset, data_sink)) {
4482 error = Error::Canceled;
4485 error = Error::Write;
4490 error = Error::Success;
4494template <
typename T>
4496 size_t offset,
size_t length,
4497 const T &is_shutting_down) {
4498 auto error = Error::Success;
4499 return write_content(strm, content_provider, offset, length, is_shutting_down,
4503template <
typename T>
4507 const T &is_shutting_down) {
4509 auto data_available =
true;
4513 data_sink.
write = [&](
const char *d,
size_t l) ->
bool {
4523 data_sink.
done = [&](void) { data_available =
false; };
4525 while (data_available && !is_shutting_down()) {
4528 }
else if (!content_provider(offset, 0, data_sink)) {
4537template <
typename T,
typename U>
4542 auto data_available =
true;
4546 data_sink.
write = [&](
const char *d,
size_t l) ->
bool {
4548 data_available = l > 0;
4551 std::string payload;
4553 [&](
const char *data,
size_t data_len) {
4554 payload.append(data, data_len);
4557 if (!payload.empty()) {
4562 !
write_data(strm, chunk.data(), chunk.size())) {
4575 auto done_with_trailer = [&](
const Headers *trailer) {
4576 if (!ok) {
return; }
4578 data_available =
false;
4580 std::string payload;
4582 [&](
const char *data,
size_t data_len) {
4583 payload.append(data, data_len);
4590 if (!payload.empty()) {
4592 auto chunk =
from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4594 !
write_data(strm, chunk.data(), chunk.size())) {
4600 static const std::string done_marker(
"0\r\n");
4601 if (!
write_data(strm, done_marker.data(), done_marker.size())) {
4607 for (
const auto &kv : *trailer) {
4608 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
4609 if (!
write_data(strm, field_line.data(), field_line.size())) {
4615 static const std::string crlf(
"\r\n");
4616 if (!
write_data(strm, crlf.data(), crlf.size())) { ok =
false; }
4619 data_sink.
done = [&](void) { done_with_trailer(
nullptr); };
4622 done_with_trailer(&trailer);
4625 while (data_available && !is_shutting_down()) {
4627 error = Error::Write;
4629 }
else if (!content_provider(offset, 0, data_sink)) {
4630 error = Error::Canceled;
4633 error = Error::Write;
4638 error = Error::Success;
4642template <
typename T,
typename U>
4646 auto error = Error::Success;
4651template <
typename T>
4653 const std::string &path,
const std::string &location,
4656 new_req.
path = path;
4659 if (res.
status == StatusCode::SeeOther_303 &&
4660 (req.method !=
"GET" && req.method !=
"HEAD")) {
4662 new_req.
body.clear();
4668 auto ret = cli.send(new_req, new_res, error);
4681 for (
auto it = params.begin(); it != params.end(); ++it) {
4682 if (it != params.begin()) { query +=
"&"; }
4692 std::set<std::string> cache;
4693 split(data, data + size,
'&', [&](
const char *b,
const char *e) {
4694 std::string kv(b, e);
4695 if (cache.find(kv) != cache.end()) {
return; }
4696 cache.insert(std::move(kv));
4700 divide(b,
static_cast<std::size_t
>(e - b),
'=',
4701 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data,
4702 std::size_t rhs_size) {
4703 key.assign(lhs_data, lhs_size);
4704 val.assign(rhs_data, rhs_size);
4718 std::string &boundary) {
4719 auto boundary_keyword =
"boundary=";
4720 auto pos = content_type.find(boundary_keyword);
4721 if (pos == std::string::npos) {
return false; }
4722 auto end = content_type.find(
';', pos);
4723 auto beg = pos + strlen(boundary_keyword);
4725 return !boundary.empty();
4729 std::set<std::string> cache;
4730 split(s.data(), s.data() + s.size(),
';', [&](
const char *b,
const char *e) {
4731 std::string kv(b, e);
4732 if (cache.find(kv) != cache.end()) { return; }
4737 split(b, e,
'=', [&](
const char *b2,
const char *e2) {
4746 params.emplace(trim_double_quotes_copy((key)),
4747 trim_double_quotes_copy((val)));
4752#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4753inline bool parse_range_header(
const std::string &s, Ranges &ranges) {
4757 auto is_valid = [](
const std::string &str) {
4758 return std::all_of(str.cbegin(), str.cend(),
4759 [](
unsigned char c) { return std::isdigit(c); });
4762 if (s.size() > 7 && s.compare(0, 6,
"bytes=") == 0) {
4763 const auto pos =
static_cast<size_t>(6);
4764 const auto len =
static_cast<size_t>(s.size() - 6);
4765 auto all_valid_ranges =
true;
4766 split(&s[pos], &s[pos + len],
',', [&](
const char *b,
const char *e) {
4767 if (!all_valid_ranges) {
return; }
4769 const auto it = std::find(b, e,
'-');
4771 all_valid_ranges =
false;
4775 const auto lhs = std::string(b, it);
4776 const auto rhs = std::string(it + 1, e);
4777 if (!is_valid(lhs) || !is_valid(rhs)) {
4778 all_valid_ranges =
false;
4783 static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
4785 static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
4786 if ((first == -1 && last == -1) ||
4787 (first != -1 && last != -1 && first > last)) {
4788 all_valid_ranges =
false;
4792 ranges.emplace_back(first, last);
4794 return all_valid_ranges && !ranges.empty();
4797#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4800}
catch (...) {
return false; }
4808 boundary_ = boundary;
4809 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4810 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4820 while (buf_size() > 0) {
4823 buf_erase(buf_find(dash_boundary_crlf_));
4824 if (dash_boundary_crlf_.size() > buf_size()) {
return true; }
4825 if (!buf_start_with(dash_boundary_crlf_)) {
return false; }
4826 buf_erase(dash_boundary_crlf_.size());
4836 auto pos = buf_find(crlf_);
4838 while (pos < buf_size()) {
4841 if (!header_callback(file_)) {
4845 buf_erase(crlf_.size());
4850 const auto header = buf_head(pos);
4853 [&](
const std::string &,
const std::string &) {})) {
4858 static const std::string header_content_type =
"Content-Type:";
4860 if (start_with_case_ignore(
header, header_content_type)) {
4861 file_.content_type =
4864 static const std::regex re_content_disposition(
4865 R
"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4866 std::regex_constants::icase);
4869 if (std::regex_match(
header, m, re_content_disposition)) {
4873 auto it = params.find(
"name");
4874 if (it != params.end()) {
4875 file_.name = it->second;
4881 it = params.find(
"filename");
4882 if (it != params.end()) { file_.filename = it->second; }
4884 it = params.find(
"filename*");
4885 if (it != params.end()) {
4887 static const std::regex re_rfc5987_encoding(
4888 R
"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4891 if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
4900 buf_erase(pos + crlf_.size());
4901 pos = buf_find(crlf_);
4903 if (state_ != 3) {
return true; }
4907 if (crlf_dash_boundary_.size() > buf_size()) {
return true; }
4908 auto pos = buf_find(crlf_dash_boundary_);
4909 if (pos < buf_size()) {
4910 if (!content_callback(buf_data(), pos)) {
4914 buf_erase(pos + crlf_dash_boundary_.size());
4917 auto len = buf_size() - crlf_dash_boundary_.size();
4919 if (!content_callback(buf_data(), len)) {
4930 if (crlf_.size() > buf_size()) {
return true; }
4931 if (buf_start_with(crlf_)) {
4932 buf_erase(crlf_.size());
4935 if (dash_.size() > buf_size()) {
return true; }
4936 if (buf_start_with(dash_)) {
4937 buf_erase(dash_.size());
4939 buf_erase(buf_size());
4953 void clear_file_info() {
4955 file_.filename.clear();
4956 file_.content_type.clear();
4959 bool start_with_case_ignore(
const std::string &a,
4960 const std::string &b)
const {
4961 if (a.size() < b.size()) {
return false; }
4962 for (
size_t i = 0; i < b.size(); i++) {
4963 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
4970 const std::string dash_ =
"--";
4971 const std::string crlf_ =
"\r\n";
4972 std::string boundary_;
4973 std::string dash_boundary_crlf_;
4974 std::string crlf_dash_boundary_;
4977 bool is_valid_ =
false;
4978 MultipartFormData file_;
4981 bool start_with(
const std::string &a,
size_t spos,
size_t epos,
4982 const std::string &b)
const {
4983 if (epos - spos < b.size()) {
return false; }
4984 for (
size_t i = 0; i < b.size(); i++) {
4985 if (a[i + spos] != b[i]) {
return false; }
4990 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
4992 const char *buf_data()
const {
return &buf_[buf_spos_]; }
4994 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_, l); }
4996 bool buf_start_with(
const std::string &s)
const {
4997 return start_with(buf_, buf_spos_, buf_epos_, s);
5000 size_t buf_find(
const std::string &s)
const {
5003 size_t off = buf_spos_;
5004 while (off < buf_epos_) {
5007 if (pos == buf_epos_) {
return buf_size(); }
5008 if (buf_[pos] == c) {
break; }
5012 auto remaining_size = buf_epos_ - pos;
5013 if (s.size() > remaining_size) {
return buf_size(); }
5015 if (start_with(buf_, pos, buf_epos_, s)) {
return pos - buf_spos_; }
5023 void buf_append(
const char *data,
size_t n) {
5024 auto remaining_size = buf_size();
5025 if (remaining_size > 0 && buf_spos_ > 0) {
5026 for (
size_t i = 0; i < remaining_size; i++) {
5027 buf_[i] = buf_[buf_spos_ + i];
5031 buf_epos_ = remaining_size;
5033 if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
5035 for (
size_t i = 0; i <
n; i++) {
5036 buf_[buf_epos_ + i] = data[i];
5041 void buf_erase(
size_t size) { buf_spos_ +=
size; }
5044 size_t buf_spos_ = 0;
5045 size_t buf_epos_ = 0;
5049 static const char data[] =
5050 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5055 static std::random_device seed_gen;
5058 static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(),
5061 static std::mt19937 engine(seed_sequence);
5064 for (
size_t i = 0; i < length; i++) {
5065 result += data[engine() % (
sizeof(data) - 1)];
5071 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
5076 for (
size_t i = 0; i < boundary.size(); i++) {
5077 auto c = boundary[i];
5078 if (!std::isalnum(c) && c !=
'-' && c !=
'_') {
5086template <
typename T>
5089 const std::string &boundary) {
5090 std::string
body =
"--" + boundary +
"\r\n";
5091 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
5092 if (!item.filename.empty()) {
5093 body +=
"; filename=\"" + item.filename +
"\"";
5096 if (!item.content_type.empty()) {
5097 body +=
"Content-Type: " + item.content_type +
"\r\n";
5108 return "--" + boundary +
"--\r\n";
5113 return "multipart/form-data; boundary=" + boundary;
5118 const std::string &boundary,
bool finish =
true) {
5121 for (
const auto &item : items) {
5132 if (!req.ranges.empty() && 200 <= res.
status && res.
status < 300) {
5138 size_t overwrapping_count = 0;
5147 for (
auto &r : req.ranges) {
5148 auto &first_pos = r.first;
5149 auto &last_pos = r.second;
5151 if (first_pos == -1 && last_pos == -1) {
5153 last_pos = contant_len;
5156 if (first_pos == -1) {
5157 first_pos = contant_len - last_pos;
5158 last_pos = contant_len - 1;
5161 if (last_pos == -1) { last_pos = contant_len - 1; }
5164 if (!(0 <= first_pos && first_pos <= last_pos &&
5165 last_pos <= contant_len - 1)) {
5170 if (first_pos <= prev_first_pos) {
return true; }
5173 if (first_pos <= prev_last_pos) {
5174 overwrapping_count++;
5175 if (overwrapping_count > 2) {
return true; }
5178 prev_first_pos = (std::max)(prev_first_pos, first_pos);
5179 prev_last_pos = (std::max)(prev_last_pos, last_pos);
5186inline std::pair<size_t, size_t>
5188 assert(r.first != -1 && r.second != -1);
5189 assert(0 <= r.first && r.first <
static_cast<ssize_t>(content_length));
5190 assert(r.first <= r.second &&
5191 r.second <
static_cast<ssize_t>(content_length));
5192 (void)(content_length);
5193 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
5197 const std::pair<size_t, size_t> &offset_and_length,
size_t content_length) {
5198 auto st = offset_and_length.first;
5199 auto ed = st + offset_and_length.second - 1;
5201 std::string field =
"bytes ";
5202 field += std::to_string(st);
5204 field += std::to_string(ed);
5206 field += std::to_string(content_length);
5210template <
typename SToken,
typename CToken,
typename Content>
5212 const std::string &boundary,
5213 const std::string &content_type,
5214 size_t content_length, SToken stoken,
5215 CToken ctoken, Content content) {
5216 for (
size_t i = 0; i < req.ranges.size(); i++) {
5220 if (!content_type.empty()) {
5221 ctoken(
"Content-Type: ");
5222 stoken(content_type);
5226 auto offset_and_length =
5229 ctoken(
"Content-Range: ");
5234 if (!content(offset_and_length.first, offset_and_length.second)) {
5248 const std::string &boundary,
5249 const std::string &content_type,
5250 size_t content_length,
5251 std::string &data) {
5253 req, boundary, content_type, content_length,
5254 [&](
const std::string &
token) { data +=
token; },
5255 [&](
const std::string &
token) { data +=
token; },
5256 [&](
size_t offset,
size_t length) {
5257 assert(offset + length <= content_length);
5258 data += res.
body.substr(offset, length);
5264 const std::string &boundary,
5265 const std::string &content_type,
5266 size_t content_length) {
5267 size_t data_length = 0;
5270 req, boundary, content_type, content_length,
5271 [&](
const std::string &
token) { data_length +=
token.size(); },
5272 [&](
const std::string &
token) { data_length +=
token.size(); },
5273 [&](
size_t ,
size_t length) {
5274 data_length += length;
5281template <
typename T>
5284 const std::string &boundary,
5285 const std::string &content_type,
5286 size_t content_length,
const T &is_shutting_down) {
5288 req, boundary, content_type, content_length,
5291 [&](
size_t offset,
size_t length) {
5298 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH" ||
5299 req.method ==
"PRI" || req.method ==
"DELETE") {
5309 if (*p ==
'\r' || *p ==
'\n') {
return true; }
5315#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5316inline std::string message_digest(
const std::string &s,
const EVP_MD *algo) {
5317 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
5318 EVP_MD_CTX_new(), EVP_MD_CTX_free);
5320 unsigned int hash_length = 0;
5321 unsigned char hash[EVP_MAX_MD_SIZE];
5323 EVP_DigestInit_ex(context.get(), algo,
nullptr);
5324 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
5325 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
5327 std::stringstream ss;
5328 for (
auto i = 0u; i < hash_length; ++i) {
5329 ss << std::hex << std::setw(2) << std::setfill(
'0')
5330 <<
static_cast<unsigned int>(hash[i]);
5336inline std::string MD5(
const std::string &s) {
5337 return message_digest(s, EVP_md5());
5340inline std::string SHA_256(
const std::string &s) {
5341 return message_digest(s, EVP_sha256());
5344inline std::string SHA_512(
const std::string &s) {
5345 return message_digest(s, EVP_sha512());
5349#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5353inline bool load_system_certs_on_windows(X509_STORE *store) {
5354 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L
"ROOT");
5355 if (!hStore) {
return false; }
5358 PCCERT_CONTEXT pContext = NULL;
5359 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
5362 static_cast<const unsigned char *
>(pContext->pbCertEncoded);
5364 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5366 X509_STORE_add_cert(store, x509);
5372 CertFreeCertificateContext(pContext);
5373 CertCloseStore(hStore, 0);
5377#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5379template <
typename T>
5381 std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
5383inline void cf_object_ptr_deleter(CFTypeRef obj) {
5384 if (obj) { CFRelease(obj); }
5387inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5388 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
5389 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
5392 CFObjectPtr<CFDictionaryRef> query(
5393 CFDictionaryCreate(
nullptr,
reinterpret_cast<const void **
>(keys), values,
5394 sizeof(keys) /
sizeof(keys[0]),
5395 &kCFTypeDictionaryKeyCallBacks,
5396 &kCFTypeDictionaryValueCallBacks),
5397 cf_object_ptr_deleter);
5399 if (!query) {
return false; }
5401 CFTypeRef security_items =
nullptr;
5402 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
5403 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
5407 certs.reset(
reinterpret_cast<CFArrayRef
>(security_items));
5411inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5412 CFArrayRef root_security_items =
nullptr;
5413 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
5417 certs.reset(root_security_items);
5421inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
5423 for (
auto i = 0; i < CFArrayGetCount(certs); ++i) {
5424 const auto cert =
reinterpret_cast<const __SecCertificate *
>(
5425 CFArrayGetValueAtIndex(certs, i));
5427 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) {
continue; }
5429 CFDataRef cert_data =
nullptr;
5430 if (SecItemExport(cert, kSecFormatX509Cert, 0,
nullptr, &cert_data) !=
5435 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
5437 auto encoded_cert =
static_cast<const unsigned char *
>(
5438 CFDataGetBytePtr(cert_data_ptr.get()));
5441 d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
5444 X509_STORE_add_cert(store, x509);
5453inline bool load_system_certs_on_macos(X509_STORE *store) {
5455 CFObjectPtr<CFArrayRef> certs(
nullptr, cf_object_ptr_deleter);
5456 if (retrieve_certs_from_keychain(certs) && certs) {
5457 result = add_certs_to_x509_store(certs.get(), store);
5460 if (retrieve_root_certs_from_keychain(certs) && certs) {
5461 result = add_certs_to_x509_store(certs.get(), store) ||
result;
5475 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ =
true;
5479 if (is_valid_) WSACleanup();
5482 bool is_valid_ =
false;
5485static WSInit wsinit_;
5488#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5489inline std::pair<std::string, std::string> make_digest_authentication_header(
5490 const Request &req,
const std::map<std::string, std::string> &auth,
5491 size_t cnonce_count,
const std::string &cnonce,
const std::string &username,
5492 const std::string &password,
bool is_proxy =
false) {
5495 std::stringstream ss;
5496 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
5501 if (auth.find(
"qop") != auth.end()) {
5502 qop = auth.at(
"qop");
5503 if (qop.find(
"auth-int") != std::string::npos) {
5505 }
else if (qop.find(
"auth") != std::string::npos) {
5512 std::string algo =
"MD5";
5513 if (auth.find(
"algorithm") != auth.end()) { algo = auth.at(
"algorithm"); }
5515 std::string response;
5517 auto H = algo ==
"SHA-256" ? detail::SHA_256
5518 : algo ==
"SHA-512" ? detail::SHA_512
5521 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
5523 auto A2 =
req.method +
":" +
req.path;
5524 if (qop ==
"auth-int") { A2 +=
":" +
H(
req.body); }
5527 response =
H(
H(A1) +
":" + auth.at(
"nonce") +
":" +
H(A2));
5529 response =
H(
H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
5530 ":" + qop +
":" +
H(A2));
5534 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
5536 auto field =
"Digest username=\"" + username +
"\", realm=\"" +
5537 auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
5538 "\", uri=\"" +
req.path +
"\", algorithm=" + algo +
5539 (qop.empty() ?
", response=\""
5540 :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" +
5541 cnonce +
"\", response=\"") +
5543 (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
5545 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5546 return std::make_pair(key, field);
5551 std::map<std::string, std::string> &auth,
5553 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
5555 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5557 auto pos = s.find(
' ');
5558 if (pos != std::string::npos) {
5559 auto type = s.substr(0, pos);
5560 if (type ==
"Basic") {
5562 }
else if (type ==
"Digest") {
5563 s = s.substr(pos + 1);
5564 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
5565 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
5567 auto key = s.substr(
static_cast<size_t>(m.position(1)),
5568 static_cast<size_t>(m.length(1)));
5569 auto val = m.length(2) > 0
5570 ? s.substr(
static_cast<size_t>(m.position(2)),
5571 static_cast<size_t>(m.length(2)))
5572 : s.substr(
static_cast<size_t>(m.position(3)),
5573 static_cast<size_t>(m.length(3)));
5587 : content_provider_(content_provider) {}
5590 return content_provider_(offset, sink);
5600 std::vector<std::string> addrs;
5602 if (addrs.empty()) {
return std::string(); }
5607 std::vector<std::string> &addrs) {
5608 struct addrinfo hints;
5609 struct addrinfo *result;
5611 memset(&hints, 0,
sizeof(
struct addrinfo));
5612 hints.ai_family = AF_UNSPEC;
5613 hints.ai_socktype = SOCK_STREAM;
5614 hints.ai_protocol = 0;
5616 if (getaddrinfo(hostname.c_str(),
nullptr, &hints, &result)) {
5617#if defined __linux__ && !defined __ANDROID__
5624 for (
auto rp = result; rp; rp = rp->ai_next) {
5626 *
reinterpret_cast<struct sockaddr_storage *
>(rp->ai_addr);
5629 if (detail::get_ip_and_port(addr,
sizeof(
struct sockaddr_storage), ip,
5631 addrs.push_back(ip);
5638 std::string path_with_query = path;
5639 const static std::regex re(
"[^?]+\\?.*");
5640 auto delm = std::regex_match(path, re) ?
'&' :
'?';
5641 path_with_query += delm + detail::params_to_query_str(params);
5642 return path_with_query;
5646inline std::pair<std::string, std::string>
5648 std::string field =
"bytes=";
5650 for (
const auto &r : ranges) {
5651 if (i != 0) { field +=
", "; }
5652 if (r.first != -1) { field += std::to_string(r.first); }
5654 if (r.second != -1) { field += std::to_string(r.second); }
5657 return std::make_pair(
"Range", std::move(field));
5660inline std::pair<std::string, std::string>
5662 const std::string &password,
bool is_proxy) {
5663 auto field =
"Basic " + detail::base64_encode(username +
":" + password);
5664 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5665 return std::make_pair(key, std::move(field));
5668inline std::pair<std::string, std::string>
5670 bool is_proxy =
false) {
5671 auto field =
"Bearer " +
token;
5672 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5673 return std::make_pair(key, std::move(field));
5677inline bool Request::has_header(
const std::string &key)
const {
5678 return detail::has_header(headers, key);
5681inline std::string Request::get_header_value(
const std::string &key,
5682 const char *def,
size_t id)
const {
5683 return detail::get_header_value(headers, key, def,
id);
5686inline size_t Request::get_header_value_count(
const std::string &key)
const {
5687 auto r = headers.equal_range(key);
5688 return static_cast<size_t>(std::distance(r.first, r.second));
5691inline void Request::set_header(
const std::string &key,
5692 const std::string &val) {
5693 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
5694 headers.emplace(key, val);
5698inline bool Request::has_param(
const std::string &key)
const {
5699 return params.find(key) != params.end();
5702inline std::string Request::get_param_value(
const std::string &key,
5704 auto rng = params.equal_range(key);
5705 auto it =
rng.first;
5706 std::advance(it,
static_cast<ssize_t>(
id));
5707 if (it !=
rng.second) {
return it->second; }
5708 return std::string();
5711inline size_t Request::get_param_value_count(
const std::string &key)
const {
5712 auto r = params.equal_range(key);
5713 return static_cast<size_t>(std::distance(r.first, r.second));
5716inline bool Request::is_multipart_form_data()
const {
5717 const auto &content_type = get_header_value(
"Content-Type");
5718 return !content_type.rfind(
"multipart/form-data", 0);
5721inline bool Request::has_file(
const std::string &key)
const {
5722 return files.find(key) != files.end();
5726 auto it = files.find(key);
5727 if (it != files.end()) {
return it->second; }
5731inline std::vector<MultipartFormData>
5732Request::get_file_values(
const std::string &key)
const {
5733 std::vector<MultipartFormData> values;
5734 auto rng = files.equal_range(key);
5735 for (
auto it =
rng.first; it !=
rng.second; it++) {
5736 values.push_back(it->second);
5742inline bool Response::has_header(
const std::string &key)
const {
5743 return headers.find(key) != headers.end();
5746inline std::string Response::get_header_value(
const std::string &key,
5749 return detail::get_header_value(headers, key, def,
id);
5752inline size_t Response::get_header_value_count(
const std::string &key)
const {
5753 auto r = headers.equal_range(key);
5754 return static_cast<size_t>(std::distance(r.first, r.second));
5757inline void Response::set_header(
const std::string &key,
5758 const std::string &val) {
5759 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
5760 headers.emplace(key, val);
5764inline void Response::set_redirect(
const std::string &url,
int stat) {
5765 if (!detail::has_crlf(url)) {
5766 set_header(
"Location", url);
5767 if (300 <= stat && stat < 400) {
5768 this->status = stat;
5770 this->status = StatusCode::Found_302;
5775inline void Response::set_content(
const char *s,
size_t n,
5776 const std::string &content_type) {
5779 auto rng = headers.equal_range(
"Content-Type");
5780 headers.erase(
rng.first,
rng.second);
5781 set_header(
"Content-Type", content_type);
5784inline void Response::set_content(
const std::string &s,
5785 const std::string &content_type) {
5786 set_content(s.data(), s.size(), content_type);
5789inline void Response::set_content(std::string &&s,
5790 const std::string &content_type) {
5791 body = std::move(s);
5793 auto rng = headers.equal_range(
"Content-Type");
5794 headers.erase(
rng.first,
rng.second);
5795 set_header(
"Content-Type", content_type);
5798inline void Response::set_content_provider(
5799 size_t in_length,
const std::string &content_type,
ContentProvider provider,
5801 set_header(
"Content-Type", content_type);
5802 content_length_ = in_length;
5803 if (in_length > 0) { content_provider_ = std::move(provider); }
5804 content_provider_resource_releaser_ = std::move(resource_releaser);
5805 is_chunked_content_provider_ =
false;
5808inline void Response::set_content_provider(
5811 set_header(
"Content-Type", content_type);
5812 content_length_ = 0;
5814 content_provider_resource_releaser_ = std::move(resource_releaser);
5815 is_chunked_content_provider_ =
false;
5818inline void Response::set_chunked_content_provider(
5821 set_header(
"Content-Type", content_type);
5822 content_length_ = 0;
5824 content_provider_resource_releaser_ = std::move(resource_releaser);
5825 is_chunked_content_provider_ =
true;
5828inline void Response::set_file_content(
const std::string &path,
5829 const std::string &content_type) {
5830 file_content_path_ = path;
5831 file_content_content_type_ = content_type;
5834inline void Response::set_file_content(
const std::string &path) {
5835 file_content_path_ = path;
5839inline bool Result::has_request_header(
const std::string &key)
const {
5840 return request_headers_.find(key) != request_headers_.end();
5843inline std::string Result::get_request_header_value(
const std::string &key,
5846 return detail::get_header_value(request_headers_, key, def,
id);
5850Result::get_request_header_value_count(
const std::string &key)
const {
5851 auto r = request_headers_.equal_range(key);
5852 return static_cast<size_t>(std::distance(r.first, r.second));
5857 return write(ptr, strlen(ptr));
5860inline ssize_t Stream::write(
const std::string &s) {
5861 return write(s.data(), s.size());
5867inline SocketStream::SocketStream(
socket_t sock, time_t read_timeout_sec,
5868 time_t read_timeout_usec,
5869 time_t write_timeout_sec,
5870 time_t write_timeout_usec)
5871 : sock_(sock), read_timeout_sec_(read_timeout_sec),
5872 read_timeout_usec_(read_timeout_usec),
5873 write_timeout_sec_(write_timeout_sec),
5874 write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
5879 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
5883 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
5890 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5892 size = (std::min)(size,
5893 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
5896 if (read_buff_off_ < read_buff_content_size_) {
5897 auto remaining_size = read_buff_content_size_ - read_buff_off_;
5898 if (size <= remaining_size) {
5899 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
5900 read_buff_off_ += size;
5901 return static_cast<ssize_t>(size);
5903 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
5904 read_buff_off_ += remaining_size;
5905 return static_cast<ssize_t>(remaining_size);
5912 read_buff_content_size_ = 0;
5914 if (size < read_buff_size_) {
5915 auto n =
read_socket(sock_, read_buff_.data(), read_buff_size_,
5919 }
else if (n <=
static_cast<ssize_t>(size)) {
5920 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(n));
5923 memcpy(ptr, read_buff_.data(), size);
5924 read_buff_off_ = size;
5925 read_buff_content_size_ =
static_cast<size_t>(n);
5926 return static_cast<ssize_t>(size);
5936#if defined(_WIN32) && !defined(_WIN64)
5938 (std::min)(size,
static_cast<size_t>((std::numeric_limits<int>::max)()));
5962#if defined(_MSC_VER) && _MSC_VER < 1910
5963 auto len_read = buffer._Copy_s(ptr, size, size, position);
5965 auto len_read = buffer.copy(ptr, size, position);
5967 position +=
static_cast<size_t>(len_read);
5968 return static_cast<ssize_t>(len_read);
5972 buffer.append(ptr, size);
5973 return static_cast<ssize_t>(size);
5987 static constexpr char marker[] =
"/:";
5990 std::size_t last_param_end = 0;
5992#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5997 std::unordered_set<std::string> param_name_set;
6001 const auto marker_pos = pattern.find(
6002 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
6003 if (marker_pos == std::string::npos) {
break; }
6005 static_fragments_.push_back(
6006 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
6008 const auto param_name_start = marker_pos + 2;
6010 auto sep_pos = pattern.find(separator, param_name_start);
6011 if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
6014 pattern.substr(param_name_start, sep_pos - param_name_start);
6016#ifndef CPPHTTPLIB_NO_EXCEPTIONS
6017 if (param_name_set.find(param_name) != param_name_set.cend()) {
6018 std::string msg =
"Encountered path parameter '" + param_name +
6019 "' multiple times in route pattern '" + pattern +
"'.";
6020 throw std::invalid_argument(msg);
6024 param_names_.push_back(std::move(param_name));
6026 last_param_end = sep_pos + 1;
6029 if (last_param_end < pattern.length()) {
6030 static_fragments_.push_back(pattern.substr(last_param_end));
6035 request.
matches = std::smatch();
6040 std::size_t starting_pos = 0;
6041 for (
size_t i = 0; i < static_fragments_.size(); ++i) {
6042 const auto &fragment = static_fragments_[i];
6044 if (starting_pos + fragment.length() > request.
path.length()) {
6050 if (std::strncmp(request.
path.c_str() + starting_pos, fragment.c_str(),
6051 fragment.length()) != 0) {
6055 starting_pos += fragment.length();
6060 if (i >= param_names_.size()) {
continue; }
6062 auto sep_pos = request.
path.find(separator, starting_pos);
6063 if (sep_pos == std::string::npos) { sep_pos = request.
path.length(); }
6065 const auto ¶m_name = param_names_[i];
6068 param_name, request.
path.substr(starting_pos, sep_pos - starting_pos));
6071 starting_pos = sep_pos + 1;
6074 return starting_pos >= request.
path.length();
6079 return std::regex_match(request.
path, request.
matches, regex_);
6089 signal(SIGPIPE, SIG_IGN);
6095inline std::unique_ptr<detail::MatcherBase>
6096Server::make_matcher(
const std::string &pattern) {
6097 if (pattern.find(
"/:") != std::string::npos) {
6098 return detail::make_unique<detail::PathParamsMatcher>(pattern);
6100 return detail::make_unique<detail::RegexMatcher>(pattern);
6105 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6110 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6116 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6117 std::move(handler));
6122 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6128 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6129 std::move(handler));
6134 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6140 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6141 std::move(handler));
6146 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6152 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6153 std::move(handler));
6158 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6163 const std::string &mount_point) {
6168 const std::string &dir,
Headers headers) {
6171 std::string mnt = !mount_point.empty() ? mount_point :
"/";
6172 if (!mnt.empty() && mnt[0] ==
'/') {
6173 base_dirs_.push_back({mnt, dir, std::move(headers)});
6181 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
6182 if (it->mount_point == mount_point) {
6183 base_dirs_.erase(it);
6192 const std::string &mime) {
6193 file_extension_and_mimetype_map_[ext] = mime;
6198 default_file_mimetype_ = mime;
6203 file_request_handler_ = std::move(handler);
6207inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
6209 error_handler_ = std::move(handler);
6213inline Server &Server::set_error_handler_core(Handler handler,
6215 error_handler_ = [handler](
const Request &req, Response &res) {
6223 exception_handler_ = std::move(handler);
6228 pre_routing_handler_ = std::move(handler);
6233 post_routing_handler_ = std::move(handler);
6238 logger_ = std::move(logger);
6244 expect_100_continue_handler_ = std::move(handler);
6249 address_family_ = family;
6264 socket_options_ = std::move(socket_options);
6269 default_headers_ = std::move(headers);
6275 header_writer_ = writer;
6314 auto ret = bind_internal(host, port, socket_flags);
6315 if (ret == -1) { is_decommisioned =
true; }
6319 auto ret = bind_internal(host, 0, socket_flags);
6320 if (ret == -1) { is_decommisioned =
true; }
6328 return bind_to_port(host, port, socket_flags) && listen_internal();
6333 return bind_to_port(host, port, socket_flags) && run_cycle_once();
6339 while (!is_running_ && !is_decommisioned) {
6340 std::this_thread::sleep_for(std::chrono::milliseconds{1});
6351 is_decommisioned =
false;
6356inline bool Server::parse_request_line(
const char *s,
Request &req)
const {
6357 auto len = strlen(s);
6358 if (len < 2 || s[len - 2] !=
'\r' || s[len - 1] !=
'\n') {
return false; }
6364 detail::split(s, s + len,
' ', [&](
const char *b,
const char *e) {
6366 case 0: req.method = std::string(b, e);
break;
6367 case 1: req.target = std::string(b, e);
break;
6368 case 2: req.version = std::string(b, e);
break;
6374 if (count != 3) {
return false; }
6377 static const std::set<std::string> methods{
6378 "GET",
"HEAD",
"POST",
"PUT",
"DELETE",
6379 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
6381 if (methods.find(
req.method) == methods.end()) {
return false; }
6383 if (
req.version !=
"HTTP/1.1" &&
req.version !=
"HTTP/1.0") {
return false; }
6387 for (
size_t i = 0; i <
req.target.size(); i++) {
6388 if (
req.target[i] ==
'#') {
6389 req.target.erase(i);
6395 [&](
const char *lhs_data, std::size_t lhs_size,
6396 const char *rhs_data, std::size_t rhs_size) {
6397 req.path = detail::decode_url(
6398 std::string(lhs_data, lhs_size), false);
6399 detail::parse_query_text(rhs_data, rhs_size, req.params);
6406inline bool Server::write_response(Stream &strm,
bool close_connection,
6407 Request &req, Response &res) {
6411 return write_response_core(strm, close_connection, req, res,
false);
6414inline bool Server::write_response_with_content(Stream &strm,
6415 bool close_connection,
6418 return write_response_core(strm, close_connection, req, res,
true);
6421inline bool Server::write_response_core(Stream &strm,
bool close_connection,
6422 const Request &req, Response &res,
6423 bool need_apply_ranges) {
6424 assert(res.status != -1);
6426 if (400 <= res.status && error_handler_ &&
6428 need_apply_ranges =
true;
6431 std::string content_type;
6432 std::string boundary;
6433 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
6436 if (close_connection ||
req.get_header_value(
"Connection") ==
"close") {
6437 res.set_header(
"Connection",
"close");
6439 std::string s =
"timeout=";
6443 res.set_header(
"Keep-Alive", s);
6446 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
6447 !res.has_header(
"Content-Type")) {
6448 res.set_header(
"Content-Type",
"text/plain");
6451 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
6452 !res.has_header(
"Content-Length")) {
6453 res.set_header(
"Content-Length",
"0");
6456 if (
req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges")) {
6457 res.set_header(
"Accept-Ranges",
"bytes");
6460 if (post_routing_handler_) { post_routing_handler_(req, res); }
6464 detail::BufferStream bstrm;
6466 if (!header_writer_(bstrm, res.headers)) {
return false; }
6469 auto &data = bstrm.get_buffer();
6475 if (
req.method !=
"HEAD") {
6476 if (!res.body.empty()) {
6480 }
else if (res.content_provider_) {
6481 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6482 res.content_provider_success_ =
true;
6490 if (logger_) { logger_(req, res); }
6496Server::write_content_with_provider(Stream &strm,
const Request &req,
6497 Response &res,
const std::string &boundary,
6498 const std::string &content_type) {
6499 auto is_shutting_down = [
this]() {
6503 if (res.content_length_ > 0) {
6504 if (
req.ranges.empty()) {
6506 res.content_length_, is_shutting_down);
6507 }
else if (
req.ranges.size() == 1) {
6509 req.ranges[0], res.content_length_);
6512 offset_and_length.first,
6513 offset_and_length.second, is_shutting_down);
6516 strm, req, res, boundary, content_type, res.content_length_,
6520 if (res.is_chunked_content_provider_) {
6523 std::unique_ptr<detail::compressor> compressor;
6525#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6526 compressor = detail::make_unique<detail::gzip_compressor>();
6529#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6530 compressor = detail::make_unique<detail::brotli_compressor>();
6533 compressor = detail::make_unique<detail::nocompressor>();
6535 assert(compressor !=
nullptr);
6538 is_shutting_down, *compressor);
6546inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
6547 MultipartFormDataMap::iterator cur;
6548 auto file_count = 0;
6549 if (read_content_core(
6552 [&](
const char *buf,
size_t n) {
6553 if (
req.body.size() + n >
req.body.max_size()) { return false; }
6554 req.body.append(buf, n);
6558 [&](
const MultipartFormData &file) {
6562 cur =
req.files.emplace(file.name, file);
6565 [&](
const char *buf,
size_t n) {
6566 auto &content = cur->second.content;
6567 if (content.size() + n > content.max_size()) {
return false; }
6568 content.append(buf, n);
6571 const auto &content_type =
req.get_header_value(
"Content-Type");
6572 if (!content_type.find(
"application/x-www-form-urlencoded")) {
6584inline bool Server::read_content_with_content_receiver(
6588 return read_content_core(strm, req, res, std::move(receiver),
6589 std::move(multipart_header),
6590 std::move(multipart_receiver));
6594Server::read_content_core(Stream &strm, Request &req, Response &res,
6598 detail::MultipartFormDataParser multipart_form_data_parser;
6601 if (
req.is_multipart_form_data()) {
6602 const auto &content_type =
req.get_header_value(
"Content-Type");
6603 std::string boundary;
6609 multipart_form_data_parser.set_boundary(std::move(boundary));
6622 return multipart_form_data_parser.parse(buf, n, multipart_receiver,
6626 out = [receiver](
const char *buf,
size_t n,
uint64_t ,
6627 uint64_t ) {
return receiver(buf, n); };
6630 if (
req.method ==
"DELETE" && !
req.has_header(
"Content-Length")) {
6639 if (
req.is_multipart_form_data()) {
6640 if (!multipart_form_data_parser.is_valid()) {
6649inline bool Server::handle_file_request(
const Request &req, Response &res,
6651 for (
const auto &entry : base_dirs_) {
6653 if (!
req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
6654 std::string sub_path =
"/" +
req.path.substr(entry.mount_point.size());
6656 auto path = entry.base_dir + sub_path;
6657 if (path.back() ==
'/') { path +=
"index.html"; }
6659 detail::FileStat stat(path);
6661 if (stat.is_dir()) {
6666 if (stat.is_file()) {
6667 for (
const auto &kv : entry.headers) {
6668 res.set_header(kv.first, kv.second);
6671 auto mm = std::make_shared<detail::mmap>(path.c_str());
6672 if (!mm->is_open()) {
return false; }
6674 res.set_content_provider(
6677 default_file_mimetype_),
6678 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
6679 sink.write(mm->data() + offset, length);
6683 if (!head && file_request_handler_) {
6684 file_request_handler_(req, res);
6696Server::create_server_socket(
const std::string &host,
int port,
6700 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
6701 ipv6_v6only_, std::move(socket_options),
6702 [](
socket_t sock,
struct addrinfo &
ai,
bool & ) ->
bool {
6703 if (::bind(sock,
ai.ai_addr,
static_cast<socklen_t
>(
ai.ai_addrlen))) {
6711inline int Server::bind_internal(
const std::string &host,
int port,
6713 if (is_decommisioned) {
return -1; }
6717 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
6721 struct sockaddr_storage addr;
6722 socklen_t addr_len =
sizeof(addr);
6723 if (getsockname(
svr_sock_,
reinterpret_cast<struct sockaddr *
>(&addr),
6727 if (addr.ss_family == AF_INET) {
6728 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&addr)->sin_port);
6729 }
else if (addr.ss_family == AF_INET6) {
6730 return ntohs(
reinterpret_cast<struct sockaddr_in6 *
>(&addr)->sin6_port);
6739inline bool Server::listen_internal() {
6740 if (is_decommisioned) {
return false; }
6744 auto se = detail::scope_exit([&]() { is_running_ =
false; });
6756 task_queue->on_idle();
6767#elif defined SOCK_CLOEXEC
6774 if (errno == EMFILE) {
6777 std::this_thread::sleep_for(std::chrono::microseconds{1});
6779 }
else if (errno == EINTR || errno == EAGAIN) {
6795 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6796 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6801 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6802 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6810 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6811 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6816 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6817 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6821 if (!task_queue->enqueue(
6822 [
this, sock]() { process_and_close_socket(sock); })) {
6828 task_queue->shutdown();
6831 is_decommisioned = !ret;
6835inline bool Server::run_cycle_once() {
6836 if(is_decommisioned) {
6842 auto se = detail::scope_exit([&]() { is_running_ =
false; });
6854 task_queue->on_idle();
6865#elif defined SOCK_CLOEXEC
6872 if(errno == EMFILE) {
6875 std::this_thread::sleep_for(std::chrono::microseconds{ 1 });
6877 }
else if(errno == EINTR || errno == EAGAIN) {
6893 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6894 reinterpret_cast<const char*
>(&timeout),
sizeof(timeout));
6899 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6900 reinterpret_cast<const void*
>(&tv),
sizeof(tv));
6908 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6909 reinterpret_cast<const char*
>(&timeout),
sizeof(timeout));
6914 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6915 reinterpret_cast<const void*
>(&tv),
sizeof(tv));
6919 if(!task_queue->enqueue(
6920 [
this, sock]() { process_and_close_socket(sock); })) {
6926 task_queue->shutdown();
6929 is_decommisioned = !ret;
6933inline bool Server::routing(Request &req, Response &res, Stream &strm) {
6934 if (pre_routing_handler_ &&
6940 auto is_head_request =
req.method ==
"HEAD";
6941 if ((
req.method ==
"GET" || is_head_request) &&
6942 handle_file_request(req, res, is_head_request)) {
6949 ContentReader reader(
6951 return read_content_with_content_receiver(
6952 strm, req, res, std::move(receiver),
nullptr,
nullptr);
6955 return read_content_with_content_receiver(strm, req, res,
nullptr,
6957 std::move(receiver));
6960 if (
req.method ==
"POST") {
6961 if (dispatch_request_for_content_reader(
6962 req, res, std::move(reader),
6963 post_handlers_for_content_reader_)) {
6966 }
else if (
req.method ==
"PUT") {
6967 if (dispatch_request_for_content_reader(
6968 req, res, std::move(reader),
6969 put_handlers_for_content_reader_)) {
6972 }
else if (
req.method ==
"PATCH") {
6973 if (dispatch_request_for_content_reader(
6974 req, res, std::move(reader),
6975 patch_handlers_for_content_reader_)) {
6978 }
else if (
req.method ==
"DELETE") {
6979 if (dispatch_request_for_content_reader(
6980 req, res, std::move(reader),
6981 delete_handlers_for_content_reader_)) {
6988 if (!read_content(strm, req, res)) {
return false; }
6992 if (
req.method ==
"GET" ||
req.method ==
"HEAD") {
6993 return dispatch_request(req, res, get_handlers_);
6994 }
else if (
req.method ==
"POST") {
6995 return dispatch_request(req, res, post_handlers_);
6996 }
else if (
req.method ==
"PUT") {
6997 return dispatch_request(req, res, put_handlers_);
6998 }
else if (
req.method ==
"DELETE") {
6999 return dispatch_request(req, res, delete_handlers_);
7000 }
else if (
req.method ==
"OPTIONS") {
7001 return dispatch_request(req, res, options_handlers_);
7002 }
else if (
req.method ==
"PATCH") {
7003 return dispatch_request(req, res, patch_handlers_);
7010inline bool Server::dispatch_request(Request &req, Response &res,
7011 const Handlers &handlers)
const {
7012 for (
const auto &x : handlers) {
7013 const auto &matcher =
x.first;
7014 const auto &handler =
x.second;
7016 if (matcher->match(req)) {
7024inline void Server::apply_ranges(
const Request &req, Response &res,
7025 std::string &content_type,
7026 std::string &boundary)
const {
7028 auto it = res.headers.find(
"Content-Type");
7029 if (it != res.headers.end()) {
7030 content_type = it->second;
7031 res.headers.erase(it);
7036 res.set_header(
"Content-Type",
7037 "multipart/byteranges; boundary=" + boundary);
7042 if (res.body.empty()) {
7043 if (res.content_length_ > 0) {
7046 length = res.content_length_;
7047 }
else if (
req.ranges.size() == 1) {
7049 req.ranges[0], res.content_length_);
7051 length = offset_and_length.second;
7054 offset_and_length, res.content_length_);
7055 res.set_header(
"Content-Range", content_range);
7058 req, boundary, content_type, res.content_length_);
7060 res.set_header(
"Content-Length", std::to_string(length));
7062 if (res.content_provider_) {
7063 if (res.is_chunked_content_provider_) {
7064 res.set_header(
"Transfer-Encoding",
"chunked");
7066 res.set_header(
"Content-Encoding",
"gzip");
7068 res.set_header(
"Content-Encoding",
"br");
7076 }
else if (
req.ranges.size() == 1) {
7077 auto offset_and_length =
7079 auto offset = offset_and_length.first;
7080 auto length = offset_and_length.second;
7083 offset_and_length, res.body.size());
7084 res.set_header(
"Content-Range", content_range);
7086 assert(offset + length <= res.body.size());
7087 res.body = res.body.substr(offset, length);
7091 res.body.size(), data);
7092 res.body.swap(data);
7096 std::unique_ptr<detail::compressor> compressor;
7097 std::string content_encoding;
7100#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7101 compressor = detail::make_unique<detail::gzip_compressor>();
7102 content_encoding =
"gzip";
7105#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7106 compressor = detail::make_unique<detail::brotli_compressor>();
7107 content_encoding =
"br";
7112 std::string compressed;
7113 if (compressor->compress(res.body.data(), res.body.size(),
true,
7114 [&](
const char *data,
size_t data_len) {
7115 compressed.append(data, data_len);
7118 res.body.swap(compressed);
7119 res.set_header(
"Content-Encoding", content_encoding);
7124 auto length = std::to_string(res.body.size());
7125 res.set_header(
"Content-Length", length);
7129inline bool Server::dispatch_request_for_content_reader(
7130 Request &req, Response &res, ContentReader content_reader,
7131 const HandlersForContentReader &handlers)
const {
7132 for (
const auto &x : handlers) {
7133 const auto &matcher =
x.first;
7134 const auto &handler =
x.second;
7136 if (matcher->match(req)) {
7137 handler(req, res, content_reader);
7146 int remote_port,
const std::string &local_addr,
7147 int local_port,
bool close_connection,
7148 bool &connection_closed,
7149 const std::function<
void(
Request &)> &setup_request) {
7150 std::array<char, 2048> buf{};
7155 if (!line_reader.
getline()) {
return false; }
7161 res.
headers = default_headers_;
7166#ifndef CPPHTTPLIB_USE_POLL
7168 if (strm.
socket() >= FD_SETSIZE) {
7172 return write_response(strm, close_connection, req, res);
7182 return write_response(strm, close_connection, req, res);
7186 if (!parse_request_line(line_reader.
ptr(), req) ||
7189 return write_response(strm, close_connection, req, res);
7192 if (req.get_header_value(
"Connection") ==
"close") {
7193 connection_closed =
true;
7196 if (req.version ==
"HTTP/1.0" &&
7197 req.get_header_value(
"Connection") !=
"Keep-Alive") {
7198 connection_closed =
true;
7201 req.remote_addr = remote_addr;
7202 req.remote_port = remote_port;
7203 req.set_header(
"REMOTE_ADDR", req.remote_addr);
7204 req.set_header(
"REMOTE_PORT", std::to_string(req.remote_port));
7206 req.local_addr = local_addr;
7207 req.local_port = local_port;
7208 req.set_header(
"LOCAL_ADDR", req.local_addr);
7209 req.set_header(
"LOCAL_PORT", std::to_string(req.local_port));
7211 if (req.has_header(
"Range")) {
7212 const auto &range_header_value = req.get_header_value(
"Range");
7215 return write_response(strm, close_connection, req, res);
7219 if (setup_request) { setup_request(req); }
7221 if (req.get_header_value(
"Expect") ==
"100-continue") {
7223 if (expect_100_continue_handler_) {
7224 status = expect_100_continue_handler_(req, res);
7233 connection_closed =
true;
7234 return write_response(strm,
true, req, res);
7239 auto routed =
false;
7240#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7241 routed = routing(req, res, strm);
7244 routed = routing(req, res, strm);
7245 }
catch (std::exception &e) {
7246 if (exception_handler_) {
7247 auto ep = std::current_exception();
7248 exception_handler_(req, res, ep);
7254 for (
size_t i = 0; s[i]; i++) {
7256 case '\r': val +=
"\\r";
break;
7257 case '\n': val +=
"\\n";
break;
7258 default: val += s[i];
break;
7264 if (exception_handler_) {
7265 auto ep = std::current_exception();
7266 exception_handler_(req, res, ep);
7285 return write_response(strm, close_connection, req, res);
7291 auto mm = std::make_shared<detail::mmap>(path.c_str());
7292 if (!mm->is_open()) {
7297 return write_response(strm, close_connection, req, res);
7301 if (content_type.empty()) {
7303 path, file_extension_and_mimetype_map_, default_file_mimetype_);
7307 mm->size(), content_type,
7308 [mm](
size_t offset,
size_t length,
DataSink &sink) ->
bool {
7309 sink.write(mm->data() + offset, length);
7314 return write_response_with_content(strm, close_connection, req, res);
7318 return write_response(strm, close_connection, req, res);
7324inline bool Server::process_and_close_socket(
socket_t sock) {
7325 std::string remote_addr;
7326 int remote_port = 0;
7329 std::string local_addr;
7337 [&](
Stream &strm,
bool close_connection,
bool &connection_closed) {
7339 local_port, close_connection, connection_closed,
7356 const std::string &client_cert_path,
7357 const std::string &client_key_path)
7358 : host_(
detail::escape_abstract_namespace_unix_domain(host)), port_(port),
7359 host_and_port_(adjust_host_string(host_) +
":" +
std::
to_string(port)),
7360 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
7381#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7382 digest_auth_username_ = rhs.digest_auth_username_;
7383 digest_auth_password_ = rhs.digest_auth_password_;
7400#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7401 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7402 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7404#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7405 ca_cert_file_path_ = rhs.ca_cert_file_path_;
7406 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7407 ca_cert_store_ = rhs.ca_cert_store_;
7409#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7410 server_certificate_verification_ = rhs.server_certificate_verification_;
7411 server_hostname_verification_ = rhs.server_hostname_verification_;
7412 server_certificate_verifier_ = rhs.server_certificate_verifier_;
7417inline socket_t ClientImpl::create_client_socket(
Error &error)
const {
7429 if (it !=
addr_map_.end()) { ip = it->second; }
7440 auto sock = create_client_socket(error);
7470#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7478inline bool ClientImpl::read_response_line(
Stream &strm,
const Request &req,
7480 std::array<char, 2048> buf{};
7482 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7484 if (!line_reader.getline()) {
return false; }
7486#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7487 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7489 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7493 if (!std::regex_match(line_reader.ptr(), m, re)) {
7494 return req.method ==
"CONNECT";
7496 res.
version = std::string(m[1]);
7497 res.
status = std::stoi(std::string(m[2]));
7498 res.
reason = std::string(m[3]);
7502 if (!line_reader.getline()) {
return false; }
7503 if (!line_reader.getline()) {
return false; }
7505 if (!std::regex_match(line_reader.ptr(), m, re)) {
return false; }
7506 res.
version = std::string(m[1]);
7507 res.
status = std::stoi(std::string(m[2]));
7508 res.
reason = std::string(m[3]);
7515 std::lock_guard<std::recursive_mutex> request_mutex_guard(
request_mutex_);
7516 auto ret = send_(req, res, error);
7519 ret = send_(req, res, error);
7524#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7525inline bool ClientImpl::is_ssl_peer_could_be_closed(SSL *ssl)
const {
7527 return !SSL_peek(ssl, buf, 1) &&
7528 SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7532inline bool ClientImpl::send_(Request &req, Response &res,
Error &error) {
7540 auto is_alive =
false;
7544#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7545 if (is_alive && is_ssl()) {
7546 if (is_ssl_peer_could_be_closed(
socket_.ssl)) {
7557 const bool shutdown_gracefully =
false;
7567#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7570 auto &scli =
static_cast<SSLClient &
>(*this);
7572 auto success =
false;
7573 if (!scli.connect_with_proxy(
socket_, res, success, error)) {
7578 if (!scli.initialize_ssl(
socket_, error)) {
return false; }
7594 if (
req.headers.find(
header.first) ==
req.headers.end()) {
7602 auto se = detail::scope_exit([&]() {
7619 ret = process_socket(
socket_, [&](Stream &strm) {
7620 return handle_request(strm, req, res, close_connection, error);
7632 return send_(std::move(req2));
7636 auto res = detail::make_unique<Response>();
7638 auto ret =
send(req, *res, error);
7639 return Result{ret ? std::move(res) : nullptr, error,
std::move(req.headers)};
7642inline bool ClientImpl::handle_request(Stream &strm, Request &req,
7643 Response &res,
bool close_connection,
7645 if (req.path.empty()) {
7650 auto req_save =
req;
7659 req.path = req_save.path;
7664 if (!ret) {
return false; }
7666 if (res.get_header_value(
"Connection") ==
"close" ||
7667 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
7683 ret = redirect(req, res, error);
7686#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7689 req.authorization_count_ < 5) {
7691 const auto &username =
7692 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
7693 const auto &password =
7694 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
7696 if (!username.empty() && !password.empty()) {
7697 std::map<std::string, std::string> auth;
7699 Request new_req =
req;
7700 new_req.authorization_count_ += 1;
7701 new_req.headers.erase(is_proxy ?
"Proxy-Authorization"
7703 new_req.headers.insert(detail::make_digest_authentication_header(
7705 username, password, is_proxy));
7709 ret =
send(new_req, new_res, error);
7710 if (ret) { res = new_res; }
7719inline bool ClientImpl::redirect(Request &req, Response &res,
Error &error) {
7720 if (
req.redirect_count_ == 0) {
7725 auto location = res.get_header_value(
"location");
7726 if (
location.empty()) {
return false; }
7728 const static std::regex re(
7729 R
"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7732 if (!std::regex_match(location, m, re)) {
return false; }
7734 auto scheme = is_ssl() ?
"https" :
"http";
7736 auto next_scheme =
m[1].str();
7737 auto next_host =
m[2].str();
7738 if (next_host.empty()) { next_host =
m[3].str(); }
7739 auto port_str =
m[4].str();
7740 auto next_path =
m[5].str();
7741 auto next_query =
m[6].str();
7743 auto next_port =
port_;
7744 if (!port_str.empty()) {
7745 next_port = std::stoi(port_str);
7746 }
else if (!next_scheme.empty()) {
7747 next_port = next_scheme ==
"https" ? 443 : 80;
7750 if (next_scheme.empty()) { next_scheme = scheme; }
7751 if (next_host.empty()) { next_host =
host_; }
7752 if (next_path.empty()) { next_path =
"/"; }
7756 if (next_scheme == scheme && next_host ==
host_ && next_port ==
port_) {
7759 if (next_scheme ==
"https") {
7760#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7761 SSLClient cli(next_host, next_port);
7762 cli.copy_settings(*
this);
7763 if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
7770 cli.copy_settings(*
this);
7778 Error &error)
const {
7779 auto is_shutting_down = []() {
return false; };
7781 if (req.is_chunked_content_provider_) {
7783 std::unique_ptr<detail::compressor> compressor;
7784#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7786 compressor = detail::make_unique<detail::gzip_compressor>();
7790 compressor = detail::make_unique<detail::nocompressor>();
7794 is_shutting_down, *compressor, error);
7797 req.content_length_, is_shutting_down, error);
7801inline bool ClientImpl::write_request(
Stream &strm,
Request &req,
7802 bool close_connection,
Error &error) {
7804 if (close_connection) {
7805 if (!req.has_header(
"Connection")) {
7806 req.set_header(
"Connection",
"close");
7810 if (!
req.has_header(
"Host")) {
7826 if (!
req.has_header(
"Accept")) {
req.set_header(
"Accept",
"*/*"); }
7828 if (!
req.content_receiver) {
7829 if (!
req.has_header(
"Accept-Encoding")) {
7830 std::string accept_encoding;
7831#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7832 accept_encoding =
"br";
7834#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7835 if (!accept_encoding.empty()) { accept_encoding +=
", "; }
7836 accept_encoding +=
"gzip, deflate";
7838 req.set_header(
"Accept-Encoding", accept_encoding);
7841#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
7842 if (!
req.has_header(
"User-Agent")) {
7844 req.set_header(
"User-Agent", agent);
7849 if (
req.body.empty()) {
7850 if (
req.content_provider_) {
7851 if (!
req.is_chunked_content_provider_) {
7852 if (!
req.has_header(
"Content-Length")) {
7853 auto length = std::to_string(
req.content_length_);
7854 req.set_header(
"Content-Length", length);
7858 if (
req.method ==
"POST" ||
req.method ==
"PUT" ||
7859 req.method ==
"PATCH") {
7860 req.set_header(
"Content-Length",
"0");
7864 if (!
req.has_header(
"Content-Type")) {
7865 req.set_header(
"Content-Type",
"text/plain");
7868 if (!
req.has_header(
"Content-Length")) {
7869 auto length = std::to_string(
req.body.size());
7870 req.set_header(
"Content-Length", length);
7875 if (!
req.has_header(
"Authorization")) {
7883 if (!
req.has_header(
"Proxy-Authorization")) {
7890 if (!
req.has_header(
"Authorization")) {
7897 if (!
req.has_header(
"Proxy-Authorization")) {
7905 detail::BufferStream bstrm;
7913 auto &data = bstrm.get_buffer();
7921 if (
req.body.empty()) {
7933inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
7934 Request &req,
const char *
body,
size_t content_length,
7937 const std::string &content_type,
Error &error) {
7938 if (!content_type.empty()) {
req.set_header(
"Content-Type", content_type); }
7940#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7941 if (
compress_) {
req.set_header(
"Content-Encoding",
"gzip"); }
7944#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7945 if (
compress_ && !content_provider_without_length) {
7947 detail::gzip_compressor compressor;
7949 if (content_provider) {
7954 data_sink.write = [&](
const char *data,
size_t data_len) ->
bool {
7956 auto last = offset + data_len == content_length;
7958 auto ret = compressor.compress(
7959 data, data_len, last,
7960 [&](
const char *compressed_data,
size_t compressed_data_len) {
7961 req.body.append(compressed_data, compressed_data_len);
7974 while (ok && offset < content_length) {
7975 if (!content_provider(offset, content_length - offset, data_sink)) {
7981 if (!compressor.compress(
body, content_length,
true,
7982 [&](
const char *data,
size_t data_len) {
7983 req.body.append(data, data_len);
7993 if (content_provider) {
7994 req.content_length_ = content_length;
7995 req.content_provider_ = std::move(content_provider);
7996 req.is_chunked_content_provider_ =
false;
7997 }
else if (content_provider_without_length) {
7998 req.content_length_ = 0;
7999 req.content_provider_ = detail::ContentProviderAdapter(
8000 std::move(content_provider_without_length));
8001 req.is_chunked_content_provider_ =
true;
8002 req.set_header(
"Transfer-Encoding",
"chunked");
8004 req.body.assign(
body, content_length);
8008 auto res = detail::make_unique<Response>();
8009 return send(req, *res, error) ? std::move(res) : nullptr;
8012inline Result ClientImpl::send_with_content_provider(
8013 const std::string &method,
const std::string &path,
const Headers &headers,
8016 const std::string &content_type,
Progress progress) {
8018 req.method = method;
8019 req.headers = headers;
8025 auto res = send_with_content_provider(
8026 req,
body, content_length, std::move(content_provider),
8027 std::move(content_provider_without_length), content_type, error);
8029 return Result{std::move(res),
error, std::move(
req.headers)};
8033ClientImpl::adjust_host_string(
const std::string &
host)
const {
8034 if (
host.find(
':') != std::string::npos) {
return "[" +
host +
"]"; }
8039 Response &res,
bool close_connection,
8042 if (!write_request(strm, req, close_connection, error)) {
return false; }
8044#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8047 if (!is_proxy_enabled) {
8048 if (is_ssl_peer_could_be_closed(
socket_.ssl)) {
8057 if (!read_response_line(strm, req, res) ||
8065 req.method !=
"CONNECT") {
8068 if (req.response_handler && !redirect) {
8069 if (!req.response_handler(res)) {
8076 req.content_receiver
8079 if (redirect) {
return true; }
8080 auto ret = req.content_receiver(buf, n, off, len);
8085 [&](
const char *buf,
size_t n,
uint64_t ,
8087 if (res.
body.size() + n > res.
body.max_size()) {
8090 res.
body.append(buf, n);
8095 if (!req.progress || redirect) {
return true; }
8096 auto ret = req.progress(current, total);
8102 if (!req.content_receiver) {
8104 res.
body.max_size());
8105 if (len > 0) { res.
body.reserve(len); }
8111 dummy_status, std::move(progress), std::move(out),
8127 size_t cur_item = 0;
8128 size_t cur_start = 0;
8131 return [&, cur_item, cur_start](
size_t offset,
8133 if (!offset && !items.empty()) {
8136 }
else if (cur_item < provider_items.size()) {
8139 provider_items[cur_item], boundary);
8140 offset += begin.size();
8146 auto has_data =
true;
8147 cur_sink.write = sink.write;
8148 cur_sink.done = [&]() { has_data =
false; };
8150 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
8169ClientImpl::process_socket(
const Socket &
socket,
8170 std::function<
bool(Stream &strm)> callback) {
8176inline bool ClientImpl::is_ssl()
const {
return false; }
8183 return Get(path,
Headers(), std::move(progress));
8195 req.headers = headers;
8196 req.progress = std::move(progress);
8198 return send_(std::move(req));
8203 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
nullptr);
8209 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
8210 std::move(progress));
8215 return Get(path, headers,
nullptr, std::move(content_receiver),
nullptr);
8221 return Get(path, headers,
nullptr, std::move(content_receiver),
8222 std::move(progress));
8228 return Get(path,
Headers(), std::move(response_handler),
8229 std::move(content_receiver),
nullptr);
8235 return Get(path, headers, std::move(response_handler),
8236 std::move(content_receiver),
nullptr);
8243 return Get(path,
Headers(), std::move(response_handler),
8244 std::move(content_receiver), std::move(progress));
8254 req.headers = headers;
8255 req.response_handler = std::move(response_handler);
8256 req.content_receiver =
8257 [content_receiver](
const char *data,
size_t data_length,
8259 return content_receiver(data, data_length);
8261 req.progress = std::move(progress);
8263 return send_(std::move(req));
8268 if (params.empty()) {
return Get(path, headers); }
8271 return Get(path_with_query, headers, std::move(progress));
8278 return Get(path, params, headers,
nullptr, std::move(content_receiver),
8279 std::move(progress));
8287 if (params.empty()) {
8288 return Get(path, headers, std::move(response_handler),
8289 std::move(content_receiver), std::move(progress));
8293 return Get(path_with_query, headers, std::move(response_handler),
8294 std::move(content_receiver), std::move(progress));
8304 req.method =
"HEAD";
8305 req.headers = headers;
8308 return send_(std::move(req));
8312 return Post(path, std::string(), std::string());
8317 return Post(path, headers,
nullptr, 0, std::string());
8321 size_t content_length,
8322 const std::string &content_type) {
8323 return Post(path,
Headers(),
body, content_length, content_type,
nullptr);
8327 const char *
body,
size_t content_length,
8328 const std::string &content_type) {
8329 return send_with_content_provider(
"POST", path, headers,
body, content_length,
8330 nullptr,
nullptr, content_type,
nullptr);
8334 const char *
body,
size_t content_length,
8335 const std::string &content_type,
8337 return send_with_content_provider(
"POST", path, headers,
body, content_length,
8338 nullptr,
nullptr, content_type, progress);
8342 const std::string &content_type) {
8347 const std::string &content_type,
8353 const std::string &
body,
8354 const std::string &content_type) {
8355 return send_with_content_provider(
"POST", path, headers,
body.data(),
8356 body.size(),
nullptr,
nullptr, content_type,
8361 const std::string &
body,
8362 const std::string &content_type,
8364 return send_with_content_provider(
"POST", path, headers,
body.data(),
8365 body.size(),
nullptr,
nullptr, content_type,
8375 const std::string &content_type) {
8376 return Post(path,
Headers(), content_length, std::move(content_provider),
8382 const std::string &content_type) {
8383 return Post(path,
Headers(), std::move(content_provider), content_type);
8387 size_t content_length,
8389 const std::string &content_type) {
8390 return send_with_content_provider(
"POST", path, headers,
nullptr,
8391 content_length, std::move(content_provider),
8392 nullptr, content_type,
nullptr);
8397 const std::string &content_type) {
8398 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr,
8399 std::move(content_provider), content_type,
8406 return Post(path, headers, query,
"application/x-www-form-urlencoded");
8412 return Post(path, headers, query,
"application/x-www-form-urlencoded",
8424 const auto &content_type =
8427 return Post(path, headers,
body, content_type);
8432 const std::string &boundary) {
8437 const auto &content_type =
8440 return Post(path, headers,
body, content_type);
8448 const auto &content_type =
8450 return send_with_content_provider(
8451 "POST", path, headers,
nullptr, 0,
nullptr,
8452 get_multipart_content_provider(boundary, items, provider_items),
8453 content_type,
nullptr);
8457 return Put(path, std::string(), std::string());
8461 size_t content_length,
8462 const std::string &content_type) {
8467 const char *
body,
size_t content_length,
8468 const std::string &content_type) {
8469 return send_with_content_provider(
"PUT", path, headers,
body, content_length,
8470 nullptr,
nullptr, content_type,
nullptr);
8474 const char *
body,
size_t content_length,
8475 const std::string &content_type,
8477 return send_with_content_provider(
"PUT", path, headers,
body, content_length,
8478 nullptr,
nullptr, content_type, progress);
8482 const std::string &content_type) {
8487 const std::string &content_type,
8493 const std::string &
body,
8494 const std::string &content_type) {
8495 return send_with_content_provider(
"PUT", path, headers,
body.data(),
8496 body.size(),
nullptr,
nullptr, content_type,
8501 const std::string &
body,
8502 const std::string &content_type,
8504 return send_with_content_provider(
"PUT", path, headers,
body.data(),
8505 body.size(),
nullptr,
nullptr, content_type,
8511 const std::string &content_type) {
8512 return Put(path,
Headers(), content_length, std::move(content_provider),
8518 const std::string &content_type) {
8519 return Put(path,
Headers(), std::move(content_provider), content_type);
8523 size_t content_length,
8525 const std::string &content_type) {
8526 return send_with_content_provider(
"PUT", path, headers,
nullptr,
8527 content_length, std::move(content_provider),
8528 nullptr, content_type,
nullptr);
8533 const std::string &content_type) {
8534 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr,
8535 std::move(content_provider), content_type,
8546 return Put(path, headers, query,
"application/x-www-form-urlencoded");
8552 return Put(path, headers, query,
"application/x-www-form-urlencoded",
8564 const auto &content_type =
8567 return Put(path, headers,
body, content_type);
8572 const std::string &boundary) {
8577 const auto &content_type =
8580 return Put(path, headers,
body, content_type);
8588 const auto &content_type =
8590 return send_with_content_provider(
8591 "PUT", path, headers,
nullptr, 0,
nullptr,
8592 get_multipart_content_provider(boundary, items, provider_items),
8593 content_type,
nullptr);
8596 return Patch(path, std::string(), std::string());
8600 size_t content_length,
8601 const std::string &content_type) {
8606 size_t content_length,
8607 const std::string &content_type,
8609 return Patch(path,
Headers(),
body, content_length, content_type, progress);
8613 const char *
body,
size_t content_length,
8614 const std::string &content_type) {
8615 return Patch(path, headers,
body, content_length, content_type,
nullptr);
8619 const char *
body,
size_t content_length,
8620 const std::string &content_type,
8622 return send_with_content_provider(
"PATCH", path, headers,
body,
8623 content_length,
nullptr,
nullptr,
8624 content_type, progress);
8628 const std::string &
body,
8629 const std::string &content_type) {
8634 const std::string &
body,
8635 const std::string &content_type,
8641 const std::string &
body,
8642 const std::string &content_type) {
8643 return Patch(path, headers,
body, content_type,
nullptr);
8647 const std::string &
body,
8648 const std::string &content_type,
8650 return send_with_content_provider(
"PATCH", path, headers,
body.data(),
8651 body.size(),
nullptr,
nullptr, content_type,
8657 const std::string &content_type) {
8658 return Patch(path,
Headers(), content_length, std::move(content_provider),
8664 const std::string &content_type) {
8665 return Patch(path,
Headers(), std::move(content_provider), content_type);
8669 size_t content_length,
8671 const std::string &content_type) {
8672 return send_with_content_provider(
"PATCH", path, headers,
nullptr,
8673 content_length, std::move(content_provider),
8674 nullptr, content_type,
nullptr);
8679 const std::string &content_type) {
8680 return send_with_content_provider(
"PATCH", path, headers,
nullptr, 0,
nullptr,
8681 std::move(content_provider), content_type,
8686 return Delete(path,
Headers(), std::string(), std::string());
8691 return Delete(path, headers, std::string(), std::string());
8695 size_t content_length,
8696 const std::string &content_type) {
8701 size_t content_length,
8702 const std::string &content_type,
8709 size_t content_length,
8710 const std::string &content_type) {
8711 return Delete(path, headers,
body, content_length, content_type,
nullptr);
8716 size_t content_length,
8717 const std::string &content_type,
8720 req.method =
"DELETE";
8721 req.headers = headers;
8723 req.progress = progress;
8725 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
8726 req.body.assign(
body, content_length);
8728 return send_(std::move(req));
8732 const std::string &
body,
8733 const std::string &content_type) {
8738 const std::string &
body,
8739 const std::string &content_type,
8747 const std::string &
body,
8748 const std::string &content_type) {
8749 return Delete(path, headers,
body.data(),
body.size(), content_type);
8754 const std::string &
body,
8755 const std::string &content_type,
8757 return Delete(path, headers,
body.data(),
body.size(), content_type,
8768 req.method =
"OPTIONS";
8769 req.headers = headers;
8772 return send_(std::move(req));
8825 const std::string &password) {
8834#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8835inline void ClientImpl::set_digest_auth(
const std::string &username,
8836 const std::string &password) {
8837 digest_auth_username_ = username;
8838 digest_auth_password_ = password;
8888 const std::string &password) {
8897#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8898inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
8899 const std::string &password) {
8900 proxy_digest_auth_username_ = username;
8901 proxy_digest_auth_password_ = password;
8904inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
8905 const std::string &ca_cert_dir_path) {
8906 ca_cert_file_path_ = ca_cert_file_path;
8907 ca_cert_dir_path_ = ca_cert_dir_path;
8910inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
8911 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
8912 ca_cert_store_ = ca_cert_store;
8916inline X509_STORE *ClientImpl::create_ca_cert_store(
const char *ca_cert,
8917 std::size_t size)
const {
8918 auto mem = BIO_new_mem_buf(ca_cert,
static_cast<int>(size));
8919 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
8920 if (!mem) {
return nullptr; }
8922 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
8923 if (!inf) {
return nullptr; }
8925 auto cts = X509_STORE_new();
8927 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
8928 auto itmp = sk_X509_INFO_value(inf, i);
8929 if (!itmp) {
continue; }
8931 if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
8932 if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
8936 sk_X509_INFO_pop_free(inf, X509_INFO_free);
8940inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
8941 server_certificate_verification_ =
enabled;
8944inline void ClientImpl::enable_server_hostname_verification(
bool enabled) {
8945 server_hostname_verification_ =
enabled;
8948inline void ClientImpl::set_server_certificate_verifier(
8949 std::function<
bool(SSL *ssl)> verifier) {
8950 server_certificate_verifier_ = verifier;
8961#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8964template <
typename U,
typename V>
8965inline SSL *ssl_new(
socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
8966 U SSL_connect_or_accept, V setup) {
8969 std::lock_guard<std::mutex> guard(ctx_mutex);
8974 set_nonblocking(sock,
true);
8975 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
8976 BIO_set_nbio(bio, 1);
8977 SSL_set_bio(ssl, bio, bio);
8979 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
8982 std::lock_guard<std::mutex> guard(ctx_mutex);
8988 BIO_set_nbio(bio, 0);
8995inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
socket_t sock,
8996 bool shutdown_gracefully) {
9001 if (shutdown_gracefully) {
9008 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
9009 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
9011 auto ret = SSL_shutdown(ssl);
9013 std::this_thread::sleep_for(std::chrono::milliseconds{100});
9014 ret = SSL_shutdown(ssl);
9019 std::lock_guard<std::mutex> guard(ctx_mutex);
9023template <
typename U>
9024bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL *ssl,
9025 U ssl_connect_or_accept,
9027 time_t timeout_usec) {
9029 while ((res = ssl_connect_or_accept(ssl)) != 1) {
9030 auto err = SSL_get_error(ssl, res);
9032 case SSL_ERROR_WANT_READ:
9033 if (
select_read(sock, timeout_sec, timeout_usec) > 0) {
continue; }
9035 case SSL_ERROR_WANT_WRITE:
9036 if (
select_write(sock, timeout_sec, timeout_usec) > 0) {
continue; }
9045template <
typename T>
9046inline bool process_server_socket_ssl(
9047 const std::atomic<socket_t> &svr_sock, SSL *ssl,
socket_t sock,
9048 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
9049 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
9050 time_t write_timeout_usec, T callback) {
9052 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
9053 [&](
bool close_connection,
bool &connection_closed) {
9054 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
9055 write_timeout_sec, write_timeout_usec);
9056 return callback(strm, close_connection, connection_closed);
9060template <
typename T>
9062process_client_socket_ssl(SSL *ssl,
socket_t sock, time_t read_timeout_sec,
9063 time_t read_timeout_usec, time_t write_timeout_sec,
9064 time_t write_timeout_usec, T callback) {
9065 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
9066 write_timeout_sec, write_timeout_usec);
9067 return callback(strm);
9074 OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
9079inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl,
9080 time_t read_timeout_sec,
9081 time_t read_timeout_usec,
9082 time_t write_timeout_sec,
9083 time_t write_timeout_usec)
9084 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
9085 read_timeout_usec_(read_timeout_usec),
9086 write_timeout_sec_(write_timeout_sec),
9087 write_timeout_usec_(write_timeout_usec) {
9088 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
9091inline SSLSocketStream::~SSLSocketStream() =
default;
9093inline bool SSLSocketStream::is_readable()
const {
9097inline bool SSLSocketStream::is_writable()
const {
9098 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9102inline ssize_t SSLSocketStream::read(
char *ptr,
size_t size) {
9103 if (SSL_pending(ssl_) > 0) {
9104 return SSL_read(ssl_, ptr,
static_cast<int>(size));
9105 }
else if (is_readable()) {
9106 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
9108 auto err = SSL_get_error(ssl_, ret);
9111 while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
9112 (err == SSL_ERROR_SYSCALL &&
9113 WSAGetLastError() == WSAETIMEDOUT))) {
9115 while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
9117 if (SSL_pending(ssl_) > 0) {
9118 return SSL_read(ssl_, ptr,
static_cast<int>(size));
9119 }
else if (is_readable()) {
9120 std::this_thread::sleep_for(std::chrono::microseconds{10});
9121 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
9122 if (ret >= 0) {
return ret; }
9123 err = SSL_get_error(ssl_, ret);
9134inline ssize_t SSLSocketStream::write(
const char *ptr,
size_t size) {
9135 if (is_writable()) {
9136 auto handle_size =
static_cast<int>(
9137 std::min<size_t>(size, (std::numeric_limits<int>::max)()));
9139 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
9141 auto err = SSL_get_error(ssl_, ret);
9144 while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
9145 (err == SSL_ERROR_SYSCALL &&
9146 WSAGetLastError() == WSAETIMEDOUT))) {
9148 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
9150 if (is_writable()) {
9151 std::this_thread::sleep_for(std::chrono::microseconds{10});
9152 ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
9153 if (ret >= 0) {
return ret; }
9154 err = SSL_get_error(ssl_, ret);
9165inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
9170inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
9175inline socket_t SSLSocketStream::socket()
const {
return sock_; }
9177static SSLInit sslinit_;
9182inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
9183 const char *client_ca_cert_file_path,
9184 const char *client_ca_cert_dir_path,
9185 const char *private_key_password) {
9186 ctx_ = SSL_CTX_new(TLS_server_method());
9189 SSL_CTX_set_options(ctx_,
9190 SSL_OP_NO_COMPRESSION |
9191 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9193 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9195 if (private_key_password !=
nullptr && (private_key_password[0] !=
'\0')) {
9196 SSL_CTX_set_default_passwd_cb_userdata(
9198 reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password)));
9201 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
9202 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
9204 SSL_CTX_check_private_key(ctx_) != 1) {
9207 }
else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
9208 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
9209 client_ca_cert_dir_path);
9212 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
9217inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
9218 X509_STORE *client_ca_cert_store) {
9219 ctx_ = SSL_CTX_new(TLS_server_method());
9222 SSL_CTX_set_options(ctx_,
9223 SSL_OP_NO_COMPRESSION |
9224 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9226 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9228 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
9229 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
9232 }
else if (client_ca_cert_store) {
9233 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9236 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
9241inline SSLServer::SSLServer(
9242 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
9243 ctx_ = SSL_CTX_new(TLS_method());
9245 if (!setup_ssl_ctx_callback(*ctx_)) {
9252inline SSLServer::~SSLServer() {
9253 if (ctx_) { SSL_CTX_free(ctx_); }
9256inline bool SSLServer::is_valid()
const {
return ctx_; }
9258inline SSL_CTX *SSLServer::ssl_context()
const {
return ctx_; }
9260inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
9261 X509_STORE *client_ca_cert_store) {
9263 std::lock_guard<std::mutex> guard(ctx_mutex_);
9265 SSL_CTX_use_certificate(ctx_, cert);
9266 SSL_CTX_use_PrivateKey(ctx_, private_key);
9268 if (client_ca_cert_store !=
nullptr) {
9269 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9273inline bool SSLServer::process_and_close_socket(
socket_t sock) {
9274 auto ssl = detail::ssl_new(
9275 sock, ctx_, ctx_mutex_,
9277 return detail::ssl_connect_or_accept_nonblocking(
9278 sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
9280 [](SSL * ) {
return true; });
9284 std::string remote_addr;
9285 int remote_port = 0;
9288 std::string local_addr;
9292 ret = detail::process_server_socket_ssl(
9293 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
9294 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9295 write_timeout_usec_,
9296 [&](Stream &strm,
bool close_connection,
bool &connection_closed) {
9297 return process_request(strm, remote_addr, remote_port, local_addr,
9298 local_port, close_connection,
9300 [&](Request &req) {
req.ssl = ssl; });
9305 const bool shutdown_gracefully = ret;
9306 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
9315inline SSLClient::SSLClient(
const std::string &host)
9318inline SSLClient::SSLClient(
const std::string &host,
int port)
9321inline SSLClient::SSLClient(
const std::string &host,
int port,
9322 const std::string &client_cert_path,
9323 const std::string &client_key_path,
9324 const std::string &private_key_password)
9325 : ClientImpl(
host, port, client_cert_path, client_key_path) {
9326 ctx_ = SSL_CTX_new(TLS_client_method());
9328 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9331 [&](
const char *b,
const char *e) {
9332 host_components_.emplace_back(b, e);
9335 if (!client_cert_path.empty() && !client_key_path.empty()) {
9336 if (!private_key_password.empty()) {
9337 SSL_CTX_set_default_passwd_cb_userdata(
9338 ctx_,
reinterpret_cast<void *
>(
9339 const_cast<char *
>(private_key_password.c_str())));
9342 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
9343 SSL_FILETYPE_PEM) != 1 ||
9344 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
9345 SSL_FILETYPE_PEM) != 1) {
9352inline SSLClient::SSLClient(
const std::string &host,
int port,
9353 X509 *client_cert, EVP_PKEY *client_key,
9354 const std::string &private_key_password)
9355 : ClientImpl(
host, port) {
9356 ctx_ = SSL_CTX_new(TLS_client_method());
9359 [&](
const char *b,
const char *e) {
9360 host_components_.emplace_back(b, e);
9363 if (client_cert !=
nullptr && client_key !=
nullptr) {
9364 if (!private_key_password.empty()) {
9365 SSL_CTX_set_default_passwd_cb_userdata(
9366 ctx_,
reinterpret_cast<void *
>(
9367 const_cast<char *
>(private_key_password.c_str())));
9370 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
9371 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
9378inline SSLClient::~SSLClient() {
9379 if (ctx_) { SSL_CTX_free(ctx_); }
9383 shutdown_ssl_impl(socket_,
true);
9386inline bool SSLClient::is_valid()
const {
return ctx_; }
9388inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
9389 if (ca_cert_store) {
9391 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
9393 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
9396 X509_STORE_free(ca_cert_store);
9401inline void SSLClient::load_ca_cert_store(
const char *ca_cert,
9403 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
9406inline long SSLClient::get_openssl_verify_result()
const {
9407 return verify_result_;
9410inline SSL_CTX *SSLClient::ssl_context()
const {
return ctx_; }
9412inline bool SSLClient::create_and_connect_socket(Socket &socket,
Error &error) {
9417inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
9418 bool &success,
Error &error) {
9422 socket.sock, read_timeout_sec_, read_timeout_usec_,
9423 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
9425 req2.method =
"CONNECT";
9426 req2.path = host_and_port_;
9427 return process_request(strm, req2, proxy_res, false, error);
9431 shutdown_ssl(socket,
true);
9439 if (!proxy_digest_auth_username_.empty() &&
9440 !proxy_digest_auth_password_.empty()) {
9441 std::map<std::string, std::string> auth;
9443 proxy_res = Response();
9445 socket.sock, read_timeout_sec_, read_timeout_usec_,
9446 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
9448 req3.method =
"CONNECT";
9449 req3.path = host_and_port_;
9450 req3.headers.insert(detail::make_digest_authentication_header(
9451 req3, auth, 1, detail::random_string(10),
9452 proxy_digest_auth_username_, proxy_digest_auth_password_,
9454 return process_request(strm, req3, proxy_res, false, error);
9458 shutdown_ssl(socket,
true);
9473 res = std::move(proxy_res);
9476 shutdown_ssl(socket,
true);
9485inline bool SSLClient::load_certs() {
9488 std::call_once(initialize_cert_, [&]() {
9489 std::lock_guard<std::mutex> guard(ctx_mutex_);
9490 if (!ca_cert_file_path_.empty()) {
9491 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
9495 }
else if (!ca_cert_dir_path_.empty()) {
9496 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr,
9497 ca_cert_dir_path_.c_str())) {
9501 auto loaded =
false;
9504 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9505#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9507 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
9510 if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
9517inline bool SSLClient::initialize_ssl(Socket &socket,
Error &error) {
9518 auto ssl = detail::ssl_new(
9519 socket.sock, ctx_, ctx_mutex_,
9521 if (server_certificate_verification_) {
9522 if (!load_certs()) {
9523 error = Error::SSLLoadingCerts;
9526 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
9529 if (!detail::ssl_connect_or_accept_nonblocking(
9530 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
9531 connection_timeout_usec_)) {
9532 error = Error::SSLConnection;
9536 if (server_certificate_verification_) {
9537 if (server_certificate_verifier_) {
9538 if (!server_certificate_verifier_(ssl2)) {
9543 verify_result_ = SSL_get_verify_result(ssl2);
9545 if (verify_result_ != X509_V_OK) {
9550 auto server_cert = SSL_get1_peer_certificate(ssl2);
9551 auto se = detail::scope_exit([&] { X509_free(server_cert); });
9553 if (server_cert ==
nullptr) {
9558 if (server_hostname_verification_) {
9559 if (!verify_host(server_cert)) {
9570#if defined(OPENSSL_IS_BORINGSSL)
9571 SSL_set_tlsext_host_name(ssl2, host_.c_str());
9575 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
9576 static_cast<void *
>(
const_cast<char *
>(host_.c_str())));
9591inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
9592 shutdown_ssl_impl(socket, shutdown_gracefully);
9595inline void SSLClient::shutdown_ssl_impl(Socket &socket,
9596 bool shutdown_gracefully) {
9598 assert(socket.ssl ==
nullptr);
9602 detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
9603 shutdown_gracefully);
9604 socket.ssl =
nullptr;
9606 assert(socket.ssl ==
nullptr);
9610SSLClient::process_socket(
const Socket &socket,
9611 std::function<
bool(Stream &strm)> callback) {
9613 return detail::process_client_socket_ssl(
9614 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
9615 write_timeout_sec_, write_timeout_usec_, std::move(callback));
9618inline bool SSLClient::is_ssl()
const {
return true; }
9620inline bool SSLClient::verify_host(X509 *server_cert)
const {
9642 return verify_host_with_subject_alt_name(server_cert) ||
9643 verify_host_with_common_name(server_cert);
9647SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
9650 auto type = GEN_DNS;
9652 struct in6_addr addr6{};
9653 struct in_addr addr{};
9654 size_t addr_len = 0;
9657 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
9659 addr_len =
sizeof(
struct in6_addr);
9660 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
9662 addr_len =
sizeof(
struct in_addr);
9666 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
9667 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
9670 auto dsn_matched =
false;
9671 auto ip_matched =
false;
9673 auto count = sk_GENERAL_NAME_num(alt_names);
9675 for (
decltype(count) i = 0; i <
count && !dsn_matched; i++) {
9676 auto val = sk_GENERAL_NAME_value(alt_names, i);
9677 if (
val->type == type) {
9679 reinterpret_cast<const char *
>(ASN1_STRING_get0_data(
val->d.ia5));
9680 auto name_len =
static_cast<size_t>(ASN1_STRING_length(
val->d.ia5));
9683 case GEN_DNS: dsn_matched = check_host_name(name, name_len);
break;
9686 if (!memcmp(&addr6, name, addr_len) ||
9687 !memcmp(&addr, name, addr_len)) {
9695 if (dsn_matched || ip_matched) { ret =
true; }
9698 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME) *
>(
9699 reinterpret_cast<const STACK_OF(GENERAL_NAME) *
>(alt_names)));
9703inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
9704 const auto subject_name = X509_get_subject_name(server_cert);
9706 if (subject_name !=
nullptr) {
9708 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
9709 name,
sizeof(name));
9711 if (name_len != -1) {
9712 return check_host_name(name,
static_cast<size_t>(name_len));
9719inline bool SSLClient::check_host_name(
const char *pattern,
9720 size_t pattern_len)
const {
9721 if (host_.size() == pattern_len && host_ == pattern) {
return true; }
9725 std::vector<std::string> pattern_components;
9727 [&](
const char *b,
const char *e) {
9728 pattern_components.emplace_back(b, e);
9731 if (host_components_.size() != pattern_components.size()) {
return false; }
9733 auto itr = pattern_components.begin();
9734 for (
const auto &h : host_components_) {
9736 if (p != h && p !=
"*") {
9737 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' &&
9738 !p.compare(0, p.size() - 1, h));
9739 if (!partial_match) {
return false; }
9750 :
Client(scheme_host_port,
std::string(),
std::string()) {}
9753 const std::string &client_cert_path,
9754 const std::string &client_key_path) {
9755 const static std::regex re(
9756 R
"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
9759 if (std::regex_match(scheme_host_port, m, re)) {
9760 auto scheme = m[1].str();
9762#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9763 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
9765 if (!scheme.empty() && scheme !=
"http") {
9767#ifndef CPPHTTPLIB_NO_EXCEPTIONS
9768 std::string msg =
"'" + scheme +
"' scheme is not supported.";
9769 throw std::invalid_argument(msg);
9774 auto is_ssl = scheme ==
"https";
9776 auto host = m[2].str();
9777 if (
host.empty()) {
host = m[3].str(); }
9779 auto port_str = m[4].str();
9780 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
9783#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9784 cli_ = detail::make_unique<SSLClient>(
host,
port, client_cert_path,
9789 cli_ = detail::make_unique<ClientImpl>(
host,
port, client_cert_path,
9795 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
9796 client_cert_path, client_key_path);
9804 const std::string &client_cert_path,
9805 const std::string &client_key_path)
9807 client_key_path)) {}
9812 return cli_ !=
nullptr && cli_->is_valid();
9817 return cli_->Get(path, headers);
9820 return cli_->Get(path, std::move(progress));
9824 return cli_->Get(path, headers, std::move(progress));
9828 return cli_->Get(path, std::move(content_receiver));
9832 return cli_->Get(path, headers, std::move(content_receiver));
9836 return cli_->Get(path, std::move(content_receiver), std::move(progress));
9840 return cli_->Get(path, headers, std::move(content_receiver),
9841 std::move(progress));
9846 return cli_->Get(path, std::move(response_handler),
9847 std::move(content_receiver));
9852 return cli_->Get(path, headers, std::move(response_handler),
9853 std::move(content_receiver));
9858 return cli_->Get(path, std::move(response_handler),
9859 std::move(content_receiver), std::move(progress));
9864 return cli_->Get(path, headers, std::move(response_handler),
9865 std::move(content_receiver), std::move(progress));
9869 return cli_->Get(path, params, headers, std::move(progress));
9874 return cli_->Get(path, params, headers, std::move(content_receiver),
9875 std::move(progress));
9881 return cli_->Get(path, params, headers, std::move(response_handler),
9882 std::move(content_receiver), std::move(progress));
9887 return cli_->Head(path, headers);
9892 return cli_->Post(path, headers);
9895 size_t content_length,
9896 const std::string &content_type) {
9897 return cli_->Post(path,
body, content_length, content_type);
9900 const char *
body,
size_t content_length,
9901 const std::string &content_type) {
9902 return cli_->Post(path, headers,
body, content_length, content_type);
9905 const char *
body,
size_t content_length,
9906 const std::string &content_type,
Progress progress) {
9907 return cli_->Post(path, headers,
body, content_length, content_type,
9911 const std::string &content_type) {
9912 return cli_->Post(path,
body, content_type);
9915 const std::string &content_type,
Progress progress) {
9916 return cli_->Post(path,
body, content_type, progress);
9919 const std::string &
body,
9920 const std::string &content_type) {
9921 return cli_->Post(path, headers,
body, content_type);
9924 const std::string &
body,
9925 const std::string &content_type,
Progress progress) {
9926 return cli_->Post(path, headers,
body, content_type, progress);
9930 const std::string &content_type) {
9931 return cli_->Post(path, content_length, std::move(content_provider),
9936 const std::string &content_type) {
9937 return cli_->Post(path, std::move(content_provider), content_type);
9940 size_t content_length,
9942 const std::string &content_type) {
9943 return cli_->Post(path, headers, content_length, std::move(content_provider),
9948 const std::string &content_type) {
9949 return cli_->Post(path, headers, std::move(content_provider), content_type);
9952 return cli_->Post(path, params);
9956 return cli_->Post(path, headers, params);
9960 return cli_->Post(path, headers, params, progress);
9964 return cli_->Post(path, items);
9968 return cli_->Post(path, headers, items);
9972 const std::string &boundary) {
9973 return cli_->Post(path, headers, items, boundary);
9979 return cli_->Post(path, headers, items, provider_items);
9983 size_t content_length,
9984 const std::string &content_type) {
9985 return cli_->Put(path,
body, content_length, content_type);
9988 const char *
body,
size_t content_length,
9989 const std::string &content_type) {
9990 return cli_->Put(path, headers,
body, content_length, content_type);
9993 const char *
body,
size_t content_length,
9994 const std::string &content_type,
Progress progress) {
9995 return cli_->Put(path, headers,
body, content_length, content_type, progress);
9998 const std::string &content_type) {
9999 return cli_->Put(path,
body, content_type);
10002 const std::string &content_type,
Progress progress) {
10003 return cli_->Put(path,
body, content_type, progress);
10006 const std::string &
body,
10007 const std::string &content_type) {
10008 return cli_->Put(path, headers,
body, content_type);
10011 const std::string &
body,
10012 const std::string &content_type,
Progress progress) {
10013 return cli_->Put(path, headers,
body, content_type, progress);
10017 const std::string &content_type) {
10018 return cli_->Put(path, content_length, std::move(content_provider),
10023 const std::string &content_type) {
10024 return cli_->Put(path, std::move(content_provider), content_type);
10027 size_t content_length,
10029 const std::string &content_type) {
10030 return cli_->Put(path, headers, content_length, std::move(content_provider),
10035 const std::string &content_type) {
10036 return cli_->Put(path, headers, std::move(content_provider), content_type);
10039 return cli_->Put(path, params);
10043 return cli_->Put(path, headers, params);
10047 return cli_->Put(path, headers, params, progress);
10051 return cli_->Put(path, items);
10055 return cli_->Put(path, headers, items);
10059 const std::string &boundary) {
10060 return cli_->Put(path, headers, items, boundary);
10066 return cli_->Put(path, headers, items, provider_items);
10069 return cli_->Patch(path);
10072 size_t content_length,
10073 const std::string &content_type) {
10074 return cli_->Patch(path,
body, content_length, content_type);
10077 size_t content_length,
10078 const std::string &content_type,
10080 return cli_->Patch(path,
body, content_length, content_type, progress);
10083 const char *
body,
size_t content_length,
10084 const std::string &content_type) {
10085 return cli_->Patch(path, headers,
body, content_length, content_type);
10088 const char *
body,
size_t content_length,
10089 const std::string &content_type,
10091 return cli_->Patch(path, headers,
body, content_length, content_type,
10095 const std::string &content_type) {
10096 return cli_->Patch(path,
body, content_type);
10099 const std::string &content_type,
10101 return cli_->Patch(path,
body, content_type, progress);
10104 const std::string &
body,
10105 const std::string &content_type) {
10106 return cli_->Patch(path, headers,
body, content_type);
10109 const std::string &
body,
10110 const std::string &content_type,
10112 return cli_->Patch(path, headers,
body, content_type, progress);
10116 const std::string &content_type) {
10117 return cli_->Patch(path, content_length, std::move(content_provider),
10122 const std::string &content_type) {
10123 return cli_->Patch(path, std::move(content_provider), content_type);
10126 size_t content_length,
10128 const std::string &content_type) {
10129 return cli_->Patch(path, headers, content_length, std::move(content_provider),
10134 const std::string &content_type) {
10135 return cli_->Patch(path, headers, std::move(content_provider), content_type);
10138 return cli_->Delete(path);
10141 return cli_->Delete(path, headers);
10144 size_t content_length,
10145 const std::string &content_type) {
10146 return cli_->Delete(path,
body, content_length, content_type);
10149 size_t content_length,
10150 const std::string &content_type,
10152 return cli_->Delete(path,
body, content_length, content_type, progress);
10155 const char *
body,
size_t content_length,
10156 const std::string &content_type) {
10157 return cli_->Delete(path, headers,
body, content_length, content_type);
10160 const char *
body,
size_t content_length,
10161 const std::string &content_type,
10163 return cli_->Delete(path, headers,
body, content_length, content_type,
10167 const std::string &content_type) {
10168 return cli_->Delete(path,
body, content_type);
10171 const std::string &content_type,
10173 return cli_->Delete(path,
body, content_type, progress);
10176 const std::string &
body,
10177 const std::string &content_type) {
10178 return cli_->Delete(path, headers,
body, content_type);
10181 const std::string &
body,
10182 const std::string &content_type,
10184 return cli_->Delete(path, headers,
body, content_type, progress);
10187 return cli_->Options(path);
10190 return cli_->Options(path, headers);
10194 return cli_->send(req, res, error);
10211 cli_->set_hostname_addr_map(std::move(addr_map));
10215 cli_->set_default_headers(std::move(headers));
10220 cli_->set_header_writer(writer);
10224 cli_->set_address_family(family);
10230 cli_->set_socket_options(std::move(socket_options));
10234 cli_->set_connection_timeout(sec, usec);
10238 cli_->set_read_timeout(sec, usec);
10242 cli_->set_write_timeout(sec, usec);
10246 const std::string &password) {
10247 cli_->set_basic_auth(username, password);
10250 cli_->set_bearer_token_auth(
token);
10252#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10253inline void Client::set_digest_auth(
const std::string &username,
10254 const std::string &password) {
10255 cli_->set_digest_auth(username, password);
10261 cli_->set_follow_location(on);
10271 cli_->set_interface(intf);
10278 const std::string &password) {
10279 cli_->set_proxy_basic_auth(username, password);
10282 cli_->set_proxy_bearer_token_auth(
token);
10284#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10285inline void Client::set_proxy_digest_auth(
const std::string &username,
10286 const std::string &password) {
10287 cli_->set_proxy_digest_auth(username, password);
10291#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10292inline void Client::enable_server_certificate_verification(
bool enabled) {
10293 cli_->enable_server_certificate_verification(enabled);
10296inline void Client::enable_server_hostname_verification(
bool enabled) {
10297 cli_->enable_server_hostname_verification(enabled);
10300inline void Client::set_server_certificate_verifier(
10301 std::function<
bool(SSL *ssl)> verifier) {
10302 cli_->set_server_certificate_verifier(verifier);
10307 cli_->set_logger(std::move(logger));
10310#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10311inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
10312 const std::string &ca_cert_dir_path) {
10313 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
10316inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
10318 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
10320 cli_->set_ca_cert_store(ca_cert_store);
10324inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
10325 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
10328inline long Client::get_openssl_verify_result()
const {
10330 return static_cast<SSLClient &
>(*cli_).get_openssl_verify_result();
10335inline SSL_CTX *Client::ssl_context()
const {
10336 if (is_ssl_) {
return static_cast<SSLClient &
>(*cli_).ssl_context(); }
10345#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
void set_read_timeout(time_t sec, time_t usec=0)
Result Post(const std::string &path)
Client(Client &&)=default
Result Put(const std::string &path)
void set_connection_timeout(time_t sec, time_t usec=0)
Result Delete(const std::string &path)
bool send(Request &req, Response &res, Error &error)
Result Options(const std::string &path)
void set_default_headers(Headers headers)
void set_proxy_bearer_token_auth(const std::string &token)
void set_hostname_addr_map(std::map< std::string, std::string > addr_map)
Client & operator=(Client &&)=default
void set_proxy_basic_auth(const std::string &username, const std::string &password)
void set_tcp_nodelay(bool on)
void set_keep_alive(bool on)
void set_interface(const std::string &intf)
void set_decompress(bool on)
size_t is_socket_open() const
void set_follow_location(bool on)
void set_proxy(const std::string &host, int port)
void set_write_timeout(time_t sec, time_t usec=0)
Client(const std::string &scheme_host_port)
void set_address_family(int family)
Result Head(const std::string &path)
Result Get(const std::string &path)
void set_bearer_token_auth(const std::string &token)
void set_url_encode(bool on)
void set_logger(Logger logger)
void set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Result Patch(const std::string &path)
void set_socket_options(SocketOptions socket_options)
void set_compress(bool on)
void set_basic_auth(const std::string &username, const std::string &password)
void set_proxy_basic_auth(const std::string &username, const std::string &password)
void set_socket_options(SocketOptions socket_options)
time_t write_timeout_sec_
Result Delete(const std::string &path)
void set_basic_auth(const std::string &username, const std::string &password)
size_t socket_requests_in_flight_
void close_socket(Socket &socket)
std::string proxy_bearer_token_auth_token_
bool write_content_with_provider(Stream &strm, const Request &req, Error &error) const
void set_decompress(bool on)
void set_proxy(const std::string &host, int port)
void set_interface(const std::string &intf)
time_t connection_timeout_sec_
void set_url_encode(bool on)
Result Patch(const std::string &path)
time_t read_timeout_usec_
void set_tcp_nodelay(bool on)
void set_proxy_bearer_token_auth(const std::string &token)
std::string basic_auth_password_
bool send(Request &req, Response &res, Error &error)
virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully)
void set_connection_timeout(time_t sec, time_t usec=0)
std::string bearer_token_auth_token_
void set_hostname_addr_map(std::map< std::string, std::string > addr_map)
void set_logger(Logger logger)
ClientImpl(const std::string &host)
void set_compress(bool on)
Result Get(const std::string &path)
void set_address_family(int family)
const std::string host_and_port_
std::string client_key_path_
Result Put(const std::string &path)
std::string client_cert_path_
void set_ipv6_v6only(bool on)
Result Post(const std::string &path)
time_t connection_timeout_usec_
std::string proxy_basic_auth_username_
void shutdown_socket(Socket &socket) const
std::function< ssize_t(Stream &, Headers &)> header_writer_
bool process_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error)
std::thread::id socket_requests_are_from_thread_
std::map< std::string, std::string > addr_map_
time_t write_timeout_usec_
std::string proxy_basic_auth_password_
void set_write_timeout(time_t sec, time_t usec=0)
void copy_settings(const ClientImpl &rhs)
void set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
std::recursive_mutex request_mutex_
Result Options(const std::string &path)
size_t is_socket_open() const
void set_default_headers(Headers headers)
void set_follow_location(bool on)
SocketOptions socket_options_
void set_keep_alive(bool on)
bool socket_should_be_closed_when_request_is_done_
Result Head(const std::string &path)
std::string basic_auth_username_
virtual bool create_and_connect_socket(Socket &socket, Error &error)
virtual bool is_valid() const
void set_read_timeout(time_t sec, time_t usec=0)
void set_bearer_token_auth(const std::string &token)
bool operator()(MultipartContentHeader header, ContentReceiver receiver) const
std::function< bool(MultipartContentHeader header, ContentReceiver receiver)> MultipartReader
bool operator()(ContentReceiver receiver) const
std::function< bool(ContentReceiver receiver)> Reader
ContentReader(Reader reader, MultipartReader multipart_reader)
MultipartReader multipart_reader_
std::function< bool(const char *data, size_t data_len)> write
DataSink(DataSink &&)=delete
DataSink(const DataSink &)=delete
DataSink & operator=(DataSink &&)=delete
DataSink & operator=(const DataSink &)=delete
std::function< void()> done
std::function< bool()> is_writable
std::function< void(const Headers &trailer)> done_with_trailer
bool has_request_header(const std::string &key) const
const Response & value() const
size_t get_request_header_value_count(const std::string &key) const
const Response & operator*() const
uint64_t get_request_header_value_u64(const std::string &key, uint64_t def=0, size_t id=0) const
bool operator==(std::nullptr_t) const
Result(std::unique_ptr< Response > &&res, Error err, Headers &&request_headers=Headers{})
bool operator!=(std::nullptr_t) const
const Response * operator->() const
std::string get_request_header_value(const std::string &key, const char *def="", size_t id=0) const
time_t read_timeout_usec_
Server & set_default_file_mimetype(const std::string &mime)
Server & Put(const std::string &pattern, Handler handler)
std::function< HandlerResponse(const Request &, Response &)> HandlerWithResponse
Server & Delete(const std::string &pattern, Handler handler)
Server & set_pre_routing_handler(HandlerWithResponse handler)
Server & set_write_timeout(time_t sec, time_t usec=0)
time_t idle_interval_usec_
size_t keep_alive_max_count_
time_t write_timeout_sec_
Server & set_error_handler(ErrorHandlerFunc &&handler)
bool listen(const std::string &host, int port, int socket_flags=0)
bool set_base_dir(const std::string &dir, const std::string &mount_point=std::string())
Server & set_tcp_nodelay(bool on)
std::function< void(const Request &, Response &, const ContentReader &content_reader)> HandlerWithContentReader
Server & set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Server & set_payload_max_length(size_t length)
Server & Options(const std::string &pattern, Handler handler)
virtual bool is_valid() const
int bind_to_any_port(const std::string &host, int socket_flags=0)
time_t idle_interval_sec_
bool bind_to_port(const std::string &host, int port, int socket_flags=0)
time_t write_timeout_usec_
time_t keep_alive_timeout_sec_
Server & Patch(const std::string &pattern, Handler handler)
Server & set_default_headers(Headers headers)
Server & Get(const std::string &pattern, Handler handler)
std::function< int(const Request &, Response &)> Expect100ContinueHandler
Server & set_file_request_handler(Handler handler)
std::function< void(const Request &, Response &)> Handler
std::atomic< socket_t > svr_sock_
void wait_until_ready() const
Server & set_post_routing_handler(Handler handler)
Server & Post(const std::string &pattern, Handler handler)
Server & set_idle_interval(time_t sec, time_t usec=0)
Server & set_address_family(int family)
std::function< void(const Request &, Response &, std::exception_ptr ep)> ExceptionHandler
bool remove_mount_point(const std::string &mount_point)
Server & set_keep_alive_timeout(time_t sec)
std::function< TaskQueue *(void)> new_task_queue
bool process_request(Stream &strm, const std::string &remote_addr, int remote_port, const std::string &local_addr, int local_port, bool close_connection, bool &connection_closed, const std::function< void(Request &)> &setup_request)
Server & set_logger(Logger logger)
bool set_mount_point(const std::string &mount_point, const std::string &dir, Headers headers=Headers())
Server & set_socket_options(SocketOptions socket_options)
Server & set_exception_handler(ExceptionHandler handler)
Server & set_expect_100_continue_handler(Expect100ContinueHandler handler)
Server & set_ipv6_v6only(bool on)
size_t payload_max_length_
Server & set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime)
bool start_server(const std::string &host, int port, int socket_flags=0)
Server & set_keep_alive_max_count(size_t count)
Server & set_read_timeout(time_t sec, time_t usec=0)
virtual ssize_t write(const char *ptr, size_t size)=0
virtual ssize_t read(char *ptr, size_t size)=0
virtual void get_remote_ip_and_port(std::string &ip, int &port) const =0
virtual void get_local_ip_and_port(std::string &ip, int &port) const =0
virtual bool is_writable() const =0
virtual ~Stream()=default
virtual socket_t socket() const =0
virtual bool is_readable() const =0
virtual bool enqueue(std::function< void()> fn)=0
virtual ~TaskQueue()=default
virtual void shutdown()=0
ThreadPool(size_t n, size_t mqr=0)
ThreadPool(const ThreadPool &)=delete
~ThreadPool() override=default
bool enqueue(std::function< void()> fn) override
void get_remote_ip_and_port(std::string &ip, int &port) const override
void get_local_ip_and_port(std::string &ip, int &port) const override
ssize_t write(const char *ptr, size_t size) override
~BufferStream() override=default
bool is_writable() const override
const std::string & get_buffer() const
socket_t socket() const override
bool is_readable() const override
ssize_t read(char *ptr, size_t size) override
bool operator()(size_t offset, size_t, DataSink &sink)
ContentProviderAdapter(ContentProviderWithoutLength &&content_provider)
virtual bool match(Request &request) const =0
virtual ~MatcherBase()=default
PathParamsMatcher(const std::string &pattern)
bool match(Request &request) const override
bool match(Request &request) const override
RegexMatcher(const std::string &pattern)
void get_remote_ip_and_port(std::string &ip, int &port) const override
socket_t socket() const override
bool is_readable() const override
ssize_t write(const char *ptr, size_t size) override
void get_local_ip_and_port(std::string &ip, int &port) const override
ssize_t read(char *ptr, size_t size) override
bool is_writable() const override
virtual bool compress(const char *data, size_t data_length, bool last, Callback callback)=0
std::function< bool(const char *data, size_t data_len)> Callback
virtual ~compressor()=default
virtual ~decompressor()=default
virtual bool decompress(const char *data, size_t data_length, Callback callback)=0
virtual bool is_valid() const =0
std::function< bool(const char *data, size_t data_len)> Callback
const char * data() const
bool open(const char *path)
~nocompressor() override=default
bool compress(const char *data, size_t data_length, bool, Callback callback) override
stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size)
bool end_with_crlf() const
#define assert(condition)
#define CPPHTTPLIB_VERSION
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_SEND_FLAGS
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_LISTEN_BACKLOG
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_RANGE_MAX_COUNT
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND
#define CPPHTTPLIB_IPV6_V6ONLY
#define CPPHTTPLIB_RECV_FLAGS
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_HEADER_MAX_LENGTH
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
#define CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
#define CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_TCP_NODELAY
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
detail namespace with internal helper functions
@ key
the parser read a key of a value in an object
@ error
throw a parse_error exception in case of a tag
void accept(sys::state &state, message const &m)
bool equal(const std::string &a, const std::string &b)
unsigned char to_lower(int c)
size_t to_utf8(int code, char *buff)
void divide(const char *data, std::size_t size, char d, std::function< void(const char *, std::size_t, const char *, std::size_t)> fn)
std::string trim_double_quotes_copy(const std::string &s)
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags)
void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port)
bool parse_www_authenticate(const Response &res, std::map< std::string, std::string > &auth, bool is_proxy)
void skip_content_with_length(Stream &strm, uint64_t len)
bool has_crlf(const std::string &s)
std::string serialize_multipart_formdata_get_content_type(const std::string &boundary)
bool is_socket_alive(socket_t sock)
bool has_header(const Headers &headers, const std::string &key)
void split(const char *b, const char *e, char d, std::function< void(const char *, const char *)> fn)
std::string trim_copy(const std::string &s)
std::string base64_encode(const std::string &in)
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, Progress progress, ContentReceiverWithProgress receiver, bool decompress)
std::string serialize_multipart_formdata_item_end()
bool is_hex(char c, int &v)
bool write_content_without_length(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down)
std::string if2ip(int address_family, const std::string &ifn)
bool is_space_or_tab(char c)
ssize_t write_response_line(Stream &strm, int status)
bool redirect(T &cli, Request &req, Response &res, const std::string &path, const std::string &location, Error &error)
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
void read_file(const std::string &path, std::string &out)
bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val)
void parse_query_text(const char *data, std::size_t size, Params ¶ms)
void set_nonblocking(socket_t sock, bool nonblocking)
int shutdown_socket(socket_t sock)
bool keep_alive(const std::atomic< socket_t > &svr_sock, socket_t sock, time_t keep_alive_timeout_sec)
std::string serialize_multipart_formdata_item_begin(const T &item, const std::string &boundary)
std::pair< size_t, size_t > get_range_offset_and_length(Range r, size_t content_length)
std::string file_extension(const std::string &path)
void parse_disposition_params(const std::string &s, Params ¶ms)
unsigned int str2tag(const std::string &s)
bool expect_content(const Request &req)
uint64_t get_header_value_u64(const Headers &headers, const std::string &key, uint64_t def, size_t id)
ssize_t handle_EINTR(T fn)
ssize_t select_write(socket_t sock, time_t sec, time_t usec)
bool get_ip_and_port(const struct sockaddr_storage &addr, socklen_t addr_len, std::string &ip, int &port)
void make_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary, const std::string &content_type, size_t content_length, std::string &data)
std::string serialize_multipart_formdata(const MultipartFormDataItems &items, const std::string &boundary, bool finish=true)
bool is_valid_path(const std::string &path)
bool parse_header(const char *beg, const char *end, T fn)
socket_t create_socket(const std::string &host, const std::string &ip, int port, int address_family, int socket_flags, bool tcp_nodelay, bool ipv6_v6only, SocketOptions socket_options, BindOrConnect bind_or_connect)
std::string random_string(size_t length)
bool process_multipart_ranges_data(const Request &req, const std::string &boundary, const std::string &content_type, size_t content_length, SToken stoken, CToken ctoken, Content content)
std::pair< size_t, size_t > trim(const char *b, const char *e, size_t left, size_t right)
bool range_error(Request &req, Response &res)
EncodingType encoding_type(const Request &req, const Response &res)
bool bind_ip_address(socket_t sock, const std::string &host)
bool parse_multipart_boundary(const std::string &content_type, std::string &boundary)
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags)
std::string serialize_multipart_formdata_finish(const std::string &boundary)
std::string params_to_query_str(const Params ¶ms)
int close_socket(socket_t sock)
ssize_t write_headers(Stream &strm, const Headers &headers)
bool is_chunked_transfer_encoding(const Headers &headers)
bool can_compress_content_type(const std::string &content_type)
bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiverWithProgress out)
std::string from_i_to_hex(size_t n)
std::string encode_query_param(const std::string &value)
bool write_content(Stream &strm, const ContentProvider &content_provider, size_t offset, size_t length, T is_shutting_down, Error &error)
socket_t create_client_socket(const std::string &host, const std::string &ip, int port, int address_family, bool tcp_nodelay, bool ipv6_v6only, SocketOptions socket_options, time_t connection_timeout_sec, time_t connection_timeout_usec, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec, const std::string &intf, Error &error)
bool process_client_socket(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec, std::function< bool(Stream &)> callback)
bool parse_range_header(const std::string &s, Ranges &ranges)
bool write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type, size_t content_length, const T &is_shutting_down)
void duration_to_sec_and_usec(const T &duration, U callback)
const char * get_header_value(const Headers &headers, const std::string &key, const char *def, size_t id)
std::string find_content_type(const std::string &path, const std::map< std::string, std::string > &user_data, const std::string &default_content_type)
std::string decode_url(const std::string &s, bool convert_plus_to_space)
bool prepare_content_receiver(T &x, int &status, ContentReceiverWithProgress receiver, bool decompress, U callback)
ssize_t select_read(socket_t sock, time_t sec, time_t usec)
std::string make_multipart_data_boundary()
void get_local_ip_and_port(socket_t sock, std::string &ip, int &port)
bool is_multipart_boundary_chars_valid(const std::string &boundary)
bool read_content_chunked(Stream &strm, T &x, ContentReceiverWithProgress out)
size_t get_multipart_ranges_data_length(const Request &req, const std::string &boundary, const std::string &content_type, size_t content_length)
ssize_t write_request_line(Stream &strm, const std::string &method, const std::string &path)
std::string make_content_range_header_field(const std::pair< size_t, size_t > &offset_and_length, size_t content_length)
std::string escape_abstract_namespace_unix_domain(const std::string &s)
bool read_headers(Stream &strm, Headers &headers)
std::string unescape_abstract_namespace_unix_domain(const std::string &s)
bool is_connection_error()
bool write_data(Stream &strm, const char *d, size_t l)
std::string encode_url(const std::string &s)
Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec)
bool write_content_chunked(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down, U &compressor, Error &error)
bool read_content_without_length(Stream &strm, ContentReceiverWithProgress out)
bool process_server_socket(const std::atomic< socket_t > &svr_sock, socket_t sock, size_t keep_alive_max_count, time_t keep_alive_timeout_sec, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec, T callback)
constexpr unsigned int str2tag_core(const char *s, size_t l, unsigned int h)
bool process_server_socket_core(const std::atomic< socket_t > &svr_sock, socket_t sock, size_t keep_alive_max_count, time_t keep_alive_timeout_sec, T callback)
std::function< void(socket_t sock)> SocketOptions
std::ostream & operator<<(std::ostream &os, const Error &obj)
std::vector< MultipartFormDataProvider > MultipartFormDataProviderItems
std::function< bool(const char *data, size_t data_length, uint64_t offset, uint64_t total_length)> ContentReceiverWithProgress
std::pair< std::string, std::string > make_range_header(const Ranges &ranges)
std::function< bool(const MultipartFormData &file)> MultipartContentHeader
std::vector< Range > Ranges
std::string hosted_at(const std::string &hostname)
std::function< bool(size_t offset, DataSink &sink)> ContentProviderWithoutLength
std::function< void(const Request &, const Response &)> Logger
@ SSLServerHostnameVerification
@ UnsupportedMultipartBoundaryChars
std::pair< ssize_t, ssize_t > Range
std::string to_string(Error error)
std::multimap< std::string, std::string > Params
std::unordered_multimap< std::string, std::string, detail::case_ignore::hash, detail::case_ignore::equal_to > Headers
std::pair< std::string, std::string > make_basic_authentication_header(const std::string &username, const std::string &password, bool is_proxy=false)
const char * status_message(int status)
std::string get_bearer_token_auth(const Request &req)
std::pair< std::string, std::string > make_bearer_token_authentication_header(const std::string &token, bool is_proxy=false)
std::vector< MultipartFormData > MultipartFormDataItems
std::function< bool(size_t offset, size_t length, DataSink &sink)> ContentProvider
std::multimap< std::string, MultipartFormData > MultipartFormDataMap
std::function< bool(uint64_t current, uint64_t total)> Progress
std::function< bool(const char *data, size_t data_length)> ContentReceiver
std::function< bool(const Response &response)> ResponseHandler
@ UnavailableForLegalReasons_451
@ ProxyAuthenticationRequired_407
@ NonAuthoritativeInformation_203
@ UnsupportedMediaType_415
@ VariantAlsoNegotiates_506
@ NetworkAuthenticationRequired_511
@ InternalServerError_500
@ InsufficientStorage_507
@ PreconditionRequired_428
@ RangeNotSatisfiable_416
@ UnprocessableContent_422
@ RequestHeaderFieldsTooLarge_431
@ HttpVersionNotSupported_505
void default_socket_options(socket_t sock)
std::string append_query_params(const std::string &path, const Params ¶ms)
std::function< void(bool success)> ContentProviderResourceReleaser
uint32_t size(sys::state const &state)
MOD_PROV_LIST constexpr uint32_t count
bool has_param(const std::string &key) const
bool has_header(const std::string &key) const
size_t get_param_value_count(const std::string &key) const
ContentReceiverWithProgress content_receiver
std::unordered_map< std::string, std::string > path_params
uint64_t get_header_value_u64(const std::string &key, uint64_t def=0, size_t id=0) const
std::vector< MultipartFormData > get_file_values(const std::string &key) const
bool has_file(const std::string &key) const
std::string get_header_value(const std::string &key, const char *def="", size_t id=0) const
std::string get_param_value(const std::string &key, size_t id=0) const
size_t authorization_count_
bool is_multipart_form_data() const
MultipartFormData get_file_value(const std::string &key) const
ContentProvider content_provider_
MultipartFormDataMap files
bool is_chunked_content_provider_
size_t get_header_value_count(const std::string &key) const
ResponseHandler response_handler
void set_header(const std::string &key, const std::string &val)
ContentProvider content_provider_
bool is_chunked_content_provider_
void set_redirect(const std::string &url, int status=StatusCode::Found_302)
Response & operator=(Response &&)=default
std::string file_content_path_
void set_content_provider(size_t length, const std::string &content_type, ContentProvider provider, ContentProviderResourceReleaser resource_releaser=nullptr)
void set_content(const char *s, size_t n, const std::string &content_type)
bool has_header(const std::string &key) const
bool content_provider_success_
ContentProviderResourceReleaser content_provider_resource_releaser_
std::string get_header_value(const std::string &key, const char *def="", size_t id=0) const
void set_chunked_content_provider(const std::string &content_type, ContentProviderWithoutLength provider, ContentProviderResourceReleaser resource_releaser=nullptr)
void set_file_content(const std::string &path, const std::string &content_type)
Response & operator=(const Response &)=default
Response(const Response &)=default
std::string file_content_content_type_
Response(Response &&)=default
void set_header(const std::string &key, const std::string &val)
size_t get_header_value_count(const std::string &key) const
uint64_t get_header_value_u64(const std::string &key, uint64_t def=0, size_t id=0) const
FileStat(const std::string &path)
bool operator()(const std::string &a, const std::string &b) const
size_t operator()(const std::string &key) const
size_t hash_core(const char *s, size_t l, size_t h) const
scope_exit(std::function< void(void)> &&f)
scope_exit(scope_exit &&rhs) noexcept