8 #ifndef CPPHTTPLIB_HTTPLIB_H
9 #define CPPHTTPLIB_HTTPLIB_H
11 #define CPPHTTPLIB_VERSION "0.18.3"
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
149 #ifndef _CRT_SECURE_NO_WARNINGS
150 #define _CRT_SECURE_NO_WARNINGS
151 #endif //_CRT_SECURE_NO_WARNINGS
153 #ifndef _CRT_NONSTDC_NO_DEPRECATE
154 #define _CRT_NONSTDC_NO_DEPRECATE
155 #endif //_CRT_NONSTDC_NO_DEPRECATE
157 #if defined(_MSC_VER)
159 #error Sorry, Visual Studio versions prior to 2015 are not supported
162 #pragma comment(lib, "ws2_32.lib")
165 using ssize_t = __int64;
167 using ssize_t = long;
172 #define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
176 #define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
184 #include <winsock2.h>
185 #include <ws2tcpip.h>
187 #ifndef WSA_FLAG_NO_HANDLE_INHERIT
188 #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
192 #ifdef CPPHTTPLIB_USE_POLL
193 #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
198 #include <arpa/inet.h>
199 #if !defined(_AIX) && !defined(__MVS__)
205 #define NI_MAXHOST 1025
210 #include <netinet/in.h>
214 #include <netinet/tcp.h>
215 #ifdef CPPHTTPLIB_USE_POLL
220 #include <sys/mman.h>
221 #include <sys/select.h>
222 #include <sys/socket.h>
227 #ifndef INVALID_SOCKET
228 #define INVALID_SOCKET (-1)
238 #include <condition_variable>
244 #include <functional>
256 #include <sys/stat.h>
258 #include <unordered_map>
259 #include <unordered_set>
262 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
264 #include <wincrypt.h>
269 #undef X509_CERT_PAIR
270 #undef X509_EXTENSIONS
271 #undef PKCS7_SIGNER_INFO
274 #pragma comment(lib, "crypt32.lib")
276 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
277 #include <TargetConditionals.h>
279 #include <CoreFoundation/CoreFoundation.h>
280 #include <Security/Security.h>
281 #endif // TARGET_OS_OSX
284 #include <openssl/err.h>
285 #include <openssl/evp.h>
286 #include <openssl/ssl.h>
287 #include <openssl/x509v3.h>
289 #if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
290 #include <openssl/applink.c>
296 #if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
297 #if OPENSSL_VERSION_NUMBER < 0x1010107f
298 #error Please use OpenSSL or a current version of BoringSSL
300 #define SSL_get1_peer_certificate SSL_get_peer_certificate
301 #elif OPENSSL_VERSION_NUMBER < 0x30000000L
302 #error Sorry, OpenSSL versions prior to 3.0.0 are not supported
307 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
311 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
312 #include <brotli/decode.h>
313 #include <brotli/encode.h>
331 template <
class T,
class... Args>
334 return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
340 typedef typename std::remove_extent<T>::type RT;
341 return std::unique_ptr<T>(
new RT[
n]);
344 namespace case_ignore {
347 const static unsigned char table[256] = {
348 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
349 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
350 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
351 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
352 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
353 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
354 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
355 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
356 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
357 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
358 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
359 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
360 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
361 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
362 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
363 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
364 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
367 return table[(
unsigned char)(
char)
c];
370 inline bool equal(
const std::string &
a,
const std::string &b) {
371 return a.size() == b.size() &&
372 std::equal(
a.begin(),
a.end(), b.begin(), [](
char ca,
char cb) {
373 return to_lower(ca) == to_lower(cb);
378 bool operator()(
const std::string &
a,
const std::string &b)
const {
385 return hash_core(key.data(), key.size(), 0);
395 static_cast<unsigned char>(
to_lower(*
s)));
406 : exit_function(
std::move(f)), execute_on_destruction{
true} {}
409 : exit_function(std::move(rhs.exit_function)),
410 execute_on_destruction{rhs.execute_on_destruction} {
415 if (execute_on_destruction) { this->exit_function(); }
418 void release() { this->execute_on_destruction =
false; }
425 std::function<void(
void)> exit_function;
426 bool execute_on_destruction;
507 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
510 using Params = std::multimap<std::string, std::string>;
513 using Progress = std::function<bool(uint64_t current, uint64_t total)>;
536 std::function<bool(
const char *
data,
size_t data_len)>
write;
543 class data_sink_streambuf final :
public std::streambuf {
545 explicit data_sink_streambuf(
DataSink &sink) : sink_(sink) {}
548 std::streamsize xsputn(
const char *
s, std::streamsize
n)
override {
549 sink_.write(
s,
static_cast<size_t>(
n));
557 data_sink_streambuf sb_;
561 std::function<bool(
size_t offset,
size_t length,
DataSink &sink)>;
564 std::function<bool(
size_t offset,
DataSink &sink)>;
577 std::function<bool(
const char *
data,
size_t data_length, uint64_t offset,
578 uint64_t total_length)>;
581 std::function<bool(
const char *
data,
size_t data_length)>;
602 return reader_(std::move(receiver));
609 using Range = std::pair<ssize_t, ssize_t>;
636 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
637 const SSL *ssl =
nullptr;
640 bool has_header(
const std::string &key)
const;
642 size_t id = 0)
const;
644 size_t id = 0)
const;
646 void set_header(
const std::string &key,
const std::string &val);
648 bool has_param(
const std::string &key)
const;
649 std::string
get_param_value(
const std::string &key,
size_t id = 0)
const;
654 bool has_file(
const std::string &key)
const;
656 std::vector<MultipartFormData>
get_file_values(
const std::string &key)
const;
674 bool has_header(
const std::string &key)
const;
676 size_t id = 0)
const;
678 size_t id = 0)
const;
680 void set_header(
const std::string &key,
const std::string &val);
683 void set_content(
const char *
s,
size_t n,
const std::string &content_type);
684 void set_content(
const std::string &
s,
const std::string &content_type);
685 void set_content(std::string &&
s,
const std::string &content_type);
688 size_t length,
const std::string &content_type,
ContentProvider provider,
700 const std::string &content_type);
731 virtual ssize_t
read(
char *ptr,
size_t size) = 0;
732 virtual ssize_t
write(
const char *ptr,
size_t size) = 0;
737 ssize_t
write(
const char *ptr);
738 ssize_t
write(
const std::string &
s);
746 virtual bool enqueue(std::function<
void()> fn) = 0;
755 : shutdown_(false), max_queued_requests_(mqr) {
757 threads_.emplace_back(
worker(*
this));
765 bool enqueue(std::function<
void()> fn)
override {
767 std::unique_lock<std::mutex> lock(mutex_);
768 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
771 jobs_.push_back(std::move(fn));
781 std::unique_lock<std::mutex> lock(mutex_);
788 for (
auto &t : threads_) {
799 std::function<void()> fn;
801 std::unique_lock<std::mutex> lock(pool_.mutex_);
804 lock, [&] {
return !pool_.jobs_.empty() || pool_.shutdown_; });
806 if (pool_.shutdown_ && pool_.jobs_.empty()) {
break; }
808 fn = pool_.jobs_.front();
809 pool_.jobs_.pop_front();
812 assert(
true ==
static_cast<bool>(fn));
816 #if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
817 !defined(LIBRESSL_VERSION_NUMBER)
818 OPENSSL_thread_stop();
826 std::vector<std::thread> threads_;
830 size_t max_queued_requests_ = 0;
832 std::condition_variable cond_;
884 static constexpr
char separator =
'/';
889 std::vector<std::string> static_fragments_;
892 std::vector<std::string> param_names_;
955 const std::string &mount_point = std::string());
956 bool set_mount_point(
const std::string &mount_point,
const std::string &dir,
960 const std::string &mime);
964 template <
class ErrorHandlerFunc>
966 return set_error_handler_core(
967 std::forward<ErrorHandlerFunc>(handler),
968 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
991 template <
class Rep,
class Period>
995 template <
class Rep,
class Period>
999 template <
class Rep,
class Period>
1004 bool bind_to_port(
const std::string &host,
int port,
int socket_flags = 0);
1008 bool listen(
const std::string &host,
int port,
int socket_flags = 0);
1019 int remote_port,
const std::string &local_addr,
1020 int local_port,
bool close_connection,
1021 bool &connection_closed,
1022 const std::function<
void(
Request &)> &setup_request);
1037 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
Handler>>;
1038 using HandlersForContentReader =
1039 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1042 static std::unique_ptr<detail::MatcherBase>
1043 make_matcher(
const std::string &pattern);
1046 Server &set_error_handler_core(
Handler handler, std::false_type);
1048 socket_t create_server_socket(
const std::string &host,
int port,
1051 int bind_internal(
const std::string &host,
int port,
int socket_flags);
1052 bool listen_internal();
1058 const Handlers &handlers)
const;
1059 bool dispatch_request_for_content_reader(
1061 const HandlersForContentReader &handlers)
const;
1063 bool parse_request_line(
const char *
s,
Request &req)
const;
1065 std::string &content_type, std::string &boundary)
const;
1066 bool write_response(
Stream &strm,
bool close_connection,
Request &req,
1068 bool write_response_with_content(
Stream &strm,
bool close_connection,
1070 bool write_response_core(
Stream &strm,
bool close_connection,
1072 bool need_apply_ranges);
1073 bool write_content_with_provider(
Stream &strm,
const Request &req,
1074 Response &res,
const std::string &boundary,
1075 const std::string &content_type);
1087 virtual bool process_and_close_socket(
socket_t sock);
1089 std::atomic<bool> is_running_{
false};
1090 std::atomic<bool> is_decommisioned{
false};
1092 struct MountPointEntry {
1093 std::string mount_point;
1094 std::string base_dir;
1097 std::vector<MountPointEntry> base_dirs_;
1098 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1099 std::string default_file_mimetype_ =
"application/octet-stream";
1100 Handler file_request_handler_;
1102 Handlers get_handlers_;
1103 Handlers post_handlers_;
1104 HandlersForContentReader post_handlers_for_content_reader_;
1105 Handlers put_handlers_;
1106 HandlersForContentReader put_handlers_for_content_reader_;
1107 Handlers patch_handlers_;
1108 HandlersForContentReader patch_handlers_for_content_reader_;
1109 Handlers delete_handlers_;
1110 HandlersForContentReader delete_handlers_for_content_reader_;
1111 Handlers options_handlers_;
1116 Handler post_routing_handler_;
1121 int address_family_ = AF_UNSPEC;
1127 std::function<ssize_t(
Stream &,
Headers &)> header_writer_ =
1162 : res_(std::move(res)), err_(err),
1163 request_headers_(std::move(request_headers)) {}
1165 operator bool()
const {
return res_ !=
nullptr; }
1166 bool operator==(std::nullptr_t)
const {
return res_ ==
nullptr; }
1167 bool operator!=(std::nullptr_t)
const {
return res_ !=
nullptr; }
1181 const char *def =
"",
1182 size_t id = 0)
const;
1184 uint64_t def = 0,
size_t id = 0)
const;
1188 std::unique_ptr<Response> res_;
1200 const std::string &client_cert_path,
1201 const std::string &client_key_path);
1244 Result Post(
const std::string &path,
const char *body,
size_t content_length,
1245 const std::string &content_type);
1247 size_t content_length,
const std::string &content_type);
1249 size_t content_length,
const std::string &content_type,
1251 Result Post(
const std::string &path,
const std::string &body,
1252 const std::string &content_type);
1253 Result Post(
const std::string &path,
const std::string &body,
1254 const std::string &content_type,
Progress progress);
1256 const std::string &body,
const std::string &content_type);
1258 const std::string &body,
const std::string &content_type,
1260 Result Post(
const std::string &path,
size_t content_length,
1262 const std::string &content_type);
1265 const std::string &content_type);
1268 const std::string &content_type);
1271 const std::string &content_type);
1287 Result Put(
const std::string &path,
const char *body,
size_t content_length,
1288 const std::string &content_type);
1289 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
1290 size_t content_length,
const std::string &content_type);
1291 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
1292 size_t content_length,
const std::string &content_type,
1294 Result Put(
const std::string &path,
const std::string &body,
1295 const std::string &content_type);
1296 Result Put(
const std::string &path,
const std::string &body,
1297 const std::string &content_type,
Progress progress);
1299 const std::string &body,
const std::string &content_type);
1301 const std::string &body,
const std::string &content_type,
1303 Result Put(
const std::string &path,
size_t content_length,
1307 const std::string &content_type);
1310 const std::string &content_type);
1313 const std::string &content_type);
1329 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
1330 const std::string &content_type);
1331 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
1332 const std::string &content_type,
Progress progress);
1334 const char *body,
size_t content_length,
1335 const std::string &content_type);
1337 const char *body,
size_t content_length,
1338 const std::string &content_type,
Progress progress);
1339 Result Patch(
const std::string &path,
const std::string &body,
1340 const std::string &content_type);
1341 Result Patch(
const std::string &path,
const std::string &body,
1342 const std::string &content_type,
Progress progress);
1344 const std::string &body,
const std::string &content_type);
1346 const std::string &body,
const std::string &content_type,
1348 Result Patch(
const std::string &path,
size_t content_length,
1350 const std::string &content_type);
1353 const std::string &content_type);
1356 const std::string &content_type);
1359 const std::string &content_type);
1363 Result Delete(
const std::string &path,
const char *body,
1364 size_t content_length,
const std::string &content_type);
1365 Result Delete(
const std::string &path,
const char *body,
1366 size_t content_length,
const std::string &content_type,
1369 const char *body,
size_t content_length,
1370 const std::string &content_type);
1372 const char *body,
size_t content_length,
1373 const std::string &content_type,
Progress progress);
1374 Result Delete(
const std::string &path,
const std::string &body,
1375 const std::string &content_type);
1376 Result Delete(
const std::string &path,
const std::string &body,
1377 const std::string &content_type,
Progress progress);
1379 const std::string &body,
const std::string &content_type);
1381 const std::string &body,
const std::string &content_type,
1392 std::string
host()
const;
1411 template <
class Rep,
class Period>
1416 template <
class Rep,
class Period>
1420 template <
class Rep,
class Period>
1423 void set_basic_auth(
const std::string &username,
const std::string &password);
1425 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1426 void set_digest_auth(
const std::string &username,
1427 const std::string &password);
1443 const std::string &password);
1445 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1446 void set_proxy_digest_auth(
const std::string &username,
1447 const std::string &password);
1450 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1451 void set_ca_cert_path(
const std::string &ca_cert_file_path,
1452 const std::string &ca_cert_dir_path = std::string());
1453 void set_ca_cert_store(X509_STORE *ca_cert_store);
1454 X509_STORE *create_ca_cert_store(
const char *ca_cert, std::size_t size)
const;
1457 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1458 void enable_server_certificate_verification(
bool enabled);
1459 void enable_server_hostname_verification(
bool enabled);
1460 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1468 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1489 bool close_connection,
Error &error);
1492 Error &error)
const;
1535 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1536 std::string digest_auth_username_;
1537 std::string digest_auth_password_;
1561 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1562 std::string proxy_digest_auth_username_;
1563 std::string proxy_digest_auth_password_;
1566 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1567 std::string ca_cert_file_path_;
1568 std::string ca_cert_dir_path_;
1570 X509_STORE *ca_cert_store_ =
nullptr;
1573 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1574 bool server_certificate_verification_ =
true;
1575 bool server_hostname_verification_ =
true;
1576 std::function<bool(SSL *ssl)> server_certificate_verifier_;
1585 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1586 bool is_ssl_peer_could_be_closed(SSL *ssl)
const;
1591 bool write_request(
Stream &strm,
Request &req,
bool close_connection,
1595 bool close_connection,
Error &error);
1596 std::unique_ptr<Response> send_with_content_provider(
1597 Request &req,
const char *body,
size_t content_length,
1600 const std::string &content_type,
Error &error);
1601 Result send_with_content_provider(
1602 const std::string &method,
const std::string &path,
1603 const Headers &headers,
const char *body,
size_t content_length,
1606 const std::string &content_type,
Progress progress);
1611 std::string adjust_host_string(
const std::string &
host)
const;
1614 std::function<
bool(
Stream &strm)> callback);
1615 virtual bool is_ssl()
const;
1621 explicit Client(
const std::string &scheme_host_port);
1623 explicit Client(
const std::string &scheme_host_port,
1624 const std::string &client_cert_path,
1625 const std::string &client_key_path);
1631 const std::string &client_cert_path,
1632 const std::string &client_key_path);
1678 Result Post(
const std::string &path,
const char *body,
size_t content_length,
1679 const std::string &content_type);
1681 size_t content_length,
const std::string &content_type);
1683 size_t content_length,
const std::string &content_type,
1685 Result Post(
const std::string &path,
const std::string &body,
1686 const std::string &content_type);
1687 Result Post(
const std::string &path,
const std::string &body,
1688 const std::string &content_type,
Progress progress);
1690 const std::string &body,
const std::string &content_type);
1692 const std::string &body,
const std::string &content_type,
1694 Result Post(
const std::string &path,
size_t content_length,
1696 const std::string &content_type);
1699 const std::string &content_type);
1702 const std::string &content_type);
1705 const std::string &content_type);
1721 Result Put(
const std::string &path,
const char *body,
size_t content_length,
1722 const std::string &content_type);
1723 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
1724 size_t content_length,
const std::string &content_type);
1725 Result Put(
const std::string &path,
const Headers &headers,
const char *body,
1726 size_t content_length,
const std::string &content_type,
1728 Result Put(
const std::string &path,
const std::string &body,
1729 const std::string &content_type);
1730 Result Put(
const std::string &path,
const std::string &body,
1731 const std::string &content_type,
Progress progress);
1733 const std::string &body,
const std::string &content_type);
1735 const std::string &body,
const std::string &content_type,
1737 Result Put(
const std::string &path,
size_t content_length,
1741 const std::string &content_type);
1744 const std::string &content_type);
1747 const std::string &content_type);
1763 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
1764 const std::string &content_type);
1765 Result Patch(
const std::string &path,
const char *body,
size_t content_length,
1766 const std::string &content_type,
Progress progress);
1768 const char *body,
size_t content_length,
1769 const std::string &content_type);
1771 const char *body,
size_t content_length,
1772 const std::string &content_type,
Progress progress);
1773 Result Patch(
const std::string &path,
const std::string &body,
1774 const std::string &content_type);
1775 Result Patch(
const std::string &path,
const std::string &body,
1776 const std::string &content_type,
Progress progress);
1778 const std::string &body,
const std::string &content_type);
1780 const std::string &body,
const std::string &content_type,
1782 Result Patch(
const std::string &path,
size_t content_length,
1784 const std::string &content_type);
1787 const std::string &content_type);
1790 const std::string &content_type);
1793 const std::string &content_type);
1797 Result Delete(
const std::string &path,
const char *body,
1798 size_t content_length,
const std::string &content_type);
1799 Result Delete(
const std::string &path,
const char *body,
1800 size_t content_length,
const std::string &content_type,
1803 const char *body,
size_t content_length,
1804 const std::string &content_type);
1806 const char *body,
size_t content_length,
1807 const std::string &content_type,
Progress progress);
1808 Result Delete(
const std::string &path,
const std::string &body,
1809 const std::string &content_type);
1810 Result Delete(
const std::string &path,
const std::string &body,
1811 const std::string &content_type,
Progress progress);
1813 const std::string &body,
const std::string &content_type);
1815 const std::string &body,
const std::string &content_type,
1826 std::string
host()
const;
1844 template <
class Rep,
class Period>
1849 template <
class Rep,
class Period>
1853 template <
class Rep,
class Period>
1856 void set_basic_auth(
const std::string &username,
const std::string &password);
1858 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1859 void set_digest_auth(
const std::string &username,
1860 const std::string &password);
1876 const std::string &password);
1878 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1879 void set_proxy_digest_auth(
const std::string &username,
1880 const std::string &password);
1883 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1884 void enable_server_certificate_verification(
bool enabled);
1885 void enable_server_hostname_verification(
bool enabled);
1886 void set_server_certificate_verifier(std::function<
bool(SSL *ssl)> verifier);
1892 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1893 void set_ca_cert_path(
const std::string &ca_cert_file_path,
1894 const std::string &ca_cert_dir_path = std::string());
1896 void set_ca_cert_store(X509_STORE *ca_cert_store);
1897 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1899 long get_openssl_verify_result()
const;
1901 SSL_CTX *ssl_context()
const;
1905 std::unique_ptr<ClientImpl> cli_;
1907 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1908 bool is_ssl_ =
false;
1912 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1913 class SSLServer :
public Server {
1915 SSLServer(
const char *cert_path,
const char *private_key_path,
1916 const char *client_ca_cert_file_path =
nullptr,
1917 const char *client_ca_cert_dir_path =
nullptr,
1918 const char *private_key_password =
nullptr);
1920 SSLServer(X509 *cert, EVP_PKEY *private_key,
1921 X509_STORE *client_ca_cert_store =
nullptr);
1924 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1926 ~SSLServer()
override;
1930 SSL_CTX *ssl_context()
const;
1932 void update_certs(X509 *cert, EVP_PKEY *private_key,
1933 X509_STORE *client_ca_cert_store =
nullptr);
1936 bool process_and_close_socket(
socket_t sock)
override;
1939 std::mutex ctx_mutex_;
1942 class SSLClient final :
public ClientImpl {
1944 explicit SSLClient(
const std::string &host);
1946 explicit SSLClient(
const std::string &host,
int port);
1948 explicit SSLClient(
const std::string &host,
int port,
1949 const std::string &client_cert_path,
1950 const std::string &client_key_path,
1951 const std::string &private_key_password = std::string());
1953 explicit SSLClient(
const std::string &host,
int port, X509 *client_cert,
1954 EVP_PKEY *client_key,
1955 const std::string &private_key_password = std::string());
1957 ~SSLClient()
override;
1961 void set_ca_cert_store(X509_STORE *ca_cert_store);
1962 void load_ca_cert_store(
const char *ca_cert, std::size_t size);
1964 long get_openssl_verify_result()
const;
1966 SSL_CTX *ssl_context()
const;
1969 bool create_and_connect_socket(Socket &socket,
Error &error)
override;
1970 void shutdown_ssl(Socket &socket,
bool shutdown_gracefully)
override;
1971 void shutdown_ssl_impl(Socket &socket,
bool shutdown_gracefully);
1973 bool process_socket(
const Socket &socket,
1974 std::function<
bool(
Stream &strm)> callback)
override;
1975 bool is_ssl()
const override;
1977 bool connect_with_proxy(Socket &sock,
Response &res,
bool &success,
1979 bool initialize_ssl(Socket &socket,
Error &error);
1983 bool verify_host(X509 *server_cert)
const;
1984 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
1985 bool verify_host_with_common_name(X509 *server_cert)
const;
1986 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
1989 std::mutex ctx_mutex_;
1990 std::once_flag initialize_cert_;
1992 std::vector<std::string> host_components_;
1994 long verify_result_ = 0;
1996 friend class ClientImpl;
2006 template <
typename T,
typename U>
2008 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2009 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2010 duration - std::chrono::seconds(sec))
2012 callback(
static_cast<time_t
>(sec),
static_cast<time_t
>(usec));
2016 const std::string &key, uint64_t def,
2018 auto rng = headers.equal_range(key);
2019 auto it = rng.first;
2020 std::advance(it,
static_cast<ssize_t
>(
id));
2021 if (it != rng.second) {
2022 return std::strtoull(it->second.data(),
nullptr, 10);
2030 uint64_t def,
size_t id)
const {
2035 uint64_t def,
size_t id)
const {
2042 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2043 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
2044 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
2045 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
2048 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
2049 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
2051 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2052 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
2067 return "Non-Authoritative Information";
2091 return "Proxy Authentication Required";
2112 return "Request Header Fields Too Large";
2114 return "Unavailable For Legal Reasons";
2120 return "HTTP Version Not Supported";
2126 return "Network Authentication Required";
2135 static std::string BearerHeaderPrefix =
"Bearer ";
2137 .substr(BearerHeaderPrefix.length());
2142 template <
class Rep,
class Period>
2150 template <
class Rep,
class Period>
2158 template <
class Rep,
class Period>
2171 case Error::Read:
return "Failed to read connection";
2172 case Error::Write:
return "Failed to write connection";
2179 return "SSL server hostname verification failed";
2181 return "Unsupported HTTP multipart boundary characters";
2194 os <<
" (" <<
static_cast<std::underlying_type<Error>::type
>(obj) <<
')';
2204 template <
class Rep,
class Period>
2206 const std::chrono::duration<Rep, Period> &duration) {
2212 template <
class Rep,
class Period>
2214 const std::chrono::duration<Rep, Period> &duration) {
2219 template <
class Rep,
class Period>
2221 const std::chrono::duration<Rep, Period> &duration) {
2226 template <
class Rep,
class Period>
2228 const std::chrono::duration<Rep, Period> &duration) {
2229 cli_->set_connection_timeout(duration);
2232 template <
class Rep,
class Period>
2235 cli_->set_read_timeout(duration);
2238 template <
class Rep,
class Period>
2241 cli_->set_write_timeout(duration);
2249 std::string
hosted_at(
const std::string &hostname);
2251 void hosted_at(
const std::string &hostname, std::vector<std::string> &addrs);
2257 std::pair<std::string, std::string>
2259 const std::string &password,
2260 bool is_proxy =
false);
2265 inline std::wstring u8string_to_wstring(
const char *
s) {
2267 auto len =
static_cast<int>(strlen(
s));
2268 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0,
s, len,
nullptr, 0);
2271 wlen = ::MultiByteToWideChar(
2273 const_cast<LPWSTR
>(
reinterpret_cast<LPCWSTR
>(ws.data())), wlen);
2274 if (wlen !=
static_cast<int>(ws.size())) { ws.clear(); }
2296 std::string
decode_url(
const std::string &
s,
bool convert_plus_to_space);
2298 void read_file(
const std::string &path, std::string &out);
2303 const char *
data, std::size_t size,
char d,
2304 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2308 const std::string &
str,
char d,
2309 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2312 void split(
const char *b,
const char *e,
char d,
2313 std::function<
void(
const char *,
const char *)> fn);
2315 void split(
const char *b,
const char *e,
char d,
size_t m,
2316 std::function<
void(
const char *,
const char *)> fn);
2319 time_t read_timeout_usec, time_t write_timeout_sec,
2320 time_t write_timeout_usec,
2321 std::function<
bool(
Stream &)> callback);
2324 int port,
int address_family,
bool tcp_nodelay,
2326 time_t connection_timeout_sec,
2327 time_t connection_timeout_usec,
2328 time_t read_timeout_sec, time_t read_timeout_usec,
2329 time_t write_timeout_sec,
2330 time_t write_timeout_usec,
2331 const std::string &intf,
Error &error);
2334 const char *def,
size_t id);
2343 std::string &boundary);
2364 ssize_t
read(
char *ptr,
size_t size)
override;
2365 ssize_t
write(
const char *ptr,
size_t size)
override;
2374 size_t position = 0;
2382 virtual bool compress(
const char *
data,
size_t data_length,
bool last,
2401 bool compress(
const char *
data,
size_t data_length,
bool ,
2405 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2406 class gzip_compressor final :
public compressor {
2409 ~gzip_compressor()
override;
2411 bool compress(
const char *
data,
size_t data_length,
bool last,
2412 Callback callback)
override;
2415 bool is_valid_ =
false;
2419 class gzip_decompressor final :
public decompressor {
2421 gzip_decompressor();
2422 ~gzip_decompressor()
override;
2426 bool decompress(
const char *
data,
size_t data_length,
2427 Callback callback)
override;
2430 bool is_valid_ =
false;
2435 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2436 class brotli_compressor final :
public compressor {
2438 brotli_compressor();
2439 ~brotli_compressor();
2441 bool compress(
const char *
data,
size_t data_length,
bool last,
2442 Callback callback)
override;
2445 BrotliEncoderState *state_ =
nullptr;
2448 class brotli_decompressor final :
public decompressor {
2450 brotli_decompressor();
2451 ~brotli_decompressor();
2455 bool decompress(
const char *
data,
size_t data_length,
2456 Callback callback)
override;
2459 BrotliDecoderResult decoder_r;
2460 BrotliDecoderState *decoder_s =
nullptr;
2469 size_t fixed_buffer_size);
2470 const char *
ptr()
const;
2471 size_t size()
const;
2476 void append(
char c);
2479 char *fixed_buffer_;
2480 const size_t fixed_buffer_size_;
2481 size_t fixed_buffer_used_size_ = 0;
2482 std::string glowable_buffer_;
2487 mmap(
const char *path);
2490 bool open(
const char *path);
2494 size_t size()
const;
2495 const char *
data()
const;
2499 HANDLE hFile_ = NULL;
2500 HANDLE hMapping_ = NULL;
2505 void *addr_ =
nullptr;
2506 bool is_open_empty_file =
false;
2520 if (0x20 <=
c && isdigit(
c)) {
2523 }
else if (
'A' <=
c &&
c <=
'F') {
2526 }
else if (
'a' <=
c &&
c <=
'f') {
2535 if (i >=
s.size()) {
return false; }
2538 for (; cnt; i++, cnt--) {
2539 if (!
s[i]) {
return false; }
2551 static const auto charset =
"0123456789abcdef";
2561 if (code < 0x0080) {
2562 buff[0] =
static_cast<char>(code & 0x7F);
2564 }
else if (code < 0x0800) {
2565 buff[0] =
static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2566 buff[1] =
static_cast<char>(0x80 | (code & 0x3F));
2568 }
else if (code < 0xD800) {
2569 buff[0] =
static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2570 buff[1] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2571 buff[2] =
static_cast<char>(0x80 | (code & 0x3F));
2573 }
else if (code < 0xE000) {
2575 }
else if (code < 0x10000) {
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 < 0x110000) {
2581 buff[0] =
static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2582 buff[1] =
static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2583 buff[2] =
static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2584 buff[3] =
static_cast<char>(0x80 | (code & 0x3F));
2595 static const auto lookup =
2596 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2599 out.reserve(in.size());
2605 val = (val << 8) + static_cast<uint8_t>(
c);
2608 out.push_back(lookup[(val >> valb) & 0x3F]);
2613 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
2615 while (out.size() % 4) {
2627 while (i < path.size() && path[i] ==
'/') {
2631 while (i < path.size()) {
2634 while (i < path.size() && path[i] !=
'/') {
2635 if (path[i] ==
'\0') {
2637 }
else if (path[i] ==
'\\') {
2646 if (!path.compare(beg, len,
".")) {
2648 }
else if (!path.compare(beg, len,
"..")) {
2649 if (level == 0) {
return false; }
2656 while (i < path.size() && path[i] ==
'/') {
2666 auto wpath = u8string_to_wstring(path.c_str());
2667 ret_ = _wstat(wpath.c_str(), &st_);
2669 ret_ = stat(path.c_str(), &st_);
2673 return ret_ >= 0 && S_ISREG(st_.st_mode);
2676 return ret_ >= 0 && S_ISDIR(st_.st_mode);
2680 std::ostringstream escaped;
2682 escaped << std::hex;
2685 if (std::isalnum(
static_cast<uint8_t
>(
c)) ||
c ==
'-' ||
c ==
'_' ||
2686 c ==
'.' ||
c ==
'!' ||
c ==
'~' ||
c ==
'*' ||
c ==
'\'' ||
c ==
'(' ||
2690 escaped << std::uppercase;
2691 escaped <<
'%' << std::setw(2)
2692 <<
static_cast<int>(
static_cast<unsigned char>(
c));
2693 escaped << std::nouppercase;
2697 return escaped.str();
2702 result.reserve(
s.size());
2704 for (
size_t i = 0;
s[i]; i++) {
2706 case ' ': result +=
"%20";
break;
2707 case '+': result +=
"%2B";
break;
2708 case '\r': result +=
"%0D";
break;
2709 case '\n': result +=
"%0A";
break;
2710 case '\'': result +=
"%27";
break;
2711 case ',': result +=
"%2C";
break;
2713 case ';': result +=
"%3B";
break;
2715 auto c =
static_cast<uint8_t
>(
s[i]);
2719 auto len = snprintf(hex,
sizeof(hex) - 1,
"%02X",
c);
2721 result.append(hex,
static_cast<size_t>(len));
2733 bool convert_plus_to_space) {
2736 for (
size_t i = 0; i <
s.size(); i++) {
2737 if (
s[i] ==
'%' && i + 1 <
s.size()) {
2738 if (
s[i + 1] ==
'u') {
2743 size_t len =
to_utf8(val, buff);
2744 if (len > 0) { result.append(buff, len); }
2753 result +=
static_cast<char>(val);
2759 }
else if (convert_plus_to_space &&
s[i] ==
'+') {
2769 inline void read_file(
const std::string &path, std::string &out) {
2770 std::ifstream fs(path, std::ios_base::binary);
2771 fs.seekg(0, std::ios_base::end);
2772 auto size = fs.tellg();
2774 out.resize(
static_cast<size_t>(size));
2775 fs.read(&out[0],
static_cast<std::streamsize
>(size));
2780 static auto re = std::regex(
"\\.([a-zA-Z0-9]+)$");
2781 if (std::regex_search(path, m, re)) {
return m[1].str(); }
2782 return std::string();
2787 inline std::pair<size_t, size_t>
trim(
const char *b,
const char *e,
size_t left,
2799 auto r =
trim(
s.data(),
s.data() +
s.size(), 0,
s.size());
2800 return s.substr(r.first, r.second - r.first);
2804 if (
s.length() >= 2 &&
s.front() ==
'"' &&
s.back() ==
'"') {
2805 return s.substr(1,
s.size() - 2);
2812 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2815 const auto found =
static_cast<std::size_t
>(it !=
data + size);
2816 const auto lhs_data =
data;
2817 const auto lhs_size =
static_cast<std::size_t
>(it -
data);
2818 const auto rhs_data = it + found;
2819 const auto rhs_size = size - lhs_size - found;
2821 fn(lhs_data, lhs_size, rhs_data, rhs_size);
2826 std::function<
void(
const char *, std::size_t,
const char *, std::size_t)>
2831 inline void split(
const char *b,
const char *e,
char d,
2832 std::function<
void(
const char *,
const char *)> fn) {
2836 inline void split(
const char *b,
const char *e,
char d,
size_t m,
2837 std::function<
void(
const char *,
const char *)> fn) {
2842 while (e ? (b + i < e) : (b[i] !=
'\0')) {
2843 if (b[i] == d && count < m) {
2844 auto r =
trim(b, e, beg, i);
2845 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2853 auto r =
trim(b, e, beg, i);
2854 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2859 size_t fixed_buffer_size)
2860 : strm_(strm), fixed_buffer_(fixed_buffer),
2861 fixed_buffer_size_(fixed_buffer_size) {}
2864 if (glowable_buffer_.empty()) {
2865 return fixed_buffer_;
2867 return glowable_buffer_.data();
2872 if (glowable_buffer_.empty()) {
2873 return fixed_buffer_used_size_;
2875 return glowable_buffer_.size();
2881 return size() >= 2 && end[-2] ==
'\r' && end[-1] ==
'\n';
2885 fixed_buffer_used_size_ = 0;
2886 glowable_buffer_.clear();
2888 #ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2892 for (
size_t i = 0;; i++) {
2894 auto n = strm_.
read(&
byte, 1);
2898 }
else if (
n == 0) {
2908 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2909 if (
byte ==
'\n') {
break; }
2911 if (prev_byte ==
'\r' &&
byte ==
'\n') {
break; }
2919 inline void stream_line_reader::append(
char c) {
2920 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2921 fixed_buffer_[fixed_buffer_used_size_++] =
c;
2922 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
2924 if (glowable_buffer_.empty()) {
2925 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
2926 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2928 glowable_buffer_ +=
c;
2940 auto wpath = u8string_to_wstring(path);
2941 if (wpath.empty()) {
return false; }
2943 #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2944 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
2945 OPEN_EXISTING, NULL);
2947 hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
2948 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2951 if (hFile_ == INVALID_HANDLE_VALUE) {
return false; }
2953 LARGE_INTEGER
size{};
2954 if (!::GetFileSizeEx(hFile_, &
size)) {
return false; }
2958 if (
static_cast<ULONGLONG
>(
size.QuadPart) >
2959 (std::numeric_limits<decltype(size_)>::
max)()) {
2963 size_ =
static_cast<size_t>(
size.QuadPart);
2965 #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2967 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
2969 hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
2973 if (hMapping_ == NULL && size_ == 0) {
2975 is_open_empty_file =
true;
2979 if (hMapping_ == NULL) {
2984 #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2985 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
2987 addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
2990 if (addr_ ==
nullptr) {
2995 fd_ =
::open(path, O_RDONLY);
2996 if (fd_ == -1) {
return false; }
2999 if (fstat(fd_, &sb) == -1) {
3003 size_ =
static_cast<size_t>(sb.st_size);
3005 addr_ =
::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
3008 if (addr_ == MAP_FAILED && size_ == 0) {
3010 is_open_empty_file =
true;
3019 return is_open_empty_file ? true : addr_ !=
nullptr;
3025 return is_open_empty_file ?
"" :
static_cast<const char *
>(addr_);
3031 ::UnmapViewOfFile(addr_);
3036 ::CloseHandle(hMapping_);
3040 if (hFile_ != INVALID_HANDLE_VALUE) {
3041 ::CloseHandle(hFile_);
3042 hFile_ = INVALID_HANDLE_VALUE;
3045 is_open_empty_file =
false;
3047 if (addr_ !=
nullptr) {
3048 munmap(addr_, size_);
3061 return closesocket(sock);
3071 if (res < 0 && errno == EINTR) {
3072 std::this_thread::sleep_for(std::chrono::microseconds{1});
3084 static_cast<char *
>(ptr),
static_cast<int>(size),
3097 static_cast<const char *
>(ptr),
static_cast<int>(size),
3106 #ifdef CPPHTTPLIB_USE_POLL
3107 struct pollfd pfd_read;
3109 pfd_read.events = POLLIN;
3111 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
3113 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
3116 if (sock >= FD_SETSIZE) {
return -1; }
3124 tv.tv_sec =
static_cast<long>(sec);
3125 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3128 return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv);
3134 #ifdef CPPHTTPLIB_USE_POLL
3135 struct pollfd pfd_read;
3137 pfd_read.events = POLLOUT;
3139 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
3141 return handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
3144 if (sock >= FD_SETSIZE) {
return -1; }
3152 tv.tv_sec =
static_cast<long>(sec);
3153 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3156 return select(
static_cast<int>(sock + 1),
nullptr, &fds,
nullptr, &tv);
3163 #ifdef CPPHTTPLIB_USE_POLL
3164 struct pollfd pfd_read;
3166 pfd_read.events = POLLIN | POLLOUT;
3168 auto timeout =
static_cast<int>(sec * 1000 + usec / 1000);
3170 auto poll_res =
handle_EINTR([&]() {
return poll(&pfd_read, 1, timeout); });
3174 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
3176 socklen_t len =
sizeof(error);
3177 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3178 reinterpret_cast<char *
>(&error), &len);
3179 auto successful = res >= 0 && !error;
3191 FD_SET(sock, &fdsr);
3197 tv.tv_sec =
static_cast<long>(sec);
3198 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(usec);
3201 return select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
3206 if (
ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3208 socklen_t len =
sizeof(error);
3209 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3210 reinterpret_cast<char *
>(&error), &len);
3211 auto successful = res >= 0 && !error;
3222 }
else if (val < 0 && errno == EBADF) {
3232 time_t write_timeout_sec, time_t write_timeout_usec);
3237 ssize_t
read(
char *ptr,
size_t size)
override;
3238 ssize_t
write(
const char *ptr,
size_t size)
override;
3245 time_t read_timeout_sec_;
3246 time_t read_timeout_usec_;
3247 time_t write_timeout_sec_;
3248 time_t write_timeout_usec_;
3250 std::vector<char> read_buff_;
3251 size_t read_buff_off_ = 0;
3252 size_t read_buff_content_size_ = 0;
3254 static const size_t read_buff_size_ = 1024l * 4;
3257 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3258 class SSLSocketStream final :
public Stream {
3260 SSLSocketStream(
socket_t sock, SSL *ssl, time_t read_timeout_sec,
3261 time_t read_timeout_usec, time_t write_timeout_sec,
3262 time_t write_timeout_usec);
3263 ~SSLSocketStream()
override;
3265 bool is_readable()
const override;
3266 bool is_writable()
const override;
3267 ssize_t
read(
char *ptr,
size_t size)
override;
3268 ssize_t
write(
const char *ptr,
size_t size)
override;
3276 time_t read_timeout_sec_;
3277 time_t read_timeout_usec_;
3278 time_t write_timeout_sec_;
3279 time_t write_timeout_usec_;
3284 time_t keep_alive_timeout_sec) {
3285 using namespace std::chrono;
3287 const auto interval_usec =
3291 if (
select_read(sock, 0, interval_usec) > 0) {
return true; }
3293 const auto start = steady_clock::now() - microseconds{interval_usec};
3294 const auto timeout = seconds{keep_alive_timeout_sec};
3304 }
else if (val == 0) {
3305 if (steady_clock::now() - start > timeout) {
3316 template <
typename T>
3319 size_t keep_alive_max_count,
3320 time_t keep_alive_timeout_sec,
T callback) {
3321 assert(keep_alive_max_count > 0);
3323 auto count = keep_alive_max_count;
3324 while (count > 0 &&
keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3325 auto close_connection = count == 1;
3326 auto connection_closed =
false;
3327 ret = callback(close_connection, connection_closed);
3328 if (!
ret || connection_closed) {
break; }
3334 template <
typename T>
3337 size_t keep_alive_max_count,
3338 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
3339 time_t read_timeout_usec, time_t write_timeout_sec,
3340 time_t write_timeout_usec,
T callback) {
3342 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3343 [&](
bool close_connection,
bool &connection_closed) {
3344 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3345 write_timeout_sec, write_timeout_usec);
3346 return callback(strm, close_connection, connection_closed);
3351 time_t read_timeout_usec,
3352 time_t write_timeout_sec,
3353 time_t write_timeout_usec,
3354 std::function<
bool(
Stream &)> callback) {
3355 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3356 write_timeout_sec, write_timeout_usec);
3357 return callback(strm);
3362 return shutdown(sock, SD_BOTH);
3364 return shutdown(sock, SHUT_RDWR);
3369 if (
s.size() > 1 &&
s[0] ==
'\0') {
3379 if (
s.size() > 1 &&
s[0] ==
'@') {
3387 template <
typename BindOrConnect>
3389 int address_family,
int socket_flags,
bool tcp_nodelay,
3391 BindOrConnect bind_or_connect) {
3393 const char *node =
nullptr;
3394 struct addrinfo hints;
3395 struct addrinfo *result;
3397 memset(&hints, 0,
sizeof(
struct addrinfo));
3398 hints.ai_socktype = SOCK_STREAM;
3399 hints.ai_protocol = IPPROTO_IP;
3404 hints.ai_family = AF_UNSPEC;
3405 hints.ai_flags = AI_NUMERICHOST;
3407 if (!host.empty()) { node = host.c_str(); }
3408 hints.ai_family = address_family;
3409 hints.ai_flags = socket_flags;
3413 if (hints.ai_family == AF_UNIX) {
3414 const auto addrlen = host.length();
3415 if (addrlen >
sizeof(sockaddr_un::sun_path)) {
return INVALID_SOCKET; }
3418 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
3421 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3426 addr.sun_family = AF_UNIX;
3429 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3431 hints.ai_addr =
reinterpret_cast<sockaddr *
>(&addr);
3432 hints.ai_addrlen =
static_cast<socklen_t
>(
3433 sizeof(addr) -
sizeof(addr.sun_path) + addrlen);
3435 #ifndef SOCK_CLOEXEC
3436 fcntl(sock, F_SETFD, FD_CLOEXEC);
3439 if (socket_options) { socket_options(sock); }
3442 if (!bind_or_connect(sock, hints, dummy)) {
3453 if (getaddrinfo(node, service.c_str(), &hints, &result)) {
3454 #if defined __linux__ && !defined __ANDROID__
3461 for (
auto rp = result; rp; rp = rp->ai_next) {
3465 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0,
3466 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3482 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3488 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3490 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3496 #if !defined _WIN32 && !defined SOCK_CLOEXEC
3497 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3506 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3507 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3509 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3510 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3514 if (rp->ai_family == AF_INET6) {
3515 auto opt = ipv6_v6only ? 1 : 0;
3517 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3518 reinterpret_cast<const char *
>(&opt),
sizeof(opt));
3520 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3521 reinterpret_cast<const void *
>(&opt),
sizeof(opt));
3525 if (socket_options) { socket_options(sock); }
3529 if (bind_or_connect(sock, *rp, quit)) {
return sock; }
3533 if (quit) {
break; }
3541 auto flags = nonblocking ? 1UL : 0UL;
3542 ioctlsocket(sock, FIONBIO, &flags);
3544 auto flags = fcntl(sock, F_GETFL, 0);
3545 fcntl(sock, F_SETFL,
3546 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3552 return WSAGetLastError() != WSAEWOULDBLOCK;
3554 return errno != EINPROGRESS;
3559 struct addrinfo hints;
3560 struct addrinfo *result;
3562 memset(&hints, 0,
sizeof(
struct addrinfo));
3563 hints.ai_family = AF_UNSPEC;
3564 hints.ai_socktype = SOCK_STREAM;
3565 hints.ai_protocol = 0;
3567 if (getaddrinfo(host.c_str(),
"0", &hints, &result)) {
return false; }
3571 for (
auto rp = result; rp; rp = rp->ai_next) {
3572 const auto &ai = *rp;
3573 if (!::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
3582 #if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3587 inline std::string
if2ip(
int address_family,
const std::string &ifn) {
3588 struct ifaddrs *ifap;
3592 std::string addr_candidate;
3593 for (
auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3594 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3595 (AF_UNSPEC == address_family ||
3596 ifa->ifa_addr->sa_family == address_family)) {
3597 if (ifa->ifa_addr->sa_family == AF_INET) {
3598 auto sa =
reinterpret_cast<struct sockaddr_in *
>(ifa->ifa_addr);
3599 char buf[INET_ADDRSTRLEN];
3600 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3601 return std::string(buf, INET_ADDRSTRLEN);
3603 }
else if (ifa->ifa_addr->sa_family == AF_INET6) {
3604 auto sa =
reinterpret_cast<struct sockaddr_in6 *
>(ifa->ifa_addr);
3605 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
3606 char buf[INET6_ADDRSTRLEN] = {};
3607 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
3609 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3610 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3611 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3613 return std::string(buf, INET6_ADDRSTRLEN);
3620 return addr_candidate;
3625 const std::string &host,
const std::string &ip,
int port,
3626 int address_family,
bool tcp_nodelay,
bool ipv6_v6only,
3627 SocketOptions socket_options, time_t connection_timeout_sec,
3628 time_t connection_timeout_usec, time_t read_timeout_sec,
3629 time_t read_timeout_usec, time_t write_timeout_sec,
3630 time_t write_timeout_usec,
const std::string &intf,
Error &error) {
3632 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
3633 std::move(socket_options),
3634 [&](
socket_t sock2,
struct addrinfo &ai,
bool &quit) ->
bool {
3635 if (!intf.empty()) {
3637 auto ip_from_if = if2ip(address_family, intf);
3638 if (ip_from_if.empty()) { ip_from_if = intf; }
3649 ::connect(sock2, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen));
3657 connection_timeout_usec);
3668 auto timeout =
static_cast<uint32_t
>(read_timeout_sec * 1000 +
3669 read_timeout_usec / 1000);
3670 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
3671 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3674 tv.tv_sec =
static_cast<long>(read_timeout_sec);
3675 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(read_timeout_usec);
3676 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
3677 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3683 auto timeout =
static_cast<uint32_t
>(write_timeout_sec * 1000 +
3684 write_timeout_usec / 1000);
3685 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
3686 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
3689 tv.tv_sec =
static_cast<long>(write_timeout_sec);
3690 tv.tv_usec =
static_cast<decltype(tv.tv_usec)
>(write_timeout_usec);
3691 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
3692 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
3710 socklen_t addr_len, std::string &ip,
int &port) {
3711 if (addr.ss_family == AF_INET) {
3712 port = ntohs(
reinterpret_cast<const struct sockaddr_in *
>(&addr)->sin_port);
3713 }
else if (addr.ss_family == AF_INET6) {
3715 ntohs(
reinterpret_cast<const struct sockaddr_in6 *
>(&addr)->sin6_port);
3720 std::array<char, NI_MAXHOST> ipstr{};
3721 if (getnameinfo(
reinterpret_cast<const struct sockaddr *
>(&addr), addr_len,
3722 ipstr.data(),
static_cast<socklen_t
>(ipstr.size()),
nullptr,
3723 0, NI_NUMERICHOST)) {
3732 struct sockaddr_storage addr;
3733 socklen_t addr_len =
sizeof(addr);
3734 if (!getsockname(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
3741 struct sockaddr_storage addr;
3742 socklen_t addr_len =
sizeof(addr);
3744 if (!getpeername(sock,
reinterpret_cast<struct sockaddr *
>(&addr),
3747 if (addr.ss_family == AF_UNIX) {
3748 #if defined(__linux__)
3750 socklen_t len =
sizeof(ucred);
3751 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
3754 #elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
3756 socklen_t len =
sizeof(pid);
3757 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
3777 static_cast<unsigned char>(*
s));
3786 inline constexpr
unsigned int operator""_t(
const char *
s,
size_t l) {
3794 const std::map<std::string, std::string> &user_data,
3795 const std::string &default_content_type) {
3798 auto it = user_data.find(ext);
3799 if (it != user_data.end()) {
return it->second; }
3801 using udl::operator
""_t;
3804 default:
return default_content_type;
3806 case "css"_t:
return "text/css";
3807 case "csv"_t:
return "text/csv";
3809 case "html"_t:
return "text/html";
3811 case "mjs"_t:
return "text/javascript";
3812 case "txt"_t:
return "text/plain";
3813 case "vtt"_t:
return "text/vtt";
3815 case "apng"_t:
return "image/apng";
3816 case "avif"_t:
return "image/avif";
3817 case "bmp"_t:
return "image/bmp";
3818 case "gif"_t:
return "image/gif";
3819 case "png"_t:
return "image/png";
3820 case "svg"_t:
return "image/svg+xml";
3821 case "webp"_t:
return "image/webp";
3822 case "ico"_t:
return "image/x-icon";
3823 case "tif"_t:
return "image/tiff";
3824 case "tiff"_t:
return "image/tiff";
3826 case "jpeg"_t:
return "image/jpeg";
3828 case "mp4"_t:
return "video/mp4";
3829 case "mpeg"_t:
return "video/mpeg";
3830 case "webm"_t:
return "video/webm";
3832 case "mp3"_t:
return "audio/mp3";
3833 case "mpga"_t:
return "audio/mpeg";
3834 case "weba"_t:
return "audio/webm";
3835 case "wav"_t:
return "audio/wave";
3837 case "otf"_t:
return "font/otf";
3838 case "ttf"_t:
return "font/ttf";
3839 case "woff"_t:
return "font/woff";
3840 case "woff2"_t:
return "font/woff2";
3842 case "7z"_t:
return "application/x-7z-compressed";
3843 case "atom"_t:
return "application/atom+xml";
3844 case "pdf"_t:
return "application/pdf";
3845 case "json"_t:
return "application/json";
3846 case "rss"_t:
return "application/rss+xml";
3847 case "tar"_t:
return "application/x-tar";
3849 case "xhtml"_t:
return "application/xhtml+xml";
3850 case "xslt"_t:
return "application/xslt+xml";
3851 case "xml"_t:
return "application/xml";
3852 case "gz"_t:
return "application/gzip";
3853 case "zip"_t:
return "application/zip";
3854 case "wasm"_t:
return "application/wasm";
3859 using udl::operator
""_t;
3861 auto tag =
str2tag(content_type);
3864 case "image/svg+xml"_t:
3865 case "application/javascript"_t:
3866 case "application/json"_t:
3867 case "application/xml"_t:
3868 case "application/protobuf"_t:
3869 case "application/xhtml+xml"_t:
return true;
3871 case "text/event-stream"_t:
return false;
3873 default:
return !content_type.rfind(
"text/", 0);
3885 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3887 ret =
s.find(
"br") != std::string::npos;
3891 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3893 ret =
s.find(
"gzip") != std::string::npos;
3902 if (!data_length) {
return true; }
3903 return callback(
data, data_length);
3906 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3907 inline gzip_compressor::gzip_compressor() {
3908 std::memset(&strm_, 0,
sizeof(strm_));
3909 strm_.zalloc = Z_NULL;
3910 strm_.zfree = Z_NULL;
3911 strm_.opaque = Z_NULL;
3913 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
3914 Z_DEFAULT_STRATEGY) == Z_OK;
3917 inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3919 inline bool gzip_compressor::compress(
const char *
data,
size_t data_length,
3920 bool last, Callback callback) {
3924 constexpr
size_t max_avail_in =
3925 (std::numeric_limits<decltype(strm_.avail_in)>
::max)();
3927 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
3928 (
std::min)(data_length, max_avail_in));
3929 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(
data));
3931 data_length -= strm_.avail_in;
3932 data += strm_.avail_in;
3934 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
3937 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3939 strm_.avail_out =
static_cast<uInt
>(buff.size());
3940 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
3943 if (
ret == Z_STREAM_ERROR) {
return false; }
3945 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3948 }
while (strm_.avail_out == 0);
3950 assert((
flush == Z_FINISH &&
ret == Z_STREAM_END) ||
3951 (
flush == Z_NO_FLUSH &&
ret == Z_OK));
3952 assert(strm_.avail_in == 0);
3953 }
while (data_length > 0);
3958 inline gzip_decompressor::gzip_decompressor() {
3959 std::memset(&strm_, 0,
sizeof(strm_));
3960 strm_.zalloc = Z_NULL;
3961 strm_.zfree = Z_NULL;
3962 strm_.opaque = Z_NULL;
3968 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
3971 inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3975 inline bool gzip_decompressor::decompress(
const char *
data,
size_t data_length,
3976 Callback callback) {
3982 constexpr
size_t max_avail_in =
3983 (std::numeric_limits<decltype(strm_.avail_in)>
::max)();
3985 strm_.avail_in =
static_cast<decltype(strm_.avail_in)
>(
3986 (
std::min)(data_length, max_avail_in));
3987 strm_.next_in =
const_cast<Bytef *
>(
reinterpret_cast<const Bytef *
>(
data));
3989 data_length -= strm_.avail_in;
3990 data += strm_.avail_in;
3992 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3993 while (strm_.avail_in > 0 &&
ret == Z_OK) {
3994 strm_.avail_out =
static_cast<uInt
>(buff.size());
3995 strm_.next_out =
reinterpret_cast<Bytef *
>(buff.data());
3997 ret = inflate(&strm_, Z_NO_FLUSH);
3999 assert(
ret != Z_STREAM_ERROR);
4003 case Z_MEM_ERROR: inflateEnd(&strm_);
return false;
4006 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
4011 if (
ret != Z_OK &&
ret != Z_STREAM_END) {
return false; }
4013 }
while (data_length > 0);
4019 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4020 inline brotli_compressor::brotli_compressor() {
4021 state_ = BrotliEncoderCreateInstance(
nullptr,
nullptr,
nullptr);
4024 inline brotli_compressor::~brotli_compressor() {
4025 BrotliEncoderDestroyInstance(state_);
4028 inline bool brotli_compressor::compress(
const char *
data,
size_t data_length,
4029 bool last, Callback callback) {
4030 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4032 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
4033 auto available_in = data_length;
4034 auto next_in =
reinterpret_cast<const uint8_t *
>(
data);
4038 if (BrotliEncoderIsFinished(state_)) {
break; }
4040 if (!available_in) {
break; }
4043 auto available_out = buff.size();
4044 auto next_out = buff.data();
4046 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
4047 &available_out, &next_out,
nullptr)) {
4051 auto output_bytes = buff.size() - available_out;
4053 callback(
reinterpret_cast<const char *
>(buff.data()), output_bytes);
4060 inline brotli_decompressor::brotli_decompressor() {
4061 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
4062 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
4063 : BROTLI_DECODER_RESULT_ERROR;
4066 inline brotli_decompressor::~brotli_decompressor() {
4067 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
4072 inline bool brotli_decompressor::decompress(
const char *
data,
4074 Callback callback) {
4075 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4076 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
4080 auto next_in =
reinterpret_cast<const uint8_t *
>(
data);
4081 size_t avail_in = data_length;
4084 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
4086 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4087 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
4088 char *next_out = buff.data();
4089 size_t avail_out = buff.size();
4091 decoder_r = BrotliDecoderDecompressStream(
4092 decoder_s, &avail_in, &next_in, &avail_out,
4093 reinterpret_cast<uint8_t **
>(&next_out), &total_out);
4095 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return false; }
4097 if (!callback(buff.data(), buff.size() - avail_out)) {
return false; }
4100 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4101 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
4106 return headers.find(key) != headers.end();
4110 const std::string &key,
const char *def,
4112 auto rng = headers.equal_range(key);
4113 auto it = rng.first;
4114 std::advance(it,
static_cast<ssize_t
>(
id));
4115 if (it != rng.second) {
return it->second.c_str(); }
4119 template <
typename T>
4127 while (p < end && *p !=
':') {
4131 if (p == end) {
return false; }
4135 if (*p++ !=
':') {
return false; }
4142 auto key_len = key_end - beg;
4143 if (!key_len) {
return false; }
4145 auto key = std::string(beg, key_end);
4147 ? std::string(p, end)
4158 static const std::string CR_LF_NUL(
"\r\n\0", 3);
4159 if (val.find_first_of(CR_LF_NUL) != std::string::npos) {
return false; }
4169 const auto bufsiz = 2048;
4174 if (!line_reader.
getline()) {
return false; }
4177 auto line_terminator_len = 2;
4180 if (line_reader.
size() == 2) {
break; }
4182 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4184 if (line_reader.
size() == 1) {
break; }
4185 line_terminator_len = 1;
4194 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
4197 [&](
const std::string &key, std::string &val) {
4198 headers.emplace(key, val);
4214 auto read_len =
static_cast<size_t>(len - r);
4216 if (
n <= 0) {
return false; }
4218 if (!out(buf,
static_cast<size_t>(
n), r, len)) {
return false; }
4219 r +=
static_cast<uint64_t
>(
n);
4222 if (!progress(r, len)) {
return false; }
4233 auto read_len =
static_cast<size_t>(len - r);
4235 if (
n <= 0) {
return; }
4236 r +=
static_cast<uint64_t
>(
n);
4246 if (
n <= 0) {
return true; }
4248 if (!out(buf,
static_cast<size_t>(
n), r, 0)) {
return false; }
4249 r +=
static_cast<uint64_t
>(
n);
4255 template <
typename T>
4258 const auto bufsiz = 16;
4263 if (!line_reader.
getline()) {
return false; }
4265 unsigned long chunk_len;
4269 chunk_len = std::strtoul(line_reader.
ptr(), &end_ptr, 16);
4271 if (end_ptr == line_reader.
ptr()) {
return false; }
4272 if (chunk_len == ULONG_MAX) {
return false; }
4274 if (chunk_len == 0) {
break; }
4280 if (!line_reader.
getline()) {
return false; }
4282 if (strcmp(line_reader.
ptr(),
"\r\n") != 0) {
return false; }
4284 if (!line_reader.
getline()) {
return false; }
4287 assert(chunk_len == 0);
4301 if (!line_reader.
getline()) {
return true; }
4303 while (strcmp(line_reader.
ptr(),
"\r\n") != 0) {
4307 constexpr
auto line_terminator_len = 2;
4308 auto end = line_reader.
ptr() + line_reader.
size() - line_terminator_len;
4311 [&](
const std::string &key,
const std::string &val) {
4312 x.headers.emplace(key, val);
4315 if (!line_reader.
getline()) {
return false; }
4326 template <
typename T,
typename U>
4329 bool decompress, U callback) {
4331 std::string encoding =
x.get_header_value(
"Content-Encoding");
4334 if (encoding ==
"gzip" || encoding ==
"deflate") {
4335 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4336 decompressor = detail::make_unique<gzip_decompressor>();
4341 }
else if (encoding.find(
"br") != std::string::npos) {
4342 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4343 decompressor = detail::make_unique<brotli_decompressor>();
4353 uint64_t off, uint64_t len) {
4355 [&](
const char *buf2,
size_t n2) {
4356 return receiver(buf2, n2, off, len);
4359 return callback(std::move(out));
4369 return receiver(buf,
n, off, len);
4371 return callback(std::move(out));
4374 template <
typename T>
4379 x,
status, std::move(receiver), decompress,
4382 auto exceed_payload_max_length =
false;
4385 ret = read_content_chunked(strm, x, out);
4387 ret = read_content_without_length(strm, out);
4389 auto len = get_header_value_u64(x.headers,
"Content-Length", 0, 0);
4390 if (len > payload_max_length) {
4391 exceed_payload_max_length = true;
4392 skip_content_with_length(strm, len);
4394 }
else if (len > 0) {
4408 const std::string &path) {
4409 std::string
s = method;
4412 s +=
" HTTP/1.1\r\n";
4413 return strm.
write(
s.data(),
s.size());
4417 std::string
s =
"HTTP/1.1 ";
4422 return strm.
write(
s.data(),
s.size());
4426 ssize_t write_len = 0;
4427 for (
const auto &
x : headers) {
4434 auto len = strm.
write(
s.data(),
s.size());
4435 if (len < 0) {
return len; }
4438 auto len = strm.
write(
"\r\n");
4439 if (len < 0) {
return len; }
4446 while (offset < l) {
4447 auto length = strm.
write(d + offset, l - offset);
4448 if (length < 0) {
return false; }
4449 offset +=
static_cast<size_t>(length);
4454 template <
typename T>
4456 size_t offset,
size_t length,
T is_shutting_down,
4458 size_t end_offset = offset + length;
4462 data_sink.
write = [&](
const char *d,
size_t l) ->
bool {
4475 while (offset < end_offset && !is_shutting_down()) {
4479 }
else if (!content_provider(offset, end_offset - offset, data_sink)) {
4492 template <
typename T>
4494 size_t offset,
size_t length,
4495 const T &is_shutting_down) {
4497 return write_content(strm, content_provider, offset, length, is_shutting_down,
4501 template <
typename T>
4505 const T &is_shutting_down) {
4507 auto data_available =
true;
4511 data_sink.
write = [&](
const char *d,
size_t l) ->
bool {
4521 data_sink.
done = [&](void) { data_available =
false; };
4523 while (data_available && !is_shutting_down()) {
4526 }
else if (!content_provider(offset, 0, data_sink)) {
4535 template <
typename T,
typename U>
4540 auto data_available =
true;
4544 data_sink.
write = [&](
const char *d,
size_t l) ->
bool {
4546 data_available = l > 0;
4549 std::string payload;
4551 [&](
const char *
data,
size_t data_len) {
4552 payload.append(data, data_len);
4555 if (!payload.empty()) {
4560 !
write_data(strm, chunk.data(), chunk.size())) {
4573 auto done_with_trailer = [&](
const Headers *trailer) {
4574 if (!
ok) {
return; }
4576 data_available =
false;
4578 std::string payload;
4580 [&](
const char *
data,
size_t data_len) {
4581 payload.append(
data, data_len);
4588 if (!payload.empty()) {
4590 auto chunk =
from_i_to_hex(payload.size()) +
"\r\n" + payload +
"\r\n";
4592 !
write_data(strm, chunk.data(), chunk.size())) {
4598 static const std::string done_marker(
"0\r\n");
4599 if (!
write_data(strm, done_marker.data(), done_marker.size())) {
4605 for (
const auto &kv : *trailer) {
4606 std::string field_line = kv.first +
": " + kv.second +
"\r\n";
4607 if (!
write_data(strm, field_line.data(), field_line.size())) {
4613 static const std::string crlf(
"\r\n");
4614 if (!
write_data(strm, crlf.data(), crlf.size())) {
ok =
false; }
4617 data_sink.
done = [&](void) { done_with_trailer(
nullptr); };
4620 done_with_trailer(&trailer);
4623 while (data_available && !is_shutting_down()) {
4627 }
else if (!content_provider(offset, 0, data_sink)) {
4640 template <
typename T,
typename U>
4649 template <
typename T>
4651 const std::string &path,
const std::string &location,
4654 new_req.
path = path;
4660 new_req.
body.clear();
4666 auto ret = cli.send(new_req, new_res, error);
4679 for (
auto it = params.begin(); it != params.end(); ++it) {
4680 if (it != params.begin()) { query +=
"&"; }
4690 std::set<std::string> cache;
4691 split(
data,
data + size,
'&', [&](
const char *b,
const char *e) {
4692 std::string kv(b, e);
4693 if (cache.find(kv) != cache.end()) {
return; }
4694 cache.insert(std::move(kv));
4698 divide(b,
static_cast<std::size_t
>(e - b),
'=',
4699 [&](
const char *lhs_data, std::size_t lhs_size,
const char *rhs_data,
4700 std::size_t rhs_size) {
4701 key.assign(lhs_data, lhs_size);
4702 val.assign(rhs_data, rhs_size);
4716 std::string &boundary) {
4717 auto boundary_keyword =
"boundary=";
4718 auto pos = content_type.find(boundary_keyword);
4719 if (pos == std::string::npos) {
return false; }
4720 auto end = content_type.find(
';', pos);
4721 auto beg = pos + strlen(boundary_keyword);
4723 return !boundary.empty();
4727 std::set<std::string> cache;
4728 split(
s.data(),
s.data() +
s.size(),
';', [&](
const char *b,
const char *e) {
4729 std::string kv(b, e);
4730 if (cache.find(kv) != cache.end()) { return; }
4735 split(b, e,
'=', [&](
const char *b2,
const char *e2) {
4744 params.emplace(trim_double_quotes_copy((key)),
4745 trim_double_quotes_copy((val)));
4750 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
4756 return std::all_of(
str.cbegin(),
str.cend(),
4757 [](
unsigned char c) { return std::isdigit(c); });
4760 if (
s.size() > 7 &&
s.compare(0, 6,
"bytes=") == 0) {
4761 const auto pos =
static_cast<size_t>(6);
4762 const auto len =
static_cast<size_t>(
s.size() - 6);
4763 auto all_valid_ranges =
true;
4764 split(&
s[pos], &
s[pos + len],
',', [&](
const char *b,
const char *e) {
4765 if (!all_valid_ranges) {
return; }
4769 all_valid_ranges =
false;
4773 const auto lhs = std::string(b, it);
4774 const auto rhs = std::string(it + 1, e);
4776 all_valid_ranges =
false;
4781 static_cast<ssize_t
>(lhs.empty() ? -1 : std::stoll(lhs));
4783 static_cast<ssize_t
>(rhs.empty() ? -1 : std::stoll(rhs));
4784 if ((first == -1 && last == -1) ||
4785 (first != -1 && last != -1 && first > last)) {
4786 all_valid_ranges =
false;
4790 ranges.emplace_back(first, last);
4792 return all_valid_ranges && !ranges.empty();
4795 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
4798 }
catch (...) {
return false; }
4806 boundary_ = boundary;
4807 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4808 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4818 while (buf_size() > 0) {
4821 buf_erase(buf_find(dash_boundary_crlf_));
4822 if (dash_boundary_crlf_.size() > buf_size()) {
return true; }
4823 if (!buf_start_with(dash_boundary_crlf_)) {
return false; }
4824 buf_erase(dash_boundary_crlf_.size());
4834 auto pos = buf_find(crlf_);
4836 while (pos < buf_size()) {
4839 if (!header_callback(file_)) {
4843 buf_erase(crlf_.size());
4848 const auto header = buf_head(pos);
4850 if (!
parse_header(header.data(), header.data() + header.size(),
4851 [&](
const std::string &,
const std::string &) {})) {
4856 static const std::string header_content_type =
"Content-Type:";
4858 if (start_with_case_ignore(header, header_content_type)) {
4859 file_.content_type =
4860 trim_copy(header.substr(header_content_type.size()));
4862 static const std::regex re_content_disposition(
4863 R
"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4864 std::regex_constants::icase);
4867 if (std::regex_match(header, m, re_content_disposition)) {
4871 auto it = params.find(
"name");
4872 if (it != params.end()) {
4873 file_.name = it->second;
4879 it = params.find(
"filename");
4880 if (it != params.end()) { file_.filename = it->second; }
4882 it = params.find(
"filename*");
4883 if (it != params.end()) {
4885 static const std::regex re_rfc5987_encoding(
4886 R
"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4889 if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
4898 buf_erase(pos + crlf_.size());
4899 pos = buf_find(crlf_);
4901 if (state_ != 3) {
return true; }
4905 if (crlf_dash_boundary_.size() > buf_size()) {
return true; }
4906 auto pos = buf_find(crlf_dash_boundary_);
4907 if (pos < buf_size()) {
4908 if (!content_callback(buf_data(), pos)) {
4912 buf_erase(pos + crlf_dash_boundary_.size());
4915 auto len = buf_size() - crlf_dash_boundary_.size();
4917 if (!content_callback(buf_data(), len)) {
4928 if (crlf_.size() > buf_size()) {
return true; }
4929 if (buf_start_with(crlf_)) {
4930 buf_erase(crlf_.size());
4933 if (dash_.size() > buf_size()) {
return true; }
4934 if (buf_start_with(dash_)) {
4935 buf_erase(dash_.size());
4937 buf_erase(buf_size());
4951 void clear_file_info() {
4953 file_.filename.clear();
4954 file_.content_type.clear();
4957 bool start_with_case_ignore(
const std::string &
a,
4958 const std::string &b)
const {
4959 if (
a.size() < b.size()) {
return false; }
4960 for (
size_t i = 0; i < b.size(); i++) {
4968 const std::string dash_ =
"--";
4969 const std::string crlf_ =
"\r\n";
4970 std::string boundary_;
4971 std::string dash_boundary_crlf_;
4972 std::string crlf_dash_boundary_;
4975 bool is_valid_ =
false;
4976 MultipartFormData file_;
4979 bool start_with(
const std::string &
a,
size_t spos,
size_t epos,
4980 const std::string &b)
const {
4981 if (epos - spos < b.size()) {
return false; }
4982 for (
size_t i = 0; i < b.size(); i++) {
4983 if (
a[i + spos] != b[i]) {
return false; }
4988 size_t buf_size()
const {
return buf_epos_ - buf_spos_; }
4990 const char *buf_data()
const {
return &buf_[buf_spos_]; }
4992 std::string buf_head(
size_t l)
const {
return buf_.substr(buf_spos_, l); }
4994 bool buf_start_with(
const std::string &
s)
const {
4995 return start_with(buf_, buf_spos_, buf_epos_,
s);
4998 size_t buf_find(
const std::string &
s)
const {
5001 size_t off = buf_spos_;
5002 while (off < buf_epos_) {
5005 if (pos == buf_epos_) {
return buf_size(); }
5006 if (buf_[pos] ==
c) {
break; }
5010 auto remaining_size = buf_epos_ - pos;
5011 if (
s.size() > remaining_size) {
return buf_size(); }
5013 if (start_with(buf_, pos, buf_epos_,
s)) {
return pos - buf_spos_; }
5021 void buf_append(
const char *
data,
size_t n) {
5022 auto remaining_size = buf_size();
5023 if (remaining_size > 0 && buf_spos_ > 0) {
5024 for (
size_t i = 0; i < remaining_size; i++) {
5025 buf_[i] = buf_[buf_spos_ + i];
5029 buf_epos_ = remaining_size;
5031 if (remaining_size +
n > buf_.size()) { buf_.resize(remaining_size +
n); }
5033 for (
size_t i = 0; i <
n; i++) {
5034 buf_[buf_epos_ + i] =
data[i];
5039 void buf_erase(
size_t size) { buf_spos_ += size; }
5042 size_t buf_spos_ = 0;
5043 size_t buf_epos_ = 0;
5047 static const char data[] =
5048 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5053 static std::random_device seed_gen;
5056 static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(),
5059 static std::mt19937 engine(seed_sequence);
5062 for (
size_t i = 0; i < length; i++) {
5063 result +=
data[engine() % (
sizeof(
data) - 1)];
5074 for (
size_t i = 0; i < boundary.size(); i++) {
5075 auto c = boundary[i];
5076 if (!std::isalnum(
c) &&
c !=
'-' &&
c !=
'_') {
5084 template <
typename T>
5087 const std::string &boundary) {
5088 std::string body =
"--" + boundary +
"\r\n";
5089 body +=
"Content-Disposition: form-data; name=\"" + item.name +
"\"";
5090 if (!item.filename.empty()) {
5091 body +=
"; filename=\"" + item.filename +
"\"";
5094 if (!item.content_type.empty()) {
5095 body +=
"Content-Type: " + item.content_type +
"\r\n";
5106 return "--" + boundary +
"--\r\n";
5111 return "multipart/form-data; boundary=" + boundary;
5116 const std::string &boundary,
bool finish =
true) {
5119 for (
const auto &item : items) {
5131 ssize_t contant_len =
static_cast<ssize_t
>(
5134 ssize_t prev_first_pos = -1;
5135 ssize_t prev_last_pos = -1;
5136 size_t overwrapping_count = 0;
5145 for (
auto &r : req.
ranges) {
5146 auto &first_pos = r.first;
5147 auto &last_pos = r.second;
5149 if (first_pos == -1 && last_pos == -1) {
5151 last_pos = contant_len;
5154 if (first_pos == -1) {
5155 first_pos = contant_len - last_pos;
5156 last_pos = contant_len - 1;
5159 if (last_pos == -1) { last_pos = contant_len - 1; }
5162 if (!(0 <= first_pos && first_pos <= last_pos &&
5163 last_pos <= contant_len - 1)) {
5168 if (first_pos <= prev_first_pos) {
return true; }
5171 if (first_pos <= prev_last_pos) {
5172 overwrapping_count++;
5173 if (overwrapping_count > 2) {
return true; }
5176 prev_first_pos = (
std::max)(prev_first_pos, first_pos);
5177 prev_last_pos = (
std::max)(prev_last_pos, last_pos);
5184 inline std::pair<size_t, size_t>
5186 assert(r.first != -1 && r.second != -1);
5187 assert(0 <= r.first && r.first <
static_cast<ssize_t
>(content_length));
5188 assert(r.first <= r.second &&
5189 r.second <
static_cast<ssize_t
>(content_length));
5190 (void)(content_length);
5191 return std::make_pair(r.first,
static_cast<size_t>(r.second - r.first) + 1);
5195 const std::pair<size_t, size_t> &offset_and_length,
size_t content_length) {
5196 auto st = offset_and_length.first;
5197 auto ed = st + offset_and_length.second - 1;
5199 std::string field =
"bytes ";
5208 template <
typename SToken,
typename CToken,
typename Content>
5210 const std::string &boundary,
5211 const std::string &content_type,
5212 size_t content_length, SToken stoken,
5213 CToken ctoken, Content content) {
5214 for (
size_t i = 0; i < req.
ranges.size(); i++) {
5218 if (!content_type.empty()) {
5219 ctoken(
"Content-Type: ");
5220 stoken(content_type);
5224 auto offset_and_length =
5227 ctoken(
"Content-Range: ");
5232 if (!content(offset_and_length.first, offset_and_length.second)) {
5246 const std::string &boundary,
5247 const std::string &content_type,
5248 size_t content_length,
5249 std::string &
data) {
5251 req, boundary, content_type, content_length,
5252 [&](
const std::string &token) {
data += token; },
5253 [&](
const std::string &token) {
data += token; },
5254 [&](
size_t offset,
size_t length) {
5255 assert(offset + length <= content_length);
5256 data += res.
body.substr(offset, length);
5262 const std::string &boundary,
5263 const std::string &content_type,
5264 size_t content_length) {
5265 size_t data_length = 0;
5268 req, boundary, content_type, content_length,
5269 [&](
const std::string &token) { data_length += token.size(); },
5270 [&](
const std::string &token) { data_length += token.size(); },
5271 [&](
size_t ,
size_t length) {
5272 data_length += length;
5279 template <
typename T>
5282 const std::string &boundary,
5283 const std::string &content_type,
5284 size_t content_length,
const T &is_shutting_down) {
5286 req, boundary, content_type, content_length,
5287 [&](
const std::string &token) { strm.
write(token); },
5288 [&](
const std::string &token) { strm.
write(token); },
5289 [&](
size_t offset,
size_t length) {
5307 if (*p ==
'\r' || *p ==
'\n') {
return true; }
5313 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5314 inline std::string message_digest(
const std::string &
s,
const EVP_MD *algo) {
5315 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
5316 EVP_MD_CTX_new(), EVP_MD_CTX_free);
5318 unsigned int hash_length = 0;
5319 unsigned char hash[EVP_MAX_MD_SIZE];
5321 EVP_DigestInit_ex(context.get(), algo,
nullptr);
5322 EVP_DigestUpdate(context.get(),
s.c_str(),
s.size());
5323 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
5325 std::stringstream ss;
5326 for (
auto i = 0u; i < hash_length; ++i) {
5327 ss << std::hex << std::setw(2) << std::setfill(
'0')
5328 <<
static_cast<unsigned int>(hash[i]);
5334 inline std::string MD5(
const std::string &
s) {
5335 return message_digest(
s, EVP_md5());
5338 inline std::string SHA_256(
const std::string &
s) {
5339 return message_digest(
s, EVP_sha256());
5342 inline std::string SHA_512(
const std::string &
s) {
5343 return message_digest(
s, EVP_sha512());
5347 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5351 inline bool load_system_certs_on_windows(X509_STORE *
store) {
5352 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L
"ROOT");
5353 if (!hStore) {
return false; }
5355 auto result =
false;
5356 PCCERT_CONTEXT pContext = NULL;
5357 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
5360 static_cast<const unsigned char *
>(pContext->pbCertEncoded);
5362 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5364 X509_STORE_add_cert(
store, x509);
5370 CertFreeCertificateContext(pContext);
5371 CertCloseStore(hStore, 0);
5375 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5377 template <
typename T>
5379 std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
5381 inline void cf_object_ptr_deleter(CFTypeRef obj) {
5382 if (obj) { CFRelease(obj); }
5385 inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5386 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
5387 CFTypeRef
values[] = {kSecClassCertificate, kSecMatchLimitAll,
5390 CFObjectPtr<CFDictionaryRef> query(
5391 CFDictionaryCreate(
nullptr,
reinterpret_cast<const void **
>(keys),
values,
5392 sizeof(keys) /
sizeof(keys[0]),
5393 &kCFTypeDictionaryKeyCallBacks,
5394 &kCFTypeDictionaryValueCallBacks),
5395 cf_object_ptr_deleter);
5397 if (!query) {
return false; }
5399 CFTypeRef security_items =
nullptr;
5400 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
5401 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
5405 certs.reset(
reinterpret_cast<CFArrayRef
>(security_items));
5409 inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5410 CFArrayRef root_security_items =
nullptr;
5411 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
5415 certs.reset(root_security_items);
5419 inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *
store) {
5420 auto result =
false;
5421 for (
auto i = 0; i < CFArrayGetCount(certs); ++i) {
5422 const auto cert =
reinterpret_cast<const __SecCertificate *
>(
5423 CFArrayGetValueAtIndex(certs, i));
5425 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) {
continue; }
5427 CFDataRef cert_data =
nullptr;
5428 if (SecItemExport(cert, kSecFormatX509Cert, 0,
nullptr, &cert_data) !=
5433 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
5435 auto encoded_cert =
static_cast<const unsigned char *
>(
5436 CFDataGetBytePtr(cert_data_ptr.get()));
5439 d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
5442 X509_STORE_add_cert(
store, x509);
5451 inline bool load_system_certs_on_macos(X509_STORE *
store) {
5452 auto result =
false;
5453 CFObjectPtr<CFArrayRef> certs(
nullptr, cf_object_ptr_deleter);
5454 if (retrieve_certs_from_keychain(certs) && certs) {
5455 result = add_certs_to_x509_store(certs.get(),
store);
5458 if (retrieve_root_certs_from_keychain(certs) && certs) {
5459 result = add_certs_to_x509_store(certs.get(),
store) || result;
5464 #endif // TARGET_OS_OSX
5466 #endif // CPPHTTPLIB_OPENSSL_SUPPORT
5473 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ =
true;
5477 if (is_valid_) WSACleanup();
5480 bool is_valid_ =
false;
5483 static WSInit wsinit_;
5486 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5487 inline std::pair<std::string, std::string> make_digest_authentication_header(
5488 const Request &req,
const std::map<std::string, std::string> &auth,
5489 size_t cnonce_count,
const std::string &cnonce,
const std::string &username,
5490 const std::string &password,
bool is_proxy =
false) {
5493 std::stringstream ss;
5494 ss << std::setfill(
'0') << std::setw(8) << std::hex << cnonce_count;
5499 if (auth.find(
"qop") != auth.end()) {
5500 qop = auth.at(
"qop");
5501 if (qop.find(
"auth-int") != std::string::npos) {
5503 }
else if (qop.find(
"auth") != std::string::npos) {
5510 std::string algo =
"MD5";
5511 if (auth.find(
"algorithm") != auth.end()) { algo = auth.at(
"algorithm"); }
5513 std::string response;
5515 auto H = algo ==
"SHA-256" ? detail::SHA_256
5516 : algo ==
"SHA-512" ? detail::SHA_512
5519 auto A1 = username +
":" + auth.at(
"realm") +
":" + password;
5521 auto A2 = req.method +
":" + req.path;
5522 if (qop ==
"auth-int") { A2 +=
":" + H(req.body); }
5525 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + H(A2));
5527 response = H(H(A1) +
":" + auth.at(
"nonce") +
":" + nc +
":" + cnonce +
5528 ":" + qop +
":" + H(A2));
5532 auto opaque = (auth.find(
"opaque") != auth.end()) ? auth.at(
"opaque") :
"";
5534 auto field =
"Digest username=\"" + username +
"\", realm=\"" +
5535 auth.at(
"realm") +
"\", nonce=\"" + auth.at(
"nonce") +
5536 "\", uri=\"" + req.path +
"\", algorithm=" + algo +
5537 (qop.empty() ?
", response=\""
5538 :
", qop=" + qop +
", nc=" + nc +
", cnonce=\"" +
5539 cnonce +
"\", response=\"") +
5541 (opaque.empty() ?
"" :
", opaque=\"" + opaque +
"\"");
5543 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5544 return std::make_pair(key, field);
5549 std::map<std::string, std::string> &auth,
5551 auto auth_key = is_proxy ?
"Proxy-Authenticate" :
"WWW-Authenticate";
5553 static auto re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5555 auto pos =
s.find(
' ');
5556 if (pos != std::string::npos) {
5557 auto type =
s.substr(0, pos);
5558 if (type ==
"Basic") {
5560 }
else if (type ==
"Digest") {
5561 s =
s.substr(pos + 1);
5562 auto beg = std::sregex_iterator(
s.begin(),
s.end(), re);
5563 for (
auto i = beg; i != std::sregex_iterator(); ++i) {
5565 auto key =
s.substr(
static_cast<size_t>(m.position(1)),
5566 static_cast<size_t>(m.length(1)));
5567 auto val = m.length(2) > 0
5568 ?
s.substr(
static_cast<size_t>(m.position(2)),
5569 static_cast<size_t>(m.length(2)))
5570 :
s.substr(
static_cast<size_t>(m.position(3)),
5571 static_cast<size_t>(m.length(3)));
5585 : content_provider_(content_provider) {}
5588 return content_provider_(offset, sink);
5598 std::vector<std::string> addrs;
5600 if (addrs.empty()) {
return std::string(); }
5605 std::vector<std::string> &addrs) {
5606 struct addrinfo hints;
5607 struct addrinfo *result;
5609 memset(&hints, 0,
sizeof(
struct addrinfo));
5610 hints.ai_family = AF_UNSPEC;
5611 hints.ai_socktype = SOCK_STREAM;
5612 hints.ai_protocol = 0;
5614 if (getaddrinfo(hostname.c_str(),
nullptr, &hints, &result)) {
5615 #if defined __linux__ && !defined __ANDROID__
5622 for (
auto rp = result; rp; rp = rp->ai_next) {
5624 *
reinterpret_cast<struct sockaddr_storage *
>(rp->ai_addr);
5629 addrs.push_back(ip);
5636 std::string path_with_query = path;
5637 const static std::regex re(
"[^?]+\\?.*");
5638 auto delm = std::regex_match(path, re) ?
'&' :
'?';
5640 return path_with_query;
5644 inline std::pair<std::string, std::string>
5646 std::string field =
"bytes=";
5648 for (
const auto &r : ranges) {
5649 if (i != 0) { field +=
", "; }
5655 return std::make_pair(
"Range", std::move(field));
5658 inline std::pair<std::string, std::string>
5660 const std::string &password,
bool is_proxy) {
5662 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5663 return std::make_pair(key, std::move(field));
5666 inline std::pair<std::string, std::string>
5668 bool is_proxy =
false) {
5669 auto field =
"Bearer " + token;
5670 auto key = is_proxy ?
"Proxy-Authorization" :
"Authorization";
5671 return std::make_pair(key, std::move(field));
5680 const char *def,
size_t id)
const {
5684 inline size_t Request::get_header_value_count(
const std::string &key)
const {
5685 auto r = headers.equal_range(key);
5686 return static_cast<size_t>(
std::distance(r.first, r.second));
5689 inline void Request::set_header(
const std::string &key,
5690 const std::string &val) {
5692 headers.emplace(key, val);
5696 inline bool Request::has_param(
const std::string &key)
const {
5697 return params.find(key) != params.end();
5700 inline std::string Request::get_param_value(
const std::string &key,
5702 auto rng = params.equal_range(key);
5703 auto it = rng.first;
5704 std::advance(it,
static_cast<ssize_t
>(
id));
5705 if (it != rng.second) {
return it->second; }
5706 return std::string();
5709 inline size_t Request::get_param_value_count(
const std::string &key)
const {
5710 auto r = params.equal_range(key);
5711 return static_cast<size_t>(
std::distance(r.first, r.second));
5714 inline bool Request::is_multipart_form_data()
const {
5716 return !content_type.rfind(
"multipart/form-data", 0);
5719 inline bool Request::has_file(
const std::string &key)
const {
5724 auto it =
files.find(key);
5725 if (it !=
files.end()) {
return it->second; }
5729 inline std::vector<MultipartFormData>
5730 Request::get_file_values(
const std::string &key)
const {
5731 std::vector<MultipartFormData>
values;
5732 auto rng =
files.equal_range(key);
5733 for (
auto it = rng.first; it != rng.second; it++) {
5734 values.push_back(it->second);
5741 return headers.find(key) != headers.end();
5750 inline size_t Response::get_header_value_count(
const std::string &key)
const {
5751 auto r = headers.equal_range(key);
5752 return static_cast<size_t>(
std::distance(r.first, r.second));
5755 inline void Response::set_header(
const std::string &key,
5756 const std::string &val) {
5758 headers.emplace(key, val);
5762 inline void Response::set_redirect(
const std::string &url,
int stat) {
5764 set_header(
"Location", url);
5765 if (300 <= stat && stat < 400) {
5773 inline void Response::set_content(
const char *
s,
size_t n,
5774 const std::string &content_type) {
5777 auto rng = headers.equal_range(
"Content-Type");
5778 headers.erase(rng.first, rng.second);
5779 set_header(
"Content-Type", content_type);
5782 inline void Response::set_content(
const std::string &
s,
5783 const std::string &content_type) {
5784 set_content(
s.data(),
s.size(), content_type);
5787 inline void Response::set_content(std::string &&
s,
5788 const std::string &content_type) {
5789 body = std::move(
s);
5791 auto rng = headers.equal_range(
"Content-Type");
5792 headers.erase(rng.first, rng.second);
5793 set_header(
"Content-Type", content_type);
5796 inline void Response::set_content_provider(
5797 size_t in_length,
const std::string &content_type,
ContentProvider provider,
5799 set_header(
"Content-Type", content_type);
5800 content_length_ = in_length;
5801 if (in_length > 0) { content_provider_ = std::move(provider); }
5802 content_provider_resource_releaser_ = std::move(resource_releaser);
5803 is_chunked_content_provider_ =
false;
5806 inline void Response::set_content_provider(
5809 set_header(
"Content-Type", content_type);
5810 content_length_ = 0;
5812 content_provider_resource_releaser_ = std::move(resource_releaser);
5813 is_chunked_content_provider_ =
false;
5816 inline void Response::set_chunked_content_provider(
5819 set_header(
"Content-Type", content_type);
5820 content_length_ = 0;
5822 content_provider_resource_releaser_ = std::move(resource_releaser);
5823 is_chunked_content_provider_ =
true;
5826 inline void Response::set_file_content(
const std::string &path,
5827 const std::string &content_type) {
5828 file_content_path_ = path;
5829 file_content_content_type_ = content_type;
5832 inline void Response::set_file_content(
const std::string &path) {
5833 file_content_path_ = path;
5837 inline bool Result::has_request_header(
const std::string &key)
const {
5838 return request_headers_.find(key) != request_headers_.end();
5841 inline std::string Result::get_request_header_value(
const std::string &key,
5848 Result::get_request_header_value_count(
const std::string &key)
const {
5849 auto r = request_headers_.equal_range(key);
5850 return static_cast<size_t>(
std::distance(r.first, r.second));
5855 return write(ptr, strlen(ptr));
5859 return write(
s.data(),
s.size());
5865 inline SocketStream::SocketStream(
socket_t sock, time_t read_timeout_sec,
5866 time_t read_timeout_usec,
5867 time_t write_timeout_sec,
5868 time_t write_timeout_usec)
5869 : sock_(sock), read_timeout_sec_(read_timeout_sec),
5870 read_timeout_usec_(read_timeout_usec),
5871 write_timeout_sec_(write_timeout_sec),
5872 write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
5877 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
5881 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
5894 if (read_buff_off_ < read_buff_content_size_) {
5895 auto remaining_size = read_buff_content_size_ - read_buff_off_;
5896 if (size <= remaining_size) {
5897 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
5898 read_buff_off_ += size;
5899 return static_cast<ssize_t
>(size);
5901 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
5902 read_buff_off_ += remaining_size;
5903 return static_cast<ssize_t
>(remaining_size);
5910 read_buff_content_size_ = 0;
5912 if (size < read_buff_size_) {
5913 auto n =
read_socket(sock_, read_buff_.data(), read_buff_size_,
5917 }
else if (
n <=
static_cast<ssize_t
>(size)) {
5918 memcpy(ptr, read_buff_.data(),
static_cast<size_t>(
n));
5921 memcpy(ptr, read_buff_.data(), size);
5922 read_buff_off_ = size;
5923 read_buff_content_size_ =
static_cast<size_t>(
n);
5924 return static_cast<ssize_t
>(size);
5934 #if defined(_WIN32) && !defined(_WIN64)
5960 #if defined(_MSC_VER) && _MSC_VER < 1910
5961 auto len_read = buffer._Copy_s(ptr, size, size, position);
5963 auto len_read = buffer.copy(ptr, size, position);
5965 position +=
static_cast<size_t>(len_read);
5966 return static_cast<ssize_t
>(len_read);
5970 buffer.append(ptr, size);
5971 return static_cast<ssize_t
>(size);
5985 static constexpr
char marker[] =
"/:";
5988 std::size_t last_param_end = 0;
5990 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
5995 std::unordered_set<std::string> param_name_set;
5999 const auto marker_pos = pattern.find(
6000 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
6001 if (marker_pos == std::string::npos) {
break; }
6003 static_fragments_.push_back(
6004 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
6006 const auto param_name_start = marker_pos + 2;
6008 auto sep_pos = pattern.find(separator, param_name_start);
6009 if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
6012 pattern.substr(param_name_start, sep_pos - param_name_start);
6014 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
6015 if (param_name_set.find(param_name) != param_name_set.cend()) {
6016 std::string msg =
"Encountered path parameter '" + param_name +
6017 "' multiple times in route pattern '" + pattern +
"'.";
6018 throw std::invalid_argument(msg);
6022 param_names_.push_back(std::move(param_name));
6024 last_param_end = sep_pos + 1;
6027 if (last_param_end < pattern.length()) {
6028 static_fragments_.push_back(pattern.substr(last_param_end));
6033 request.
matches = std::smatch();
6038 std::size_t starting_pos = 0;
6039 for (
size_t i = 0; i < static_fragments_.size(); ++i) {
6040 const auto &fragment = static_fragments_[i];
6042 if (starting_pos + fragment.length() > request.
path.length()) {
6048 if (std::strncmp(request.
path.c_str() + starting_pos, fragment.c_str(),
6049 fragment.length()) != 0) {
6053 starting_pos += fragment.length();
6058 if (i >= param_names_.size()) {
continue; }
6060 auto sep_pos = request.
path.find(separator, starting_pos);
6061 if (sep_pos == std::string::npos) { sep_pos = request.
path.length(); }
6063 const auto ¶m_name = param_names_[i];
6066 param_name, request.
path.substr(starting_pos, sep_pos - starting_pos));
6069 starting_pos = sep_pos + 1;
6072 return starting_pos >= request.
path.length();
6077 return std::regex_match(request.
path, request.
matches, regex_);
6087 signal(SIGPIPE, SIG_IGN);
6093 inline std::unique_ptr<detail::MatcherBase>
6094 Server::make_matcher(
const std::string &pattern) {
6095 if (pattern.find(
"/:") != std::string::npos) {
6096 return detail::make_unique<detail::PathParamsMatcher>(pattern);
6098 return detail::make_unique<detail::RegexMatcher>(pattern);
6103 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6108 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6114 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6115 std::move(handler));
6120 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6126 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6127 std::move(handler));
6132 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6138 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6139 std::move(handler));
6144 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6150 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6151 std::move(handler));
6156 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6161 const std::string &mount_point) {
6166 const std::string &dir,
Headers headers) {
6169 std::string mnt = !mount_point.empty() ? mount_point :
"/";
6170 if (!mnt.empty() && mnt[0] ==
'/') {
6171 base_dirs_.push_back({mnt, dir, std::move(headers)});
6179 for (
auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
6180 if (it->mount_point == mount_point) {
6181 base_dirs_.erase(it);
6190 const std::string &mime) {
6191 file_extension_and_mimetype_map_[ext] = mime;
6196 default_file_mimetype_ = mime;
6201 file_request_handler_ = std::move(handler);
6205 inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
6207 error_handler_ = std::move(handler);
6211 inline Server &Server::set_error_handler_core(Handler handler,
6213 error_handler_ = [handler](
const Request &req,
Response &res) {
6221 exception_handler_ = std::move(handler);
6226 pre_routing_handler_ = std::move(handler);
6231 post_routing_handler_ = std::move(handler);
6236 logger_ = std::move(logger);
6242 expect_100_continue_handler_ = std::move(handler);
6247 address_family_ = family;
6262 socket_options_ = std::move(socket_options);
6267 default_headers_ = std::move(headers);
6272 std::function<ssize_t(
Stream &,
Headers &)>
const &writer) {
6273 header_writer_ = writer;
6312 auto ret = bind_internal(host, port, socket_flags);
6313 if (
ret == -1) { is_decommisioned =
true; }
6317 auto ret = bind_internal(host, 0, socket_flags);
6318 if (
ret == -1) { is_decommisioned =
true; }
6326 return bind_to_port(host, port, socket_flags) && listen_internal();
6332 while (!is_running_ && !is_decommisioned) {
6333 std::this_thread::sleep_for(std::chrono::milliseconds{1});
6344 is_decommisioned =
false;
6349 inline bool Server::parse_request_line(
const char *
s,
Request &req)
const {
6350 auto len = strlen(
s);
6351 if (len < 2 ||
s[len - 2] !=
'\r' ||
s[len - 1] !=
'\n') {
return false; }
6359 case 0: req.
method = std::string(b, e);
break;
6360 case 1: req.
target = std::string(b, e);
break;
6361 case 2: req.
version = std::string(b, e);
break;
6367 if (count != 3) {
return false; }
6370 static const std::set<std::string> methods{
6371 "GET",
"HEAD",
"POST",
"PUT",
"DELETE",
6372 "CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
"PRI"};
6374 if (methods.find(req.
method) == methods.end()) {
return false; }
6376 if (req.
version !=
"HTTP/1.1" && req.
version !=
"HTTP/1.0") {
return false; }
6380 for (
size_t i = 0; i < req.
target.size(); i++) {
6381 if (req.
target[i] ==
'#') {
6388 [&](
const char *lhs_data, std::size_t lhs_size,
6389 const char *rhs_data, std::size_t rhs_size) {
6390 req.path = detail::decode_url(
6391 std::string(lhs_data, lhs_size), false);
6392 detail::parse_query_text(rhs_data, rhs_size, req.params);
6399 inline bool Server::write_response(
Stream &strm,
bool close_connection,
6404 return write_response_core(strm, close_connection, req, res,
false);
6407 inline bool Server::write_response_with_content(
Stream &strm,
6408 bool close_connection,
6411 return write_response_core(strm, close_connection, req, res,
true);
6414 inline bool Server::write_response_core(
Stream &strm,
bool close_connection,
6416 bool need_apply_ranges) {
6417 assert(res.
status != -1);
6419 if (400 <= res.
status && error_handler_ &&
6421 need_apply_ranges =
true;
6424 std::string content_type;
6425 std::string boundary;
6426 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
6429 if (close_connection || req.get_header_value(
"Connection") ==
"close") {
6430 res.set_header(
"Connection",
"close");
6432 std::string
s =
"timeout=";
6436 res.set_header(
"Keep-Alive",
s);
6439 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
6440 !res.has_header(
"Content-Type")) {
6441 res.set_header(
"Content-Type",
"text/plain");
6444 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
6445 !res.has_header(
"Content-Length")) {
6446 res.set_header(
"Content-Length",
"0");
6449 if (req.method ==
"HEAD" && !res.has_header(
"Accept-Ranges")) {
6450 res.set_header(
"Accept-Ranges",
"bytes");
6453 if (post_routing_handler_) { post_routing_handler_(req, res); }
6457 detail::BufferStream bstrm;
6459 if (!header_writer_(bstrm, res.headers)) {
return false; }
6462 auto &
data = bstrm.get_buffer();
6468 if (req.method !=
"HEAD") {
6469 if (!res.body.empty()) {
6473 }
else if (res.content_provider_) {
6474 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6475 res.content_provider_success_ =
true;
6483 if (logger_) { logger_(req, res); }
6489 Server::write_content_with_provider(
Stream &strm,
const Request &req,
6490 Response &res,
const std::string &boundary,
6491 const std::string &content_type) {
6492 auto is_shutting_down = [
this]() {
6496 if (res.content_length_ > 0) {
6497 if (req.ranges.empty()) {
6499 res.content_length_, is_shutting_down);
6500 }
else if (req.ranges.size() == 1) {
6502 req.ranges[0], res.content_length_);
6505 offset_and_length.first,
6506 offset_and_length.second, is_shutting_down);
6509 strm, req, res, boundary, content_type, res.content_length_,
6513 if (res.is_chunked_content_provider_) {
6516 std::unique_ptr<detail::compressor> compressor;
6518 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
6519 compressor = detail::make_unique<detail::gzip_compressor>();
6522 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
6523 compressor = detail::make_unique<detail::brotli_compressor>();
6526 compressor = detail::make_unique<detail::nocompressor>();
6528 assert(compressor !=
nullptr);
6531 is_shutting_down, *compressor);
6539 inline bool Server::read_content(
Stream &strm, Request &req,
Response &res) {
6540 MultipartFormDataMap::iterator cur;
6541 auto file_count = 0;
6542 if (read_content_core(
6545 [&](
const char *buf,
size_t n) {
6546 if (req.body.size() +
n > req.body.max_size()) { return false; }
6547 req.body.append(buf,
n);
6551 [&](
const MultipartFormData &file) {
6555 cur = req.files.emplace(file.name, file);
6558 [&](
const char *buf,
size_t n) {
6559 auto &content = cur->second.content;
6560 if (content.size() +
n > content.max_size()) {
return false; }
6561 content.append(buf,
n);
6564 const auto &content_type = req.get_header_value(
"Content-Type");
6565 if (!content_type.find(
"application/x-www-form-urlencoded")) {
6577 inline bool Server::read_content_with_content_receiver(
6581 return read_content_core(strm, req, res, std::move(receiver),
6582 std::move(multipart_header),
6583 std::move(multipart_receiver));
6587 Server::read_content_core(
Stream &strm, Request &req,
Response &res,
6591 detail::MultipartFormDataParser multipart_form_data_parser;
6594 if (req.is_multipart_form_data()) {
6595 const auto &content_type = req.get_header_value(
"Content-Type");
6596 std::string boundary;
6602 multipart_form_data_parser.set_boundary(std::move(boundary));
6603 out = [&](
const char *buf,
size_t n, uint64_t , uint64_t ) {
6615 return multipart_form_data_parser.parse(buf,
n, multipart_receiver,
6619 out = [receiver](
const char *buf,
size_t n, uint64_t ,
6620 uint64_t ) {
return receiver(buf,
n); };
6623 if (req.method ==
"DELETE" && !req.has_header(
"Content-Length")) {
6632 if (req.is_multipart_form_data()) {
6633 if (!multipart_form_data_parser.is_valid()) {
6642 inline bool Server::handle_file_request(
const Request &req,
Response &res,
6644 for (
const auto &entry : base_dirs_) {
6646 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
6647 std::string sub_path =
"/" + req.path.substr(entry.mount_point.size());
6649 auto path = entry.base_dir + sub_path;
6650 if (path.back() ==
'/') { path +=
"index.html"; }
6652 detail::FileStat stat(path);
6654 if (stat.is_dir()) {
6659 if (stat.is_file()) {
6660 for (
const auto &kv : entry.headers) {
6661 res.set_header(kv.first, kv.second);
6664 auto mm = std::make_shared<detail::mmap>(path.c_str());
6665 if (!mm->is_open()) {
return false; }
6667 res.set_content_provider(
6670 default_file_mimetype_),
6671 [mm](
size_t offset,
size_t length, DataSink &sink) ->
bool {
6672 sink.write(mm->data() + offset, length);
6676 if (!head && file_request_handler_) {
6677 file_request_handler_(req, res);
6689 Server::create_server_socket(
const std::string &host,
int port,
6693 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
6694 ipv6_v6only_, std::move(socket_options),
6695 [](
socket_t sock,
struct addrinfo &ai,
bool & ) ->
bool {
6696 if (::bind(sock, ai.ai_addr,
static_cast<socklen_t
>(ai.ai_addrlen))) {
6704 inline int Server::bind_internal(
const std::string &host,
int port,
6706 if (is_decommisioned) {
return -1; }
6710 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
6714 struct sockaddr_storage addr;
6715 socklen_t addr_len =
sizeof(addr);
6716 if (getsockname(
svr_sock_,
reinterpret_cast<struct sockaddr *
>(&addr),
6720 if (addr.ss_family == AF_INET) {
6721 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&addr)->sin_port);
6722 }
else if (addr.ss_family == AF_INET6) {
6723 return ntohs(
reinterpret_cast<struct sockaddr_in6 *
>(&addr)->sin6_port);
6732 inline bool Server::listen_internal() {
6733 if (is_decommisioned) {
return false; }
6737 auto se = detail::scope_exit([&]() { is_running_ =
false; });
6749 task_queue->on_idle();
6760 #elif defined SOCK_CLOEXEC
6767 if (errno == EMFILE) {
6770 std::this_thread::sleep_for(std::chrono::microseconds{1});
6772 }
else if (errno == EINTR || errno == EAGAIN) {
6788 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6789 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6794 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6795 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6803 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6804 reinterpret_cast<const char *
>(&timeout),
sizeof(timeout));
6809 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6810 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
6814 if (!task_queue->enqueue(
6815 [
this, sock]() { process_and_close_socket(sock); })) {
6821 task_queue->shutdown();
6824 is_decommisioned = !
ret;
6828 inline bool Server::routing(Request &req,
Response &res,
Stream &strm) {
6829 if (pre_routing_handler_ &&
6835 auto is_head_request = req.method ==
"HEAD";
6836 if ((req.method ==
"GET" || is_head_request) &&
6837 handle_file_request(req, res, is_head_request)) {
6844 ContentReader reader(
6846 return read_content_with_content_receiver(
6847 strm, req, res, std::move(receiver),
nullptr,
nullptr);
6850 return read_content_with_content_receiver(strm, req, res,
nullptr,
6852 std::move(receiver));
6855 if (req.method ==
"POST") {
6856 if (dispatch_request_for_content_reader(
6857 req, res, std::move(reader),
6858 post_handlers_for_content_reader_)) {
6861 }
else if (req.method ==
"PUT") {
6862 if (dispatch_request_for_content_reader(
6863 req, res, std::move(reader),
6864 put_handlers_for_content_reader_)) {
6867 }
else if (req.method ==
"PATCH") {
6868 if (dispatch_request_for_content_reader(
6869 req, res, std::move(reader),
6870 patch_handlers_for_content_reader_)) {
6873 }
else if (req.method ==
"DELETE") {
6874 if (dispatch_request_for_content_reader(
6875 req, res, std::move(reader),
6876 delete_handlers_for_content_reader_)) {
6887 if (req.method ==
"GET" || req.method ==
"HEAD") {
6888 return dispatch_request(req, res, get_handlers_);
6889 }
else if (req.method ==
"POST") {
6890 return dispatch_request(req, res, post_handlers_);
6891 }
else if (req.method ==
"PUT") {
6892 return dispatch_request(req, res, put_handlers_);
6893 }
else if (req.method ==
"DELETE") {
6894 return dispatch_request(req, res, delete_handlers_);
6895 }
else if (req.method ==
"OPTIONS") {
6896 return dispatch_request(req, res, options_handlers_);
6897 }
else if (req.method ==
"PATCH") {
6898 return dispatch_request(req, res, patch_handlers_);
6905 inline bool Server::dispatch_request(Request &req,
Response &res,
6906 const Handlers &handlers)
const {
6907 for (
const auto &
x : handlers) {
6908 const auto &matcher =
x.first;
6909 const auto &handler =
x.second;
6911 if (matcher->match(req)) {
6919 inline void Server::apply_ranges(
const Request &req,
Response &res,
6920 std::string &content_type,
6921 std::string &boundary)
const {
6923 auto it = res.headers.find(
"Content-Type");
6924 if (it != res.headers.end()) {
6925 content_type = it->second;
6926 res.headers.erase(it);
6931 res.set_header(
"Content-Type",
6932 "multipart/byteranges; boundary=" + boundary);
6937 if (res.body.empty()) {
6938 if (res.content_length_ > 0) {
6941 length = res.content_length_;
6942 }
else if (req.ranges.size() == 1) {
6944 req.ranges[0], res.content_length_);
6946 length = offset_and_length.second;
6949 offset_and_length, res.content_length_);
6950 res.set_header(
"Content-Range", content_range);
6953 req, boundary, content_type, res.content_length_);
6957 if (res.content_provider_) {
6958 if (res.is_chunked_content_provider_) {
6959 res.set_header(
"Transfer-Encoding",
"chunked");
6961 res.set_header(
"Content-Encoding",
"gzip");
6963 res.set_header(
"Content-Encoding",
"br");
6971 }
else if (req.ranges.size() == 1) {
6972 auto offset_and_length =
6974 auto offset = offset_and_length.first;
6975 auto length = offset_and_length.second;
6978 offset_and_length, res.body.size());
6979 res.set_header(
"Content-Range", content_range);
6981 assert(offset + length <= res.body.size());
6982 res.body = res.body.substr(offset, length);
6986 res.body.size(),
data);
6987 res.body.swap(
data);
6991 std::unique_ptr<detail::compressor> compressor;
6992 std::string content_encoding;
6995 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
6996 compressor = detail::make_unique<detail::gzip_compressor>();
6997 content_encoding =
"gzip";
7000 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
7001 compressor = detail::make_unique<detail::brotli_compressor>();
7002 content_encoding =
"br";
7007 std::string compressed;
7008 if (compressor->compress(res.body.
data(), res.body.size(),
true,
7009 [&](
const char *
data,
size_t data_len) {
7010 compressed.append(data, data_len);
7013 res.body.swap(compressed);
7014 res.set_header(
"Content-Encoding", content_encoding);
7020 res.set_header(
"Content-Length", length);
7024 inline bool Server::dispatch_request_for_content_reader(
7025 Request &req,
Response &res, ContentReader content_reader,
7026 const HandlersForContentReader &handlers)
const {
7027 for (
const auto &
x : handlers) {
7028 const auto &matcher =
x.first;
7029 const auto &handler =
x.second;
7031 if (matcher->match(req)) {
7032 handler(req, res, content_reader);
7041 int remote_port,
const std::string &local_addr,
7042 int local_port,
bool close_connection,
7043 bool &connection_closed,
7044 const std::function<
void(
Request &)> &setup_request) {
7045 std::array<char, 2048> buf{};
7050 if (!line_reader.
getline()) {
return false; }
7056 res.
headers = default_headers_;
7061 #ifndef CPPHTTPLIB_USE_POLL
7063 if (strm.
socket() >= FD_SETSIZE) {
7067 return write_response(strm, close_connection, req, res);
7077 return write_response(strm, close_connection, req, res);
7081 if (!parse_request_line(line_reader.
ptr(), req) ||
7084 return write_response(strm, close_connection, req, res);
7088 connection_closed =
true;
7091 if (req.
version ==
"HTTP/1.0" &&
7093 connection_closed =
true;
7110 return write_response(strm, close_connection, req, res);
7114 if (setup_request) { setup_request(req); }
7118 if (expect_100_continue_handler_) {
7119 status = expect_100_continue_handler_(req, res);
7128 connection_closed =
true;
7129 return write_response(strm,
true, req, res);
7134 auto routed =
false;
7135 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
7136 routed = routing(req, res, strm);
7139 routed = routing(req, res, strm);
7140 }
catch (std::exception &e) {
7141 if (exception_handler_) {
7142 auto ep = std::current_exception();
7143 exception_handler_(req, res, ep);
7149 for (
size_t i = 0;
s[i]; i++) {
7151 case '\r': val +=
"\\r";
break;
7152 case '\n': val +=
"\\n";
break;
7153 default: val +=
s[i];
break;
7159 if (exception_handler_) {
7160 auto ep = std::current_exception();
7161 exception_handler_(req, res, ep);
7180 return write_response(strm, close_connection, req, res);
7186 auto mm = std::make_shared<detail::mmap>(path.c_str());
7187 if (!mm->is_open()) {
7192 return write_response(strm, close_connection, req, res);
7196 if (content_type.empty()) {
7198 path, file_extension_and_mimetype_map_, default_file_mimetype_);
7202 mm->size(), content_type,
7203 [mm](
size_t offset,
size_t length,
DataSink &sink) ->
bool {
7204 sink.write(mm->data() + offset, length);
7209 return write_response_with_content(strm, close_connection, req, res);
7213 return write_response(strm, close_connection, req, res);
7219 inline bool Server::process_and_close_socket(
socket_t sock) {
7220 std::string remote_addr;
7221 int remote_port = 0;
7224 std::string local_addr;
7232 [&](
Stream &strm,
bool close_connection,
bool &connection_closed) {
7234 local_port, close_connection, connection_closed,
7251 const std::string &client_cert_path,
7252 const std::string &client_key_path)
7254 host_and_port_(adjust_host_string(host_) +
":" +
std::
to_string(port)),
7255 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
7276 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7277 digest_auth_username_ = rhs.digest_auth_username_;
7278 digest_auth_password_ = rhs.digest_auth_password_;
7295 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7296 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7297 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7299 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7300 ca_cert_file_path_ = rhs.ca_cert_file_path_;
7301 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7302 ca_cert_store_ = rhs.ca_cert_store_;
7304 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7305 server_certificate_verification_ = rhs.server_certificate_verification_;
7306 server_hostname_verification_ = rhs.server_hostname_verification_;
7307 server_certificate_verifier_ = rhs.server_certificate_verifier_;
7324 if (it !=
addr_map_.end()) { ip = it->second; }
7365 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7366 assert(
socket.ssl ==
nullptr);
7373 inline bool ClientImpl::read_response_line(
Stream &strm,
const Request &req,
7375 std::array<char, 2048> buf{};
7377 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7379 if (!line_reader.getline()) {
return false; }
7381 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7382 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7384 const static std::regex re(
"(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7388 if (!std::regex_match(line_reader.ptr(), m, re)) {
7389 return req.
method ==
"CONNECT";
7391 res.
version = std::string(m[1]);
7392 res.
status = std::stoi(std::string(m[2]));
7393 res.
reason = std::string(m[3]);
7397 if (!line_reader.getline()) {
return false; }
7398 if (!line_reader.getline()) {
return false; }
7400 if (!std::regex_match(line_reader.ptr(), m, re)) {
return false; }
7401 res.
version = std::string(m[1]);
7402 res.
status = std::stoi(std::string(m[2]));
7403 res.
reason = std::string(m[3]);
7410 std::lock_guard<std::recursive_mutex> request_mutex_guard(
request_mutex_);
7411 auto ret = send_(req, res, error);
7414 ret = send_(req, res, error);
7419 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7420 inline bool ClientImpl::is_ssl_peer_could_be_closed(SSL *ssl)
const {
7426 return !SSL_peek(ssl, buf, 1) &&
7427 SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7431 inline bool ClientImpl::send_(Request &req,
Response &res,
Error &error) {
7439 auto is_alive =
false;
7443 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7444 if (is_alive && is_ssl()) {
7445 if (is_ssl_peer_could_be_closed(
socket_.ssl)) { is_alive =
false; }
7454 const bool shutdown_gracefully =
false;
7464 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7467 auto &scli =
static_cast<SSLClient &
>(*this);
7470 if (!scli.connect_with_proxy(
socket_, res, success, error)) {
7475 if (!scli.initialize_ssl(
socket_, error)) {
return false; }
7491 if (req.headers.find(header.first) == req.headers.end()) {
7492 req.headers.insert(header);
7499 auto se = detail::scope_exit([&]() {
7517 return handle_request(strm, req, res, close_connection, error);
7529 return send_(std::move(req2));
7533 auto res = detail::make_unique<Response>();
7535 auto ret =
send(req, *res, error);
7536 return Result{
ret ? std::move(res) : nullptr, error,
std::move(req.headers)};
7539 inline bool ClientImpl::handle_request(
Stream &strm, Request &req,
7540 Response &res,
bool close_connection,
7542 if (req.path.empty()) {
7547 auto req_save = req;
7556 req.path = req_save.path;
7561 if (!
ret) {
return false; }
7563 if (res.get_header_value(
"Connection") ==
"close" ||
7564 (res.version ==
"HTTP/1.0" && res.reason !=
"Connection established")) {
7583 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7586 req.authorization_count_ < 5) {
7588 const auto &username =
7589 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
7590 const auto &password =
7591 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
7593 if (!username.empty() && !password.empty()) {
7594 std::map<std::string, std::string> auth;
7596 Request new_req = req;
7597 new_req.authorization_count_ += 1;
7598 new_req.headers.erase(is_proxy ?
"Proxy-Authorization"
7600 new_req.headers.insert(detail::make_digest_authentication_header(
7602 username, password, is_proxy));
7606 ret =
send(new_req, new_res, error);
7607 if (
ret) { res = new_res; }
7617 if (req.redirect_count_ == 0) {
7622 auto location = res.get_header_value(
"location");
7623 if (location.empty()) {
return false; }
7625 const static std::regex re(
7626 R
"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7629 if (!std::regex_match(location, m, re)) {
return false; }
7631 auto scheme = is_ssl() ?
"https" :
"http";
7633 auto next_scheme = m[1].str();
7634 auto next_host = m[2].str();
7635 if (next_host.empty()) { next_host = m[3].str(); }
7636 auto port_str = m[4].str();
7637 auto next_path = m[5].str();
7638 auto next_query = m[6].str();
7640 auto next_port =
port_;
7641 if (!port_str.empty()) {
7642 next_port = std::stoi(port_str);
7643 }
else if (!next_scheme.empty()) {
7644 next_port = next_scheme ==
"https" ? 443 : 80;
7647 if (next_scheme.empty()) { next_scheme = scheme; }
7648 if (next_host.empty()) { next_host =
host_; }
7649 if (next_path.empty()) { next_path =
"/"; }
7653 if (next_scheme == scheme && next_host ==
host_ && next_port ==
port_) {
7656 if (next_scheme ==
"https") {
7657 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7658 SSLClient cli(next_host, next_port);
7659 cli.copy_settings(*
this);
7660 if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
7667 cli.copy_settings(*
this);
7675 Error &error)
const {
7676 auto is_shutting_down = []() {
return false; };
7680 std::unique_ptr<detail::compressor> compressor;
7681 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7683 compressor = detail::make_unique<detail::gzip_compressor>();
7687 compressor = detail::make_unique<detail::nocompressor>();
7691 is_shutting_down, *compressor, error);
7698 inline bool ClientImpl::write_request(
Stream &strm,
Request &req,
7699 bool close_connection,
Error &error) {
7701 if (close_connection) {
7727 std::string accept_encoding;
7728 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
7729 accept_encoding =
"br";
7731 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7732 if (!accept_encoding.empty()) { accept_encoding +=
", "; }
7733 accept_encoding +=
"gzip, deflate";
7735 req.
set_header(
"Accept-Encoding", accept_encoding);
7738 #ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
7746 if (req.
body.empty()) {
7762 req.
set_header(
"Content-Type",
"text/plain");
7780 if (!req.
has_header(
"Proxy-Authorization")) {
7794 if (!req.
has_header(
"Proxy-Authorization")) {
7802 detail::BufferStream bstrm;
7804 const auto &path_with_query =
7816 auto &
data = bstrm.get_buffer();
7824 if (req.
body.empty()) {
7836 inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
7837 Request &req,
const char *body,
size_t content_length,
7840 const std::string &content_type,
Error &error) {
7841 if (!content_type.empty()) { req.set_header(
"Content-Type", content_type); }
7843 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7844 if (
compress_) { req.set_header(
"Content-Encoding",
"gzip"); }
7847 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7848 if (
compress_ && !content_provider_without_length) {
7850 detail::gzip_compressor compressor;
7852 if (content_provider) {
7857 data_sink.write = [&](
const char *
data,
size_t data_len) ->
bool {
7859 auto last = offset + data_len == content_length;
7861 auto ret = compressor.compress(
7862 data, data_len, last,
7863 [&](
const char *compressed_data,
size_t compressed_data_len) {
7864 req.body.append(compressed_data, compressed_data_len);
7877 while (
ok && offset < content_length) {
7878 if (!content_provider(offset, content_length - offset, data_sink)) {
7884 if (!compressor.compress(body, content_length,
true,
7885 [&](
const char *
data,
size_t data_len) {
7886 req.body.append(data, data_len);
7896 if (content_provider) {
7897 req.content_length_ = content_length;
7898 req.content_provider_ = std::move(content_provider);
7899 req.is_chunked_content_provider_ =
false;
7900 }
else if (content_provider_without_length) {
7901 req.content_length_ = 0;
7902 req.content_provider_ = detail::ContentProviderAdapter(
7903 std::move(content_provider_without_length));
7904 req.is_chunked_content_provider_ =
true;
7905 req.set_header(
"Transfer-Encoding",
"chunked");
7907 req.body.assign(body, content_length);
7911 auto res = detail::make_unique<Response>();
7912 return send(req, *res, error) ? std::move(res) : nullptr;
7915 inline Result ClientImpl::send_with_content_provider(
7916 const std::string &method,
const std::string &path,
const Headers &headers,
7917 const char *body,
size_t content_length,
ContentProvider content_provider,
7919 const std::string &content_type,
Progress progress) {
7921 req.method = method;
7922 req.headers = headers;
7924 req.progress = progress;
7928 auto res = send_with_content_provider(
7929 req, body, content_length, std::move(content_provider),
7930 std::move(content_provider_without_length), content_type, error);
7932 return Result{std::move(res), error, std::move(req.headers)};
7936 ClientImpl::adjust_host_string(
const std::string &
host)
const {
7937 if (
host.find(
':') != std::string::npos) {
return "[" +
host +
"]"; }
7942 Response &res,
bool close_connection,
7945 if (!write_request(strm, req, close_connection, error)) {
return false; }
7947 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7950 if (!is_proxy_enabled) {
7951 if (is_ssl_peer_could_be_closed(
socket_.ssl)) {
7960 if (!read_response_line(strm, req, res) ||
7968 req.
method !=
"CONNECT") {
7983 [&](
const char *buf,
size_t n, uint64_t off, uint64_t len) {
7990 [&](
const char *buf,
size_t n, uint64_t ,
7992 assert(res.
body.size() +
n <= res.
body.max_size());
7993 res.
body.append(buf,
n);
7997 auto progress = [&](uint64_t current, uint64_t total) {
8007 if (len > res.
body.max_size()) {
8011 res.
body.reserve(
static_cast<size_t>(len));
8018 dummy_status, std::move(progress),
8035 size_t cur_item = 0;
8036 size_t cur_start = 0;
8039 return [&, cur_item, cur_start](
size_t offset,
8041 if (!offset && !items.empty()) {
8044 }
else if (cur_item < provider_items.size()) {
8047 provider_items[cur_item], boundary);
8048 offset += begin.size();
8054 auto has_data =
true;
8055 cur_sink.write = sink.write;
8056 cur_sink.done = [&]() { has_data =
false; };
8058 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
8077 ClientImpl::process_socket(
const Socket &
socket,
8078 std::function<
bool(
Stream &strm)> callback) {
8084 inline bool ClientImpl::is_ssl()
const {
return false; }
8091 return Get(path,
Headers(), std::move(progress));
8104 req.
progress = std::move(progress);
8106 return send_(std::move(req));
8111 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
nullptr);
8117 return Get(path,
Headers(),
nullptr, std::move(content_receiver),
8118 std::move(progress));
8123 return Get(path, headers,
nullptr, std::move(content_receiver),
nullptr);
8129 return Get(path, headers,
nullptr, std::move(content_receiver),
8130 std::move(progress));
8136 return Get(path,
Headers(), std::move(response_handler),
8137 std::move(content_receiver),
nullptr);
8143 return Get(path, headers, std::move(response_handler),
8144 std::move(content_receiver),
nullptr);
8151 return Get(path,
Headers(), std::move(response_handler),
8152 std::move(content_receiver), std::move(progress));
8165 [content_receiver](
const char *
data,
size_t data_length,
8166 uint64_t , uint64_t ) {
8167 return content_receiver(
data, data_length);
8169 req.
progress = std::move(progress);
8171 return send_(std::move(req));
8176 if (params.empty()) {
return Get(path, headers); }
8179 return Get(path_with_query, headers, std::move(progress));
8186 return Get(path, params, headers,
nullptr, std::move(content_receiver),
8187 std::move(progress));
8195 if (params.empty()) {
8196 return Get(path, headers, std::move(response_handler),
8197 std::move(content_receiver), std::move(progress));
8201 return Get(path_with_query, headers, std::move(response_handler),
8202 std::move(content_receiver), std::move(progress));
8216 return send_(std::move(req));
8220 return Post(path, std::string(), std::string());
8225 return Post(path, headers,
nullptr, 0, std::string());
8229 size_t content_length,
8230 const std::string &content_type) {
8231 return Post(path,
Headers(), body, content_length, content_type,
nullptr);
8235 const char *body,
size_t content_length,
8236 const std::string &content_type) {
8237 return send_with_content_provider(
"POST", path, headers, body, content_length,
8238 nullptr,
nullptr, content_type,
nullptr);
8242 const char *body,
size_t content_length,
8243 const std::string &content_type,
8245 return send_with_content_provider(
"POST", path, headers, body, content_length,
8246 nullptr,
nullptr, content_type, progress);
8250 const std::string &content_type) {
8255 const std::string &content_type,
8257 return Post(path,
Headers(), body, content_type, progress);
8261 const std::string &body,
8262 const std::string &content_type) {
8263 return send_with_content_provider(
"POST", path, headers, body.data(),
8264 body.size(),
nullptr,
nullptr, content_type,
8269 const std::string &body,
8270 const std::string &content_type,
8272 return send_with_content_provider(
"POST", path, headers, body.data(),
8273 body.size(),
nullptr,
nullptr, content_type,
8283 const std::string &content_type) {
8284 return Post(path,
Headers(), content_length, std::move(content_provider),
8290 const std::string &content_type) {
8291 return Post(path,
Headers(), std::move(content_provider), content_type);
8295 size_t content_length,
8297 const std::string &content_type) {
8298 return send_with_content_provider(
"POST", path, headers,
nullptr,
8299 content_length, std::move(content_provider),
8300 nullptr, content_type,
nullptr);
8305 const std::string &content_type) {
8306 return send_with_content_provider(
"POST", path, headers,
nullptr, 0,
nullptr,
8307 std::move(content_provider), content_type,
8314 return Post(path, headers, query,
"application/x-www-form-urlencoded");
8320 return Post(path, headers, query,
"application/x-www-form-urlencoded",
8332 const auto &content_type =
8335 return Post(path, headers, body, content_type);
8340 const std::string &boundary) {
8345 const auto &content_type =
8348 return Post(path, headers, body, content_type);
8356 const auto &content_type =
8358 return send_with_content_provider(
8359 "POST", path, headers,
nullptr, 0,
nullptr,
8360 get_multipart_content_provider(boundary, items, provider_items),
8361 content_type,
nullptr);
8365 return Put(path, std::string(), std::string());
8369 size_t content_length,
8370 const std::string &content_type) {
8371 return Put(path,
Headers(), body, content_length, content_type);
8375 const char *body,
size_t content_length,
8376 const std::string &content_type) {
8377 return send_with_content_provider(
"PUT", path, headers, body, content_length,
8378 nullptr,
nullptr, content_type,
nullptr);
8382 const char *body,
size_t content_length,
8383 const std::string &content_type,
8385 return send_with_content_provider(
"PUT", path, headers, body, content_length,
8386 nullptr,
nullptr, content_type, progress);
8390 const std::string &content_type) {
8391 return Put(path,
Headers(), body, content_type);
8395 const std::string &content_type,
8397 return Put(path,
Headers(), body, content_type, progress);
8401 const std::string &body,
8402 const std::string &content_type) {
8403 return send_with_content_provider(
"PUT", path, headers, body.data(),
8404 body.size(),
nullptr,
nullptr, content_type,
8409 const std::string &body,
8410 const std::string &content_type,
8412 return send_with_content_provider(
"PUT", path, headers, body.data(),
8413 body.size(),
nullptr,
nullptr, content_type,
8419 const std::string &content_type) {
8420 return Put(path,
Headers(), content_length, std::move(content_provider),
8426 const std::string &content_type) {
8427 return Put(path,
Headers(), std::move(content_provider), content_type);
8431 size_t content_length,
8433 const std::string &content_type) {
8434 return send_with_content_provider(
"PUT", path, headers,
nullptr,
8435 content_length, std::move(content_provider),
8436 nullptr, content_type,
nullptr);
8441 const std::string &content_type) {
8442 return send_with_content_provider(
"PUT", path, headers,
nullptr, 0,
nullptr,
8443 std::move(content_provider), content_type,
8454 return Put(path, headers, query,
"application/x-www-form-urlencoded");
8460 return Put(path, headers, query,
"application/x-www-form-urlencoded",
8472 const auto &content_type =
8475 return Put(path, headers, body, content_type);
8480 const std::string &boundary) {
8485 const auto &content_type =
8488 return Put(path, headers, body, content_type);
8496 const auto &content_type =
8498 return send_with_content_provider(
8499 "PUT", path, headers,
nullptr, 0,
nullptr,
8500 get_multipart_content_provider(boundary, items, provider_items),
8501 content_type,
nullptr);
8504 return Patch(path, std::string(), std::string());
8508 size_t content_length,
8509 const std::string &content_type) {
8510 return Patch(path,
Headers(), body, content_length, content_type);
8514 size_t content_length,
8515 const std::string &content_type,
8517 return Patch(path,
Headers(), body, content_length, content_type, progress);
8521 const char *body,
size_t content_length,
8522 const std::string &content_type) {
8523 return Patch(path, headers, body, content_length, content_type,
nullptr);
8527 const char *body,
size_t content_length,
8528 const std::string &content_type,
8530 return send_with_content_provider(
"PATCH", path, headers, body,
8531 content_length,
nullptr,
nullptr,
8532 content_type, progress);
8536 const std::string &body,
8537 const std::string &content_type) {
8542 const std::string &body,
8543 const std::string &content_type,
8545 return Patch(path,
Headers(), body, content_type, progress);
8549 const std::string &body,
8550 const std::string &content_type) {
8551 return Patch(path, headers, body, content_type,
nullptr);
8555 const std::string &body,
8556 const std::string &content_type,
8558 return send_with_content_provider(
"PATCH", path, headers, body.data(),
8559 body.size(),
nullptr,
nullptr, content_type,
8565 const std::string &content_type) {
8566 return Patch(path,
Headers(), content_length, std::move(content_provider),
8572 const std::string &content_type) {
8573 return Patch(path,
Headers(), std::move(content_provider), content_type);
8577 size_t content_length,
8579 const std::string &content_type) {
8580 return send_with_content_provider(
"PATCH", path, headers,
nullptr,
8581 content_length, std::move(content_provider),
8582 nullptr, content_type,
nullptr);
8587 const std::string &content_type) {
8588 return send_with_content_provider(
"PATCH", path, headers,
nullptr, 0,
nullptr,
8589 std::move(content_provider), content_type,
8594 return Delete(path,
Headers(), std::string(), std::string());
8599 return Delete(path, headers, std::string(), std::string());
8603 size_t content_length,
8604 const std::string &content_type) {
8605 return Delete(path,
Headers(), body, content_length, content_type);
8609 size_t content_length,
8610 const std::string &content_type,
8612 return Delete(path,
Headers(), body, content_length, content_type, progress);
8616 const Headers &headers,
const char *body,
8617 size_t content_length,
8618 const std::string &content_type) {
8619 return Delete(path, headers, body, content_length, content_type,
nullptr);
8623 const Headers &headers,
const char *body,
8624 size_t content_length,
8625 const std::string &content_type,
8633 if (!content_type.empty()) { req.
set_header(
"Content-Type", content_type); }
8634 req.
body.assign(body, content_length);
8636 return send_(std::move(req));
8640 const std::string &body,
8641 const std::string &content_type) {
8642 return Delete(path,
Headers(), body.data(), body.size(), content_type);
8646 const std::string &body,
8647 const std::string &content_type,
8649 return Delete(path,
Headers(), body.data(), body.size(), content_type,
8655 const std::string &body,
8656 const std::string &content_type) {
8657 return Delete(path, headers, body.data(), body.size(), content_type);
8662 const std::string &body,
8663 const std::string &content_type,
8665 return Delete(path, headers, body.data(), body.size(), content_type,
8680 return send_(std::move(req));
8733 const std::string &password) {
8742 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8743 inline void ClientImpl::set_digest_auth(
const std::string &username,
8744 const std::string &password) {
8745 digest_auth_username_ = username;
8746 digest_auth_password_ = password;
8766 std::function<ssize_t(
Stream &,
Headers &)>
const &writer) {
8796 const std::string &password) {
8805 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8806 inline void ClientImpl::set_proxy_digest_auth(
const std::string &username,
8807 const std::string &password) {
8808 proxy_digest_auth_username_ = username;
8809 proxy_digest_auth_password_ = password;
8812 inline void ClientImpl::set_ca_cert_path(
const std::string &ca_cert_file_path,
8813 const std::string &ca_cert_dir_path) {
8814 ca_cert_file_path_ = ca_cert_file_path;
8815 ca_cert_dir_path_ = ca_cert_dir_path;
8818 inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
8819 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
8820 ca_cert_store_ = ca_cert_store;
8824 inline X509_STORE *ClientImpl::create_ca_cert_store(
const char *ca_cert,
8825 std::size_t size)
const {
8826 auto mem = BIO_new_mem_buf(ca_cert,
static_cast<int>(size));
8827 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
8828 if (!mem) {
return nullptr; }
8830 auto inf = PEM_X509_INFO_read_bio(mem,
nullptr,
nullptr,
nullptr);
8831 if (!inf) {
return nullptr; }
8833 auto cts = X509_STORE_new();
8835 for (
auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
8836 auto itmp = sk_X509_INFO_value(inf, i);
8837 if (!itmp) {
continue; }
8839 if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
8840 if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
8844 sk_X509_INFO_pop_free(inf, X509_INFO_free);
8848 inline void ClientImpl::enable_server_certificate_verification(
bool enabled) {
8849 server_certificate_verification_ =
enabled;
8852 inline void ClientImpl::enable_server_hostname_verification(
bool enabled) {
8853 server_hostname_verification_ =
enabled;
8856 inline void ClientImpl::set_server_certificate_verifier(
8857 std::function<
bool(SSL *ssl)> verifier) {
8858 server_certificate_verifier_ = verifier;
8869 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8872 template <
typename U,
typename V>
8873 inline SSL *ssl_new(
socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
8874 U SSL_connect_or_accept, V setup) {
8877 std::lock_guard<std::mutex> guard(ctx_mutex);
8883 auto bio = BIO_new_socket(
static_cast<int>(sock), BIO_NOCLOSE);
8884 BIO_set_nbio(bio, 1);
8885 SSL_set_bio(ssl, bio, bio);
8887 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
8890 std::lock_guard<std::mutex> guard(ctx_mutex);
8896 BIO_set_nbio(bio, 0);
8903 inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
socket_t sock,
8904 bool shutdown_gracefully) {
8909 if (shutdown_gracefully) {
8916 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
8917 reinterpret_cast<const void *
>(&tv),
sizeof(tv));
8919 auto ret = SSL_shutdown(ssl);
8921 std::this_thread::sleep_for(std::chrono::milliseconds{100});
8922 ret = SSL_shutdown(ssl);
8927 std::lock_guard<std::mutex> guard(ctx_mutex);
8931 template <
typename U>
8932 bool ssl_connect_or_accept_nonblocking(
socket_t sock, SSL *ssl,
8933 U ssl_connect_or_accept,
8935 time_t timeout_usec) {
8937 while ((res = ssl_connect_or_accept(ssl)) != 1) {
8938 auto err = SSL_get_error(ssl, res);
8940 case SSL_ERROR_WANT_READ:
8941 if (
select_read(sock, timeout_sec, timeout_usec) > 0) {
continue; }
8943 case SSL_ERROR_WANT_WRITE:
8944 if (
select_write(sock, timeout_sec, timeout_usec) > 0) {
continue; }
8953 template <
typename T>
8954 inline bool process_server_socket_ssl(
8955 const std::atomic<socket_t> &svr_sock, SSL *ssl,
socket_t sock,
8956 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
8957 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
8958 time_t write_timeout_usec,
T callback) {
8960 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
8961 [&](
bool close_connection,
bool &connection_closed) {
8962 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
8963 write_timeout_sec, write_timeout_usec);
8964 return callback(strm, close_connection, connection_closed);
8968 template <
typename T>
8970 process_client_socket_ssl(SSL *ssl,
socket_t sock, time_t read_timeout_sec,
8971 time_t read_timeout_usec, time_t write_timeout_sec,
8972 time_t write_timeout_usec,
T callback) {
8973 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
8974 write_timeout_sec, write_timeout_usec);
8975 return callback(strm);
8982 OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
8987 inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl,
8988 time_t read_timeout_sec,
8989 time_t read_timeout_usec,
8990 time_t write_timeout_sec,
8991 time_t write_timeout_usec)
8996 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
8999 inline SSLSocketStream::~SSLSocketStream() =
default;
9001 inline bool SSLSocketStream::is_readable()
const {
9005 inline bool SSLSocketStream::is_writable()
const {
9006 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9011 if (SSL_pending(ssl_) > 0) {
9012 return SSL_read(ssl_, ptr,
static_cast<int>(size));
9013 }
else if (is_readable()) {
9014 auto ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
9016 auto err = SSL_get_error(ssl_,
ret);
9019 while (--
n >= 0 && (err == SSL_ERROR_WANT_READ ||
9020 (err == SSL_ERROR_SYSCALL &&
9021 WSAGetLastError() == WSAETIMEDOUT))) {
9023 while (--
n >= 0 && err == SSL_ERROR_WANT_READ) {
9025 if (SSL_pending(ssl_) > 0) {
9026 return SSL_read(ssl_, ptr,
static_cast<int>(size));
9027 }
else if (is_readable()) {
9028 std::this_thread::sleep_for(std::chrono::microseconds{10});
9029 ret = SSL_read(ssl_, ptr,
static_cast<int>(size));
9030 if (
ret >= 0) {
return ret; }
9031 err = SSL_get_error(ssl_,
ret);
9043 if (is_writable()) {
9044 auto handle_size =
static_cast<int>(
9047 auto ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
9049 auto err = SSL_get_error(ssl_,
ret);
9052 while (--
n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
9053 (err == SSL_ERROR_SYSCALL &&
9054 WSAGetLastError() == WSAETIMEDOUT))) {
9056 while (--
n >= 0 && err == SSL_ERROR_WANT_WRITE) {
9058 if (is_writable()) {
9059 std::this_thread::sleep_for(std::chrono::microseconds{10});
9060 ret = SSL_write(ssl_, ptr,
static_cast<int>(handle_size));
9061 if (
ret >= 0) {
return ret; }
9062 err = SSL_get_error(ssl_,
ret);
9083 inline socket_t SSLSocketStream::socket()
const {
return sock_; }
9085 static SSLInit sslinit_;
9090 inline SSLServer::SSLServer(
const char *cert_path,
const char *private_key_path,
9091 const char *client_ca_cert_file_path,
9092 const char *client_ca_cert_dir_path,
9093 const char *private_key_password) {
9094 ctx_ = SSL_CTX_new(TLS_server_method());
9097 SSL_CTX_set_options(ctx_,
9098 SSL_OP_NO_COMPRESSION |
9099 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9101 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9103 if (private_key_password !=
nullptr && (private_key_password[0] !=
'\0')) {
9104 SSL_CTX_set_default_passwd_cb_userdata(
9106 reinterpret_cast<void *
>(
const_cast<char *
>(private_key_password)));
9109 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
9110 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
9112 SSL_CTX_check_private_key(ctx_) != 1) {
9115 }
else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
9116 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
9117 client_ca_cert_dir_path);
9120 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
9125 inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
9126 X509_STORE *client_ca_cert_store) {
9127 ctx_ = SSL_CTX_new(TLS_server_method());
9130 SSL_CTX_set_options(ctx_,
9131 SSL_OP_NO_COMPRESSION |
9132 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9134 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9136 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
9137 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
9140 }
else if (client_ca_cert_store) {
9141 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9144 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
9149 inline SSLServer::SSLServer(
9150 const std::function<
bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
9151 ctx_ = SSL_CTX_new(TLS_method());
9153 if (!setup_ssl_ctx_callback(*ctx_)) {
9160 inline SSLServer::~SSLServer() {
9161 if (ctx_) { SSL_CTX_free(ctx_); }
9166 inline SSL_CTX *SSLServer::ssl_context()
const {
return ctx_; }
9168 inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
9169 X509_STORE *client_ca_cert_store) {
9171 std::lock_guard<std::mutex> guard(ctx_mutex_);
9173 SSL_CTX_use_certificate(ctx_, cert);
9174 SSL_CTX_use_PrivateKey(ctx_, private_key);
9176 if (client_ca_cert_store !=
nullptr) {
9177 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9181 inline bool SSLServer::process_and_close_socket(
socket_t sock) {
9182 auto ssl = detail::ssl_new(
9183 sock, ctx_, ctx_mutex_,
9185 return detail::ssl_connect_or_accept_nonblocking(
9186 sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
9188 [](SSL * ) {
return true; });
9192 std::string remote_addr;
9193 int remote_port = 0;
9196 std::string local_addr;
9200 ret = detail::process_server_socket_ssl(
9201 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
9202 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9203 write_timeout_usec_,
9204 [&](
Stream &strm,
bool close_connection,
bool &connection_closed) {
9205 return process_request(strm, remote_addr, remote_port, local_addr,
9206 local_port, close_connection,
9208 [&](Request &req) { req.ssl = ssl; });
9213 const bool shutdown_gracefully =
ret;
9214 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
9223 inline SSLClient::SSLClient(
const std::string &host)
9224 : SSLClient(host, 443,
std::string(),
std::string()) {}
9226 inline SSLClient::SSLClient(
const std::string &host,
int port)
9227 : SSLClient(host, port,
std::string(),
std::string()) {}
9229 inline SSLClient::SSLClient(
const std::string &host,
int port,
9230 const std::string &client_cert_path,
9231 const std::string &client_key_path,
9232 const std::string &private_key_password)
9233 : ClientImpl(host, port, client_cert_path, client_key_path) {
9234 ctx_ = SSL_CTX_new(TLS_client_method());
9236 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9239 [&](
const char *b,
const char *e) {
9240 host_components_.emplace_back(b, e);
9243 if (!client_cert_path.empty() && !client_key_path.empty()) {
9244 if (!private_key_password.empty()) {
9245 SSL_CTX_set_default_passwd_cb_userdata(
9246 ctx_,
reinterpret_cast<void *
>(
9247 const_cast<char *
>(private_key_password.c_str())));
9250 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
9251 SSL_FILETYPE_PEM) != 1 ||
9252 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
9253 SSL_FILETYPE_PEM) != 1) {
9260 inline SSLClient::SSLClient(
const std::string &host,
int port,
9261 X509 *client_cert, EVP_PKEY *client_key,
9262 const std::string &private_key_password)
9263 : ClientImpl(host, port) {
9264 ctx_ = SSL_CTX_new(TLS_client_method());
9267 [&](
const char *b,
const char *e) {
9268 host_components_.emplace_back(b, e);
9271 if (client_cert !=
nullptr && client_key !=
nullptr) {
9272 if (!private_key_password.empty()) {
9273 SSL_CTX_set_default_passwd_cb_userdata(
9274 ctx_,
reinterpret_cast<void *
>(
9275 const_cast<char *
>(private_key_password.c_str())));
9278 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
9279 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
9286 inline SSLClient::~SSLClient() {
9287 if (ctx_) { SSL_CTX_free(ctx_); }
9291 shutdown_ssl_impl(socket_,
true);
9296 inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
9297 if (ca_cert_store) {
9299 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
9301 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
9304 X509_STORE_free(ca_cert_store);
9309 inline void SSLClient::load_ca_cert_store(
const char *ca_cert,
9311 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
9314 inline long SSLClient::get_openssl_verify_result()
const {
9315 return verify_result_;
9318 inline SSL_CTX *SSLClient::ssl_context()
const {
return ctx_; }
9320 inline bool SSLClient::create_and_connect_socket(Socket &socket,
Error &error) {
9325 inline bool SSLClient::connect_with_proxy(Socket &socket,
Response &res,
9326 bool &success,
Error &error) {
9330 socket.sock, read_timeout_sec_, read_timeout_usec_,
9331 write_timeout_sec_, write_timeout_usec_, [&](
Stream &strm) {
9333 req2.method =
"CONNECT";
9334 req2.path = host_and_port_;
9335 return process_request(strm, req2, proxy_res, false, error);
9339 shutdown_ssl(socket,
true);
9347 if (!proxy_digest_auth_username_.empty() &&
9348 !proxy_digest_auth_password_.empty()) {
9349 std::map<std::string, std::string> auth;
9353 socket.sock, read_timeout_sec_, read_timeout_usec_,
9354 write_timeout_sec_, write_timeout_usec_, [&](
Stream &strm) {
9356 req3.method =
"CONNECT";
9357 req3.path = host_and_port_;
9358 req3.headers.insert(detail::make_digest_authentication_header(
9359 req3, auth, 1, detail::random_string(10),
9360 proxy_digest_auth_username_, proxy_digest_auth_password_,
9362 return process_request(strm, req3, proxy_res, false, error);
9366 shutdown_ssl(socket,
true);
9381 res = std::move(proxy_res);
9384 shutdown_ssl(socket,
true);
9393 inline bool SSLClient::load_certs() {
9396 std::call_once(initialize_cert_, [&]() {
9397 std::lock_guard<std::mutex> guard(ctx_mutex_);
9398 if (!ca_cert_file_path_.empty()) {
9399 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
9403 }
else if (!ca_cert_dir_path_.empty()) {
9404 if (!SSL_CTX_load_verify_locations(ctx_,
nullptr,
9405 ca_cert_dir_path_.c_str())) {
9409 auto loaded =
false;
9412 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9413 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9415 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
9416 #endif // TARGET_OS_OSX
9418 if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
9425 inline bool SSLClient::initialize_ssl(Socket &socket,
Error &error) {
9426 auto ssl = detail::ssl_new(
9427 socket.sock, ctx_, ctx_mutex_,
9429 if (server_certificate_verification_) {
9430 if (!load_certs()) {
9431 error = Error::SSLLoadingCerts;
9434 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
9437 if (!detail::ssl_connect_or_accept_nonblocking(
9438 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
9439 connection_timeout_usec_)) {
9440 error = Error::SSLConnection;
9444 if (server_certificate_verification_) {
9445 if (server_certificate_verifier_) {
9446 if (!server_certificate_verifier_(ssl2)) {
9451 verify_result_ = SSL_get_verify_result(ssl2);
9453 if (verify_result_ != X509_V_OK) {
9458 auto server_cert = SSL_get1_peer_certificate(ssl2);
9459 auto se = detail::scope_exit([&] { X509_free(server_cert); });
9461 if (server_cert ==
nullptr) {
9466 if (server_hostname_verification_) {
9467 if (!verify_host(server_cert)) {
9478 #if defined(OPENSSL_IS_BORINGSSL)
9479 SSL_set_tlsext_host_name(ssl2, host_.c_str());
9483 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
9484 static_cast<void *
>(
const_cast<char *
>(host_.c_str())));
9499 inline void SSLClient::shutdown_ssl(Socket &socket,
bool shutdown_gracefully) {
9500 shutdown_ssl_impl(socket, shutdown_gracefully);
9503 inline void SSLClient::shutdown_ssl_impl(Socket &socket,
9504 bool shutdown_gracefully) {
9506 assert(socket.ssl ==
nullptr);
9510 detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
9511 shutdown_gracefully);
9512 socket.ssl =
nullptr;
9514 assert(socket.ssl ==
nullptr);
9518 SSLClient::process_socket(
const Socket &socket,
9519 std::function<
bool(
Stream &strm)> callback) {
9521 return detail::process_client_socket_ssl(
9522 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
9523 write_timeout_sec_, write_timeout_usec_, std::move(callback));
9526 inline bool SSLClient::is_ssl()
const {
return true; }
9528 inline bool SSLClient::verify_host(X509 *server_cert)
const {
9550 return verify_host_with_subject_alt_name(server_cert) ||
9551 verify_host_with_common_name(server_cert);
9555 SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
9558 auto type = GEN_DNS;
9560 struct in6_addr addr6{};
9561 struct in_addr addr{};
9562 size_t addr_len = 0;
9565 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
9567 addr_len =
sizeof(
struct in6_addr);
9568 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
9570 addr_len =
sizeof(
struct in_addr);
9574 auto alt_names =
static_cast<const struct stack_st_GENERAL_NAME *
>(
9575 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr));
9578 auto dsn_matched =
false;
9579 auto ip_matched =
false;
9581 auto count = sk_GENERAL_NAME_num(alt_names);
9583 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
9584 auto val = sk_GENERAL_NAME_value(alt_names, i);
9585 if (val->type == type) {
9587 reinterpret_cast<const char *
>(ASN1_STRING_get0_data(val->d.ia5));
9588 auto name_len =
static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
9591 case GEN_DNS: dsn_matched = check_host_name(name, name_len);
break;
9594 if (!memcmp(&addr6, name, addr_len) ||
9595 !memcmp(&addr, name, addr_len)) {
9603 if (dsn_matched || ip_matched) {
ret =
true; }
9606 GENERAL_NAMES_free(
const_cast<STACK_OF(GENERAL_NAME) *
>(
9607 reinterpret_cast<const STACK_OF(GENERAL_NAME) *
>(alt_names)));
9611 inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
9612 const auto subject_name = X509_get_subject_name(server_cert);
9614 if (subject_name !=
nullptr) {
9616 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
9617 name,
sizeof(name));
9619 if (name_len != -1) {
9620 return check_host_name(name,
static_cast<size_t>(name_len));
9627 inline bool SSLClient::check_host_name(
const char *pattern,
9628 size_t pattern_len)
const {
9629 if (host_.size() == pattern_len && host_ == pattern) {
return true; }
9633 std::vector<std::string> pattern_components;
9635 [&](
const char *b,
const char *e) {
9636 pattern_components.emplace_back(b, e);
9639 if (host_components_.size() != pattern_components.size()) {
return false; }
9641 auto itr = pattern_components.begin();
9642 for (
const auto &h : host_components_) {
9644 if (p != h && p !=
"*") {
9645 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' &&
9646 !p.compare(0, p.size() - 1, h));
9647 if (!partial_match) {
return false; }
9658 :
Client(scheme_host_port,
std::string(),
std::string()) {}
9661 const std::string &client_cert_path,
9662 const std::string &client_key_path) {
9663 const static std::regex re(
9664 R
"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
9667 if (std::regex_match(scheme_host_port, m, re)) {
9668 auto scheme = m[1].str();
9670 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9671 if (!scheme.empty() && (scheme !=
"http" && scheme !=
"https")) {
9673 if (!scheme.empty() && scheme !=
"http") {
9675 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
9676 std::string msg =
"'" + scheme +
"' scheme is not supported.";
9677 throw std::invalid_argument(msg);
9682 auto is_ssl = scheme ==
"https";
9684 auto host = m[2].str();
9685 if (
host.empty()) {
host = m[3].str(); }
9687 auto port_str = m[4].str();
9688 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
9691 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9692 cli_ = detail::make_unique<SSLClient>(
host,
port, client_cert_path,
9697 cli_ = detail::make_unique<ClientImpl>(
host,
port, client_cert_path,
9703 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
9704 client_cert_path, client_key_path);
9712 const std::string &client_cert_path,
9713 const std::string &client_key_path)
9715 client_key_path)) {}
9720 return cli_ !=
nullptr && cli_->is_valid();
9725 return cli_->Get(path, headers);
9728 return cli_->Get(path, std::move(progress));
9732 return cli_->Get(path, headers, std::move(progress));
9736 return cli_->Get(path, std::move(content_receiver));
9740 return cli_->Get(path, headers, std::move(content_receiver));
9744 return cli_->Get(path, std::move(content_receiver), std::move(progress));
9748 return cli_->Get(path, headers, std::move(content_receiver),
9749 std::move(progress));
9754 return cli_->Get(path, std::move(response_handler),
9755 std::move(content_receiver));
9760 return cli_->Get(path, headers, std::move(response_handler),
9761 std::move(content_receiver));
9766 return cli_->Get(path, std::move(response_handler),
9767 std::move(content_receiver), std::move(progress));
9772 return cli_->Get(path, headers, std::move(response_handler),
9773 std::move(content_receiver), std::move(progress));
9777 return cli_->Get(path, params, headers, std::move(progress));
9782 return cli_->Get(path, params, headers, std::move(content_receiver),
9783 std::move(progress));
9789 return cli_->Get(path, params, headers, std::move(response_handler),
9790 std::move(content_receiver), std::move(progress));
9795 return cli_->Head(path, headers);
9800 return cli_->Post(path, headers);
9803 size_t content_length,
9804 const std::string &content_type) {
9805 return cli_->Post(path, body, content_length, content_type);
9808 const char *body,
size_t content_length,
9809 const std::string &content_type) {
9810 return cli_->Post(path, headers, body, content_length, content_type);
9813 const char *body,
size_t content_length,
9814 const std::string &content_type,
Progress progress) {
9815 return cli_->Post(path, headers, body, content_length, content_type,
9819 const std::string &content_type) {
9820 return cli_->Post(path, body, content_type);
9823 const std::string &content_type,
Progress progress) {
9824 return cli_->Post(path, body, content_type, progress);
9827 const std::string &body,
9828 const std::string &content_type) {
9829 return cli_->Post(path, headers, body, content_type);
9832 const std::string &body,
9833 const std::string &content_type,
Progress progress) {
9834 return cli_->Post(path, headers, body, content_type, progress);
9838 const std::string &content_type) {
9839 return cli_->Post(path, content_length, std::move(content_provider),
9844 const std::string &content_type) {
9845 return cli_->Post(path, std::move(content_provider), content_type);
9848 size_t content_length,
9850 const std::string &content_type) {
9851 return cli_->Post(path, headers, content_length, std::move(content_provider),
9856 const std::string &content_type) {
9857 return cli_->Post(path, headers, std::move(content_provider), content_type);
9860 return cli_->Post(path, params);
9864 return cli_->Post(path, headers, params);
9868 return cli_->Post(path, headers, params, progress);
9872 return cli_->Post(path, items);
9876 return cli_->Post(path, headers, items);
9880 const std::string &boundary) {
9881 return cli_->Post(path, headers, items, boundary);
9887 return cli_->Post(path, headers, items, provider_items);
9891 size_t content_length,
9892 const std::string &content_type) {
9893 return cli_->Put(path, body, content_length, content_type);
9896 const char *body,
size_t content_length,
9897 const std::string &content_type) {
9898 return cli_->Put(path, headers, body, content_length, content_type);
9901 const char *body,
size_t content_length,
9902 const std::string &content_type,
Progress progress) {
9903 return cli_->Put(path, headers, body, content_length, content_type, progress);
9906 const std::string &content_type) {
9907 return cli_->Put(path, body, content_type);
9910 const std::string &content_type,
Progress progress) {
9911 return cli_->Put(path, body, content_type, progress);
9914 const std::string &body,
9915 const std::string &content_type) {
9916 return cli_->Put(path, headers, body, content_type);
9919 const std::string &body,
9920 const std::string &content_type,
Progress progress) {
9921 return cli_->Put(path, headers, body, content_type, progress);
9925 const std::string &content_type) {
9926 return cli_->Put(path, content_length, std::move(content_provider),
9931 const std::string &content_type) {
9932 return cli_->Put(path, std::move(content_provider), content_type);
9935 size_t content_length,
9937 const std::string &content_type) {
9938 return cli_->Put(path, headers, content_length, std::move(content_provider),
9943 const std::string &content_type) {
9944 return cli_->Put(path, headers, std::move(content_provider), content_type);
9947 return cli_->Put(path, params);
9951 return cli_->Put(path, headers, params);
9955 return cli_->Put(path, headers, params, progress);
9959 return cli_->Put(path, items);
9963 return cli_->Put(path, headers, items);
9967 const std::string &boundary) {
9968 return cli_->Put(path, headers, items, boundary);
9974 return cli_->Put(path, headers, items, provider_items);
9977 return cli_->Patch(path);
9980 size_t content_length,
9981 const std::string &content_type) {
9982 return cli_->Patch(path, body, content_length, content_type);
9985 size_t content_length,
9986 const std::string &content_type,
9988 return cli_->Patch(path, body, content_length, content_type, progress);
9991 const char *body,
size_t content_length,
9992 const std::string &content_type) {
9993 return cli_->Patch(path, headers, body, content_length, content_type);
9996 const char *body,
size_t content_length,
9997 const std::string &content_type,
9999 return cli_->Patch(path, headers, body, content_length, content_type,
10003 const std::string &content_type) {
10004 return cli_->Patch(path, body, content_type);
10007 const std::string &content_type,
10009 return cli_->Patch(path, body, content_type, progress);
10012 const std::string &body,
10013 const std::string &content_type) {
10014 return cli_->Patch(path, headers, body, content_type);
10017 const std::string &body,
10018 const std::string &content_type,
10020 return cli_->Patch(path, headers, body, content_type, progress);
10024 const std::string &content_type) {
10025 return cli_->Patch(path, content_length, std::move(content_provider),
10030 const std::string &content_type) {
10031 return cli_->Patch(path, std::move(content_provider), content_type);
10034 size_t content_length,
10036 const std::string &content_type) {
10037 return cli_->Patch(path, headers, content_length, std::move(content_provider),
10042 const std::string &content_type) {
10043 return cli_->Patch(path, headers, std::move(content_provider), content_type);
10046 return cli_->Delete(path);
10049 return cli_->Delete(path, headers);
10052 size_t content_length,
10053 const std::string &content_type) {
10054 return cli_->Delete(path, body, content_length, content_type);
10057 size_t content_length,
10058 const std::string &content_type,
10060 return cli_->Delete(path, body, content_length, content_type, progress);
10063 const char *body,
size_t content_length,
10064 const std::string &content_type) {
10065 return cli_->Delete(path, headers, body, content_length, content_type);
10068 const char *body,
size_t content_length,
10069 const std::string &content_type,
10071 return cli_->Delete(path, headers, body, content_length, content_type,
10075 const std::string &content_type) {
10076 return cli_->Delete(path, body, content_type);
10079 const std::string &content_type,
10081 return cli_->Delete(path, body, content_type, progress);
10084 const std::string &body,
10085 const std::string &content_type) {
10086 return cli_->Delete(path, headers, body, content_type);
10089 const std::string &body,
10090 const std::string &content_type,
10092 return cli_->Delete(path, headers, body, content_type, progress);
10095 return cli_->Options(path);
10098 return cli_->Options(path, headers);
10102 return cli_->send(req, res, error);
10119 cli_->set_hostname_addr_map(std::move(addr_map));
10123 cli_->set_default_headers(std::move(headers));
10127 std::function<ssize_t(
Stream &,
Headers &)>
const &writer) {
10128 cli_->set_header_writer(writer);
10132 cli_->set_address_family(family);
10138 cli_->set_socket_options(std::move(socket_options));
10142 cli_->set_connection_timeout(sec, usec);
10146 cli_->set_read_timeout(sec, usec);
10150 cli_->set_write_timeout(sec, usec);
10154 const std::string &password) {
10155 cli_->set_basic_auth(username, password);
10158 cli_->set_bearer_token_auth(token);
10160 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10161 inline void Client::set_digest_auth(
const std::string &username,
10162 const std::string &password) {
10163 cli_->set_digest_auth(username, password);
10169 cli_->set_follow_location(on);
10179 cli_->set_interface(intf);
10186 const std::string &password) {
10187 cli_->set_proxy_basic_auth(username, password);
10190 cli_->set_proxy_bearer_token_auth(token);
10192 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10193 inline void Client::set_proxy_digest_auth(
const std::string &username,
10194 const std::string &password) {
10195 cli_->set_proxy_digest_auth(username, password);
10199 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10200 inline void Client::enable_server_certificate_verification(
bool enabled) {
10201 cli_->enable_server_certificate_verification(
enabled);
10204 inline void Client::enable_server_hostname_verification(
bool enabled) {
10205 cli_->enable_server_hostname_verification(
enabled);
10208 inline void Client::set_server_certificate_verifier(
10209 std::function<
bool(SSL *ssl)> verifier) {
10210 cli_->set_server_certificate_verifier(verifier);
10215 cli_->set_logger(std::move(logger));
10218 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10219 inline void Client::set_ca_cert_path(
const std::string &ca_cert_file_path,
10220 const std::string &ca_cert_dir_path) {
10221 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
10224 inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
10226 static_cast<SSLClient &
>(*cli_).set_ca_cert_store(ca_cert_store);
10228 cli_->set_ca_cert_store(ca_cert_store);
10232 inline void Client::load_ca_cert_store(
const char *ca_cert, std::size_t size) {
10233 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
10236 inline long Client::get_openssl_verify_result()
const {
10238 return static_cast<SSLClient &
>(*cli_).get_openssl_verify_result();
10243 inline SSL_CTX *Client::ssl_context()
const {
10244 if (is_ssl_) {
return static_cast<SSLClient &
>(*cli_).ssl_context(); }
10253 #if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
10257 #endif // CPPHTTPLIB_HTTPLIB_H