maph 1.0.0 High-Performance Memory-Mapped Key-Value Database with O(1) Lookups

Loading...
Searching...
No Matches
httplib.h
Go to the documentation of this file.
1//
2// httplib.h
3//
4// Copyright (c) 2025 Yuji Hirose. All rights reserved.
5// MIT License
6//
7
8#ifndef CPPHTTPLIB_HTTPLIB_H
9#define CPPHTTPLIB_HTTPLIB_H
10
11#define CPPHTTPLIB_VERSION "0.26.0"
12#define CPPHTTPLIB_VERSION_NUM "0x001A00"
13
14/*
15 * Platform compatibility check
16 */
17
18#if defined(_WIN32) && !defined(_WIN64)
19#if defined(_MSC_VER)
20#pragma message( \
21 "cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler.")
22#else
23#warning \
24 "cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler."
25#endif
26#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ < 8
27#warning \
28 "cpp-httplib doesn't support 32-bit platforms. Please use a 64-bit compiler."
29#elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ < 8
30#warning \
31 "cpp-httplib doesn't support platforms where size_t is less than 64 bits."
32#endif
33
34#ifdef _WIN32
35#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
36#error \
37 "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later."
38#endif
39#endif
40
41/*
42 * Configuration
43 */
44
45#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
46#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
47#endif
48
49#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
50#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
51#endif
52
53#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
54#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
55#endif
56
57#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
58#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
59#endif
60
61#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
62#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
63#endif
64
65#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
66#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
67#endif
68
69#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
70#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
71#endif
72
73#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
74#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
75#endif
76
77#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
78#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
79#endif
80
81#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
82#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
83#endif
84
85#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
86#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
87#endif
88
89#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
90#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
91#endif
92
93#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
94#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
95#endif
96
97#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
98#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
99#endif
100
101#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
102#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
103#endif
104
105#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
106#ifdef _WIN32
107#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
108#else
109#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
110#endif
111#endif
112
113#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
114#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
115#endif
116
117#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
118#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
119#endif
120
121#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
122#define CPPHTTPLIB_HEADER_MAX_COUNT 100
123#endif
124
125#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
126#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
127#endif
128
129#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
130#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
131#endif
132
133#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
134#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
135#endif
136
137#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
138#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
139#endif
140
141#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
142#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
143#endif
144
145#ifndef CPPHTTPLIB_TCP_NODELAY
146#define CPPHTTPLIB_TCP_NODELAY false
147#endif
148
149#ifndef CPPHTTPLIB_IPV6_V6ONLY
150#define CPPHTTPLIB_IPV6_V6ONLY false
151#endif
152
153#ifndef CPPHTTPLIB_RECV_BUFSIZ
154#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
155#endif
156
157#ifndef CPPHTTPLIB_SEND_BUFSIZ
158#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
159#endif
160
161#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
162#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
163#endif
164
165#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
166#define CPPHTTPLIB_THREAD_POOL_COUNT \
167 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
168 ? std::thread::hardware_concurrency() - 1 \
169 : 0))
170#endif
171
172#ifndef CPPHTTPLIB_RECV_FLAGS
173#define CPPHTTPLIB_RECV_FLAGS 0
174#endif
175
176#ifndef CPPHTTPLIB_SEND_FLAGS
177#define CPPHTTPLIB_SEND_FLAGS 0
178#endif
179
180#ifndef CPPHTTPLIB_LISTEN_BACKLOG
181#define CPPHTTPLIB_LISTEN_BACKLOG 5
182#endif
183
184#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
185#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
186#endif
187
188/*
189 * Headers
190 */
191
192#ifdef _WIN32
193#ifndef _CRT_SECURE_NO_WARNINGS
194#define _CRT_SECURE_NO_WARNINGS
195#endif //_CRT_SECURE_NO_WARNINGS
196
197#ifndef _CRT_NONSTDC_NO_DEPRECATE
198#define _CRT_NONSTDC_NO_DEPRECATE
199#endif //_CRT_NONSTDC_NO_DEPRECATE
200
201#if defined(_MSC_VER)
202#if _MSC_VER < 1900
203#error Sorry, Visual Studio versions prior to 2015 are not supported
204#endif
205
206#pragma comment(lib, "ws2_32.lib")
207
208 using ssize_t = __int64;
209#endif // _MSC_VER
210
211#ifndef S_ISREG
212#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
213#endif // S_ISREG
214
215#ifndef S_ISDIR
216#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
217#endif // S_ISDIR
218
219#ifndef NOMINMAX
220#define NOMINMAX
221#endif // NOMINMAX
222
223#include <io.h>
224#include <winsock2.h>
225#include <ws2tcpip.h>
226
227#if defined(__has_include)
228#if __has_include(<afunix.h>)
229// afunix.h uses types declared in winsock2.h, so has to be included after it.
230#include <afunix.h>
231#define CPPHTTPLIB_HAVE_AFUNIX_H 1
232#endif
233#endif
234
235#ifndef WSA_FLAG_NO_HANDLE_INHERIT
236#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
237#endif
238
239 using nfds_t = unsigned long;
240 using socket_t = SOCKET;
241 using socklen_t = int;
242
243#else // not _WIN32
244
245#include <arpa/inet.h>
246#if !defined(_AIX) && !defined(__MVS__)
247#include <ifaddrs.h>
248#endif
249#ifdef __MVS__
250#include <strings.h>
251#ifndef NI_MAXHOST
252#define NI_MAXHOST 1025
253#endif
254#endif
255#include <net/if.h>
256#include <netdb.h>
257#include <netinet/in.h>
258#ifdef __linux__
259#include <resolv.h>
260#endif
261#include <csignal>
262#include <netinet/tcp.h>
263#include <poll.h>
264#include <pthread.h>
265#include <sys/mman.h>
266#include <sys/socket.h>
267#include <sys/un.h>
268#include <unistd.h>
269
270 using socket_t = int;
271#ifndef INVALID_SOCKET
272#define INVALID_SOCKET (-1)
273#endif
274#endif //_WIN32
275
276#if defined(__APPLE__)
277#include <TargetConditionals.h>
278#endif
279
280#include <algorithm>
281#include <array>
282#include <atomic>
283#include <cassert>
284#include <cctype>
285#include <climits>
286#include <condition_variable>
287#include <cstring>
288#include <errno.h>
289#include <exception>
290#include <fcntl.h>
291#include <functional>
292#include <iomanip>
293#include <iostream>
294#include <list>
295#include <map>
296#include <memory>
297#include <mutex>
298#include <random>
299#include <regex>
300#include <set>
301#include <sstream>
302#include <string>
303#include <sys/stat.h>
304#include <thread>
305#include <unordered_map>
306#include <unordered_set>
307#include <utility>
308
309#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) || \
310 defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
311#if TARGET_OS_MAC
312#include <CFNetwork/CFHost.h>
313#include <CoreFoundation/CoreFoundation.h>
314#endif
315#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or
316 // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
317
318#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
319#ifdef _WIN32
320#include <wincrypt.h>
321
322// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
323// used
324#undef X509_NAME
325#undef X509_CERT_PAIR
326#undef X509_EXTENSIONS
327#undef PKCS7_SIGNER_INFO
328
329#ifdef _MSC_VER
330#pragma comment(lib, "crypt32.lib")
331#endif
332#endif // _WIN32
333
334#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
335#if TARGET_OS_MAC
336#include <Security/Security.h>
337#endif
338#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
339
340#include <openssl/err.h>
341#include <openssl/evp.h>
342#include <openssl/ssl.h>
343#include <openssl/x509v3.h>
344
345#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
346#include <openssl/applink.c>
347#endif
348
349#include <iostream>
350#include <sstream>
351
352#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
353#if OPENSSL_VERSION_NUMBER < 0x1010107f
354#error Please use OpenSSL or a current version of BoringSSL
355#endif
356#define SSL_get1_peer_certificate SSL_get_peer_certificate
357#elif OPENSSL_VERSION_NUMBER < 0x30000000L
358#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
359#endif
360
361#endif // CPPHTTPLIB_OPENSSL_SUPPORT
362
363#ifdef CPPHTTPLIB_ZLIB_SUPPORT
364#include <zlib.h>
365#endif
366
367#ifdef CPPHTTPLIB_BROTLI_SUPPORT
368#include <brotli/decode.h>
369#include <brotli/encode.h>
370#endif
371
372#ifdef CPPHTTPLIB_ZSTD_SUPPORT
373#include <zstd.h>
374#endif
375
376/*
377 * Declaration
378 */
379 namespace httplib {
380
381 namespace detail {
382
383/*
384 * Backport std::make_unique from C++14.
385 *
386 * NOTE: This code came up with the following stackoverflow post:
387 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
388 *
389 */
390
391 template <class T, class... Args>
392 typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
393 make_unique(Args &&...args) {
394 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
395}
396
397 template <class T>
398 typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
399 make_unique(std::size_t n) {
400 typedef typename std::remove_extent<T>::type RT;
401 return std::unique_ptr<T>(new RT[n]);
402}
403
404 namespace case_ignore {
405
406 inline unsigned char to_lower(int c) {
407 const static unsigned char table[256] = {
408 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
409 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
410 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
411 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
412 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
413 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
414 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
415 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
416 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
417 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
418 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
419 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
420 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
421 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
422 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
423 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
424 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
425 255,
426 };
427 return table[(unsigned char)(char)c];
428}
429
430 inline bool equal(const std::string &a, const std::string &b) {
431 return a.size() == b.size() &&
432 std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
433 return to_lower(ca) == to_lower(cb);
434 });
435}
436
437 struct equal_to {
438 bool operator()(const std::string &a, const std::string &b) const {
439 return equal(a, b);
440 }
441};
442
443 struct hash {
444 size_t operator()(const std::string &key) const {
445 return hash_core(key.data(), key.size(), 0);
446 }
447
448 size_t hash_core(const char *s, size_t l, size_t h) const {
449 return (l == 0) ? h
450 : hash_core(s + 1, l - 1,
451 // Unsets the 6 high bits of h, therefore no
452 // overflow happens
453 (((std::numeric_limits<size_t>::max)() >> 6) &
454 h * 33) ^
455 static_cast<unsigned char>(to_lower(*s)));
456 }
457};
458
459 template <typename T>
460 using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
461 detail::case_ignore::equal_to>;
462
463} // namespace case_ignore
464
465// This is based on
466// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
467
468 struct scope_exit {
469 explicit scope_exit(std::function<void(void)> &&f)
470 : exit_function(std::move(f)), execute_on_destruction{true} {}
471
472 scope_exit(scope_exit &&rhs) noexcept
473 : exit_function(std::move(rhs.exit_function)),
474 execute_on_destruction{rhs.execute_on_destruction} {
475 rhs.release();
476 }
477
478 ~scope_exit() {
479 if (execute_on_destruction) { this->exit_function(); }
480 }
481
482 void release() { this->execute_on_destruction = false; }
483
484 private:
485 scope_exit(const scope_exit &) = delete;
486 void operator=(const scope_exit &) = delete;
487 scope_exit &operator=(scope_exit &&) = delete;
488
489 std::function<void(void)> exit_function;
490 bool execute_on_destruction;
491};
492
493} // namespace detail
494
495 enum SSLVerifierResponse {
496 // no decision has been made, use the built-in certificate verifier
497 NoDecisionMade,
498 // connection certificate is verified and accepted
499 CertificateAccepted,
500 // connection certificate was processed but is rejected
501 CertificateRejected
502};
503
504 enum StatusCode {
505 // Information responses
506 Continue_100 = 100,
507 SwitchingProtocol_101 = 101,
508 Processing_102 = 102,
509 EarlyHints_103 = 103,
510
511 // Successful responses
512 OK_200 = 200,
513 Created_201 = 201,
514 Accepted_202 = 202,
515 NonAuthoritativeInformation_203 = 203,
516 NoContent_204 = 204,
517 ResetContent_205 = 205,
518 PartialContent_206 = 206,
519 MultiStatus_207 = 207,
520 AlreadyReported_208 = 208,
521 IMUsed_226 = 226,
522
523 // Redirection messages
524 MultipleChoices_300 = 300,
525 MovedPermanently_301 = 301,
526 Found_302 = 302,
527 SeeOther_303 = 303,
528 NotModified_304 = 304,
529 UseProxy_305 = 305,
530 unused_306 = 306,
531 TemporaryRedirect_307 = 307,
532 PermanentRedirect_308 = 308,
533
534 // Client error responses
535 BadRequest_400 = 400,
536 Unauthorized_401 = 401,
537 PaymentRequired_402 = 402,
538 Forbidden_403 = 403,
539 NotFound_404 = 404,
540 MethodNotAllowed_405 = 405,
541 NotAcceptable_406 = 406,
542 ProxyAuthenticationRequired_407 = 407,
543 RequestTimeout_408 = 408,
544 Conflict_409 = 409,
545 Gone_410 = 410,
546 LengthRequired_411 = 411,
547 PreconditionFailed_412 = 412,
548 PayloadTooLarge_413 = 413,
549 UriTooLong_414 = 414,
550 UnsupportedMediaType_415 = 415,
551 RangeNotSatisfiable_416 = 416,
552 ExpectationFailed_417 = 417,
553 ImATeapot_418 = 418,
554 MisdirectedRequest_421 = 421,
555 UnprocessableContent_422 = 422,
556 Locked_423 = 423,
557 FailedDependency_424 = 424,
558 TooEarly_425 = 425,
559 UpgradeRequired_426 = 426,
560 PreconditionRequired_428 = 428,
561 TooManyRequests_429 = 429,
562 RequestHeaderFieldsTooLarge_431 = 431,
563 UnavailableForLegalReasons_451 = 451,
564
565 // Server error responses
566 InternalServerError_500 = 500,
567 NotImplemented_501 = 501,
568 BadGateway_502 = 502,
569 ServiceUnavailable_503 = 503,
570 GatewayTimeout_504 = 504,
571 HttpVersionNotSupported_505 = 505,
572 VariantAlsoNegotiates_506 = 506,
573 InsufficientStorage_507 = 507,
574 LoopDetected_508 = 508,
575 NotExtended_510 = 510,
576 NetworkAuthenticationRequired_511 = 511,
577};
578
579 using Headers =
580 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
581 detail::case_ignore::equal_to>;
582
583 using Params = std::multimap<std::string, std::string>;
584 using Match = std::smatch;
585
586 using DownloadProgress = std::function<bool(size_t current, size_t total)>;
587 using UploadProgress = std::function<bool(size_t current, size_t total)>;
588
589 struct Response;
590 using ResponseHandler = std::function<bool(const Response &response)>;
591
592 struct FormData {
593 std::string name;
594 std::string content;
595 std::string filename;
596 std::string content_type;
597 Headers headers;
598};
599
600 struct FormField {
601 std::string name;
602 std::string content;
603 Headers headers;
604};
605 using FormFields = std::multimap<std::string, FormField>;
606
607 using FormFiles = std::multimap<std::string, FormData>;
608
609 struct MultipartFormData {
610 FormFields fields; // Text fields from multipart
611 FormFiles files; // Files from multipart
612
613 // Text field access
614 std::string get_field(const std::string &key, size_t id = 0) const;
615 std::vector<std::string> get_fields(const std::string &key) const;
616 bool has_field(const std::string &key) const;
617 size_t get_field_count(const std::string &key) const;
618
619 // File access
620 FormData get_file(const std::string &key, size_t id = 0) const;
621 std::vector<FormData> get_files(const std::string &key) const;
622 bool has_file(const std::string &key) const;
623 size_t get_file_count(const std::string &key) const;
624};
625
626 struct UploadFormData {
627 std::string name;
628 std::string content;
629 std::string filename;
630 std::string content_type;
631};
632 using UploadFormDataItems = std::vector<UploadFormData>;
633
634 class DataSink {
635 public:
636 DataSink() : os(&sb_), sb_(*this) {}
637
638 DataSink(const DataSink &) = delete;
639 DataSink &operator=(const DataSink &) = delete;
640 DataSink(DataSink &&) = delete;
641 DataSink &operator=(DataSink &&) = delete;
642
643 std::function<bool(const char *data, size_t data_len)> write;
644 std::function<bool()> is_writable;
645 std::function<void()> done;
646 std::function<void(const Headers &trailer)> done_with_trailer;
647 std::ostream os;
648
649 private:
650 class data_sink_streambuf final : public std::streambuf {
651 public:
652 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
653
654 protected:
655 std::streamsize xsputn(const char *s, std::streamsize n) override {
656 sink_.write(s, static_cast<size_t>(n));
657 return n;
658 }
659
660 private:
661 DataSink &sink_;
662 };
663
664 data_sink_streambuf sb_;
665};
666
667 using ContentProvider =
668 std::function<bool(size_t offset, size_t length, DataSink &sink)>;
669
670 using ContentProviderWithoutLength =
671 std::function<bool(size_t offset, DataSink &sink)>;
672
673 using ContentProviderResourceReleaser = std::function<void(bool success)>;
674
675 struct FormDataProvider {
676 std::string name;
677 ContentProviderWithoutLength provider;
678 std::string filename;
679 std::string content_type;
680};
681 using FormDataProviderItems = std::vector<FormDataProvider>;
682
683 using ContentReceiverWithProgress = std::function<bool(
684 const char *data, size_t data_length, size_t offset, size_t total_length)>;
685
686 using ContentReceiver =
687 std::function<bool(const char *data, size_t data_length)>;
688
689 using FormDataHeader = std::function<bool(const FormData &file)>;
690
691 class ContentReader {
692 public:
693 using Reader = std::function<bool(ContentReceiver receiver)>;
694 using FormDataReader =
695 std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
696
697 ContentReader(Reader reader, FormDataReader multipart_reader)
698 : reader_(std::move(reader)),
699 formdata_reader_(std::move(multipart_reader)) {}
700
701 bool operator()(FormDataHeader header, ContentReceiver receiver) const {
702 return formdata_reader_(std::move(header), std::move(receiver));
703 }
704
705 bool operator()(ContentReceiver receiver) const {
706 return reader_(std::move(receiver));
707 }
708
709 Reader reader_;
710 FormDataReader formdata_reader_;
711};
712
713 using Range = std::pair<ssize_t, ssize_t>;
714 using Ranges = std::vector<Range>;
715
716 struct Request {
717 std::string method;
718 std::string path;
719 std::string matched_route;
720 Params params;
721 Headers headers;
722 Headers trailers;
723 std::string body;
724
725 std::string remote_addr;
726 int remote_port = -1;
727 std::string local_addr;
728 int local_port = -1;
729
730 // for server
731 std::string version;
732 std::string target;
733 MultipartFormData form;
734 Ranges ranges;
735 Match matches;
736 std::unordered_map<std::string, std::string> path_params;
737 std::function<bool()> is_connection_closed = []() { return true; };
738
739 // for client
740 std::vector<std::string> accept_content_types;
741 ResponseHandler response_handler;
742 ContentReceiverWithProgress content_receiver;
743 DownloadProgress download_progress;
744 UploadProgress upload_progress;
745#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
746 const SSL *ssl = nullptr;
747#endif
748
749 bool has_header(const std::string &key) const;
750 std::string get_header_value(const std::string &key, const char *def = "",
751 size_t id = 0) const;
752 size_t get_header_value_u64(const std::string &key, size_t def = 0,
753 size_t id = 0) const;
754 size_t get_header_value_count(const std::string &key) const;
755 void set_header(const std::string &key, const std::string &val);
756
757 bool has_trailer(const std::string &key) const;
758 std::string get_trailer_value(const std::string &key, size_t id = 0) const;
759 size_t get_trailer_value_count(const std::string &key) const;
760
761 bool has_param(const std::string &key) const;
762 std::string get_param_value(const std::string &key, size_t id = 0) const;
763 size_t get_param_value_count(const std::string &key) const;
764
765 bool is_multipart_form_data() const;
766
767 // private members...
768 size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
769 size_t content_length_ = 0;
770 ContentProvider content_provider_;
771 bool is_chunked_content_provider_ = false;
772 size_t authorization_count_ = 0;
773 std::chrono::time_point<std::chrono::steady_clock> start_time_ =
774 (std::chrono::steady_clock::time_point::min)();
775};
776
777 struct Response {
778 std::string version;
779 int status = -1;
780 std::string reason;
781 Headers headers;
782 Headers trailers;
783 std::string body;
784 std::string location; // Redirect location
785
786 bool has_header(const std::string &key) const;
787 std::string get_header_value(const std::string &key, const char *def = "",
788 size_t id = 0) const;
789 size_t get_header_value_u64(const std::string &key, size_t def = 0,
790 size_t id = 0) const;
791 size_t get_header_value_count(const std::string &key) const;
792 void set_header(const std::string &key, const std::string &val);
793
794 bool has_trailer(const std::string &key) const;
795 std::string get_trailer_value(const std::string &key, size_t id = 0) const;
796 size_t get_trailer_value_count(const std::string &key) const;
797
798 void set_redirect(const std::string &url, int status = StatusCode::Found_302);
799 void set_content(const char *s, size_t n, const std::string &content_type);
800 void set_content(const std::string &s, const std::string &content_type);
801 void set_content(std::string &&s, const std::string &content_type);
802
803 void set_content_provider(
804 size_t length, const std::string &content_type, ContentProvider provider,
805 ContentProviderResourceReleaser resource_releaser = nullptr);
806
807 void set_content_provider(
808 const std::string &content_type, ContentProviderWithoutLength provider,
809 ContentProviderResourceReleaser resource_releaser = nullptr);
810
811 void set_chunked_content_provider(
812 const std::string &content_type, ContentProviderWithoutLength provider,
813 ContentProviderResourceReleaser resource_releaser = nullptr);
814
815 void set_file_content(const std::string &path,
816 const std::string &content_type);
817 void set_file_content(const std::string &path);
818
819 Response() = default;
820 Response(const Response &) = default;
821 Response &operator=(const Response &) = default;
822 Response(Response &&) = default;
823 Response &operator=(Response &&) = default;
824 ~Response() {
825 if (content_provider_resource_releaser_) {
826 content_provider_resource_releaser_(content_provider_success_);
827 }
828 }
829
830 // private members...
831 size_t content_length_ = 0;
832 ContentProvider content_provider_;
833 ContentProviderResourceReleaser content_provider_resource_releaser_;
834 bool is_chunked_content_provider_ = false;
835 bool content_provider_success_ = false;
836 std::string file_content_path_;
837 std::string file_content_content_type_;
838};
839
840 class Stream {
841 public:
842 virtual ~Stream() = default;
843
844 virtual bool is_readable() const = 0;
845 virtual bool wait_readable() const = 0;
846 virtual bool wait_writable() const = 0;
847
848 virtual ssize_t read(char *ptr, size_t size) = 0;
849 virtual ssize_t write(const char *ptr, size_t size) = 0;
850 virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
851 virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
852 virtual socket_t socket() const = 0;
853
854 virtual time_t duration() const = 0;
855
856 ssize_t write(const char *ptr);
857 ssize_t write(const std::string &s);
858};
859
860 class TaskQueue {
861 public:
862 TaskQueue() = default;
863 virtual ~TaskQueue() = default;
864
865 virtual bool enqueue(std::function<void()> fn) = 0;
866 virtual void shutdown() = 0;
867
868 virtual void on_idle() {}
869};
870
871 class ThreadPool final : public TaskQueue {
872 public:
873 explicit ThreadPool(size_t n, size_t mqr = 0)
874 : shutdown_(false), max_queued_requests_(mqr) {
875 while (n) {
876 threads_.emplace_back(worker(*this));
877 n--;
878 }
879 }
880
881 ThreadPool(const ThreadPool &) = delete;
882 ~ThreadPool() override = default;
883
884 bool enqueue(std::function<void()> fn) override {
885 {
886 std::unique_lock<std::mutex> lock(mutex_);
887 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
888 return false;
889 }
890 jobs_.push_back(std::move(fn));
891 }
892
893 cond_.notify_one();
894 return true;
895 }
896
897 void shutdown() override {
898 // Stop all worker threads...
899 {
900 std::unique_lock<std::mutex> lock(mutex_);
901 shutdown_ = true;
902 }
903
904 cond_.notify_all();
905
906 // Join...
907 for (auto &t : threads_) {
908 t.join();
909 }
910 }
911
912 private:
913 struct worker {
914 explicit worker(ThreadPool &pool) : pool_(pool) {}
915
916 void operator()() {
917 for (;;) {
918 std::function<void()> fn;
919 {
920 std::unique_lock<std::mutex> lock(pool_.mutex_);
921
922 pool_.cond_.wait(
923 lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
924
925 if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
926
927 fn = pool_.jobs_.front();
928 pool_.jobs_.pop_front();
929 }
930
931 assert(true == static_cast<bool>(fn));
932 fn();
933 }
934
935#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
936 !defined(LIBRESSL_VERSION_NUMBER)
937 OPENSSL_thread_stop();
938#endif
939 }
940
941 ThreadPool &pool_;
942 };
943 friend struct worker;
944
945 std::vector<std::thread> threads_;
946 std::list<std::function<void()>> jobs_;
947
948 bool shutdown_;
949 size_t max_queued_requests_ = 0;
950
951 std::condition_variable cond_;
952 std::mutex mutex_;
953};
954
955 using Logger = std::function<void(const Request &, const Response &)>;
956
957// Forward declaration for Error type
958 enum class Error;
959 using ErrorLogger = std::function<void(const Error &, const Request *)>;
960
961 using SocketOptions = std::function<void(socket_t sock)>;
962
963 namespace detail {
964
965 bool set_socket_opt_impl(socket_t sock, int level, int optname,
966 const void *optval, socklen_t optlen);
967 bool set_socket_opt(socket_t sock, int level, int optname, int opt);
968 bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,
969 time_t usec);
970
971} // namespace detail
972
973 void default_socket_options(socket_t sock);
974
975 const char *status_message(int status);
976
977std::string get_bearer_token_auth(const Request &req);
978
979 namespace detail {
980
981 class MatcherBase {
982 public:
983 MatcherBase(std::string pattern) : pattern_(pattern) {}
984 virtual ~MatcherBase() = default;
985
986 const std::string &pattern() const { return pattern_; }
987
988 // Match request path and populate its matches and
989 virtual bool match(Request &request) const = 0;
990
991 private:
992 std::string pattern_;
993};
994
1013 class PathParamsMatcher final : public MatcherBase {
1014 public:
1015 PathParamsMatcher(const std::string &pattern);
1016
1017 bool match(Request &request) const override;
1018
1019 private:
1020 // Treat segment separators as the end of path parameter capture
1021 // Does not need to handle query parameters as they are parsed before path
1022 // matching
1023 static constexpr char separator = '/';
1024
1025 // Contains static path fragments to match against, excluding the '/' after
1026 // path params
1027 // Fragments are separated by path params
1028 std::vector<std::string> static_fragments_;
1029 // Stores the names of the path parameters to be used as keys in the
1030 // Request::path_params map
1031 std::vector<std::string> param_names_;
1032};
1033
1042 class RegexMatcher final : public MatcherBase {
1043 public:
1044 RegexMatcher(const std::string &pattern)
1045 : MatcherBase(pattern), regex_(pattern) {}
1046
1047 bool match(Request &request) const override;
1048
1049 private:
1050 std::regex regex_;
1051};
1052
1053ssize_t write_headers(Stream &strm, const Headers &headers);
1054
1055} // namespace detail
1056
1057 class Server {
1058 public:
1059 using Handler = std::function<void(const Request &, Response &)>;
1060
1061 using ExceptionHandler =
1062 std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1063
1064 enum class HandlerResponse {
1065 Handled,
1066 Unhandled,
1067 };
1068 using HandlerWithResponse =
1069 std::function<HandlerResponse(const Request &, Response &)>;
1070
1071 using HandlerWithContentReader = std::function<void(
1072 const Request &, Response &, const ContentReader &content_reader)>;
1073
1074 using Expect100ContinueHandler =
1075 std::function<int(const Request &, Response &)>;
1076
1077 Server();
1078
1079 virtual ~Server();
1080
1081 virtual bool is_valid() const;
1082
1083 Server &Get(const std::string &pattern, Handler handler);
1084 Server &Post(const std::string &pattern, Handler handler);
1085 Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1086 Server &Put(const std::string &pattern, Handler handler);
1087 Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1088 Server &Patch(const std::string &pattern, Handler handler);
1089 Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1090 Server &Delete(const std::string &pattern, Handler handler);
1091 Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1092 Server &Options(const std::string &pattern, Handler handler);
1093
1094 bool set_base_dir(const std::string &dir,
1095 const std::string &mount_point = std::string());
1096 bool set_mount_point(const std::string &mount_point, const std::string &dir,
1097 Headers headers = Headers());
1098 bool remove_mount_point(const std::string &mount_point);
1099 Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1100 const std::string &mime);
1101 Server &set_default_file_mimetype(const std::string &mime);
1102 Server &set_file_request_handler(Handler handler);
1103
1104 template <class ErrorHandlerFunc>
1105 Server &set_error_handler(ErrorHandlerFunc &&handler) {
1106 return set_error_handler_core(
1107 std::forward<ErrorHandlerFunc>(handler),
1108 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1109 }
1110
1111 Server &set_exception_handler(ExceptionHandler handler);
1112
1113 Server &set_pre_routing_handler(HandlerWithResponse handler);
1114 Server &set_post_routing_handler(Handler handler);
1115
1116 Server &set_pre_request_handler(HandlerWithResponse handler);
1117
1118 Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1119 Server &set_logger(Logger logger);
1120 Server &set_pre_compression_logger(Logger logger);
1121 Server &set_error_logger(ErrorLogger error_logger);
1122
1123 Server &set_address_family(int family);
1124 Server &set_tcp_nodelay(bool on);
1125 Server &set_ipv6_v6only(bool on);
1126 Server &set_socket_options(SocketOptions socket_options);
1127
1128 Server &set_default_headers(Headers headers);
1129 Server &
1130 set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1131
1132 Server &set_keep_alive_max_count(size_t count);
1133 Server &set_keep_alive_timeout(time_t sec);
1134
1135 Server &set_read_timeout(time_t sec, time_t usec = 0);
1136 template <class Rep, class Period>
1137 Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1138
1139 Server &set_write_timeout(time_t sec, time_t usec = 0);
1140 template <class Rep, class Period>
1141 Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1142
1143 Server &set_idle_interval(time_t sec, time_t usec = 0);
1144 template <class Rep, class Period>
1145 Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1146
1147 Server &set_payload_max_length(size_t length);
1148
1149 bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1150 int bind_to_any_port(const std::string &host, int socket_flags = 0);
1151 bool listen_after_bind();
1152
1153 bool listen(const std::string &host, int port, int socket_flags = 0);
1154
1155 bool is_running() const;
1156 void wait_until_ready() const;
1157 void stop();
1158 void decommission();
1159
1160 std::function<TaskQueue *(void)> new_task_queue;
1161
1162 protected:
1163 bool process_request(Stream &strm, const std::string &remote_addr,
1164 int remote_port, const std::string &local_addr,
1165 int local_port, bool close_connection,
1166 bool &connection_closed,
1167 const std::function<void(Request &)> &setup_request);
1168
1169 std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1170 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
1171 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
1172 time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1173 time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1174 time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1175 time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
1176 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
1177 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
1178 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1179
1180 private:
1181 using Handlers =
1182 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1183 using HandlersForContentReader =
1184 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1185 HandlerWithContentReader>>;
1186
1187 static std::unique_ptr<detail::MatcherBase>
1188 make_matcher(const std::string &pattern);
1189
1190 Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1191 Server &set_error_handler_core(Handler handler, std::false_type);
1192
1193 socket_t create_server_socket(const std::string &host, int port,
1194 int socket_flags,
1195 SocketOptions socket_options) const;
1196 int bind_internal(const std::string &host, int port, int socket_flags);
1197 bool listen_internal();
1198
1199 bool routing(Request &req, Response &res, Stream &strm);
1200 bool handle_file_request(const Request &req, Response &res);
1201 bool dispatch_request(Request &req, Response &res,
1202 const Handlers &handlers) const;
1203 bool dispatch_request_for_content_reader(
1204 Request &req, Response &res, ContentReader content_reader,
1205 const HandlersForContentReader &handlers) const;
1206
1207 bool parse_request_line(const char *s, Request &req) const;
1208 void apply_ranges(const Request &req, Response &res,
1209 std::string &content_type, std::string &boundary) const;
1210 bool write_response(Stream &strm, bool close_connection, Request &req,
1211 Response &res);
1212 bool write_response_with_content(Stream &strm, bool close_connection,
1213 const Request &req, Response &res);
1214 bool write_response_core(Stream &strm, bool close_connection,
1215 const Request &req, Response &res,
1216 bool need_apply_ranges);
1217 bool write_content_with_provider(Stream &strm, const Request &req,
1218 Response &res, const std::string &boundary,
1219 const std::string &content_type);
1220 bool read_content(Stream &strm, Request &req, Response &res);
1221 bool read_content_with_content_receiver(Stream &strm, Request &req,
1222 Response &res,
1223 ContentReceiver receiver,
1224 FormDataHeader multipart_header,
1225 ContentReceiver multipart_receiver);
1226 bool read_content_core(Stream &strm, Request &req, Response &res,
1227 ContentReceiver receiver,
1228 FormDataHeader multipart_header,
1229 ContentReceiver multipart_receiver) const;
1230
1231 virtual bool process_and_close_socket(socket_t sock);
1232
1233 void output_log(const Request &req, const Response &res) const;
1234 void output_pre_compression_log(const Request &req,
1235 const Response &res) const;
1236 void output_error_log(const Error &err, const Request *req) const;
1237
1238 std::atomic<bool> is_running_{false};
1239 std::atomic<bool> is_decommissioned{false};
1240
1241 struct MountPointEntry {
1242 std::string mount_point;
1243 std::string base_dir;
1244 Headers headers;
1245 };
1246 std::vector<MountPointEntry> base_dirs_;
1247 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1248 std::string default_file_mimetype_ = "application/octet-stream";
1249 Handler file_request_handler_;
1250
1251 Handlers get_handlers_;
1252 Handlers post_handlers_;
1253 HandlersForContentReader post_handlers_for_content_reader_;
1254 Handlers put_handlers_;
1255 HandlersForContentReader put_handlers_for_content_reader_;
1256 Handlers patch_handlers_;
1257 HandlersForContentReader patch_handlers_for_content_reader_;
1258 Handlers delete_handlers_;
1259 HandlersForContentReader delete_handlers_for_content_reader_;
1260 Handlers options_handlers_;
1261
1262 HandlerWithResponse error_handler_;
1263 ExceptionHandler exception_handler_;
1264 HandlerWithResponse pre_routing_handler_;
1265 Handler post_routing_handler_;
1266 HandlerWithResponse pre_request_handler_;
1267 Expect100ContinueHandler expect_100_continue_handler_;
1268
1269 mutable std::mutex logger_mutex_;
1270 Logger logger_;
1271 Logger pre_compression_logger_;
1272 ErrorLogger error_logger_;
1273
1274 int address_family_ = AF_UNSPEC;
1275 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1276 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1277 SocketOptions socket_options_ = default_socket_options;
1278
1279 Headers default_headers_;
1280 std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1281 detail::write_headers;
1282};
1283
1284 enum class Error {
1285 Success = 0,
1286 Unknown,
1287 Connection,
1288 BindIPAddress,
1289 Read,
1290 Write,
1291 ExceedRedirectCount,
1292 Canceled,
1293 SSLConnection,
1294 SSLLoadingCerts,
1295 SSLServerVerification,
1296 SSLServerHostnameVerification,
1297 UnsupportedMultipartBoundaryChars,
1298 Compression,
1299 ConnectionTimeout,
1300 ProxyConnection,
1301 ResourceExhaustion,
1302 TooManyFormDataFiles,
1303 ExceedMaxPayloadSize,
1304 ExceedUriMaxLength,
1305 ExceedMaxSocketDescriptorCount,
1306 InvalidRequestLine,
1307 InvalidHTTPMethod,
1308 InvalidHTTPVersion,
1309 InvalidHeaders,
1310 MultipartParsing,
1311 OpenFile,
1312 Listen,
1313 GetSockName,
1314 UnsupportedAddressFamily,
1315 HTTPParsing,
1316 InvalidRangeHeader,
1317
1318 // For internal use only
1319 SSLPeerCouldBeClosed_,
1320};
1321
1322std::string to_string(Error error);
1323
1324std::ostream &operator<<(std::ostream &os, const Error &obj);
1325
1326 class Result {
1327 public:
1328 Result() = default;
1329 Result(std::unique_ptr<Response> &&res, Error err,
1330 Headers &&request_headers = Headers{})
1331 : res_(std::move(res)), err_(err),
1332 request_headers_(std::move(request_headers)) {}
1333#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1334 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1335 int ssl_error)
1336 : res_(std::move(res)), err_(err),
1337 request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
1338 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1339 int ssl_error, unsigned long ssl_openssl_error)
1340 : res_(std::move(res)), err_(err),
1341 request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1342 ssl_openssl_error_(ssl_openssl_error) {}
1343#endif
1344 // Response
1345 operator bool() const { return res_ != nullptr; }
1346 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1347 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1348 const Response &value() const { return *res_; }
1349 Response &value() { return *res_; }
1350 const Response &operator*() const { return *res_; }
1351 Response &operator*() { return *res_; }
1352 const Response *operator->() const { return res_.get(); }
1353 Response *operator->() { return res_.get(); }
1354
1355 // Error
1356 Error error() const { return err_; }
1357
1358#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1359 // SSL Error
1360 int ssl_error() const { return ssl_error_; }
1361 // OpenSSL Error
1362 unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }
1363#endif
1364
1365 // Request Headers
1366 bool has_request_header(const std::string &key) const;
1367 std::string get_request_header_value(const std::string &key,
1368 const char *def = "",
1369 size_t id = 0) const;
1370 size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
1371 size_t id = 0) const;
1372 size_t get_request_header_value_count(const std::string &key) const;
1373
1374 private:
1375 std::unique_ptr<Response> res_;
1376 Error err_ = Error::Unknown;
1377 Headers request_headers_;
1378#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1379 int ssl_error_ = 0;
1380 unsigned long ssl_openssl_error_ = 0;
1381#endif
1382};
1383
1384 class ClientImpl {
1385 public:
1386 explicit ClientImpl(const std::string &host);
1387
1388 explicit ClientImpl(const std::string &host, int port);
1389
1390 explicit ClientImpl(const std::string &host, int port,
1391 const std::string &client_cert_path,
1392 const std::string &client_key_path);
1393
1394 virtual ~ClientImpl();
1395
1396 virtual bool is_valid() const;
1397
1398 // clang-format off
1399 Result Get(const std::string &path, DownloadProgress progress = nullptr);
1400 Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1401 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1402 Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1403 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1404 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1405 Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1406 Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1407 Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1408
1409 Result Head(const std::string &path);
1410 Result Head(const std::string &path, const Headers &headers);
1411
1412 Result Post(const std::string &path);
1413 Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1414 Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1415 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1416 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1417 Result Post(const std::string &path, const Params &params);
1418 Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1419 Result Post(const std::string &path, const Headers &headers);
1420 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1421 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1422 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1423 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1424 Result Post(const std::string &path, const Headers &headers, const Params &params);
1425 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1426 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1427 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1428 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1429
1430 Result Put(const std::string &path);
1431 Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1432 Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1433 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1434 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1435 Result Put(const std::string &path, const Params &params);
1436 Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1437 Result Put(const std::string &path, const Headers &headers);
1438 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1439 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1440 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1441 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1442 Result Put(const std::string &path, const Headers &headers, const Params &params);
1443 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1444 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1445 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1446 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1447
1448 Result Patch(const std::string &path);
1449 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1450 Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1451 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1452 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1453 Result Patch(const std::string &path, const Params &params);
1454 Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1455 Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
1456 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1457 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1458 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1459 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1460 Result Patch(const std::string &path, const Headers &headers, const Params &params);
1461 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1462 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1463 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1464 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1465
1466 Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1467 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1468 Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1469 Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1470 Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1471 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1472 Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1473 Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1474
1475 Result Options(const std::string &path);
1476 Result Options(const std::string &path, const Headers &headers);
1477 // clang-format on
1478
1479 bool send(Request &req, Response &res, Error &error);
1480 Result send(const Request &req);
1481
1482 void stop();
1483
1484 std::string host() const;
1485 int port() const;
1486
1487 size_t is_socket_open() const;
1488 socket_t socket() const;
1489
1490 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1491
1492 void set_default_headers(Headers headers);
1493
1494 void
1495 set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1496
1497 void set_address_family(int family);
1498 void set_tcp_nodelay(bool on);
1499 void set_ipv6_v6only(bool on);
1500 void set_socket_options(SocketOptions socket_options);
1501
1502 void set_connection_timeout(time_t sec, time_t usec = 0);
1503 template <class Rep, class Period>
1504 void
1505 set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1506
1507 void set_read_timeout(time_t sec, time_t usec = 0);
1508 template <class Rep, class Period>
1509 void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1510
1511 void set_write_timeout(time_t sec, time_t usec = 0);
1512 template <class Rep, class Period>
1513 void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1514
1515 void set_max_timeout(time_t msec);
1516 template <class Rep, class Period>
1517 void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1518
1519 void set_basic_auth(const std::string &username, const std::string &password);
1520 void set_bearer_token_auth(const std::string &token);
1521#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1522 void set_digest_auth(const std::string &username,
1523 const std::string &password);
1524#endif
1525
1526 void set_keep_alive(bool on);
1527 void set_follow_location(bool on);
1528
1529 void set_path_encode(bool on);
1530
1531 void set_compress(bool on);
1532
1533 void set_decompress(bool on);
1534
1535 void set_interface(const std::string &intf);
1536
1537 void set_proxy(const std::string &host, int port);
1538 void set_proxy_basic_auth(const std::string &username,
1539 const std::string &password);
1540 void set_proxy_bearer_token_auth(const std::string &token);
1541#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1542 void set_proxy_digest_auth(const std::string &username,
1543 const std::string &password);
1544#endif
1545
1546#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1547 void set_ca_cert_path(const std::string &ca_cert_file_path,
1548 const std::string &ca_cert_dir_path = std::string());
1549 void set_ca_cert_store(X509_STORE *ca_cert_store);
1550 X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1551#endif
1552
1553#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1554 void enable_server_certificate_verification(bool enabled);
1555 void enable_server_hostname_verification(bool enabled);
1556 void set_server_certificate_verifier(
1557 std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1558#endif
1559
1560 void set_logger(Logger logger);
1561 void set_error_logger(ErrorLogger error_logger);
1562
1563 protected:
1564 struct Socket {
1565 socket_t sock = INVALID_SOCKET;
1566#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1567 SSL *ssl = nullptr;
1568#endif
1569
1570 bool is_open() const { return sock != INVALID_SOCKET; }
1571 };
1572
1573 virtual bool create_and_connect_socket(Socket &socket, Error &error);
1574
1575 // All of:
1576 // shutdown_ssl
1577 // shutdown_socket
1578 // close_socket
1579 // should ONLY be called when socket_mutex_ is locked.
1580 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1581 // with a DIFFERENT thread sending requests using that socket.
1582 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1583 void shutdown_socket(Socket &socket) const;
1584 void close_socket(Socket &socket);
1585
1586 bool process_request(Stream &strm, Request &req, Response &res,
1587 bool close_connection, Error &error);
1588
1589 bool write_content_with_provider(Stream &strm, const Request &req,
1590 Error &error) const;
1591
1592 void copy_settings(const ClientImpl &rhs);
1593
1594 void output_log(const Request &req, const Response &res) const;
1595 void output_error_log(const Error &err, const Request *req) const;
1596
1597 // Socket endpoint information
1598 const std::string host_;
1599 const int port_;
1600 const std::string host_and_port_;
1601
1602 // Current open socket
1603 Socket socket_;
1604 mutable std::mutex socket_mutex_;
1605 std::recursive_mutex request_mutex_;
1606
1607 // These are all protected under socket_mutex
1608 size_t socket_requests_in_flight_ = 0;
1609 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1610 bool socket_should_be_closed_when_request_is_done_ = false;
1611
1612 // Hostname-IP map
1613 std::map<std::string, std::string> addr_map_;
1614
1615 // Default headers
1616 Headers default_headers_;
1617
1618 // Header writer
1619 std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1620 detail::write_headers;
1621
1622 // Settings
1623 std::string client_cert_path_;
1624 std::string client_key_path_;
1625
1626 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1627 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1628 time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1629 time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1630 time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1631 time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1632 time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
1633
1634 std::string basic_auth_username_;
1635 std::string basic_auth_password_;
1636 std::string bearer_token_auth_token_;
1637#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1638 std::string digest_auth_username_;
1639 std::string digest_auth_password_;
1640#endif
1641
1642 bool keep_alive_ = false;
1643 bool follow_location_ = false;
1644
1645 bool path_encode_ = true;
1646
1647 int address_family_ = AF_UNSPEC;
1648 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1649 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1650 SocketOptions socket_options_ = nullptr;
1651
1652 bool compress_ = false;
1653 bool decompress_ = true;
1654
1655 std::string interface_;
1656
1657 std::string proxy_host_;
1658 int proxy_port_ = -1;
1659
1660 std::string proxy_basic_auth_username_;
1661 std::string proxy_basic_auth_password_;
1662 std::string proxy_bearer_token_auth_token_;
1663#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1664 std::string proxy_digest_auth_username_;
1665 std::string proxy_digest_auth_password_;
1666#endif
1667
1668#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1669 std::string ca_cert_file_path_;
1670 std::string ca_cert_dir_path_;
1671
1672 X509_STORE *ca_cert_store_ = nullptr;
1673#endif
1674
1675#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1676 bool server_certificate_verification_ = true;
1677 bool server_hostname_verification_ = true;
1678 std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
1679#endif
1680
1681 mutable std::mutex logger_mutex_;
1682 Logger logger_;
1683 ErrorLogger error_logger_;
1684
1685#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1686 int last_ssl_error_ = 0;
1687 unsigned long last_openssl_error_ = 0;
1688#endif
1689
1690 private:
1691 bool send_(Request &req, Response &res, Error &error);
1692 Result send_(Request &&req);
1693
1694 socket_t create_client_socket(Error &error) const;
1695 bool read_response_line(Stream &strm, const Request &req,
1696 Response &res) const;
1697 bool write_request(Stream &strm, Request &req, bool close_connection,
1698 Error &error);
1699 bool redirect(Request &req, Response &res, Error &error);
1700 bool create_redirect_client(const std::string &scheme,
1701 const std::string &host, int port, Request &req,
1702 Response &res, const std::string &path,
1703 const std::string &location, Error &error);
1704 template <typename ClientType> void setup_redirect_client(ClientType &client);
1705 bool handle_request(Stream &strm, Request &req, Response &res,
1706 bool close_connection, Error &error);
1707 std::unique_ptr<Response> send_with_content_provider(
1708 Request &req, const char *body, size_t content_length,
1709 ContentProvider content_provider,
1710 ContentProviderWithoutLength content_provider_without_length,
1711 const std::string &content_type, Error &error);
1712 Result send_with_content_provider(
1713 const std::string &method, const std::string &path,
1714 const Headers &headers, const char *body, size_t content_length,
1715 ContentProvider content_provider,
1716 ContentProviderWithoutLength content_provider_without_length,
1717 const std::string &content_type, UploadProgress progress);
1718 ContentProviderWithoutLength get_multipart_content_provider(
1719 const std::string &boundary, const UploadFormDataItems &items,
1720 const FormDataProviderItems &provider_items) const;
1721
1722 std::string adjust_host_string(const std::string &host) const;
1723
1724 virtual bool
1725 process_socket(const Socket &socket,
1726 std::chrono::time_point<std::chrono::steady_clock> start_time,
1727 std::function<bool(Stream &strm)> callback);
1728 virtual bool is_ssl() const;
1729};
1730
1731 class Client {
1732 public:
1733 // Universal interface
1734 explicit Client(const std::string &scheme_host_port);
1735
1736 explicit Client(const std::string &scheme_host_port,
1737 const std::string &client_cert_path,
1738 const std::string &client_key_path);
1739
1740 // HTTP only interface
1741 explicit Client(const std::string &host, int port);
1742
1743 explicit Client(const std::string &host, int port,
1744 const std::string &client_cert_path,
1745 const std::string &client_key_path);
1746
1747 Client(Client &&) = default;
1748 Client &operator=(Client &&) = default;
1749
1750 ~Client();
1751
1752 bool is_valid() const;
1753
1754 // clang-format off
1755 Result Get(const std::string &path, DownloadProgress progress = nullptr);
1756 Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1757 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1758 Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1759 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1760 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1761 Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1762 Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1763 Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1764
1765 Result Head(const std::string &path);
1766 Result Head(const std::string &path, const Headers &headers);
1767
1768 Result Post(const std::string &path);
1769 Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1770 Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1771 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1772 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1773 Result Post(const std::string &path, const Params &params);
1774 Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1775 Result Post(const std::string &path, const Headers &headers);
1776 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1777 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1778 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1779 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1780 Result Post(const std::string &path, const Headers &headers, const Params &params);
1781 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1782 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1783 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1784 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1785
1786 Result Put(const std::string &path);
1787 Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1788 Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1789 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1790 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1791 Result Put(const std::string &path, const Params &params);
1792 Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1793 Result Put(const std::string &path, const Headers &headers);
1794 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1795 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1796 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1797 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1798 Result Put(const std::string &path, const Headers &headers, const Params &params);
1799 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1800 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1801 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1802 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1803
1804 Result Patch(const std::string &path);
1805 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1806 Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1807 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1808 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1809 Result Patch(const std::string &path, const Params &params);
1810 Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1811 Result Patch(const std::string &path, const Headers &headers);
1812 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1813 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1814 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1815 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1816 Result Patch(const std::string &path, const Headers &headers, const Params &params);
1817 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1818 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1819 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1820 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1821
1822 Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1823 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1824 Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1825 Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1826 Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1827 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1828 Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1829 Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1830
1831 Result Options(const std::string &path);
1832 Result Options(const std::string &path, const Headers &headers);
1833 // clang-format on
1834
1835 bool send(Request &req, Response &res, Error &error);
1836 Result send(const Request &req);
1837
1838 void stop();
1839
1840 std::string host() const;
1841 int port() const;
1842
1843 size_t is_socket_open() const;
1844 socket_t socket() const;
1845
1846 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1847
1848 void set_default_headers(Headers headers);
1849
1850 void
1851 set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1852
1853 void set_address_family(int family);
1854 void set_tcp_nodelay(bool on);
1855 void set_socket_options(SocketOptions socket_options);
1856
1857 void set_connection_timeout(time_t sec, time_t usec = 0);
1858 template <class Rep, class Period>
1859 void
1860 set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1861
1862 void set_read_timeout(time_t sec, time_t usec = 0);
1863 template <class Rep, class Period>
1864 void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1865
1866 void set_write_timeout(time_t sec, time_t usec = 0);
1867 template <class Rep, class Period>
1868 void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1869
1870 void set_max_timeout(time_t msec);
1871 template <class Rep, class Period>
1872 void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1873
1874 void set_basic_auth(const std::string &username, const std::string &password);
1875 void set_bearer_token_auth(const std::string &token);
1876#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1877 void set_digest_auth(const std::string &username,
1878 const std::string &password);
1879#endif
1880
1881 void set_keep_alive(bool on);
1882 void set_follow_location(bool on);
1883
1884 void set_path_encode(bool on);
1885 void set_url_encode(bool on);
1886
1887 void set_compress(bool on);
1888
1889 void set_decompress(bool on);
1890
1891 void set_interface(const std::string &intf);
1892
1893 void set_proxy(const std::string &host, int port);
1894 void set_proxy_basic_auth(const std::string &username,
1895 const std::string &password);
1896 void set_proxy_bearer_token_auth(const std::string &token);
1897#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1898 void set_proxy_digest_auth(const std::string &username,
1899 const std::string &password);
1900#endif
1901
1902#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1903 void enable_server_certificate_verification(bool enabled);
1904 void enable_server_hostname_verification(bool enabled);
1905 void set_server_certificate_verifier(
1906 std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1907#endif
1908
1909 void set_logger(Logger logger);
1910 void set_error_logger(ErrorLogger error_logger);
1911
1912 // SSL
1913#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1914 void set_ca_cert_path(const std::string &ca_cert_file_path,
1915 const std::string &ca_cert_dir_path = std::string());
1916
1917 void set_ca_cert_store(X509_STORE *ca_cert_store);
1918 void load_ca_cert_store(const char *ca_cert, std::size_t size);
1919
1920 long get_openssl_verify_result() const;
1921
1922 SSL_CTX *ssl_context() const;
1923#endif
1924
1925 private:
1926 std::unique_ptr<ClientImpl> cli_;
1927
1928#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1929 bool is_ssl_ = false;
1930#endif
1931};
1932
1933#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1934 class SSLServer : public Server {
1935 public:
1936 SSLServer(const char *cert_path, const char *private_key_path,
1937 const char *client_ca_cert_file_path = nullptr,
1938 const char *client_ca_cert_dir_path = nullptr,
1939 const char *private_key_password = nullptr);
1940
1941 SSLServer(X509 *cert, EVP_PKEY *private_key,
1942 X509_STORE *client_ca_cert_store = nullptr);
1943
1944 SSLServer(
1945 const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1946
1947 ~SSLServer() override;
1948
1949 bool is_valid() const override;
1950
1951 SSL_CTX *ssl_context() const;
1952
1953 void update_certs(X509 *cert, EVP_PKEY *private_key,
1954 X509_STORE *client_ca_cert_store = nullptr);
1955
1956 private:
1957 bool process_and_close_socket(socket_t sock) override;
1958
1959 SSL_CTX *ctx_;
1960 std::mutex ctx_mutex_;
1961#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1962 int last_ssl_error_ = 0;
1963#endif
1964};
1965
1966 class SSLClient final : public ClientImpl {
1967 public:
1968 explicit SSLClient(const std::string &host);
1969
1970 explicit SSLClient(const std::string &host, int port);
1971
1972 explicit SSLClient(const std::string &host, int port,
1973 const std::string &client_cert_path,
1974 const std::string &client_key_path,
1975 const std::string &private_key_password = std::string());
1976
1977 explicit SSLClient(const std::string &host, int port, X509 *client_cert,
1978 EVP_PKEY *client_key,
1979 const std::string &private_key_password = std::string());
1980
1981 ~SSLClient() override;
1982
1983 bool is_valid() const override;
1984
1985 void set_ca_cert_store(X509_STORE *ca_cert_store);
1986 void load_ca_cert_store(const char *ca_cert, std::size_t size);
1987
1988 long get_openssl_verify_result() const;
1989
1990 SSL_CTX *ssl_context() const;
1991
1992 private:
1993 bool create_and_connect_socket(Socket &socket, Error &error) override;
1994 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1995 void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
1996
1997 bool
1998 process_socket(const Socket &socket,
1999 std::chrono::time_point<std::chrono::steady_clock> start_time,
2000 std::function<bool(Stream &strm)> callback) override;
2001 bool is_ssl() const override;
2002
2003 bool connect_with_proxy(
2004 Socket &sock,
2005 std::chrono::time_point<std::chrono::steady_clock> start_time,
2006 Response &res, bool &success, Error &error);
2007 bool initialize_ssl(Socket &socket, Error &error);
2008
2009 bool load_certs();
2010
2011 bool verify_host(X509 *server_cert) const;
2012 bool verify_host_with_subject_alt_name(X509 *server_cert) const;
2013 bool verify_host_with_common_name(X509 *server_cert) const;
2014 bool check_host_name(const char *pattern, size_t pattern_len) const;
2015
2016 SSL_CTX *ctx_;
2017 std::mutex ctx_mutex_;
2018 std::once_flag initialize_cert_;
2019
2020 std::vector<std::string> host_components_;
2021
2022 long verify_result_ = 0;
2023
2024 friend class ClientImpl;
2025};
2026#endif
2027
2028/*
2029 * Implementation of template methods.
2030 */
2031
2032 namespace detail {
2033
2034 template <typename T, typename U>
2035 inline void duration_to_sec_and_usec(const T &duration, U callback) {
2036 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2037 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2038 duration - std::chrono::seconds(sec))
2039 .count();
2040 callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2041}
2042
2043 template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2044 return N - 1;
2045}
2046
2047 inline bool is_numeric(const std::string &str) {
2048 return !str.empty() &&
2049 std::all_of(str.cbegin(), str.cend(),
2050 [](unsigned char c) { return std::isdigit(c); });
2051}
2052
2053 inline size_t get_header_value_u64(const Headers &headers,
2054 const std::string &key, size_t def,
2055 size_t id, bool &is_invalid_value) {
2056 is_invalid_value = false;
2057 auto rng = headers.equal_range(key);
2058 auto it = rng.first;
2059 std::advance(it, static_cast<ssize_t>(id));
2060 if (it != rng.second) {
2061 if (is_numeric(it->second)) {
2062 return std::strtoull(it->second.data(), nullptr, 10);
2063 } else {
2064 is_invalid_value = true;
2065 }
2066 }
2067 return def;
2068}
2069
2070 inline size_t get_header_value_u64(const Headers &headers,
2071 const std::string &key, size_t def,
2072 size_t id) {
2073 auto dummy = false;
2074 return get_header_value_u64(headers, key, def, id, dummy);
2075}
2076
2077} // namespace detail
2078
2079 inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
2080 size_t id) const {
2081 return detail::get_header_value_u64(headers, key, def, id);
2082}
2083
2084 inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
2085 size_t id) const {
2086 return detail::get_header_value_u64(headers, key, def, id);
2087}
2088
2089 namespace detail {
2090
2091 inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
2092 const void *optval, socklen_t optlen) {
2093 return setsockopt(sock, level, optname,
2094#ifdef _WIN32
2095 reinterpret_cast<const char *>(optval),
2096#else
2097 optval,
2098#endif
2099 optlen) == 0;
2100}
2101
2102 inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
2103 return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
2104}
2105
2106 inline bool set_socket_opt_time(socket_t sock, int level, int optname,
2107 time_t sec, time_t usec) {
2108#ifdef _WIN32
2109 auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
2110#else
2111 timeval timeout;
2112 timeout.tv_sec = static_cast<long>(sec);
2113 timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
2114#endif
2115 return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
2116}
2117
2118} // namespace detail
2119
2120 inline void default_socket_options(socket_t sock) {
2121 detail::set_socket_opt(sock, SOL_SOCKET,
2122#ifdef SO_REUSEPORT
2123 SO_REUSEPORT,
2124#else
2125 SO_REUSEADDR,
2126#endif
2127 1);
2128}
2129
2130 inline const char *status_message(int status) {
2131 switch (status) {
2132 case StatusCode::Continue_100: return "Continue";
2133 case StatusCode::SwitchingProtocol_101: return "Switching Protocol";
2134 case StatusCode::Processing_102: return "Processing";
2135 case StatusCode::EarlyHints_103: return "Early Hints";
2136 case StatusCode::OK_200: return "OK";
2137 case StatusCode::Created_201: return "Created";
2138 case StatusCode::Accepted_202: return "Accepted";
2139 case StatusCode::NonAuthoritativeInformation_203:
2140 return "Non-Authoritative Information";
2141 case StatusCode::NoContent_204: return "No Content";
2142 case StatusCode::ResetContent_205: return "Reset Content";
2143 case StatusCode::PartialContent_206: return "Partial Content";
2144 case StatusCode::MultiStatus_207: return "Multi-Status";
2145 case StatusCode::AlreadyReported_208: return "Already Reported";
2146 case StatusCode::IMUsed_226: return "IM Used";
2147 case StatusCode::MultipleChoices_300: return "Multiple Choices";
2148 case StatusCode::MovedPermanently_301: return "Moved Permanently";
2149 case StatusCode::Found_302: return "Found";
2150 case StatusCode::SeeOther_303: return "See Other";
2151 case StatusCode::NotModified_304: return "Not Modified";
2152 case StatusCode::UseProxy_305: return "Use Proxy";
2153 case StatusCode::unused_306: return "unused";
2154 case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";
2155 case StatusCode::PermanentRedirect_308: return "Permanent Redirect";
2156 case StatusCode::BadRequest_400: return "Bad Request";
2157 case StatusCode::Unauthorized_401: return "Unauthorized";
2158 case StatusCode::PaymentRequired_402: return "Payment Required";
2159 case StatusCode::Forbidden_403: return "Forbidden";
2160 case StatusCode::NotFound_404: return "Not Found";
2161 case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";
2162 case StatusCode::NotAcceptable_406: return "Not Acceptable";
2163 case StatusCode::ProxyAuthenticationRequired_407:
2164 return "Proxy Authentication Required";
2165 case StatusCode::RequestTimeout_408: return "Request Timeout";
2166 case StatusCode::Conflict_409: return "Conflict";
2167 case StatusCode::Gone_410: return "Gone";
2168 case StatusCode::LengthRequired_411: return "Length Required";
2169 case StatusCode::PreconditionFailed_412: return "Precondition Failed";
2170 case StatusCode::PayloadTooLarge_413: return "Payload Too Large";
2171 case StatusCode::UriTooLong_414: return "URI Too Long";
2172 case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";
2173 case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";
2174 case StatusCode::ExpectationFailed_417: return "Expectation Failed";
2175 case StatusCode::ImATeapot_418: return "I'm a teapot";
2176 case StatusCode::MisdirectedRequest_421: return "Misdirected Request";
2177 case StatusCode::UnprocessableContent_422: return "Unprocessable Content";
2178 case StatusCode::Locked_423: return "Locked";
2179 case StatusCode::FailedDependency_424: return "Failed Dependency";
2180 case StatusCode::TooEarly_425: return "Too Early";
2181 case StatusCode::UpgradeRequired_426: return "Upgrade Required";
2182 case StatusCode::PreconditionRequired_428: return "Precondition Required";
2183 case StatusCode::TooManyRequests_429: return "Too Many Requests";
2184 case StatusCode::RequestHeaderFieldsTooLarge_431:
2185 return "Request Header Fields Too Large";
2186 case StatusCode::UnavailableForLegalReasons_451:
2187 return "Unavailable For Legal Reasons";
2188 case StatusCode::NotImplemented_501: return "Not Implemented";
2189 case StatusCode::BadGateway_502: return "Bad Gateway";
2190 case StatusCode::ServiceUnavailable_503: return "Service Unavailable";
2191 case StatusCode::GatewayTimeout_504: return "Gateway Timeout";
2192 case StatusCode::HttpVersionNotSupported_505:
2193 return "HTTP Version Not Supported";
2194 case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";
2195 case StatusCode::InsufficientStorage_507: return "Insufficient Storage";
2196 case StatusCode::LoopDetected_508: return "Loop Detected";
2197 case StatusCode::NotExtended_510: return "Not Extended";
2198 case StatusCode::NetworkAuthenticationRequired_511:
2199 return "Network Authentication Required";
2200
2201 default:
2202 case StatusCode::InternalServerError_500: return "Internal Server Error";
2203 }
2204}
2205
2206 inline std::string get_bearer_token_auth(const Request &req) {
2207 if (req.has_header("Authorization")) {
2208 constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2209 return req.get_header_value("Authorization")
2210 .substr(bearer_header_prefix_len);
2211 }
2212 return "";
2213}
2214
2215 template <class Rep, class Period>
2216 inline Server &
2217 Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2218 detail::duration_to_sec_and_usec(
2219 duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2220 return *this;
2221}
2222
2223 template <class Rep, class Period>
2224 inline Server &
2225 Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2226 detail::duration_to_sec_and_usec(
2227 duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2228 return *this;
2229}
2230
2231 template <class Rep, class Period>
2232 inline Server &
2233 Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
2234 detail::duration_to_sec_and_usec(
2235 duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2236 return *this;
2237}
2238
2239 inline std::string to_string(const Error error) {
2240 switch (error) {
2241 case Error::Success: return "Success (no error)";
2242 case Error::Unknown: return "Unknown";
2243 case Error::Connection: return "Could not establish connection";
2244 case Error::BindIPAddress: return "Failed to bind IP address";
2245 case Error::Read: return "Failed to read connection";
2246 case Error::Write: return "Failed to write connection";
2247 case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
2248 case Error::Canceled: return "Connection handling canceled";
2249 case Error::SSLConnection: return "SSL connection failed";
2250 case Error::SSLLoadingCerts: return "SSL certificate loading failed";
2251 case Error::SSLServerVerification: return "SSL server verification failed";
2252 case Error::SSLServerHostnameVerification:
2253 return "SSL server hostname verification failed";
2254 case Error::UnsupportedMultipartBoundaryChars:
2255 return "Unsupported HTTP multipart boundary characters";
2256 case Error::Compression: return "Compression failed";
2257 case Error::ConnectionTimeout: return "Connection timed out";
2258 case Error::ProxyConnection: return "Proxy connection failed";
2259 case Error::ResourceExhaustion: return "Resource exhaustion";
2260 case Error::TooManyFormDataFiles: return "Too many form data files";
2261 case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
2262 case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
2263 case Error::ExceedMaxSocketDescriptorCount:
2264 return "Exceeded maximum socket descriptor count";
2265 case Error::InvalidRequestLine: return "Invalid request line";
2266 case Error::InvalidHTTPMethod: return "Invalid HTTP method";
2267 case Error::InvalidHTTPVersion: return "Invalid HTTP version";
2268 case Error::InvalidHeaders: return "Invalid headers";
2269 case Error::MultipartParsing: return "Multipart parsing failed";
2270 case Error::OpenFile: return "Failed to open file";
2271 case Error::Listen: return "Failed to listen on socket";
2272 case Error::GetSockName: return "Failed to get socket name";
2273 case Error::UnsupportedAddressFamily: return "Unsupported address family";
2274 case Error::HTTPParsing: return "HTTP parsing failed";
2275 case Error::InvalidRangeHeader: return "Invalid Range header";
2276 default: break;
2277 }
2278
2279 return "Invalid";
2280}
2281
2282 inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
2283 os << to_string(obj);
2284 os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
2285 return os;
2286}
2287
2288 inline size_t Result::get_request_header_value_u64(const std::string &key,
2289 size_t def,
2290 size_t id) const {
2291 return detail::get_header_value_u64(request_headers_, key, def, id);
2292}
2293
2294 template <class Rep, class Period>
2295 inline void ClientImpl::set_connection_timeout(
2296 const std::chrono::duration<Rep, Period> &duration) {
2297 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2298 set_connection_timeout(sec, usec);
2299 });
2300}
2301
2302 template <class Rep, class Period>
2303 inline void ClientImpl::set_read_timeout(
2304 const std::chrono::duration<Rep, Period> &duration) {
2305 detail::duration_to_sec_and_usec(
2306 duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2307}
2308
2309 template <class Rep, class Period>
2310 inline void ClientImpl::set_write_timeout(
2311 const std::chrono::duration<Rep, Period> &duration) {
2312 detail::duration_to_sec_and_usec(
2313 duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2314}
2315
2316 template <class Rep, class Period>
2317 inline void ClientImpl::set_max_timeout(
2318 const std::chrono::duration<Rep, Period> &duration) {
2319 auto msec =
2320 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2321 set_max_timeout(msec);
2322}
2323
2324 template <class Rep, class Period>
2325 inline void Client::set_connection_timeout(
2326 const std::chrono::duration<Rep, Period> &duration) {
2327 cli_->set_connection_timeout(duration);
2328}
2329
2330 template <class Rep, class Period>
2331 inline void
2332 Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2333 cli_->set_read_timeout(duration);
2334}
2335
2336 template <class Rep, class Period>
2337 inline void
2338 Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2339 cli_->set_write_timeout(duration);
2340}
2341
2342 inline void Client::set_max_timeout(time_t msec) {
2343 cli_->set_max_timeout(msec);
2344}
2345
2346 template <class Rep, class Period>
2347 inline void
2348 Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2349 cli_->set_max_timeout(duration);
2350}
2351
2352/*
2353 * Forward declarations and types that will be part of the .h file if split into
2354 * .h + .cc.
2355 */
2356
2357std::string hosted_at(const std::string &hostname);
2358
2359 void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2360
2361// JavaScript-style URL encoding/decoding functions
2362std::string encode_uri_component(const std::string &value);
2363std::string encode_uri(const std::string &value);
2364std::string decode_uri_component(const std::string &value);
2365std::string decode_uri(const std::string &value);
2366
2367// RFC 3986 compliant URL component encoding/decoding functions
2368std::string encode_path_component(const std::string &component);
2369std::string decode_path_component(const std::string &component);
2370std::string encode_query_component(const std::string &component,
2371 bool space_as_plus = true);
2372std::string decode_query_component(const std::string &component,
2373 bool plus_as_space = true);
2374
2375std::string append_query_params(const std::string &path, const Params &params);
2376
2377std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2378
2379std::pair<std::string, std::string>
2380 make_basic_authentication_header(const std::string &username,
2381 const std::string &password,
2382 bool is_proxy = false);
2383
2384 namespace detail {
2385
2386#if defined(_WIN32)
2387 inline std::wstring u8string_to_wstring(const char *s) {
2388 std::wstring ws;
2389 auto len = static_cast<int>(strlen(s));
2390 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2391 if (wlen > 0) {
2392 ws.resize(wlen);
2393 wlen = ::MultiByteToWideChar(
2394 CP_UTF8, 0, s, len,
2395 const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2396 if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
2397 }
2398 return ws;
2399}
2400#endif
2401
2402 struct FileStat {
2403 FileStat(const std::string &path);
2404 bool is_file() const;
2405 bool is_dir() const;
2406
2407 private:
2408#if defined(_WIN32)
2409 struct _stat st_;
2410#else
2411 struct stat st_;
2412#endif
2413 int ret_ = -1;
2414};
2415
2416std::string trim_copy(const std::string &s);
2417
2418 void divide(
2419 const char *data, std::size_t size, char d,
2420 std::function<void(const char *, std::size_t, const char *, std::size_t)>
2421 fn);
2422
2423 void divide(
2424 const std::string &str, char d,
2425 std::function<void(const char *, std::size_t, const char *, std::size_t)>
2426 fn);
2427
2428 void split(const char *b, const char *e, char d,
2429 std::function<void(const char *, const char *)> fn);
2430
2431 void split(const char *b, const char *e, char d, size_t m,
2432 std::function<void(const char *, const char *)> fn);
2433
2434 bool process_client_socket(
2435 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2436 time_t write_timeout_sec, time_t write_timeout_usec,
2437 time_t max_timeout_msec,
2438 std::chrono::time_point<std::chrono::steady_clock> start_time,
2439 std::function<bool(Stream &)> callback);
2440
2441 socket_t create_client_socket(const std::string &host, const std::string &ip,
2442 int port, int address_family, bool tcp_nodelay,
2443 bool ipv6_v6only, SocketOptions socket_options,
2444 time_t connection_timeout_sec,
2445 time_t connection_timeout_usec,
2446 time_t read_timeout_sec, time_t read_timeout_usec,
2447 time_t write_timeout_sec,
2448 time_t write_timeout_usec,
2449 const std::string &intf, Error &error);
2450
2451 const char *get_header_value(const Headers &headers, const std::string &key,
2452 const char *def, size_t id);
2453
2454std::string params_to_query_str(const Params &params);
2455
2456 void parse_query_text(const char *data, std::size_t size, Params &params);
2457
2458 void parse_query_text(const std::string &s, Params &params);
2459
2460 bool parse_multipart_boundary(const std::string &content_type,
2461 std::string &boundary);
2462
2463 bool parse_range_header(const std::string &s, Ranges &ranges);
2464
2465 bool parse_accept_header(const std::string &s,
2466 std::vector<std::string> &content_types);
2467
2468 int close_socket(socket_t sock);
2469
2470ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2471
2472ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2473
2474 enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2475
2476EncodingType encoding_type(const Request &req, const Response &res);
2477
2478 class BufferStream final : public Stream {
2479 public:
2480 BufferStream() = default;
2481 ~BufferStream() override = default;
2482
2483 bool is_readable() const override;
2484 bool wait_readable() const override;
2485 bool wait_writable() const override;
2486 ssize_t read(char *ptr, size_t size) override;
2487 ssize_t write(const char *ptr, size_t size) override;
2488 void get_remote_ip_and_port(std::string &ip, int &port) const override;
2489 void get_local_ip_and_port(std::string &ip, int &port) const override;
2490 socket_t socket() const override;
2491 time_t duration() const override;
2492
2493 const std::string &get_buffer() const;
2494
2495 private:
2496 std::string buffer;
2497 size_t position = 0;
2498};
2499
2500 class compressor {
2501 public:
2502 virtual ~compressor() = default;
2503
2504 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2505 virtual bool compress(const char *data, size_t data_length, bool last,
2506 Callback callback) = 0;
2507};
2508
2509 class decompressor {
2510 public:
2511 virtual ~decompressor() = default;
2512
2513 virtual bool is_valid() const = 0;
2514
2515 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2516 virtual bool decompress(const char *data, size_t data_length,
2517 Callback callback) = 0;
2518};
2519
2520 class nocompressor final : public compressor {
2521 public:
2522 ~nocompressor() override = default;
2523
2524 bool compress(const char *data, size_t data_length, bool /*last*/,
2525 Callback callback) override;
2526};
2527
2528#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2529 class gzip_compressor final : public compressor {
2530 public:
2531 gzip_compressor();
2532 ~gzip_compressor() override;
2533
2534 bool compress(const char *data, size_t data_length, bool last,
2535 Callback callback) override;
2536
2537 private:
2538 bool is_valid_ = false;
2539 z_stream strm_;
2540};
2541
2542 class gzip_decompressor final : public decompressor {
2543 public:
2544 gzip_decompressor();
2545 ~gzip_decompressor() override;
2546
2547 bool is_valid() const override;
2548
2549 bool decompress(const char *data, size_t data_length,
2550 Callback callback) override;
2551
2552 private:
2553 bool is_valid_ = false;
2554 z_stream strm_;
2555};
2556#endif
2557
2558#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2559 class brotli_compressor final : public compressor {
2560 public:
2561 brotli_compressor();
2562 ~brotli_compressor();
2563
2564 bool compress(const char *data, size_t data_length, bool last,
2565 Callback callback) override;
2566
2567 private:
2568 BrotliEncoderState *state_ = nullptr;
2569};
2570
2571 class brotli_decompressor final : public decompressor {
2572 public:
2573 brotli_decompressor();
2574 ~brotli_decompressor();
2575
2576 bool is_valid() const override;
2577
2578 bool decompress(const char *data, size_t data_length,
2579 Callback callback) override;
2580
2581 private:
2582 BrotliDecoderResult decoder_r;
2583 BrotliDecoderState *decoder_s = nullptr;
2584};
2585#endif
2586
2587#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2588 class zstd_compressor : public compressor {
2589 public:
2590 zstd_compressor();
2591 ~zstd_compressor();
2592
2593 bool compress(const char *data, size_t data_length, bool last,
2594 Callback callback) override;
2595
2596 private:
2597 ZSTD_CCtx *ctx_ = nullptr;
2598};
2599
2600 class zstd_decompressor : public decompressor {
2601 public:
2602 zstd_decompressor();
2603 ~zstd_decompressor();
2604
2605 bool is_valid() const override;
2606
2607 bool decompress(const char *data, size_t data_length,
2608 Callback callback) override;
2609
2610 private:
2611 ZSTD_DCtx *ctx_ = nullptr;
2612};
2613#endif
2614
2615// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2616// to store data. The call can set memory on stack for performance.
2617 class stream_line_reader {
2618 public:
2619 stream_line_reader(Stream &strm, char *fixed_buffer,
2620 size_t fixed_buffer_size);
2621 const char *ptr() const;
2622 size_t size() const;
2623 bool end_with_crlf() const;
2624 bool getline();
2625
2626 private:
2627 void append(char c);
2628
2629 Stream &strm_;
2630 char *fixed_buffer_;
2631 const size_t fixed_buffer_size_;
2632 size_t fixed_buffer_used_size_ = 0;
2633 std::string growable_buffer_;
2634};
2635
2636 class mmap {
2637 public:
2638 mmap(const char *path);
2639 ~mmap();
2640
2641 bool open(const char *path);
2642 void close();
2643
2644 bool is_open() const;
2645 size_t size() const;
2646 const char *data() const;
2647
2648 private:
2649#if defined(_WIN32)
2650 HANDLE hFile_ = NULL;
2651 HANDLE hMapping_ = NULL;
2652#else
2653 int fd_ = -1;
2654#endif
2655 size_t size_ = 0;
2656 void *addr_ = nullptr;
2657 bool is_open_empty_file = false;
2658};
2659
2660// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
2661 namespace fields {
2662
2663 inline bool is_token_char(char c) {
2664 return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
2665 c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
2666 c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
2667}
2668
2669 inline bool is_token(const std::string &s) {
2670 if (s.empty()) { return false; }
2671 for (auto c : s) {
2672 if (!is_token_char(c)) { return false; }
2673 }
2674 return true;
2675}
2676
2677 inline bool is_field_name(const std::string &s) { return is_token(s); }
2678
2679 inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
2680
2681 inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
2682
2683 inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
2684
2685 inline bool is_field_content(const std::string &s) {
2686 if (s.empty()) { return true; }
2687
2688 if (s.size() == 1) {
2689 return is_field_vchar(s[0]);
2690 } else if (s.size() == 2) {
2691 return is_field_vchar(s[0]) && is_field_vchar(s[1]);
2692 } else {
2693 size_t i = 0;
2694
2695 if (!is_field_vchar(s[i])) { return false; }
2696 i++;
2697
2698 while (i < s.size() - 1) {
2699 auto c = s[i++];
2700 if (c == ' ' || c == '\t' || is_field_vchar(c)) {
2701 } else {
2702 return false;
2703 }
2704 }
2705
2706 return is_field_vchar(s[i]);
2707 }
2708}
2709
2710 inline bool is_field_value(const std::string &s) { return is_field_content(s); }
2711
2712} // namespace fields
2713
2714} // namespace detail
2715
2716// ----------------------------------------------------------------------------
2717
2718/*
2719 * Implementation that will be part of the .cc file if split into .h + .cc.
2720 */
2721
2722 namespace detail {
2723
2724 inline bool is_hex(char c, int &v) {
2725 if (0x20 <= c && isdigit(c)) {
2726 v = c - '0';
2727 return true;
2728 } else if ('A' <= c && c <= 'F') {
2729 v = c - 'A' + 10;
2730 return true;
2731 } else if ('a' <= c && c <= 'f') {
2732 v = c - 'a' + 10;
2733 return true;
2734 }
2735 return false;
2736}
2737
2738 inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
2739 int &val) {
2740 if (i >= s.size()) { return false; }
2741
2742 val = 0;
2743 for (; cnt; i++, cnt--) {
2744 if (!s[i]) { return false; }
2745 auto v = 0;
2746 if (is_hex(s[i], v)) {
2747 val = val * 16 + v;
2748 } else {
2749 return false;
2750 }
2751 }
2752 return true;
2753}
2754
2755 inline std::string from_i_to_hex(size_t n) {
2756 static const auto charset = "0123456789abcdef";
2757 std::string ret;
2758 do {
2759 ret = charset[n & 15] + ret;
2760 n >>= 4;
2761 } while (n > 0);
2762 return ret;
2763}
2764
2765 inline size_t to_utf8(int code, char *buff) {
2766 if (code < 0x0080) {
2767 buff[0] = static_cast<char>(code & 0x7F);
2768 return 1;
2769 } else if (code < 0x0800) {
2770 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2771 buff[1] = static_cast<char>(0x80 | (code & 0x3F));
2772 return 2;
2773 } else if (code < 0xD800) {
2774 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2775 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2776 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
2777 return 3;
2778 } else if (code < 0xE000) { // D800 - DFFF is invalid...
2779 return 0;
2780 } else if (code < 0x10000) {
2781 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2782 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2783 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
2784 return 3;
2785 } else if (code < 0x110000) {
2786 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2787 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2788 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2789 buff[3] = static_cast<char>(0x80 | (code & 0x3F));
2790 return 4;
2791 }
2792
2793 // NOTREACHED
2794 return 0;
2795}
2796
2797// NOTE: This code came up with the following stackoverflow post:
2798// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
2799 inline std::string base64_encode(const std::string &in) {
2800 static const auto lookup =
2801 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2802
2803 std::string out;
2804 out.reserve(in.size());
2805
2806 auto val = 0;
2807 auto valb = -6;
2808
2809 for (auto c : in) {
2810 val = (val << 8) + static_cast<uint8_t>(c);
2811 valb += 8;
2812 while (valb >= 0) {
2813 out.push_back(lookup[(val >> valb) & 0x3F]);
2814 valb -= 6;
2815 }
2816 }
2817
2818 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
2819
2820 while (out.size() % 4) {
2821 out.push_back('=');
2822 }
2823
2824 return out;
2825}
2826
2827 inline bool is_valid_path(const std::string &path) {
2828 size_t level = 0;
2829 size_t i = 0;
2830
2831 // Skip slash
2832 while (i < path.size() && path[i] == '/') {
2833 i++;
2834 }
2835
2836 while (i < path.size()) {
2837 // Read component
2838 auto beg = i;
2839 while (i < path.size() && path[i] != '/') {
2840 if (path[i] == '\0') {
2841 return false;
2842 } else if (path[i] == '\\') {
2843 return false;
2844 }
2845 i++;
2846 }
2847
2848 auto len = i - beg;
2849 assert(len > 0);
2850
2851 if (!path.compare(beg, len, ".")) {
2852 ;
2853 } else if (!path.compare(beg, len, "..")) {
2854 if (level == 0) { return false; }
2855 level--;
2856 } else {
2857 level++;
2858 }
2859
2860 // Skip slash
2861 while (i < path.size() && path[i] == '/') {
2862 i++;
2863 }
2864 }
2865
2866 return true;
2867}
2868
2869 inline FileStat::FileStat(const std::string &path) {
2870#if defined(_WIN32)
2871 auto wpath = u8string_to_wstring(path.c_str());
2872 ret_ = _wstat(wpath.c_str(), &st_);
2873#else
2874 ret_ = stat(path.c_str(), &st_);
2875#endif
2876}
2877 inline bool FileStat::is_file() const {
2878 return ret_ >= 0 && S_ISREG(st_.st_mode);
2879}
2880 inline bool FileStat::is_dir() const {
2881 return ret_ >= 0 && S_ISDIR(st_.st_mode);
2882}
2883
2884 inline std::string encode_path(const std::string &s) {
2885 std::string result;
2886 result.reserve(s.size());
2887
2888 for (size_t i = 0; s[i]; i++) {
2889 switch (s[i]) {
2890 case ' ': result += "%20"; break;
2891 case '+': result += "%2B"; break;
2892 case '\r': result += "%0D"; break;
2893 case '\n': result += "%0A"; break;
2894 case '\'': result += "%27"; break;
2895 case ',': result += "%2C"; break;
2896 // case ':': result += "%3A"; break; // ok? probably...
2897 case ';': result += "%3B"; break;
2898 default:
2899 auto c = static_cast<uint8_t>(s[i]);
2900 if (c >= 0x80) {
2901 result += '%';
2902 char hex[4];
2903 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
2904 assert(len == 2);
2905 result.append(hex, static_cast<size_t>(len));
2906 } else {
2907 result += s[i];
2908 }
2909 break;
2910 }
2911 }
2912
2913 return result;
2914}
2915
2916 inline std::string file_extension(const std::string &path) {
2917 std::smatch m;
2918 thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
2919 if (std::regex_search(path, m, re)) { return m[1].str(); }
2920 return std::string();
2921}
2922
2923 inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
2924
2925 inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
2926 size_t right) {
2927 while (b + left < e && is_space_or_tab(b[left])) {
2928 left++;
2929 }
2930 while (right > 0 && is_space_or_tab(b[right - 1])) {
2931 right--;
2932 }
2933 return std::make_pair(left, right);
2934}
2935
2936 inline std::string trim_copy(const std::string &s) {
2937 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2938 return s.substr(r.first, r.second - r.first);
2939}
2940
2941 inline std::string trim_double_quotes_copy(const std::string &s) {
2942 if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
2943 return s.substr(1, s.size() - 2);
2944 }
2945 return s;
2946}
2947
2948 inline void
2949divide(const char *data, std::size_t size, char d,
2950 std::function<void(const char *, std::size_t, const char *, std::size_t)>
2951 fn) {
2952 const auto it = std::find(data, data + size, d);
2953 const auto found = static_cast<std::size_t>(it != data + size);
2954 const auto lhs_data = data;
2955 const auto lhs_size = static_cast<std::size_t>(it - data);
2956 const auto rhs_data = it + found;
2957 const auto rhs_size = size - lhs_size - found;
2958
2959 fn(lhs_data, lhs_size, rhs_data, rhs_size);
2960}
2961
2962 inline void
2963divide(const std::string &str, char d,
2964 std::function<void(const char *, std::size_t, const char *, std::size_t)>
2965 fn) {
2966 divide(str.data(), str.size(), d, std::move(fn));
2967}
2968
2969 inline void split(const char *b, const char *e, char d,
2970 std::function<void(const char *, const char *)> fn) {
2971 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
2972}
2973
2974 inline void split(const char *b, const char *e, char d, size_t m,
2975 std::function<void(const char *, const char *)> fn) {
2976 size_t i = 0;
2977 size_t beg = 0;
2978 size_t count = 1;
2979
2980 while (e ? (b + i < e) : (b[i] != '\0')) {
2981 if (b[i] == d && count < m) {
2982 auto r = trim(b, e, beg, i);
2983 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2984 beg = i + 1;
2985 count++;
2986 }
2987 i++;
2988 }
2989
2990 if (i) {
2991 auto r = trim(b, e, beg, i);
2992 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2993 }
2994}
2995
2996 inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
2997 size_t fixed_buffer_size)
2998 : strm_(strm), fixed_buffer_(fixed_buffer),
2999 fixed_buffer_size_(fixed_buffer_size) {}
3000
3001 inline const char *stream_line_reader::ptr() const {
3002 if (growable_buffer_.empty()) {
3003 return fixed_buffer_;
3004 } else {
3005 return growable_buffer_.data();
3006 }
3007}
3008
3009 inline size_t stream_line_reader::size() const {
3010 if (growable_buffer_.empty()) {
3011 return fixed_buffer_used_size_;
3012 } else {
3013 return growable_buffer_.size();
3014 }
3015}
3016
3017 inline bool stream_line_reader::end_with_crlf() const {
3018 auto end = ptr() + size();
3019 return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
3020}
3021
3022 inline bool stream_line_reader::getline() {
3023 fixed_buffer_used_size_ = 0;
3024 growable_buffer_.clear();
3025
3026#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3027 char prev_byte = 0;
3028#endif
3029
3030 for (size_t i = 0;; i++) {
3031 if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
3032 // Treat exceptionally long lines as an error to
3033 // prevent infinite loops/memory exhaustion
3034 return false;
3035 }
3036 char byte;
3037 auto n = strm_.read(&byte, 1);
3038
3039 if (n < 0) {
3040 return false;
3041 } else if (n == 0) {
3042 if (i == 0) {
3043 return false;
3044 } else {
3045 break;
3046 }
3047 }
3048
3049 append(byte);
3050
3051#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3052 if (byte == '\n') { break; }
3053#else
3054 if (prev_byte == '\r' && byte == '\n') { break; }
3055 prev_byte = byte;
3056#endif
3057 }
3058
3059 return true;
3060}
3061
3062 inline void stream_line_reader::append(char c) {
3063 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
3064 fixed_buffer_[fixed_buffer_used_size_++] = c;
3065 fixed_buffer_[fixed_buffer_used_size_] = '\0';
3066 } else {
3067 if (growable_buffer_.empty()) {
3068 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
3069 growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
3070 }
3071 growable_buffer_ += c;
3072 }
3073}
3074
3075 inline mmap::mmap(const char *path) { open(path); }
3076
3077 inline mmap::~mmap() { close(); }
3078
3079 inline bool mmap::open(const char *path) {
3080 close();
3081
3082#if defined(_WIN32)
3083 auto wpath = u8string_to_wstring(path);
3084 if (wpath.empty()) { return false; }
3085
3086 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
3087 OPEN_EXISTING, NULL);
3088
3089 if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
3090
3091 LARGE_INTEGER size{};
3092 if (!::GetFileSizeEx(hFile_, &size)) { return false; }
3093 // If the following line doesn't compile due to QuadPart, update Windows SDK.
3094 // See:
3095 // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
3096 if (static_cast<ULONGLONG>(size.QuadPart) >
3097 (std::numeric_limits<decltype(size_)>::max)()) {
3098 // `size_t` might be 32-bits, on 32-bits Windows.
3099 return false;
3100 }
3101 size_ = static_cast<size_t>(size.QuadPart);
3102
3103 hMapping_ =
3104 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
3105
3106 // Special treatment for an empty file...
3107 if (hMapping_ == NULL && size_ == 0) {
3108 close();
3109 is_open_empty_file = true;
3110 return true;
3111 }
3112
3113 if (hMapping_ == NULL) {
3114 close();
3115 return false;
3116 }
3117
3118 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
3119
3120 if (addr_ == nullptr) {
3121 close();
3122 return false;
3123 }
3124#else
3125 fd_ = ::open(path, O_RDONLY);
3126 if (fd_ == -1) { return false; }
3127
3128 struct stat sb;
3129 if (fstat(fd_, &sb) == -1) {
3130 close();
3131 return false;
3132 }
3133 size_ = static_cast<size_t>(sb.st_size);
3134
3135 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
3136
3137 // Special treatment for an empty file...
3138 if (addr_ == MAP_FAILED && size_ == 0) {
3139 close();
3140 is_open_empty_file = true;
3141 return false;
3142 }
3143#endif
3144
3145 return true;
3146}
3147
3148 inline bool mmap::is_open() const {
3149 return is_open_empty_file ? true : addr_ != nullptr;
3150}
3151
3152 inline size_t mmap::size() const { return size_; }
3153
3154 inline const char *mmap::data() const {
3155 return is_open_empty_file ? "" : static_cast<const char *>(addr_);
3156}
3157
3158 inline void mmap::close() {
3159#if defined(_WIN32)
3160 if (addr_) {
3161 ::UnmapViewOfFile(addr_);
3162 addr_ = nullptr;
3163 }
3164
3165 if (hMapping_) {
3166 ::CloseHandle(hMapping_);
3167 hMapping_ = NULL;
3168 }
3169
3170 if (hFile_ != INVALID_HANDLE_VALUE) {
3171 ::CloseHandle(hFile_);
3172 hFile_ = INVALID_HANDLE_VALUE;
3173 }
3174
3175 is_open_empty_file = false;
3176#else
3177 if (addr_ != nullptr) {
3178 munmap(addr_, size_);
3179 addr_ = nullptr;
3180 }
3181
3182 if (fd_ != -1) {
3183 ::close(fd_);
3184 fd_ = -1;
3185 }
3186#endif
3187 size_ = 0;
3188}
3189 inline int close_socket(socket_t sock) {
3190#ifdef _WIN32
3191 return closesocket(sock);
3192#else
3193 return close(sock);
3194#endif
3195}
3196
3197 template <typename T> inline ssize_t handle_EINTR(T fn) {
3198 ssize_t res = 0;
3199 while (true) {
3200 res = fn();
3201 if (res < 0 && errno == EINTR) {
3202 std::this_thread::sleep_for(std::chrono::microseconds{1});
3203 continue;
3204 }
3205 break;
3206 }
3207 return res;
3208}
3209
3210 inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
3211 return handle_EINTR([&]() {
3212 return recv(sock,
3213#ifdef _WIN32
3214 static_cast<char *>(ptr), static_cast<int>(size),
3215#else
3216 ptr, size,
3217#endif
3218 flags);
3219 });
3220}
3221
3222 inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
3223 int flags) {
3224 return handle_EINTR([&]() {
3225 return send(sock,
3226#ifdef _WIN32
3227 static_cast<const char *>(ptr), static_cast<int>(size),
3228#else
3229 ptr, size,
3230#endif
3231 flags);
3232 });
3233}
3234
3235 inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
3236#ifdef _WIN32
3237 return ::WSAPoll(fds, nfds, timeout);
3238#else
3239 return ::poll(fds, nfds, timeout);
3240#endif
3241}
3242
3243 template <bool Read>
3244 inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
3245#ifdef __APPLE__
3246 if (sock >= FD_SETSIZE) { return -1; }
3247
3248 fd_set fds, *rfds, *wfds;
3249 FD_ZERO(&fds);
3250 FD_SET(sock, &fds);
3251 rfds = (Read ? &fds : nullptr);
3252 wfds = (Read ? nullptr : &fds);
3253
3254 timeval tv;
3255 tv.tv_sec = static_cast<long>(sec);
3256 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3257
3258 return handle_EINTR([&]() {
3259 return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
3260 });
3261#else
3262 struct pollfd pfd;
3263 pfd.fd = sock;
3264 pfd.events = (Read ? POLLIN : POLLOUT);
3265
3266 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3267
3268 return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
3269#endif
3270}
3271
3272 inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
3273 return select_impl<true>(sock, sec, usec);
3274}
3275
3276 inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
3277 return select_impl<false>(sock, sec, usec);
3278}
3279
3280 inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
3281 time_t usec) {
3282#ifdef __APPLE__
3283 if (sock >= FD_SETSIZE) { return Error::Connection; }
3284
3285 fd_set fdsr, fdsw;
3286 FD_ZERO(&fdsr);
3287 FD_ZERO(&fdsw);
3288 FD_SET(sock, &fdsr);
3289 FD_SET(sock, &fdsw);
3290
3291 timeval tv;
3292 tv.tv_sec = static_cast<long>(sec);
3293 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3294
3295 auto ret = handle_EINTR([&]() {
3296 return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
3297 });
3298
3299 if (ret == 0) { return Error::ConnectionTimeout; }
3300
3301 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3302 auto error = 0;
3303 socklen_t len = sizeof(error);
3304 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3305 reinterpret_cast<char *>(&error), &len);
3306 auto successful = res >= 0 && !error;
3307 return successful ? Error::Success : Error::Connection;
3308 }
3309
3310 return Error::Connection;
3311#else
3312 struct pollfd pfd_read;
3313 pfd_read.fd = sock;
3314 pfd_read.events = POLLIN | POLLOUT;
3315
3316 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3317
3318 auto poll_res =
3319 handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
3320
3321 if (poll_res == 0) { return Error::ConnectionTimeout; }
3322
3323 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
3324 auto error = 0;
3325 socklen_t len = sizeof(error);
3326 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3327 reinterpret_cast<char *>(&error), &len);
3328 auto successful = res >= 0 && !error;
3329 return successful ? Error::Success : Error::Connection;
3330 }
3331
3332 return Error::Connection;
3333#endif
3334}
3335
3336 inline bool is_socket_alive(socket_t sock) {
3337 const auto val = detail::select_read(sock, 0, 0);
3338 if (val == 0) {
3339 return true;
3340 } else if (val < 0 && errno == EBADF) {
3341 return false;
3342 }
3343 char buf[1];
3344 return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
3345}
3346
3347 class SocketStream final : public Stream {
3348 public:
3349 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3350 time_t write_timeout_sec, time_t write_timeout_usec,
3351 time_t max_timeout_msec = 0,
3352 std::chrono::time_point<std::chrono::steady_clock> start_time =
3353 (std::chrono::steady_clock::time_point::min)());
3354 ~SocketStream() override;
3355
3356 bool is_readable() const override;
3357 bool wait_readable() const override;
3358 bool wait_writable() const override;
3359 ssize_t read(char *ptr, size_t size) override;
3360 ssize_t write(const char *ptr, size_t size) override;
3361 void get_remote_ip_and_port(std::string &ip, int &port) const override;
3362 void get_local_ip_and_port(std::string &ip, int &port) const override;
3363 socket_t socket() const override;
3364 time_t duration() const override;
3365
3366 private:
3367 socket_t sock_;
3368 time_t read_timeout_sec_;
3369 time_t read_timeout_usec_;
3370 time_t write_timeout_sec_;
3371 time_t write_timeout_usec_;
3372 time_t max_timeout_msec_;
3373 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
3374
3375 std::vector<char> read_buff_;
3376 size_t read_buff_off_ = 0;
3377 size_t read_buff_content_size_ = 0;
3378
3379 static const size_t read_buff_size_ = 1024l * 4;
3380};
3381
3382#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3383 class SSLSocketStream final : public Stream {
3384 public:
3385 SSLSocketStream(
3386 socket_t sock, SSL *ssl, time_t read_timeout_sec,
3387 time_t read_timeout_usec, time_t write_timeout_sec,
3388 time_t write_timeout_usec, time_t max_timeout_msec = 0,
3389 std::chrono::time_point<std::chrono::steady_clock> start_time =
3390 (std::chrono::steady_clock::time_point::min)());
3391 ~SSLSocketStream() override;
3392
3393 bool is_readable() const override;
3394 bool wait_readable() const override;
3395 bool wait_writable() const override;
3396 ssize_t read(char *ptr, size_t size) override;
3397 ssize_t write(const char *ptr, size_t size) override;
3398 void get_remote_ip_and_port(std::string &ip, int &port) const override;
3399 void get_local_ip_and_port(std::string &ip, int &port) const override;
3400 socket_t socket() const override;
3401 time_t duration() const override;
3402
3403 private:
3404 socket_t sock_;
3405 SSL *ssl_;
3406 time_t read_timeout_sec_;
3407 time_t read_timeout_usec_;
3408 time_t write_timeout_sec_;
3409 time_t write_timeout_usec_;
3410 time_t max_timeout_msec_;
3411 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
3412};
3413#endif
3414
3415 inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
3416 time_t keep_alive_timeout_sec) {
3417 using namespace std::chrono;
3418
3419 const auto interval_usec =
3420 CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
3421
3422 // Avoid expensive `steady_clock::now()` call for the first time
3423 if (select_read(sock, 0, interval_usec) > 0) { return true; }
3424
3425 const auto start = steady_clock::now() - microseconds{interval_usec};
3426 const auto timeout = seconds{keep_alive_timeout_sec};
3427
3428 while (true) {
3429 if (svr_sock == INVALID_SOCKET) {
3430 break; // Server socket is closed
3431 }
3432
3433 auto val = select_read(sock, 0, interval_usec);
3434 if (val < 0) {
3435 break; // Ssocket error
3436 } else if (val == 0) {
3437 if (steady_clock::now() - start > timeout) {
3438 break; // Timeout
3439 }
3440 } else {
3441 return true; // Ready for read
3442 }
3443 }
3444
3445 return false;
3446}
3447
3448 template <typename T>
3449 inline bool
3450process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
3451 size_t keep_alive_max_count,
3452 time_t keep_alive_timeout_sec, T callback) {
3453 assert(keep_alive_max_count > 0);
3454 auto ret = false;
3455 auto count = keep_alive_max_count;
3456 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3457 auto close_connection = count == 1;
3458 auto connection_closed = false;
3459 ret = callback(close_connection, connection_closed);
3460 if (!ret || connection_closed) { break; }
3461 count--;
3462 }
3463 return ret;
3464}
3465
3466 template <typename T>
3467 inline bool
3468process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
3469 size_t keep_alive_max_count,
3470 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
3471 time_t read_timeout_usec, time_t write_timeout_sec,
3472 time_t write_timeout_usec, T callback) {
3473 return process_server_socket_core(
3474 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3475 [&](bool close_connection, bool &connection_closed) {
3476 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3477 write_timeout_sec, write_timeout_usec);
3478 return callback(strm, close_connection, connection_closed);
3479 });
3480}
3481
3482 inline bool process_client_socket(
3483 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3484 time_t write_timeout_sec, time_t write_timeout_usec,
3485 time_t max_timeout_msec,
3486 std::chrono::time_point<std::chrono::steady_clock> start_time,
3487 std::function<bool(Stream &)> callback) {
3488 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3489 write_timeout_sec, write_timeout_usec, max_timeout_msec,
3490 start_time);
3491 return callback(strm);
3492}
3493
3494 inline int shutdown_socket(socket_t sock) {
3495#ifdef _WIN32
3496 return shutdown(sock, SD_BOTH);
3497#else
3498 return shutdown(sock, SHUT_RDWR);
3499#endif
3500}
3501
3502 inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
3503 if (s.size() > 1 && s[0] == '\0') {
3504 auto ret = s;
3505 ret[0] = '@';
3506 return ret;
3507 }
3508 return s;
3509}
3510
3511 inline std::string
3512unescape_abstract_namespace_unix_domain(const std::string &s) {
3513 if (s.size() > 1 && s[0] == '@') {
3514 auto ret = s;
3515 ret[0] = '\0';
3516 return ret;
3517 }
3518 return s;
3519}
3520
3521 inline int getaddrinfo_with_timeout(const char *node, const char *service,
3522 const struct addrinfo *hints,
3523 struct addrinfo **res, time_t timeout_sec) {
3524#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
3525 if (timeout_sec <= 0) {
3526 // No timeout specified, use standard getaddrinfo
3527 return getaddrinfo(node, service, hints, res);
3528 }
3529
3530#ifdef _WIN32
3531 // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
3532 OVERLAPPED overlapped = {0};
3533 HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
3534 if (!event) { return EAI_FAIL; }
3535
3536 overlapped.hEvent = event;
3537
3538 PADDRINFOEXW result_addrinfo = nullptr;
3539 HANDLE cancel_handle = nullptr;
3540
3541 ADDRINFOEXW hints_ex = {0};
3542 if (hints) {
3543 hints_ex.ai_flags = hints->ai_flags;
3544 hints_ex.ai_family = hints->ai_family;
3545 hints_ex.ai_socktype = hints->ai_socktype;
3546 hints_ex.ai_protocol = hints->ai_protocol;
3547 }
3548
3549 auto wnode = u8string_to_wstring(node);
3550 auto wservice = u8string_to_wstring(service);
3551
3552 auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
3553 hints ? &hints_ex : nullptr, &result_addrinfo,
3554 nullptr, &overlapped, nullptr, &cancel_handle);
3555
3556 if (ret == WSA_IO_PENDING) {
3557 auto wait_result =
3558 ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
3559 if (wait_result == WAIT_TIMEOUT) {
3560 if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
3561 ::CloseHandle(event);
3562 return EAI_AGAIN;
3563 }
3564
3565 DWORD bytes_returned;
3566 if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
3567 &bytes_returned, FALSE)) {
3568 ::CloseHandle(event);
3569 return ::WSAGetLastError();
3570 }
3571 }
3572
3573 ::CloseHandle(event);
3574
3575 if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
3576 *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
3577 return 0;
3578 }
3579
3580 return ret;
3581#elif TARGET_OS_MAC
3582 // macOS implementation using CFHost API for asynchronous DNS resolution
3583 CFStringRef hostname_ref = CFStringCreateWithCString(
3584 kCFAllocatorDefault, node, kCFStringEncodingUTF8);
3585 if (!hostname_ref) { return EAI_MEMORY; }
3586
3587 CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
3588 CFRelease(hostname_ref);
3589 if (!host_ref) { return EAI_MEMORY; }
3590
3591 // Set up context for callback
3592 struct CFHostContext {
3593 bool completed = false;
3594 bool success = false;
3595 CFArrayRef addresses = nullptr;
3596 std::mutex mutex;
3597 std::condition_variable cv;
3598 } context;
3599
3600 CFHostClientContext client_context;
3601 memset(&client_context, 0, sizeof(client_context));
3602 client_context.info = &context;
3603
3604 // Set callback
3605 auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
3606 const CFStreamError *error, void *info) {
3607 auto ctx = static_cast<CFHostContext *>(info);
3608 std::lock_guard<std::mutex> lock(ctx->mutex);
3609
3610 if (error && error->error != 0) {
3611 ctx->success = false;
3612 } else {
3613 Boolean hasBeenResolved;
3614 ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
3615 if (ctx->addresses && hasBeenResolved) {
3616 CFRetain(ctx->addresses);
3617 ctx->success = true;
3618 } else {
3619 ctx->success = false;
3620 }
3621 }
3622 ctx->completed = true;
3623 ctx->cv.notify_one();
3624 };
3625
3626 if (!CFHostSetClient(host_ref, callback, &client_context)) {
3627 CFRelease(host_ref);
3628 return EAI_SYSTEM;
3629 }
3630
3631 // Schedule on run loop
3632 CFRunLoopRef run_loop = CFRunLoopGetCurrent();
3633 CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
3634
3635 // Start resolution
3636 CFStreamError stream_error;
3637 if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
3638 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
3639 CFRelease(host_ref);
3640 return EAI_FAIL;
3641 }
3642
3643 // Wait for completion with timeout
3644 auto timeout_time =
3645 std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
3646 bool timed_out = false;
3647
3648 {
3649 std::unique_lock<std::mutex> lock(context.mutex);
3650
3651 while (!context.completed) {
3652 auto now = std::chrono::steady_clock::now();
3653 if (now >= timeout_time) {
3654 timed_out = true;
3655 break;
3656 }
3657
3658 // Run the runloop for a short time
3659 lock.unlock();
3660 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
3661 lock.lock();
3662 }
3663 }
3664
3665 // Clean up
3666 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
3667 CFHostSetClient(host_ref, nullptr, nullptr);
3668
3669 if (timed_out || !context.completed) {
3670 CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
3671 CFRelease(host_ref);
3672 return EAI_AGAIN;
3673 }
3674
3675 if (!context.success || !context.addresses) {
3676 CFRelease(host_ref);
3677 return EAI_NODATA;
3678 }
3679
3680 // Convert CFArray to addrinfo
3681 CFIndex count = CFArrayGetCount(context.addresses);
3682 if (count == 0) {
3683 CFRelease(context.addresses);
3684 CFRelease(host_ref);
3685 return EAI_NODATA;
3686 }
3687
3688 struct addrinfo *result_addrinfo = nullptr;
3689 struct addrinfo **current = &result_addrinfo;
3690
3691 for (CFIndex i = 0; i < count; i++) {
3692 CFDataRef addr_data =
3693 static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
3694 if (!addr_data) continue;
3695
3696 const struct sockaddr *sockaddr_ptr =
3697 reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
3698 socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
3699
3700 // Allocate addrinfo structure
3701 *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
3702 if (!*current) {
3703 freeaddrinfo(result_addrinfo);
3704 CFRelease(context.addresses);
3705 CFRelease(host_ref);
3706 return EAI_MEMORY;
3707 }
3708
3709 memset(*current, 0, sizeof(struct addrinfo));
3710
3711 // Set up addrinfo fields
3712 (*current)->ai_family = sockaddr_ptr->sa_family;
3713 (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
3714 (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
3715 (*current)->ai_addrlen = sockaddr_len;
3716
3717 // Copy sockaddr
3718 (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
3719 if (!(*current)->ai_addr) {
3720 freeaddrinfo(result_addrinfo);
3721 CFRelease(context.addresses);
3722 CFRelease(host_ref);
3723 return EAI_MEMORY;
3724 }
3725 memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
3726
3727 // Set port if service is specified
3728 if (service && strlen(service) > 0) {
3729 int port = atoi(service);
3730 if (port > 0) {
3731 if (sockaddr_ptr->sa_family == AF_INET) {
3732 reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
3733 ->sin_port = htons(static_cast<uint16_t>(port));
3734 } else if (sockaddr_ptr->sa_family == AF_INET6) {
3735 reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
3736 ->sin6_port = htons(static_cast<uint16_t>(port));
3737 }
3738 }
3739 }
3740
3741 current = &((*current)->ai_next);
3742 }
3743
3744 CFRelease(context.addresses);
3745 CFRelease(host_ref);
3746
3747 *res = result_addrinfo;
3748 return 0;
3749#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
3750 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
3751 // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
3752 struct gaicb request;
3753 struct gaicb *requests[1] = {&request};
3754 struct sigevent sevp;
3755 struct timespec timeout;
3756
3757 // Initialize the request structure
3758 memset(&request, 0, sizeof(request));
3759 request.ar_name = node;
3760 request.ar_service = service;
3761 request.ar_request = hints;
3762
3763 // Set up timeout
3764 timeout.tv_sec = timeout_sec;
3765 timeout.tv_nsec = 0;
3766
3767 // Initialize sigevent structure (not used, but required)
3768 memset(&sevp, 0, sizeof(sevp));
3769 sevp.sigev_notify = SIGEV_NONE;
3770
3771 // Start asynchronous resolution
3772 int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
3773 if (start_result != 0) { return start_result; }
3774
3775 // Wait for completion with timeout
3776 int wait_result =
3777 gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
3778
3779 if (wait_result == 0 || wait_result == EAI_ALLDONE) {
3780 // Completed successfully, get the result
3781 int gai_result = gai_error(&request);
3782 if (gai_result == 0) {
3783 *res = request.ar_result;
3784 return 0;
3785 } else {
3786 // Clean up on error
3787 if (request.ar_result) { freeaddrinfo(request.ar_result); }
3788 return gai_result;
3789 }
3790 } else if (wait_result == EAI_AGAIN) {
3791 // Timeout occurred, cancel the request
3792 gai_cancel(&request);
3793 return EAI_AGAIN;
3794 } else {
3795 // Other error occurred
3796 gai_cancel(&request);
3797 return wait_result;
3798 }
3799#else
3800 // Fallback implementation using thread-based timeout for other Unix systems
3801 std::mutex result_mutex;
3802 std::condition_variable result_cv;
3803 auto completed = false;
3804 auto result = EAI_SYSTEM;
3805 struct addrinfo *result_addrinfo = nullptr;
3806
3807 std::thread resolve_thread([&]() {
3808 auto thread_result = getaddrinfo(node, service, hints, &result_addrinfo);
3809
3810 std::lock_guard<std::mutex> lock(result_mutex);
3811 result = thread_result;
3812 completed = true;
3813 result_cv.notify_one();
3814 });
3815
3816 // Wait for completion or timeout
3817 std::unique_lock<std::mutex> lock(result_mutex);
3818 auto finished = result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
3819 [&] { return completed; });
3820
3821 if (finished) {
3822 // Operation completed within timeout
3823 resolve_thread.join();
3824 *res = result_addrinfo;
3825 return result;
3826 } else {
3827 // Timeout occurred
3828 resolve_thread.detach(); // Let the thread finish in background
3829 return EAI_AGAIN; // Return timeout error
3830 }
3831#endif
3832#else
3833 (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
3834 return getaddrinfo(node, service, hints, res);
3835#endif
3836}
3837
3838 template <typename BindOrConnect>
3839 socket_t create_socket(const std::string &host, const std::string &ip, int port,
3840 int address_family, int socket_flags, bool tcp_nodelay,
3841 bool ipv6_v6only, SocketOptions socket_options,
3842 BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
3843 // Get address info
3844 const char *node = nullptr;
3845 struct addrinfo hints;
3846 struct addrinfo *result;
3847
3848 memset(&hints, 0, sizeof(struct addrinfo));
3849 hints.ai_socktype = SOCK_STREAM;
3850 hints.ai_protocol = IPPROTO_IP;
3851
3852 if (!ip.empty()) {
3853 node = ip.c_str();
3854 // Ask getaddrinfo to convert IP in c-string to address
3855 hints.ai_family = AF_UNSPEC;
3856 hints.ai_flags = AI_NUMERICHOST;
3857 } else {
3858 if (!host.empty()) { node = host.c_str(); }
3859 hints.ai_family = address_family;
3860 hints.ai_flags = socket_flags;
3861 }
3862
3863#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
3864 if (hints.ai_family == AF_UNIX) {
3865 const auto addrlen = host.length();
3866 if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
3867
3868#ifdef SOCK_CLOEXEC
3869 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
3870 hints.ai_protocol);
3871#else
3872 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3873#endif
3874
3875 if (sock != INVALID_SOCKET) {
3876 sockaddr_un addr{};
3877 addr.sun_family = AF_UNIX;
3878
3879 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
3880 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3881
3882 hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
3883 hints.ai_addrlen = static_cast<socklen_t>(
3884 sizeof(addr) - sizeof(addr.sun_path) + addrlen);
3885
3886#ifndef SOCK_CLOEXEC
3887#ifndef _WIN32
3888 fcntl(sock, F_SETFD, FD_CLOEXEC);
3889#endif
3890#endif
3891
3892 if (socket_options) { socket_options(sock); }
3893
3894#ifdef _WIN32
3895 // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
3896 // remove the option.
3897 detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
3898#endif
3899
3900 bool dummy;
3901 if (!bind_or_connect(sock, hints, dummy)) {
3902 close_socket(sock);
3903 sock = INVALID_SOCKET;
3904 }
3905 }
3906 return sock;
3907 }
3908#endif
3909
3910 auto service = std::to_string(port);
3911
3912 if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
3913 timeout_sec)) {
3914#if defined __linux__ && !defined __ANDROID__
3915 res_init();
3916#endif
3917 return INVALID_SOCKET;
3918 }
3919 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3920
3921 for (auto rp = result; rp; rp = rp->ai_next) {
3922 // Create a socket
3923#ifdef _WIN32
3924 auto sock =
3925 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
3926 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3941 if (sock == INVALID_SOCKET) {
3942 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3943 }
3944#else
3945
3946#ifdef SOCK_CLOEXEC
3947 auto sock =
3948 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3949#else
3950 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3951#endif
3952
3953#endif
3954 if (sock == INVALID_SOCKET) { continue; }
3955
3956#if !defined _WIN32 && !defined SOCK_CLOEXEC
3957 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3958 close_socket(sock);
3959 continue;
3960 }
3961#endif
3962
3963 if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
3964
3965 if (rp->ai_family == AF_INET6) {
3966 set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
3967 }
3968
3969 if (socket_options) { socket_options(sock); }
3970
3971 // bind or connect
3972 auto quit = false;
3973 if (bind_or_connect(sock, *rp, quit)) { return sock; }
3974
3975 close_socket(sock);
3976
3977 if (quit) { break; }
3978 }
3979
3980 return INVALID_SOCKET;
3981}
3982
3983 inline void set_nonblocking(socket_t sock, bool nonblocking) {
3984#ifdef _WIN32
3985 auto flags = nonblocking ? 1UL : 0UL;
3986 ioctlsocket(sock, FIONBIO, &flags);
3987#else
3988 auto flags = fcntl(sock, F_GETFL, 0);
3989 fcntl(sock, F_SETFL,
3990 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3991#endif
3992}
3993
3994 inline bool is_connection_error() {
3995#ifdef _WIN32
3996 return WSAGetLastError() != WSAEWOULDBLOCK;
3997#else
3998 return errno != EINPROGRESS;
3999#endif
4000}
4001
4002 inline bool bind_ip_address(socket_t sock, const std::string &host) {
4003 struct addrinfo hints;
4004 struct addrinfo *result;
4005
4006 memset(&hints, 0, sizeof(struct addrinfo));
4007 hints.ai_family = AF_UNSPEC;
4008 hints.ai_socktype = SOCK_STREAM;
4009 hints.ai_protocol = 0;
4010
4011 if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
4012 return false;
4013 }
4014
4015 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
4016
4017 auto ret = false;
4018 for (auto rp = result; rp; rp = rp->ai_next) {
4019 const auto &ai = *rp;
4020 if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
4021 ret = true;
4022 break;
4023 }
4024 }
4025
4026 return ret;
4027}
4028
4029#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
4030#define USE_IF2IP
4031#endif
4032
4033#ifdef USE_IF2IP
4034 inline std::string if2ip(int address_family, const std::string &ifn) {
4035 struct ifaddrs *ifap;
4036 getifaddrs(&ifap);
4037 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
4038
4039 std::string addr_candidate;
4040 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
4041 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
4042 (AF_UNSPEC == address_family ||
4043 ifa->ifa_addr->sa_family == address_family)) {
4044 if (ifa->ifa_addr->sa_family == AF_INET) {
4045 auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
4046 char buf[INET_ADDRSTRLEN];
4047 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
4048 return std::string(buf, INET_ADDRSTRLEN);
4049 }
4050 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
4051 auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
4052 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
4053 char buf[INET6_ADDRSTRLEN] = {};
4054 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
4055 // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
4056 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
4057 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
4058 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
4059 } else {
4060 return std::string(buf, INET6_ADDRSTRLEN);
4061 }
4062 }
4063 }
4064 }
4065 }
4066 }
4067 return addr_candidate;
4068}
4069#endif
4070
4071 inline socket_t create_client_socket(
4072 const std::string &host, const std::string &ip, int port,
4073 int address_family, bool tcp_nodelay, bool ipv6_v6only,
4074 SocketOptions socket_options, time_t connection_timeout_sec,
4075 time_t connection_timeout_usec, time_t read_timeout_sec,
4076 time_t read_timeout_usec, time_t write_timeout_sec,
4077 time_t write_timeout_usec, const std::string &intf, Error &error) {
4078 auto sock = create_socket(
4079 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
4080 std::move(socket_options),
4081 [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
4082 if (!intf.empty()) {
4083#ifdef USE_IF2IP
4084 auto ip_from_if = if2ip(address_family, intf);
4085 if (ip_from_if.empty()) { ip_from_if = intf; }
4086 if (!bind_ip_address(sock2, ip_from_if)) {
4087 error = Error::BindIPAddress;
4088 return false;
4089 }
4090#endif
4091 }
4092
4093 set_nonblocking(sock2, true);
4094
4095 auto ret =
4096 ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
4097
4098 if (ret < 0) {
4099 if (is_connection_error()) {
4100 error = Error::Connection;
4101 return false;
4102 }
4103 error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
4104 connection_timeout_usec);
4105 if (error != Error::Success) {
4106 if (error == Error::ConnectionTimeout) { quit = true; }
4107 return false;
4108 }
4109 }
4110
4111 set_nonblocking(sock2, false);
4112 set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
4113 read_timeout_usec);
4114 set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
4115 write_timeout_usec);
4116
4117 error = Error::Success;
4118 return true;
4119 },
4120 connection_timeout_sec); // Pass DNS timeout
4121
4122 if (sock != INVALID_SOCKET) {
4123 error = Error::Success;
4124 } else {
4125 if (error == Error::Success) { error = Error::Connection; }
4126 }
4127
4128 return sock;
4129}
4130
4131 inline bool get_ip_and_port(const struct sockaddr_storage &addr,
4132 socklen_t addr_len, std::string &ip, int &port) {
4133 if (addr.ss_family == AF_INET) {
4134 port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
4135 } else if (addr.ss_family == AF_INET6) {
4136 port =
4137 ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
4138 } else {
4139 return false;
4140 }
4141
4142 std::array<char, NI_MAXHOST> ipstr{};
4143 if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
4144 ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
4145 0, NI_NUMERICHOST)) {
4146 return false;
4147 }
4148
4149 ip = ipstr.data();
4150 return true;
4151}
4152
4153 inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
4154 struct sockaddr_storage addr;
4155 socklen_t addr_len = sizeof(addr);
4156 if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
4157 &addr_len)) {
4158 get_ip_and_port(addr, addr_len, ip, port);
4159 }
4160}
4161
4162 inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
4163 struct sockaddr_storage addr;
4164 socklen_t addr_len = sizeof(addr);
4165
4166 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
4167 &addr_len)) {
4168#ifndef _WIN32
4169 if (addr.ss_family == AF_UNIX) {
4170#if defined(__linux__)
4171 struct ucred ucred;
4172 socklen_t len = sizeof(ucred);
4173 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
4174 port = ucred.pid;
4175 }
4176#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
4177 pid_t pid;
4178 socklen_t len = sizeof(pid);
4179 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
4180 port = pid;
4181 }
4182#endif
4183 return;
4184 }
4185#endif
4186 get_ip_and_port(addr, addr_len, ip, port);
4187 }
4188}
4189
4190 inline constexpr unsigned int str2tag_core(const char *s, size_t l,
4191 unsigned int h) {
4192 return (l == 0)
4193 ? h
4194 : str2tag_core(
4195 s + 1, l - 1,
4196 // Unsets the 6 high bits of h, therefore no overflow happens
4197 (((std::numeric_limits<unsigned int>::max)() >> 6) &
4198 h * 33) ^
4199 static_cast<unsigned char>(*s));
4200}
4201
4202 inline unsigned int str2tag(const std::string &s) {
4203 return str2tag_core(s.data(), s.size(), 0);
4204}
4205
4206 namespace udl {
4207
4208 inline constexpr unsigned int operator""_t(const char *s, size_t l) {
4209 return str2tag_core(s, l, 0);
4210}
4211
4212} // namespace udl
4213
4214 inline std::string
4215find_content_type(const std::string &path,
4216 const std::map<std::string, std::string> &user_data,
4217 const std::string &default_content_type) {
4218 auto ext = file_extension(path);
4219
4220 auto it = user_data.find(ext);
4221 if (it != user_data.end()) { return it->second; }
4222
4223 using udl::operator""_t;
4224
4225 switch (str2tag(ext)) {
4226 default: return default_content_type;
4227
4228 case "css"_t: return "text/css";
4229 case "csv"_t: return "text/csv";
4230 case "htm"_t:
4231 case "html"_t: return "text/html";
4232 case "js"_t:
4233 case "mjs"_t: return "text/javascript";
4234 case "txt"_t: return "text/plain";
4235 case "vtt"_t: return "text/vtt";
4236
4237 case "apng"_t: return "image/apng";
4238 case "avif"_t: return "image/avif";
4239 case "bmp"_t: return "image/bmp";
4240 case "gif"_t: return "image/gif";
4241 case "png"_t: return "image/png";
4242 case "svg"_t: return "image/svg+xml";
4243 case "webp"_t: return "image/webp";
4244 case "ico"_t: return "image/x-icon";
4245 case "tif"_t: return "image/tiff";
4246 case "tiff"_t: return "image/tiff";
4247 case "jpg"_t:
4248 case "jpeg"_t: return "image/jpeg";
4249
4250 case "mp4"_t: return "video/mp4";
4251 case "mpeg"_t: return "video/mpeg";
4252 case "webm"_t: return "video/webm";
4253
4254 case "mp3"_t: return "audio/mp3";
4255 case "mpga"_t: return "audio/mpeg";
4256 case "weba"_t: return "audio/webm";
4257 case "wav"_t: return "audio/wave";
4258
4259 case "otf"_t: return "font/otf";
4260 case "ttf"_t: return "font/ttf";
4261 case "woff"_t: return "font/woff";
4262 case "woff2"_t: return "font/woff2";
4263
4264 case "7z"_t: return "application/x-7z-compressed";
4265 case "atom"_t: return "application/atom+xml";
4266 case "pdf"_t: return "application/pdf";
4267 case "json"_t: return "application/json";
4268 case "rss"_t: return "application/rss+xml";
4269 case "tar"_t: return "application/x-tar";
4270 case "xht"_t:
4271 case "xhtml"_t: return "application/xhtml+xml";
4272 case "xslt"_t: return "application/xslt+xml";
4273 case "xml"_t: return "application/xml";
4274 case "gz"_t: return "application/gzip";
4275 case "zip"_t: return "application/zip";
4276 case "wasm"_t: return "application/wasm";
4277 }
4278}
4279
4280 inline bool can_compress_content_type(const std::string &content_type) {
4281 using udl::operator""_t;
4282
4283 auto tag = str2tag(content_type);
4284
4285 switch (tag) {
4286 case "image/svg+xml"_t:
4287 case "application/javascript"_t:
4288 case "application/json"_t:
4289 case "application/xml"_t:
4290 case "application/protobuf"_t:
4291 case "application/xhtml+xml"_t: return true;
4292
4293 case "text/event-stream"_t: return false;
4294
4295 default: return !content_type.rfind("text/", 0);
4296 }
4297}
4298
4299 inline EncodingType encoding_type(const Request &req, const Response &res) {
4300 auto ret =
4301 detail::can_compress_content_type(res.get_header_value("Content-Type"));
4302 if (!ret) { return EncodingType::None; }
4303
4304 const auto &s = req.get_header_value("Accept-Encoding");
4305 (void)(s);
4306
4307#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4308 // TODO: 'Accept-Encoding' has br, not br;q=0
4309 ret = s.find("br") != std::string::npos;
4310 if (ret) { return EncodingType::Brotli; }
4311#endif
4312
4313#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4314 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
4315 ret = s.find("gzip") != std::string::npos;
4316 if (ret) { return EncodingType::Gzip; }
4317#endif
4318
4319#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4320 // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
4321 ret = s.find("zstd") != std::string::npos;
4322 if (ret) { return EncodingType::Zstd; }
4323#endif
4324
4325 return EncodingType::None;
4326}
4327
4328 inline bool nocompressor::compress(const char *data, size_t data_length,
4329 bool /*last*/, Callback callback) {
4330 if (!data_length) { return true; }
4331 return callback(data, data_length);
4332}
4333
4334#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4335 inline gzip_compressor::gzip_compressor() {
4336 std::memset(&strm_, 0, sizeof(strm_));
4337 strm_.zalloc = Z_NULL;
4338 strm_.zfree = Z_NULL;
4339 strm_.opaque = Z_NULL;
4340
4341 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
4342 Z_DEFAULT_STRATEGY) == Z_OK;
4343}
4344
4345 inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
4346
4347 inline bool gzip_compressor::compress(const char *data, size_t data_length,
4348 bool last, Callback callback) {
4349 assert(is_valid_);
4350
4351 do {
4352 constexpr size_t max_avail_in =
4353 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
4354
4355 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
4356 (std::min)(data_length, max_avail_in));
4357 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
4358
4359 data_length -= strm_.avail_in;
4360 data += strm_.avail_in;
4361
4362 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
4363 auto ret = Z_OK;
4364
4365 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4366 do {
4367 strm_.avail_out = static_cast<uInt>(buff.size());
4368 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
4369
4370 ret = deflate(&strm_, flush);
4371 if (ret == Z_STREAM_ERROR) { return false; }
4372
4373 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
4374 return false;
4375 }
4376 } while (strm_.avail_out == 0);
4377
4378 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
4379 (flush == Z_NO_FLUSH && ret == Z_OK));
4380 assert(strm_.avail_in == 0);
4381 } while (data_length > 0);
4382
4383 return true;
4384}
4385
4386 inline gzip_decompressor::gzip_decompressor() {
4387 std::memset(&strm_, 0, sizeof(strm_));
4388 strm_.zalloc = Z_NULL;
4389 strm_.zfree = Z_NULL;
4390 strm_.opaque = Z_NULL;
4391
4392 // 15 is the value of wbits, which should be at the maximum possible value
4393 // to ensure that any gzip stream can be decoded. The offset of 32 specifies
4394 // that the stream type should be automatically detected either gzip or
4395 // deflate.
4396 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
4397}
4398
4399 inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
4400
4401 inline bool gzip_decompressor::is_valid() const { return is_valid_; }
4402
4403 inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
4404 Callback callback) {
4405 assert(is_valid_);
4406
4407 auto ret = Z_OK;
4408
4409 do {
4410 constexpr size_t max_avail_in =
4411 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
4412
4413 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
4414 (std::min)(data_length, max_avail_in));
4415 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
4416
4417 data_length -= strm_.avail_in;
4418 data += strm_.avail_in;
4419
4420 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4421 while (strm_.avail_in > 0 && ret == Z_OK) {
4422 strm_.avail_out = static_cast<uInt>(buff.size());
4423 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
4424
4425 ret = inflate(&strm_, Z_NO_FLUSH);
4426
4427 assert(ret != Z_STREAM_ERROR);
4428 switch (ret) {
4429 case Z_NEED_DICT:
4430 case Z_DATA_ERROR:
4431 case Z_MEM_ERROR: inflateEnd(&strm_); return false;
4432 }
4433
4434 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
4435 return false;
4436 }
4437 }
4438
4439 if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
4440
4441 } while (data_length > 0);
4442
4443 return true;
4444}
4445#endif
4446
4447#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4448 inline brotli_compressor::brotli_compressor() {
4449 state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
4450}
4451
4452 inline brotli_compressor::~brotli_compressor() {
4453 BrotliEncoderDestroyInstance(state_);
4454}
4455
4456 inline bool brotli_compressor::compress(const char *data, size_t data_length,
4457 bool last, Callback callback) {
4458 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4459
4460 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
4461 auto available_in = data_length;
4462 auto next_in = reinterpret_cast<const uint8_t *>(data);
4463
4464 for (;;) {
4465 if (last) {
4466 if (BrotliEncoderIsFinished(state_)) { break; }
4467 } else {
4468 if (!available_in) { break; }
4469 }
4470
4471 auto available_out = buff.size();
4472 auto next_out = buff.data();
4473
4474 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
4475 &available_out, &next_out, nullptr)) {
4476 return false;
4477 }
4478
4479 auto output_bytes = buff.size() - available_out;
4480 if (output_bytes) {
4481 callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
4482 }
4483 }
4484
4485 return true;
4486}
4487
4488 inline brotli_decompressor::brotli_decompressor() {
4489 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
4490 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
4491 : BROTLI_DECODER_RESULT_ERROR;
4492}
4493
4494 inline brotli_decompressor::~brotli_decompressor() {
4495 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
4496}
4497
4498 inline bool brotli_decompressor::is_valid() const { return decoder_s; }
4499
4500 inline bool brotli_decompressor::decompress(const char *data,
4501 size_t data_length,
4502 Callback callback) {
4503 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4504 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
4505 return 0;
4506 }
4507
4508 auto next_in = reinterpret_cast<const uint8_t *>(data);
4509 size_t avail_in = data_length;
4510 size_t total_out;
4511
4512 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
4513
4514 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4515 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
4516 char *next_out = buff.data();
4517 size_t avail_out = buff.size();
4518
4519 decoder_r = BrotliDecoderDecompressStream(
4520 decoder_s, &avail_in, &next_in, &avail_out,
4521 reinterpret_cast<uint8_t **>(&next_out), &total_out);
4522
4523 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
4524
4525 if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
4526 }
4527
4528 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4529 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
4530}
4531#endif
4532
4533#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4534 inline zstd_compressor::zstd_compressor() {
4535 ctx_ = ZSTD_createCCtx();
4536 ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
4537}
4538
4539 inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
4540
4541 inline bool zstd_compressor::compress(const char *data, size_t data_length,
4542 bool last, Callback callback) {
4543 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4544
4545 ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4546 ZSTD_inBuffer input = {data, data_length, 0};
4547
4548 bool finished;
4549 do {
4550 ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
4551 size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
4552
4553 if (ZSTD_isError(remaining)) { return false; }
4554
4555 if (!callback(buff.data(), output.pos)) { return false; }
4556
4557 finished = last ? (remaining == 0) : (input.pos == input.size);
4558
4559 } while (!finished);
4560
4561 return true;
4562}
4563
4564 inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
4565
4566 inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
4567
4568 inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
4569
4570 inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
4571 Callback callback) {
4572 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4573 ZSTD_inBuffer input = {data, data_length, 0};
4574
4575 while (input.pos < input.size) {
4576 ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
4577 size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
4578
4579 if (ZSTD_isError(remaining)) { return false; }
4580
4581 if (!callback(buff.data(), output.pos)) { return false; }
4582 }
4583
4584 return true;
4585}
4586#endif
4587
4588 inline bool has_header(const Headers &headers, const std::string &key) {
4589 return headers.find(key) != headers.end();
4590}
4591
4592 inline const char *get_header_value(const Headers &headers,
4593 const std::string &key, const char *def,
4594 size_t id) {
4595 auto rng = headers.equal_range(key);
4596 auto it = rng.first;
4597 std::advance(it, static_cast<ssize_t>(id));
4598 if (it != rng.second) { return it->second.c_str(); }
4599 return def;
4600}
4601
4602 template <typename T>
4603 inline bool parse_header(const char *beg, const char *end, T fn) {
4604 // Skip trailing spaces and tabs.
4605 while (beg < end && is_space_or_tab(end[-1])) {
4606 end--;
4607 }
4608
4609 auto p = beg;
4610 while (p < end && *p != ':') {
4611 p++;
4612 }
4613
4614 auto name = std::string(beg, p);
4615 if (!detail::fields::is_field_name(name)) { return false; }
4616
4617 if (p == end) { return false; }
4618
4619 auto key_end = p;
4620
4621 if (*p++ != ':') { return false; }
4622
4623 while (p < end && is_space_or_tab(*p)) {
4624 p++;
4625 }
4626
4627 if (p <= end) {
4628 auto key_len = key_end - beg;
4629 if (!key_len) { return false; }
4630
4631 auto key = std::string(beg, key_end);
4632 auto val = std::string(p, end);
4633
4634 if (!detail::fields::is_field_value(val)) { return false; }
4635
4636 if (case_ignore::equal(key, "Location") ||
4637 case_ignore::equal(key, "Referer")) {
4638 fn(key, val);
4639 } else {
4640 fn(key, decode_path_component(val));
4641 }
4642
4643 return true;
4644 }
4645
4646 return false;
4647}
4648
4649 inline bool read_headers(Stream &strm, Headers &headers) {
4650 const auto bufsiz = 2048;
4651 char buf[bufsiz];
4652 stream_line_reader line_reader(strm, buf, bufsiz);
4653
4654 size_t header_count = 0;
4655
4656 for (;;) {
4657 if (!line_reader.getline()) { return false; }
4658
4659 // Check if the line ends with CRLF.
4660 auto line_terminator_len = 2;
4661 if (line_reader.end_with_crlf()) {
4662 // Blank line indicates end of headers.
4663 if (line_reader.size() == 2) { break; }
4664 } else {
4665#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4666 // Blank line indicates end of headers.
4667 if (line_reader.size() == 1) { break; }
4668 line_terminator_len = 1;
4669#else
4670 continue; // Skip invalid line.
4671#endif
4672 }
4673
4674 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
4675
4676 // Check header count limit
4677 if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
4678
4679 // Exclude line terminator
4680 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4681
4682 if (!parse_header(line_reader.ptr(), end,
4683 [&](const std::string &key, const std::string &val) {
4684 headers.emplace(key, val);
4685 })) {
4686 return false;
4687 }
4688
4689 header_count++;
4690 }
4691
4692 return true;
4693}
4694
4695 inline bool read_content_with_length(Stream &strm, size_t len,
4696 DownloadProgress progress,
4697 ContentReceiverWithProgress out) {
4698 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4699
4700 size_t r = 0;
4701 while (r < len) {
4702 auto read_len = static_cast<size_t>(len - r);
4703 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4704 if (n <= 0) { return false; }
4705
4706 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
4707 r += static_cast<size_t>(n);
4708
4709 if (progress) {
4710 if (!progress(r, len)) { return false; }
4711 }
4712 }
4713
4714 return true;
4715}
4716
4717 inline void skip_content_with_length(Stream &strm, size_t len) {
4718 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4719 size_t r = 0;
4720 while (r < len) {
4721 auto read_len = static_cast<size_t>(len - r);
4722 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4723 if (n <= 0) { return; }
4724 r += static_cast<size_t>(n);
4725 }
4726}
4727
4728 enum class ReadContentResult {
4729 Success, // Successfully read the content
4730 PayloadTooLarge, // The content exceeds the specified payload limit
4731 Error // An error occurred while reading the content
4732};
4733
4734 inline ReadContentResult
4735read_content_without_length(Stream &strm, size_t payload_max_length,
4736 ContentReceiverWithProgress out) {
4737 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4738 size_t r = 0;
4739 for (;;) {
4740 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4741 if (n == 0) { return ReadContentResult::Success; }
4742 if (n < 0) { return ReadContentResult::Error; }
4743
4744 // Check if adding this data would exceed the payload limit
4745 if (r > payload_max_length ||
4746 payload_max_length - r < static_cast<size_t>(n)) {
4747 return ReadContentResult::PayloadTooLarge;
4748 }
4749
4750 if (!out(buf, static_cast<size_t>(n), r, 0)) {
4751 return ReadContentResult::Error;
4752 }
4753 r += static_cast<size_t>(n);
4754 }
4755
4756 return ReadContentResult::Success;
4757}
4758
4759 template <typename T>
4760 inline ReadContentResult read_content_chunked(Stream &strm, T &x,
4761 size_t payload_max_length,
4762 ContentReceiverWithProgress out) {
4763 const auto bufsiz = 16;
4764 char buf[bufsiz];
4765
4766 stream_line_reader line_reader(strm, buf, bufsiz);
4767
4768 if (!line_reader.getline()) { return ReadContentResult::Error; }
4769
4770 unsigned long chunk_len;
4771 size_t total_len = 0;
4772 while (true) {
4773 char *end_ptr;
4774
4775 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
4776
4777 if (end_ptr == line_reader.ptr()) { return ReadContentResult::Error; }
4778 if (chunk_len == ULONG_MAX) { return ReadContentResult::Error; }
4779
4780 if (chunk_len == 0) { break; }
4781
4782 // Check if adding this chunk would exceed the payload limit
4783 if (total_len > payload_max_length ||
4784 payload_max_length - total_len < chunk_len) {
4785 return ReadContentResult::PayloadTooLarge;
4786 }
4787
4788 total_len += chunk_len;
4789
4790 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
4791 return ReadContentResult::Error;
4792 }
4793
4794 if (!line_reader.getline()) { return ReadContentResult::Error; }
4795
4796 if (strcmp(line_reader.ptr(), "\r\n") != 0) {
4797 return ReadContentResult::Error;
4798 }
4799
4800 if (!line_reader.getline()) { return ReadContentResult::Error; }
4801 }
4802
4803 assert(chunk_len == 0);
4804
4805 // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
4806 // transfer coding is complete when a chunk with a chunk-size of zero is
4807 // received, possibly followed by a trailer section, and finally terminated by
4808 // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
4809 //
4810 // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
4811 // does't care for the existence of the final CRLF. In other words, it seems
4812 // to be ok whether the final CRLF exists or not in the chunked data.
4813 // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4814 //
4815 // According to the reference code in RFC 9112, cpp-httplib now allows
4816 // chunked transfer coding data without the final CRLF.
4817 if (!line_reader.getline()) { return ReadContentResult::Success; }
4818
4819 // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
4820 thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
4821 // Message framing
4822 "transfer-encoding", "content-length",
4823
4824 // Routing
4825 "host",
4826
4827 // Authentication
4828 "authorization", "www-authenticate", "proxy-authenticate",
4829 "proxy-authorization", "cookie", "set-cookie",
4830
4831 // Request modifiers
4832 "cache-control", "expect", "max-forwards", "pragma", "range", "te",
4833
4834 // Response control
4835 "age", "expires", "date", "location", "retry-after", "vary", "warning",
4836
4837 // Payload processing
4838 "content-encoding", "content-type", "content-range", "trailer"};
4839
4840 // Parse declared trailer headers once for performance
4841 case_ignore::unordered_set<std::string> declared_trailers;
4842 if (has_header(x.headers, "Trailer")) {
4843 auto trailer_header = get_header_value(x.headers, "Trailer", "", 0);
4844 auto len = std::strlen(trailer_header);
4845
4846 split(trailer_header, trailer_header + len, ',',
4847 [&](const char *b, const char *e) {
4848 std::string key(b, e);
4849 if (prohibited_trailers.find(key) == prohibited_trailers.end()) {
4850 declared_trailers.insert(key);
4851 }
4852 });
4853 }
4854
4855 size_t trailer_header_count = 0;
4856 while (strcmp(line_reader.ptr(), "\r\n") != 0) {
4857 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4858 return ReadContentResult::Error;
4859 }
4860
4861 // Check trailer header count limit
4862 if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {
4863 return ReadContentResult::Error;
4864 }
4865
4866 // Exclude line terminator
4867 constexpr auto line_terminator_len = 2;
4868 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4869
4870 parse_header(line_reader.ptr(), end,
4871 [&](const std::string &key, const std::string &val) {
4872 if (declared_trailers.find(key) != declared_trailers.end()) {
4873 x.trailers.emplace(key, val);
4874 trailer_header_count++;
4875 }
4876 });
4877
4878 if (!line_reader.getline()) { return ReadContentResult::Error; }
4879 }
4880
4881 return ReadContentResult::Success;
4882}
4883
4884 inline bool is_chunked_transfer_encoding(const Headers &headers) {
4885 return case_ignore::equal(
4886 get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
4887}
4888
4889 template <typename T, typename U>
4890 bool prepare_content_receiver(T &x, int &status,
4891 ContentReceiverWithProgress receiver,
4892 bool decompress, U callback) {
4893 if (decompress) {
4894 std::string encoding = x.get_header_value("Content-Encoding");
4895 std::unique_ptr<decompressor> decompressor;
4896
4897 if (encoding == "gzip" || encoding == "deflate") {
4898#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4899 decompressor = detail::make_unique<gzip_decompressor>();
4900#else
4901 status = StatusCode::UnsupportedMediaType_415;
4902 return false;
4903#endif
4904 } else if (encoding.find("br") != std::string::npos) {
4905#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4906 decompressor = detail::make_unique<brotli_decompressor>();
4907#else
4908 status = StatusCode::UnsupportedMediaType_415;
4909 return false;
4910#endif
4911 } else if (encoding == "zstd") {
4912#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4913 decompressor = detail::make_unique<zstd_decompressor>();
4914#else
4915 status = StatusCode::UnsupportedMediaType_415;
4916 return false;
4917#endif
4918 }
4919
4920 if (decompressor) {
4921 if (decompressor->is_valid()) {
4922 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
4923 size_t off, size_t len) {
4924 return decompressor->decompress(buf, n,
4925 [&](const char *buf2, size_t n2) {
4926 return receiver(buf2, n2, off, len);
4927 });
4928 };
4929 return callback(std::move(out));
4930 } else {
4931 status = StatusCode::InternalServerError_500;
4932 return false;
4933 }
4934 }
4935 }
4936
4937 ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
4938 size_t len) {
4939 return receiver(buf, n, off, len);
4940 };
4941 return callback(std::move(out));
4942}
4943
4944 template <typename T>
4945 bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
4946 DownloadProgress progress,
4947 ContentReceiverWithProgress receiver, bool decompress) {
4948 return prepare_content_receiver(
4949 x, status, std::move(receiver), decompress,
4950 [&](const ContentReceiverWithProgress &out) {
4951 auto ret = true;
4952 auto exceed_payload_max_length = false;
4953
4954 if (is_chunked_transfer_encoding(x.headers)) {
4955 auto result = read_content_chunked(strm, x, payload_max_length, out);
4956 if (result == ReadContentResult::Success) {
4957 ret = true;
4958 } else if (result == ReadContentResult::PayloadTooLarge) {
4959 exceed_payload_max_length = true;
4960 ret = false;
4961 } else {
4962 ret = false;
4963 }
4964 } else if (!has_header(x.headers, "Content-Length")) {
4965 auto result =
4966 read_content_without_length(strm, payload_max_length, out);
4967 if (result == ReadContentResult::Success) {
4968 ret = true;
4969 } else if (result == ReadContentResult::PayloadTooLarge) {
4970 exceed_payload_max_length = true;
4971 ret = false;
4972 } else {
4973 ret = false;
4974 }
4975 } else {
4976 auto is_invalid_value = false;
4977 auto len = get_header_value_u64(x.headers, "Content-Length",
4978 (std::numeric_limits<size_t>::max)(),
4979 0, is_invalid_value);
4980
4981 if (is_invalid_value) {
4982 ret = false;
4983 } else if (len > payload_max_length) {
4984 exceed_payload_max_length = true;
4985 skip_content_with_length(strm, len);
4986 ret = false;
4987 } else if (len > 0) {
4988 ret = read_content_with_length(strm, len, std::move(progress), out);
4989 }
4990 }
4991
4992 if (!ret) {
4993 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
4994 : StatusCode::BadRequest_400;
4995 }
4996 return ret;
4997 });
4998}
4999
5000 inline ssize_t write_request_line(Stream &strm, const std::string &method,
5001 const std::string &path) {
5002 std::string s = method;
5003 s += " ";
5004 s += path;
5005 s += " HTTP/1.1\r\n";
5006 return strm.write(s.data(), s.size());
5007}
5008
5009 inline ssize_t write_response_line(Stream &strm, int status) {
5010 std::string s = "HTTP/1.1 ";
5011 s += std::to_string(status);
5012 s += " ";
5013 s += httplib::status_message(status);
5014 s += "\r\n";
5015 return strm.write(s.data(), s.size());
5016}
5017
5018 inline ssize_t write_headers(Stream &strm, const Headers &headers) {
5019 ssize_t write_len = 0;
5020 for (const auto &x : headers) {
5021 std::string s;
5022 s = x.first;
5023 s += ": ";
5024 s += x.second;
5025 s += "\r\n";
5026
5027 auto len = strm.write(s.data(), s.size());
5028 if (len < 0) { return len; }
5029 write_len += len;
5030 }
5031 auto len = strm.write("\r\n");
5032 if (len < 0) { return len; }
5033 write_len += len;
5034 return write_len;
5035}
5036
5037 inline bool write_data(Stream &strm, const char *d, size_t l) {
5038 size_t offset = 0;
5039 while (offset < l) {
5040 auto length = strm.write(d + offset, l - offset);
5041 if (length < 0) { return false; }
5042 offset += static_cast<size_t>(length);
5043 }
5044 return true;
5045}
5046
5047 template <typename T>
5048 inline bool write_content_with_progress(Stream &strm,
5049 const ContentProvider &content_provider,
5050 size_t offset, size_t length,
5051 T is_shutting_down,
5052 const UploadProgress &upload_progress,
5053 Error &error) {
5054 size_t end_offset = offset + length;
5055 size_t start_offset = offset;
5056 auto ok = true;
5057 DataSink data_sink;
5058
5059 data_sink.write = [&](const char *d, size_t l) -> bool {
5060 if (ok) {
5061 if (write_data(strm, d, l)) {
5062 offset += l;
5063
5064 if (upload_progress && length > 0) {
5065 size_t current_written = offset - start_offset;
5066 if (!upload_progress(current_written, length)) {
5067 ok = false;
5068 return false;
5069 }
5070 }
5071 } else {
5072 ok = false;
5073 }
5074 }
5075 return ok;
5076 };
5077
5078 data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
5079
5080 while (offset < end_offset && !is_shutting_down()) {
5081 if (!strm.wait_writable()) {
5082 error = Error::Write;
5083 return false;
5084 } else if (!content_provider(offset, end_offset - offset, data_sink)) {
5085 error = Error::Canceled;
5086 return false;
5087 } else if (!ok) {
5088 error = Error::Write;
5089 return false;
5090 }
5091 }
5092
5093 error = Error::Success;
5094 return true;
5095}
5096
5097 template <typename T>
5098 inline bool write_content(Stream &strm, const ContentProvider &content_provider,
5099 size_t offset, size_t length, T is_shutting_down,
5100 Error &error) {
5101 return write_content_with_progress<T>(strm, content_provider, offset, length,
5102 is_shutting_down, nullptr, error);
5103}
5104
5105 template <typename T>
5106 inline bool write_content(Stream &strm, const ContentProvider &content_provider,
5107 size_t offset, size_t length,
5108 const T &is_shutting_down) {
5109 auto error = Error::Success;
5110 return write_content(strm, content_provider, offset, length, is_shutting_down,
5111 error);
5112}
5113
5114 template <typename T>
5115 inline bool
5116write_content_without_length(Stream &strm,
5117 const ContentProvider &content_provider,
5118 const T &is_shutting_down) {
5119 size_t offset = 0;
5120 auto data_available = true;
5121 auto ok = true;
5122 DataSink data_sink;
5123
5124 data_sink.write = [&](const char *d, size_t l) -> bool {
5125 if (ok) {
5126 offset += l;
5127 if (!write_data(strm, d, l)) { ok = false; }
5128 }
5129 return ok;
5130 };
5131
5132 data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
5133
5134 data_sink.done = [&](void) { data_available = false; };
5135
5136 while (data_available && !is_shutting_down()) {
5137 if (!strm.wait_writable()) {
5138 return false;
5139 } else if (!content_provider(offset, 0, data_sink)) {
5140 return false;
5141 } else if (!ok) {
5142 return false;
5143 }
5144 }
5145 return true;
5146}
5147
5148 template <typename T, typename U>
5149 inline bool
5150write_content_chunked(Stream &strm, const ContentProvider &content_provider,
5151 const T &is_shutting_down, U &compressor, Error &error) {
5152 size_t offset = 0;
5153 auto data_available = true;
5154 auto ok = true;
5155 DataSink data_sink;
5156
5157 data_sink.write = [&](const char *d, size_t l) -> bool {
5158 if (ok) {
5159 data_available = l > 0;
5160 offset += l;
5161
5162 std::string payload;
5163 if (compressor.compress(d, l, false,
5164 [&](const char *data, size_t data_len) {
5165 payload.append(data, data_len);
5166 return true;
5167 })) {
5168 if (!payload.empty()) {
5169 // Emit chunked response header and footer for each chunk
5170 auto chunk =
5171 from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
5172 if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
5173 }
5174 } else {
5175 ok = false;
5176 }
5177 }
5178 return ok;
5179 };
5180
5181 data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
5182
5183 auto done_with_trailer = [&](const Headers *trailer) {
5184 if (!ok) { return; }
5185
5186 data_available = false;
5187
5188 std::string payload;
5189 if (!compressor.compress(nullptr, 0, true,
5190 [&](const char *data, size_t data_len) {
5191 payload.append(data, data_len);
5192 return true;
5193 })) {
5194 ok = false;
5195 return;
5196 }
5197
5198 if (!payload.empty()) {
5199 // Emit chunked response header and footer for each chunk
5200 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
5201 if (!write_data(strm, chunk.data(), chunk.size())) {
5202 ok = false;
5203 return;
5204 }
5205 }
5206
5207 constexpr const char done_marker[] = "0\r\n";
5208 if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
5209
5210 // Trailer
5211 if (trailer) {
5212 for (const auto &kv : *trailer) {
5213 std::string field_line = kv.first + ": " + kv.second + "\r\n";
5214 if (!write_data(strm, field_line.data(), field_line.size())) {
5215 ok = false;
5216 }
5217 }
5218 }
5219
5220 constexpr const char crlf[] = "\r\n";
5221 if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
5222 };
5223
5224 data_sink.done = [&](void) { done_with_trailer(nullptr); };
5225
5226 data_sink.done_with_trailer = [&](const Headers &trailer) {
5227 done_with_trailer(&trailer);
5228 };
5229
5230 while (data_available && !is_shutting_down()) {
5231 if (!strm.wait_writable()) {
5232 error = Error::Write;
5233 return false;
5234 } else if (!content_provider(offset, 0, data_sink)) {
5235 error = Error::Canceled;
5236 return false;
5237 } else if (!ok) {
5238 error = Error::Write;
5239 return false;
5240 }
5241 }
5242
5243 error = Error::Success;
5244 return true;
5245}
5246
5247 template <typename T, typename U>
5248 inline bool write_content_chunked(Stream &strm,
5249 const ContentProvider &content_provider,
5250 const T &is_shutting_down, U &compressor) {
5251 auto error = Error::Success;
5252 return write_content_chunked(strm, content_provider, is_shutting_down,
5253 compressor, error);
5254}
5255
5256 template <typename T>
5257 inline bool redirect(T &cli, Request &req, Response &res,
5258 const std::string &path, const std::string &location,
5259 Error &error) {
5260 Request new_req = req;
5261 new_req.path = path;
5262 new_req.redirect_count_ -= 1;
5263
5264 if (res.status == StatusCode::SeeOther_303 &&
5265 (req.method != "GET" && req.method != "HEAD")) {
5266 new_req.method = "GET";
5267 new_req.body.clear();
5268 new_req.headers.clear();
5269 }
5270
5271 Response new_res;
5272
5273 auto ret = cli.send(new_req, new_res, error);
5274 if (ret) {
5275 req = new_req;
5276 res = new_res;
5277
5278 if (res.location.empty()) { res.location = location; }
5279 }
5280 return ret;
5281}
5282
5283 inline std::string params_to_query_str(const Params &params) {
5284 std::string query;
5285
5286 for (auto it = params.begin(); it != params.end(); ++it) {
5287 if (it != params.begin()) { query += "&"; }
5288 query += encode_query_component(it->first);
5289 query += "=";
5290 query += encode_query_component(it->second);
5291 }
5292 return query;
5293}
5294
5295 inline void parse_query_text(const char *data, std::size_t size,
5296 Params &params) {
5297 std::set<std::string> cache;
5298 split(data, data + size, '&', [&](const char *b, const char *e) {
5299 std::string kv(b, e);
5300 if (cache.find(kv) != cache.end()) { return; }
5301 cache.insert(std::move(kv));
5302
5303 std::string key;
5304 std::string val;
5305 divide(b, static_cast<std::size_t>(e - b), '=',
5306 [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
5307 std::size_t rhs_size) {
5308 key.assign(lhs_data, lhs_size);
5309 val.assign(rhs_data, rhs_size);
5310 });
5311
5312 if (!key.empty()) {
5313 params.emplace(decode_query_component(key), decode_query_component(val));
5314 }
5315 });
5316}
5317
5318 inline void parse_query_text(const std::string &s, Params &params) {
5319 parse_query_text(s.data(), s.size(), params);
5320}
5321
5322 inline bool parse_multipart_boundary(const std::string &content_type,
5323 std::string &boundary) {
5324 auto boundary_keyword = "boundary=";
5325 auto pos = content_type.find(boundary_keyword);
5326 if (pos == std::string::npos) { return false; }
5327 auto end = content_type.find(';', pos);
5328 auto beg = pos + strlen(boundary_keyword);
5329 boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
5330 return !boundary.empty();
5331}
5332
5333 inline void parse_disposition_params(const std::string &s, Params &params) {
5334 std::set<std::string> cache;
5335 split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
5336 std::string kv(b, e);
5337 if (cache.find(kv) != cache.end()) { return; }
5338 cache.insert(kv);
5339
5340 std::string key;
5341 std::string val;
5342 split(b, e, '=', [&](const char *b2, const char *e2) {
5343 if (key.empty()) {
5344 key.assign(b2, e2);
5345 } else {
5346 val.assign(b2, e2);
5347 }
5348 });
5349
5350 if (!key.empty()) {
5351 params.emplace(trim_double_quotes_copy((key)),
5352 trim_double_quotes_copy((val)));
5353 }
5354 });
5355}
5356
5357#ifdef CPPHTTPLIB_NO_EXCEPTIONS
5358 inline bool parse_range_header(const std::string &s, Ranges &ranges) {
5359#else
5360 inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
5361#endif
5362 auto is_valid = [](const std::string &str) {
5363 return std::all_of(str.cbegin(), str.cend(),
5364 [](unsigned char c) { return std::isdigit(c); });
5365 };
5366
5367 if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
5368 const auto pos = static_cast<size_t>(6);
5369 const auto len = static_cast<size_t>(s.size() - 6);
5370 auto all_valid_ranges = true;
5371 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
5372 if (!all_valid_ranges) { return; }
5373
5374 const auto it = std::find(b, e, '-');
5375 if (it == e) {
5376 all_valid_ranges = false;
5377 return;
5378 }
5379
5380 const auto lhs = std::string(b, it);
5381 const auto rhs = std::string(it + 1, e);
5382 if (!is_valid(lhs) || !is_valid(rhs)) {
5383 all_valid_ranges = false;
5384 return;
5385 }
5386
5387 const auto first =
5388 static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
5389 const auto last =
5390 static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
5391 if ((first == -1 && last == -1) ||
5392 (first != -1 && last != -1 && first > last)) {
5393 all_valid_ranges = false;
5394 return;
5395 }
5396
5397 ranges.emplace_back(first, last);
5398 });
5399 return all_valid_ranges && !ranges.empty();
5400 }
5401 return false;
5402#ifdef CPPHTTPLIB_NO_EXCEPTIONS
5403}
5404#else
5405} catch (...) { return false; }
5406#endif
5407
5408 inline bool parse_accept_header(const std::string &s,
5409 std::vector<std::string> &content_types) {
5410 content_types.clear();
5411
5412 // Empty string is considered valid (no preference)
5413 if (s.empty()) { return true; }
5414
5415 // Check for invalid patterns: leading/trailing commas or consecutive commas
5416 if (s.front() == ',' || s.back() == ',' ||
5417 s.find(",,") != std::string::npos) {
5418 return false;
5419 }
5420
5421 struct AcceptEntry {
5422 std::string media_type;
5423 double quality;
5424 int order; // Original order in header
5425 };
5426
5427 std::vector<AcceptEntry> entries;
5428 int order = 0;
5429 bool has_invalid_entry = false;
5430
5431 // Split by comma and parse each entry
5432 split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
5433 std::string entry(b, e);
5434 entry = trim_copy(entry);
5435
5436 if (entry.empty()) {
5437 has_invalid_entry = true;
5438 return;
5439 }
5440
5441 AcceptEntry accept_entry;
5442 accept_entry.quality = 1.0; // Default quality
5443 accept_entry.order = order++;
5444
5445 // Find q= parameter
5446 auto q_pos = entry.find(";q=");
5447 if (q_pos == std::string::npos) { q_pos = entry.find("; q="); }
5448
5449 if (q_pos != std::string::npos) {
5450 // Extract media type (before q parameter)
5451 accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
5452
5453 // Extract quality value
5454 auto q_start = entry.find('=', q_pos) + 1;
5455 auto q_end = entry.find(';', q_start);
5456 if (q_end == std::string::npos) { q_end = entry.length(); }
5457
5458 std::string quality_str =
5459 trim_copy(entry.substr(q_start, q_end - q_start));
5460 if (quality_str.empty()) {
5461 has_invalid_entry = true;
5462 return;
5463 }
5464
5465#ifdef CPPHTTPLIB_NO_EXCEPTIONS
5466 {
5467 std::istringstream iss(quality_str);
5468 iss >> accept_entry.quality;
5469
5470 // Check if conversion was successful and entire string was consumed
5471 if (iss.fail() || !iss.eof()) {
5472 has_invalid_entry = true;
5473 return;
5474 }
5475 }
5476#else
5477 try {
5478 accept_entry.quality = std::stod(quality_str);
5479 } catch (...) {
5480 has_invalid_entry = true;
5481 return;
5482 }
5483#endif
5484 // Check if quality is in valid range [0.0, 1.0]
5485 if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
5486 has_invalid_entry = true;
5487 return;
5488 }
5489 } else {
5490 // No quality parameter, use entire entry as media type
5491 accept_entry.media_type = entry;
5492 }
5493
5494 // Remove additional parameters from media type
5495 auto param_pos = accept_entry.media_type.find(';');
5496 if (param_pos != std::string::npos) {
5497 accept_entry.media_type =
5498 trim_copy(accept_entry.media_type.substr(0, param_pos));
5499 }
5500
5501 // Basic validation of media type format
5502 if (accept_entry.media_type.empty()) {
5503 has_invalid_entry = true;
5504 return;
5505 }
5506
5507 // Check for basic media type format (should contain '/' or be '*')
5508 if (accept_entry.media_type != "*" &&
5509 accept_entry.media_type.find('/') == std::string::npos) {
5510 has_invalid_entry = true;
5511 return;
5512 }
5513
5514 entries.push_back(accept_entry);
5515 });
5516
5517 // Return false if any invalid entry was found
5518 if (has_invalid_entry) { return false; }
5519
5520 // Sort by quality (descending), then by original order (ascending)
5521 std::sort(entries.begin(), entries.end(),
5522 [](const AcceptEntry &a, const AcceptEntry &b) {
5523 if (a.quality != b.quality) {
5524 return a.quality > b.quality; // Higher quality first
5525 }
5526 return a.order < b.order; // Earlier order first for same quality
5527 });
5528
5529 // Extract sorted media types
5530 content_types.reserve(entries.size());
5531 for (const auto &entry : entries) {
5532 content_types.push_back(entry.media_type);
5533 }
5534
5535 return true;
5536}
5537
5538 class FormDataParser {
5539 public:
5540 FormDataParser() = default;
5541
5542 void set_boundary(std::string &&boundary) {
5543 boundary_ = boundary;
5544 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
5545 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
5546 }
5547
5548 bool is_valid() const { return is_valid_; }
5549
5550 bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
5551 const ContentReceiver &content_callback) {
5552
5553 buf_append(buf, n);
5554
5555 while (buf_size() > 0) {
5556 switch (state_) {
5557 case 0: { // Initial boundary
5558 auto pos = buf_find(dash_boundary_crlf_);
5559 if (pos == buf_size()) { return true; }
5560 buf_erase(pos + dash_boundary_crlf_.size());
5561 state_ = 1;
5562 break;
5563 }
5564 case 1: { // New entry
5565 clear_file_info();
5566 state_ = 2;
5567 break;
5568 }
5569 case 2: { // Headers
5570 auto pos = buf_find(crlf_);
5571 if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
5572 while (pos < buf_size()) {
5573 // Empty line
5574 if (pos == 0) {
5575 if (!header_callback(file_)) {
5576 is_valid_ = false;
5577 return false;
5578 }
5579 buf_erase(crlf_.size());
5580 state_ = 3;
5581 break;
5582 }
5583
5584 const auto header = buf_head(pos);
5585
5586 if (!parse_header(header.data(), header.data() + header.size(),
5587 [&](const std::string &, const std::string &) {})) {
5588 is_valid_ = false;
5589 return false;
5590 }
5591
5592 // Parse and emplace space trimmed headers into a map
5593 if (!parse_header(
5594 header.data(), header.data() + header.size(),
5595 [&](const std::string &key, const std::string &val) {
5596 file_.headers.emplace(key, val);
5597 })) {
5598 is_valid_ = false;
5599 return false;
5600 }
5601
5602 constexpr const char header_content_type[] = "Content-Type:";
5603
5604 if (start_with_case_ignore(header, header_content_type)) {
5605 file_.content_type =
5606 trim_copy(header.substr(str_len(header_content_type)));
5607 } else {
5608 thread_local const std::regex re_content_disposition(
5609 R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
5610 std::regex_constants::icase);
5611
5612 std::smatch m;
5613 if (std::regex_match(header, m, re_content_disposition)) {
5614 Params params;
5615 parse_disposition_params(m[1], params);
5616
5617 auto it = params.find("name");
5618 if (it != params.end()) {
5619 file_.name = it->second;
5620 } else {
5621 is_valid_ = false;
5622 return false;
5623 }
5624
5625 it = params.find("filename");
5626 if (it != params.end()) { file_.filename = it->second; }
5627
5628 it = params.find("filename*");
5629 if (it != params.end()) {
5630 // Only allow UTF-8 encoding...
5631 thread_local const std::regex re_rfc5987_encoding(
5632 R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
5633
5634 std::smatch m2;
5635 if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
5636 file_.filename = decode_path_component(m2[1]); // override...
5637 } else {
5638 is_valid_ = false;
5639 return false;
5640 }
5641 }
5642 }
5643 }
5644 buf_erase(pos + crlf_.size());
5645 pos = buf_find(crlf_);
5646 }
5647 if (state_ != 3) { return true; }
5648 break;
5649 }
5650 case 3: { // Body
5651 if (crlf_dash_boundary_.size() > buf_size()) { return true; }
5652 auto pos = buf_find(crlf_dash_boundary_);
5653 if (pos < buf_size()) {
5654 if (!content_callback(buf_data(), pos)) {
5655 is_valid_ = false;
5656 return false;
5657 }
5658 buf_erase(pos + crlf_dash_boundary_.size());
5659 state_ = 4;
5660 } else {
5661 auto len = buf_size() - crlf_dash_boundary_.size();
5662 if (len > 0) {
5663 if (!content_callback(buf_data(), len)) {
5664 is_valid_ = false;
5665 return false;
5666 }
5667 buf_erase(len);
5668 }
5669 return true;
5670 }
5671 break;
5672 }
5673 case 4: { // Boundary
5674 if (crlf_.size() > buf_size()) { return true; }
5675 if (buf_start_with(crlf_)) {
5676 buf_erase(crlf_.size());
5677 state_ = 1;
5678 } else {
5679 if (dash_.size() > buf_size()) { return true; }
5680 if (buf_start_with(dash_)) {
5681 buf_erase(dash_.size());
5682 is_valid_ = true;
5683 buf_erase(buf_size()); // Remove epilogue
5684 } else {
5685 return true;
5686 }
5687 }
5688 break;
5689 }
5690 }
5691 }
5692
5693 return true;
5694 }
5695
5696 private:
5697 void clear_file_info() {
5698 file_.name.clear();
5699 file_.filename.clear();
5700 file_.content_type.clear();
5701 file_.headers.clear();
5702 }
5703
5704 bool start_with_case_ignore(const std::string &a, const char *b) const {
5705 const auto b_len = strlen(b);
5706 if (a.size() < b_len) { return false; }
5707 for (size_t i = 0; i < b_len; i++) {
5708 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
5709 return false;
5710 }
5711 }
5712 return true;
5713 }
5714
5715 const std::string dash_ = "--";
5716 const std::string crlf_ = "\r\n";
5717 std::string boundary_;
5718 std::string dash_boundary_crlf_;
5719 std::string crlf_dash_boundary_;
5720
5721 size_t state_ = 0;
5722 bool is_valid_ = false;
5723 FormData file_;
5724
5725 // Buffer
5726 bool start_with(const std::string &a, size_t spos, size_t epos,
5727 const std::string &b) const {
5728 if (epos - spos < b.size()) { return false; }
5729 for (size_t i = 0; i < b.size(); i++) {
5730 if (a[i + spos] != b[i]) { return false; }
5731 }
5732 return true;
5733 }
5734
5735 size_t buf_size() const { return buf_epos_ - buf_spos_; }
5736
5737 const char *buf_data() const { return &buf_[buf_spos_]; }
5738
5739 std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
5740
5741 bool buf_start_with(const std::string &s) const {
5742 return start_with(buf_, buf_spos_, buf_epos_, s);
5743 }
5744
5745 size_t buf_find(const std::string &s) const {
5746 auto c = s.front();
5747
5748 size_t off = buf_spos_;
5749 while (off < buf_epos_) {
5750 auto pos = off;
5751 while (true) {
5752 if (pos == buf_epos_) { return buf_size(); }
5753 if (buf_[pos] == c) { break; }
5754 pos++;
5755 }
5756
5757 auto remaining_size = buf_epos_ - pos;
5758 if (s.size() > remaining_size) { return buf_size(); }
5759
5760 if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
5761
5762 off = pos + 1;
5763 }
5764
5765 return buf_size();
5766 }
5767
5768 void buf_append(const char *data, size_t n) {
5769 auto remaining_size = buf_size();
5770 if (remaining_size > 0 && buf_spos_ > 0) {
5771 for (size_t i = 0; i < remaining_size; i++) {
5772 buf_[i] = buf_[buf_spos_ + i];
5773 }
5774 }
5775 buf_spos_ = 0;
5776 buf_epos_ = remaining_size;
5777
5778 if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
5779
5780 for (size_t i = 0; i < n; i++) {
5781 buf_[buf_epos_ + i] = data[i];
5782 }
5783 buf_epos_ += n;
5784 }
5785
5786 void buf_erase(size_t size) { buf_spos_ += size; }
5787
5788 std::string buf_;
5789 size_t buf_spos_ = 0;
5790 size_t buf_epos_ = 0;
5791};
5792
5793 inline std::string random_string(size_t length) {
5794 constexpr const char data[] =
5795 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5796
5797 thread_local auto engine([]() {
5798 // std::random_device might actually be deterministic on some
5799 // platforms, but due to lack of support in the c++ standard library,
5800 // doing better requires either some ugly hacks or breaking portability.
5801 std::random_device seed_gen;
5802 // Request 128 bits of entropy for initialization
5803 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
5804 return std::mt19937(seed_sequence);
5805 }());
5806
5807 std::string result;
5808 for (size_t i = 0; i < length; i++) {
5809 result += data[engine() % (sizeof(data) - 1)];
5810 }
5811 return result;
5812}
5813
5814 inline std::string make_multipart_data_boundary() {
5815 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
5816}
5817
5818 inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
5819 auto valid = true;
5820 for (size_t i = 0; i < boundary.size(); i++) {
5821 auto c = boundary[i];
5822 if (!std::isalnum(c) && c != '-' && c != '_') {
5823 valid = false;
5824 break;
5825 }
5826 }
5827 return valid;
5828}
5829
5830 template <typename T>
5831 inline std::string
5832serialize_multipart_formdata_item_begin(const T &item,
5833 const std::string &boundary) {
5834 std::string body = "--" + boundary + "\r\n";
5835 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
5836 if (!item.filename.empty()) {
5837 body += "; filename=\"" + item.filename + "\"";
5838 }
5839 body += "\r\n";
5840 if (!item.content_type.empty()) {
5841 body += "Content-Type: " + item.content_type + "\r\n";
5842 }
5843 body += "\r\n";
5844
5845 return body;
5846}
5847
5848 inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
5849
5850 inline std::string
5851serialize_multipart_formdata_finish(const std::string &boundary) {
5852 return "--" + boundary + "--\r\n";
5853}
5854
5855 inline std::string
5856serialize_multipart_formdata_get_content_type(const std::string &boundary) {
5857 return "multipart/form-data; boundary=" + boundary;
5858}
5859
5860 inline std::string
5861serialize_multipart_formdata(const UploadFormDataItems &items,
5862 const std::string &boundary, bool finish = true) {
5863 std::string body;
5864
5865 for (const auto &item : items) {
5866 body += serialize_multipart_formdata_item_begin(item, boundary);
5867 body += item.content + serialize_multipart_formdata_item_end();
5868 }
5869
5870 if (finish) { body += serialize_multipart_formdata_finish(boundary); }
5871
5872 return body;
5873}
5874
5875 inline void coalesce_ranges(Ranges &ranges, size_t content_length) {
5876 if (ranges.size() <= 1) return;
5877
5878 // Sort ranges by start position
5879 std::sort(ranges.begin(), ranges.end(),
5880 [](const Range &a, const Range &b) { return a.first < b.first; });
5881
5882 Ranges coalesced;
5883 coalesced.reserve(ranges.size());
5884
5885 for (auto &r : ranges) {
5886 auto first_pos = r.first;
5887 auto last_pos = r.second;
5888
5889 // Handle special cases like in range_error
5890 if (first_pos == -1 && last_pos == -1) {
5891 first_pos = 0;
5892 last_pos = static_cast<ssize_t>(content_length);
5893 }
5894
5895 if (first_pos == -1) {
5896 first_pos = static_cast<ssize_t>(content_length) - last_pos;
5897 last_pos = static_cast<ssize_t>(content_length) - 1;
5898 }
5899
5900 if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
5901 last_pos = static_cast<ssize_t>(content_length) - 1;
5902 }
5903
5904 // Skip invalid ranges
5905 if (!(0 <= first_pos && first_pos <= last_pos &&
5906 last_pos < static_cast<ssize_t>(content_length))) {
5907 continue;
5908 }
5909
5910 // Coalesce with previous range if overlapping or adjacent (but not
5911 // identical)
5912 if (!coalesced.empty()) {
5913 auto &prev = coalesced.back();
5914 // Check if current range overlaps or is adjacent to previous range
5915 // but don't coalesce identical ranges (allow duplicates)
5916 if (first_pos <= prev.second + 1 &&
5917 !(first_pos == prev.first && last_pos == prev.second)) {
5918 // Extend the previous range
5919 prev.second = (std::max)(prev.second, last_pos);
5920 continue;
5921 }
5922 }
5923
5924 // Add new range
5925 coalesced.emplace_back(first_pos, last_pos);
5926 }
5927
5928 ranges = std::move(coalesced);
5929}
5930
5931 inline bool range_error(Request &req, Response &res) {
5932 if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
5933 ssize_t content_len = static_cast<ssize_t>(
5934 res.content_length_ ? res.content_length_ : res.body.size());
5935
5936 std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
5937 size_t overwrapping_count = 0;
5938
5939 // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
5940 // 'HTTP Semantics' to avoid potential denial-of-service attacks.
5941 // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
5942
5943 // Too many ranges
5944 if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }
5945
5946 for (auto &r : req.ranges) {
5947 auto &first_pos = r.first;
5948 auto &last_pos = r.second;
5949
5950 if (first_pos == -1 && last_pos == -1) {
5951 first_pos = 0;
5952 last_pos = content_len;
5953 }
5954
5955 if (first_pos == -1) {
5956 first_pos = content_len - last_pos;
5957 last_pos = content_len - 1;
5958 }
5959
5960 // NOTE: RFC-9110 '14.1.2. Byte Ranges':
5961 // A client can limit the number of bytes requested without knowing the
5962 // size of the selected representation. If the last-pos value is absent,
5963 // or if the value is greater than or equal to the current length of the
5964 // representation data, the byte range is interpreted as the remainder of
5965 // the representation (i.e., the server replaces the value of last-pos
5966 // with a value that is one less than the current length of the selected
5967 // representation).
5968 // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
5969 if (last_pos == -1 || last_pos >= content_len) {
5970 last_pos = content_len - 1;
5971 }
5972
5973 // Range must be within content length
5974 if (!(0 <= first_pos && first_pos <= last_pos &&
5975 last_pos <= content_len - 1)) {
5976 return true;
5977 }
5978
5979 // Request must not have more than two overlapping ranges
5980 for (const auto &processed_range : processed_ranges) {
5981 if (!(last_pos < processed_range.first ||
5982 first_pos > processed_range.second)) {
5983 overwrapping_count++;
5984 if (overwrapping_count > 2) { return true; }
5985 break; // Only count once per range
5986 }
5987 }
5988
5989 processed_ranges.emplace_back(first_pos, last_pos);
5990 }
5991
5992 // After validation, coalesce overlapping ranges as per RFC 9110
5993 coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
5994 }
5995
5996 return false;
5997}
5998
5999 inline std::pair<size_t, size_t>
6000get_range_offset_and_length(Range r, size_t content_length) {
6001 assert(r.first != -1 && r.second != -1);
6002 assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
6003 assert(r.first <= r.second &&
6004 r.second < static_cast<ssize_t>(content_length));
6005 (void)(content_length);
6006 return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
6007}
6008
6009 inline std::string make_content_range_header_field(
6010 const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
6011 auto st = offset_and_length.first;
6012 auto ed = st + offset_and_length.second - 1;
6013
6014 std::string field = "bytes ";
6015 field += std::to_string(st);
6016 field += "-";
6017 field += std::to_string(ed);
6018 field += "/";
6019 field += std::to_string(content_length);
6020 return field;
6021}
6022
6023 template <typename SToken, typename CToken, typename Content>
6024 bool process_multipart_ranges_data(const Request &req,
6025 const std::string &boundary,
6026 const std::string &content_type,
6027 size_t content_length, SToken stoken,
6028 CToken ctoken, Content content) {
6029 for (size_t i = 0; i < req.ranges.size(); i++) {
6030 ctoken("--");
6031 stoken(boundary);
6032 ctoken("\r\n");
6033 if (!content_type.empty()) {
6034 ctoken("Content-Type: ");
6035 stoken(content_type);
6036 ctoken("\r\n");
6037 }
6038
6039 auto offset_and_length =
6040 get_range_offset_and_length(req.ranges[i], content_length);
6041
6042 ctoken("Content-Range: ");
6043 stoken(make_content_range_header_field(offset_and_length, content_length));
6044 ctoken("\r\n");
6045 ctoken("\r\n");
6046
6047 if (!content(offset_and_length.first, offset_and_length.second)) {
6048 return false;
6049 }
6050 ctoken("\r\n");
6051 }
6052
6053 ctoken("--");
6054 stoken(boundary);
6055 ctoken("--");
6056
6057 return true;
6058}
6059
6060 inline void make_multipart_ranges_data(const Request &req, Response &res,
6061 const std::string &boundary,
6062 const std::string &content_type,
6063 size_t content_length,
6064 std::string &data) {
6065 process_multipart_ranges_data(
6066 req, boundary, content_type, content_length,
6067 [&](const std::string &token) { data += token; },
6068 [&](const std::string &token) { data += token; },
6069 [&](size_t offset, size_t length) {
6070 assert(offset + length <= content_length);
6071 data += res.body.substr(offset, length);
6072 return true;
6073 });
6074}
6075
6076 inline size_t get_multipart_ranges_data_length(const Request &req,
6077 const std::string &boundary,
6078 const std::string &content_type,
6079 size_t content_length) {
6080 size_t data_length = 0;
6081
6082 process_multipart_ranges_data(
6083 req, boundary, content_type, content_length,
6084 [&](const std::string &token) { data_length += token.size(); },
6085 [&](const std::string &token) { data_length += token.size(); },
6086 [&](size_t /*offset*/, size_t length) {
6087 data_length += length;
6088 return true;
6089 });
6090
6091 return data_length;
6092}
6093
6094 template <typename T>
6095 inline bool
6096write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
6097 const std::string &boundary,
6098 const std::string &content_type,
6099 size_t content_length, const T &is_shutting_down) {
6100 return process_multipart_ranges_data(
6101 req, boundary, content_type, content_length,
6102 [&](const std::string &token) { strm.write(token); },
6103 [&](const std::string &token) { strm.write(token); },
6104 [&](size_t offset, size_t length) {
6105 return write_content(strm, res.content_provider_, offset, length,
6106 is_shutting_down);
6107 });
6108}
6109
6110 inline bool expect_content(const Request &req) {
6111 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
6112 req.method == "DELETE") {
6113 return true;
6114 }
6115 if (req.has_header("Content-Length") &&
6116 req.get_header_value_u64("Content-Length") > 0) {
6117 return true;
6118 }
6119 if (is_chunked_transfer_encoding(req.headers)) { return true; }
6120 return false;
6121}
6122
6123 inline bool has_crlf(const std::string &s) {
6124 auto p = s.c_str();
6125 while (*p) {
6126 if (*p == '\r' || *p == '\n') { return true; }
6127 p++;
6128 }
6129 return false;
6130}
6131
6132#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6133 inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
6134 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
6135 EVP_MD_CTX_new(), EVP_MD_CTX_free);
6136
6137 unsigned int hash_length = 0;
6138 unsigned char hash[EVP_MAX_MD_SIZE];
6139
6140 EVP_DigestInit_ex(context.get(), algo, nullptr);
6141 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
6142 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
6143
6144 std::stringstream ss;
6145 for (auto i = 0u; i < hash_length; ++i) {
6146 ss << std::hex << std::setw(2) << std::setfill('0')
6147 << static_cast<unsigned int>(hash[i]);
6148 }
6149
6150 return ss.str();
6151}
6152
6153 inline std::string MD5(const std::string &s) {
6154 return message_digest(s, EVP_md5());
6155}
6156
6157 inline std::string SHA_256(const std::string &s) {
6158 return message_digest(s, EVP_sha256());
6159}
6160
6161 inline std::string SHA_512(const std::string &s) {
6162 return message_digest(s, EVP_sha512());
6163}
6164
6165 inline std::pair<std::string, std::string> make_digest_authentication_header(
6166 const Request &req, const std::map<std::string, std::string> &auth,
6167 size_t cnonce_count, const std::string &cnonce, const std::string &username,
6168 const std::string &password, bool is_proxy = false) {
6169 std::string nc;
6170 {
6171 std::stringstream ss;
6172 ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
6173 nc = ss.str();
6174 }
6175
6176 std::string qop;
6177 if (auth.find("qop") != auth.end()) {
6178 qop = auth.at("qop");
6179 if (qop.find("auth-int") != std::string::npos) {
6180 qop = "auth-int";
6181 } else if (qop.find("auth") != std::string::npos) {
6182 qop = "auth";
6183 } else {
6184 qop.clear();
6185 }
6186 }
6187
6188 std::string algo = "MD5";
6189 if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
6190
6191 std::string response;
6192 {
6193 auto H = algo == "SHA-256" ? detail::SHA_256
6194 : algo == "SHA-512" ? detail::SHA_512
6195 : detail::MD5;
6196
6197 auto A1 = username + ":" + auth.at("realm") + ":" + password;
6198
6199 auto A2 = req.method + ":" + req.path;
6200 if (qop == "auth-int") { A2 += ":" + H(req.body); }
6201
6202 if (qop.empty()) {
6203 response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
6204 } else {
6205 response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
6206 ":" + qop + ":" + H(A2));
6207 }
6208 }
6209
6210 auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
6211
6212 auto field = "Digest username=\"" + username + "\", realm=\"" +
6213 auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
6214 "\", uri=\"" + req.path + "\", algorithm=" + algo +
6215 (qop.empty() ? ", response=\""
6216 : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
6217 cnonce + "\", response=\"") +
6218 response + "\"" +
6219 (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
6220
6221 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
6222 return std::make_pair(key, field);
6223}
6224
6225 inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
6226 detail::set_nonblocking(sock, true);
6227 auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
6228
6229 char buf[1];
6230 return !SSL_peek(ssl, buf, 1) &&
6231 SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
6232}
6233
6234#ifdef _WIN32
6235// NOTE: This code came up with the following stackoverflow post:
6236// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
6237 inline bool load_system_certs_on_windows(X509_STORE *store) {
6238 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
6239 if (!hStore) { return false; }
6240
6241 auto result = false;
6242 PCCERT_CONTEXT pContext = NULL;
6243 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
6244 nullptr) {
6245 auto encoded_cert =
6246 static_cast<const unsigned char *>(pContext->pbCertEncoded);
6247
6248 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
6249 if (x509) {
6250 X509_STORE_add_cert(store, x509);
6251 X509_free(x509);
6252 result = true;
6253 }
6254 }
6255
6256 CertFreeCertificateContext(pContext);
6257 CertCloseStore(hStore, 0);
6258
6259 return result;
6260}
6261#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
6262 template <typename T>
6263 using CFObjectPtr =
6264 std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
6265
6266 inline void cf_object_ptr_deleter(CFTypeRef obj) {
6267 if (obj) { CFRelease(obj); }
6268}
6269
6270 inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
6271 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
6272 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
6273 kCFBooleanTrue};
6274
6275 CFObjectPtr<CFDictionaryRef> query(
6276 CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
6277 sizeof(keys) / sizeof(keys[0]),
6278 &kCFTypeDictionaryKeyCallBacks,
6279 &kCFTypeDictionaryValueCallBacks),
6280 cf_object_ptr_deleter);
6281
6282 if (!query) { return false; }
6283
6284 CFTypeRef security_items = nullptr;
6285 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
6286 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
6287 return false;
6288 }
6289
6290 certs.reset(reinterpret_cast<CFArrayRef>(security_items));
6291 return true;
6292}
6293
6294 inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
6295 CFArrayRef root_security_items = nullptr;
6296 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
6297 return false;
6298 }
6299
6300 certs.reset(root_security_items);
6301 return true;
6302}
6303
6304 inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
6305 auto result = false;
6306 for (auto i = 0; i < CFArrayGetCount(certs); ++i) {
6307 const auto cert = reinterpret_cast<const __SecCertificate *>(
6308 CFArrayGetValueAtIndex(certs, i));
6309
6310 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
6311
6312 CFDataRef cert_data = nullptr;
6313 if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
6314 errSecSuccess) {
6315 continue;
6316 }
6317
6318 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
6319
6320 auto encoded_cert = static_cast<const unsigned char *>(
6321 CFDataGetBytePtr(cert_data_ptr.get()));
6322
6323 auto x509 =
6324 d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
6325
6326 if (x509) {
6327 X509_STORE_add_cert(store, x509);
6328 X509_free(x509);
6329 result = true;
6330 }
6331 }
6332
6333 return result;
6334}
6335
6336 inline bool load_system_certs_on_macos(X509_STORE *store) {
6337 auto result = false;
6338 CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
6339 if (retrieve_certs_from_keychain(certs) && certs) {
6340 result = add_certs_to_x509_store(certs.get(), store);
6341 }
6342
6343 if (retrieve_root_certs_from_keychain(certs) && certs) {
6344 result = add_certs_to_x509_store(certs.get(), store) || result;
6345 }
6346
6347 return result;
6348}
6349#endif // _WIN32
6350#endif // CPPHTTPLIB_OPENSSL_SUPPORT
6351
6352#ifdef _WIN32
6353 class WSInit {
6354 public:
6355 WSInit() {
6356 WSADATA wsaData;
6357 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
6358 }
6359
6360 ~WSInit() {
6361 if (is_valid_) WSACleanup();
6362 }
6363
6364 bool is_valid_ = false;
6365};
6366
6367 static WSInit wsinit_;
6368#endif
6369
6370 inline bool parse_www_authenticate(const Response &res,
6371 std::map<std::string, std::string> &auth,
6372 bool is_proxy) {
6373 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
6374 if (res.has_header(auth_key)) {
6375 thread_local auto re =
6376 std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
6377 auto s = res.get_header_value(auth_key);
6378 auto pos = s.find(' ');
6379 if (pos != std::string::npos) {
6380 auto type = s.substr(0, pos);
6381 if (type == "Basic") {
6382 return false;
6383 } else if (type == "Digest") {
6384 s = s.substr(pos + 1);
6385 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
6386 for (auto i = beg; i != std::sregex_iterator(); ++i) {
6387 const auto &m = *i;
6388 auto key = s.substr(static_cast<size_t>(m.position(1)),
6389 static_cast<size_t>(m.length(1)));
6390 auto val = m.length(2) > 0
6391 ? s.substr(static_cast<size_t>(m.position(2)),
6392 static_cast<size_t>(m.length(2)))
6393 : s.substr(static_cast<size_t>(m.position(3)),
6394 static_cast<size_t>(m.length(3)));
6395 auth[key] = val;
6396 }
6397 return true;
6398 }
6399 }
6400 }
6401 return false;
6402}
6403
6404 class ContentProviderAdapter {
6405 public:
6406 explicit ContentProviderAdapter(
6407 ContentProviderWithoutLength &&content_provider)
6408 : content_provider_(content_provider) {}
6409
6410 bool operator()(size_t offset, size_t, DataSink &sink) {
6411 return content_provider_(offset, sink);
6412 }
6413
6414 private:
6415 ContentProviderWithoutLength content_provider_;
6416};
6417
6418} // namespace detail
6419
6420 inline std::string hosted_at(const std::string &hostname) {
6421 std::vector<std::string> addrs;
6422 hosted_at(hostname, addrs);
6423 if (addrs.empty()) { return std::string(); }
6424 return addrs[0];
6425}
6426
6427 inline void hosted_at(const std::string &hostname,
6428 std::vector<std::string> &addrs) {
6429 struct addrinfo hints;
6430 struct addrinfo *result;
6431
6432 memset(&hints, 0, sizeof(struct addrinfo));
6433 hints.ai_family = AF_UNSPEC;
6434 hints.ai_socktype = SOCK_STREAM;
6435 hints.ai_protocol = 0;
6436
6437 if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
6438 &result, 0)) {
6439#if defined __linux__ && !defined __ANDROID__
6440 res_init();
6441#endif
6442 return;
6443 }
6444 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
6445
6446 for (auto rp = result; rp; rp = rp->ai_next) {
6447 const auto &addr =
6448 *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
6449 std::string ip;
6450 auto dummy = -1;
6451 if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
6452 dummy)) {
6453 addrs.push_back(ip);
6454 }
6455 }
6456}
6457
6458 inline std::string encode_uri_component(const std::string &value) {
6459 std::ostringstream escaped;
6460 escaped.fill('0');
6461 escaped << std::hex;
6462
6463 for (auto c : value) {
6464 if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
6465 c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
6466 c == ')') {
6467 escaped << c;
6468 } else {
6469 escaped << std::uppercase;
6470 escaped << '%' << std::setw(2)
6471 << static_cast<int>(static_cast<unsigned char>(c));
6472 escaped << std::nouppercase;
6473 }
6474 }
6475
6476 return escaped.str();
6477}
6478
6479 inline std::string encode_uri(const std::string &value) {
6480 std::ostringstream escaped;
6481 escaped.fill('0');
6482 escaped << std::hex;
6483
6484 for (auto c : value) {
6485 if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
6486 c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
6487 c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
6488 c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {
6489 escaped << c;
6490 } else {
6491 escaped << std::uppercase;
6492 escaped << '%' << std::setw(2)
6493 << static_cast<int>(static_cast<unsigned char>(c));
6494 escaped << std::nouppercase;
6495 }
6496 }
6497
6498 return escaped.str();
6499}
6500
6501 inline std::string decode_uri_component(const std::string &value) {
6502 std::string result;
6503
6504 for (size_t i = 0; i < value.size(); i++) {
6505 if (value[i] == '%' && i + 2 < value.size()) {
6506 auto val = 0;
6507 if (detail::from_hex_to_i(value, i + 1, 2, val)) {
6508 result += static_cast<char>(val);
6509 i += 2;
6510 } else {
6511 result += value[i];
6512 }
6513 } else {
6514 result += value[i];
6515 }
6516 }
6517
6518 return result;
6519}
6520
6521 inline std::string decode_uri(const std::string &value) {
6522 std::string result;
6523
6524 for (size_t i = 0; i < value.size(); i++) {
6525 if (value[i] == '%' && i + 2 < value.size()) {
6526 auto val = 0;
6527 if (detail::from_hex_to_i(value, i + 1, 2, val)) {
6528 result += static_cast<char>(val);
6529 i += 2;
6530 } else {
6531 result += value[i];
6532 }
6533 } else {
6534 result += value[i];
6535 }
6536 }
6537
6538 return result;
6539}
6540
6541 inline std::string encode_path_component(const std::string &component) {
6542 std::string result;
6543 result.reserve(component.size() * 3);
6544
6545 for (size_t i = 0; i < component.size(); i++) {
6546 auto c = static_cast<unsigned char>(component[i]);
6547
6548 // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
6549 if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
6550 result += static_cast<char>(c);
6551 }
6552 // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
6553 // "," / ";" / "="
6554 else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
6555 c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
6556 c == '=') {
6557 result += static_cast<char>(c);
6558 }
6559 // Colon is allowed in path segments except first segment
6560 else if (c == ':') {
6561 result += static_cast<char>(c);
6562 }
6563 // @ is allowed in path
6564 else if (c == '@') {
6565 result += static_cast<char>(c);
6566 } else {
6567 result += '%';
6568 char hex[3];
6569 snprintf(hex, sizeof(hex), "%02X", c);
6570 result.append(hex, 2);
6571 }
6572 }
6573 return result;
6574}
6575
6576 inline std::string decode_path_component(const std::string &component) {
6577 std::string result;
6578 result.reserve(component.size());
6579
6580 for (size_t i = 0; i < component.size(); i++) {
6581 if (component[i] == '%' && i + 1 < component.size()) {
6582 if (component[i + 1] == 'u') {
6583 // Unicode %uXXXX encoding
6584 auto val = 0;
6585 if (detail::from_hex_to_i(component, i + 2, 4, val)) {
6586 // 4 digits Unicode codes
6587 char buff[4];
6588 size_t len = detail::to_utf8(val, buff);
6589 if (len > 0) { result.append(buff, len); }
6590 i += 5; // 'u0000'
6591 } else {
6592 result += component[i];
6593 }
6594 } else {
6595 // Standard %XX encoding
6596 auto val = 0;
6597 if (detail::from_hex_to_i(component, i + 1, 2, val)) {
6598 // 2 digits hex codes
6599 result += static_cast<char>(val);
6600 i += 2; // 'XX'
6601 } else {
6602 result += component[i];
6603 }
6604 }
6605 } else {
6606 result += component[i];
6607 }
6608 }
6609 return result;
6610}
6611
6612 inline std::string encode_query_component(const std::string &component,
6613 bool space_as_plus) {
6614 std::string result;
6615 result.reserve(component.size() * 3);
6616
6617 for (size_t i = 0; i < component.size(); i++) {
6618 auto c = static_cast<unsigned char>(component[i]);
6619
6620 // Unreserved characters per RFC 3986
6621 if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
6622 result += static_cast<char>(c);
6623 }
6624 // Space handling
6625 else if (c == ' ') {
6626 if (space_as_plus) {
6627 result += '+';
6628 } else {
6629 result += "%20";
6630 }
6631 }
6632 // Plus sign handling
6633 else if (c == '+') {
6634 if (space_as_plus) {
6635 result += "%2B";
6636 } else {
6637 result += static_cast<char>(c);
6638 }
6639 }
6640 // Query-safe sub-delimiters (excluding & and = which are query delimiters)
6641 else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
6642 c == '*' || c == ',' || c == ';') {
6643 result += static_cast<char>(c);
6644 }
6645 // Colon and @ are allowed in query
6646 else if (c == ':' || c == '@') {
6647 result += static_cast<char>(c);
6648 }
6649 // Forward slash is allowed in query values
6650 else if (c == '/') {
6651 result += static_cast<char>(c);
6652 }
6653 // Question mark is allowed in query values (after first ?)
6654 else if (c == '?') {
6655 result += static_cast<char>(c);
6656 } else {
6657 result += '%';
6658 char hex[3];
6659 snprintf(hex, sizeof(hex), "%02X", c);
6660 result.append(hex, 2);
6661 }
6662 }
6663 return result;
6664}
6665
6666 inline std::string decode_query_component(const std::string &component,
6667 bool plus_as_space) {
6668 std::string result;
6669 result.reserve(component.size());
6670
6671 for (size_t i = 0; i < component.size(); i++) {
6672 if (component[i] == '%' && i + 2 < component.size()) {
6673 std::string hex = component.substr(i + 1, 2);
6674 char *end;
6675 unsigned long value = std::strtoul(hex.c_str(), &end, 16);
6676 if (end == hex.c_str() + 2) {
6677 result += static_cast<char>(value);
6678 i += 2;
6679 } else {
6680 result += component[i];
6681 }
6682 } else if (component[i] == '+' && plus_as_space) {
6683 result += ' '; // + becomes space in form-urlencoded
6684 } else {
6685 result += component[i];
6686 }
6687 }
6688 return result;
6689}
6690
6691 inline std::string append_query_params(const std::string &path,
6692 const Params &params) {
6693 std::string path_with_query = path;
6694 thread_local const std::regex re("[^?]+\\?.*");
6695 auto delm = std::regex_match(path, re) ? '&' : '?';
6696 path_with_query += delm + detail::params_to_query_str(params);
6697 return path_with_query;
6698}
6699
6700// Header utilities
6701 inline std::pair<std::string, std::string>
6702 make_range_header(const Ranges &ranges) {
6703 std::string field = "bytes=";
6704 auto i = 0;
6705 for (const auto &r : ranges) {
6706 if (i != 0) { field += ", "; }
6707 if (r.first != -1) { field += std::to_string(r.first); }
6708 field += '-';
6709 if (r.second != -1) { field += std::to_string(r.second); }
6710 i++;
6711 }
6712 return std::make_pair("Range", std::move(field));
6713}
6714
6715 inline std::pair<std::string, std::string>
6716 make_basic_authentication_header(const std::string &username,
6717 const std::string &password, bool is_proxy) {
6718 auto field = "Basic " + detail::base64_encode(username + ":" + password);
6719 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
6720 return std::make_pair(key, std::move(field));
6721}
6722
6723 inline std::pair<std::string, std::string>
6724 make_bearer_token_authentication_header(const std::string &token,
6725 bool is_proxy = false) {
6726 auto field = "Bearer " + token;
6727 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
6728 return std::make_pair(key, std::move(field));
6729}
6730
6731// Request implementation
6732 inline bool Request::has_header(const std::string &key) const {
6733 return detail::has_header(headers, key);
6734}
6735
6736 inline std::string Request::get_header_value(const std::string &key,
6737 const char *def, size_t id) const {
6738 return detail::get_header_value(headers, key, def, id);
6739}
6740
6741 inline size_t Request::get_header_value_count(const std::string &key) const {
6742 auto r = headers.equal_range(key);
6743 return static_cast<size_t>(std::distance(r.first, r.second));
6744}
6745
6746 inline void Request::set_header(const std::string &key,
6747 const std::string &val) {
6748 if (detail::fields::is_field_name(key) &&
6749 detail::fields::is_field_value(val)) {
6750 headers.emplace(key, val);
6751 }
6752}
6753
6754 inline bool Request::has_trailer(const std::string &key) const {
6755 return trailers.find(key) != trailers.end();
6756}
6757
6758 inline std::string Request::get_trailer_value(const std::string &key,
6759 size_t id) const {
6760 auto rng = trailers.equal_range(key);
6761 auto it = rng.first;
6762 std::advance(it, static_cast<ssize_t>(id));
6763 if (it != rng.second) { return it->second; }
6764 return std::string();
6765}
6766
6767 inline size_t Request::get_trailer_value_count(const std::string &key) const {
6768 auto r = trailers.equal_range(key);
6769 return static_cast<size_t>(std::distance(r.first, r.second));
6770}
6771
6772 inline bool Request::has_param(const std::string &key) const {
6773 return params.find(key) != params.end();
6774}
6775
6776 inline std::string Request::get_param_value(const std::string &key,
6777 size_t id) const {
6778 auto rng = params.equal_range(key);
6779 auto it = rng.first;
6780 std::advance(it, static_cast<ssize_t>(id));
6781 if (it != rng.second) { return it->second; }
6782 return std::string();
6783}
6784
6785 inline size_t Request::get_param_value_count(const std::string &key) const {
6786 auto r = params.equal_range(key);
6787 return static_cast<size_t>(std::distance(r.first, r.second));
6788}
6789
6790 inline bool Request::is_multipart_form_data() const {
6791 const auto &content_type = get_header_value("Content-Type");
6792 return !content_type.rfind("multipart/form-data", 0);
6793}
6794
6795// Multipart FormData implementation
6796 inline std::string MultipartFormData::get_field(const std::string &key,
6797 size_t id) const {
6798 auto rng = fields.equal_range(key);
6799 auto it = rng.first;
6800 std::advance(it, static_cast<ssize_t>(id));
6801 if (it != rng.second) { return it->second.content; }
6802 return std::string();
6803}
6804
6805 inline std::vector<std::string>
6806MultipartFormData::get_fields(const std::string &key) const {
6807 std::vector<std::string> values;
6808 auto rng = fields.equal_range(key);
6809 for (auto it = rng.first; it != rng.second; it++) {
6810 values.push_back(it->second.content);
6811 }
6812 return values;
6813}
6814
6815 inline bool MultipartFormData::has_field(const std::string &key) const {
6816 return fields.find(key) != fields.end();
6817}
6818
6819 inline size_t MultipartFormData::get_field_count(const std::string &key) const {
6820 auto r = fields.equal_range(key);
6821 return static_cast<size_t>(std::distance(r.first, r.second));
6822}
6823
6824 inline FormData MultipartFormData::get_file(const std::string &key,
6825 size_t id) const {
6826 auto rng = files.equal_range(key);
6827 auto it = rng.first;
6828 std::advance(it, static_cast<ssize_t>(id));
6829 if (it != rng.second) { return it->second; }
6830 return FormData();
6831}
6832
6833 inline std::vector<FormData>
6834MultipartFormData::get_files(const std::string &key) const {
6835 std::vector<FormData> values;
6836 auto rng = files.equal_range(key);
6837 for (auto it = rng.first; it != rng.second; it++) {
6838 values.push_back(it->second);
6839 }
6840 return values;
6841}
6842
6843 inline bool MultipartFormData::has_file(const std::string &key) const {
6844 return files.find(key) != files.end();
6845}
6846
6847 inline size_t MultipartFormData::get_file_count(const std::string &key) const {
6848 auto r = files.equal_range(key);
6849 return static_cast<size_t>(std::distance(r.first, r.second));
6850}
6851
6852// Response implementation
6853 inline bool Response::has_header(const std::string &key) const {
6854 return headers.find(key) != headers.end();
6855}
6856
6857 inline std::string Response::get_header_value(const std::string &key,
6858 const char *def,
6859 size_t id) const {
6860 return detail::get_header_value(headers, key, def, id);
6861}
6862
6863 inline size_t Response::get_header_value_count(const std::string &key) const {
6864 auto r = headers.equal_range(key);
6865 return static_cast<size_t>(std::distance(r.first, r.second));
6866}
6867
6868 inline void Response::set_header(const std::string &key,
6869 const std::string &val) {
6870 if (detail::fields::is_field_name(key) &&
6871 detail::fields::is_field_value(val)) {
6872 headers.emplace(key, val);
6873 }
6874}
6875 inline bool Response::has_trailer(const std::string &key) const {
6876 return trailers.find(key) != trailers.end();
6877}
6878
6879 inline std::string Response::get_trailer_value(const std::string &key,
6880 size_t id) const {
6881 auto rng = trailers.equal_range(key);
6882 auto it = rng.first;
6883 std::advance(it, static_cast<ssize_t>(id));
6884 if (it != rng.second) { return it->second; }
6885 return std::string();
6886}
6887
6888 inline size_t Response::get_trailer_value_count(const std::string &key) const {
6889 auto r = trailers.equal_range(key);
6890 return static_cast<size_t>(std::distance(r.first, r.second));
6891}
6892
6893 inline void Response::set_redirect(const std::string &url, int stat) {
6894 if (detail::fields::is_field_value(url)) {
6895 set_header("Location", url);
6896 if (300 <= stat && stat < 400) {
6897 this->status = stat;
6898 } else {
6899 this->status = StatusCode::Found_302;
6900 }
6901 }
6902}
6903
6904 inline void Response::set_content(const char *s, size_t n,
6905 const std::string &content_type) {
6906 body.assign(s, n);
6907
6908 auto rng = headers.equal_range("Content-Type");
6909 headers.erase(rng.first, rng.second);
6910 set_header("Content-Type", content_type);
6911}
6912
6913 inline void Response::set_content(const std::string &s,
6914 const std::string &content_type) {
6915 set_content(s.data(), s.size(), content_type);
6916}
6917
6918 inline void Response::set_content(std::string &&s,
6919 const std::string &content_type) {
6920 body = std::move(s);
6921
6922 auto rng = headers.equal_range("Content-Type");
6923 headers.erase(rng.first, rng.second);
6924 set_header("Content-Type", content_type);
6925}
6926
6927 inline void Response::set_content_provider(
6928 size_t in_length, const std::string &content_type, ContentProvider provider,
6929 ContentProviderResourceReleaser resource_releaser) {
6930 set_header("Content-Type", content_type);
6931 content_length_ = in_length;
6932 if (in_length > 0) { content_provider_ = std::move(provider); }
6933 content_provider_resource_releaser_ = std::move(resource_releaser);
6934 is_chunked_content_provider_ = false;
6935}
6936
6937 inline void Response::set_content_provider(
6938 const std::string &content_type, ContentProviderWithoutLength provider,
6939 ContentProviderResourceReleaser resource_releaser) {
6940 set_header("Content-Type", content_type);
6941 content_length_ = 0;
6942 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
6943 content_provider_resource_releaser_ = std::move(resource_releaser);
6944 is_chunked_content_provider_ = false;
6945}
6946
6947 inline void Response::set_chunked_content_provider(
6948 const std::string &content_type, ContentProviderWithoutLength provider,
6949 ContentProviderResourceReleaser resource_releaser) {
6950 set_header("Content-Type", content_type);
6951 content_length_ = 0;
6952 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
6953 content_provider_resource_releaser_ = std::move(resource_releaser);
6954 is_chunked_content_provider_ = true;
6955}
6956
6957 inline void Response::set_file_content(const std::string &path,
6958 const std::string &content_type) {
6959 file_content_path_ = path;
6960 file_content_content_type_ = content_type;
6961}
6962
6963 inline void Response::set_file_content(const std::string &path) {
6964 file_content_path_ = path;
6965}
6966
6967// Result implementation
6968 inline bool Result::has_request_header(const std::string &key) const {
6969 return request_headers_.find(key) != request_headers_.end();
6970}
6971
6972 inline std::string Result::get_request_header_value(const std::string &key,
6973 const char *def,
6974 size_t id) const {
6975 return detail::get_header_value(request_headers_, key, def, id);
6976}
6977
6978 inline size_t
6979Result::get_request_header_value_count(const std::string &key) const {
6980 auto r = request_headers_.equal_range(key);
6981 return static_cast<size_t>(std::distance(r.first, r.second));
6982}
6983
6984// Stream implementation
6985 inline ssize_t Stream::write(const char *ptr) {
6986 return write(ptr, strlen(ptr));
6987}
6988
6989 inline ssize_t Stream::write(const std::string &s) {
6990 return write(s.data(), s.size());
6991}
6992
6993 namespace detail {
6994
6995 inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
6996 time_t timeout_sec, time_t timeout_usec,
6997 time_t &actual_timeout_sec,
6998 time_t &actual_timeout_usec) {
6999 auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
7000
7001 auto actual_timeout_msec =
7002 (std::min)(max_timeout_msec - duration_msec, timeout_msec);
7003
7004 if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
7005
7006 actual_timeout_sec = actual_timeout_msec / 1000;
7007 actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
7008}
7009
7010// Socket stream implementation
7011 inline SocketStream::SocketStream(
7012 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
7013 time_t write_timeout_sec, time_t write_timeout_usec,
7014 time_t max_timeout_msec,
7015 std::chrono::time_point<std::chrono::steady_clock> start_time)
7016 : sock_(sock), read_timeout_sec_(read_timeout_sec),
7017 read_timeout_usec_(read_timeout_usec),
7018 write_timeout_sec_(write_timeout_sec),
7019 write_timeout_usec_(write_timeout_usec),
7020 max_timeout_msec_(max_timeout_msec), start_time_(start_time),
7021 read_buff_(read_buff_size_, 0) {}
7022
7023 inline SocketStream::~SocketStream() = default;
7024
7025 inline bool SocketStream::is_readable() const {
7026 return read_buff_off_ < read_buff_content_size_;
7027}
7028
7029 inline bool SocketStream::wait_readable() const {
7030 if (max_timeout_msec_ <= 0) {
7031 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
7032 }
7033
7034 time_t read_timeout_sec;
7035 time_t read_timeout_usec;
7036 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
7037 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
7038
7039 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
7040}
7041
7042 inline bool SocketStream::wait_writable() const {
7043 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
7044 is_socket_alive(sock_);
7045}
7046
7047 inline ssize_t SocketStream::read(char *ptr, size_t size) {
7048#ifdef _WIN32
7049 size =
7050 (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
7051#else
7052 size = (std::min)(size,
7053 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
7054#endif
7055
7056 if (read_buff_off_ < read_buff_content_size_) {
7057 auto remaining_size = read_buff_content_size_ - read_buff_off_;
7058 if (size <= remaining_size) {
7059 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
7060 read_buff_off_ += size;
7061 return static_cast<ssize_t>(size);
7062 } else {
7063 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
7064 read_buff_off_ += remaining_size;
7065 return static_cast<ssize_t>(remaining_size);
7066 }
7067 }
7068
7069 if (!wait_readable()) { return -1; }
7070
7071 read_buff_off_ = 0;
7072 read_buff_content_size_ = 0;
7073
7074 if (size < read_buff_size_) {
7075 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
7076 CPPHTTPLIB_RECV_FLAGS);
7077 if (n <= 0) {
7078 return n;
7079 } else if (n <= static_cast<ssize_t>(size)) {
7080 memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
7081 return n;
7082 } else {
7083 memcpy(ptr, read_buff_.data(), size);
7084 read_buff_off_ = size;
7085 read_buff_content_size_ = static_cast<size_t>(n);
7086 return static_cast<ssize_t>(size);
7087 }
7088 } else {
7089 return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
7090 }
7091}
7092
7093 inline ssize_t SocketStream::write(const char *ptr, size_t size) {
7094 if (!wait_writable()) { return -1; }
7095
7096#if defined(_WIN32) && !defined(_WIN64)
7097 size =
7098 (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
7099#endif
7100
7101 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
7102}
7103
7104 inline void SocketStream::get_remote_ip_and_port(std::string &ip,
7105 int &port) const {
7106 return detail::get_remote_ip_and_port(sock_, ip, port);
7107}
7108
7109 inline void SocketStream::get_local_ip_and_port(std::string &ip,
7110 int &port) const {
7111 return detail::get_local_ip_and_port(sock_, ip, port);
7112}
7113
7114 inline socket_t SocketStream::socket() const { return sock_; }
7115
7116 inline time_t SocketStream::duration() const {
7117 return std::chrono::duration_cast<std::chrono::milliseconds>(
7118 std::chrono::steady_clock::now() - start_time_)
7119 .count();
7120}
7121
7122// Buffer stream implementation
7123 inline bool BufferStream::is_readable() const { return true; }
7124
7125 inline bool BufferStream::wait_readable() const { return true; }
7126
7127 inline bool BufferStream::wait_writable() const { return true; }
7128
7129 inline ssize_t BufferStream::read(char *ptr, size_t size) {
7130#if defined(_MSC_VER) && _MSC_VER < 1910
7131 auto len_read = buffer._Copy_s(ptr, size, size, position);
7132#else
7133 auto len_read = buffer.copy(ptr, size, position);
7134#endif
7135 position += static_cast<size_t>(len_read);
7136 return static_cast<ssize_t>(len_read);
7137}
7138
7139 inline ssize_t BufferStream::write(const char *ptr, size_t size) {
7140 buffer.append(ptr, size);
7141 return static_cast<ssize_t>(size);
7142}
7143
7144 inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
7145 int & /*port*/) const {}
7146
7147 inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
7148 int & /*port*/) const {}
7149
7150 inline socket_t BufferStream::socket() const { return 0; }
7151
7152 inline time_t BufferStream::duration() const { return 0; }
7153
7154 inline const std::string &BufferStream::get_buffer() const { return buffer; }
7155
7156 inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
7157 : MatcherBase(pattern) {
7158 constexpr const char marker[] = "/:";
7159
7160 // One past the last ending position of a path param substring
7161 std::size_t last_param_end = 0;
7162
7163#ifndef CPPHTTPLIB_NO_EXCEPTIONS
7164 // Needed to ensure that parameter names are unique during matcher
7165 // construction
7166 // If exceptions are disabled, only last duplicate path
7167 // parameter will be set
7168 std::unordered_set<std::string> param_name_set;
7169#endif
7170
7171 while (true) {
7172 const auto marker_pos = pattern.find(
7173 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
7174 if (marker_pos == std::string::npos) { break; }
7175
7176 static_fragments_.push_back(
7177 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
7178
7179 const auto param_name_start = marker_pos + str_len(marker);
7180
7181 auto sep_pos = pattern.find(separator, param_name_start);
7182 if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
7183
7184 auto param_name =
7185 pattern.substr(param_name_start, sep_pos - param_name_start);
7186
7187#ifndef CPPHTTPLIB_NO_EXCEPTIONS
7188 if (param_name_set.find(param_name) != param_name_set.cend()) {
7189 std::string msg = "Encountered path parameter '" + param_name +
7190 "' multiple times in route pattern '" + pattern + "'.";
7191 throw std::invalid_argument(msg);
7192 }
7193#endif
7194
7195 param_names_.push_back(std::move(param_name));
7196
7197 last_param_end = sep_pos + 1;
7198 }
7199
7200 if (last_param_end < pattern.length()) {
7201 static_fragments_.push_back(pattern.substr(last_param_end));
7202 }
7203}
7204
7205 inline bool PathParamsMatcher::match(Request &request) const {
7206 request.matches = std::smatch();
7207 request.path_params.clear();
7208 request.path_params.reserve(param_names_.size());
7209
7210 // One past the position at which the path matched the pattern last time
7211 std::size_t starting_pos = 0;
7212 for (size_t i = 0; i < static_fragments_.size(); ++i) {
7213 const auto &fragment = static_fragments_[i];
7214
7215 if (starting_pos + fragment.length() > request.path.length()) {
7216 return false;
7217 }
7218
7219 // Avoid unnecessary allocation by using strncmp instead of substr +
7220 // comparison
7221 if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
7222 fragment.length()) != 0) {
7223 return false;
7224 }
7225
7226 starting_pos += fragment.length();
7227
7228 // Should only happen when we have a static fragment after a param
7229 // Example: '/users/:id/subscriptions'
7230 // The 'subscriptions' fragment here does not have a corresponding param
7231 if (i >= param_names_.size()) { continue; }
7232
7233 auto sep_pos = request.path.find(separator, starting_pos);
7234 if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
7235
7236 const auto &param_name = param_names_[i];
7237
7238 request.path_params.emplace(
7239 param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
7240
7241 // Mark everything up to '/' as matched
7242 starting_pos = sep_pos + 1;
7243 }
7244 // Returns false if the path is longer than the pattern
7245 return starting_pos >= request.path.length();
7246}
7247
7248 inline bool RegexMatcher::match(Request &request) const {
7249 request.path_params.clear();
7250 return std::regex_match(request.path, request.matches, regex_);
7251}
7252
7253} // namespace detail
7254
7255// HTTP server implementation
7256 inline Server::Server()
7257 : new_task_queue(
7258 [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
7259#ifndef _WIN32
7260 signal(SIGPIPE, SIG_IGN);
7261#endif
7262}
7263
7264 inline Server::~Server() = default;
7265
7266 inline std::unique_ptr<detail::MatcherBase>
7267Server::make_matcher(const std::string &pattern) {
7268 if (pattern.find("/:") != std::string::npos) {
7269 return detail::make_unique<detail::PathParamsMatcher>(pattern);
7270 } else {
7271 return detail::make_unique<detail::RegexMatcher>(pattern);
7272 }
7273}
7274
7275 inline Server &Server::Get(const std::string &pattern, Handler handler) {
7276 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
7277 return *this;
7278}
7279
7280 inline Server &Server::Post(const std::string &pattern, Handler handler) {
7281 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
7282 return *this;
7283}
7284
7285 inline Server &Server::Post(const std::string &pattern,
7286 HandlerWithContentReader handler) {
7287 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
7288 std::move(handler));
7289 return *this;
7290}
7291
7292 inline Server &Server::Put(const std::string &pattern, Handler handler) {
7293 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
7294 return *this;
7295}
7296
7297 inline Server &Server::Put(const std::string &pattern,
7298 HandlerWithContentReader handler) {
7299 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
7300 std::move(handler));
7301 return *this;
7302}
7303
7304 inline Server &Server::Patch(const std::string &pattern, Handler handler) {
7305 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
7306 return *this;
7307}
7308
7309 inline Server &Server::Patch(const std::string &pattern,
7310 HandlerWithContentReader handler) {
7311 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
7312 std::move(handler));
7313 return *this;
7314}
7315
7316 inline Server &Server::Delete(const std::string &pattern, Handler handler) {
7317 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
7318 return *this;
7319}
7320
7321 inline Server &Server::Delete(const std::string &pattern,
7322 HandlerWithContentReader handler) {
7323 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
7324 std::move(handler));
7325 return *this;
7326}
7327
7328 inline Server &Server::Options(const std::string &pattern, Handler handler) {
7329 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
7330 return *this;
7331}
7332
7333 inline bool Server::set_base_dir(const std::string &dir,
7334 const std::string &mount_point) {
7335 return set_mount_point(mount_point, dir);
7336}
7337
7338 inline bool Server::set_mount_point(const std::string &mount_point,
7339 const std::string &dir, Headers headers) {
7340 detail::FileStat stat(dir);
7341 if (stat.is_dir()) {
7342 std::string mnt = !mount_point.empty() ? mount_point : "/";
7343 if (!mnt.empty() && mnt[0] == '/') {
7344 base_dirs_.push_back({mnt, dir, std::move(headers)});
7345 return true;
7346 }
7347 }
7348 return false;
7349}
7350
7351 inline bool Server::remove_mount_point(const std::string &mount_point) {
7352 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
7353 if (it->mount_point == mount_point) {
7354 base_dirs_.erase(it);
7355 return true;
7356 }
7357 }
7358 return false;
7359}
7360
7361 inline Server &
7362 Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
7363 const std::string &mime) {
7364 file_extension_and_mimetype_map_[ext] = mime;
7365 return *this;
7366}
7367
7368 inline Server &Server::set_default_file_mimetype(const std::string &mime) {
7369 default_file_mimetype_ = mime;
7370 return *this;
7371}
7372
7373 inline Server &Server::set_file_request_handler(Handler handler) {
7374 file_request_handler_ = std::move(handler);
7375 return *this;
7376}
7377
7378 inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
7379 std::true_type) {
7380 error_handler_ = std::move(handler);
7381 return *this;
7382}
7383
7384 inline Server &Server::set_error_handler_core(Handler handler,
7385 std::false_type) {
7386 error_handler_ = [handler](const Request &req, Response &res) {
7387 handler(req, res);
7388 return HandlerResponse::Handled;
7389 };
7390 return *this;
7391}
7392
7393 inline Server &Server::set_exception_handler(ExceptionHandler handler) {
7394 exception_handler_ = std::move(handler);
7395 return *this;
7396}
7397
7398 inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
7399 pre_routing_handler_ = std::move(handler);
7400 return *this;
7401}
7402
7403 inline Server &Server::set_post_routing_handler(Handler handler) {
7404 post_routing_handler_ = std::move(handler);
7405 return *this;
7406}
7407
7408 inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
7409 pre_request_handler_ = std::move(handler);
7410 return *this;
7411}
7412
7413 inline Server &Server::set_logger(Logger logger) {
7414 logger_ = std::move(logger);
7415 return *this;
7416}
7417
7418 inline Server &Server::set_error_logger(ErrorLogger error_logger) {
7419 error_logger_ = std::move(error_logger);
7420 return *this;
7421}
7422
7423 inline Server &Server::set_pre_compression_logger(Logger logger) {
7424 pre_compression_logger_ = std::move(logger);
7425 return *this;
7426}
7427
7428 inline Server &
7429 Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
7430 expect_100_continue_handler_ = std::move(handler);
7431 return *this;
7432}
7433
7434 inline Server &Server::set_address_family(int family) {
7435 address_family_ = family;
7436 return *this;
7437}
7438
7439 inline Server &Server::set_tcp_nodelay(bool on) {
7440 tcp_nodelay_ = on;
7441 return *this;
7442}
7443
7444 inline Server &Server::set_ipv6_v6only(bool on) {
7445 ipv6_v6only_ = on;
7446 return *this;
7447}
7448
7449 inline Server &Server::set_socket_options(SocketOptions socket_options) {
7450 socket_options_ = std::move(socket_options);
7451 return *this;
7452}
7453
7454 inline Server &Server::set_default_headers(Headers headers) {
7455 default_headers_ = std::move(headers);
7456 return *this;
7457}
7458
7459 inline Server &Server::set_header_writer(
7460 std::function<ssize_t(Stream &, Headers &)> const &writer) {
7461 header_writer_ = writer;
7462 return *this;
7463}
7464
7465 inline Server &Server::set_keep_alive_max_count(size_t count) {
7466 keep_alive_max_count_ = count;
7467 return *this;
7468}
7469
7470 inline Server &Server::set_keep_alive_timeout(time_t sec) {
7471 keep_alive_timeout_sec_ = sec;
7472 return *this;
7473}
7474
7475 inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
7476 read_timeout_sec_ = sec;
7477 read_timeout_usec_ = usec;
7478 return *this;
7479}
7480
7481 inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
7482 write_timeout_sec_ = sec;
7483 write_timeout_usec_ = usec;
7484 return *this;
7485}
7486
7487 inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
7488 idle_interval_sec_ = sec;
7489 idle_interval_usec_ = usec;
7490 return *this;
7491}
7492
7493 inline Server &Server::set_payload_max_length(size_t length) {
7494 payload_max_length_ = length;
7495 return *this;
7496}
7497
7498 inline bool Server::bind_to_port(const std::string &host, int port,
7499 int socket_flags) {
7500 auto ret = bind_internal(host, port, socket_flags);
7501 if (ret == -1) { is_decommissioned = true; }
7502 return ret >= 0;
7503}
7504 inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
7505 auto ret = bind_internal(host, 0, socket_flags);
7506 if (ret == -1) { is_decommissioned = true; }
7507 return ret;
7508}
7509
7510 inline bool Server::listen_after_bind() { return listen_internal(); }
7511
7512 inline bool Server::listen(const std::string &host, int port,
7513 int socket_flags) {
7514 return bind_to_port(host, port, socket_flags) && listen_internal();
7515}
7516
7517 inline bool Server::is_running() const { return is_running_; }
7518
7519 inline void Server::wait_until_ready() const {
7520 while (!is_running_ && !is_decommissioned) {
7521 std::this_thread::sleep_for(std::chrono::milliseconds{1});
7522 }
7523}
7524
7525 inline void Server::stop() {
7526 if (is_running_) {
7527 assert(svr_sock_ != INVALID_SOCKET);
7528 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
7529 detail::shutdown_socket(sock);
7530 detail::close_socket(sock);
7531 }
7532 is_decommissioned = false;
7533}
7534
7535 inline void Server::decommission() { is_decommissioned = true; }
7536
7537 inline bool Server::parse_request_line(const char *s, Request &req) const {
7538 auto len = strlen(s);
7539 if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
7540 len -= 2;
7541
7542 {
7543 size_t count = 0;
7544
7545 detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
7546 switch (count) {
7547 case 0: req.method = std::string(b, e); break;
7548 case 1: req.target = std::string(b, e); break;
7549 case 2: req.version = std::string(b, e); break;
7550 default: break;
7551 }
7552 count++;
7553 });
7554
7555 if (count != 3) { return false; }
7556 }
7557
7558 thread_local const std::set<std::string> methods{
7559 "GET", "HEAD", "POST", "PUT", "DELETE",
7560 "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
7561
7562 if (methods.find(req.method) == methods.end()) {
7563 output_error_log(Error::InvalidHTTPMethod, &req);
7564 return false;
7565 }
7566
7567 if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
7568 output_error_log(Error::InvalidHTTPVersion, &req);
7569 return false;
7570 }
7571
7572 {
7573 // Skip URL fragment
7574 for (size_t i = 0; i < req.target.size(); i++) {
7575 if (req.target[i] == '#') {
7576 req.target.erase(i);
7577 break;
7578 }
7579 }
7580
7581 detail::divide(req.target, '?',
7582 [&](const char *lhs_data, std::size_t lhs_size,
7583 const char *rhs_data, std::size_t rhs_size) {
7584 req.path =
7585 decode_path_component(std::string(lhs_data, lhs_size));
7586 detail::parse_query_text(rhs_data, rhs_size, req.params);
7587 });
7588 }
7589
7590 return true;
7591}
7592
7593 inline bool Server::write_response(Stream &strm, bool close_connection,
7594 Request &req, Response &res) {
7595 // NOTE: `req.ranges` should be empty, otherwise it will be applied
7596 // incorrectly to the error content.
7597 req.ranges.clear();
7598 return write_response_core(strm, close_connection, req, res, false);
7599}
7600
7601 inline bool Server::write_response_with_content(Stream &strm,
7602 bool close_connection,
7603 const Request &req,
7604 Response &res) {
7605 return write_response_core(strm, close_connection, req, res, true);
7606}
7607
7608 inline bool Server::write_response_core(Stream &strm, bool close_connection,
7609 const Request &req, Response &res,
7610 bool need_apply_ranges) {
7611 assert(res.status != -1);
7612
7613 if (400 <= res.status && error_handler_ &&
7614 error_handler_(req, res) == HandlerResponse::Handled) {
7615 need_apply_ranges = true;
7616 }
7617
7618 std::string content_type;
7619 std::string boundary;
7620 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
7621
7622 // Prepare additional headers
7623 if (close_connection || req.get_header_value("Connection") == "close") {
7624 res.set_header("Connection", "close");
7625 } else {
7626 std::string s = "timeout=";
7627 s += std::to_string(keep_alive_timeout_sec_);
7628 s += ", max=";
7629 s += std::to_string(keep_alive_max_count_);
7630 res.set_header("Keep-Alive", s);
7631 }
7632
7633 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
7634 !res.has_header("Content-Type")) {
7635 res.set_header("Content-Type", "text/plain");
7636 }
7637
7638 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
7639 !res.has_header("Content-Length")) {
7640 res.set_header("Content-Length", "0");
7641 }
7642
7643 if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
7644 res.set_header("Accept-Ranges", "bytes");
7645 }
7646
7647 if (post_routing_handler_) { post_routing_handler_(req, res); }
7648
7649 // Response line and headers
7650 {
7651 detail::BufferStream bstrm;
7652 if (!detail::write_response_line(bstrm, res.status)) { return false; }
7653 if (!header_writer_(bstrm, res.headers)) { return false; }
7654
7655 // Flush buffer
7656 auto &data = bstrm.get_buffer();
7657 detail::write_data(strm, data.data(), data.size());
7658 }
7659
7660 // Body
7661 auto ret = true;
7662 if (req.method != "HEAD") {
7663 if (!res.body.empty()) {
7664 if (!detail::write_data(strm, res.body.data(), res.body.size())) {
7665 ret = false;
7666 }
7667 } else if (res.content_provider_) {
7668 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
7669 res.content_provider_success_ = true;
7670 } else {
7671 ret = false;
7672 }
7673 }
7674 }
7675
7676 // Log
7677 output_log(req, res);
7678
7679 return ret;
7680}
7681
7682 inline bool
7683Server::write_content_with_provider(Stream &strm, const Request &req,
7684 Response &res, const std::string &boundary,
7685 const std::string &content_type) {
7686 auto is_shutting_down = [this]() {
7687 return this->svr_sock_ == INVALID_SOCKET;
7688 };
7689
7690 if (res.content_length_ > 0) {
7691 if (req.ranges.empty()) {
7692 return detail::write_content(strm, res.content_provider_, 0,
7693 res.content_length_, is_shutting_down);
7694 } else if (req.ranges.size() == 1) {
7695 auto offset_and_length = detail::get_range_offset_and_length(
7696 req.ranges[0], res.content_length_);
7697
7698 return detail::write_content(strm, res.content_provider_,
7699 offset_and_length.first,
7700 offset_and_length.second, is_shutting_down);
7701 } else {
7702 return detail::write_multipart_ranges_data(
7703 strm, req, res, boundary, content_type, res.content_length_,
7704 is_shutting_down);
7705 }
7706 } else {
7707 if (res.is_chunked_content_provider_) {
7708 auto type = detail::encoding_type(req, res);
7709
7710 std::unique_ptr<detail::compressor> compressor;
7711 if (type == detail::EncodingType::Gzip) {
7712#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7713 compressor = detail::make_unique<detail::gzip_compressor>();
7714#endif
7715 } else if (type == detail::EncodingType::Brotli) {
7716#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7717 compressor = detail::make_unique<detail::brotli_compressor>();
7718#endif
7719 } else if (type == detail::EncodingType::Zstd) {
7720#ifdef CPPHTTPLIB_ZSTD_SUPPORT
7721 compressor = detail::make_unique<detail::zstd_compressor>();
7722#endif
7723 } else {
7724 compressor = detail::make_unique<detail::nocompressor>();
7725 }
7726 assert(compressor != nullptr);
7727
7728 return detail::write_content_chunked(strm, res.content_provider_,
7729 is_shutting_down, *compressor);
7730 } else {
7731 return detail::write_content_without_length(strm, res.content_provider_,
7732 is_shutting_down);
7733 }
7734 }
7735}
7736
7737 inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
7738 FormFields::iterator cur_field;
7739 FormFiles::iterator cur_file;
7740 auto is_text_field = false;
7741 size_t count = 0;
7742 if (read_content_core(
7743 strm, req, res,
7744 // Regular
7745 [&](const char *buf, size_t n) {
7746 if (req.body.size() + n > req.body.max_size()) { return false; }
7747 req.body.append(buf, n);
7748 return true;
7749 },
7750 // Multipart FormData
7751 [&](const FormData &file) {
7752 if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
7753 output_error_log(Error::TooManyFormDataFiles, &req);
7754 return false;
7755 }
7756
7757 if (file.filename.empty()) {
7758 cur_field = req.form.fields.emplace(
7759 file.name, FormField{file.name, file.content, file.headers});
7760 is_text_field = true;
7761 } else {
7762 cur_file = req.form.files.emplace(file.name, file);
7763 is_text_field = false;
7764 }
7765 return true;
7766 },
7767 [&](const char *buf, size_t n) {
7768 if (is_text_field) {
7769 auto &content = cur_field->second.content;
7770 if (content.size() + n > content.max_size()) { return false; }
7771 content.append(buf, n);
7772 } else {
7773 auto &content = cur_file->second.content;
7774 if (content.size() + n > content.max_size()) { return false; }
7775 content.append(buf, n);
7776 }
7777 return true;
7778 })) {
7779 const auto &content_type = req.get_header_value("Content-Type");
7780 if (!content_type.find("application/x-www-form-urlencoded")) {
7781 if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
7782 res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
7783 output_error_log(Error::ExceedMaxPayloadSize, &req);
7784 return false;
7785 }
7786 detail::parse_query_text(req.body, req.params);
7787 }
7788 return true;
7789 }
7790 return false;
7791}
7792
7793 inline bool Server::read_content_with_content_receiver(
7794 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
7795 FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
7796 return read_content_core(strm, req, res, std::move(receiver),
7797 std::move(multipart_header),
7798 std::move(multipart_receiver));
7799}
7800
7801 inline bool Server::read_content_core(
7802 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
7803 FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {
7804 detail::FormDataParser multipart_form_data_parser;
7805 ContentReceiverWithProgress out;
7806
7807 if (req.is_multipart_form_data()) {
7808 const auto &content_type = req.get_header_value("Content-Type");
7809 std::string boundary;
7810 if (!detail::parse_multipart_boundary(content_type, boundary)) {
7811 res.status = StatusCode::BadRequest_400;
7812 output_error_log(Error::MultipartParsing, &req);
7813 return false;
7814 }
7815
7816 multipart_form_data_parser.set_boundary(std::move(boundary));
7817 out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {
7818 return multipart_form_data_parser.parse(buf, n, multipart_header,
7819 multipart_receiver);
7820 };
7821 } else {
7822 out = [receiver](const char *buf, size_t n, size_t /*off*/,
7823 size_t /*len*/) { return receiver(buf, n); };
7824 }
7825
7826 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
7827 return true;
7828 }
7829
7830 if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
7831 out, true)) {
7832 return false;
7833 }
7834
7835 if (req.is_multipart_form_data()) {
7836 if (!multipart_form_data_parser.is_valid()) {
7837 res.status = StatusCode::BadRequest_400;
7838 output_error_log(Error::MultipartParsing, &req);
7839 return false;
7840 }
7841 }
7842
7843 return true;
7844}
7845
7846 inline bool Server::handle_file_request(const Request &req, Response &res) {
7847 for (const auto &entry : base_dirs_) {
7848 // Prefix match
7849 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
7850 std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
7851 if (detail::is_valid_path(sub_path)) {
7852 auto path = entry.base_dir + sub_path;
7853 if (path.back() == '/') { path += "index.html"; }
7854
7855 detail::FileStat stat(path);
7856
7857 if (stat.is_dir()) {
7858 res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
7859 return true;
7860 }
7861
7862 if (stat.is_file()) {
7863 for (const auto &kv : entry.headers) {
7864 res.set_header(kv.first, kv.second);
7865 }
7866
7867 auto mm = std::make_shared<detail::mmap>(path.c_str());
7868 if (!mm->is_open()) {
7869 output_error_log(Error::OpenFile, &req);
7870 return false;
7871 }
7872
7873 res.set_content_provider(
7874 mm->size(),
7875 detail::find_content_type(path, file_extension_and_mimetype_map_,
7876 default_file_mimetype_),
7877 [mm](size_t offset, size_t length, DataSink &sink) -> bool {
7878 sink.write(mm->data() + offset, length);
7879 return true;
7880 });
7881
7882 if (req.method != "HEAD" && file_request_handler_) {
7883 file_request_handler_(req, res);
7884 }
7885
7886 return true;
7887 } else {
7888 output_error_log(Error::OpenFile, &req);
7889 }
7890 }
7891 }
7892 }
7893 return false;
7894}
7895
7896 inline socket_t
7897Server::create_server_socket(const std::string &host, int port,
7898 int socket_flags,
7899 SocketOptions socket_options) const {
7900 return detail::create_socket(
7901 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
7902 ipv6_v6only_, std::move(socket_options),
7903 [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
7904 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
7905 output_error_log(Error::BindIPAddress, nullptr);
7906 return false;
7907 }
7908 if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
7909 output_error_log(Error::Listen, nullptr);
7910 return false;
7911 }
7912 return true;
7913 });
7914}
7915
7916 inline int Server::bind_internal(const std::string &host, int port,
7917 int socket_flags) {
7918 if (is_decommissioned) { return -1; }
7919
7920 if (!is_valid()) { return -1; }
7921
7922 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
7923 if (svr_sock_ == INVALID_SOCKET) { return -1; }
7924
7925 if (port == 0) {
7926 struct sockaddr_storage addr;
7927 socklen_t addr_len = sizeof(addr);
7928 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
7929 &addr_len) == -1) {
7930 output_error_log(Error::GetSockName, nullptr);
7931 return -1;
7932 }
7933 if (addr.ss_family == AF_INET) {
7934 return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
7935 } else if (addr.ss_family == AF_INET6) {
7936 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
7937 } else {
7938 output_error_log(Error::UnsupportedAddressFamily, nullptr);
7939 return -1;
7940 }
7941 } else {
7942 return port;
7943 }
7944}
7945
7946 inline bool Server::listen_internal() {
7947 if (is_decommissioned) { return false; }
7948
7949 auto ret = true;
7950 is_running_ = true;
7951 auto se = detail::scope_exit([&]() { is_running_ = false; });
7952
7953 {
7954 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
7955
7956 while (svr_sock_ != INVALID_SOCKET) {
7957#ifndef _WIN32
7958 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
7959#endif
7960 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
7961 idle_interval_usec_);
7962 if (val == 0) { // Timeout
7963 task_queue->on_idle();
7964 continue;
7965 }
7966#ifndef _WIN32
7967 }
7968#endif
7969
7970#if defined _WIN32
7971 // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
7972 // OVERLAPPED
7973 socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
7974#elif defined SOCK_CLOEXEC
7975 socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
7976#else
7977 socket_t sock = accept(svr_sock_, nullptr, nullptr);
7978#endif
7979
7980 if (sock == INVALID_SOCKET) {
7981 if (errno == EMFILE) {
7982 // The per-process limit of open file descriptors has been reached.
7983 // Try to accept new connections after a short sleep.
7984 std::this_thread::sleep_for(std::chrono::microseconds{1});
7985 continue;
7986 } else if (errno == EINTR || errno == EAGAIN) {
7987 continue;
7988 }
7989 if (svr_sock_ != INVALID_SOCKET) {
7990 detail::close_socket(svr_sock_);
7991 ret = false;
7992 output_error_log(Error::Connection, nullptr);
7993 } else {
7994 ; // The server socket was closed by user.
7995 }
7996 break;
7997 }
7998
7999 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
8000 read_timeout_sec_, read_timeout_usec_);
8001 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
8002 write_timeout_sec_, write_timeout_usec_);
8003
8004 if (!task_queue->enqueue(
8005 [this, sock]() { process_and_close_socket(sock); })) {
8006 output_error_log(Error::ResourceExhaustion, nullptr);
8007 detail::shutdown_socket(sock);
8008 detail::close_socket(sock);
8009 }
8010 }
8011
8012 task_queue->shutdown();
8013 }
8014
8015 is_decommissioned = !ret;
8016 return ret;
8017}
8018
8019 inline bool Server::routing(Request &req, Response &res, Stream &strm) {
8020 if (pre_routing_handler_ &&
8021 pre_routing_handler_(req, res) == HandlerResponse::Handled) {
8022 return true;
8023 }
8024
8025 // File handler
8026 if ((req.method == "GET" || req.method == "HEAD") &&
8027 handle_file_request(req, res)) {
8028 return true;
8029 }
8030
8031 if (detail::expect_content(req)) {
8032 // Content reader handler
8033 {
8034 ContentReader reader(
8035 [&](ContentReceiver receiver) {
8036 auto result = read_content_with_content_receiver(
8037 strm, req, res, std::move(receiver), nullptr, nullptr);
8038 if (!result) { output_error_log(Error::Read, &req); }
8039 return result;
8040 },
8041 [&](FormDataHeader header, ContentReceiver receiver) {
8042 auto result = read_content_with_content_receiver(
8043 strm, req, res, nullptr, std::move(header),
8044 std::move(receiver));
8045 if (!result) { output_error_log(Error::Read, &req); }
8046 return result;
8047 });
8048
8049 if (req.method == "POST") {
8050 if (dispatch_request_for_content_reader(
8051 req, res, std::move(reader),
8052 post_handlers_for_content_reader_)) {
8053 return true;
8054 }
8055 } else if (req.method == "PUT") {
8056 if (dispatch_request_for_content_reader(
8057 req, res, std::move(reader),
8058 put_handlers_for_content_reader_)) {
8059 return true;
8060 }
8061 } else if (req.method == "PATCH") {
8062 if (dispatch_request_for_content_reader(
8063 req, res, std::move(reader),
8064 patch_handlers_for_content_reader_)) {
8065 return true;
8066 }
8067 } else if (req.method == "DELETE") {
8068 if (dispatch_request_for_content_reader(
8069 req, res, std::move(reader),
8070 delete_handlers_for_content_reader_)) {
8071 return true;
8072 }
8073 }
8074 }
8075
8076 // Read content into `req.body`
8077 if (!read_content(strm, req, res)) {
8078 output_error_log(Error::Read, &req);
8079 return false;
8080 }
8081 }
8082
8083 // Regular handler
8084 if (req.method == "GET" || req.method == "HEAD") {
8085 return dispatch_request(req, res, get_handlers_);
8086 } else if (req.method == "POST") {
8087 return dispatch_request(req, res, post_handlers_);
8088 } else if (req.method == "PUT") {
8089 return dispatch_request(req, res, put_handlers_);
8090 } else if (req.method == "DELETE") {
8091 return dispatch_request(req, res, delete_handlers_);
8092 } else if (req.method == "OPTIONS") {
8093 return dispatch_request(req, res, options_handlers_);
8094 } else if (req.method == "PATCH") {
8095 return dispatch_request(req, res, patch_handlers_);
8096 }
8097
8098 res.status = StatusCode::BadRequest_400;
8099 return false;
8100}
8101
8102 inline bool Server::dispatch_request(Request &req, Response &res,
8103 const Handlers &handlers) const {
8104 for (const auto &x : handlers) {
8105 const auto &matcher = x.first;
8106 const auto &handler = x.second;
8107
8108 if (matcher->match(req)) {
8109 req.matched_route = matcher->pattern();
8110 if (!pre_request_handler_ ||
8111 pre_request_handler_(req, res) != HandlerResponse::Handled) {
8112 handler(req, res);
8113 }
8114 return true;
8115 }
8116 }
8117 return false;
8118}
8119
8120 inline void Server::apply_ranges(const Request &req, Response &res,
8121 std::string &content_type,
8122 std::string &boundary) const {
8123 if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
8124 auto it = res.headers.find("Content-Type");
8125 if (it != res.headers.end()) {
8126 content_type = it->second;
8127 res.headers.erase(it);
8128 }
8129
8130 boundary = detail::make_multipart_data_boundary();
8131
8132 res.set_header("Content-Type",
8133 "multipart/byteranges; boundary=" + boundary);
8134 }
8135
8136 auto type = detail::encoding_type(req, res);
8137
8138 if (res.body.empty()) {
8139 if (res.content_length_ > 0) {
8140 size_t length = 0;
8141 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
8142 length = res.content_length_;
8143 } else if (req.ranges.size() == 1) {
8144 auto offset_and_length = detail::get_range_offset_and_length(
8145 req.ranges[0], res.content_length_);
8146
8147 length = offset_and_length.second;
8148
8149 auto content_range = detail::make_content_range_header_field(
8150 offset_and_length, res.content_length_);
8151 res.set_header("Content-Range", content_range);
8152 } else {
8153 length = detail::get_multipart_ranges_data_length(
8154 req, boundary, content_type, res.content_length_);
8155 }
8156 res.set_header("Content-Length", std::to_string(length));
8157 } else {
8158 if (res.content_provider_) {
8159 if (res.is_chunked_content_provider_) {
8160 res.set_header("Transfer-Encoding", "chunked");
8161 if (type == detail::EncodingType::Gzip) {
8162 res.set_header("Content-Encoding", "gzip");
8163 } else if (type == detail::EncodingType::Brotli) {
8164 res.set_header("Content-Encoding", "br");
8165 } else if (type == detail::EncodingType::Zstd) {
8166 res.set_header("Content-Encoding", "zstd");
8167 }
8168 }
8169 }
8170 }
8171 } else {
8172 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
8173 ;
8174 } else if (req.ranges.size() == 1) {
8175 auto offset_and_length =
8176 detail::get_range_offset_and_length(req.ranges[0], res.body.size());
8177 auto offset = offset_and_length.first;
8178 auto length = offset_and_length.second;
8179
8180 auto content_range = detail::make_content_range_header_field(
8181 offset_and_length, res.body.size());
8182 res.set_header("Content-Range", content_range);
8183
8184 assert(offset + length <= res.body.size());
8185 res.body = res.body.substr(offset, length);
8186 } else {
8187 std::string data;
8188 detail::make_multipart_ranges_data(req, res, boundary, content_type,
8189 res.body.size(), data);
8190 res.body.swap(data);
8191 }
8192
8193 if (type != detail::EncodingType::None) {
8194 output_pre_compression_log(req, res);
8195
8196 std::unique_ptr<detail::compressor> compressor;
8197 std::string content_encoding;
8198
8199 if (type == detail::EncodingType::Gzip) {
8200#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8201 compressor = detail::make_unique<detail::gzip_compressor>();
8202 content_encoding = "gzip";
8203#endif
8204 } else if (type == detail::EncodingType::Brotli) {
8205#ifdef CPPHTTPLIB_BROTLI_SUPPORT
8206 compressor = detail::make_unique<detail::brotli_compressor>();
8207 content_encoding = "br";
8208#endif
8209 } else if (type == detail::EncodingType::Zstd) {
8210#ifdef CPPHTTPLIB_ZSTD_SUPPORT
8211 compressor = detail::make_unique<detail::zstd_compressor>();
8212 content_encoding = "zstd";
8213#endif
8214 }
8215
8216 if (compressor) {
8217 std::string compressed;
8218 if (compressor->compress(res.body.data(), res.body.size(), true,
8219 [&](const char *data, size_t data_len) {
8220 compressed.append(data, data_len);
8221 return true;
8222 })) {
8223 res.body.swap(compressed);
8224 res.set_header("Content-Encoding", content_encoding);
8225 }
8226 }
8227 }
8228
8229 auto length = std::to_string(res.body.size());
8230 res.set_header("Content-Length", length);
8231 }
8232}
8233
8234 inline bool Server::dispatch_request_for_content_reader(
8235 Request &req, Response &res, ContentReader content_reader,
8236 const HandlersForContentReader &handlers) const {
8237 for (const auto &x : handlers) {
8238 const auto &matcher = x.first;
8239 const auto &handler = x.second;
8240
8241 if (matcher->match(req)) {
8242 req.matched_route = matcher->pattern();
8243 if (!pre_request_handler_ ||
8244 pre_request_handler_(req, res) != HandlerResponse::Handled) {
8245 handler(req, res, content_reader);
8246 }
8247 return true;
8248 }
8249 }
8250 return false;
8251}
8252
8253 inline bool
8254 Server::process_request(Stream &strm, const std::string &remote_addr,
8255 int remote_port, const std::string &local_addr,
8256 int local_port, bool close_connection,
8257 bool &connection_closed,
8258 const std::function<void(Request &)> &setup_request) {
8259 std::array<char, 2048> buf{};
8260
8261 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
8262
8263 // Connection has been closed on client
8264 if (!line_reader.getline()) { return false; }
8265
8266 Request req;
8267 req.start_time_ = std::chrono::steady_clock::now();
8268
8269 Response res;
8270 res.version = "HTTP/1.1";
8271 res.headers = default_headers_;
8272
8273#ifdef __APPLE__
8274 // Socket file descriptor exceeded FD_SETSIZE...
8275 if (strm.socket() >= FD_SETSIZE) {
8276 Headers dummy;
8277 detail::read_headers(strm, dummy);
8278 res.status = StatusCode::InternalServerError_500;
8279 output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
8280 return write_response(strm, close_connection, req, res);
8281 }
8282#endif
8283
8284 // Request line and headers
8285 if (!parse_request_line(line_reader.ptr(), req)) {
8286 res.status = StatusCode::BadRequest_400;
8287 output_error_log(Error::InvalidRequestLine, &req);
8288 return write_response(strm, close_connection, req, res);
8289 }
8290
8291 // Request headers
8292 if (!detail::read_headers(strm, req.headers)) {
8293 res.status = StatusCode::BadRequest_400;
8294 output_error_log(Error::InvalidHeaders, &req);
8295 return write_response(strm, close_connection, req, res);
8296 }
8297
8298 // Check if the request URI doesn't exceed the limit
8299 if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
8300 Headers dummy;
8301 detail::read_headers(strm, dummy);
8302 res.status = StatusCode::UriTooLong_414;
8303 output_error_log(Error::ExceedUriMaxLength, &req);
8304 return write_response(strm, close_connection, req, res);
8305 }
8306
8307 if (req.get_header_value("Connection") == "close") {
8308 connection_closed = true;
8309 }
8310
8311 if (req.version == "HTTP/1.0" &&
8312 req.get_header_value("Connection") != "Keep-Alive") {
8313 connection_closed = true;
8314 }
8315
8316 req.remote_addr = remote_addr;
8317 req.remote_port = remote_port;
8318 req.set_header("REMOTE_ADDR", req.remote_addr);
8319 req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
8320
8321 req.local_addr = local_addr;
8322 req.local_port = local_port;
8323 req.set_header("LOCAL_ADDR", req.local_addr);
8324 req.set_header("LOCAL_PORT", std::to_string(req.local_port));
8325
8326 if (req.has_header("Accept")) {
8327 const auto &accept_header = req.get_header_value("Accept");
8328 if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
8329 res.status = StatusCode::BadRequest_400;
8330 output_error_log(Error::HTTPParsing, &req);
8331 return write_response(strm, close_connection, req, res);
8332 }
8333 }
8334
8335 if (req.has_header("Range")) {
8336 const auto &range_header_value = req.get_header_value("Range");
8337 if (!detail::parse_range_header(range_header_value, req.ranges)) {
8338 res.status = StatusCode::RangeNotSatisfiable_416;
8339 output_error_log(Error::InvalidRangeHeader, &req);
8340 return write_response(strm, close_connection, req, res);
8341 }
8342 }
8343
8344 if (setup_request) { setup_request(req); }
8345
8346 if (req.get_header_value("Expect") == "100-continue") {
8347 int status = StatusCode::Continue_100;
8348 if (expect_100_continue_handler_) {
8349 status = expect_100_continue_handler_(req, res);
8350 }
8351 switch (status) {
8352 case StatusCode::Continue_100:
8353 case StatusCode::ExpectationFailed_417:
8354 detail::write_response_line(strm, status);
8355 strm.write("\r\n");
8356 break;
8357 default:
8358 connection_closed = true;
8359 return write_response(strm, true, req, res);
8360 }
8361 }
8362
8363 // Setup `is_connection_closed` method
8364 auto sock = strm.socket();
8365 req.is_connection_closed = [sock]() {
8366 return !detail::is_socket_alive(sock);
8367 };
8368
8369 // Routing
8370 auto routed = false;
8371#ifdef CPPHTTPLIB_NO_EXCEPTIONS
8372 routed = routing(req, res, strm);
8373#else
8374 try {
8375 routed = routing(req, res, strm);
8376 } catch (std::exception &e) {
8377 if (exception_handler_) {
8378 auto ep = std::current_exception();
8379 exception_handler_(req, res, ep);
8380 routed = true;
8381 } else {
8382 res.status = StatusCode::InternalServerError_500;
8383 std::string val;
8384 auto s = e.what();
8385 for (size_t i = 0; s[i]; i++) {
8386 switch (s[i]) {
8387 case '\r': val += "\\r"; break;
8388 case '\n': val += "\\n"; break;
8389 default: val += s[i]; break;
8390 }
8391 }
8392 res.set_header("EXCEPTION_WHAT", val);
8393 }
8394 } catch (...) {
8395 if (exception_handler_) {
8396 auto ep = std::current_exception();
8397 exception_handler_(req, res, ep);
8398 routed = true;
8399 } else {
8400 res.status = StatusCode::InternalServerError_500;
8401 res.set_header("EXCEPTION_WHAT", "UNKNOWN");
8402 }
8403 }
8404#endif
8405 if (routed) {
8406 if (res.status == -1) {
8407 res.status = req.ranges.empty() ? StatusCode::OK_200
8408 : StatusCode::PartialContent_206;
8409 }
8410
8411 // Serve file content by using a content provider
8412 if (!res.file_content_path_.empty()) {
8413 const auto &path = res.file_content_path_;
8414 auto mm = std::make_shared<detail::mmap>(path.c_str());
8415 if (!mm->is_open()) {
8416 res.body.clear();
8417 res.content_length_ = 0;
8418 res.content_provider_ = nullptr;
8419 res.status = StatusCode::NotFound_404;
8420 output_error_log(Error::OpenFile, &req);
8421 return write_response(strm, close_connection, req, res);
8422 }
8423
8424 auto content_type = res.file_content_content_type_;
8425 if (content_type.empty()) {
8426 content_type = detail::find_content_type(
8427 path, file_extension_and_mimetype_map_, default_file_mimetype_);
8428 }
8429
8430 res.set_content_provider(
8431 mm->size(), content_type,
8432 [mm](size_t offset, size_t length, DataSink &sink) -> bool {
8433 sink.write(mm->data() + offset, length);
8434 return true;
8435 });
8436 }
8437
8438 if (detail::range_error(req, res)) {
8439 res.body.clear();
8440 res.content_length_ = 0;
8441 res.content_provider_ = nullptr;
8442 res.status = StatusCode::RangeNotSatisfiable_416;
8443 return write_response(strm, close_connection, req, res);
8444 }
8445
8446 return write_response_with_content(strm, close_connection, req, res);
8447 } else {
8448 if (res.status == -1) { res.status = StatusCode::NotFound_404; }
8449
8450 return write_response(strm, close_connection, req, res);
8451 }
8452}
8453
8454 inline bool Server::is_valid() const { return true; }
8455
8456 inline bool Server::process_and_close_socket(socket_t sock) {
8457 std::string remote_addr;
8458 int remote_port = 0;
8459 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
8460
8461 std::string local_addr;
8462 int local_port = 0;
8463 detail::get_local_ip_and_port(sock, local_addr, local_port);
8464
8465 auto ret = detail::process_server_socket(
8466 svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
8467 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8468 write_timeout_usec_,
8469 [&](Stream &strm, bool close_connection, bool &connection_closed) {
8470 return process_request(strm, remote_addr, remote_port, local_addr,
8471 local_port, close_connection, connection_closed,
8472 nullptr);
8473 });
8474
8475 detail::shutdown_socket(sock);
8476 detail::close_socket(sock);
8477 return ret;
8478}
8479
8480 inline void Server::output_log(const Request &req, const Response &res) const {
8481 if (logger_) {
8482 std::lock_guard<std::mutex> guard(logger_mutex_);
8483 logger_(req, res);
8484 }
8485}
8486
8487 inline void Server::output_pre_compression_log(const Request &req,
8488 const Response &res) const {
8489 if (pre_compression_logger_) {
8490 std::lock_guard<std::mutex> guard(logger_mutex_);
8491 pre_compression_logger_(req, res);
8492 }
8493}
8494
8495 inline void Server::output_error_log(const Error &err,
8496 const Request *req) const {
8497 if (error_logger_) {
8498 std::lock_guard<std::mutex> guard(logger_mutex_);
8499 error_logger_(err, req);
8500 }
8501}
8502
8503// HTTP client implementation
8504 inline ClientImpl::ClientImpl(const std::string &host)
8505 : ClientImpl(host, 80, std::string(), std::string()) {}
8506
8507 inline ClientImpl::ClientImpl(const std::string &host, int port)
8508 : ClientImpl(host, port, std::string(), std::string()) {}
8509
8510 inline ClientImpl::ClientImpl(const std::string &host, int port,
8511 const std::string &client_cert_path,
8512 const std::string &client_key_path)
8513 : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
8514 host_and_port_(adjust_host_string(host_) + ":" + std::to_string(port)),
8515 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
8516
8517 inline ClientImpl::~ClientImpl() {
8518 // Wait until all the requests in flight are handled.
8519 size_t retry_count = 10;
8520 while (retry_count-- > 0) {
8521 {
8522 std::lock_guard<std::mutex> guard(socket_mutex_);
8523 if (socket_requests_in_flight_ == 0) { break; }
8524 }
8525 std::this_thread::sleep_for(std::chrono::milliseconds{1});
8526 }
8527
8528 std::lock_guard<std::mutex> guard(socket_mutex_);
8529 shutdown_socket(socket_);
8530 close_socket(socket_);
8531}
8532
8533 inline bool ClientImpl::is_valid() const { return true; }
8534
8535 inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
8536 client_cert_path_ = rhs.client_cert_path_;
8537 client_key_path_ = rhs.client_key_path_;
8538 connection_timeout_sec_ = rhs.connection_timeout_sec_;
8539 read_timeout_sec_ = rhs.read_timeout_sec_;
8540 read_timeout_usec_ = rhs.read_timeout_usec_;
8541 write_timeout_sec_ = rhs.write_timeout_sec_;
8542 write_timeout_usec_ = rhs.write_timeout_usec_;
8543 max_timeout_msec_ = rhs.max_timeout_msec_;
8544 basic_auth_username_ = rhs.basic_auth_username_;
8545 basic_auth_password_ = rhs.basic_auth_password_;
8546 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
8547#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8548 digest_auth_username_ = rhs.digest_auth_username_;
8549 digest_auth_password_ = rhs.digest_auth_password_;
8550#endif
8551 keep_alive_ = rhs.keep_alive_;
8552 follow_location_ = rhs.follow_location_;
8553 path_encode_ = rhs.path_encode_;
8554 address_family_ = rhs.address_family_;
8555 tcp_nodelay_ = rhs.tcp_nodelay_;
8556 ipv6_v6only_ = rhs.ipv6_v6only_;
8557 socket_options_ = rhs.socket_options_;
8558 compress_ = rhs.compress_;
8559 decompress_ = rhs.decompress_;
8560 interface_ = rhs.interface_;
8561 proxy_host_ = rhs.proxy_host_;
8562 proxy_port_ = rhs.proxy_port_;
8563 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
8564 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
8565 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
8566#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8567 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
8568 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
8569#endif
8570#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8571 ca_cert_file_path_ = rhs.ca_cert_file_path_;
8572 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
8573 ca_cert_store_ = rhs.ca_cert_store_;
8574#endif
8575#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8576 server_certificate_verification_ = rhs.server_certificate_verification_;
8577 server_hostname_verification_ = rhs.server_hostname_verification_;
8578 server_certificate_verifier_ = rhs.server_certificate_verifier_;
8579#endif
8580 logger_ = rhs.logger_;
8581 error_logger_ = rhs.error_logger_;
8582}
8583
8584 inline socket_t ClientImpl::create_client_socket(Error &error) const {
8585 if (!proxy_host_.empty() && proxy_port_ != -1) {
8586 return detail::create_client_socket(
8587 proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
8588 ipv6_v6only_, socket_options_, connection_timeout_sec_,
8589 connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
8590 write_timeout_sec_, write_timeout_usec_, interface_, error);
8591 }
8592
8593 // Check is custom IP specified for host_
8594 std::string ip;
8595 auto it = addr_map_.find(host_);
8596 if (it != addr_map_.end()) { ip = it->second; }
8597
8598 return detail::create_client_socket(
8599 host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
8600 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
8601 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8602 write_timeout_usec_, interface_, error);
8603}
8604
8605 inline bool ClientImpl::create_and_connect_socket(Socket &socket,
8606 Error &error) {
8607 auto sock = create_client_socket(error);
8608 if (sock == INVALID_SOCKET) { return false; }
8609 socket.sock = sock;
8610 return true;
8611}
8612
8613 inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
8614 bool /*shutdown_gracefully*/) {
8615 // If there are any requests in flight from threads other than us, then it's
8616 // a thread-unsafe race because individual ssl* objects are not thread-safe.
8617 assert(socket_requests_in_flight_ == 0 ||
8618 socket_requests_are_from_thread_ == std::this_thread::get_id());
8619}
8620
8621 inline void ClientImpl::shutdown_socket(Socket &socket) const {
8622 if (socket.sock == INVALID_SOCKET) { return; }
8623 detail::shutdown_socket(socket.sock);
8624}
8625
8626 inline void ClientImpl::close_socket(Socket &socket) {
8627 // If there are requests in flight in another thread, usually closing
8628 // the socket will be fine and they will simply receive an error when
8629 // using the closed socket, but it is still a bug since rarely the OS
8630 // may reassign the socket id to be used for a new socket, and then
8631 // suddenly they will be operating on a live socket that is different
8632 // than the one they intended!
8633 assert(socket_requests_in_flight_ == 0 ||
8634 socket_requests_are_from_thread_ == std::this_thread::get_id());
8635
8636 // It is also a bug if this happens while SSL is still active
8637#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8638 assert(socket.ssl == nullptr);
8639#endif
8640 if (socket.sock == INVALID_SOCKET) { return; }
8641 detail::close_socket(socket.sock);
8642 socket.sock = INVALID_SOCKET;
8643}
8644
8645 inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
8646 Response &res) const {
8647 std::array<char, 2048> buf{};
8648
8649 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
8650
8651 if (!line_reader.getline()) { return false; }
8652
8653#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
8654 thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
8655#else
8656 thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
8657#endif
8658
8659 std::cmatch m;
8660 if (!std::regex_match(line_reader.ptr(), m, re)) {
8661 return req.method == "CONNECT";
8662 }
8663 res.version = std::string(m[1]);
8664 res.status = std::stoi(std::string(m[2]));
8665 res.reason = std::string(m[3]);
8666
8667 // Ignore '100 Continue'
8668 while (res.status == StatusCode::Continue_100) {
8669 if (!line_reader.getline()) { return false; } // CRLF
8670 if (!line_reader.getline()) { return false; } // next response line
8671
8672 if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
8673 res.version = std::string(m[1]);
8674 res.status = std::stoi(std::string(m[2]));
8675 res.reason = std::string(m[3]);
8676 }
8677
8678 return true;
8679}
8680
8681 inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
8682 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
8683 auto ret = send_(req, res, error);
8684 if (error == Error::SSLPeerCouldBeClosed_) {
8685 assert(!ret);
8686 ret = send_(req, res, error);
8687 }
8688 return ret;
8689}
8690
8691 inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
8692 {
8693 std::lock_guard<std::mutex> guard(socket_mutex_);
8694
8695 // Set this to false immediately - if it ever gets set to true by the end of
8696 // the request, we know another thread instructed us to close the socket.
8697 socket_should_be_closed_when_request_is_done_ = false;
8698
8699 auto is_alive = false;
8700 if (socket_.is_open()) {
8701 is_alive = detail::is_socket_alive(socket_.sock);
8702
8703#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8704 if (is_alive && is_ssl()) {
8705 if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
8706 is_alive = false;
8707 }
8708 }
8709#endif
8710
8711 if (!is_alive) {
8712 // Attempt to avoid sigpipe by shutting down non-gracefully if it seems
8713 // like the other side has already closed the connection Also, there
8714 // cannot be any requests in flight from other threads since we locked
8715 // request_mutex_, so safe to close everything immediately
8716 const bool shutdown_gracefully = false;
8717 shutdown_ssl(socket_, shutdown_gracefully);
8718 shutdown_socket(socket_);
8719 close_socket(socket_);
8720 }
8721 }
8722
8723 if (!is_alive) {
8724 if (!create_and_connect_socket(socket_, error)) {
8725 output_error_log(error, &req);
8726 return false;
8727 }
8728
8729#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8730 // TODO: refactoring
8731 if (is_ssl()) {
8732 auto &scli = static_cast<SSLClient &>(*this);
8733 if (!proxy_host_.empty() && proxy_port_ != -1) {
8734 auto success = false;
8735 if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
8736 error)) {
8737 if (!success) { output_error_log(error, &req); }
8738 return success;
8739 }
8740 }
8741
8742 if (!scli.initialize_ssl(socket_, error)) {
8743 output_error_log(error, &req);
8744 return false;
8745 }
8746 }
8747#endif
8748 }
8749
8750 // Mark the current socket as being in use so that it cannot be closed by
8751 // anyone else while this request is ongoing, even though we will be
8752 // releasing the mutex.
8753 if (socket_requests_in_flight_ > 1) {
8754 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
8755 }
8756 socket_requests_in_flight_ += 1;
8757 socket_requests_are_from_thread_ = std::this_thread::get_id();
8758 }
8759
8760 for (const auto &header : default_headers_) {
8761 if (req.headers.find(header.first) == req.headers.end()) {
8762 req.headers.insert(header);
8763 }
8764 }
8765
8766 auto ret = false;
8767 auto close_connection = !keep_alive_;
8768
8769 auto se = detail::scope_exit([&]() {
8770 // Briefly lock mutex in order to mark that a request is no longer ongoing
8771 std::lock_guard<std::mutex> guard(socket_mutex_);
8772 socket_requests_in_flight_ -= 1;
8773 if (socket_requests_in_flight_ <= 0) {
8774 assert(socket_requests_in_flight_ == 0);
8775 socket_requests_are_from_thread_ = std::thread::id();
8776 }
8777
8778 if (socket_should_be_closed_when_request_is_done_ || close_connection ||
8779 !ret) {
8780 shutdown_ssl(socket_, true);
8781 shutdown_socket(socket_);
8782 close_socket(socket_);
8783 }
8784 });
8785
8786 ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
8787 return handle_request(strm, req, res, close_connection, error);
8788 });
8789
8790 if (!ret) {
8791 if (error == Error::Success) {
8792 error = Error::Unknown;
8793 output_error_log(error, &req);
8794 }
8795 }
8796
8797 return ret;
8798}
8799
8800 inline Result ClientImpl::send(const Request &req) {
8801 auto req2 = req;
8802 return send_(std::move(req2));
8803}
8804
8805 inline Result ClientImpl::send_(Request &&req) {
8806 auto res = detail::make_unique<Response>();
8807 auto error = Error::Success;
8808 auto ret = send(req, *res, error);
8809#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8810 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
8811 last_ssl_error_, last_openssl_error_};
8812#else
8813 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
8814#endif
8815}
8816
8817 inline bool ClientImpl::handle_request(Stream &strm, Request &req,
8818 Response &res, bool close_connection,
8819 Error &error) {
8820 if (req.path.empty()) {
8821 error = Error::Connection;
8822 output_error_log(error, &req);
8823 return false;
8824 }
8825
8826 auto req_save = req;
8827
8828 bool ret;
8829
8830 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
8831 auto req2 = req;
8832 req2.path = "http://" + host_and_port_ + req.path;
8833 ret = process_request(strm, req2, res, close_connection, error);
8834 req = req2;
8835 req.path = req_save.path;
8836 } else {
8837 ret = process_request(strm, req, res, close_connection, error);
8838 }
8839
8840 if (!ret) { return false; }
8841
8842 if (res.get_header_value("Connection") == "close" ||
8843 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
8844 // TODO this requires a not-entirely-obvious chain of calls to be correct
8845 // for this to be safe.
8846
8847 // This is safe to call because handle_request is only called by send_
8848 // which locks the request mutex during the process. It would be a bug
8849 // to call it from a different thread since it's a thread-safety issue
8850 // to do these things to the socket if another thread is using the socket.
8851 std::lock_guard<std::mutex> guard(socket_mutex_);
8852 shutdown_ssl(socket_, true);
8853 shutdown_socket(socket_);
8854 close_socket(socket_);
8855 }
8856
8857 if (300 < res.status && res.status < 400 && follow_location_) {
8858 req = req_save;
8859 ret = redirect(req, res, error);
8860 }
8861
8862#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8863 if ((res.status == StatusCode::Unauthorized_401 ||
8864 res.status == StatusCode::ProxyAuthenticationRequired_407) &&
8865 req.authorization_count_ < 5) {
8866 auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
8867 const auto &username =
8868 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
8869 const auto &password =
8870 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
8871
8872 if (!username.empty() && !password.empty()) {
8873 std::map<std::string, std::string> auth;
8874 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
8875 Request new_req = req;
8876 new_req.authorization_count_ += 1;
8877 new_req.headers.erase(is_proxy ? "Proxy-Authorization"
8878 : "Authorization");
8879 new_req.headers.insert(detail::make_digest_authentication_header(
8880 req, auth, new_req.authorization_count_, detail::random_string(10),
8881 username, password, is_proxy));
8882
8883 Response new_res;
8884
8885 ret = send(new_req, new_res, error);
8886 if (ret) { res = new_res; }
8887 }
8888 }
8889 }
8890#endif
8891
8892 return ret;
8893}
8894
8895 inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
8896 if (req.redirect_count_ == 0) {
8897 error = Error::ExceedRedirectCount;
8898 output_error_log(error, &req);
8899 return false;
8900 }
8901
8902 auto location = res.get_header_value("location");
8903 if (location.empty()) { return false; }
8904
8905 thread_local const std::regex re(
8906 R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
8907
8908 std::smatch m;
8909 if (!std::regex_match(location, m, re)) { return false; }
8910
8911 auto scheme = is_ssl() ? "https" : "http";
8912
8913 auto next_scheme = m[1].str();
8914 auto next_host = m[2].str();
8915 if (next_host.empty()) { next_host = m[3].str(); }
8916 auto port_str = m[4].str();
8917 auto next_path = m[5].str();
8918 auto next_query = m[6].str();
8919
8920 auto next_port = port_;
8921 if (!port_str.empty()) {
8922 next_port = std::stoi(port_str);
8923 } else if (!next_scheme.empty()) {
8924 next_port = next_scheme == "https" ? 443 : 80;
8925 }
8926
8927 if (next_scheme.empty()) { next_scheme = scheme; }
8928 if (next_host.empty()) { next_host = host_; }
8929 if (next_path.empty()) { next_path = "/"; }
8930
8931 auto path = decode_query_component(next_path, true) + next_query;
8932
8933 // Same host redirect - use current client
8934 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
8935 return detail::redirect(*this, req, res, path, location, error);
8936 }
8937
8938 // Cross-host/scheme redirect - create new client with robust setup
8939 return create_redirect_client(next_scheme, next_host, next_port, req, res,
8940 path, location, error);
8941}
8942
8943// New method for robust redirect client creation
8944 inline bool ClientImpl::create_redirect_client(
8945 const std::string &scheme, const std::string &host, int port, Request &req,
8946 Response &res, const std::string &path, const std::string &location,
8947 Error &error) {
8948 // Determine if we need SSL
8949 auto need_ssl = (scheme == "https");
8950
8951 // Clean up request headers that are host/client specific
8952 // Remove headers that should not be carried over to new host
8953 auto headers_to_remove =
8954 std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
8955
8956 for (const auto &header_name : headers_to_remove) {
8957 auto it = req.headers.find(header_name);
8958 while (it != req.headers.end()) {
8959 it = req.headers.erase(it);
8960 it = req.headers.find(header_name);
8961 }
8962 }
8963
8964 // Create appropriate client type and handle redirect
8965 if (need_ssl) {
8966#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8967 // Create SSL client for HTTPS redirect
8968 SSLClient redirect_client(host, port);
8969
8970 // Setup basic client configuration first
8971 setup_redirect_client(redirect_client);
8972
8973 // SSL-specific configuration for proxy environments
8974 if (!proxy_host_.empty() && proxy_port_ != -1) {
8975 // Critical: Disable SSL verification for proxy environments
8976 redirect_client.enable_server_certificate_verification(false);
8977 redirect_client.enable_server_hostname_verification(false);
8978 } else {
8979 // For direct SSL connections, copy SSL verification settings
8980 redirect_client.enable_server_certificate_verification(
8981 server_certificate_verification_);
8982 redirect_client.enable_server_hostname_verification(
8983 server_hostname_verification_);
8984 }
8985
8986 // Handle CA certificate store and paths if available
8987 if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_)) {
8988 redirect_client.set_ca_cert_store(ca_cert_store_);
8989 }
8990 if (!ca_cert_file_path_.empty()) {
8991 redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
8992 }
8993
8994 // Client certificates are set through constructor for SSLClient
8995 // NOTE: SSLClient constructor already takes client_cert_path and
8996 // client_key_path so we need to create it properly if client certs are
8997 // needed
8998
8999 // Execute the redirect
9000 return detail::redirect(redirect_client, req, res, path, location, error);
9001#else
9002 // SSL not supported - set appropriate error
9003 error = Error::SSLConnection;
9004 output_error_log(error, &req);
9005 return false;
9006#endif
9007 } else {
9008 // HTTP redirect
9009 ClientImpl redirect_client(host, port);
9010
9011 // Setup client with robust configuration
9012 setup_redirect_client(redirect_client);
9013
9014 // Execute the redirect
9015 return detail::redirect(redirect_client, req, res, path, location, error);
9016 }
9017}
9018
9019// New method for robust client setup (based on basic_manual_redirect.cpp logic)
9020 template <typename ClientType>
9021 inline void ClientImpl::setup_redirect_client(ClientType &client) {
9022 // Copy basic settings first
9023 client.set_connection_timeout(connection_timeout_sec_);
9024 client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
9025 client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
9026 client.set_keep_alive(keep_alive_);
9027 client.set_follow_location(
9028 true); // Enable redirects to handle multi-step redirects
9029 client.set_path_encode(path_encode_);
9030 client.set_compress(compress_);
9031 client.set_decompress(decompress_);
9032
9033 // Copy authentication settings BEFORE proxy setup
9034 if (!basic_auth_username_.empty()) {
9035 client.set_basic_auth(basic_auth_username_, basic_auth_password_);
9036 }
9037 if (!bearer_token_auth_token_.empty()) {
9038 client.set_bearer_token_auth(bearer_token_auth_token_);
9039 }
9040#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9041 if (!digest_auth_username_.empty()) {
9042 client.set_digest_auth(digest_auth_username_, digest_auth_password_);
9043 }
9044#endif
9045
9046 // Setup proxy configuration (CRITICAL ORDER - proxy must be set
9047 // before proxy auth)
9048 if (!proxy_host_.empty() && proxy_port_ != -1) {
9049 // First set proxy host and port
9050 client.set_proxy(proxy_host_, proxy_port_);
9051
9052 // Then set proxy authentication (order matters!)
9053 if (!proxy_basic_auth_username_.empty()) {
9054 client.set_proxy_basic_auth(proxy_basic_auth_username_,
9055 proxy_basic_auth_password_);
9056 }
9057 if (!proxy_bearer_token_auth_token_.empty()) {
9058 client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
9059 }
9060#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9061 if (!proxy_digest_auth_username_.empty()) {
9062 client.set_proxy_digest_auth(proxy_digest_auth_username_,
9063 proxy_digest_auth_password_);
9064 }
9065#endif
9066 }
9067
9068 // Copy network and socket settings
9069 client.set_address_family(address_family_);
9070 client.set_tcp_nodelay(tcp_nodelay_);
9071 client.set_ipv6_v6only(ipv6_v6only_);
9072 if (socket_options_) { client.set_socket_options(socket_options_); }
9073 if (!interface_.empty()) { client.set_interface(interface_); }
9074
9075 // Copy logging and headers
9076 if (logger_) { client.set_logger(logger_); }
9077 if (error_logger_) { client.set_error_logger(error_logger_); }
9078
9079 // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
9080 // Each new client should generate its own headers based on its target host
9081}
9082
9083 inline bool ClientImpl::write_content_with_provider(Stream &strm,
9084 const Request &req,
9085 Error &error) const {
9086 auto is_shutting_down = []() { return false; };
9087
9088 if (req.is_chunked_content_provider_) {
9089 // TODO: Brotli support
9090 std::unique_ptr<detail::compressor> compressor;
9091#ifdef CPPHTTPLIB_ZLIB_SUPPORT
9092 if (compress_) {
9093 compressor = detail::make_unique<detail::gzip_compressor>();
9094 } else
9095#endif
9096 {
9097 compressor = detail::make_unique<detail::nocompressor>();
9098 }
9099
9100 return detail::write_content_chunked(strm, req.content_provider_,
9101 is_shutting_down, *compressor, error);
9102 } else {
9103 return detail::write_content_with_progress(
9104 strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
9105 req.upload_progress, error);
9106 }
9107}
9108
9109 inline bool ClientImpl::write_request(Stream &strm, Request &req,
9110 bool close_connection, Error &error) {
9111 // Prepare additional headers
9112 if (close_connection) {
9113 if (!req.has_header("Connection")) {
9114 req.set_header("Connection", "close");
9115 }
9116 }
9117
9118 if (!req.has_header("Host")) {
9119 // For Unix socket connections, use "localhost" as Host header (similar to
9120 // curl behavior)
9121 if (address_family_ == AF_UNIX) {
9122 req.set_header("Host", "localhost");
9123 } else if (is_ssl()) {
9124 if (port_ == 443) {
9125 req.set_header("Host", host_);
9126 } else {
9127 req.set_header("Host", host_and_port_);
9128 }
9129 } else {
9130 if (port_ == 80) {
9131 req.set_header("Host", host_);
9132 } else {
9133 req.set_header("Host", host_and_port_);
9134 }
9135 }
9136 }
9137
9138 if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }
9139
9140 if (!req.content_receiver) {
9141 if (!req.has_header("Accept-Encoding")) {
9142 std::string accept_encoding;
9143#ifdef CPPHTTPLIB_BROTLI_SUPPORT
9144 accept_encoding = "br";
9145#endif
9146#ifdef CPPHTTPLIB_ZLIB_SUPPORT
9147 if (!accept_encoding.empty()) { accept_encoding += ", "; }
9148 accept_encoding += "gzip, deflate";
9149#endif
9150#ifdef CPPHTTPLIB_ZSTD_SUPPORT
9151 if (!accept_encoding.empty()) { accept_encoding += ", "; }
9152 accept_encoding += "zstd";
9153#endif
9154 req.set_header("Accept-Encoding", accept_encoding);
9155 }
9156
9157#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
9158 if (!req.has_header("User-Agent")) {
9159 auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
9160 req.set_header("User-Agent", agent);
9161 }
9162#endif
9163 };
9164
9165 if (req.body.empty()) {
9166 if (req.content_provider_) {
9167 if (!req.is_chunked_content_provider_) {
9168 if (!req.has_header("Content-Length")) {
9169 auto length = std::to_string(req.content_length_);
9170 req.set_header("Content-Length", length);
9171 }
9172 }
9173 } else {
9174 if (req.method == "POST" || req.method == "PUT" ||
9175 req.method == "PATCH") {
9176 req.set_header("Content-Length", "0");
9177 }
9178 }
9179 } else {
9180 if (!req.has_header("Content-Type")) {
9181 req.set_header("Content-Type", "text/plain");
9182 }
9183
9184 if (!req.has_header("Content-Length")) {
9185 auto length = std::to_string(req.body.size());
9186 req.set_header("Content-Length", length);
9187 }
9188 }
9189
9190 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
9191 if (!req.has_header("Authorization")) {
9192 req.headers.insert(make_basic_authentication_header(
9193 basic_auth_username_, basic_auth_password_, false));
9194 }
9195 }
9196
9197 if (!proxy_basic_auth_username_.empty() &&
9198 !proxy_basic_auth_password_.empty()) {
9199 if (!req.has_header("Proxy-Authorization")) {
9200 req.headers.insert(make_basic_authentication_header(
9201 proxy_basic_auth_username_, proxy_basic_auth_password_, true));
9202 }
9203 }
9204
9205 if (!bearer_token_auth_token_.empty()) {
9206 if (!req.has_header("Authorization")) {
9207 req.headers.insert(make_bearer_token_authentication_header(
9208 bearer_token_auth_token_, false));
9209 }
9210 }
9211
9212 if (!proxy_bearer_token_auth_token_.empty()) {
9213 if (!req.has_header("Proxy-Authorization")) {
9214 req.headers.insert(make_bearer_token_authentication_header(
9215 proxy_bearer_token_auth_token_, true));
9216 }
9217 }
9218
9219 // Request line and headers
9220 {
9221 detail::BufferStream bstrm;
9222
9223 // Extract path and query from req.path
9224 std::string path_part, query_part;
9225 auto query_pos = req.path.find('?');
9226 if (query_pos != std::string::npos) {
9227 path_part = req.path.substr(0, query_pos);
9228 query_part = req.path.substr(query_pos + 1);
9229 } else {
9230 path_part = req.path;
9231 query_part = "";
9232 }
9233
9234 // Encode path and query
9235 auto path_with_query =
9236 path_encode_ ? detail::encode_path(path_part) : path_part;
9237
9238 detail::parse_query_text(query_part, req.params);
9239 if (!req.params.empty()) {
9240 path_with_query = append_query_params(path_with_query, req.params);
9241 }
9242
9243 // Write request line and headers
9244 detail::write_request_line(bstrm, req.method, path_with_query);
9245 header_writer_(bstrm, req.headers);
9246
9247 // Flush buffer
9248 auto &data = bstrm.get_buffer();
9249 if (!detail::write_data(strm, data.data(), data.size())) {
9250 error = Error::Write;
9251 output_error_log(error, &req);
9252 return false;
9253 }
9254 }
9255
9256 // Body
9257 if (req.body.empty()) {
9258 return write_content_with_provider(strm, req, error);
9259 }
9260
9261 if (req.upload_progress) {
9262 auto body_size = req.body.size();
9263 size_t written = 0;
9264 auto data = req.body.data();
9265
9266 while (written < body_size) {
9267 size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
9268 if (!detail::write_data(strm, data + written, to_write)) {
9269 error = Error::Write;
9270 output_error_log(error, &req);
9271 return false;
9272 }
9273 written += to_write;
9274
9275 if (!req.upload_progress(written, body_size)) {
9276 error = Error::Canceled;
9277 output_error_log(error, &req);
9278 return false;
9279 }
9280 }
9281 } else {
9282 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
9283 error = Error::Write;
9284 output_error_log(error, &req);
9285 return false;
9286 }
9287 }
9288
9289 return true;
9290}
9291
9292 inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
9293 Request &req, const char *body, size_t content_length,
9294 ContentProvider content_provider,
9295 ContentProviderWithoutLength content_provider_without_length,
9296 const std::string &content_type, Error &error) {
9297 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
9298
9299#ifdef CPPHTTPLIB_ZLIB_SUPPORT
9300 if (compress_) { req.set_header("Content-Encoding", "gzip"); }
9301#endif
9302
9303#ifdef CPPHTTPLIB_ZLIB_SUPPORT
9304 if (compress_ && !content_provider_without_length) {
9305 // TODO: Brotli support
9306 detail::gzip_compressor compressor;
9307
9308 if (content_provider) {
9309 auto ok = true;
9310 size_t offset = 0;
9311 DataSink data_sink;
9312
9313 data_sink.write = [&](const char *data, size_t data_len) -> bool {
9314 if (ok) {
9315 auto last = offset + data_len == content_length;
9316
9317 auto ret = compressor.compress(
9318 data, data_len, last,
9319 [&](const char *compressed_data, size_t compressed_data_len) {
9320 req.body.append(compressed_data, compressed_data_len);
9321 return true;
9322 });
9323
9324 if (ret) {
9325 offset += data_len;
9326 } else {
9327 ok = false;
9328 }
9329 }
9330 return ok;
9331 };
9332
9333 while (ok && offset < content_length) {
9334 if (!content_provider(offset, content_length - offset, data_sink)) {
9335 error = Error::Canceled;
9336 output_error_log(error, &req);
9337 return nullptr;
9338 }
9339 }
9340 } else {
9341 if (!compressor.compress(body, content_length, true,
9342 [&](const char *data, size_t data_len) {
9343 req.body.append(data, data_len);
9344 return true;
9345 })) {
9346 error = Error::Compression;
9347 output_error_log(error, &req);
9348 return nullptr;
9349 }
9350 }
9351 } else
9352#endif
9353 {
9354 if (content_provider) {
9355 req.content_length_ = content_length;
9356 req.content_provider_ = std::move(content_provider);
9357 req.is_chunked_content_provider_ = false;
9358 } else if (content_provider_without_length) {
9359 req.content_length_ = 0;
9360 req.content_provider_ = detail::ContentProviderAdapter(
9361 std::move(content_provider_without_length));
9362 req.is_chunked_content_provider_ = true;
9363 req.set_header("Transfer-Encoding", "chunked");
9364 } else {
9365 req.body.assign(body, content_length);
9366 }
9367 }
9368
9369 auto res = detail::make_unique<Response>();
9370 return send(req, *res, error) ? std::move(res) : nullptr;
9371}
9372
9373 inline Result ClientImpl::send_with_content_provider(
9374 const std::string &method, const std::string &path, const Headers &headers,
9375 const char *body, size_t content_length, ContentProvider content_provider,
9376 ContentProviderWithoutLength content_provider_without_length,
9377 const std::string &content_type, UploadProgress progress) {
9378 Request req;
9379 req.method = method;
9380 req.headers = headers;
9381 req.path = path;
9382 req.upload_progress = std::move(progress);
9383 if (max_timeout_msec_ > 0) {
9384 req.start_time_ = std::chrono::steady_clock::now();
9385 }
9386
9387 auto error = Error::Success;
9388
9389 auto res = send_with_content_provider(
9390 req, body, content_length, std::move(content_provider),
9391 std::move(content_provider_without_length), content_type, error);
9392
9393#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9394 return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
9395 last_openssl_error_};
9396#else
9397 return Result{std::move(res), error, std::move(req.headers)};
9398#endif
9399}
9400
9401 inline std::string
9402ClientImpl::adjust_host_string(const std::string &host) const {
9403 if (host.find(':') != std::string::npos) { return "[" + host + "]"; }
9404 return host;
9405}
9406
9407 inline void ClientImpl::output_log(const Request &req,
9408 const Response &res) const {
9409 if (logger_) {
9410 std::lock_guard<std::mutex> guard(logger_mutex_);
9411 logger_(req, res);
9412 }
9413}
9414
9415 inline void ClientImpl::output_error_log(const Error &err,
9416 const Request *req) const {
9417 if (error_logger_) {
9418 std::lock_guard<std::mutex> guard(logger_mutex_);
9419 error_logger_(err, req);
9420 }
9421}
9422
9423 inline bool ClientImpl::process_request(Stream &strm, Request &req,
9424 Response &res, bool close_connection,
9425 Error &error) {
9426 // Send request
9427 if (!write_request(strm, req, close_connection, error)) { return false; }
9428
9429#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9430 if (is_ssl()) {
9431 auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
9432 if (!is_proxy_enabled) {
9433 if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
9434 error = Error::SSLPeerCouldBeClosed_;
9435 output_error_log(error, &req);
9436 return false;
9437 }
9438 }
9439 }
9440#endif
9441
9442 // Receive response and headers
9443 if (!read_response_line(strm, req, res) ||
9444 !detail::read_headers(strm, res.headers)) {
9445 error = Error::Read;
9446 output_error_log(error, &req);
9447 return false;
9448 }
9449
9450 // Body
9451 if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
9452 req.method != "CONNECT") {
9453 auto redirect = 300 < res.status && res.status < 400 &&
9454 res.status != StatusCode::NotModified_304 &&
9455 follow_location_;
9456
9457 if (req.response_handler && !redirect) {
9458 if (!req.response_handler(res)) {
9459 error = Error::Canceled;
9460 output_error_log(error, &req);
9461 return false;
9462 }
9463 }
9464
9465 auto out =
9466 req.content_receiver
9467 ? static_cast<ContentReceiverWithProgress>(
9468 [&](const char *buf, size_t n, size_t off, size_t len) {
9469 if (redirect) { return true; }
9470 auto ret = req.content_receiver(buf, n, off, len);
9471 if (!ret) {
9472 error = Error::Canceled;
9473 output_error_log(error, &req);
9474 }
9475 return ret;
9476 })
9477 : static_cast<ContentReceiverWithProgress>(
9478 [&](const char *buf, size_t n, size_t /*off*/,
9479 size_t /*len*/) {
9480 assert(res.body.size() + n <= res.body.max_size());
9481 res.body.append(buf, n);
9482 return true;
9483 });
9484
9485 auto progress = [&](size_t current, size_t total) {
9486 if (!req.download_progress || redirect) { return true; }
9487 auto ret = req.download_progress(current, total);
9488 if (!ret) {
9489 error = Error::Canceled;
9490 output_error_log(error, &req);
9491 }
9492 return ret;
9493 };
9494
9495 if (res.has_header("Content-Length")) {
9496 if (!req.content_receiver) {
9497 auto len = res.get_header_value_u64("Content-Length");
9498 if (len > res.body.max_size()) {
9499 error = Error::Read;
9500 output_error_log(error, &req);
9501 return false;
9502 }
9503 res.body.reserve(static_cast<size_t>(len));
9504 }
9505 }
9506
9507 if (res.status != StatusCode::NotModified_304) {
9508 int dummy_status;
9509 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
9510 dummy_status, std::move(progress),
9511 std::move(out), decompress_)) {
9512 if (error != Error::Canceled) { error = Error::Read; }
9513 output_error_log(error, &req);
9514 return false;
9515 }
9516 }
9517 }
9518
9519 // Log
9520 output_log(req, res);
9521
9522 return true;
9523}
9524
9525 inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
9526 const std::string &boundary, const UploadFormDataItems &items,
9527 const FormDataProviderItems &provider_items) const {
9528 size_t cur_item = 0;
9529 size_t cur_start = 0;
9530 // cur_item and cur_start are copied to within the std::function and maintain
9531 // state between successive calls
9532 return [&, cur_item, cur_start](size_t offset,
9533 DataSink &sink) mutable -> bool {
9534 if (!offset && !items.empty()) {
9535 sink.os << detail::serialize_multipart_formdata(items, boundary, false);
9536 return true;
9537 } else if (cur_item < provider_items.size()) {
9538 if (!cur_start) {
9539 const auto &begin = detail::serialize_multipart_formdata_item_begin(
9540 provider_items[cur_item], boundary);
9541 offset += begin.size();
9542 cur_start = offset;
9543 sink.os << begin;
9544 }
9545
9546 DataSink cur_sink;
9547 auto has_data = true;
9548 cur_sink.write = sink.write;
9549 cur_sink.done = [&]() { has_data = false; };
9550
9551 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
9552 return false;
9553 }
9554
9555 if (!has_data) {
9556 sink.os << detail::serialize_multipart_formdata_item_end();
9557 cur_item++;
9558 cur_start = 0;
9559 }
9560 return true;
9561 } else {
9562 sink.os << detail::serialize_multipart_formdata_finish(boundary);
9563 sink.done();
9564 return true;
9565 }
9566 };
9567}
9568
9569 inline bool ClientImpl::process_socket(
9570 const Socket &socket,
9571 std::chrono::time_point<std::chrono::steady_clock> start_time,
9572 std::function<bool(Stream &strm)> callback) {
9573 return detail::process_client_socket(
9574 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9575 write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
9576}
9577
9578 inline bool ClientImpl::is_ssl() const { return false; }
9579
9580 inline Result ClientImpl::Get(const std::string &path,
9581 DownloadProgress progress) {
9582 return Get(path, Headers(), std::move(progress));
9583}
9584
9585 inline Result ClientImpl::Get(const std::string &path, const Params &params,
9586 const Headers &headers,
9587 DownloadProgress progress) {
9588 if (params.empty()) { return Get(path, headers); }
9589
9590 std::string path_with_query = append_query_params(path, params);
9591 return Get(path_with_query, headers, std::move(progress));
9592}
9593
9594 inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
9595 DownloadProgress progress) {
9596 Request req;
9597 req.method = "GET";
9598 req.path = path;
9599 req.headers = headers;
9600 req.download_progress = std::move(progress);
9601 if (max_timeout_msec_ > 0) {
9602 req.start_time_ = std::chrono::steady_clock::now();
9603 }
9604
9605 return send_(std::move(req));
9606}
9607
9608 inline Result ClientImpl::Get(const std::string &path,
9609 ContentReceiver content_receiver,
9610 DownloadProgress progress) {
9611 return Get(path, Headers(), nullptr, std::move(content_receiver),
9612 std::move(progress));
9613}
9614
9615 inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
9616 ContentReceiver content_receiver,
9617 DownloadProgress progress) {
9618 return Get(path, headers, nullptr, std::move(content_receiver),
9619 std::move(progress));
9620}
9621
9622 inline Result ClientImpl::Get(const std::string &path,
9623 ResponseHandler response_handler,
9624 ContentReceiver content_receiver,
9625 DownloadProgress progress) {
9626 return Get(path, Headers(), std::move(response_handler),
9627 std::move(content_receiver), std::move(progress));
9628}
9629
9630 inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
9631 ResponseHandler response_handler,
9632 ContentReceiver content_receiver,
9633 DownloadProgress progress) {
9634 Request req;
9635 req.method = "GET";
9636 req.path = path;
9637 req.headers = headers;
9638 req.response_handler = std::move(response_handler);
9639 req.content_receiver =
9640 [content_receiver](const char *data, size_t data_length,
9641 size_t /*offset*/, size_t /*total_length*/) {
9642 return content_receiver(data, data_length);
9643 };
9644 req.download_progress = std::move(progress);
9645 if (max_timeout_msec_ > 0) {
9646 req.start_time_ = std::chrono::steady_clock::now();
9647 }
9648
9649 return send_(std::move(req));
9650}
9651
9652 inline Result ClientImpl::Get(const std::string &path, const Params &params,
9653 const Headers &headers,
9654 ContentReceiver content_receiver,
9655 DownloadProgress progress) {
9656 return Get(path, params, headers, nullptr, std::move(content_receiver),
9657 std::move(progress));
9658}
9659
9660 inline Result ClientImpl::Get(const std::string &path, const Params &params,
9661 const Headers &headers,
9662 ResponseHandler response_handler,
9663 ContentReceiver content_receiver,
9664 DownloadProgress progress) {
9665 if (params.empty()) {
9666 return Get(path, headers, std::move(response_handler),
9667 std::move(content_receiver), std::move(progress));
9668 }
9669
9670 std::string path_with_query = append_query_params(path, params);
9671 return Get(path_with_query, headers, std::move(response_handler),
9672 std::move(content_receiver), std::move(progress));
9673}
9674
9675 inline Result ClientImpl::Head(const std::string &path) {
9676 return Head(path, Headers());
9677}
9678
9679 inline Result ClientImpl::Head(const std::string &path,
9680 const Headers &headers) {
9681 Request req;
9682 req.method = "HEAD";
9683 req.headers = headers;
9684 req.path = path;
9685 if (max_timeout_msec_ > 0) {
9686 req.start_time_ = std::chrono::steady_clock::now();
9687 }
9688
9689 return send_(std::move(req));
9690}
9691
9692 inline Result ClientImpl::Post(const std::string &path) {
9693 return Post(path, std::string(), std::string());
9694}
9695
9696 inline Result ClientImpl::Post(const std::string &path,
9697 const Headers &headers) {
9698 return Post(path, headers, nullptr, 0, std::string());
9699}
9700
9701 inline Result ClientImpl::Post(const std::string &path, const char *body,
9702 size_t content_length,
9703 const std::string &content_type,
9704 UploadProgress progress) {
9705 return Post(path, Headers(), body, content_length, content_type, progress);
9706}
9707
9708 inline Result ClientImpl::Post(const std::string &path, const std::string &body,
9709 const std::string &content_type,
9710 UploadProgress progress) {
9711 return Post(path, Headers(), body, content_type, progress);
9712}
9713
9714 inline Result ClientImpl::Post(const std::string &path, const Params &params) {
9715 return Post(path, Headers(), params);
9716}
9717
9718 inline Result ClientImpl::Post(const std::string &path, size_t content_length,
9719 ContentProvider content_provider,
9720 const std::string &content_type,
9721 UploadProgress progress) {
9722 return Post(path, Headers(), content_length, std::move(content_provider),
9723 content_type, progress);
9724}
9725
9726 inline Result ClientImpl::Post(const std::string &path,
9727 ContentProviderWithoutLength content_provider,
9728 const std::string &content_type,
9729 UploadProgress progress) {
9730 return Post(path, Headers(), std::move(content_provider), content_type,
9731 progress);
9732}
9733
9734 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9735 const Params &params) {
9736 auto query = detail::params_to_query_str(params);
9737 return Post(path, headers, query, "application/x-www-form-urlencoded");
9738}
9739
9740 inline Result ClientImpl::Post(const std::string &path,
9741 const UploadFormDataItems &items,
9742 UploadProgress progress) {
9743 return Post(path, Headers(), items, progress);
9744}
9745
9746 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9747 const UploadFormDataItems &items,
9748 UploadProgress progress) {
9749 const auto &boundary = detail::make_multipart_data_boundary();
9750 const auto &content_type =
9751 detail::serialize_multipart_formdata_get_content_type(boundary);
9752 const auto &body = detail::serialize_multipart_formdata(items, boundary);
9753 return Post(path, headers, body, content_type, progress);
9754}
9755
9756 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9757 const UploadFormDataItems &items,
9758 const std::string &boundary,
9759 UploadProgress progress) {
9760 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
9761 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
9762 }
9763
9764 const auto &content_type =
9765 detail::serialize_multipart_formdata_get_content_type(boundary);
9766 const auto &body = detail::serialize_multipart_formdata(items, boundary);
9767 return Post(path, headers, body, content_type, progress);
9768}
9769
9770 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9771 const char *body, size_t content_length,
9772 const std::string &content_type,
9773 UploadProgress progress) {
9774 return send_with_content_provider("POST", path, headers, body, content_length,
9775 nullptr, nullptr, content_type, progress);
9776}
9777
9778 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9779 const std::string &body,
9780 const std::string &content_type,
9781 UploadProgress progress) {
9782 return send_with_content_provider("POST", path, headers, body.data(),
9783 body.size(), nullptr, nullptr, content_type,
9784 progress);
9785}
9786
9787 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9788 size_t content_length,
9789 ContentProvider content_provider,
9790 const std::string &content_type,
9791 UploadProgress progress) {
9792 return send_with_content_provider("POST", path, headers, nullptr,
9793 content_length, std::move(content_provider),
9794 nullptr, content_type, progress);
9795}
9796
9797 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9798 ContentProviderWithoutLength content_provider,
9799 const std::string &content_type,
9800 UploadProgress progress) {
9801 return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
9802 std::move(content_provider), content_type,
9803 progress);
9804}
9805
9806 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9807 const UploadFormDataItems &items,
9808 const FormDataProviderItems &provider_items,
9809 UploadProgress progress) {
9810 const auto &boundary = detail::make_multipart_data_boundary();
9811 const auto &content_type =
9812 detail::serialize_multipart_formdata_get_content_type(boundary);
9813 return send_with_content_provider(
9814 "POST", path, headers, nullptr, 0, nullptr,
9815 get_multipart_content_provider(boundary, items, provider_items),
9816 content_type, progress);
9817}
9818
9819 inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9820 const std::string &body,
9821 const std::string &content_type,
9822 ContentReceiver content_receiver,
9823 DownloadProgress progress) {
9824 Request req;
9825 req.method = "POST";
9826 req.path = path;
9827 req.headers = headers;
9828 req.body = body;
9829 req.content_receiver =
9830 [content_receiver](const char *data, size_t data_length,
9831 size_t /*offset*/, size_t /*total_length*/) {
9832 return content_receiver(data, data_length);
9833 };
9834 req.download_progress = std::move(progress);
9835
9836 if (max_timeout_msec_ > 0) {
9837 req.start_time_ = std::chrono::steady_clock::now();
9838 }
9839
9840 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
9841
9842 return send_(std::move(req));
9843}
9844
9845 inline Result ClientImpl::Put(const std::string &path) {
9846 return Put(path, std::string(), std::string());
9847}
9848
9849 inline Result ClientImpl::Put(const std::string &path, const Headers &headers) {
9850 return Put(path, headers, nullptr, 0, std::string());
9851}
9852
9853 inline Result ClientImpl::Put(const std::string &path, const char *body,
9854 size_t content_length,
9855 const std::string &content_type,
9856 UploadProgress progress) {
9857 return Put(path, Headers(), body, content_length, content_type, progress);
9858}
9859
9860 inline Result ClientImpl::Put(const std::string &path, const std::string &body,
9861 const std::string &content_type,
9862 UploadProgress progress) {
9863 return Put(path, Headers(), body, content_type, progress);
9864}
9865
9866 inline Result ClientImpl::Put(const std::string &path, const Params &params) {
9867 return Put(path, Headers(), params);
9868}
9869
9870 inline Result ClientImpl::Put(const std::string &path, size_t content_length,
9871 ContentProvider content_provider,
9872 const std::string &content_type,
9873 UploadProgress progress) {
9874 return Put(path, Headers(), content_length, std::move(content_provider),
9875 content_type, progress);
9876}
9877
9878 inline Result ClientImpl::Put(const std::string &path,
9879 ContentProviderWithoutLength content_provider,
9880 const std::string &content_type,
9881 UploadProgress progress) {
9882 return Put(path, Headers(), std::move(content_provider), content_type,
9883 progress);
9884}
9885
9886 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9887 const Params &params) {
9888 auto query = detail::params_to_query_str(params);
9889 return Put(path, headers, query, "application/x-www-form-urlencoded");
9890}
9891
9892 inline Result ClientImpl::Put(const std::string &path,
9893 const UploadFormDataItems &items,
9894 UploadProgress progress) {
9895 return Put(path, Headers(), items, progress);
9896}
9897
9898 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9899 const UploadFormDataItems &items,
9900 UploadProgress progress) {
9901 const auto &boundary = detail::make_multipart_data_boundary();
9902 const auto &content_type =
9903 detail::serialize_multipart_formdata_get_content_type(boundary);
9904 const auto &body = detail::serialize_multipart_formdata(items, boundary);
9905 return Put(path, headers, body, content_type, progress);
9906}
9907
9908 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9909 const UploadFormDataItems &items,
9910 const std::string &boundary,
9911 UploadProgress progress) {
9912 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
9913 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
9914 }
9915
9916 const auto &content_type =
9917 detail::serialize_multipart_formdata_get_content_type(boundary);
9918 const auto &body = detail::serialize_multipart_formdata(items, boundary);
9919 return Put(path, headers, body, content_type, progress);
9920}
9921
9922 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9923 const char *body, size_t content_length,
9924 const std::string &content_type,
9925 UploadProgress progress) {
9926 return send_with_content_provider("PUT", path, headers, body, content_length,
9927 nullptr, nullptr, content_type, progress);
9928}
9929
9930 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9931 const std::string &body,
9932 const std::string &content_type,
9933 UploadProgress progress) {
9934 return send_with_content_provider("PUT", path, headers, body.data(),
9935 body.size(), nullptr, nullptr, content_type,
9936 progress);
9937}
9938
9939 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9940 size_t content_length,
9941 ContentProvider content_provider,
9942 const std::string &content_type,
9943 UploadProgress progress) {
9944 return send_with_content_provider("PUT", path, headers, nullptr,
9945 content_length, std::move(content_provider),
9946 nullptr, content_type, progress);
9947}
9948
9949 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9950 ContentProviderWithoutLength content_provider,
9951 const std::string &content_type,
9952 UploadProgress progress) {
9953 return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
9954 std::move(content_provider), content_type,
9955 progress);
9956}
9957
9958 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9959 const UploadFormDataItems &items,
9960 const FormDataProviderItems &provider_items,
9961 UploadProgress progress) {
9962 const auto &boundary = detail::make_multipart_data_boundary();
9963 const auto &content_type =
9964 detail::serialize_multipart_formdata_get_content_type(boundary);
9965 return send_with_content_provider(
9966 "PUT", path, headers, nullptr, 0, nullptr,
9967 get_multipart_content_provider(boundary, items, provider_items),
9968 content_type, progress);
9969}
9970
9971 inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9972 const std::string &body,
9973 const std::string &content_type,
9974 ContentReceiver content_receiver,
9975 DownloadProgress progress) {
9976 Request req;
9977 req.method = "PUT";
9978 req.path = path;
9979 req.headers = headers;
9980 req.body = body;
9981 req.content_receiver =
9982 [content_receiver](const char *data, size_t data_length,
9983 size_t /*offset*/, size_t /*total_length*/) {
9984 return content_receiver(data, data_length);
9985 };
9986 req.download_progress = std::move(progress);
9987
9988 if (max_timeout_msec_ > 0) {
9989 req.start_time_ = std::chrono::steady_clock::now();
9990 }
9991
9992 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
9993
9994 return send_(std::move(req));
9995}
9996
9997 inline Result ClientImpl::Patch(const std::string &path) {
9998 return Patch(path, std::string(), std::string());
9999}
10000
10001 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10002 UploadProgress progress) {
10003 return Patch(path, headers, nullptr, 0, std::string(), progress);
10004}
10005
10006 inline Result ClientImpl::Patch(const std::string &path, const char *body,
10007 size_t content_length,
10008 const std::string &content_type,
10009 UploadProgress progress) {
10010 return Patch(path, Headers(), body, content_length, content_type, progress);
10011}
10012
10013 inline Result ClientImpl::Patch(const std::string &path,
10014 const std::string &body,
10015 const std::string &content_type,
10016 UploadProgress progress) {
10017 return Patch(path, Headers(), body, content_type, progress);
10018}
10019
10020 inline Result ClientImpl::Patch(const std::string &path, const Params &params) {
10021 return Patch(path, Headers(), params);
10022}
10023
10024 inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
10025 ContentProvider content_provider,
10026 const std::string &content_type,
10027 UploadProgress progress) {
10028 return Patch(path, Headers(), content_length, std::move(content_provider),
10029 content_type, progress);
10030}
10031
10032 inline Result ClientImpl::Patch(const std::string &path,
10033 ContentProviderWithoutLength content_provider,
10034 const std::string &content_type,
10035 UploadProgress progress) {
10036 return Patch(path, Headers(), std::move(content_provider), content_type,
10037 progress);
10038}
10039
10040 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10041 const Params &params) {
10042 auto query = detail::params_to_query_str(params);
10043 return Patch(path, headers, query, "application/x-www-form-urlencoded");
10044}
10045
10046 inline Result ClientImpl::Patch(const std::string &path,
10047 const UploadFormDataItems &items,
10048 UploadProgress progress) {
10049 return Patch(path, Headers(), items, progress);
10050}
10051
10052 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10053 const UploadFormDataItems &items,
10054 UploadProgress progress) {
10055 const auto &boundary = detail::make_multipart_data_boundary();
10056 const auto &content_type =
10057 detail::serialize_multipart_formdata_get_content_type(boundary);
10058 const auto &body = detail::serialize_multipart_formdata(items, boundary);
10059 return Patch(path, headers, body, content_type, progress);
10060}
10061
10062 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10063 const UploadFormDataItems &items,
10064 const std::string &boundary,
10065 UploadProgress progress) {
10066 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
10067 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
10068 }
10069
10070 const auto &content_type =
10071 detail::serialize_multipart_formdata_get_content_type(boundary);
10072 const auto &body = detail::serialize_multipart_formdata(items, boundary);
10073 return Patch(path, headers, body, content_type, progress);
10074}
10075
10076 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10077 const char *body, size_t content_length,
10078 const std::string &content_type,
10079 UploadProgress progress) {
10080 return send_with_content_provider("PATCH", path, headers, body,
10081 content_length, nullptr, nullptr,
10082 content_type, progress);
10083}
10084
10085 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10086 const std::string &body,
10087 const std::string &content_type,
10088 UploadProgress progress) {
10089 return send_with_content_provider("PATCH", path, headers, body.data(),
10090 body.size(), nullptr, nullptr, content_type,
10091 progress);
10092}
10093
10094 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10095 size_t content_length,
10096 ContentProvider content_provider,
10097 const std::string &content_type,
10098 UploadProgress progress) {
10099 return send_with_content_provider("PATCH", path, headers, nullptr,
10100 content_length, std::move(content_provider),
10101 nullptr, content_type, progress);
10102}
10103
10104 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10105 ContentProviderWithoutLength content_provider,
10106 const std::string &content_type,
10107 UploadProgress progress) {
10108 return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
10109 std::move(content_provider), content_type,
10110 progress);
10111}
10112
10113 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10114 const UploadFormDataItems &items,
10115 const FormDataProviderItems &provider_items,
10116 UploadProgress progress) {
10117 const auto &boundary = detail::make_multipart_data_boundary();
10118 const auto &content_type =
10119 detail::serialize_multipart_formdata_get_content_type(boundary);
10120 return send_with_content_provider(
10121 "PATCH", path, headers, nullptr, 0, nullptr,
10122 get_multipart_content_provider(boundary, items, provider_items),
10123 content_type, progress);
10124}
10125
10126 inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
10127 const std::string &body,
10128 const std::string &content_type,
10129 ContentReceiver content_receiver,
10130 DownloadProgress progress) {
10131 Request req;
10132 req.method = "PATCH";
10133 req.path = path;
10134 req.headers = headers;
10135 req.body = body;
10136 req.content_receiver =
10137 [content_receiver](const char *data, size_t data_length,
10138 size_t /*offset*/, size_t /*total_length*/) {
10139 return content_receiver(data, data_length);
10140 };
10141 req.download_progress = std::move(progress);
10142
10143 if (max_timeout_msec_ > 0) {
10144 req.start_time_ = std::chrono::steady_clock::now();
10145 }
10146
10147 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
10148
10149 return send_(std::move(req));
10150}
10151
10152 inline Result ClientImpl::Delete(const std::string &path,
10153 DownloadProgress progress) {
10154 return Delete(path, Headers(), std::string(), std::string(), progress);
10155}
10156
10157 inline Result ClientImpl::Delete(const std::string &path,
10158 const Headers &headers,
10159 DownloadProgress progress) {
10160 return Delete(path, headers, std::string(), std::string(), progress);
10161}
10162
10163 inline Result ClientImpl::Delete(const std::string &path, const char *body,
10164 size_t content_length,
10165 const std::string &content_type,
10166 DownloadProgress progress) {
10167 return Delete(path, Headers(), body, content_length, content_type, progress);
10168}
10169
10170 inline Result ClientImpl::Delete(const std::string &path,
10171 const std::string &body,
10172 const std::string &content_type,
10173 DownloadProgress progress) {
10174 return Delete(path, Headers(), body.data(), body.size(), content_type,
10175 progress);
10176}
10177
10178 inline Result ClientImpl::Delete(const std::string &path,
10179 const Headers &headers,
10180 const std::string &body,
10181 const std::string &content_type,
10182 DownloadProgress progress) {
10183 return Delete(path, headers, body.data(), body.size(), content_type,
10184 progress);
10185}
10186
10187 inline Result ClientImpl::Delete(const std::string &path, const Params &params,
10188 DownloadProgress progress) {
10189 return Delete(path, Headers(), params, progress);
10190}
10191
10192 inline Result ClientImpl::Delete(const std::string &path,
10193 const Headers &headers, const Params &params,
10194 DownloadProgress progress) {
10195 auto query = detail::params_to_query_str(params);
10196 return Delete(path, headers, query, "application/x-www-form-urlencoded",
10197 progress);
10198}
10199
10200 inline Result ClientImpl::Delete(const std::string &path,
10201 const Headers &headers, const char *body,
10202 size_t content_length,
10203 const std::string &content_type,
10204 DownloadProgress progress) {
10205 Request req;
10206 req.method = "DELETE";
10207 req.headers = headers;
10208 req.path = path;
10209 req.download_progress = std::move(progress);
10210 if (max_timeout_msec_ > 0) {
10211 req.start_time_ = std::chrono::steady_clock::now();
10212 }
10213
10214 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
10215 req.body.assign(body, content_length);
10216
10217 return send_(std::move(req));
10218}
10219
10220 inline Result ClientImpl::Options(const std::string &path) {
10221 return Options(path, Headers());
10222}
10223
10224 inline Result ClientImpl::Options(const std::string &path,
10225 const Headers &headers) {
10226 Request req;
10227 req.method = "OPTIONS";
10228 req.headers = headers;
10229 req.path = path;
10230 if (max_timeout_msec_ > 0) {
10231 req.start_time_ = std::chrono::steady_clock::now();
10232 }
10233
10234 return send_(std::move(req));
10235}
10236
10237 inline void ClientImpl::stop() {
10238 std::lock_guard<std::mutex> guard(socket_mutex_);
10239
10240 // If there is anything ongoing right now, the ONLY thread-safe thing we can
10241 // do is to shutdown_socket, so that threads using this socket suddenly
10242 // discover they can't read/write any more and error out. Everything else
10243 // (closing the socket, shutting ssl down) is unsafe because these actions are
10244 // not thread-safe.
10245 if (socket_requests_in_flight_ > 0) {
10246 shutdown_socket(socket_);
10247
10248 // Aside from that, we set a flag for the socket to be closed when we're
10249 // done.
10250 socket_should_be_closed_when_request_is_done_ = true;
10251 return;
10252 }
10253
10254 // Otherwise, still holding the mutex, we can shut everything down ourselves
10255 shutdown_ssl(socket_, true);
10256 shutdown_socket(socket_);
10257 close_socket(socket_);
10258}
10259
10260 inline std::string ClientImpl::host() const { return host_; }
10261
10262 inline int ClientImpl::port() const { return port_; }
10263
10264 inline size_t ClientImpl::is_socket_open() const {
10265 std::lock_guard<std::mutex> guard(socket_mutex_);
10266 return socket_.is_open();
10267}
10268
10269 inline socket_t ClientImpl::socket() const { return socket_.sock; }
10270
10271 inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
10272 connection_timeout_sec_ = sec;
10273 connection_timeout_usec_ = usec;
10274}
10275
10276 inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
10277 read_timeout_sec_ = sec;
10278 read_timeout_usec_ = usec;
10279}
10280
10281 inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
10282 write_timeout_sec_ = sec;
10283 write_timeout_usec_ = usec;
10284}
10285
10286 inline void ClientImpl::set_max_timeout(time_t msec) {
10287 max_timeout_msec_ = msec;
10288}
10289
10290 inline void ClientImpl::set_basic_auth(const std::string &username,
10291 const std::string &password) {
10292 basic_auth_username_ = username;
10293 basic_auth_password_ = password;
10294}
10295
10296 inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
10297 bearer_token_auth_token_ = token;
10298}
10299
10300#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10301 inline void ClientImpl::set_digest_auth(const std::string &username,
10302 const std::string &password) {
10303 digest_auth_username_ = username;
10304 digest_auth_password_ = password;
10305}
10306#endif
10307
10308 inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
10309
10310 inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
10311
10312 inline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
10313
10314 inline void
10315 ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
10316 addr_map_ = std::move(addr_map);
10317}
10318
10319 inline void ClientImpl::set_default_headers(Headers headers) {
10320 default_headers_ = std::move(headers);
10321}
10322
10323 inline void ClientImpl::set_header_writer(
10324 std::function<ssize_t(Stream &, Headers &)> const &writer) {
10325 header_writer_ = writer;
10326}
10327
10328 inline void ClientImpl::set_address_family(int family) {
10329 address_family_ = family;
10330}
10331
10332 inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
10333
10334 inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
10335
10336 inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
10337 socket_options_ = std::move(socket_options);
10338}
10339
10340 inline void ClientImpl::set_compress(bool on) { compress_ = on; }
10341
10342 inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
10343
10344 inline void ClientImpl::set_interface(const std::string &intf) {
10345 interface_ = intf;
10346}
10347
10348 inline void ClientImpl::set_proxy(const std::string &host, int port) {
10349 proxy_host_ = host;
10350 proxy_port_ = port;
10351}
10352
10353 inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
10354 const std::string &password) {
10355 proxy_basic_auth_username_ = username;
10356 proxy_basic_auth_password_ = password;
10357}
10358
10359 inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
10360 proxy_bearer_token_auth_token_ = token;
10361}
10362
10363#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10364 inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
10365 const std::string &password) {
10366 proxy_digest_auth_username_ = username;
10367 proxy_digest_auth_password_ = password;
10368}
10369
10370 inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
10371 const std::string &ca_cert_dir_path) {
10372 ca_cert_file_path_ = ca_cert_file_path;
10373 ca_cert_dir_path_ = ca_cert_dir_path;
10374}
10375
10376 inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
10377 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
10378 ca_cert_store_ = ca_cert_store;
10379 }
10380}
10381
10382 inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
10383 std::size_t size) const {
10384 auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
10385 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
10386 if (!mem) { return nullptr; }
10387
10388 auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
10389 if (!inf) { return nullptr; }
10390
10391 auto cts = X509_STORE_new();
10392 if (cts) {
10393 for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
10394 auto itmp = sk_X509_INFO_value(inf, i);
10395 if (!itmp) { continue; }
10396
10397 if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
10398 if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
10399 }
10400 }
10401
10402 sk_X509_INFO_pop_free(inf, X509_INFO_free);
10403 return cts;
10404}
10405
10406 inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
10407 server_certificate_verification_ = enabled;
10408}
10409
10410 inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
10411 server_hostname_verification_ = enabled;
10412}
10413
10414 inline void ClientImpl::set_server_certificate_verifier(
10415 std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
10416 server_certificate_verifier_ = verifier;
10417}
10418#endif
10419
10420 inline void ClientImpl::set_logger(Logger logger) {
10421 logger_ = std::move(logger);
10422}
10423
10424 inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
10425 error_logger_ = std::move(error_logger);
10426}
10427
10428/*
10429 * SSL Implementation
10430 */
10431#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10432 namespace detail {
10433
10434 inline bool is_ip_address(const std::string &host) {
10435 struct in_addr addr4;
10436 struct in6_addr addr6;
10437 return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
10438 inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
10439}
10440
10441 template <typename U, typename V>
10442 inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
10443 U SSL_connect_or_accept, V setup) {
10444 SSL *ssl = nullptr;
10445 {
10446 std::lock_guard<std::mutex> guard(ctx_mutex);
10447 ssl = SSL_new(ctx);
10448 }
10449
10450 if (ssl) {
10451 set_nonblocking(sock, true);
10452 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
10453 BIO_set_nbio(bio, 1);
10454 SSL_set_bio(ssl, bio, bio);
10455
10456 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
10457 SSL_shutdown(ssl);
10458 {
10459 std::lock_guard<std::mutex> guard(ctx_mutex);
10460 SSL_free(ssl);
10461 }
10462 set_nonblocking(sock, false);
10463 return nullptr;
10464 }
10465 BIO_set_nbio(bio, 0);
10466 set_nonblocking(sock, false);
10467 }
10468
10469 return ssl;
10470}
10471
10472 inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
10473 bool shutdown_gracefully) {
10474 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
10475 // the remote has closed the network connection
10476 // Note that it is not always possible to avoid SIGPIPE, this is merely a
10477 // best-efforts.
10478 if (shutdown_gracefully) {
10479 (void)(sock);
10480 // SSL_shutdown() returns 0 on first call (indicating close_notify alert
10481 // sent) and 1 on subsequent call (indicating close_notify alert received)
10482 if (SSL_shutdown(ssl) == 0) {
10483 // Expected to return 1, but even if it doesn't, we free ssl
10484 SSL_shutdown(ssl);
10485 }
10486 }
10487
10488 std::lock_guard<std::mutex> guard(ctx_mutex);
10489 SSL_free(ssl);
10490}
10491
10492 template <typename U>
10493 bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
10494 U ssl_connect_or_accept,
10495 time_t timeout_sec, time_t timeout_usec,
10496 int *ssl_error) {
10497 auto res = 0;
10498 while ((res = ssl_connect_or_accept(ssl)) != 1) {
10499 auto err = SSL_get_error(ssl, res);
10500 switch (err) {
10501 case SSL_ERROR_WANT_READ:
10502 if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
10503 break;
10504 case SSL_ERROR_WANT_WRITE:
10505 if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
10506 break;
10507 default: break;
10508 }
10509 if (ssl_error) { *ssl_error = err; }
10510 return false;
10511 }
10512 return true;
10513}
10514
10515 template <typename T>
10516 inline bool process_server_socket_ssl(
10517 const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
10518 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
10519 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
10520 time_t write_timeout_usec, T callback) {
10521 return process_server_socket_core(
10522 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
10523 [&](bool close_connection, bool &connection_closed) {
10524 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
10525 write_timeout_sec, write_timeout_usec);
10526 return callback(strm, close_connection, connection_closed);
10527 });
10528}
10529
10530 template <typename T>
10531 inline bool process_client_socket_ssl(
10532 SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
10533 time_t write_timeout_sec, time_t write_timeout_usec,
10534 time_t max_timeout_msec,
10535 std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
10536 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
10537 write_timeout_sec, write_timeout_usec, max_timeout_msec,
10538 start_time);
10539 return callback(strm);
10540}
10541
10542// SSL socket stream implementation
10543 inline SSLSocketStream::SSLSocketStream(
10544 socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
10545 time_t write_timeout_sec, time_t write_timeout_usec,
10546 time_t max_timeout_msec,
10547 std::chrono::time_point<std::chrono::steady_clock> start_time)
10548 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
10549 read_timeout_usec_(read_timeout_usec),
10550 write_timeout_sec_(write_timeout_sec),
10551 write_timeout_usec_(write_timeout_usec),
10552 max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
10553 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
10554}
10555
10556 inline SSLSocketStream::~SSLSocketStream() = default;
10557
10558 inline bool SSLSocketStream::is_readable() const {
10559 return SSL_pending(ssl_) > 0;
10560}
10561
10562 inline bool SSLSocketStream::wait_readable() const {
10563 if (max_timeout_msec_ <= 0) {
10564 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10565 }
10566
10567 time_t read_timeout_sec;
10568 time_t read_timeout_usec;
10569 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10570 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10571
10572 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10573}
10574
10575 inline bool SSLSocketStream::wait_writable() const {
10576 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
10577 is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
10578}
10579
10580 inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
10581 if (SSL_pending(ssl_) > 0) {
10582 return SSL_read(ssl_, ptr, static_cast<int>(size));
10583 } else if (wait_readable()) {
10584 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
10585 if (ret < 0) {
10586 auto err = SSL_get_error(ssl_, ret);
10587 auto n = 1000;
10588#ifdef _WIN32
10589 while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
10590 (err == SSL_ERROR_SYSCALL &&
10591 WSAGetLastError() == WSAETIMEDOUT))) {
10592#else
10593 while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
10594#endif
10595 if (SSL_pending(ssl_) > 0) {
10596 return SSL_read(ssl_, ptr, static_cast<int>(size));
10597 } else if (wait_readable()) {
10598 std::this_thread::sleep_for(std::chrono::microseconds{10});
10599 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
10600 if (ret >= 0) { return ret; }
10601 err = SSL_get_error(ssl_, ret);
10602 } else {
10603 break;
10604 }
10605 }
10606 assert(ret < 0);
10607 }
10608 return ret;
10609 } else {
10610 return -1;
10611 }
10612}
10613
10614 inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
10615 if (wait_writable()) {
10616 auto handle_size = static_cast<int>(
10617 std::min<size_t>(size, (std::numeric_limits<int>::max)()));
10618
10619 auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
10620 if (ret < 0) {
10621 auto err = SSL_get_error(ssl_, ret);
10622 auto n = 1000;
10623#ifdef _WIN32
10624 while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
10625 (err == SSL_ERROR_SYSCALL &&
10626 WSAGetLastError() == WSAETIMEDOUT))) {
10627#else
10628 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
10629#endif
10630 if (wait_writable()) {
10631 std::this_thread::sleep_for(std::chrono::microseconds{10});
10632 ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
10633 if (ret >= 0) { return ret; }
10634 err = SSL_get_error(ssl_, ret);
10635 } else {
10636 break;
10637 }
10638 }
10639 assert(ret < 0);
10640 }
10641 return ret;
10642 }
10643 return -1;
10644}
10645
10646 inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
10647 int &port) const {
10648 detail::get_remote_ip_and_port(sock_, ip, port);
10649}
10650
10651 inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
10652 int &port) const {
10653 detail::get_local_ip_and_port(sock_, ip, port);
10654}
10655
10656 inline socket_t SSLSocketStream::socket() const { return sock_; }
10657
10658 inline time_t SSLSocketStream::duration() const {
10659 return std::chrono::duration_cast<std::chrono::milliseconds>(
10660 std::chrono::steady_clock::now() - start_time_)
10661 .count();
10662}
10663
10664} // namespace detail
10665
10666// SSL HTTP server implementation
10667 inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
10668 const char *client_ca_cert_file_path,
10669 const char *client_ca_cert_dir_path,
10670 const char *private_key_password) {
10671 ctx_ = SSL_CTX_new(TLS_server_method());
10672
10673 if (ctx_) {
10674 SSL_CTX_set_options(ctx_,
10675 SSL_OP_NO_COMPRESSION |
10676 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
10677
10678 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
10679
10680 if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
10681 SSL_CTX_set_default_passwd_cb_userdata(
10682 ctx_,
10683 reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
10684 }
10685
10686 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
10687 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
10688 1 ||
10689 SSL_CTX_check_private_key(ctx_) != 1) {
10690 last_ssl_error_ = static_cast<int>(ERR_get_error());
10691 SSL_CTX_free(ctx_);
10692 ctx_ = nullptr;
10693 } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
10694 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
10695 client_ca_cert_dir_path);
10696
10697 SSL_CTX_set_verify(
10698 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
10699 }
10700 }
10701}
10702
10703 inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
10704 X509_STORE *client_ca_cert_store) {
10705 ctx_ = SSL_CTX_new(TLS_server_method());
10706
10707 if (ctx_) {
10708 SSL_CTX_set_options(ctx_,
10709 SSL_OP_NO_COMPRESSION |
10710 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
10711
10712 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
10713
10714 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
10715 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
10716 SSL_CTX_free(ctx_);
10717 ctx_ = nullptr;
10718 } else if (client_ca_cert_store) {
10719 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
10720
10721 SSL_CTX_set_verify(
10722 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
10723 }
10724 }
10725}
10726
10727 inline SSLServer::SSLServer(
10728 const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
10729 ctx_ = SSL_CTX_new(TLS_method());
10730 if (ctx_) {
10731 if (!setup_ssl_ctx_callback(*ctx_)) {
10732 SSL_CTX_free(ctx_);
10733 ctx_ = nullptr;
10734 }
10735 }
10736}
10737
10738 inline SSLServer::~SSLServer() {
10739 if (ctx_) { SSL_CTX_free(ctx_); }
10740}
10741
10742 inline bool SSLServer::is_valid() const { return ctx_; }
10743
10744 inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
10745
10746 inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
10747 X509_STORE *client_ca_cert_store) {
10748
10749 std::lock_guard<std::mutex> guard(ctx_mutex_);
10750
10751 SSL_CTX_use_certificate(ctx_, cert);
10752 SSL_CTX_use_PrivateKey(ctx_, private_key);
10753
10754 if (client_ca_cert_store != nullptr) {
10755 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
10756 }
10757}
10758
10759 inline bool SSLServer::process_and_close_socket(socket_t sock) {
10760 auto ssl = detail::ssl_new(
10761 sock, ctx_, ctx_mutex_,
10762 [&](SSL *ssl2) {
10763 return detail::ssl_connect_or_accept_nonblocking(
10764 sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
10765 &last_ssl_error_);
10766 },
10767 [](SSL * /*ssl2*/) { return true; });
10768
10769 auto ret = false;
10770 if (ssl) {
10771 std::string remote_addr;
10772 int remote_port = 0;
10773 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
10774
10775 std::string local_addr;
10776 int local_port = 0;
10777 detail::get_local_ip_and_port(sock, local_addr, local_port);
10778
10779 ret = detail::process_server_socket_ssl(
10780 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
10781 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
10782 write_timeout_usec_,
10783 [&](Stream &strm, bool close_connection, bool &connection_closed) {
10784 return process_request(strm, remote_addr, remote_port, local_addr,
10785 local_port, close_connection,
10786 connection_closed,
10787 [&](Request &req) { req.ssl = ssl; });
10788 });
10789
10790 // Shutdown gracefully if the result seemed successful, non-gracefully if
10791 // the connection appeared to be closed.
10792 const bool shutdown_gracefully = ret;
10793 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
10794 }
10795
10796 detail::shutdown_socket(sock);
10797 detail::close_socket(sock);
10798 return ret;
10799}
10800
10801// SSL HTTP client implementation
10802 inline SSLClient::SSLClient(const std::string &host)
10803 : SSLClient(host, 443, std::string(), std::string()) {}
10804
10805 inline SSLClient::SSLClient(const std::string &host, int port)
10806 : SSLClient(host, port, std::string(), std::string()) {}
10807
10808 inline SSLClient::SSLClient(const std::string &host, int port,
10809 const std::string &client_cert_path,
10810 const std::string &client_key_path,
10811 const std::string &private_key_password)
10812 : ClientImpl(host, port, client_cert_path, client_key_path) {
10813 ctx_ = SSL_CTX_new(TLS_client_method());
10814
10815 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
10816
10817 detail::split(&host_[0], &host_[host_.size()], '.',
10818 [&](const char *b, const char *e) {
10819 host_components_.emplace_back(b, e);
10820 });
10821
10822 if (!client_cert_path.empty() && !client_key_path.empty()) {
10823 if (!private_key_password.empty()) {
10824 SSL_CTX_set_default_passwd_cb_userdata(
10825 ctx_, reinterpret_cast<void *>(
10826 const_cast<char *>(private_key_password.c_str())));
10827 }
10828
10829 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
10830 SSL_FILETYPE_PEM) != 1 ||
10831 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
10832 SSL_FILETYPE_PEM) != 1) {
10833 last_openssl_error_ = ERR_get_error();
10834 SSL_CTX_free(ctx_);
10835 ctx_ = nullptr;
10836 }
10837 }
10838}
10839
10840 inline SSLClient::SSLClient(const std::string &host, int port,
10841 X509 *client_cert, EVP_PKEY *client_key,
10842 const std::string &private_key_password)
10843 : ClientImpl(host, port) {
10844 ctx_ = SSL_CTX_new(TLS_client_method());
10845
10846 detail::split(&host_[0], &host_[host_.size()], '.',
10847 [&](const char *b, const char *e) {
10848 host_components_.emplace_back(b, e);
10849 });
10850
10851 if (client_cert != nullptr && client_key != nullptr) {
10852 if (!private_key_password.empty()) {
10853 SSL_CTX_set_default_passwd_cb_userdata(
10854 ctx_, reinterpret_cast<void *>(
10855 const_cast<char *>(private_key_password.c_str())));
10856 }
10857
10858 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
10859 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
10860 last_openssl_error_ = ERR_get_error();
10861 SSL_CTX_free(ctx_);
10862 ctx_ = nullptr;
10863 }
10864 }
10865}
10866
10867 inline SSLClient::~SSLClient() {
10868 if (ctx_) { SSL_CTX_free(ctx_); }
10869 // Make sure to shut down SSL since shutdown_ssl will resolve to the
10870 // base function rather than the derived function once we get to the
10871 // base class destructor, and won't free the SSL (causing a leak).
10872 shutdown_ssl_impl(socket_, true);
10873}
10874
10875 inline bool SSLClient::is_valid() const { return ctx_; }
10876
10877 inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
10878 if (ca_cert_store) {
10879 if (ctx_) {
10880 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
10881 // Free memory allocated for old cert and use new store `ca_cert_store`
10882 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
10883 ca_cert_store_ = ca_cert_store;
10884 }
10885 } else {
10886 X509_STORE_free(ca_cert_store);
10887 }
10888 }
10889}
10890
10891 inline void SSLClient::load_ca_cert_store(const char *ca_cert,
10892 std::size_t size) {
10893 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
10894}
10895
10896 inline long SSLClient::get_openssl_verify_result() const {
10897 return verify_result_;
10898}
10899
10900 inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
10901
10902 inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
10903 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
10904}
10905
10906// Assumes that socket_mutex_ is locked and that there are no requests in flight
10907 inline bool SSLClient::connect_with_proxy(
10908 Socket &socket,
10909 std::chrono::time_point<std::chrono::steady_clock> start_time,
10910 Response &res, bool &success, Error &error) {
10911 success = true;
10912 Response proxy_res;
10913 if (!detail::process_client_socket(
10914 socket.sock, read_timeout_sec_, read_timeout_usec_,
10915 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
10916 start_time, [&](Stream &strm) {
10917 Request req2;
10918 req2.method = "CONNECT";
10919 req2.path = host_and_port_;
10920 if (max_timeout_msec_ > 0) {
10921 req2.start_time_ = std::chrono::steady_clock::now();
10922 }
10923 return process_request(strm, req2, proxy_res, false, error);
10924 })) {
10925 // Thread-safe to close everything because we are assuming there are no
10926 // requests in flight
10927 shutdown_ssl(socket, true);
10928 shutdown_socket(socket);
10929 close_socket(socket);
10930 success = false;
10931 return false;
10932 }
10933
10934 if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
10935 if (!proxy_digest_auth_username_.empty() &&
10936 !proxy_digest_auth_password_.empty()) {
10937 std::map<std::string, std::string> auth;
10938 if (detail::parse_www_authenticate(proxy_res, auth, true)) {
10939 // Close the current socket and create a new one for the authenticated
10940 // request
10941 shutdown_ssl(socket, true);
10942 shutdown_socket(socket);
10943 close_socket(socket);
10944
10945 // Create a new socket for the authenticated CONNECT request
10946 if (!create_and_connect_socket(socket, error)) {
10947 success = false;
10948 output_error_log(error, nullptr);
10949 return false;
10950 }
10951
10952 proxy_res = Response();
10953 if (!detail::process_client_socket(
10954 socket.sock, read_timeout_sec_, read_timeout_usec_,
10955 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
10956 start_time, [&](Stream &strm) {
10957 Request req3;
10958 req3.method = "CONNECT";
10959 req3.path = host_and_port_;
10960 req3.headers.insert(detail::make_digest_authentication_header(
10961 req3, auth, 1, detail::random_string(10),
10962 proxy_digest_auth_username_, proxy_digest_auth_password_,
10963 true));
10964 if (max_timeout_msec_ > 0) {
10965 req3.start_time_ = std::chrono::steady_clock::now();
10966 }
10967 return process_request(strm, req3, proxy_res, false, error);
10968 })) {
10969 // Thread-safe to close everything because we are assuming there are
10970 // no requests in flight
10971 shutdown_ssl(socket, true);
10972 shutdown_socket(socket);
10973 close_socket(socket);
10974 success = false;
10975 return false;
10976 }
10977 }
10978 }
10979 }
10980
10981 // If status code is not 200, proxy request is failed.
10982 // Set error to ProxyConnection and return proxy response
10983 // as the response of the request
10984 if (proxy_res.status != StatusCode::OK_200) {
10985 error = Error::ProxyConnection;
10986 output_error_log(error, nullptr);
10987 res = std::move(proxy_res);
10988 // Thread-safe to close everything because we are assuming there are
10989 // no requests in flight
10990 shutdown_ssl(socket, true);
10991 shutdown_socket(socket);
10992 close_socket(socket);
10993 return false;
10994 }
10995
10996 return true;
10997}
10998
10999 inline bool SSLClient::load_certs() {
11000 auto ret = true;
11001
11002 std::call_once(initialize_cert_, [&]() {
11003 std::lock_guard<std::mutex> guard(ctx_mutex_);
11004 if (!ca_cert_file_path_.empty()) {
11005 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
11006 nullptr)) {
11007 last_openssl_error_ = ERR_get_error();
11008 ret = false;
11009 }
11010 } else if (!ca_cert_dir_path_.empty()) {
11011 if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
11012 ca_cert_dir_path_.c_str())) {
11013 last_openssl_error_ = ERR_get_error();
11014 ret = false;
11015 }
11016 } else {
11017 auto loaded = false;
11018#ifdef _WIN32
11019 loaded =
11020 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
11021#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
11022 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
11023#endif // _WIN32
11024 if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
11025 }
11026 });
11027
11028 return ret;
11029}
11030
11031 inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
11032 auto ssl = detail::ssl_new(
11033 socket.sock, ctx_, ctx_mutex_,
11034 [&](SSL *ssl2) {
11035 if (server_certificate_verification_) {
11036 if (!load_certs()) {
11037 error = Error::SSLLoadingCerts;
11038 output_error_log(error, nullptr);
11039 return false;
11040 }
11041 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
11042 }
11043
11044 if (!detail::ssl_connect_or_accept_nonblocking(
11045 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
11046 connection_timeout_usec_, &last_ssl_error_)) {
11047 error = Error::SSLConnection;
11048 output_error_log(error, nullptr);
11049 return false;
11050 }
11051
11052 if (server_certificate_verification_) {
11053 auto verification_status = SSLVerifierResponse::NoDecisionMade;
11054
11055 if (server_certificate_verifier_) {
11056 verification_status = server_certificate_verifier_(ssl2);
11057 }
11058
11059 if (verification_status == SSLVerifierResponse::CertificateRejected) {
11060 last_openssl_error_ = ERR_get_error();
11061 error = Error::SSLServerVerification;
11062 output_error_log(error, nullptr);
11063 return false;
11064 }
11065
11066 if (verification_status == SSLVerifierResponse::NoDecisionMade) {
11067 verify_result_ = SSL_get_verify_result(ssl2);
11068
11069 if (verify_result_ != X509_V_OK) {
11070 last_openssl_error_ = static_cast<unsigned long>(verify_result_);
11071 error = Error::SSLServerVerification;
11072 output_error_log(error, nullptr);
11073 return false;
11074 }
11075
11076 auto server_cert = SSL_get1_peer_certificate(ssl2);
11077 auto se = detail::scope_exit([&] { X509_free(server_cert); });
11078
11079 if (server_cert == nullptr) {
11080 last_openssl_error_ = ERR_get_error();
11081 error = Error::SSLServerVerification;
11082 output_error_log(error, nullptr);
11083 return false;
11084 }
11085
11086 if (server_hostname_verification_) {
11087 if (!verify_host(server_cert)) {
11088 last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
11089 error = Error::SSLServerHostnameVerification;
11090 output_error_log(error, nullptr);
11091 return false;
11092 }
11093 }
11094 }
11095 }
11096
11097 return true;
11098 },
11099 [&](SSL *ssl2) {
11100 // Set SNI only if host is not IP address
11101 if (!detail::is_ip_address(host_)) {
11102#if defined(OPENSSL_IS_BORINGSSL)
11103 SSL_set_tlsext_host_name(ssl2, host_.c_str());
11104#else
11105 // NOTE: Direct call instead of using the OpenSSL macro to suppress
11106 // -Wold-style-cast warning
11107 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,
11108 TLSEXT_NAMETYPE_host_name,
11109 static_cast<void *>(const_cast<char *>(host_.c_str())));
11110#endif
11111 }
11112 return true;
11113 });
11114
11115 if (ssl) {
11116 socket.ssl = ssl;
11117 return true;
11118 }
11119
11120 shutdown_socket(socket);
11121 close_socket(socket);
11122 return false;
11123}
11124
11125 inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
11126 shutdown_ssl_impl(socket, shutdown_gracefully);
11127}
11128
11129 inline void SSLClient::shutdown_ssl_impl(Socket &socket,
11130 bool shutdown_gracefully) {
11131 if (socket.sock == INVALID_SOCKET) {
11132 assert(socket.ssl == nullptr);
11133 return;
11134 }
11135 if (socket.ssl) {
11136 detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
11137 shutdown_gracefully);
11138 socket.ssl = nullptr;
11139 }
11140 assert(socket.ssl == nullptr);
11141}
11142
11143 inline bool SSLClient::process_socket(
11144 const Socket &socket,
11145 std::chrono::time_point<std::chrono::steady_clock> start_time,
11146 std::function<bool(Stream &strm)> callback) {
11147 assert(socket.ssl);
11148 return detail::process_client_socket_ssl(
11149 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
11150 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
11151 std::move(callback));
11152}
11153
11154 inline bool SSLClient::is_ssl() const { return true; }
11155
11156 inline bool SSLClient::verify_host(X509 *server_cert) const {
11157 /* Quote from RFC2818 section 3.1 "Server Identity"
11158
11159 If a subjectAltName extension of type dNSName is present, that MUST
11160 be used as the identity. Otherwise, the (most specific) Common Name
11161 field in the Subject field of the certificate MUST be used. Although
11162 the use of the Common Name is existing practice, it is deprecated and
11163 Certification Authorities are encouraged to use the dNSName instead.
11164
11165 Matching is performed using the matching rules specified by
11166 [RFC2459]. If more than one identity of a given type is present in
11167 the certificate (e.g., more than one dNSName name, a match in any one
11168 of the set is considered acceptable.) Names may contain the wildcard
11169 character * which is considered to match any single domain name
11170 component or component fragment. E.g., *.a.com matches foo.a.com but
11171 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
11172
11173 In some cases, the URI is specified as an IP address rather than a
11174 hostname. In this case, the iPAddress subjectAltName must be present
11175 in the certificate and must exactly match the IP in the URI.
11176
11177 */
11178 return verify_host_with_subject_alt_name(server_cert) ||
11179 verify_host_with_common_name(server_cert);
11180}
11181
11182 inline bool
11183SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
11184 auto ret = false;
11185
11186 auto type = GEN_DNS;
11187
11188 struct in6_addr addr6 = {};
11189 struct in_addr addr = {};
11190 size_t addr_len = 0;
11191
11192#ifndef __MINGW32__
11193 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
11194 type = GEN_IPADD;
11195 addr_len = sizeof(struct in6_addr);
11196 } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
11197 type = GEN_IPADD;
11198 addr_len = sizeof(struct in_addr);
11199 }
11200#endif
11201
11202 auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
11203 X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
11204
11205 if (alt_names) {
11206 auto dsn_matched = false;
11207 auto ip_matched = false;
11208
11209 auto count = sk_GENERAL_NAME_num(alt_names);
11210
11211 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
11212 auto val = sk_GENERAL_NAME_value(alt_names, i);
11213 if (val->type == type) {
11214 auto name =
11215 reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
11216 auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
11217
11218 switch (type) {
11219 case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
11220
11221 case GEN_IPADD:
11222 if (!memcmp(&addr6, name, addr_len) ||
11223 !memcmp(&addr, name, addr_len)) {
11224 ip_matched = true;
11225 }
11226 break;
11227 }
11228 }
11229 }
11230
11231 if (dsn_matched || ip_matched) { ret = true; }
11232 }
11233
11234 GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
11235 reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
11236 return ret;
11237}
11238
11239 inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
11240 const auto subject_name = X509_get_subject_name(server_cert);
11241
11242 if (subject_name != nullptr) {
11243 char name[BUFSIZ];
11244 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
11245 name, sizeof(name));
11246
11247 if (name_len != -1) {
11248 return check_host_name(name, static_cast<size_t>(name_len));
11249 }
11250 }
11251
11252 return false;
11253}
11254
11255 inline bool SSLClient::check_host_name(const char *pattern,
11256 size_t pattern_len) const {
11257 if (host_.size() == pattern_len && host_ == pattern) { return true; }
11258
11259 // Wildcard match
11260 // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
11261 std::vector<std::string> pattern_components;
11262 detail::split(&pattern[0], &pattern[pattern_len], '.',
11263 [&](const char *b, const char *e) {
11264 pattern_components.emplace_back(b, e);
11265 });
11266
11267 if (host_components_.size() != pattern_components.size()) { return false; }
11268
11269 auto itr = pattern_components.begin();
11270 for (const auto &h : host_components_) {
11271 auto &p = *itr;
11272 if (p != h && p != "*") {
11273 auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
11274 !p.compare(0, p.size() - 1, h));
11275 if (!partial_match) { return false; }
11276 }
11277 ++itr;
11278 }
11279
11280 return true;
11281}
11282#endif
11283
11284// Universal client implementation
11285 inline Client::Client(const std::string &scheme_host_port)
11286 : Client(scheme_host_port, std::string(), std::string()) {}
11287
11288 inline Client::Client(const std::string &scheme_host_port,
11289 const std::string &client_cert_path,
11290 const std::string &client_key_path) {
11291 const static std::regex re(
11292 R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
11293
11294 std::smatch m;
11295 if (std::regex_match(scheme_host_port, m, re)) {
11296 auto scheme = m[1].str();
11297
11298#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11299 if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
11300#else
11301 if (!scheme.empty() && scheme != "http") {
11302#endif
11303#ifndef CPPHTTPLIB_NO_EXCEPTIONS
11304 std::string msg = "'" + scheme + "' scheme is not supported.";
11305 throw std::invalid_argument(msg);
11306#endif
11307 return;
11308 }
11309
11310 auto is_ssl = scheme == "https";
11311
11312 auto host = m[2].str();
11313 if (host.empty()) { host = m[3].str(); }
11314
11315 auto port_str = m[4].str();
11316 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
11317
11318 if (is_ssl) {
11319#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11320 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
11321 client_key_path);
11322 is_ssl_ = is_ssl;
11323#endif
11324 } else {
11325 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
11326 client_key_path);
11327 }
11328 } else {
11329 // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
11330 // if port param below changes.
11331 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
11332 client_cert_path, client_key_path);
11333 }
11334} // namespace detail
11335
11336 inline Client::Client(const std::string &host, int port)
11337 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
11338
11339 inline Client::Client(const std::string &host, int port,
11340 const std::string &client_cert_path,
11341 const std::string &client_key_path)
11342 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
11343 client_key_path)) {}
11344
11345 inline Client::~Client() = default;
11346
11347 inline bool Client::is_valid() const {
11348 return cli_ != nullptr && cli_->is_valid();
11349}
11350
11351 inline Result Client::Get(const std::string &path, DownloadProgress progress) {
11352 return cli_->Get(path, std::move(progress));
11353}
11354 inline Result Client::Get(const std::string &path, const Headers &headers,
11355 DownloadProgress progress) {
11356 return cli_->Get(path, headers, std::move(progress));
11357}
11358 inline Result Client::Get(const std::string &path,
11359 ContentReceiver content_receiver,
11360 DownloadProgress progress) {
11361 return cli_->Get(path, std::move(content_receiver), std::move(progress));
11362}
11363 inline Result Client::Get(const std::string &path, const Headers &headers,
11364 ContentReceiver content_receiver,
11365 DownloadProgress progress) {
11366 return cli_->Get(path, headers, std::move(content_receiver),
11367 std::move(progress));
11368}
11369 inline Result Client::Get(const std::string &path,
11370 ResponseHandler response_handler,
11371 ContentReceiver content_receiver,
11372 DownloadProgress progress) {
11373 return cli_->Get(path, std::move(response_handler),
11374 std::move(content_receiver), std::move(progress));
11375}
11376 inline Result Client::Get(const std::string &path, const Headers &headers,
11377 ResponseHandler response_handler,
11378 ContentReceiver content_receiver,
11379 DownloadProgress progress) {
11380 return cli_->Get(path, headers, std::move(response_handler),
11381 std::move(content_receiver), std::move(progress));
11382}
11383 inline Result Client::Get(const std::string &path, const Params &params,
11384 const Headers &headers, DownloadProgress progress) {
11385 return cli_->Get(path, params, headers, std::move(progress));
11386}
11387 inline Result Client::Get(const std::string &path, const Params &params,
11388 const Headers &headers,
11389 ContentReceiver content_receiver,
11390 DownloadProgress progress) {
11391 return cli_->Get(path, params, headers, std::move(content_receiver),
11392 std::move(progress));
11393}
11394 inline Result Client::Get(const std::string &path, const Params &params,
11395 const Headers &headers,
11396 ResponseHandler response_handler,
11397 ContentReceiver content_receiver,
11398 DownloadProgress progress) {
11399 return cli_->Get(path, params, headers, std::move(response_handler),
11400 std::move(content_receiver), std::move(progress));
11401}
11402
11403 inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
11404 inline Result Client::Head(const std::string &path, const Headers &headers) {
11405 return cli_->Head(path, headers);
11406}
11407
11408 inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
11409 inline Result Client::Post(const std::string &path, const Headers &headers) {
11410 return cli_->Post(path, headers);
11411}
11412 inline Result Client::Post(const std::string &path, const char *body,
11413 size_t content_length,
11414 const std::string &content_type,
11415 UploadProgress progress) {
11416 return cli_->Post(path, body, content_length, content_type, progress);
11417}
11418 inline Result Client::Post(const std::string &path, const Headers &headers,
11419 const char *body, size_t content_length,
11420 const std::string &content_type,
11421 UploadProgress progress) {
11422 return cli_->Post(path, headers, body, content_length, content_type,
11423 progress);
11424}
11425 inline Result Client::Post(const std::string &path, const std::string &body,
11426 const std::string &content_type,
11427 UploadProgress progress) {
11428 return cli_->Post(path, body, content_type, progress);
11429}
11430 inline Result Client::Post(const std::string &path, const Headers &headers,
11431 const std::string &body,
11432 const std::string &content_type,
11433 UploadProgress progress) {
11434 return cli_->Post(path, headers, body, content_type, progress);
11435}
11436 inline Result Client::Post(const std::string &path, size_t content_length,
11437 ContentProvider content_provider,
11438 const std::string &content_type,
11439 UploadProgress progress) {
11440 return cli_->Post(path, content_length, std::move(content_provider),
11441 content_type, progress);
11442}
11443 inline Result Client::Post(const std::string &path,
11444 ContentProviderWithoutLength content_provider,
11445 const std::string &content_type,
11446 UploadProgress progress) {
11447 return cli_->Post(path, std::move(content_provider), content_type, progress);
11448}
11449 inline Result Client::Post(const std::string &path, const Headers &headers,
11450 size_t content_length,
11451 ContentProvider content_provider,
11452 const std::string &content_type,
11453 UploadProgress progress) {
11454 return cli_->Post(path, headers, content_length, std::move(content_provider),
11455 content_type, progress);
11456}
11457 inline Result Client::Post(const std::string &path, const Headers &headers,
11458 ContentProviderWithoutLength content_provider,
11459 const std::string &content_type,
11460 UploadProgress progress) {
11461 return cli_->Post(path, headers, std::move(content_provider), content_type,
11462 progress);
11463}
11464 inline Result Client::Post(const std::string &path, const Params &params) {
11465 return cli_->Post(path, params);
11466}
11467 inline Result Client::Post(const std::string &path, const Headers &headers,
11468 const Params &params) {
11469 return cli_->Post(path, headers, params);
11470}
11471 inline Result Client::Post(const std::string &path,
11472 const UploadFormDataItems &items,
11473 UploadProgress progress) {
11474 return cli_->Post(path, items, progress);
11475}
11476 inline Result Client::Post(const std::string &path, const Headers &headers,
11477 const UploadFormDataItems &items,
11478 UploadProgress progress) {
11479 return cli_->Post(path, headers, items, progress);
11480}
11481 inline Result Client::Post(const std::string &path, const Headers &headers,
11482 const UploadFormDataItems &items,
11483 const std::string &boundary,
11484 UploadProgress progress) {
11485 return cli_->Post(path, headers, items, boundary, progress);
11486}
11487 inline Result Client::Post(const std::string &path, const Headers &headers,
11488 const UploadFormDataItems &items,
11489 const FormDataProviderItems &provider_items,
11490 UploadProgress progress) {
11491 return cli_->Post(path, headers, items, provider_items, progress);
11492}
11493 inline Result Client::Post(const std::string &path, const Headers &headers,
11494 const std::string &body,
11495 const std::string &content_type,
11496 ContentReceiver content_receiver,
11497 DownloadProgress progress) {
11498 return cli_->Post(path, headers, body, content_type, content_receiver,
11499 progress);
11500}
11501
11502 inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
11503 inline Result Client::Put(const std::string &path, const Headers &headers) {
11504 return cli_->Put(path, headers);
11505}
11506 inline Result Client::Put(const std::string &path, const char *body,
11507 size_t content_length,
11508 const std::string &content_type,
11509 UploadProgress progress) {
11510 return cli_->Put(path, body, content_length, content_type, progress);
11511}
11512 inline Result Client::Put(const std::string &path, const Headers &headers,
11513 const char *body, size_t content_length,
11514 const std::string &content_type,
11515 UploadProgress progress) {
11516 return cli_->Put(path, headers, body, content_length, content_type, progress);
11517}
11518 inline Result Client::Put(const std::string &path, const std::string &body,
11519 const std::string &content_type,
11520 UploadProgress progress) {
11521 return cli_->Put(path, body, content_type, progress);
11522}
11523 inline Result Client::Put(const std::string &path, const Headers &headers,
11524 const std::string &body,
11525 const std::string &content_type,
11526 UploadProgress progress) {
11527 return cli_->Put(path, headers, body, content_type, progress);
11528}
11529 inline Result Client::Put(const std::string &path, size_t content_length,
11530 ContentProvider content_provider,
11531 const std::string &content_type,
11532 UploadProgress progress) {
11533 return cli_->Put(path, content_length, std::move(content_provider),
11534 content_type, progress);
11535}
11536 inline Result Client::Put(const std::string &path,
11537 ContentProviderWithoutLength content_provider,
11538 const std::string &content_type,
11539 UploadProgress progress) {
11540 return cli_->Put(path, std::move(content_provider), content_type, progress);
11541}
11542 inline Result Client::Put(const std::string &path, const Headers &headers,
11543 size_t content_length,
11544 ContentProvider content_provider,
11545 const std::string &content_type,
11546 UploadProgress progress) {
11547 return cli_->Put(path, headers, content_length, std::move(content_provider),
11548 content_type, progress);
11549}
11550 inline Result Client::Put(const std::string &path, const Headers &headers,
11551 ContentProviderWithoutLength content_provider,
11552 const std::string &content_type,
11553 UploadProgress progress) {
11554 return cli_->Put(path, headers, std::move(content_provider), content_type,
11555 progress);
11556}
11557 inline Result Client::Put(const std::string &path, const Params &params) {
11558 return cli_->Put(path, params);
11559}
11560 inline Result Client::Put(const std::string &path, const Headers &headers,
11561 const Params &params) {
11562 return cli_->Put(path, headers, params);
11563}
11564 inline Result Client::Put(const std::string &path,
11565 const UploadFormDataItems &items,
11566 UploadProgress progress) {
11567 return cli_->Put(path, items, progress);
11568}
11569 inline Result Client::Put(const std::string &path, const Headers &headers,
11570 const UploadFormDataItems &items,
11571 UploadProgress progress) {
11572 return cli_->Put(path, headers, items, progress);
11573}
11574 inline Result Client::Put(const std::string &path, const Headers &headers,
11575 const UploadFormDataItems &items,
11576 const std::string &boundary,
11577 UploadProgress progress) {
11578 return cli_->Put(path, headers, items, boundary, progress);
11579}
11580 inline Result Client::Put(const std::string &path, const Headers &headers,
11581 const UploadFormDataItems &items,
11582 const FormDataProviderItems &provider_items,
11583 UploadProgress progress) {
11584 return cli_->Put(path, headers, items, provider_items, progress);
11585}
11586 inline Result Client::Put(const std::string &path, const Headers &headers,
11587 const std::string &body,
11588 const std::string &content_type,
11589 ContentReceiver content_receiver,
11590 DownloadProgress progress) {
11591 return cli_->Put(path, headers, body, content_type, content_receiver,
11592 progress);
11593}
11594
11595 inline Result Client::Patch(const std::string &path) {
11596 return cli_->Patch(path);
11597}
11598 inline Result Client::Patch(const std::string &path, const Headers &headers) {
11599 return cli_->Patch(path, headers);
11600}
11601 inline Result Client::Patch(const std::string &path, const char *body,
11602 size_t content_length,
11603 const std::string &content_type,
11604 UploadProgress progress) {
11605 return cli_->Patch(path, body, content_length, content_type, progress);
11606}
11607 inline Result Client::Patch(const std::string &path, const Headers &headers,
11608 const char *body, size_t content_length,
11609 const std::string &content_type,
11610 UploadProgress progress) {
11611 return cli_->Patch(path, headers, body, content_length, content_type,
11612 progress);
11613}
11614 inline Result Client::Patch(const std::string &path, const std::string &body,
11615 const std::string &content_type,
11616 UploadProgress progress) {
11617 return cli_->Patch(path, body, content_type, progress);
11618}
11619 inline Result Client::Patch(const std::string &path, const Headers &headers,
11620 const std::string &body,
11621 const std::string &content_type,
11622 UploadProgress progress) {
11623 return cli_->Patch(path, headers, body, content_type, progress);
11624}
11625 inline Result Client::Patch(const std::string &path, size_t content_length,
11626 ContentProvider content_provider,
11627 const std::string &content_type,
11628 UploadProgress progress) {
11629 return cli_->Patch(path, content_length, std::move(content_provider),
11630 content_type, progress);
11631}
11632 inline Result Client::Patch(const std::string &path,
11633 ContentProviderWithoutLength content_provider,
11634 const std::string &content_type,
11635 UploadProgress progress) {
11636 return cli_->Patch(path, std::move(content_provider), content_type, progress);
11637}
11638 inline Result Client::Patch(const std::string &path, const Headers &headers,
11639 size_t content_length,
11640 ContentProvider content_provider,
11641 const std::string &content_type,
11642 UploadProgress progress) {
11643 return cli_->Patch(path, headers, content_length, std::move(content_provider),
11644 content_type, progress);
11645}
11646 inline Result Client::Patch(const std::string &path, const Headers &headers,
11647 ContentProviderWithoutLength content_provider,
11648 const std::string &content_type,
11649 UploadProgress progress) {
11650 return cli_->Patch(path, headers, std::move(content_provider), content_type,
11651 progress);
11652}
11653 inline Result Client::Patch(const std::string &path, const Params &params) {
11654 return cli_->Patch(path, params);
11655}
11656 inline Result Client::Patch(const std::string &path, const Headers &headers,
11657 const Params &params) {
11658 return cli_->Patch(path, headers, params);
11659}
11660 inline Result Client::Patch(const std::string &path,
11661 const UploadFormDataItems &items,
11662 UploadProgress progress) {
11663 return cli_->Patch(path, items, progress);
11664}
11665 inline Result Client::Patch(const std::string &path, const Headers &headers,
11666 const UploadFormDataItems &items,
11667 UploadProgress progress) {
11668 return cli_->Patch(path, headers, items, progress);
11669}
11670 inline Result Client::Patch(const std::string &path, const Headers &headers,
11671 const UploadFormDataItems &items,
11672 const std::string &boundary,
11673 UploadProgress progress) {
11674 return cli_->Patch(path, headers, items, boundary, progress);
11675}
11676 inline Result Client::Patch(const std::string &path, const Headers &headers,
11677 const UploadFormDataItems &items,
11678 const FormDataProviderItems &provider_items,
11679 UploadProgress progress) {
11680 return cli_->Patch(path, headers, items, provider_items, progress);
11681}
11682 inline Result Client::Patch(const std::string &path, const Headers &headers,
11683 const std::string &body,
11684 const std::string &content_type,
11685 ContentReceiver content_receiver,
11686 DownloadProgress progress) {
11687 return cli_->Patch(path, headers, body, content_type, content_receiver,
11688 progress);
11689}
11690
11691 inline Result Client::Delete(const std::string &path,
11692 DownloadProgress progress) {
11693 return cli_->Delete(path, progress);
11694}
11695 inline Result Client::Delete(const std::string &path, const Headers &headers,
11696 DownloadProgress progress) {
11697 return cli_->Delete(path, headers, progress);
11698}
11699 inline Result Client::Delete(const std::string &path, const char *body,
11700 size_t content_length,
11701 const std::string &content_type,
11702 DownloadProgress progress) {
11703 return cli_->Delete(path, body, content_length, content_type, progress);
11704}
11705 inline Result Client::Delete(const std::string &path, const Headers &headers,
11706 const char *body, size_t content_length,
11707 const std::string &content_type,
11708 DownloadProgress progress) {
11709 return cli_->Delete(path, headers, body, content_length, content_type,
11710 progress);
11711}
11712 inline Result Client::Delete(const std::string &path, const std::string &body,
11713 const std::string &content_type,
11714 DownloadProgress progress) {
11715 return cli_->Delete(path, body, content_type, progress);
11716}
11717 inline Result Client::Delete(const std::string &path, const Headers &headers,
11718 const std::string &body,
11719 const std::string &content_type,
11720 DownloadProgress progress) {
11721 return cli_->Delete(path, headers, body, content_type, progress);
11722}
11723 inline Result Client::Delete(const std::string &path, const Params &params,
11724 DownloadProgress progress) {
11725 return cli_->Delete(path, params, progress);
11726}
11727 inline Result Client::Delete(const std::string &path, const Headers &headers,
11728 const Params &params, DownloadProgress progress) {
11729 return cli_->Delete(path, headers, params, progress);
11730}
11731
11732 inline Result Client::Options(const std::string &path) {
11733 return cli_->Options(path);
11734}
11735 inline Result Client::Options(const std::string &path, const Headers &headers) {
11736 return cli_->Options(path, headers);
11737}
11738
11739 inline bool Client::send(Request &req, Response &res, Error &error) {
11740 return cli_->send(req, res, error);
11741}
11742
11743 inline Result Client::send(const Request &req) { return cli_->send(req); }
11744
11745 inline void Client::stop() { cli_->stop(); }
11746
11747 inline std::string Client::host() const { return cli_->host(); }
11748
11749 inline int Client::port() const { return cli_->port(); }
11750
11751 inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
11752
11753 inline socket_t Client::socket() const { return cli_->socket(); }
11754
11755 inline void
11756 Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
11757 cli_->set_hostname_addr_map(std::move(addr_map));
11758}
11759
11760 inline void Client::set_default_headers(Headers headers) {
11761 cli_->set_default_headers(std::move(headers));
11762}
11763
11764 inline void Client::set_header_writer(
11765 std::function<ssize_t(Stream &, Headers &)> const &writer) {
11766 cli_->set_header_writer(writer);
11767}
11768
11769 inline void Client::set_address_family(int family) {
11770 cli_->set_address_family(family);
11771}
11772
11773 inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
11774
11775 inline void Client::set_socket_options(SocketOptions socket_options) {
11776 cli_->set_socket_options(std::move(socket_options));
11777}
11778
11779 inline void Client::set_connection_timeout(time_t sec, time_t usec) {
11780 cli_->set_connection_timeout(sec, usec);
11781}
11782
11783 inline void Client::set_read_timeout(time_t sec, time_t usec) {
11784 cli_->set_read_timeout(sec, usec);
11785}
11786
11787 inline void Client::set_write_timeout(time_t sec, time_t usec) {
11788 cli_->set_write_timeout(sec, usec);
11789}
11790
11791 inline void Client::set_basic_auth(const std::string &username,
11792 const std::string &password) {
11793 cli_->set_basic_auth(username, password);
11794}
11795 inline void Client::set_bearer_token_auth(const std::string &token) {
11796 cli_->set_bearer_token_auth(token);
11797}
11798#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11799 inline void Client::set_digest_auth(const std::string &username,
11800 const std::string &password) {
11801 cli_->set_digest_auth(username, password);
11802}
11803#endif
11804
11805 inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
11806 inline void Client::set_follow_location(bool on) {
11807 cli_->set_follow_location(on);
11808}
11809
11810 inline void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }
11811
11812[[deprecated("Use set_path_encode instead")]]
11813 inline void Client::set_url_encode(bool on) {
11814 cli_->set_path_encode(on);
11815}
11816
11817 inline void Client::set_compress(bool on) { cli_->set_compress(on); }
11818
11819 inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
11820
11821 inline void Client::set_interface(const std::string &intf) {
11822 cli_->set_interface(intf);
11823}
11824
11825 inline void Client::set_proxy(const std::string &host, int port) {
11826 cli_->set_proxy(host, port);
11827}
11828 inline void Client::set_proxy_basic_auth(const std::string &username,
11829 const std::string &password) {
11830 cli_->set_proxy_basic_auth(username, password);
11831}
11832 inline void Client::set_proxy_bearer_token_auth(const std::string &token) {
11833 cli_->set_proxy_bearer_token_auth(token);
11834}
11835#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11836 inline void Client::set_proxy_digest_auth(const std::string &username,
11837 const std::string &password) {
11838 cli_->set_proxy_digest_auth(username, password);
11839}
11840#endif
11841
11842#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11843 inline void Client::enable_server_certificate_verification(bool enabled) {
11844 cli_->enable_server_certificate_verification(enabled);
11845}
11846
11847 inline void Client::enable_server_hostname_verification(bool enabled) {
11848 cli_->enable_server_hostname_verification(enabled);
11849}
11850
11851 inline void Client::set_server_certificate_verifier(
11852 std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
11853 cli_->set_server_certificate_verifier(verifier);
11854}
11855#endif
11856
11857 inline void Client::set_logger(Logger logger) {
11858 cli_->set_logger(std::move(logger));
11859}
11860
11861 inline void Client::set_error_logger(ErrorLogger error_logger) {
11862 cli_->set_error_logger(std::move(error_logger));
11863}
11864
11865#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11866 inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
11867 const std::string &ca_cert_dir_path) {
11868 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
11869}
11870
11871 inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
11872 if (is_ssl_) {
11873 static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
11874 } else {
11875 cli_->set_ca_cert_store(ca_cert_store);
11876 }
11877}
11878
11879 inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
11880 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
11881}
11882
11883 inline long Client::get_openssl_verify_result() const {
11884 if (is_ssl_) {
11885 return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
11886 }
11887 return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
11888}
11889
11890 inline SSL_CTX *Client::ssl_context() const {
11891 if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
11892 return nullptr;
11893}
11894#endif
11895
11896// ----------------------------------------------------------------------------
11897
11898} // namespace httplib
11899
11900#endif // CPPHTTPLIB_HTTPLIB_H
httplib::Client
Definition httplib.h:1731
httplib::Client::set_read_timeout
void set_read_timeout(time_t sec, time_t usec=0)
Definition httplib.h:11783
httplib::Client::Post
Result Post(const std::string &path)
Definition httplib.h:11408
httplib::Client::~Client
~Client()
httplib::Client::Client
Client(Client &&)=default
httplib::Client::Put
Result Put(const std::string &path)
Definition httplib.h:11502
httplib::Client::set_connection_timeout
void set_connection_timeout(time_t sec, time_t usec=0)
Definition httplib.h:11779
httplib::Client::send
bool send(Request &req, Response &res, Error &error)
Definition httplib.h:11739
httplib::Client::Options
Result Options(const std::string &path)
Definition httplib.h:11732
httplib::Client::set_default_headers
void set_default_headers(Headers headers)
Definition httplib.h:11760
httplib::Client::set_proxy_bearer_token_auth
void set_proxy_bearer_token_auth(const std::string &token)
Definition httplib.h:11832
httplib::Client::set_hostname_addr_map
void set_hostname_addr_map(std::map< std::string, std::string > addr_map)
Definition httplib.h:11756
httplib::Client::operator=
Client & operator=(Client &&)=default
httplib::Client::set_proxy_basic_auth
void set_proxy_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:11828
httplib::Client::set_tcp_nodelay
void set_tcp_nodelay(bool on)
Definition httplib.h:11773
httplib::Client::set_keep_alive
void set_keep_alive(bool on)
Definition httplib.h:11805
httplib::Client::stop
void stop()
Definition httplib.h:11745
httplib::Client::set_max_timeout
void set_max_timeout(time_t msec)
Definition httplib.h:2342
httplib::Client::set_interface
void set_interface(const std::string &intf)
Definition httplib.h:11821
httplib::Client::socket
socket_t socket() const
Definition httplib.h:11753
httplib::Client::Get
Result Get(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:11351
httplib::Client::set_decompress
void set_decompress(bool on)
Definition httplib.h:11819
httplib::Client::is_socket_open
size_t is_socket_open() const
Definition httplib.h:11751
httplib::Client::is_valid
bool is_valid() const
Definition httplib.h:11347
httplib::Client::set_follow_location
void set_follow_location(bool on)
Definition httplib.h:11806
httplib::Client::set_path_encode
void set_path_encode(bool on)
Definition httplib.h:11810
httplib::Client::set_proxy
void set_proxy(const std::string &host, int port)
Definition httplib.h:11825
httplib::Client::set_write_timeout
void set_write_timeout(time_t sec, time_t usec=0)
Definition httplib.h:11787
httplib::Client::Client
Client(const std::string &scheme_host_port)
Definition httplib.h:11285
httplib::Client::set_address_family
void set_address_family(int family)
Definition httplib.h:11769
httplib::Client::Head
Result Head(const std::string &path)
Definition httplib.h:11403
httplib::Client::host
std::string host() const
Definition httplib.h:11747
httplib::Client::set_error_logger
void set_error_logger(ErrorLogger error_logger)
Definition httplib.h:11861
httplib::Client::set_bearer_token_auth
void set_bearer_token_auth(const std::string &token)
Definition httplib.h:11795
httplib::Client::set_url_encode
void set_url_encode(bool on)
Definition httplib.h:11813
httplib::Client::set_logger
void set_logger(Logger logger)
Definition httplib.h:11857
httplib::Client::set_header_writer
void set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Definition httplib.h:11764
httplib::Client::Patch
Result Patch(const std::string &path)
Definition httplib.h:11595
httplib::Client::set_socket_options
void set_socket_options(SocketOptions socket_options)
Definition httplib.h:11775
httplib::Client::set_compress
void set_compress(bool on)
Definition httplib.h:11817
httplib::Client::Delete
Result Delete(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:11691
httplib::Client::set_basic_auth
void set_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:11791
httplib::Client::port
int port() const
Definition httplib.h:11749
httplib::ClientImpl
Definition httplib.h:1384
httplib::ClientImpl::set_proxy_basic_auth
void set_proxy_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:10353
httplib::ClientImpl::set_socket_options
void set_socket_options(SocketOptions socket_options)
Definition httplib.h:10336
httplib::ClientImpl::write_timeout_sec_
time_t write_timeout_sec_
Definition httplib.h:1630
httplib::ClientImpl::host
std::string host() const
Definition httplib.h:10260
httplib::ClientImpl::decompress_
bool decompress_
Definition httplib.h:1653
httplib::ClientImpl::set_basic_auth
void set_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:10290
httplib::ClientImpl::read_timeout_sec_
time_t read_timeout_sec_
Definition httplib.h:1628
httplib::ClientImpl::set_error_logger
void set_error_logger(ErrorLogger error_logger)
Definition httplib.h:10424
httplib::ClientImpl::socket_requests_in_flight_
size_t socket_requests_in_flight_
Definition httplib.h:1608
httplib::ClientImpl::close_socket
void close_socket(Socket &socket)
Definition httplib.h:8626
httplib::ClientImpl::proxy_bearer_token_auth_token_
std::string proxy_bearer_token_auth_token_
Definition httplib.h:1662
httplib::ClientImpl::write_content_with_provider
bool write_content_with_provider(Stream &strm, const Request &req, Error &error) const
Definition httplib.h:9083
httplib::ClientImpl::set_decompress
void set_decompress(bool on)
Definition httplib.h:10342
httplib::ClientImpl::socket
socket_t socket() const
Definition httplib.h:10269
httplib::ClientImpl::set_proxy
void set_proxy(const std::string &host, int port)
Definition httplib.h:10348
httplib::ClientImpl::socket_
Socket socket_
Definition httplib.h:1603
httplib::ClientImpl::set_interface
void set_interface(const std::string &intf)
Definition httplib.h:10344
httplib::ClientImpl::connection_timeout_sec_
time_t connection_timeout_sec_
Definition httplib.h:1626
httplib::ClientImpl::follow_location_
bool follow_location_
Definition httplib.h:1643
httplib::ClientImpl::Patch
Result Patch(const std::string &path)
Definition httplib.h:9997
httplib::ClientImpl::stop
void stop()
Definition httplib.h:10237
httplib::ClientImpl::tcp_nodelay_
bool tcp_nodelay_
Definition httplib.h:1648
httplib::ClientImpl::host_
const std::string host_
Definition httplib.h:1598
httplib::ClientImpl::max_timeout_msec_
time_t max_timeout_msec_
Definition httplib.h:1632
httplib::ClientImpl::read_timeout_usec_
time_t read_timeout_usec_
Definition httplib.h:1629
httplib::ClientImpl::proxy_host_
std::string proxy_host_
Definition httplib.h:1657
httplib::ClientImpl::Delete
Result Delete(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:10152
httplib::ClientImpl::set_tcp_nodelay
void set_tcp_nodelay(bool on)
Definition httplib.h:10332
httplib::ClientImpl::set_proxy_bearer_token_auth
void set_proxy_bearer_token_auth(const std::string &token)
Definition httplib.h:10359
httplib::ClientImpl::basic_auth_password_
std::string basic_auth_password_
Definition httplib.h:1635
httplib::ClientImpl::send
bool send(Request &req, Response &res, Error &error)
Definition httplib.h:8681
httplib::ClientImpl::shutdown_ssl
virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully)
Definition httplib.h:8613
httplib::ClientImpl::set_connection_timeout
void set_connection_timeout(time_t sec, time_t usec=0)
Definition httplib.h:10271
httplib::ClientImpl::bearer_token_auth_token_
std::string bearer_token_auth_token_
Definition httplib.h:1636
httplib::ClientImpl::set_hostname_addr_map
void set_hostname_addr_map(std::map< std::string, std::string > addr_map)
Definition httplib.h:10315
httplib::ClientImpl::proxy_port_
int proxy_port_
Definition httplib.h:1658
httplib::ClientImpl::set_logger
void set_logger(Logger logger)
Definition httplib.h:10420
httplib::ClientImpl::ClientImpl
ClientImpl(const std::string &host)
Definition httplib.h:8504
httplib::ClientImpl::set_compress
void set_compress(bool on)
Definition httplib.h:10340
httplib::ClientImpl::output_log
void output_log(const Request &req, const Response &res) const
Definition httplib.h:9407
httplib::ClientImpl::path_encode_
bool path_encode_
Definition httplib.h:1645
httplib::ClientImpl::port
int port() const
Definition httplib.h:10262
httplib::ClientImpl::address_family_
int address_family_
Definition httplib.h:1647
httplib::ClientImpl::set_address_family
void set_address_family(int family)
Definition httplib.h:10328
httplib::ClientImpl::host_and_port_
const std::string host_and_port_
Definition httplib.h:1600
httplib::ClientImpl::client_key_path_
std::string client_key_path_
Definition httplib.h:1624
httplib::ClientImpl::interface_
std::string interface_
Definition httplib.h:1655
httplib::ClientImpl::Put
Result Put(const std::string &path)
Definition httplib.h:9845
httplib::ClientImpl::default_headers_
Headers default_headers_
Definition httplib.h:1616
httplib::ClientImpl::client_cert_path_
std::string client_cert_path_
Definition httplib.h:1623
httplib::ClientImpl::set_ipv6_v6only
void set_ipv6_v6only(bool on)
Definition httplib.h:10334
httplib::ClientImpl::ipv6_v6only_
bool ipv6_v6only_
Definition httplib.h:1649
httplib::ClientImpl::Post
Result Post(const std::string &path)
Definition httplib.h:9692
httplib::ClientImpl::connection_timeout_usec_
time_t connection_timeout_usec_
Definition httplib.h:1627
httplib::ClientImpl::proxy_basic_auth_username_
std::string proxy_basic_auth_username_
Definition httplib.h:1660
httplib::ClientImpl::shutdown_socket
void shutdown_socket(Socket &socket) const
Definition httplib.h:8621
httplib::ClientImpl::logger_
Logger logger_
Definition httplib.h:1682
httplib::ClientImpl::header_writer_
std::function< ssize_t(Stream &, Headers &)> header_writer_
Definition httplib.h:1619
httplib::ClientImpl::process_request
bool process_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error)
Definition httplib.h:9423
httplib::ClientImpl::socket_requests_are_from_thread_
std::thread::id socket_requests_are_from_thread_
Definition httplib.h:1609
httplib::ClientImpl::compress_
bool compress_
Definition httplib.h:1652
httplib::ClientImpl::logger_mutex_
std::mutex logger_mutex_
Definition httplib.h:1681
httplib::ClientImpl::addr_map_
std::map< std::string, std::string > addr_map_
Definition httplib.h:1613
httplib::ClientImpl::error_logger_
ErrorLogger error_logger_
Definition httplib.h:1683
httplib::ClientImpl::write_timeout_usec_
time_t write_timeout_usec_
Definition httplib.h:1631
httplib::ClientImpl::proxy_basic_auth_password_
std::string proxy_basic_auth_password_
Definition httplib.h:1661
httplib::ClientImpl::set_write_timeout
void set_write_timeout(time_t sec, time_t usec=0)
Definition httplib.h:10281
httplib::ClientImpl::copy_settings
void copy_settings(const ClientImpl &rhs)
Definition httplib.h:8535
httplib::ClientImpl::output_error_log
void output_error_log(const Error &err, const Request *req) const
Definition httplib.h:9415
httplib::ClientImpl::set_max_timeout
void set_max_timeout(time_t msec)
Definition httplib.h:10286
httplib::ClientImpl::set_header_writer
void set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Definition httplib.h:10323
httplib::ClientImpl::request_mutex_
std::recursive_mutex request_mutex_
Definition httplib.h:1605
httplib::ClientImpl::port_
const int port_
Definition httplib.h:1599
httplib::ClientImpl::Options
Result Options(const std::string &path)
Definition httplib.h:10220
httplib::ClientImpl::is_socket_open
size_t is_socket_open() const
Definition httplib.h:10264
httplib::ClientImpl::set_default_headers
void set_default_headers(Headers headers)
Definition httplib.h:10319
httplib::ClientImpl::set_follow_location
void set_follow_location(bool on)
Definition httplib.h:10310
httplib::ClientImpl::socket_options_
SocketOptions socket_options_
Definition httplib.h:1650
httplib::ClientImpl::set_keep_alive
void set_keep_alive(bool on)
Definition httplib.h:10308
httplib::ClientImpl::set_path_encode
void set_path_encode(bool on)
Definition httplib.h:10312
httplib::ClientImpl::socket_should_be_closed_when_request_is_done_
bool socket_should_be_closed_when_request_is_done_
Definition httplib.h:1610
httplib::ClientImpl::keep_alive_
bool keep_alive_
Definition httplib.h:1642
httplib::ClientImpl::Head
Result Head(const std::string &path)
Definition httplib.h:9675
httplib::ClientImpl::socket_mutex_
std::mutex socket_mutex_
Definition httplib.h:1604
httplib::ClientImpl::~ClientImpl
virtual ~ClientImpl()
Definition httplib.h:8517
httplib::ClientImpl::basic_auth_username_
std::string basic_auth_username_
Definition httplib.h:1634
httplib::ClientImpl::create_and_connect_socket
virtual bool create_and_connect_socket(Socket &socket, Error &error)
Definition httplib.h:8605
httplib::ClientImpl::Get
Result Get(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:9580
httplib::ClientImpl::is_valid
virtual bool is_valid() const
Definition httplib.h:8533
httplib::ClientImpl::set_read_timeout
void set_read_timeout(time_t sec, time_t usec=0)
Definition httplib.h:10276
httplib::ClientImpl::set_bearer_token_auth
void set_bearer_token_auth(const std::string &token)
Definition httplib.h:10296
httplib::ContentReader
Definition httplib.h:691
httplib::ContentReader::reader_
Reader reader_
Definition httplib.h:709
httplib::ContentReader::ContentReader
ContentReader(Reader reader, FormDataReader multipart_reader)
Definition httplib.h:697
httplib::ContentReader::operator()
bool operator()(FormDataHeader header, ContentReceiver receiver) const
Definition httplib.h:701
httplib::ContentReader::operator()
bool operator()(ContentReceiver receiver) const
Definition httplib.h:705
httplib::ContentReader::Reader
std::function< bool(ContentReceiver receiver)> Reader
Definition httplib.h:693
httplib::ContentReader::formdata_reader_
FormDataReader formdata_reader_
Definition httplib.h:710
httplib::ContentReader::FormDataReader
std::function< bool(FormDataHeader header, ContentReceiver receiver)> FormDataReader
Definition httplib.h:695
httplib::DataSink
Definition httplib.h:634
httplib::DataSink::write
std::function< bool(const char *data, size_t data_len)> write
Definition httplib.h:643
httplib::DataSink::DataSink
DataSink(DataSink &&)=delete
httplib::DataSink::DataSink
DataSink(const DataSink &)=delete
httplib::DataSink::operator=
DataSink & operator=(DataSink &&)=delete
httplib::DataSink::operator=
DataSink & operator=(const DataSink &)=delete
httplib::DataSink::done
std::function< void()> done
Definition httplib.h:645
httplib::DataSink::is_writable
std::function< bool()> is_writable
Definition httplib.h:644
httplib::DataSink::DataSink
DataSink()
Definition httplib.h:636
httplib::DataSink::os
std::ostream os
Definition httplib.h:647
httplib::DataSink::done_with_trailer
std::function< void(const Headers &trailer)> done_with_trailer
Definition httplib.h:646
httplib::Result
Definition httplib.h:1326
httplib::Result::has_request_header
bool has_request_header(const std::string &key) const
Definition httplib.h:6968
httplib::Result::value
Response & value()
Definition httplib.h:1349
httplib::Result::Result
Result()=default
httplib::Result::value
const Response & value() const
Definition httplib.h:1348
httplib::Result::get_request_header_value_count
size_t get_request_header_value_count(const std::string &key) const
Definition httplib.h:6979
httplib::Result::operator*
const Response & operator*() const
Definition httplib.h:1350
httplib::Result::operator==
bool operator==(std::nullptr_t) const
Definition httplib.h:1346
httplib::Result::Result
Result(std::unique_ptr< Response > &&res, Error err, Headers &&request_headers=Headers{})
Definition httplib.h:1329
httplib::Result::operator!=
bool operator!=(std::nullptr_t) const
Definition httplib.h:1347
httplib::Result::operator->
const Response * operator->() const
Definition httplib.h:1352
httplib::Result::operator->
Response * operator->()
Definition httplib.h:1353
httplib::Result::get_request_header_value_u64
size_t get_request_header_value_u64(const std::string &key, size_t def=0, size_t id=0) const
Definition httplib.h:2288
httplib::Result::error
Error error() const
Definition httplib.h:1356
httplib::Result::operator*
Response & operator*()
Definition httplib.h:1351
httplib::Result::get_request_header_value
std::string get_request_header_value(const std::string &key, const char *def="", size_t id=0) const
Definition httplib.h:6972
httplib::Server
Definition httplib.h:1057
httplib::Server::read_timeout_usec_
time_t read_timeout_usec_
Definition httplib.h:1173
httplib::Server::set_default_file_mimetype
Server & set_default_file_mimetype(const std::string &mime)
Definition httplib.h:7368
httplib::Server::stop
void stop()
Definition httplib.h:7525
httplib::Server::Put
Server & Put(const std::string &pattern, Handler handler)
Definition httplib.h:7292
httplib::Server::HandlerWithResponse
std::function< HandlerResponse(const Request &, Response &)> HandlerWithResponse
Definition httplib.h:1069
httplib::Server::Delete
Server & Delete(const std::string &pattern, Handler handler)
Definition httplib.h:7316
httplib::Server::set_pre_routing_handler
Server & set_pre_routing_handler(HandlerWithResponse handler)
Definition httplib.h:7398
httplib::Server::set_write_timeout
Server & set_write_timeout(time_t sec, time_t usec=0)
Definition httplib.h:7481
httplib::Server::idle_interval_usec_
time_t idle_interval_usec_
Definition httplib.h:1177
httplib::Server::keep_alive_max_count_
size_t keep_alive_max_count_
Definition httplib.h:1170
httplib::Server::write_timeout_sec_
time_t write_timeout_sec_
Definition httplib.h:1174
httplib::Server::set_error_handler
Server & set_error_handler(ErrorHandlerFunc &&handler)
Definition httplib.h:1105
httplib::Server::listen
bool listen(const std::string &host, int port, int socket_flags=0)
Definition httplib.h:7512
httplib::Server::set_base_dir
bool set_base_dir(const std::string &dir, const std::string &mount_point=std::string())
Definition httplib.h:7333
httplib::Server::decommission
void decommission()
Definition httplib.h:7535
httplib::Server::listen_after_bind
bool listen_after_bind()
Definition httplib.h:7510
httplib::Server::set_tcp_nodelay
Server & set_tcp_nodelay(bool on)
Definition httplib.h:7439
httplib::Server::HandlerWithContentReader
std::function< void(const Request &, Response &, const ContentReader &content_reader)> HandlerWithContentReader
Definition httplib.h:1072
httplib::Server::set_header_writer
Server & set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Definition httplib.h:7459
httplib::Server::set_payload_max_length
Server & set_payload_max_length(size_t length)
Definition httplib.h:7493
httplib::Server::Options
Server & Options(const std::string &pattern, Handler handler)
Definition httplib.h:7328
httplib::Server::is_valid
virtual bool is_valid() const
Definition httplib.h:8454
httplib::Server::bind_to_any_port
int bind_to_any_port(const std::string &host, int socket_flags=0)
Definition httplib.h:7504
httplib::Server::Server
Server()
Definition httplib.h:7256
httplib::Server::idle_interval_sec_
time_t idle_interval_sec_
Definition httplib.h:1176
httplib::Server::bind_to_port
bool bind_to_port(const std::string &host, int port, int socket_flags=0)
Definition httplib.h:7498
httplib::Server::write_timeout_usec_
time_t write_timeout_usec_
Definition httplib.h:1175
httplib::Server::keep_alive_timeout_sec_
time_t keep_alive_timeout_sec_
Definition httplib.h:1171
httplib::Server::HandlerResponse
HandlerResponse
Definition httplib.h:1064
httplib::Server::HandlerResponse::Unhandled
@ Unhandled
httplib::Server::HandlerResponse::Handled
@ Handled
httplib::Server::Patch
Server & Patch(const std::string &pattern, Handler handler)
Definition httplib.h:7304
httplib::Server::set_default_headers
Server & set_default_headers(Headers headers)
Definition httplib.h:7454
httplib::Server::Get
Server & Get(const std::string &pattern, Handler handler)
Definition httplib.h:7275
httplib::Server::Expect100ContinueHandler
std::function< int(const Request &, Response &)> Expect100ContinueHandler
Definition httplib.h:1075
httplib::Server::set_file_request_handler
Server & set_file_request_handler(Handler handler)
Definition httplib.h:7373
httplib::Server::Handler
std::function< void(const Request &, Response &)> Handler
Definition httplib.h:1059
httplib::Server::svr_sock_
std::atomic< socket_t > svr_sock_
Definition httplib.h:1169
httplib::Server::wait_until_ready
void wait_until_ready() const
Definition httplib.h:7519
httplib::Server::set_post_routing_handler
Server & set_post_routing_handler(Handler handler)
Definition httplib.h:7403
httplib::Server::is_running
bool is_running() const
Definition httplib.h:7517
httplib::Server::Post
Server & Post(const std::string &pattern, Handler handler)
Definition httplib.h:7280
httplib::Server::set_idle_interval
Server & set_idle_interval(time_t sec, time_t usec=0)
Definition httplib.h:7487
httplib::Server::set_address_family
Server & set_address_family(int family)
Definition httplib.h:7434
httplib::Server::ExceptionHandler
std::function< void(const Request &, Response &, std::exception_ptr ep)> ExceptionHandler
Definition httplib.h:1062
httplib::Server::remove_mount_point
bool remove_mount_point(const std::string &mount_point)
Definition httplib.h:7351
httplib::Server::set_keep_alive_timeout
Server & set_keep_alive_timeout(time_t sec)
Definition httplib.h:7470
httplib::Server::new_task_queue
std::function< TaskQueue *(void)> new_task_queue
Definition httplib.h:1160
httplib::Server::process_request
bool process_request(Stream &strm, const std::string &remote_addr, int remote_port, const std::string &local_addr, int local_port, bool close_connection, bool &connection_closed, const std::function< void(Request &)> &setup_request)
Definition httplib.h:8254
httplib::Server::set_logger
Server & set_logger(Logger logger)
Definition httplib.h:7413
httplib::Server::set_mount_point
bool set_mount_point(const std::string &mount_point, const std::string &dir, Headers headers=Headers())
Definition httplib.h:7338
httplib::Server::set_socket_options
Server & set_socket_options(SocketOptions socket_options)
Definition httplib.h:7449
httplib::Server::set_pre_compression_logger
Server & set_pre_compression_logger(Logger logger)
Definition httplib.h:7423
httplib::Server::read_timeout_sec_
time_t read_timeout_sec_
Definition httplib.h:1172
httplib::Server::set_pre_request_handler
Server & set_pre_request_handler(HandlerWithResponse handler)
Definition httplib.h:7408
httplib::Server::set_exception_handler
Server & set_exception_handler(ExceptionHandler handler)
Definition httplib.h:7393
httplib::Server::set_error_logger
Server & set_error_logger(ErrorLogger error_logger)
Definition httplib.h:7418
httplib::Server::set_expect_100_continue_handler
Server & set_expect_100_continue_handler(Expect100ContinueHandler handler)
Definition httplib.h:7429
httplib::Server::set_ipv6_v6only
Server & set_ipv6_v6only(bool on)
Definition httplib.h:7444
httplib::Server::payload_max_length_
size_t payload_max_length_
Definition httplib.h:1178
httplib::Server::~Server
virtual ~Server()
httplib::Server::set_file_extension_and_mimetype_mapping
Server & set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime)
Definition httplib.h:7362
httplib::Server::set_keep_alive_max_count
Server & set_keep_alive_max_count(size_t count)
Definition httplib.h:7465
httplib::Server::set_read_timeout
Server & set_read_timeout(time_t sec, time_t usec=0)
Definition httplib.h:7475
httplib::Stream
Definition httplib.h:840
httplib::Stream::wait_writable
virtual bool wait_writable() const =0
httplib::Stream::write
ssize_t write(const char *ptr)
Definition httplib.h:6985
httplib::Stream::wait_readable
virtual bool wait_readable() const =0
httplib::Stream::write
virtual ssize_t write(const char *ptr, size_t size)=0
httplib::Stream::read
virtual ssize_t read(char *ptr, size_t size)=0
httplib::Stream::get_remote_ip_and_port
virtual void get_remote_ip_and_port(std::string &ip, int &port) const =0
httplib::Stream::get_local_ip_and_port
virtual void get_local_ip_and_port(std::string &ip, int &port) const =0
httplib::Stream::duration
virtual time_t duration() const =0
httplib::Stream::~Stream
virtual ~Stream()=default
httplib::Stream::socket
virtual socket_t socket() const =0
httplib::Stream::is_readable
virtual bool is_readable() const =0
httplib::TaskQueue
Definition httplib.h:860
httplib::TaskQueue::enqueue
virtual bool enqueue(std::function< void()> fn)=0
httplib::TaskQueue::on_idle
virtual void on_idle()
Definition httplib.h:868
httplib::TaskQueue::TaskQueue
TaskQueue()=default
httplib::TaskQueue::~TaskQueue
virtual ~TaskQueue()=default
httplib::TaskQueue::shutdown
virtual void shutdown()=0
httplib::ThreadPool
Definition httplib.h:871
httplib::ThreadPool::worker
friend struct worker
Definition httplib.h:943
httplib::ThreadPool::ThreadPool
ThreadPool(size_t n, size_t mqr=0)
Definition httplib.h:873
httplib::ThreadPool::shutdown
void shutdown() override
Definition httplib.h:897
httplib::ThreadPool::ThreadPool
ThreadPool(const ThreadPool &)=delete
httplib::ThreadPool::~ThreadPool
~ThreadPool() override=default
httplib::ThreadPool::enqueue
bool enqueue(std::function< void()> fn) override
Definition httplib.h:884
httplib::detail::MatcherBase
Definition httplib.h:981
httplib::detail::MatcherBase::match
virtual bool match(Request &request) const =0
httplib::detail::MatcherBase::~MatcherBase
virtual ~MatcherBase()=default
httplib::detail::MatcherBase::MatcherBase
MatcherBase(std::string pattern)
Definition httplib.h:983
httplib::detail::MatcherBase::pattern
const std::string & pattern() const
Definition httplib.h:986
httplib::detail::PathParamsMatcher
Captures parameters in request path and stores them in Request::path_params.
Definition httplib.h:1013
httplib::detail::PathParamsMatcher::match
bool match(Request &request) const override
Definition httplib.h:7205
httplib::detail::RegexMatcher
Performs std::regex_match on request path and stores the result in Request::matches.
Definition httplib.h:1042
httplib::detail::RegexMatcher::match
bool match(Request &request) const override
Definition httplib.h:7248
httplib::detail::RegexMatcher::RegexMatcher
RegexMatcher(const std::string &pattern)
Definition httplib.h:1044
SSL
const int SSL
Definition echo_server.c:4
CPPHTTPLIB_VERSION
#define CPPHTTPLIB_VERSION
Definition httplib.h:11
CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
Definition httplib.h:130
CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH
Definition httplib.h:134
CPPHTTPLIB_HEADER_MAX_COUNT
#define CPPHTTPLIB_HEADER_MAX_COUNT
Definition httplib.h:122
CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
Definition httplib.h:66
INVALID_SOCKET
#define INVALID_SOCKET
Definition httplib.h:272
CPPHTTPLIB_SEND_FLAGS
#define CPPHTTPLIB_SEND_FLAGS
Definition httplib.h:177
CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
Definition httplib.h:50
CPPHTTPLIB_SEND_BUFSIZ
#define CPPHTTPLIB_SEND_BUFSIZ
Definition httplib.h:158
CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
Definition httplib.h:70
CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
Definition httplib.h:74
CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
Definition httplib.h:94
CPPHTTPLIB_LISTEN_BACKLOG
#define CPPHTTPLIB_LISTEN_BACKLOG
Definition httplib.h:181
CPPHTTPLIB_IDLE_INTERVAL_SECOND
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND
Definition httplib.h:102
CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT
Definition httplib.h:54
CPPHTTPLIB_RANGE_MAX_COUNT
#define CPPHTTPLIB_RANGE_MAX_COUNT
Definition httplib.h:142
CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
Definition httplib.h:138
CPPHTTPLIB_IDLE_INTERVAL_USECOND
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND
Definition httplib.h:109
CPPHTTPLIB_IPV6_V6ONLY
#define CPPHTTPLIB_IPV6_V6ONLY
Definition httplib.h:150
CPPHTTPLIB_RECV_FLAGS
#define CPPHTTPLIB_RECV_FLAGS
Definition httplib.h:173
CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
Definition httplib.h:82
socket_t
int socket_t
Definition httplib.h:270
CPPHTTPLIB_HEADER_MAX_LENGTH
#define CPPHTTPLIB_HEADER_MAX_LENGTH
Definition httplib.h:118
CPPHTTPLIB_COMPRESSION_BUFSIZ
#define CPPHTTPLIB_COMPRESSION_BUFSIZ
Definition httplib.h:162
CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
Definition httplib.h:46
CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT
Definition httplib.h:166
CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
Definition httplib.h:98
CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
Definition httplib.h:78
CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_RECV_BUFSIZ
Definition httplib.h:154
CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
Definition httplib.h:114
CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_REDIRECT_MAX_COUNT
Definition httplib.h:126
CPPHTTPLIB_MAX_LINE_LENGTH
#define CPPHTTPLIB_MAX_LINE_LENGTH
Definition httplib.h:185
CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
Definition httplib.h:86
CPPHTTPLIB_TCP_NODELAY
#define CPPHTTPLIB_TCP_NODELAY
Definition httplib.h:146
CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
Definition httplib.h:90
CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
Definition httplib.h:58
CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
Definition httplib.h:62
httplib::detail::case_ignore::unordered_set
std::unordered_set< T, detail::case_ignore::hash, detail::case_ignore::equal_to > unordered_set
Definition httplib.h:461
httplib::detail::case_ignore::equal
bool equal(const std::string &a, const std::string &b)
Definition httplib.h:430
httplib::detail::case_ignore::to_lower
unsigned char to_lower(int c)
Definition httplib.h:406
httplib::detail::make_unique
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
Definition httplib.h:393
httplib::detail::set_socket_opt_impl
bool set_socket_opt_impl(socket_t sock, int level, int optname, const void *optval, socklen_t optlen)
Definition httplib.h:2091
httplib::detail::write_headers
ssize_t write_headers(Stream &strm, const Headers &headers)
Definition httplib.h:5018
httplib::detail::set_socket_opt
bool set_socket_opt(socket_t sock, int level, int optname, int opt)
Definition httplib.h:2102
httplib::detail::set_socket_opt_time
bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec, time_t usec)
Definition httplib.h:2106
httplib
Definition httplib.h:379
httplib::SocketOptions
std::function< void(socket_t sock)> SocketOptions
Definition httplib.h:961
httplib::operator<<
std::ostream & operator<<(std::ostream &os, const Error &obj)
Definition httplib.h:2282
httplib::FormDataProvider::name
std::string name
Definition httplib.h:676
httplib::FormData::content
std::string content
Definition httplib.h:594
httplib::FormData::filename
std::string filename
Definition httplib.h:595
httplib::make_range_header
std::pair< std::string, std::string > make_range_header(const Ranges &ranges)
Definition httplib.h:6702
httplib::encode_path_component
std::string encode_path_component(const std::string &component)
Definition httplib.h:6541
httplib::Ranges
std::vector< Range > Ranges
Definition httplib.h:714
httplib::Match
std::smatch Match
Definition httplib.h:584
httplib::hosted_at
std::string hosted_at(const std::string &hostname)
Definition httplib.h:6420
httplib::decode_uri_component
std::string decode_uri_component(const std::string &value)
Definition httplib.h:6501
httplib::FormField::content
std::string content
Definition httplib.h:602
httplib::FormDataProvider::filename
std::string filename
Definition httplib.h:678
httplib::ContentProviderWithoutLength
std::function< bool(size_t offset, DataSink &sink)> ContentProviderWithoutLength
Definition httplib.h:671
httplib::DownloadProgress
std::function< bool(size_t current, size_t total)> DownloadProgress
Definition httplib.h:586
httplib::Logger
std::function< void(const Request &, const Response &)> Logger
Definition httplib.h:955
httplib::FormData::content_type
std::string content_type
Definition httplib.h:596
httplib::Error
Error
Definition httplib.h:1284
httplib::Error::Canceled
@ Canceled
httplib::Error::ConnectionTimeout
@ ConnectionTimeout
httplib::Error::Write
@ Write
httplib::Error::ExceedUriMaxLength
@ ExceedUriMaxLength
httplib::Error::TooManyFormDataFiles
@ TooManyFormDataFiles
httplib::Error::SSLServerVerification
@ SSLServerVerification
httplib::Error::SSLServerHostnameVerification
@ SSLServerHostnameVerification
httplib::Error::ExceedMaxSocketDescriptorCount
@ ExceedMaxSocketDescriptorCount
httplib::Error::InvalidHTTPMethod
@ InvalidHTTPMethod
httplib::Error::OpenFile
@ OpenFile
httplib::Error::ResourceExhaustion
@ ResourceExhaustion
httplib::Error::Success
@ Success
httplib::Error::BindIPAddress
@ BindIPAddress
httplib::Error::Read
@ Read
httplib::Error::InvalidRequestLine
@ InvalidRequestLine
httplib::Error::Compression
@ Compression
httplib::Error::GetSockName
@ GetSockName
httplib::Error::Unknown
@ Unknown
httplib::Error::InvalidHeaders
@ InvalidHeaders
httplib::Error::SSLConnection
@ SSLConnection
httplib::Error::UnsupportedAddressFamily
@ UnsupportedAddressFamily
httplib::Error::ProxyConnection
@ ProxyConnection
httplib::Error::InvalidRangeHeader
@ InvalidRangeHeader
httplib::Error::ExceedMaxPayloadSize
@ ExceedMaxPayloadSize
httplib::Error::MultipartParsing
@ MultipartParsing
httplib::Error::Connection
@ Connection
httplib::Error::Listen
@ Listen
httplib::Error::HTTPParsing
@ HTTPParsing
httplib::Error::ExceedRedirectCount
@ ExceedRedirectCount
httplib::Error::InvalidHTTPVersion
@ InvalidHTTPVersion
httplib::Error::SSLLoadingCerts
@ SSLLoadingCerts
httplib::Error::UnsupportedMultipartBoundaryChars
@ UnsupportedMultipartBoundaryChars
httplib::Error::SSLPeerCouldBeClosed_
@ SSLPeerCouldBeClosed_
httplib::Range
std::pair< ssize_t, ssize_t > Range
Definition httplib.h:713
httplib::to_string
std::string to_string(Error error)
Definition httplib.h:2239
httplib::FormField::headers
Headers headers
Definition httplib.h:603
httplib::FormDataProvider::content_type
std::string content_type
Definition httplib.h:679
httplib::UploadFormData::filename
std::string filename
Definition httplib.h:629
httplib::ErrorLogger
std::function< void(const Error &, const Request *)> ErrorLogger
Definition httplib.h:959
httplib::Params
std::multimap< std::string, std::string > Params
Definition httplib.h:583
httplib::FormData::headers
Headers headers
Definition httplib.h:597
httplib::UploadFormData::content_type
std::string content_type
Definition httplib.h:630
httplib::Headers
std::unordered_multimap< std::string, std::string, detail::case_ignore::hash, detail::case_ignore::equal_to > Headers
Definition httplib.h:581
httplib::encode_uri_component
std::string encode_uri_component(const std::string &value)
Definition httplib.h:6458
httplib::make_basic_authentication_header
std::pair< std::string, std::string > make_basic_authentication_header(const std::string &username, const std::string &password, bool is_proxy=false)
Definition httplib.h:6716
httplib::UploadFormData::content
std::string content
Definition httplib.h:628
httplib::status_message
const char * status_message(int status)
Definition httplib.h:2130
httplib::FormData::name
std::string name
Definition httplib.h:593
httplib::SSLVerifierResponse
SSLVerifierResponse
Definition httplib.h:495
httplib::CertificateAccepted
@ CertificateAccepted
Definition httplib.h:499
httplib::CertificateRejected
@ CertificateRejected
Definition httplib.h:501
httplib::NoDecisionMade
@ NoDecisionMade
Definition httplib.h:497
httplib::get_bearer_token_auth
std::string get_bearer_token_auth(const Request &req)
Definition httplib.h:2206
httplib::FormField::name
std::string name
Definition httplib.h:601
httplib::FormDataProvider::provider
ContentProviderWithoutLength provider
Definition httplib.h:677
httplib::make_bearer_token_authentication_header
std::pair< std::string, std::string > make_bearer_token_authentication_header(const std::string &token, bool is_proxy=false)
Definition httplib.h:6724
httplib::ContentReceiverWithProgress
std::function< bool(const char *data, size_t data_length, size_t offset, size_t total_length)> ContentReceiverWithProgress
Definition httplib.h:684
httplib::ContentProvider
std::function< bool(size_t offset, size_t length, DataSink &sink)> ContentProvider
Definition httplib.h:668
httplib::FormDataProviderItems
std::vector< FormDataProvider > FormDataProviderItems
Definition httplib.h:681
httplib::UploadProgress
std::function< bool(size_t current, size_t total)> UploadProgress
Definition httplib.h:587
httplib::FormFiles
std::multimap< std::string, FormData > FormFiles
Definition httplib.h:607
httplib::FormDataHeader
std::function< bool(const FormData &file)> FormDataHeader
Definition httplib.h:689
httplib::decode_path_component
std::string decode_path_component(const std::string &component)
Definition httplib.h:6576
httplib::decode_uri
std::string decode_uri(const std::string &value)
Definition httplib.h:6521
httplib::UploadFormDataItems
std::vector< UploadFormData > UploadFormDataItems
Definition httplib.h:632
httplib::FormFields
std::multimap< std::string, FormField > FormFields
Definition httplib.h:605
httplib::UploadFormData::name
std::string name
Definition httplib.h:627
httplib::encode_query_component
std::string encode_query_component(const std::string &component, bool space_as_plus=true)
Definition httplib.h:6612
httplib::ContentReceiver
std::function< bool(const char *data, size_t data_length)> ContentReceiver
Definition httplib.h:687
httplib::ResponseHandler
std::function< bool(const Response &response)> ResponseHandler
Definition httplib.h:590
httplib::decode_query_component
std::string decode_query_component(const std::string &component, bool plus_as_space=true)
Definition httplib.h:6666
httplib::StatusCode
StatusCode
Definition httplib.h:504
httplib::Conflict_409
@ Conflict_409
Definition httplib.h:544
httplib::IMUsed_226
@ IMUsed_226
Definition httplib.h:521
httplib::SwitchingProtocol_101
@ SwitchingProtocol_101
Definition httplib.h:507
httplib::MisdirectedRequest_421
@ MisdirectedRequest_421
Definition httplib.h:554
httplib::FailedDependency_424
@ FailedDependency_424
Definition httplib.h:557
httplib::Found_302
@ Found_302
Definition httplib.h:526
httplib::MultipleChoices_300
@ MultipleChoices_300
Definition httplib.h:524
httplib::UnavailableForLegalReasons_451
@ UnavailableForLegalReasons_451
Definition httplib.h:563
httplib::LoopDetected_508
@ LoopDetected_508
Definition httplib.h:574
httplib::ServiceUnavailable_503
@ ServiceUnavailable_503
Definition httplib.h:569
httplib::EarlyHints_103
@ EarlyHints_103
Definition httplib.h:509
httplib::ProxyAuthenticationRequired_407
@ ProxyAuthenticationRequired_407
Definition httplib.h:542
httplib::Processing_102
@ Processing_102
Definition httplib.h:508
httplib::Locked_423
@ Locked_423
Definition httplib.h:556
httplib::PaymentRequired_402
@ PaymentRequired_402
Definition httplib.h:537
httplib::NonAuthoritativeInformation_203
@ NonAuthoritativeInformation_203
Definition httplib.h:515
httplib::UriTooLong_414
@ UriTooLong_414
Definition httplib.h:549
httplib::Accepted_202
@ Accepted_202
Definition httplib.h:514
httplib::ResetContent_205
@ ResetContent_205
Definition httplib.h:517
httplib::Forbidden_403
@ Forbidden_403
Definition httplib.h:538
httplib::Gone_410
@ Gone_410
Definition httplib.h:545
httplib::UnsupportedMediaType_415
@ UnsupportedMediaType_415
Definition httplib.h:550
httplib::NotExtended_510
@ NotExtended_510
Definition httplib.h:575
httplib::NotModified_304
@ NotModified_304
Definition httplib.h:528
httplib::VariantAlsoNegotiates_506
@ VariantAlsoNegotiates_506
Definition httplib.h:572
httplib::unused_306
@ unused_306
Definition httplib.h:530
httplib::TooManyRequests_429
@ TooManyRequests_429
Definition httplib.h:561
httplib::RequestTimeout_408
@ RequestTimeout_408
Definition httplib.h:543
httplib::OK_200
@ OK_200
Definition httplib.h:512
httplib::NetworkAuthenticationRequired_511
@ NetworkAuthenticationRequired_511
Definition httplib.h:576
httplib::AlreadyReported_208
@ AlreadyReported_208
Definition httplib.h:520
httplib::MultiStatus_207
@ MultiStatus_207
Definition httplib.h:519
httplib::ImATeapot_418
@ ImATeapot_418
Definition httplib.h:553
httplib::NotAcceptable_406
@ NotAcceptable_406
Definition httplib.h:541
httplib::InternalServerError_500
@ InternalServerError_500
Definition httplib.h:566
httplib::ExpectationFailed_417
@ ExpectationFailed_417
Definition httplib.h:552
httplib::MethodNotAllowed_405
@ MethodNotAllowed_405
Definition httplib.h:540
httplib::NotFound_404
@ NotFound_404
Definition httplib.h:539
httplib::TemporaryRedirect_307
@ TemporaryRedirect_307
Definition httplib.h:531
httplib::PermanentRedirect_308
@ PermanentRedirect_308
Definition httplib.h:532
httplib::SeeOther_303
@ SeeOther_303
Definition httplib.h:527
httplib::Unauthorized_401
@ Unauthorized_401
Definition httplib.h:536
httplib::PreconditionFailed_412
@ PreconditionFailed_412
Definition httplib.h:547
httplib::Created_201
@ Created_201
Definition httplib.h:513
httplib::NotImplemented_501
@ NotImplemented_501
Definition httplib.h:567
httplib::UseProxy_305
@ UseProxy_305
Definition httplib.h:529
httplib::NoContent_204
@ NoContent_204
Definition httplib.h:516
httplib::InsufficientStorage_507
@ InsufficientStorage_507
Definition httplib.h:573
httplib::TooEarly_425
@ TooEarly_425
Definition httplib.h:558
httplib::BadGateway_502
@ BadGateway_502
Definition httplib.h:568
httplib::MovedPermanently_301
@ MovedPermanently_301
Definition httplib.h:525
httplib::PreconditionRequired_428
@ PreconditionRequired_428
Definition httplib.h:560
httplib::PartialContent_206
@ PartialContent_206
Definition httplib.h:518
httplib::LengthRequired_411
@ LengthRequired_411
Definition httplib.h:546
httplib::PayloadTooLarge_413
@ PayloadTooLarge_413
Definition httplib.h:548
httplib::RangeNotSatisfiable_416
@ RangeNotSatisfiable_416
Definition httplib.h:551
httplib::UnprocessableContent_422
@ UnprocessableContent_422
Definition httplib.h:555
httplib::RequestHeaderFieldsTooLarge_431
@ RequestHeaderFieldsTooLarge_431
Definition httplib.h:562
httplib::Continue_100
@ Continue_100
Definition httplib.h:506
httplib::BadRequest_400
@ BadRequest_400
Definition httplib.h:535
httplib::UpgradeRequired_426
@ UpgradeRequired_426
Definition httplib.h:559
httplib::GatewayTimeout_504
@ GatewayTimeout_504
Definition httplib.h:570
httplib::HttpVersionNotSupported_505
@ HttpVersionNotSupported_505
Definition httplib.h:571
httplib::encode_uri
std::string encode_uri(const std::string &value)
Definition httplib.h:6479
httplib::default_socket_options
void default_socket_options(socket_t sock)
Definition httplib.h:2120
httplib::append_query_params
std::string append_query_params(const std::string &path, const Params &params)
Definition httplib.h:6691
httplib::ContentProviderResourceReleaser
std::function< void(bool success)> ContentProviderResourceReleaser
Definition httplib.h:673
httplib::FormData
Definition httplib.h:592
httplib::FormField
Definition httplib.h:600
httplib::UploadFormData
Definition httplib.h:626
httplib::FormDataProvider
Definition httplib.h:675
maph::open
auto open(const std::string &path)
Definition maph.hpp:958
httplib::ClientImpl::Socket
Definition httplib.h:1564
httplib::ClientImpl::Socket::is_open
bool is_open() const
Definition httplib.h:1570
httplib::ClientImpl::Socket::sock
socket_t sock
Definition httplib.h:1565
httplib::MultipartFormData
Definition httplib.h:609
httplib::MultipartFormData::get_field
std::string get_field(const std::string &key, size_t id=0) const
Definition httplib.h:6796
httplib::MultipartFormData::has_file
bool has_file(const std::string &key) const
Definition httplib.h:6843
httplib::MultipartFormData::get_file
FormData get_file(const std::string &key, size_t id=0) const
Definition httplib.h:6824
httplib::MultipartFormData::has_field
bool has_field(const std::string &key) const
Definition httplib.h:6815
httplib::MultipartFormData::get_file_count
size_t get_file_count(const std::string &key) const
Definition httplib.h:6847
httplib::MultipartFormData::get_files
std::vector< FormData > get_files(const std::string &key) const
Definition httplib.h:6834
httplib::MultipartFormData::files
FormFiles files
Definition httplib.h:611
httplib::MultipartFormData::fields
FormFields fields
Definition httplib.h:610
httplib::MultipartFormData::get_fields
std::vector< std::string > get_fields(const std::string &key) const
Definition httplib.h:6806
httplib::MultipartFormData::get_field_count
size_t get_field_count(const std::string &key) const
Definition httplib.h:6819
httplib::Request
Definition httplib.h:716
httplib::Request::form
MultipartFormData form
Definition httplib.h:733
httplib::Request::has_param
bool has_param(const std::string &key) const
Definition httplib.h:6772
httplib::Request::has_header
bool has_header(const std::string &key) const
Definition httplib.h:6732
httplib::Request::download_progress
DownloadProgress download_progress
Definition httplib.h:743
httplib::Request::get_param_value_count
size_t get_param_value_count(const std::string &key) const
Definition httplib.h:6785
httplib::Request::accept_content_types
std::vector< std::string > accept_content_types
Definition httplib.h:740
httplib::Request::get_trailer_value_count
size_t get_trailer_value_count(const std::string &key) const
Definition httplib.h:6767
httplib::Request::content_receiver
ContentReceiverWithProgress content_receiver
Definition httplib.h:742
httplib::Request::local_addr
std::string local_addr
Definition httplib.h:727
httplib::Request::path_params
std::unordered_map< std::string, std::string > path_params
Definition httplib.h:736
httplib::Request::upload_progress
UploadProgress upload_progress
Definition httplib.h:744
httplib::Request::trailers
Headers trailers
Definition httplib.h:722
httplib::Request::remote_addr
std::string remote_addr
Definition httplib.h:725
httplib::Request::get_header_value
std::string get_header_value(const std::string &key, const char *def="", size_t id=0) const
Definition httplib.h:6736
httplib::Request::remote_port
int remote_port
Definition httplib.h:726
httplib::Request::params
Params params
Definition httplib.h:720
httplib::Request::target
std::string target
Definition httplib.h:732
httplib::Request::get_header_value_u64
size_t get_header_value_u64(const std::string &key, size_t def=0, size_t id=0) const
Definition httplib.h:2079
httplib::Request::get_param_value
std::string get_param_value(const std::string &key, size_t id=0) const
Definition httplib.h:6776
httplib::Request::authorization_count_
size_t authorization_count_
Definition httplib.h:772
httplib::Request::is_multipart_form_data
bool is_multipart_form_data() const
Definition httplib.h:6790
httplib::Request::matches
Match matches
Definition httplib.h:735
httplib::Request::version
std::string version
Definition httplib.h:731
httplib::Request::has_trailer
bool has_trailer(const std::string &key) const
Definition httplib.h:6754
httplib::Request::start_time_
std::chrono::time_point< std::chrono::steady_clock > start_time_
Definition httplib.h:773
httplib::Request::content_provider_
ContentProvider content_provider_
Definition httplib.h:770
httplib::Request::get_trailer_value
std::string get_trailer_value(const std::string &key, size_t id=0) const
Definition httplib.h:6758
httplib::Request::content_length_
size_t content_length_
Definition httplib.h:769
httplib::Request::ranges
Ranges ranges
Definition httplib.h:734
httplib::Request::path
std::string path
Definition httplib.h:718
httplib::Request::is_chunked_content_provider_
bool is_chunked_content_provider_
Definition httplib.h:771
httplib::Request::is_connection_closed
std::function< bool()> is_connection_closed
Definition httplib.h:737
httplib::Request::body
std::string body
Definition httplib.h:723
httplib::Request::redirect_count_
size_t redirect_count_
Definition httplib.h:768
httplib::Request::matched_route
std::string matched_route
Definition httplib.h:719
httplib::Request::get_header_value_count
size_t get_header_value_count(const std::string &key) const
Definition httplib.h:6741
httplib::Request::response_handler
ResponseHandler response_handler
Definition httplib.h:741
httplib::Request::local_port
int local_port
Definition httplib.h:728
httplib::Request::method
std::string method
Definition httplib.h:717
httplib::Request::set_header
void set_header(const std::string &key, const std::string &val)
Definition httplib.h:6746
httplib::Request::headers
Headers headers
Definition httplib.h:721
httplib::Response
Definition httplib.h:777
httplib::Response::content_provider_
ContentProvider content_provider_
Definition httplib.h:832
httplib::Response::is_chunked_content_provider_
bool is_chunked_content_provider_
Definition httplib.h:834
httplib::Response::trailers
Headers trailers
Definition httplib.h:782
httplib::Response::set_redirect
void set_redirect(const std::string &url, int status=StatusCode::Found_302)
Definition httplib.h:6893
httplib::Response::operator=
Response & operator=(Response &&)=default
httplib::Response::file_content_path_
std::string file_content_path_
Definition httplib.h:836
httplib::Response::get_header_value_u64
size_t get_header_value_u64(const std::string &key, size_t def=0, size_t id=0) const
Definition httplib.h:2084
httplib::Response::Response
Response()=default
httplib::Response::content_length_
size_t content_length_
Definition httplib.h:831
httplib::Response::set_content_provider
void set_content_provider(size_t length, const std::string &content_type, ContentProvider provider, ContentProviderResourceReleaser resource_releaser=nullptr)
Definition httplib.h:6927
httplib::Response::version
std::string version
Definition httplib.h:778
httplib::Response::headers
Headers headers
Definition httplib.h:781
httplib::Response::set_content
void set_content(const char *s, size_t n, const std::string &content_type)
Definition httplib.h:6904
httplib::Response::has_header
bool has_header(const std::string &key) const
Definition httplib.h:6853
httplib::Response::content_provider_success_
bool content_provider_success_
Definition httplib.h:835
httplib::Response::content_provider_resource_releaser_
ContentProviderResourceReleaser content_provider_resource_releaser_
Definition httplib.h:833
httplib::Response::get_header_value
std::string get_header_value(const std::string &key, const char *def="", size_t id=0) const
Definition httplib.h:6857
httplib::Response::set_chunked_content_provider
void set_chunked_content_provider(const std::string &content_type, ContentProviderWithoutLength provider, ContentProviderResourceReleaser resource_releaser=nullptr)
Definition httplib.h:6947
httplib::Response::status
int status
Definition httplib.h:779
httplib::Response::get_trailer_value
std::string get_trailer_value(const std::string &key, size_t id=0) const
Definition httplib.h:6879
httplib::Response::reason
std::string reason
Definition httplib.h:780
httplib::Response::get_trailer_value_count
size_t get_trailer_value_count(const std::string &key) const
Definition httplib.h:6888
httplib::Response::body
std::string body
Definition httplib.h:783
httplib::Response::set_file_content
void set_file_content(const std::string &path, const std::string &content_type)
Definition httplib.h:6957
httplib::Response::has_trailer
bool has_trailer(const std::string &key) const
Definition httplib.h:6875
httplib::Response::operator=
Response & operator=(const Response &)=default
httplib::Response::Response
Response(const Response &)=default
httplib::Response::~Response
~Response()
Definition httplib.h:824
httplib::Response::file_content_content_type_
std::string file_content_content_type_
Definition httplib.h:837
httplib::Response::Response
Response(Response &&)=default
httplib::Response::set_header
void set_header(const std::string &key, const std::string &val)
Definition httplib.h:6868
httplib::Response::get_header_value_count
size_t get_header_value_count(const std::string &key) const
Definition httplib.h:6863
httplib::Response::location
std::string location
Definition httplib.h:784
httplib::detail::case_ignore::equal_to
Definition httplib.h:437
httplib::detail::case_ignore::equal_to::operator()
bool operator()(const std::string &a, const std::string &b) const
Definition httplib.h:438
httplib::detail::case_ignore::hash
Definition httplib.h:443
httplib::detail::case_ignore::hash::operator()
size_t operator()(const std::string &key) const
Definition httplib.h:444
httplib::detail::case_ignore::hash::hash_core
size_t hash_core(const char *s, size_t l, size_t h) const
Definition httplib.h:448
httplib::detail::scope_exit
Definition httplib.h:468
httplib::detail::scope_exit::scope_exit
scope_exit(std::function< void(void)> &&f)
Definition httplib.h:469
httplib::detail::scope_exit::scope_exit
scope_exit(scope_exit &&rhs) noexcept
Definition httplib.h:472
httplib::detail::scope_exit::release
void release()
Definition httplib.h:482
httplib::detail::scope_exit::~scope_exit
~scope_exit()
Definition httplib.h:478