Instrument Neutral Distributed Interface INDI 2.2.5
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.29.0"
12#define CPPHTTPLIB_VERSION_NUM "0x001D00"
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
208using 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
239using nfds_t = unsigned long;
240using socket_t = SOCKET;
241using 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#undef _res // Undefine _res macro to avoid conflicts with user code (#2278)
261#endif
262#include <csignal>
263#include <netinet/tcp.h>
264#include <poll.h>
265#include <pthread.h>
266#include <sys/mman.h>
267#include <sys/socket.h>
268#include <sys/un.h>
269#include <unistd.h>
270
271using socket_t = int;
272#ifndef INVALID_SOCKET
273#define INVALID_SOCKET (-1)
274#endif
275#endif //_WIN32
276
277#if defined(__APPLE__)
278#include <TargetConditionals.h>
279#endif
280
281#include <algorithm>
282#include <array>
283#include <atomic>
284#include <cassert>
285#include <cctype>
286#include <climits>
287#include <condition_variable>
288#include <cstring>
289#include <errno.h>
290#include <exception>
291#include <fcntl.h>
292#include <functional>
293#include <iomanip>
294#include <iostream>
295#include <list>
296#include <map>
297#include <memory>
298#include <mutex>
299#include <random>
300#include <regex>
301#include <set>
302#include <sstream>
303#include <string>
304#include <sys/stat.h>
305#include <thread>
306#include <unordered_map>
307#include <unordered_set>
308#include <utility>
309
310#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) || \
311 defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
312#if TARGET_OS_MAC
313#include <CFNetwork/CFHost.h>
314#include <CoreFoundation/CoreFoundation.h>
315#endif
316#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or
317// CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
318
319#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
320#ifdef _WIN32
321#include <wincrypt.h>
322
323// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
324// used
325#undef X509_NAME
326#undef X509_CERT_PAIR
327#undef X509_EXTENSIONS
328#undef PKCS7_SIGNER_INFO
329
330#ifdef _MSC_VER
331#pragma comment(lib, "crypt32.lib")
332#endif
333#endif // _WIN32
334
335#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
336#if TARGET_OS_MAC
337#include <Security/Security.h>
338#endif
339#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
340
341#include <openssl/err.h>
342#include <openssl/evp.h>
343#include <openssl/ssl.h>
344#include <openssl/x509v3.h>
345
346#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
347#include <openssl/applink.c>
348#endif
349
350#include <iostream>
351#include <sstream>
352
353#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
354#if OPENSSL_VERSION_NUMBER < 0x1010107f
355#error Please use OpenSSL or a current version of BoringSSL
356#endif
357#define SSL_get1_peer_certificate SSL_get_peer_certificate
358#elif OPENSSL_VERSION_NUMBER < 0x30000000L
359#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
360#endif
361
362#endif // CPPHTTPLIB_OPENSSL_SUPPORT
363
364#ifdef CPPHTTPLIB_ZLIB_SUPPORT
365#include <zlib.h>
366#endif
367
368#ifdef CPPHTTPLIB_BROTLI_SUPPORT
369#include <brotli/decode.h>
370#include <brotli/encode.h>
371#endif
372
373#ifdef CPPHTTPLIB_ZSTD_SUPPORT
374#include <zstd.h>
375#endif
376
377/*
378 * Declaration
379 */
380namespace httplib
381{
382
383namespace detail
384{
385
386/*
387 * Backport std::make_unique from C++14.
388 *
389 * NOTE: This code came up with the following stackoverflow post:
390 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
391 *
392 */
393
394template <class T, class... Args>
395typename std::enable_if < !std::is_array<T>::value, std::unique_ptr<T >>::type
396make_unique(Args &&...args)
397{
398 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
399}
400
401template <class T>
402typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
403make_unique(std::size_t n)
404{
405 typedef typename std::remove_extent<T>::type RT;
406 return std::unique_ptr<T>(new RT[n]);
407}
408
409namespace case_ignore
410{
411
412inline unsigned char to_lower(int c)
413{
414 const static unsigned char table[256] =
415 {
416 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
417 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
418 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
419 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
420 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
421 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
422 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
423 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
424 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
425 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
426 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
427 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
428 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
429 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
430 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
431 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
432 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
433 255,
434 };
435 return table[(unsigned char)(char)c];
436}
437
438inline bool equal(const std::string &a, const std::string &b)
439{
440 return a.size() == b.size() &&
441 std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb)
442 {
443 return to_lower(ca) == to_lower(cb);
444 });
445}
446
448{
449 bool operator()(const std::string &a, const std::string &b) const
450 {
451 return equal(a, b);
452 }
453};
454
455struct hash
456{
457 size_t operator()(const std::string &key) const
458 {
459 return hash_core(key.data(), key.size(), 0);
460 }
461
462 size_t hash_core(const char *s, size_t l, size_t h) const
463 {
464 return (l == 0) ? h
465 : hash_core(s + 1, l - 1,
466 // Unsets the 6 high bits of h, therefore no
467 // overflow happens
468 (((std::numeric_limits<size_t>::max)() >> 6) &
469 h * 33) ^
470 static_cast<unsigned char>(to_lower(*s)));
471 }
472};
473
474template <typename T>
475using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
477
478} // namespace case_ignore
479
480// This is based on
481// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
482
484{
485 explicit scope_exit(std::function<void(void)> &&f)
486 : exit_function(std::move(f)), execute_on_destruction{true} {}
487
488 scope_exit(scope_exit &&rhs) noexcept
489 : exit_function(std::move(rhs.exit_function)),
490 execute_on_destruction{rhs.execute_on_destruction}
491 {
492 rhs.release();
493 }
494
496 {
497 if (execute_on_destruction)
498 {
499 this->exit_function();
500 }
501 }
502
503 void release()
504 {
505 this->execute_on_destruction = false;
506 }
507
508 private:
509 scope_exit(const scope_exit &) = delete;
510 void operator=(const scope_exit &) = delete;
511 scope_exit &operator=(scope_exit &&) = delete;
512
513 std::function<void(void)> exit_function;
514 bool execute_on_destruction;
515};
516
517} // namespace detail
518
520{
521 // no decision has been made, use the built-in certificate verifier
523 // connection certificate is verified and accepted
525 // connection certificate was processed but is rejected
528
530{
531 // Information responses
536
537 // Successful responses
538 OK_200 = 200,
548
549 // Redirection messages
559
560 // Client error responses
571 Gone_410 = 410,
590
591 // Server error responses
603};
604
605using Headers =
606 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
608
609using Params = std::multimap<std::string, std::string>;
610using Match = std::smatch;
611
612using DownloadProgress = std::function<bool(size_t current, size_t total)>;
613using UploadProgress = std::function<bool(size_t current, size_t total)>;
614
615struct Response;
616using ResponseHandler = std::function<bool(const Response &response)>;
617
619{
620 std::string name;
621 std::string content;
622 std::string filename;
623 std::string content_type;
625};
626
628{
629 std::string name;
630 std::string content;
632};
633using FormFields = std::multimap<std::string, FormField>;
634
635using FormFiles = std::multimap<std::string, FormData>;
636
638{
639 FormFields fields; // Text fields from multipart
640 FormFiles files; // Files from multipart
641
642 // Text field access
643 std::string get_field(const std::string &key, size_t id = 0) const;
644 std::vector<std::string> get_fields(const std::string &key) const;
645 bool has_field(const std::string &key) const;
646 size_t get_field_count(const std::string &key) const;
647
648 // File access
649 FormData get_file(const std::string &key, size_t id = 0) const;
650 std::vector<FormData> get_files(const std::string &key) const;
651 bool has_file(const std::string &key) const;
652 size_t get_file_count(const std::string &key) const;
653};
654
656{
657 std::string name;
658 std::string content;
659 std::string filename;
660 std::string content_type;
661};
662using UploadFormDataItems = std::vector<UploadFormData>;
663
665{
666 public:
667 DataSink() : os(&sb_), sb_(*this) {}
668
669 DataSink(const DataSink &) = delete;
670 DataSink &operator=(const DataSink &) = delete;
671 DataSink(DataSink &&) = delete;
673
674 std::function<bool(const char *data, size_t data_len)> write;
675 std::function<bool()> is_writable;
676 std::function<void()> done;
677 std::function<void(const Headers &trailer)> done_with_trailer;
678 std::ostream os;
679
680 private:
681 class data_sink_streambuf final : public std::streambuf
682 {
683 public:
684 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
685
686 protected:
687 std::streamsize xsputn(const char *s, std::streamsize n) override
688 {
689 sink_.write(s, static_cast<size_t>(n));
690 return n;
691 }
692
693 private:
694 DataSink &sink_;
695 };
696
697 data_sink_streambuf sb_;
698};
699
701 std::function<bool(size_t offset, size_t length, DataSink &sink)>;
702
704 std::function<bool(size_t offset, DataSink &sink)>;
705
706using ContentProviderResourceReleaser = std::function<void(bool success)>;
707
709{
710 std::string name;
712 std::string filename;
713 std::string content_type;
714};
715using FormDataProviderItems = std::vector<FormDataProvider>;
716
717using ContentReceiverWithProgress = std::function<bool(
718 const char *data, size_t data_length, size_t offset, size_t total_length)>;
719
721 std::function<bool(const char *data, size_t data_length)>;
722
723using FormDataHeader = std::function<bool(const FormData &file)>;
724
726{
727 public:
728 using Reader = std::function<bool(ContentReceiver receiver)>;
730 std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
731
732 ContentReader(Reader reader, FormDataReader multipart_reader)
733 : reader_(std::move(reader)),
734 formdata_reader_(std::move(multipart_reader)) {}
735
737 {
738 return formdata_reader_(std::move(header), std::move(receiver));
739 }
740
742 {
743 return reader_(std::move(receiver));
744 }
745
748};
749
750using Range = std::pair<ssize_t, ssize_t>;
751using Ranges = std::vector<Range>;
752
754{
755 std::string method;
756 std::string path;
757 std::string matched_route;
761 std::string body;
762
763 std::string remote_addr;
764 int remote_port = -1;
765 std::string local_addr;
766 int local_port = -1;
767
768 // for server
769 std::string version;
770 std::string target;
774 std::unordered_map<std::string, std::string> path_params;
775 std::function<bool()> is_connection_closed = []()
776 {
777 return true;
778 };
779
780 // for client
781 std::vector<std::string> accept_content_types;
786#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
787 const SSL *ssl = nullptr;
788#endif
789
790 bool has_header(const std::string &key) const;
791 std::string get_header_value(const std::string &key, const char *def = "",
792 size_t id = 0) const;
793 size_t get_header_value_u64(const std::string &key, size_t def = 0,
794 size_t id = 0) const;
795 size_t get_header_value_count(const std::string &key) const;
796 void set_header(const std::string &key, const std::string &val);
797
798 bool has_trailer(const std::string &key) const;
799 std::string get_trailer_value(const std::string &key, size_t id = 0) const;
800 size_t get_trailer_value_count(const std::string &key) const;
801
802 bool has_param(const std::string &key) const;
803 std::string get_param_value(const std::string &key, size_t id = 0) const;
804 size_t get_param_value_count(const std::string &key) const;
805
806 bool is_multipart_form_data() const;
807
808 // private members...
810 size_t content_length_ = 0;
814 std::chrono::time_point<std::chrono::steady_clock> start_time_ =
815 (std::chrono::steady_clock::time_point::min)();
816};
817
819{
820 std::string version;
821 int status = -1;
822 std::string reason;
825 std::string body;
826 std::string location; // Redirect location
827
828 bool has_header(const std::string &key) const;
829 std::string get_header_value(const std::string &key, const char *def = "",
830 size_t id = 0) const;
831 size_t get_header_value_u64(const std::string &key, size_t def = 0,
832 size_t id = 0) const;
833 size_t get_header_value_count(const std::string &key) const;
834 void set_header(const std::string &key, const std::string &val);
835
836 bool has_trailer(const std::string &key) const;
837 std::string get_trailer_value(const std::string &key, size_t id = 0) const;
838 size_t get_trailer_value_count(const std::string &key) const;
839
840 void set_redirect(const std::string &url, int status = StatusCode::Found_302);
841 void set_content(const char *s, size_t n, const std::string &content_type);
842 void set_content(const std::string &s, const std::string &content_type);
843 void set_content(std::string &&s, const std::string &content_type);
844
846 size_t length, const std::string &content_type, ContentProvider provider,
847 ContentProviderResourceReleaser resource_releaser = nullptr);
848
850 const std::string &content_type, ContentProviderWithoutLength provider,
851 ContentProviderResourceReleaser resource_releaser = nullptr);
852
854 const std::string &content_type, ContentProviderWithoutLength provider,
855 ContentProviderResourceReleaser resource_releaser = nullptr);
856
857 void set_file_content(const std::string &path,
858 const std::string &content_type);
859 void set_file_content(const std::string &path);
860
861 Response() = default;
862 Response(const Response &) = default;
863 Response &operator=(const Response &) = default;
864 Response(Response &&) = default;
873
874 // private members...
875 size_t content_length_ = 0;
882};
883
924
925std::string to_string(Error error);
926
927std::ostream &operator<<(std::ostream &os, const Error &obj);
928
930{
931 public:
932 virtual ~Stream() = default;
933
934 virtual bool is_readable() const = 0;
935 virtual bool wait_readable() const = 0;
936 virtual bool wait_writable() const = 0;
937
938 virtual ssize_t read(char *ptr, size_t size) = 0;
939 virtual ssize_t write(const char *ptr, size_t size) = 0;
940 virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
941 virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
942 virtual socket_t socket() const = 0;
943
944 virtual time_t duration() const = 0;
945
946 ssize_t write(const char *ptr);
947 ssize_t write(const std::string &s);
948
950 {
951 return error_;
952 }
953
954 protected:
956};
957
959{
960 public:
961 TaskQueue() = default;
962 virtual ~TaskQueue() = default;
963
964 virtual bool enqueue(std::function<void()> fn) = 0;
965 virtual void shutdown() = 0;
966
967 virtual void on_idle() {}
968};
969
970class ThreadPool final : public TaskQueue
971{
972 public:
973 explicit ThreadPool(size_t n, size_t mqr = 0)
974 : shutdown_(false), max_queued_requests_(mqr)
975 {
976 threads_.reserve(n);
977 while (n)
978 {
979 threads_.emplace_back(worker(*this));
980 n--;
981 }
982 }
983
984 ThreadPool(const ThreadPool &) = delete;
985 ~ThreadPool() override = default;
986
987 bool enqueue(std::function<void()> fn) override
988 {
989 {
990 std::unique_lock<std::mutex> lock(mutex_);
991 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_)
992 {
993 return false;
994 }
995 jobs_.push_back(std::move(fn));
996 }
997
998 cond_.notify_one();
999 return true;
1000 }
1001
1002 void shutdown() override
1003 {
1004 // Stop all worker threads...
1005 {
1006 std::unique_lock<std::mutex> lock(mutex_);
1007 shutdown_ = true;
1008 }
1009
1010 cond_.notify_all();
1011
1012 // Join...
1013 for (auto &t : threads_)
1014 {
1015 t.join();
1016 }
1017 }
1018
1019 private:
1020 struct worker
1021 {
1022 explicit worker(ThreadPool &pool) : pool_(pool) {}
1023
1024 void operator()()
1025 {
1026 for (;;)
1027 {
1028 std::function<void()> fn;
1029 {
1030 std::unique_lock<std::mutex> lock(pool_.mutex_);
1031
1032 pool_.cond_.wait(
1033 lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
1034
1035 if (pool_.shutdown_ && pool_.jobs_.empty())
1036 {
1037 break;
1038 }
1039
1040 fn = pool_.jobs_.front();
1041 pool_.jobs_.pop_front();
1042 }
1043
1044 assert(true == static_cast<bool>(fn));
1045 fn();
1046 }
1047
1048#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
1049 !defined(LIBRESSL_VERSION_NUMBER)
1050 OPENSSL_thread_stop();
1051#endif
1052 }
1053
1054 ThreadPool &pool_;
1055 };
1056 friend struct worker;
1057
1058 std::vector<std::thread> threads_;
1059 std::list<std::function<void()>> jobs_;
1060
1061 bool shutdown_;
1062 size_t max_queued_requests_ = 0;
1063
1064 std::condition_variable cond_;
1065 std::mutex mutex_;
1066};
1067
1068using Logger = std::function<void(const Request &, const Response &)>;
1069
1070// Forward declaration for Error type
1071enum class Error;
1072using ErrorLogger = std::function<void(const Error &, const Request *)>;
1073
1074using SocketOptions = std::function<void(socket_t sock)>;
1075
1077
1078const char *status_message(int status);
1079
1080std::string to_string(Error error);
1081
1082std::ostream &operator<<(std::ostream &os, const Error &obj);
1083
1084std::string get_bearer_token_auth(const Request &req);
1085
1086namespace detail
1087{
1088
1090{
1091 public:
1092 MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
1093 virtual ~MatcherBase() = default;
1094
1095 const std::string &pattern() const
1096 {
1097 return pattern_;
1098 }
1099
1100 // Match request path and populate its matches and
1101 virtual bool match(Request &request) const = 0;
1102
1103 private:
1104 std::string pattern_;
1105};
1106
1125class PathParamsMatcher final : public MatcherBase
1126{
1127 public:
1128 PathParamsMatcher(const std::string &pattern);
1129
1130 bool match(Request &request) const override;
1131
1132 private:
1133 // Treat segment separators as the end of path parameter capture
1134 // Does not need to handle query parameters as they are parsed before path
1135 // matching
1136 static constexpr char separator = '/';
1137
1138 // Contains static path fragments to match against, excluding the '/' after
1139 // path params
1140 // Fragments are separated by path params
1141 std::vector<std::string> static_fragments_;
1142 // Stores the names of the path parameters to be used as keys in the
1143 // Request::path_params map
1144 std::vector<std::string> param_names_;
1145};
1146
1155class RegexMatcher final : public MatcherBase
1156{
1157 public:
1158 RegexMatcher(const std::string &pattern)
1159 : MatcherBase(pattern), regex_(pattern) {}
1160
1161 bool match(Request &request) const override;
1162
1163 private:
1164 std::regex regex_;
1165};
1166
1167int close_socket(socket_t sock);
1168
1169ssize_t write_headers(Stream &strm, const Headers &headers);
1170
1171} // namespace detail
1172
1174{
1175 public:
1176 using Handler = std::function<void(const Request &, Response &)>;
1177
1179 std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1180
1182 {
1183 Handled,
1184 Unhandled,
1185 };
1187 std::function<HandlerResponse(const Request &, Response &)>;
1188
1189 using HandlerWithContentReader = std::function<void(
1190 const Request &, Response &, const ContentReader &content_reader)>;
1191
1193 std::function<int(const Request &, Response &)>;
1194
1195 Server();
1196
1197 virtual ~Server();
1198
1199 virtual bool is_valid() const;
1200
1201 Server &Get(const std::string &pattern, Handler handler);
1202 Server &Post(const std::string &pattern, Handler handler);
1203 Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1204 Server &Put(const std::string &pattern, Handler handler);
1205 Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1206 Server &Patch(const std::string &pattern, Handler handler);
1207 Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1208 Server &Delete(const std::string &pattern, Handler handler);
1209 Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1210 Server &Options(const std::string &pattern, Handler handler);
1211
1212 bool set_base_dir(const std::string &dir,
1213 const std::string &mount_point = std::string());
1214 bool set_mount_point(const std::string &mount_point, const std::string &dir,
1215 Headers headers = Headers());
1216 bool remove_mount_point(const std::string &mount_point);
1217 Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1218 const std::string &mime);
1219 Server &set_default_file_mimetype(const std::string &mime);
1221
1222 template <class ErrorHandlerFunc>
1223 Server &set_error_handler(ErrorHandlerFunc &&handler)
1224 {
1225 return set_error_handler_core(
1226 std::forward<ErrorHandlerFunc>(handler),
1227 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse> {});
1228 }
1229
1231
1234
1236
1238 Server &set_logger(Logger logger);
1240 Server &set_error_logger(ErrorLogger error_logger);
1241
1242 Server &set_address_family(int family);
1243 Server &set_tcp_nodelay(bool on);
1244 Server &set_ipv6_v6only(bool on);
1245 Server &set_socket_options(SocketOptions socket_options);
1246
1248 Server &
1249 set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1250
1251 Server &set_trusted_proxies(const std::vector<std::string> &proxies);
1252
1253 Server &set_keep_alive_max_count(size_t count);
1254 Server &set_keep_alive_timeout(time_t sec);
1255
1256 Server &set_read_timeout(time_t sec, time_t usec = 0);
1257 template <class Rep, class Period>
1258 Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1259
1260 Server &set_write_timeout(time_t sec, time_t usec = 0);
1261 template <class Rep, class Period>
1262 Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1263
1264 Server &set_idle_interval(time_t sec, time_t usec = 0);
1265 template <class Rep, class Period>
1266 Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1267
1268 Server &set_payload_max_length(size_t length);
1269
1270 bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1271 int bind_to_any_port(const std::string &host, int socket_flags = 0);
1272 bool listen_after_bind();
1273
1274 bool listen(const std::string &host, int port, int socket_flags = 0);
1275
1276 bool is_running() const;
1277 void wait_until_ready() const;
1278 void stop();
1279 void decommission();
1280
1281 std::function<TaskQueue *(void)> new_task_queue;
1282
1283 protected:
1284 bool process_request(Stream &strm, const std::string &remote_addr,
1285 int remote_port, const std::string &local_addr,
1286 int local_port, bool close_connection,
1287 bool &connection_closed,
1288 const std::function<void(Request &)> &setup_request);
1289
1290 std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1291
1292 std::vector<std::string> trusted_proxies_;
1293
1303
1304 private:
1305 using Handlers =
1306 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1307 using HandlersForContentReader =
1308 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1310
1311 static std::unique_ptr<detail::MatcherBase>
1312 make_matcher(const std::string &pattern);
1313
1314 Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1315 Server &set_error_handler_core(Handler handler, std::false_type);
1316
1317 socket_t create_server_socket(const std::string &host, int port,
1318 int socket_flags,
1319 SocketOptions socket_options) const;
1320 int bind_internal(const std::string &host, int port, int socket_flags);
1321 bool listen_internal();
1322
1323 bool routing(Request &req, Response &res, Stream &strm);
1324 bool handle_file_request(Request &req, Response &res);
1325 bool check_if_not_modified(const Request &req, Response &res,
1326 const std::string &etag, time_t mtime) const;
1327 bool check_if_range(Request &req, const std::string &etag,
1328 time_t mtime) const;
1329 bool dispatch_request(Request &req, Response &res,
1330 const Handlers &handlers) const;
1331 bool dispatch_request_for_content_reader(
1332 Request &req, Response &res, ContentReader content_reader,
1333 const HandlersForContentReader &handlers) const;
1334
1335 bool parse_request_line(const char *s, Request &req) const;
1336 void apply_ranges(const Request &req, Response &res,
1337 std::string &content_type, std::string &boundary) const;
1338 bool write_response(Stream &strm, bool close_connection, Request &req,
1339 Response &res);
1340 bool write_response_with_content(Stream &strm, bool close_connection,
1341 const Request &req, Response &res);
1342 bool write_response_core(Stream &strm, bool close_connection,
1343 const Request &req, Response &res,
1344 bool need_apply_ranges);
1345 bool write_content_with_provider(Stream &strm, const Request &req,
1346 Response &res, const std::string &boundary,
1347 const std::string &content_type);
1348 bool read_content(Stream &strm, Request &req, Response &res);
1349 bool read_content_with_content_receiver(Stream &strm, Request &req,
1350 Response &res,
1352 FormDataHeader multipart_header,
1353 ContentReceiver multipart_receiver);
1354 bool read_content_core(Stream &strm, Request &req, Response &res,
1356 FormDataHeader multipart_header,
1357 ContentReceiver multipart_receiver) const;
1358
1359 virtual bool process_and_close_socket(socket_t sock);
1360
1361 void output_log(const Request &req, const Response &res) const;
1362 void output_pre_compression_log(const Request &req,
1363 const Response &res) const;
1364 void output_error_log(const Error &err, const Request *req) const;
1365
1366 std::atomic<bool> is_running_{false};
1367 std::atomic<bool> is_decommissioned{false};
1368
1369 struct MountPointEntry
1370 {
1371 std::string mount_point;
1372 std::string base_dir;
1373 Headers headers;
1374 };
1375 std::vector<MountPointEntry> base_dirs_;
1376 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1377 std::string default_file_mimetype_ = "application/octet-stream";
1378 Handler file_request_handler_;
1379
1380 Handlers get_handlers_;
1381 Handlers post_handlers_;
1382 HandlersForContentReader post_handlers_for_content_reader_;
1383 Handlers put_handlers_;
1384 HandlersForContentReader put_handlers_for_content_reader_;
1385 Handlers patch_handlers_;
1386 HandlersForContentReader patch_handlers_for_content_reader_;
1387 Handlers delete_handlers_;
1388 HandlersForContentReader delete_handlers_for_content_reader_;
1389 Handlers options_handlers_;
1390
1391 HandlerWithResponse error_handler_;
1392 ExceptionHandler exception_handler_;
1393 HandlerWithResponse pre_routing_handler_;
1394 Handler post_routing_handler_;
1395 HandlerWithResponse pre_request_handler_;
1396 Expect100ContinueHandler expect_100_continue_handler_;
1397
1398 mutable std::mutex logger_mutex_;
1399 Logger logger_;
1400 Logger pre_compression_logger_;
1401 ErrorLogger error_logger_;
1402
1403 int address_family_ = AF_UNSPEC;
1404 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1405 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1406 SocketOptions socket_options_ = default_socket_options;
1407
1408 Headers default_headers_;
1409 std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1411};
1412
1414{
1415 public:
1416 Result() = default;
1417 Result(std::unique_ptr<Response> &&res, Error err,
1418 Headers &&request_headers = Headers{})
1419 : res_(std::move(res)), err_(err),
1420 request_headers_(std::move(request_headers)) {}
1421#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1422 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1423 int ssl_error)
1424 : res_(std::move(res)), err_(err),
1425 request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
1426 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1427 int ssl_error, unsigned long ssl_openssl_error)
1428 : res_(std::move(res)), err_(err),
1429 request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1430 ssl_openssl_error_(ssl_openssl_error) {}
1431#endif
1432 // Response
1433 operator bool() const
1434 {
1435 return res_ != nullptr;
1436 }
1437 bool operator==(std::nullptr_t) const
1438 {
1439 return res_ == nullptr;
1440 }
1441 bool operator!=(std::nullptr_t) const
1442 {
1443 return res_ != nullptr;
1444 }
1445 const Response &value() const
1446 {
1447 return *res_;
1448 }
1450 {
1451 return *res_;
1452 }
1453 const Response &operator*() const
1454 {
1455 return *res_;
1456 }
1458 {
1459 return *res_;
1460 }
1461 const Response *operator->() const
1462 {
1463 return res_.get();
1464 }
1466 {
1467 return res_.get();
1468 }
1469
1470 // Error
1471 Error error() const
1472 {
1473 return err_;
1474 }
1475
1476#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1477 // SSL Error
1478 int ssl_error() const
1479 {
1480 return ssl_error_;
1481 }
1482 // OpenSSL Error
1483 unsigned long ssl_openssl_error() const
1484 {
1485 return ssl_openssl_error_;
1486 }
1487#endif
1488
1489 // Request Headers
1490 bool has_request_header(const std::string &key) const;
1491 std::string get_request_header_value(const std::string &key,
1492 const char *def = "",
1493 size_t id = 0) const;
1494 size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
1495 size_t id = 0) const;
1496 size_t get_request_header_value_count(const std::string &key) const;
1497
1498 private:
1499 std::unique_ptr<Response> res_;
1500 Error err_ = Error::Unknown;
1501 Headers request_headers_;
1502#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1503 int ssl_error_ = 0;
1504 unsigned long ssl_openssl_error_ = 0;
1505#endif
1506};
1507
1509{
1511#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1512 SSL *ssl = nullptr;
1513#endif
1514
1515 bool is_open() const
1516 {
1517 return sock != INVALID_SOCKET;
1518 }
1519
1520 ClientConnection() = default;
1521
1523 {
1524#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1525 if (ssl)
1526 {
1527 SSL_free(ssl);
1528 ssl = nullptr;
1529 }
1530#endif
1531 if (sock != INVALID_SOCKET)
1532 {
1535 }
1536 }
1537
1540
1542 : sock(other.sock)
1543#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1544 ,
1545 ssl(other.ssl)
1546#endif
1547 {
1548 other.sock = INVALID_SOCKET;
1549#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1550 other.ssl = nullptr;
1551#endif
1552 }
1553
1555 {
1556 if (this != &other)
1557 {
1558 sock = other.sock;
1559#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1560 ssl = other.ssl;
1561#endif
1562 other.sock = INVALID_SOCKET;
1563#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1564 other.ssl = nullptr;
1565#endif
1566 }
1567 return *this;
1568 }
1569};
1570
1571namespace detail
1572{
1573
1574struct ChunkedDecoder;
1575
1577{
1578 Stream *stream = nullptr;
1579 size_t content_length = 0;
1580 size_t bytes_read = 0;
1581 bool chunked = false;
1582 bool eof = false;
1583 std::unique_ptr<ChunkedDecoder> chunked_decoder;
1585
1586 ssize_t read(char *buf, size_t len);
1587 bool has_error() const
1588 {
1589 return last_error != Error::Success;
1590 }
1591};
1592
1593inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
1594 size_t len)
1595{
1596 (void)stream;
1597 return br.read(buf, len);
1598}
1599
1600class decompressor;
1601
1602} // namespace detail
1603
1605{
1606 public:
1607 explicit ClientImpl(const std::string &host);
1608
1609 explicit ClientImpl(const std::string &host, int port);
1610
1611 explicit ClientImpl(const std::string &host, int port,
1612 const std::string &client_cert_path,
1613 const std::string &client_key_path);
1614
1615 virtual ~ClientImpl();
1616
1617 virtual bool is_valid() const;
1618
1620 {
1621 std::unique_ptr<Response> response;
1623
1624 StreamHandle() = default;
1625 StreamHandle(const StreamHandle &) = delete;
1629 ~StreamHandle() = default;
1630
1631 bool is_valid() const
1632 {
1633 return response != nullptr && error == Error::Success;
1634 }
1635
1636 ssize_t read(char *buf, size_t len);
1639 {
1640 return body_reader_.last_error;
1641 }
1642 bool has_read_error() const
1643 {
1644 return body_reader_.has_error();
1645 }
1646
1647 bool trailers_parsed_ = false;
1648
1649 private:
1650 friend class ClientImpl;
1651
1652 ssize_t read_with_decompression(char *buf, size_t len);
1653
1654 std::unique_ptr<ClientConnection> connection_;
1655 std::unique_ptr<Stream> socket_stream_;
1656 Stream *stream_ = nullptr;
1657 detail::BodyReader body_reader_;
1658
1659 std::unique_ptr<detail::decompressor> decompressor_;
1660 std::string decompress_buffer_;
1661 size_t decompress_offset_ = 0;
1662 };
1663
1664 // clang-format off
1665 Result Get(const std::string &path, DownloadProgress progress = nullptr);
1666 Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1667 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1668 DownloadProgress progress = nullptr);
1669 Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1670 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver,
1671 DownloadProgress progress = nullptr);
1672 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
1673 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1674 Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1675 Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver,
1676 DownloadProgress progress = nullptr);
1677 Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler,
1678 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1679
1680 Result Head(const std::string &path);
1681 Result Head(const std::string &path, const Headers &headers);
1682
1683 Result Post(const std::string &path);
1684 Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1685 UploadProgress progress = nullptr);
1686 Result Post(const std::string &path, const std::string &body, const std::string &content_type,
1687 UploadProgress progress = nullptr);
1688 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider,
1689 const std::string &content_type, UploadProgress progress = nullptr);
1690 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider,
1691 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1692 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
1693 UploadProgress progress = nullptr);
1694 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
1695 ContentReceiver content_receiver, UploadProgress progress = nullptr);
1696 Result Post(const std::string &path, const Params &params);
1697 Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1698 Result Post(const std::string &path, const Headers &headers);
1699 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1700 const std::string &content_type, UploadProgress progress = nullptr);
1701 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1702 UploadProgress progress = nullptr);
1703 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1704 const std::string &content_type, UploadProgress progress = nullptr);
1705 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1706 const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1707 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1708 const std::string &content_type, UploadProgress progress = nullptr);
1709 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1710 const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1711 Result Post(const std::string &path, const Headers &headers, const Params &params);
1712 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
1713 UploadProgress progress = nullptr);
1714 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary,
1715 UploadProgress progress = nullptr);
1716 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
1717 const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1718 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1719 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1720
1721 Result Put(const std::string &path);
1722 Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1723 UploadProgress progress = nullptr);
1724 Result Put(const std::string &path, const std::string &body, const std::string &content_type,
1725 UploadProgress progress = nullptr);
1726 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider,
1727 const std::string &content_type, UploadProgress progress = nullptr);
1728 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider,
1729 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1730 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
1731 UploadProgress progress = nullptr);
1732 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
1733 ContentReceiver content_receiver, UploadProgress progress = nullptr);
1734 Result Put(const std::string &path, const Params &params);
1735 Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1736 Result Put(const std::string &path, const Headers &headers);
1737 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1738 const std::string &content_type, UploadProgress progress = nullptr);
1739 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1740 UploadProgress progress = nullptr);
1741 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1742 const std::string &content_type, UploadProgress progress = nullptr);
1743 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1744 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1745 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1746 const std::string &content_type, UploadProgress progress = nullptr);
1747 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1748 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1749 Result Put(const std::string &path, const Headers &headers, const Params &params);
1750 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
1751 UploadProgress progress = nullptr);
1752 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary,
1753 UploadProgress progress = nullptr);
1754 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
1755 const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1756 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1757 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1758
1759 Result Patch(const std::string &path);
1760 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1761 UploadProgress progress = nullptr);
1762 Result Patch(const std::string &path, const std::string &body, const std::string &content_type,
1763 UploadProgress progress = nullptr);
1764 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
1765 const std::string &content_type, UploadProgress progress = nullptr);
1766 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
1767 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1768 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
1769 UploadProgress progress = nullptr);
1770 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
1771 ContentReceiver content_receiver, UploadProgress progress = nullptr);
1772 Result Patch(const std::string &path, const Params &params);
1773 Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1774 Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
1775 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1776 const std::string &content_type, UploadProgress progress = nullptr);
1777 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1778 UploadProgress progress = nullptr);
1779 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1780 const std::string &content_type, UploadProgress progress = nullptr);
1781 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1782 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1783 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1784 const std::string &content_type, UploadProgress progress = nullptr);
1785 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1786 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1787 Result Patch(const std::string &path, const Headers &headers, const Params &params);
1788 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
1789 UploadProgress progress = nullptr);
1790 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary,
1791 UploadProgress progress = nullptr);
1792 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
1793 const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1794 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1795 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1796
1797 Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1798 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1799 DownloadProgress progress = nullptr);
1800 Result Delete(const std::string &path, const std::string &body, const std::string &content_type,
1801 DownloadProgress progress = nullptr);
1802 Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1803 Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1804 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1805 const std::string &content_type, DownloadProgress progress = nullptr);
1806 Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1807 DownloadProgress progress = nullptr);
1808 Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1809
1810 Result Options(const std::string &path);
1811 Result Options(const std::string &path, const Headers &headers);
1812 // clang-format on
1813
1814 // Streaming API: Open a stream for reading response body incrementally
1815 // Socket ownership is transferred to StreamHandle for true streaming
1816 // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
1817 StreamHandle open_stream(const std::string &method, const std::string &path,
1818 const Params &params = {},
1819 const Headers &headers = {},
1820 const std::string &body = {},
1821 const std::string &content_type = {});
1822
1823 bool send(Request &req, Response &res, Error &error);
1824 Result send(const Request &req);
1825
1826 void stop();
1827
1828 std::string host() const;
1829 int port() const;
1830
1831 size_t is_socket_open() const;
1832 socket_t socket() const;
1833
1834 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1835
1836 void set_default_headers(Headers headers);
1837
1838 void
1839 set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1840
1841 void set_address_family(int family);
1842 void set_tcp_nodelay(bool on);
1843 void set_ipv6_v6only(bool on);
1844 void set_socket_options(SocketOptions socket_options);
1845
1846 void set_connection_timeout(time_t sec, time_t usec = 0);
1847 template <class Rep, class Period>
1848 void
1849 set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1850
1851 void set_read_timeout(time_t sec, time_t usec = 0);
1852 template <class Rep, class Period>
1853 void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1854
1855 void set_write_timeout(time_t sec, time_t usec = 0);
1856 template <class Rep, class Period>
1857 void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1858
1859 void set_max_timeout(time_t msec);
1860 template <class Rep, class Period>
1861 void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1862
1863 void set_basic_auth(const std::string &username, const std::string &password);
1864 void set_bearer_token_auth(const std::string &token);
1865#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1866 void set_digest_auth(const std::string &username,
1867 const std::string &password);
1868#endif
1869
1870 void set_keep_alive(bool on);
1871 void set_follow_location(bool on);
1872
1873 void set_path_encode(bool on);
1874
1875 void set_compress(bool on);
1876
1877 void set_decompress(bool on);
1878
1879 void set_interface(const std::string &intf);
1880
1881 void set_proxy(const std::string &host, int port);
1882 void set_proxy_basic_auth(const std::string &username,
1883 const std::string &password);
1884 void set_proxy_bearer_token_auth(const std::string &token);
1885#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1886 void set_proxy_digest_auth(const std::string &username,
1887 const std::string &password);
1888#endif
1889
1890#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1891 void set_ca_cert_path(const std::string &ca_cert_file_path,
1892 const std::string &ca_cert_dir_path = std::string());
1893 void set_ca_cert_store(X509_STORE *ca_cert_store);
1894 X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1895#endif
1896
1897#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1898 void enable_server_certificate_verification(bool enabled);
1899 void enable_server_hostname_verification(bool enabled);
1900 void set_server_certificate_verifier(
1901 std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1902#endif
1903
1904 void set_logger(Logger logger);
1905 void set_error_logger(ErrorLogger error_logger);
1906
1907 protected:
1908 struct Socket
1909 {
1911#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1912 SSL *ssl = nullptr;
1913#endif
1914
1915 bool is_open() const
1916 {
1917 return sock != INVALID_SOCKET;
1918 }
1919 };
1920
1921 virtual bool create_and_connect_socket(Socket &socket, Error &error);
1922 virtual bool ensure_socket_connection(Socket &socket, Error &error);
1923
1924 // All of:
1925 // shutdown_ssl
1926 // shutdown_socket
1927 // close_socket
1928 // should ONLY be called when socket_mutex_ is locked.
1929 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1930 // with a DIFFERENT thread sending requests using that socket.
1931 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1932 void shutdown_socket(Socket &socket) const;
1933 void close_socket(Socket &socket);
1934
1935 bool process_request(Stream &strm, Request &req, Response &res,
1936 bool close_connection, Error &error);
1937
1938 bool write_content_with_provider(Stream &strm, const Request &req,
1939 Error &error) const;
1940
1941 void copy_settings(const ClientImpl &rhs);
1942
1943 void output_log(const Request &req, const Response &res) const;
1944 void output_error_log(const Error &err, const Request *req) const;
1945
1946 // Socket endpoint information
1947 const std::string host_;
1948 const int port_;
1949
1950 // Current open socket
1952 mutable std::mutex socket_mutex_;
1953 std::recursive_mutex request_mutex_;
1954
1955 // These are all protected under socket_mutex
1957 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1959
1960 // Hostname-IP map
1961 std::map<std::string, std::string> addr_map_;
1962
1963 // Default headers
1965
1966 // Header writer
1967 std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1969
1970 // Settings
1972 std::string client_key_path_;
1973
1981
1985#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1986 std::string digest_auth_username_;
1987 std::string digest_auth_password_;
1988#endif
1989
1990 bool keep_alive_ = false;
1991 bool follow_location_ = false;
1992
1993 bool path_encode_ = true;
1994
1995 int address_family_ = AF_UNSPEC;
1999
2000 bool compress_ = false;
2001 bool decompress_ = true;
2002
2003 std::string interface_;
2004
2005 std::string proxy_host_;
2006 int proxy_port_ = -1;
2007
2011#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2012 std::string proxy_digest_auth_username_;
2013 std::string proxy_digest_auth_password_;
2014#endif
2015
2016#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2017 std::string ca_cert_file_path_;
2018 std::string ca_cert_dir_path_;
2019
2020 X509_STORE *ca_cert_store_ = nullptr;
2021#endif
2022
2023#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2024 bool server_certificate_verification_ = true;
2025 bool server_hostname_verification_ = true;
2026 std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
2027#endif
2028
2029 mutable std::mutex logger_mutex_;
2032
2033#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2034 int last_ssl_error_ = 0;
2035 unsigned long last_openssl_error_ = 0;
2036#endif
2037
2038 private:
2039 bool send_(Request &req, Response &res, Error &error);
2040 Result send_(Request &&req);
2041
2042 socket_t create_client_socket(Error &error) const;
2043 bool read_response_line(Stream &strm, const Request &req,
2044 Response &res) const;
2045 bool write_request(Stream &strm, Request &req, bool close_connection,
2046 Error &error);
2047 void prepare_default_headers(Request &r, bool for_stream,
2048 const std::string &ct);
2049 bool redirect(Request &req, Response &res, Error &error);
2050 bool create_redirect_client(const std::string &scheme,
2051 const std::string &host, int port, Request &req,
2052 Response &res, const std::string &path,
2053 const std::string &location, Error &error);
2054 template <typename ClientType> void setup_redirect_client(ClientType &client);
2055 bool handle_request(Stream &strm, Request &req, Response &res,
2056 bool close_connection, Error &error);
2057 std::unique_ptr<Response> send_with_content_provider_and_receiver(
2058 Request &req, const char *body, size_t content_length,
2059 ContentProvider content_provider,
2060 ContentProviderWithoutLength content_provider_without_length,
2061 const std::string &content_type, ContentReceiver content_receiver,
2062 Error &error);
2063 Result send_with_content_provider_and_receiver(
2064 const std::string &method, const std::string &path,
2065 const Headers &headers, const char *body, size_t content_length,
2066 ContentProvider content_provider,
2067 ContentProviderWithoutLength content_provider_without_length,
2068 const std::string &content_type, ContentReceiver content_receiver,
2069 UploadProgress progress);
2070 ContentProviderWithoutLength get_multipart_content_provider(
2071 const std::string &boundary, const UploadFormDataItems &items,
2072 const FormDataProviderItems &provider_items) const;
2073
2074 virtual bool
2075 process_socket(const Socket &socket,
2076 std::chrono::time_point<std::chrono::steady_clock> start_time,
2077 std::function<bool(Stream &strm)> callback);
2078 virtual bool is_ssl() const;
2079
2080 void transfer_socket_ownership_to_handle(StreamHandle &handle);
2081};
2082
2084{
2085 public:
2086 // Universal interface
2087 explicit Client(const std::string &scheme_host_port);
2088
2089 explicit Client(const std::string &scheme_host_port,
2090 const std::string &client_cert_path,
2091 const std::string &client_key_path);
2092
2093 // HTTP only interface
2094 explicit Client(const std::string &host, int port);
2095
2096 explicit Client(const std::string &host, int port,
2097 const std::string &client_cert_path,
2098 const std::string &client_key_path);
2099
2100 Client(Client &&) = default;
2101 Client &operator=(Client &&) = default;
2102
2104
2105 bool is_valid() const;
2106
2107 // clang-format off
2108 Result Get(const std::string &path, DownloadProgress progress = nullptr);
2109 Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2110 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
2111 DownloadProgress progress = nullptr);
2112 Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2113 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver,
2114 DownloadProgress progress = nullptr);
2115 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
2116 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2117 Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
2118 Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver,
2119 DownloadProgress progress = nullptr);
2120 Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler,
2121 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2122
2123 Result Head(const std::string &path);
2124 Result Head(const std::string &path, const Headers &headers);
2125
2126 Result Post(const std::string &path);
2127 Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
2128 UploadProgress progress = nullptr);
2129 Result Post(const std::string &path, const std::string &body, const std::string &content_type,
2130 UploadProgress progress = nullptr);
2131 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider,
2132 const std::string &content_type, UploadProgress progress = nullptr);
2133 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider,
2134 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2135 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
2136 UploadProgress progress = nullptr);
2137 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
2138 ContentReceiver content_receiver, UploadProgress progress = nullptr);
2139 Result Post(const std::string &path, const Params &params);
2140 Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2141 Result Post(const std::string &path, const Headers &headers);
2142 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
2143 const std::string &content_type, UploadProgress progress = nullptr);
2144 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2145 UploadProgress progress = nullptr);
2146 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
2147 const std::string &content_type, UploadProgress progress = nullptr);
2148 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
2149 const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2150 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
2151 const std::string &content_type, UploadProgress progress = nullptr);
2152 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
2153 const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2154 Result Post(const std::string &path, const Headers &headers, const Params &params);
2155 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
2156 UploadProgress progress = nullptr);
2157 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary,
2158 UploadProgress progress = nullptr);
2159 Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
2160 const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2161 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2162 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2163
2164 Result Put(const std::string &path);
2165 Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
2166 UploadProgress progress = nullptr);
2167 Result Put(const std::string &path, const std::string &body, const std::string &content_type,
2168 UploadProgress progress = nullptr);
2169 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider,
2170 const std::string &content_type, UploadProgress progress = nullptr);
2171 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider,
2172 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2173 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
2174 UploadProgress progress = nullptr);
2175 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
2176 ContentReceiver content_receiver, UploadProgress progress = nullptr);
2177 Result Put(const std::string &path, const Params &params);
2178 Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2179 Result Put(const std::string &path, const Headers &headers);
2180 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
2181 const std::string &content_type, UploadProgress progress = nullptr);
2182 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2183 UploadProgress progress = nullptr);
2184 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
2185 const std::string &content_type, UploadProgress progress = nullptr);
2186 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
2187 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2188 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
2189 const std::string &content_type, UploadProgress progress = nullptr);
2190 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
2191 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2192 Result Put(const std::string &path, const Headers &headers, const Params &params);
2193 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
2194 UploadProgress progress = nullptr);
2195 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary,
2196 UploadProgress progress = nullptr);
2197 Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
2198 const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2199 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2200 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2201
2202 Result Patch(const std::string &path);
2203 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
2204 UploadProgress progress = nullptr);
2205 Result Patch(const std::string &path, const std::string &body, const std::string &content_type,
2206 UploadProgress progress = nullptr);
2207 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
2208 const std::string &content_type, UploadProgress progress = nullptr);
2209 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
2210 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2211 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
2212 UploadProgress progress = nullptr);
2213 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type,
2214 ContentReceiver content_receiver, UploadProgress progress = nullptr);
2215 Result Patch(const std::string &path, const Params &params);
2216 Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2217 Result Patch(const std::string &path, const Headers &headers);
2218 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
2219 const std::string &content_type, UploadProgress progress = nullptr);
2220 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2221 UploadProgress progress = nullptr);
2222 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
2223 const std::string &content_type, UploadProgress progress = nullptr);
2224 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
2225 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2226 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
2227 const std::string &content_type, UploadProgress progress = nullptr);
2228 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
2229 const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2230 Result Patch(const std::string &path, const Headers &headers, const Params &params);
2231 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
2232 UploadProgress progress = nullptr);
2233 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary,
2234 UploadProgress progress = nullptr);
2235 Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items,
2236 const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2237 Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2238 ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2239
2240 Result Delete(const std::string &path, DownloadProgress progress = nullptr);
2241 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
2242 DownloadProgress progress = nullptr);
2243 Result Delete(const std::string &path, const std::string &body, const std::string &content_type,
2244 DownloadProgress progress = nullptr);
2245 Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2246 Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2247 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
2248 const std::string &content_type, DownloadProgress progress = nullptr);
2249 Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
2250 DownloadProgress progress = nullptr);
2251 Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2252
2253 Result Options(const std::string &path);
2254 Result Options(const std::string &path, const Headers &headers);
2255 // clang-format on
2256
2257 // Streaming API: Open a stream for reading response body incrementally
2258 // Socket ownership is transferred to StreamHandle for true streaming
2259 // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
2260 ClientImpl::StreamHandle open_stream(const std::string &method,
2261 const std::string &path,
2262 const Params &params = {},
2263 const Headers &headers = {},
2264 const std::string &body = {},
2265 const std::string &content_type = {});
2266
2267 bool send(Request &req, Response &res, Error &error);
2268 Result send(const Request &req);
2269
2270 void stop();
2271
2272 std::string host() const;
2273 int port() const;
2274
2275 size_t is_socket_open() const;
2276 socket_t socket() const;
2277
2278 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2279
2280 void set_default_headers(Headers headers);
2281
2282 void
2283 set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2284
2285 void set_address_family(int family);
2286 void set_tcp_nodelay(bool on);
2287 void set_socket_options(SocketOptions socket_options);
2288
2289 void set_connection_timeout(time_t sec, time_t usec = 0);
2290 template <class Rep, class Period>
2291 void
2292 set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2293
2294 void set_read_timeout(time_t sec, time_t usec = 0);
2295 template <class Rep, class Period>
2296 void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2297
2298 void set_write_timeout(time_t sec, time_t usec = 0);
2299 template <class Rep, class Period>
2300 void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2301
2302 void set_max_timeout(time_t msec);
2303 template <class Rep, class Period>
2304 void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2305
2306 void set_basic_auth(const std::string &username, const std::string &password);
2307 void set_bearer_token_auth(const std::string &token);
2308#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2309 void set_digest_auth(const std::string &username,
2310 const std::string &password);
2311#endif
2312
2313 void set_keep_alive(bool on);
2314 void set_follow_location(bool on);
2315
2316 void set_path_encode(bool on);
2317 void set_url_encode(bool on);
2318
2319 void set_compress(bool on);
2320
2321 void set_decompress(bool on);
2322
2323 void set_interface(const std::string &intf);
2324
2325 void set_proxy(const std::string &host, int port);
2326 void set_proxy_basic_auth(const std::string &username,
2327 const std::string &password);
2328 void set_proxy_bearer_token_auth(const std::string &token);
2329#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2330 void set_proxy_digest_auth(const std::string &username,
2331 const std::string &password);
2332#endif
2333
2334#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2335 void enable_server_certificate_verification(bool enabled);
2336 void enable_server_hostname_verification(bool enabled);
2337 void set_server_certificate_verifier(
2338 std::function<SSLVerifierResponse(SSL *ssl)> verifier);
2339#endif
2340
2341 void set_logger(Logger logger);
2342 void set_error_logger(ErrorLogger error_logger);
2343
2344 // SSL
2345#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2346 void set_ca_cert_path(const std::string &ca_cert_file_path,
2347 const std::string &ca_cert_dir_path = std::string());
2348
2349 void set_ca_cert_store(X509_STORE *ca_cert_store);
2350 void load_ca_cert_store(const char *ca_cert, std::size_t size);
2351
2352 long get_openssl_verify_result() const;
2353
2354 SSL_CTX *ssl_context() const;
2355#endif
2356
2357 private:
2358 std::unique_ptr<ClientImpl> cli_;
2359
2360#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2361 bool is_ssl_ = false;
2362#endif
2363};
2364
2365#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2366class SSLServer : public Server
2367{
2368 public:
2369 SSLServer(const char *cert_path, const char *private_key_path,
2370 const char *client_ca_cert_file_path = nullptr,
2371 const char *client_ca_cert_dir_path = nullptr,
2372 const char *private_key_password = nullptr);
2373
2374 SSLServer(X509 *cert, EVP_PKEY *private_key,
2375 X509_STORE *client_ca_cert_store = nullptr);
2376
2377 SSLServer(
2378 const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
2379
2380 ~SSLServer() override;
2381
2382 bool is_valid() const override;
2383
2384 SSL_CTX *ssl_context() const;
2385
2386 void update_certs(X509 *cert, EVP_PKEY *private_key,
2387 X509_STORE *client_ca_cert_store = nullptr);
2388
2389 int ssl_last_error() const
2390 {
2391 return last_ssl_error_;
2392 }
2393
2394 private:
2395 bool process_and_close_socket(socket_t sock) override;
2396
2397 STACK_OF(X509_NAME) * extract_ca_names_from_x509_store(X509_STORE *store);
2398
2399 SSL_CTX *ctx_;
2400 std::mutex ctx_mutex_;
2401
2402 int last_ssl_error_ = 0;
2403};
2404
2405class SSLClient final : public ClientImpl
2406{
2407 public:
2408 explicit SSLClient(const std::string &host);
2409
2410 explicit SSLClient(const std::string &host, int port);
2411
2412 explicit SSLClient(const std::string &host, int port,
2413 const std::string &client_cert_path,
2414 const std::string &client_key_path,
2415 const std::string &private_key_password = std::string());
2416
2417 explicit SSLClient(const std::string &host, int port, X509 *client_cert,
2418 EVP_PKEY *client_key,
2419 const std::string &private_key_password = std::string());
2420
2421 ~SSLClient() override;
2422
2423 bool is_valid() const override;
2424
2425 void set_ca_cert_store(X509_STORE *ca_cert_store);
2426 void load_ca_cert_store(const char *ca_cert, std::size_t size);
2427
2428 long get_openssl_verify_result() const;
2429
2430 SSL_CTX *ssl_context() const;
2431
2432 private:
2433 bool create_and_connect_socket(Socket &socket, Error &error) override;
2434 bool ensure_socket_connection(Socket &socket, Error &error) override;
2435 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
2436 void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
2437
2438 bool
2439 process_socket(const Socket &socket,
2440 std::chrono::time_point<std::chrono::steady_clock> start_time,
2441 std::function<bool(Stream &strm)> callback) override;
2442 bool is_ssl() const override;
2443
2444 bool connect_with_proxy(
2445 Socket &sock,
2446 std::chrono::time_point<std::chrono::steady_clock> start_time,
2447 Response &res, bool &success, Error &error);
2448 bool initialize_ssl(Socket &socket, Error &error);
2449
2450 bool load_certs();
2451
2452 bool verify_host(X509 *server_cert) const;
2453 bool verify_host_with_subject_alt_name(X509 *server_cert) const;
2454 bool verify_host_with_common_name(X509 *server_cert) const;
2455 bool check_host_name(const char *pattern, size_t pattern_len) const;
2456
2457 SSL_CTX *ctx_;
2458 std::mutex ctx_mutex_;
2459 std::once_flag initialize_cert_;
2460
2461 std::vector<std::string> host_components_;
2462
2463 long verify_result_ = 0;
2464
2465 friend class ClientImpl;
2466};
2467#endif
2468
2469/*
2470 * Implementation of template methods.
2471 */
2472
2473namespace detail
2474{
2475
2476template <typename T, typename U>
2477inline void duration_to_sec_and_usec(const T &duration, U callback)
2478{
2479 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2480 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2481 duration - std::chrono::seconds(sec))
2482 .count();
2483 callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2484}
2485
2486template <size_t N> inline constexpr size_t str_len(const char (&)[N])
2487{
2488 return N - 1;
2489}
2490
2491inline bool is_numeric(const std::string &str)
2492{
2493 return !str.empty() &&
2494 std::all_of(str.cbegin(), str.cend(),
2495 [](unsigned char c)
2496 {
2497 return std::isdigit(c);
2498 });
2499}
2500
2501inline size_t get_header_value_u64(const Headers &headers,
2502 const std::string &key, size_t def,
2503 size_t id, bool &is_invalid_value)
2504{
2505 is_invalid_value = false;
2506 auto rng = headers.equal_range(key);
2507 auto it = rng.first;
2508 std::advance(it, static_cast<ssize_t>(id));
2509 if (it != rng.second)
2510 {
2511 if (is_numeric(it->second))
2512 {
2513 return std::strtoull(it->second.data(), nullptr, 10);
2514 }
2515 else
2516 {
2517 is_invalid_value = true;
2518 }
2519 }
2520 return def;
2521}
2522
2523inline size_t get_header_value_u64(const Headers &headers,
2524 const std::string &key, size_t def,
2525 size_t id)
2526{
2527 auto dummy = false;
2528 return get_header_value_u64(headers, key, def, id, dummy);
2529}
2530
2531} // namespace detail
2532
2533inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
2534 size_t id) const
2535{
2536 return detail::get_header_value_u64(headers, key, def, id);
2537}
2538
2539inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
2540 size_t id) const
2541{
2542 return detail::get_header_value_u64(headers, key, def, id);
2543}
2544
2545namespace detail
2546{
2547
2548inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
2549 const void *optval, socklen_t optlen)
2550{
2551 return setsockopt(sock, level, optname,
2552#ifdef _WIN32
2553 reinterpret_cast<const char *>(optval),
2554#else
2555 optval,
2556#endif
2557 optlen) == 0;
2558}
2559
2560inline bool set_socket_opt(socket_t sock, int level, int optname, int optval)
2561{
2562 return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
2563}
2564
2565inline bool set_socket_opt_time(socket_t sock, int level, int optname,
2566 time_t sec, time_t usec)
2567{
2568#ifdef _WIN32
2569 auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
2570#else
2571 timeval timeout;
2572 timeout.tv_sec = static_cast<long>(sec);
2573 timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
2574#endif
2575 return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
2576}
2577
2578} // namespace detail
2579
2581{
2582 detail::set_socket_opt(sock, SOL_SOCKET,
2583#ifdef SO_REUSEPORT
2584 SO_REUSEPORT,
2585#else
2586 SO_REUSEADDR,
2587#endif
2588 1);
2589}
2590
2591inline std::string get_bearer_token_auth(const Request &req)
2592{
2593 if (req.has_header("Authorization"))
2594 {
2595 constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2596 return req.get_header_value("Authorization")
2597 .substr(bearer_header_prefix_len);
2598 }
2599 return "";
2600}
2601
2602template <class Rep, class Period>
2603inline Server &
2604Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration)
2605{
2606 detail::duration_to_sec_and_usec(
2607 duration, [&](time_t sec, time_t usec)
2608 {
2609 set_read_timeout(sec, usec);
2610 });
2611 return *this;
2612}
2613
2614template <class Rep, class Period>
2615inline Server &
2616Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration)
2617{
2618 detail::duration_to_sec_and_usec(
2619 duration, [&](time_t sec, time_t usec)
2620 {
2621 set_write_timeout(sec, usec);
2622 });
2623 return *this;
2624}
2625
2626template <class Rep, class Period>
2627inline Server &
2628Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration)
2629{
2630 detail::duration_to_sec_and_usec(
2631 duration, [&](time_t sec, time_t usec)
2632 {
2633 set_idle_interval(sec, usec);
2634 });
2635 return *this;
2636}
2637
2638inline size_t Result::get_request_header_value_u64(const std::string &key,
2639 size_t def,
2640 size_t id) const
2641{
2642 return detail::get_header_value_u64(request_headers_, key, def, id);
2643}
2644
2645template <class Rep, class Period>
2647 const std::chrono::duration<Rep, Period> &duration)
2648{
2649 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec)
2650 {
2651 set_connection_timeout(sec, usec);
2652 });
2653}
2654
2655template <class Rep, class Period>
2657 const std::chrono::duration<Rep, Period> &duration)
2658{
2659 detail::duration_to_sec_and_usec(
2660 duration, [&](time_t sec, time_t usec)
2661 {
2662 set_read_timeout(sec, usec);
2663 });
2664}
2665
2666template <class Rep, class Period>
2668 const std::chrono::duration<Rep, Period> &duration)
2669{
2670 detail::duration_to_sec_and_usec(
2671 duration, [&](time_t sec, time_t usec)
2672 {
2673 set_write_timeout(sec, usec);
2674 });
2675}
2676
2677template <class Rep, class Period>
2679 const std::chrono::duration<Rep, Period> &duration)
2680{
2681 auto msec =
2682 std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2683 set_max_timeout(msec);
2684}
2685
2686template <class Rep, class Period>
2688 const std::chrono::duration<Rep, Period> &duration)
2689{
2690 cli_->set_connection_timeout(duration);
2691}
2692
2693template <class Rep, class Period>
2694inline void
2695Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration)
2696{
2697 cli_->set_read_timeout(duration);
2698}
2699
2700template <class Rep, class Period>
2701inline void
2702Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration)
2703{
2704 cli_->set_write_timeout(duration);
2705}
2706
2707inline void Client::set_max_timeout(time_t msec)
2708{
2709 cli_->set_max_timeout(msec);
2710}
2711
2712template <class Rep, class Period>
2713inline void
2714Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration)
2715{
2716 cli_->set_max_timeout(duration);
2717}
2718
2719/*
2720 * Forward declarations and types that will be part of the .h file if split into
2721 * .h + .cc.
2722 */
2723
2724std::string hosted_at(const std::string &hostname);
2725
2726void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2727
2728// JavaScript-style URL encoding/decoding functions
2729std::string encode_uri_component(const std::string &value);
2730std::string encode_uri(const std::string &value);
2731std::string decode_uri_component(const std::string &value);
2732std::string decode_uri(const std::string &value);
2733
2734// RFC 3986 compliant URL component encoding/decoding functions
2735std::string encode_path_component(const std::string &component);
2736std::string decode_path_component(const std::string &component);
2737std::string encode_query_component(const std::string &component,
2738 bool space_as_plus = true);
2739std::string decode_query_component(const std::string &component,
2740 bool plus_as_space = true);
2741
2742std::string append_query_params(const std::string &path, const Params &params);
2743
2744std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2745
2746std::pair<std::string, std::string>
2747make_basic_authentication_header(const std::string &username,
2748 const std::string &password,
2749 bool is_proxy = false);
2750
2751namespace detail
2752{
2753
2754#if defined(_WIN32)
2755inline std::wstring u8string_to_wstring(const char *s)
2756{
2757 std::wstring ws;
2758 auto len = static_cast<int>(strlen(s));
2759 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2760 if (wlen > 0)
2761 {
2762 ws.resize(wlen);
2763 wlen = ::MultiByteToWideChar(
2764 CP_UTF8, 0, s, len,
2765 const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2766 if (wlen != static_cast<int>(ws.size()))
2767 {
2768 ws.clear();
2769 }
2770 }
2771 return ws;
2772}
2773#endif
2774
2775struct FileStat
2776{
2777 FileStat(const std::string &path);
2778 bool is_file() const;
2779 bool is_dir() const;
2780 time_t mtime() const;
2781 size_t size() const;
2782
2783 private:
2784#if defined(_WIN32)
2785 struct _stat st_;
2786#else
2787 struct stat st_;
2788#endif
2789 int ret_ = -1;
2790};
2791
2792std::string make_host_and_port_string(const std::string &host, int port,
2793 bool is_ssl);
2794
2795std::string trim_copy(const std::string &s);
2796
2797void divide(
2798 const char *data, std::size_t size, char d,
2799 std::function<void(const char *, std::size_t, const char *, std::size_t)>
2800 fn);
2801
2802void divide(
2803 const std::string &str, char d,
2804 std::function<void(const char *, std::size_t, const char *, std::size_t)>
2805 fn);
2806
2807void split(const char *b, const char *e, char d,
2808 std::function<void(const char *, const char *)> fn);
2809
2810void split(const char *b, const char *e, char d, size_t m,
2811 std::function<void(const char *, const char *)> fn);
2812
2813bool process_client_socket(
2814 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2815 time_t write_timeout_sec, time_t write_timeout_usec,
2816 time_t max_timeout_msec,
2817 std::chrono::time_point<std::chrono::steady_clock> start_time,
2818 std::function<bool(Stream &)> callback);
2819
2820socket_t create_client_socket(const std::string &host, const std::string &ip,
2821 int port, int address_family, bool tcp_nodelay,
2822 bool ipv6_v6only, SocketOptions socket_options,
2823 time_t connection_timeout_sec,
2824 time_t connection_timeout_usec,
2825 time_t read_timeout_sec, time_t read_timeout_usec,
2826 time_t write_timeout_sec,
2827 time_t write_timeout_usec,
2828 const std::string &intf, Error &error);
2829
2830const char *get_header_value(const Headers &headers, const std::string &key,
2831 const char *def, size_t id);
2832
2833std::string params_to_query_str(const Params &params);
2834
2835void parse_query_text(const char *data, std::size_t size, Params &params);
2836
2837void parse_query_text(const std::string &s, Params &params);
2838
2839bool parse_multipart_boundary(const std::string &content_type,
2840 std::string &boundary);
2841
2842bool parse_range_header(const std::string &s, Ranges &ranges);
2843
2844bool parse_accept_header(const std::string &s,
2845 std::vector<std::string> &content_types);
2846
2847int close_socket(socket_t sock);
2848
2849ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2850
2851ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2852
2853enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2854
2855EncodingType encoding_type(const Request &req, const Response &res);
2856
2857class BufferStream final : public Stream
2858{
2859 public:
2860 BufferStream() = default;
2861 ~BufferStream() override = default;
2862
2863 bool is_readable() const override;
2864 bool wait_readable() const override;
2865 bool wait_writable() const override;
2866 ssize_t read(char *ptr, size_t size) override;
2867 ssize_t write(const char *ptr, size_t size) override;
2868 void get_remote_ip_and_port(std::string &ip, int &port) const override;
2869 void get_local_ip_and_port(std::string &ip, int &port) const override;
2870 socket_t socket() const override;
2871 time_t duration() const override;
2872
2873 const std::string &get_buffer() const;
2874
2875 private:
2876 std::string buffer;
2877 size_t position = 0;
2878};
2879
2880class compressor
2881{
2882 public:
2883 virtual ~compressor() = default;
2884
2885 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2886 virtual bool compress(const char *data, size_t data_length, bool last,
2887 Callback callback) = 0;
2888};
2889
2890class decompressor
2891{
2892 public:
2893 virtual ~decompressor() = default;
2894
2895 virtual bool is_valid() const = 0;
2896
2897 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2898 virtual bool decompress(const char *data, size_t data_length,
2899 Callback callback) = 0;
2900};
2901
2902class nocompressor final : public compressor
2903{
2904 public:
2905 ~nocompressor() override = default;
2906
2907 bool compress(const char *data, size_t data_length, bool /*last*/,
2908 Callback callback) override;
2909};
2910
2911#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2912class gzip_compressor final : public compressor
2913{
2914 public:
2915 gzip_compressor();
2916 ~gzip_compressor() override;
2917
2918 bool compress(const char *data, size_t data_length, bool last,
2919 Callback callback) override;
2920
2921 private:
2922 bool is_valid_ = false;
2923 z_stream strm_;
2924};
2925
2926class gzip_decompressor final : public decompressor
2927{
2928 public:
2929 gzip_decompressor();
2930 ~gzip_decompressor() override;
2931
2932 bool is_valid() const override;
2933
2934 bool decompress(const char *data, size_t data_length,
2935 Callback callback) override;
2936
2937 private:
2938 bool is_valid_ = false;
2939 z_stream strm_;
2940};
2941#endif
2942
2943#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2944class brotli_compressor final : public compressor
2945{
2946 public:
2947 brotli_compressor();
2948 ~brotli_compressor();
2949
2950 bool compress(const char *data, size_t data_length, bool last,
2951 Callback callback) override;
2952
2953 private:
2954 BrotliEncoderState *state_ = nullptr;
2955};
2956
2957class brotli_decompressor final : public decompressor
2958{
2959 public:
2960 brotli_decompressor();
2961 ~brotli_decompressor();
2962
2963 bool is_valid() const override;
2964
2965 bool decompress(const char *data, size_t data_length,
2966 Callback callback) override;
2967
2968 private:
2969 BrotliDecoderResult decoder_r;
2970 BrotliDecoderState *decoder_s = nullptr;
2971};
2972#endif
2973
2974#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2975class zstd_compressor : public compressor
2976{
2977 public:
2978 zstd_compressor();
2979 ~zstd_compressor();
2980
2981 bool compress(const char *data, size_t data_length, bool last,
2982 Callback callback) override;
2983
2984 private:
2985 ZSTD_CCtx *ctx_ = nullptr;
2986};
2987
2988class zstd_decompressor : public decompressor
2989{
2990 public:
2991 zstd_decompressor();
2992 ~zstd_decompressor();
2993
2994 bool is_valid() const override;
2995
2996 bool decompress(const char *data, size_t data_length,
2997 Callback callback) override;
2998
2999 private:
3000 ZSTD_DCtx *ctx_ = nullptr;
3001};
3002#endif
3003
3004// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
3005// to store data. The call can set memory on stack for performance.
3006class stream_line_reader
3007{
3008 public:
3009 stream_line_reader(Stream &strm, char *fixed_buffer,
3010 size_t fixed_buffer_size);
3011 const char *ptr() const;
3012 size_t size() const;
3013 bool end_with_crlf() const;
3014 bool getline();
3015
3016 private:
3017 void append(char c);
3018
3019 Stream &strm_;
3020 char *fixed_buffer_;
3021 const size_t fixed_buffer_size_;
3022 size_t fixed_buffer_used_size_ = 0;
3023 std::string growable_buffer_;
3024};
3025
3026bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
3027 const Headers &src_headers);
3028
3029struct ChunkedDecoder
3030{
3031 Stream &strm;
3032 size_t chunk_remaining = 0;
3033 bool finished = false;
3034 char line_buf[64];
3035 size_t last_chunk_total = 0;
3036 size_t last_chunk_offset = 0;
3037
3038 explicit ChunkedDecoder(Stream &s);
3039
3040 ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
3041 size_t &out_chunk_total);
3042
3043 bool parse_trailers_into(Headers &dest, const Headers &src_headers);
3044};
3045
3046class mmap
3047{
3048 public:
3049 mmap(const char *path);
3050 ~mmap();
3051
3052 bool open(const char *path);
3053 void close();
3054
3055 bool is_open() const;
3056 size_t size() const;
3057 const char *data() const;
3058
3059 private:
3060#if defined(_WIN32)
3061 HANDLE hFile_ = NULL;
3062 HANDLE hMapping_ = NULL;
3063#else
3064 int fd_ = -1;
3065#endif
3066 size_t size_ = 0;
3067 void *addr_ = nullptr;
3068 bool is_open_empty_file = false;
3069};
3070
3071// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
3072namespace fields
3073{
3074
3075bool is_token_char(char c);
3076bool is_token(const std::string &s);
3077bool is_field_name(const std::string &s);
3078bool is_vchar(char c);
3079bool is_obs_text(char c);
3080bool is_field_vchar(char c);
3081bool is_field_content(const std::string &s);
3082bool is_field_value(const std::string &s);
3083
3084} // namespace fields
3085
3086} // namespace detail
3087
3088namespace stream
3089{
3090
3091class Result
3092{
3093 public:
3094 Result() : chunk_size_(8192) {}
3095
3096 explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192)
3097 : handle_(std::move(handle)), chunk_size_(chunk_size) {}
3098
3099 Result(Result &&other) noexcept
3100 : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
3101 current_size_(other.current_size_), chunk_size_(other.chunk_size_),
3102 finished_(other.finished_)
3103 {
3104 other.current_size_ = 0;
3105 other.finished_ = true;
3106 }
3107
3108 Result &operator=(Result &&other) noexcept
3109 {
3110 if (this != &other)
3111 {
3112 handle_ = std::move(other.handle_);
3113 buffer_ = std::move(other.buffer_);
3114 current_size_ = other.current_size_;
3115 chunk_size_ = other.chunk_size_;
3116 finished_ = other.finished_;
3117 other.current_size_ = 0;
3118 other.finished_ = true;
3119 }
3120 return *this;
3121 }
3122
3123 Result(const Result &) = delete;
3124 Result &operator=(const Result &) = delete;
3125
3126 // Check if the result is valid (connection succeeded and response received)
3127 bool is_valid() const
3128 {
3129 return handle_.is_valid();
3130 }
3131 explicit operator bool() const
3132 {
3133 return is_valid();
3134 }
3135
3136 // Response status code
3137 int status() const
3138 {
3139 return handle_.response ? handle_.response->status : -1;
3140 }
3141
3142 // Response headers
3143 const Headers &headers() const
3144 {
3145 static const Headers empty_headers;
3146 return handle_.response ? handle_.response->headers : empty_headers;
3147 }
3148
3149 std::string get_header_value(const std::string &key,
3150 const char *def = "") const
3151 {
3152 return handle_.response ? handle_.response->get_header_value(key, def)
3153 : def;
3154 }
3155
3156 bool has_header(const std::string &key) const
3157 {
3158 return handle_.response ? handle_.response->has_header(key) : false;
3159 }
3160
3161 // Error information
3162 Error error() const
3163 {
3164 return handle_.error;
3165 }
3166 Error read_error() const
3167 {
3168 return handle_.get_read_error();
3169 }
3170 bool has_read_error() const
3171 {
3172 return handle_.has_read_error();
3173 }
3174
3175 // Streaming iteration API
3176 // Call next() to read the next chunk, then access data via data()/size()
3177 // Returns true if data was read, false when stream is exhausted
3178 bool next()
3179 {
3180 if (!handle_.is_valid() || finished_)
3181 {
3182 return false;
3183 }
3184
3185 if (buffer_.size() < chunk_size_)
3186 {
3187 buffer_.resize(chunk_size_);
3188 }
3189
3190 ssize_t n = handle_.read(&buffer_[0], chunk_size_);
3191 if (n > 0)
3192 {
3193 current_size_ = static_cast<size_t>(n);
3194 return true;
3195 }
3196
3197 current_size_ = 0;
3198 finished_ = true;
3199 return false;
3200 }
3201
3202 // Pointer to current chunk data (valid after next() returns true)
3203 const char *data() const
3204 {
3205 return buffer_.data();
3206 }
3207
3208 // Size of current chunk (valid after next() returns true)
3209 size_t size() const
3210 {
3211 return current_size_;
3212 }
3213
3214 // Convenience method: read all remaining data into a string
3215 std::string read_all()
3216 {
3217 std::string result;
3218 while (next())
3219 {
3220 result.append(data(), size());
3221 }
3222 return result;
3223 }
3224
3225 private:
3227 std::string buffer_;
3228 size_t current_size_ = 0;
3229 size_t chunk_size_;
3230 bool finished_ = false;
3231};
3232
3233// GET
3234template <typename ClientType>
3235inline Result Get(ClientType &cli, const std::string &path,
3236 size_t chunk_size = 8192)
3237{
3238 return Result{cli.open_stream("GET", path), chunk_size};
3239}
3240
3241template <typename ClientType>
3242inline Result Get(ClientType &cli, const std::string &path,
3243 const Headers &headers, size_t chunk_size = 8192)
3244{
3245 return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
3246}
3247
3248template <typename ClientType>
3249inline Result Get(ClientType &cli, const std::string &path,
3250 const Params &params, size_t chunk_size = 8192)
3251{
3252 return Result{cli.open_stream("GET", path, params), chunk_size};
3253}
3254
3255template <typename ClientType>
3256inline Result Get(ClientType &cli, const std::string &path,
3257 const Params &params, const Headers &headers,
3258 size_t chunk_size = 8192)
3259{
3260 return Result{cli.open_stream("GET", path, params, headers), chunk_size};
3261}
3262
3263// POST
3264template <typename ClientType>
3265inline Result Post(ClientType &cli, const std::string &path,
3266 const std::string &body, const std::string &content_type,
3267 size_t chunk_size = 8192)
3268{
3269 return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
3270 chunk_size};
3271}
3272
3273template <typename ClientType>
3274inline Result Post(ClientType &cli, const std::string &path,
3275 const Headers &headers, const std::string &body,
3276 const std::string &content_type, size_t chunk_size = 8192)
3277{
3278 return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
3279 chunk_size};
3280}
3281
3282template <typename ClientType>
3283inline Result Post(ClientType &cli, const std::string &path,
3284 const Params &params, const std::string &body,
3285 const std::string &content_type, size_t chunk_size = 8192)
3286{
3287 return Result{cli.open_stream("POST", path, params, {}, body, content_type),
3288 chunk_size};
3289}
3290
3291template <typename ClientType>
3292inline Result Post(ClientType &cli, const std::string &path,
3293 const Params &params, const Headers &headers,
3294 const std::string &body, const std::string &content_type,
3295 size_t chunk_size = 8192)
3296{
3297 return Result
3298 {
3299 cli.open_stream("POST", path, params, headers, body, content_type),
3300 chunk_size};
3301}
3302
3303// PUT
3304template <typename ClientType>
3305inline Result Put(ClientType &cli, const std::string &path,
3306 const std::string &body, const std::string &content_type,
3307 size_t chunk_size = 8192)
3308{
3309 return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
3310 chunk_size};
3311}
3312
3313template <typename ClientType>
3314inline Result Put(ClientType &cli, const std::string &path,
3315 const Headers &headers, const std::string &body,
3316 const std::string &content_type, size_t chunk_size = 8192)
3317{
3318 return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
3319 chunk_size};
3320}
3321
3322template <typename ClientType>
3323inline Result Put(ClientType &cli, const std::string &path,
3324 const Params &params, const std::string &body,
3325 const std::string &content_type, size_t chunk_size = 8192)
3326{
3327 return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
3328 chunk_size};
3329}
3330
3331template <typename ClientType>
3332inline Result Put(ClientType &cli, const std::string &path,
3333 const Params &params, const Headers &headers,
3334 const std::string &body, const std::string &content_type,
3335 size_t chunk_size = 8192)
3336{
3337 return Result
3338 {
3339 cli.open_stream("PUT", path, params, headers, body, content_type),
3340 chunk_size};
3341}
3342
3343// PATCH
3344template <typename ClientType>
3345inline Result Patch(ClientType &cli, const std::string &path,
3346 const std::string &body, const std::string &content_type,
3347 size_t chunk_size = 8192)
3348{
3349 return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
3350 chunk_size};
3351}
3352
3353template <typename ClientType>
3354inline Result Patch(ClientType &cli, const std::string &path,
3355 const Headers &headers, const std::string &body,
3356 const std::string &content_type, size_t chunk_size = 8192)
3357{
3358 return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
3359 chunk_size};
3360}
3361
3362template <typename ClientType>
3363inline Result Patch(ClientType &cli, const std::string &path,
3364 const Params &params, const std::string &body,
3365 const std::string &content_type, size_t chunk_size = 8192)
3366{
3367 return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
3368 chunk_size};
3369}
3370
3371template <typename ClientType>
3372inline Result Patch(ClientType &cli, const std::string &path,
3373 const Params &params, const Headers &headers,
3374 const std::string &body, const std::string &content_type,
3375 size_t chunk_size = 8192)
3376{
3377 return Result
3378 {
3379 cli.open_stream("PATCH", path, params, headers, body, content_type),
3380 chunk_size};
3381}
3382
3383// DELETE
3384template <typename ClientType>
3385inline Result Delete(ClientType &cli, const std::string &path,
3386 size_t chunk_size = 8192)
3387{
3388 return Result{cli.open_stream("DELETE", path), chunk_size};
3389}
3390
3391template <typename ClientType>
3392inline Result Delete(ClientType &cli, const std::string &path,
3393 const Headers &headers, size_t chunk_size = 8192)
3394{
3395 return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
3396}
3397
3398template <typename ClientType>
3399inline Result Delete(ClientType &cli, const std::string &path,
3400 const std::string &body, const std::string &content_type,
3401 size_t chunk_size = 8192)
3402{
3403 return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
3404 chunk_size};
3405}
3406
3407template <typename ClientType>
3408inline Result Delete(ClientType &cli, const std::string &path,
3409 const Headers &headers, const std::string &body,
3410 const std::string &content_type,
3411 size_t chunk_size = 8192)
3412{
3413 return Result
3414 {
3415 cli.open_stream("DELETE", path, {}, headers, body, content_type),
3416 chunk_size};
3417}
3418
3419template <typename ClientType>
3420inline Result Delete(ClientType &cli, const std::string &path,
3421 const Params &params, size_t chunk_size = 8192)
3422{
3423 return Result{cli.open_stream("DELETE", path, params), chunk_size};
3424}
3425
3426template <typename ClientType>
3427inline Result Delete(ClientType &cli, const std::string &path,
3428 const Params &params, const Headers &headers,
3429 size_t chunk_size = 8192)
3430{
3431 return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
3432}
3433
3434template <typename ClientType>
3435inline Result Delete(ClientType &cli, const std::string &path,
3436 const Params &params, const std::string &body,
3437 const std::string &content_type,
3438 size_t chunk_size = 8192)
3439{
3440 return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
3441 chunk_size};
3442}
3443
3444template <typename ClientType>
3445inline Result Delete(ClientType &cli, const std::string &path,
3446 const Params &params, const Headers &headers,
3447 const std::string &body, const std::string &content_type,
3448 size_t chunk_size = 8192)
3449{
3450 return Result
3451 {
3452 cli.open_stream("DELETE", path, params, headers, body, content_type),
3453 chunk_size};
3454}
3455
3456// HEAD
3457template <typename ClientType>
3458inline Result Head(ClientType &cli, const std::string &path,
3459 size_t chunk_size = 8192)
3460{
3461 return Result{cli.open_stream("HEAD", path), chunk_size};
3462}
3463
3464template <typename ClientType>
3465inline Result Head(ClientType &cli, const std::string &path,
3466 const Headers &headers, size_t chunk_size = 8192)
3467{
3468 return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
3469}
3470
3471template <typename ClientType>
3472inline Result Head(ClientType &cli, const std::string &path,
3473 const Params &params, size_t chunk_size = 8192)
3474{
3475 return Result{cli.open_stream("HEAD", path, params), chunk_size};
3476}
3477
3478template <typename ClientType>
3479inline Result Head(ClientType &cli, const std::string &path,
3480 const Params &params, const Headers &headers,
3481 size_t chunk_size = 8192)
3482{
3483 return Result{cli.open_stream("HEAD", path, params, headers), chunk_size};
3484}
3485
3486// OPTIONS
3487template <typename ClientType>
3488inline Result Options(ClientType &cli, const std::string &path,
3489 size_t chunk_size = 8192)
3490{
3491 return Result{cli.open_stream("OPTIONS", path), chunk_size};
3492}
3493
3494template <typename ClientType>
3495inline Result Options(ClientType &cli, const std::string &path,
3496 const Headers &headers, size_t chunk_size = 8192)
3497{
3498 return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
3499}
3500
3501template <typename ClientType>
3502inline Result Options(ClientType &cli, const std::string &path,
3503 const Params &params, size_t chunk_size = 8192)
3504{
3505 return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
3506}
3507
3508template <typename ClientType>
3509inline Result Options(ClientType &cli, const std::string &path,
3510 const Params &params, const Headers &headers,
3511 size_t chunk_size = 8192)
3512{
3513 return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
3514}
3515
3516} // namespace stream
3517
3518namespace sse
3519{
3520
3521struct SSEMessage
3522{
3523 std::string event; // Event type (default: "message")
3524 std::string data; // Event payload
3525 std::string id; // Event ID for Last-Event-ID header
3526
3527 SSEMessage() : event("message") {}
3528
3529 void clear()
3530 {
3531 event = "message";
3532 data.clear();
3533 id.clear();
3534 }
3535};
3536
3537class SSEClient
3538{
3539 public:
3540 using MessageHandler = std::function<void(const SSEMessage &)>;
3541 using ErrorHandler = std::function<void(Error)>;
3542 using OpenHandler = std::function<void()>;
3543
3544 SSEClient(Client &client, const std::string &path)
3545 : client_(client), path_(path) {}
3546
3547 SSEClient(Client &client, const std::string &path, const Headers &headers)
3548 : client_(client), path_(path), headers_(headers) {}
3549
3550 ~SSEClient()
3551 {
3552 stop();
3553 }
3554
3555 SSEClient(const SSEClient &) = delete;
3556 SSEClient &operator=(const SSEClient &) = delete;
3557
3558 // Event handlers
3559 SSEClient &on_message(MessageHandler handler)
3560 {
3561 on_message_ = std::move(handler);
3562 return *this;
3563 }
3564
3565 SSEClient &on_event(const std::string &type, MessageHandler handler)
3566 {
3567 event_handlers_[type] = std::move(handler);
3568 return *this;
3569 }
3570
3571 SSEClient &on_open(OpenHandler handler)
3572 {
3573 on_open_ = std::move(handler);
3574 return *this;
3575 }
3576
3577 SSEClient &on_error(ErrorHandler handler)
3578 {
3579 on_error_ = std::move(handler);
3580 return *this;
3581 }
3582
3583 SSEClient &set_reconnect_interval(int ms)
3584 {
3585 reconnect_interval_ms_ = ms;
3586 return *this;
3587 }
3588
3589 SSEClient &set_max_reconnect_attempts(int n)
3590 {
3591 max_reconnect_attempts_ = n;
3592 return *this;
3593 }
3594
3595 // State accessors
3596 bool is_connected() const
3597 {
3598 return connected_.load();
3599 }
3600 const std::string &last_event_id() const
3601 {
3602 return last_event_id_;
3603 }
3604
3605 // Blocking start - runs event loop with auto-reconnect
3606 void start()
3607 {
3608 running_.store(true);
3609 run_event_loop();
3610 }
3611
3612 // Non-blocking start - runs in background thread
3613 void start_async()
3614 {
3615 running_.store(true);
3616 async_thread_ = std::thread([this]()
3617 {
3618 run_event_loop();
3619 });
3620 }
3621
3622 // Stop the client (thread-safe)
3623 void stop()
3624 {
3625 running_.store(false);
3626 client_.stop(); // Cancel any pending operations
3627 if (async_thread_.joinable())
3628 {
3629 async_thread_.join();
3630 }
3631 }
3632
3633 private:
3634 // Parse a single SSE field line
3635 // Returns true if this line ends an event (blank line)
3636 bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms)
3637 {
3638 // Blank line signals end of event
3639 if (line.empty() || line == "\r")
3640 {
3641 return true;
3642 }
3643
3644 // Lines starting with ':' are comments (ignored)
3645 if (!line.empty() && line[0] == ':')
3646 {
3647 return false;
3648 }
3649
3650 // Find the colon separator
3651 auto colon_pos = line.find(':');
3652 if (colon_pos == std::string::npos)
3653 {
3654 // Line with no colon is treated as field name with empty value
3655 return false;
3656 }
3657
3658 auto field = line.substr(0, colon_pos);
3659 std::string value;
3660
3661 // Value starts after colon, skip optional single space
3662 if (colon_pos + 1 < line.size())
3663 {
3664 auto value_start = colon_pos + 1;
3665 if (line[value_start] == ' ')
3666 {
3667 value_start++;
3668 }
3669 value = line.substr(value_start);
3670 // Remove trailing \r if present
3671 if (!value.empty() && value.back() == '\r')
3672 {
3673 value.pop_back();
3674 }
3675 }
3676
3677 // Handle known fields
3678 if (field == "event")
3679 {
3680 msg.event = value;
3681 }
3682 else if (field == "data")
3683 {
3684 // Multiple data lines are concatenated with newlines
3685 if (!msg.data.empty())
3686 {
3687 msg.data += "\n";
3688 }
3689 msg.data += value;
3690 }
3691 else if (field == "id")
3692 {
3693 // Empty id is valid (clears the last event ID)
3694 msg.id = value;
3695 }
3696 else if (field == "retry")
3697 {
3698 // Parse retry interval in milliseconds
3699 try
3700 {
3701 retry_ms = std::stoi(value);
3702 }
3703 catch (...)
3704 {
3705 // Invalid retry value, ignore
3706 }
3707 }
3708 // Unknown fields are ignored per SSE spec
3709
3710 return false;
3711 }
3712
3713 // Main event loop with auto-reconnect
3714 void run_event_loop()
3715 {
3716 auto reconnect_count = 0;
3717
3718 while (running_.load())
3719 {
3720 // Build headers, including Last-Event-ID if we have one
3721 auto request_headers = headers_;
3722 if (!last_event_id_.empty())
3723 {
3724 request_headers.emplace("Last-Event-ID", last_event_id_);
3725 }
3726
3727 // Open streaming connection
3728 auto result = stream::Get(client_, path_, request_headers);
3729
3730 // Connection error handling
3731 if (!result)
3732 {
3733 connected_.store(false);
3734 if (on_error_)
3735 {
3736 on_error_(result.error());
3737 }
3738
3739 if (!should_reconnect(reconnect_count))
3740 {
3741 break;
3742 }
3743 wait_for_reconnect();
3744 reconnect_count++;
3745 continue;
3746 }
3747
3748 if (result.status() != 200)
3749 {
3750 connected_.store(false);
3751 // For certain errors, don't reconnect
3752 if (result.status() == 204 || // No Content - server wants us to stop
3753 result.status() == 404 || // Not Found
3754 result.status() == 401 || // Unauthorized
3755 result.status() == 403) // Forbidden
3756 {
3757 if (on_error_)
3758 {
3759 on_error_(Error::Connection);
3760 }
3761 break;
3762 }
3763
3764 if (on_error_)
3765 {
3766 on_error_(Error::Connection);
3767 }
3768
3769 if (!should_reconnect(reconnect_count))
3770 {
3771 break;
3772 }
3773 wait_for_reconnect();
3774 reconnect_count++;
3775 continue;
3776 }
3777
3778 // Connection successful
3779 connected_.store(true);
3780 reconnect_count = 0;
3781 if (on_open_)
3782 {
3783 on_open_();
3784 }
3785
3786 // Event receiving loop
3787 std::string buffer;
3788 SSEMessage current_msg;
3789
3790 while (running_.load() && result.next())
3791 {
3792 buffer.append(result.data(), result.size());
3793
3794 // Process complete lines in the buffer
3795 size_t line_start = 0;
3796 size_t newline_pos;
3797
3798 while ((newline_pos = buffer.find('\n', line_start)) !=
3799 std::string::npos)
3800 {
3801 auto line = buffer.substr(line_start, newline_pos - line_start);
3802 line_start = newline_pos + 1;
3803
3804 // Parse the line and check if event is complete
3805 auto event_complete =
3806 parse_sse_line(line, current_msg, reconnect_interval_ms_);
3807
3808 if (event_complete && !current_msg.data.empty())
3809 {
3810 // Update last_event_id for reconnection
3811 if (!current_msg.id.empty())
3812 {
3813 last_event_id_ = current_msg.id;
3814 }
3815
3816 // Dispatch event to appropriate handler
3817 dispatch_event(current_msg);
3818
3819 current_msg.clear();
3820 }
3821 }
3822
3823 // Keep unprocessed data in buffer
3824 buffer.erase(0, line_start);
3825 }
3826
3827 // Connection ended
3828 connected_.store(false);
3829
3830 if (!running_.load())
3831 {
3832 break;
3833 }
3834
3835 // Check for read errors
3836 if (result.has_read_error())
3837 {
3838 if (on_error_)
3839 {
3840 on_error_(result.read_error());
3841 }
3842 }
3843
3844 if (!should_reconnect(reconnect_count))
3845 {
3846 break;
3847 }
3848 wait_for_reconnect();
3849 reconnect_count++;
3850 }
3851
3852 connected_.store(false);
3853 }
3854
3855 // Dispatch event to appropriate handler
3856 void dispatch_event(const SSEMessage &msg)
3857 {
3858 // Check for specific event type handler first
3859 auto it = event_handlers_.find(msg.event);
3860 if (it != event_handlers_.end())
3861 {
3862 it->second(msg);
3863 return;
3864 }
3865
3866 // Fall back to generic message handler
3867 if (on_message_)
3868 {
3869 on_message_(msg);
3870 }
3871 }
3872
3873 // Check if we should attempt to reconnect
3874 bool should_reconnect(int count) const
3875 {
3876 if (!running_.load())
3877 {
3878 return false;
3879 }
3880 if (max_reconnect_attempts_ == 0)
3881 {
3882 return true; // unlimited
3883 }
3884 return count < max_reconnect_attempts_;
3885 }
3886
3887 // Wait for reconnect interval
3888 void wait_for_reconnect()
3889 {
3890 // Use small increments to check running_ flag frequently
3891 auto waited = 0;
3892 while (running_.load() && waited < reconnect_interval_ms_)
3893 {
3894 std::this_thread::sleep_for(std::chrono::milliseconds(100));
3895 waited += 100;
3896 }
3897 }
3898
3899 // Client and path
3900 Client &client_;
3901 std::string path_;
3902 Headers headers_;
3903
3904 // Callbacks
3905 MessageHandler on_message_;
3906 std::map<std::string, MessageHandler> event_handlers_;
3907 OpenHandler on_open_;
3908 ErrorHandler on_error_;
3909
3910 // Configuration
3911 int reconnect_interval_ms_ = 3000;
3912 int max_reconnect_attempts_ = 0; // 0 = unlimited
3913
3914 // State
3915 std::atomic<bool> running_{false};
3916 std::atomic<bool> connected_{false};
3917 std::string last_event_id_;
3918
3919 // Async support
3920 std::thread async_thread_;
3921};
3922
3923} // namespace sse
3924
3925// ----------------------------------------------------------------------------
3926
3927/*
3928 * Implementation that will be part of the .cc file if split into .h + .cc.
3929 */
3930
3931namespace detail
3932{
3933
3934inline bool is_hex(char c, int &v)
3935{
3936 if (isdigit(c))
3937 {
3938 v = c - '0';
3939 return true;
3940 }
3941 else if ('A' <= c && c <= 'F')
3942 {
3943 v = c - 'A' + 10;
3944 return true;
3945 }
3946 else if ('a' <= c && c <= 'f')
3947 {
3948 v = c - 'a' + 10;
3949 return true;
3950 }
3951 return false;
3952}
3953
3954inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
3955 int &val)
3956{
3957 if (i >= s.size())
3958 {
3959 return false;
3960 }
3961
3962 val = 0;
3963 for (; cnt; i++, cnt--)
3964 {
3965 if (!s[i])
3966 {
3967 return false;
3968 }
3969 auto v = 0;
3970 if (is_hex(s[i], v))
3971 {
3972 val = val * 16 + v;
3973 }
3974 else
3975 {
3976 return false;
3977 }
3978 }
3979 return true;
3980}
3981
3982inline std::string from_i_to_hex(size_t n)
3983{
3984 static const auto charset = "0123456789abcdef";
3985 std::string ret;
3986 do
3987 {
3988 ret = charset[n & 15] + ret;
3989 n >>= 4;
3990 }
3991 while (n > 0);
3992 return ret;
3993}
3994
3995inline std::string compute_etag(const FileStat &fs)
3996{
3997 if (!fs.is_file())
3998 {
3999 return std::string();
4000 }
4001
4002 // If mtime cannot be determined (negative value indicates an error
4003 // or sentinel), do not generate an ETag. Returning a neutral / fixed
4004 // value like 0 could collide with a real file that legitimately has
4005 // mtime == 0 (epoch) and lead to misleading validators.
4006 auto mtime_raw = fs.mtime();
4007 if (mtime_raw < 0)
4008 {
4009 return std::string();
4010 }
4011
4012 auto mtime = static_cast<size_t>(mtime_raw);
4013 auto size = fs.size();
4014
4015 return std::string("W/\"") + from_i_to_hex(mtime) + "-" +
4016 from_i_to_hex(size) + "\"";
4017}
4018
4019// Format time_t as HTTP-date (RFC 9110 Section 5.6.7): "Sun, 06 Nov 1994
4020// 08:49:37 GMT" This implementation is defensive: it validates `mtime`, checks
4021// return values from `gmtime_r`/`gmtime_s`, and ensures `strftime` succeeds.
4022inline std::string file_mtime_to_http_date(time_t mtime)
4023{
4024 if (mtime < 0)
4025 {
4026 return std::string();
4027 }
4028
4029 struct tm tm_buf;
4030#ifdef _WIN32
4031 if (gmtime_s(&tm_buf, &mtime) != 0)
4032 {
4033 return std::string();
4034 }
4035#else
4036 if (gmtime_r(&mtime, &tm_buf) == nullptr)
4037 {
4038 return std::string();
4039 }
4040#endif
4041 char buf[64];
4042 if (strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0)
4043 {
4044 return std::string();
4045 }
4046
4047 return std::string(buf);
4048}
4049
4050// Parse HTTP-date (RFC 9110 Section 5.6.7) to time_t. Returns -1 on failure.
4051inline time_t parse_http_date(const std::string &date_str)
4052{
4053 struct tm tm_buf;
4054
4055 // Create a classic locale object once for all parsing attempts
4056 const std::locale classic_locale = std::locale::classic();
4057
4058 // Try to parse using std::get_time (C++11, cross-platform)
4059 auto try_parse = [&](const char *fmt) -> bool
4060 {
4061 std::istringstream ss(date_str);
4062 ss.imbue(classic_locale);
4063
4064 memset(&tm_buf, 0, sizeof(tm_buf));
4065 ss >> std::get_time(&tm_buf, fmt);
4066
4067 return !ss.fail();
4068 };
4069
4070 // RFC 9110 preferred format (HTTP-date): "Sun, 06 Nov 1994 08:49:37 GMT"
4071 if (!try_parse("%a, %d %b %Y %H:%M:%S"))
4072 {
4073 // RFC 850 format: "Sunday, 06-Nov-94 08:49:37 GMT"
4074 if (!try_parse("%A, %d-%b-%y %H:%M:%S"))
4075 {
4076 // asctime format: "Sun Nov 6 08:49:37 1994"
4077 if (!try_parse("%a %b %d %H:%M:%S %Y"))
4078 {
4079 return static_cast<time_t>(-1);
4080 }
4081 }
4082 }
4083
4084#ifdef _WIN32
4085 return _mkgmtime(&tm_buf);
4086#else
4087 return timegm(&tm_buf);
4088#endif
4089}
4090
4091inline bool is_weak_etag(const std::string &s)
4092{
4093 // Check if the string is a weak ETag (starts with 'W/"')
4094 return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
4095}
4096
4097inline bool is_strong_etag(const std::string &s)
4098{
4099 // Check if the string is a strong ETag (starts and ends with '"', at least 2
4100 // chars)
4101 return s.size() >= 2 && s[0] == '"' && s.back() == '"';
4102}
4103
4104inline size_t to_utf8(int code, char *buff)
4105{
4106 if (code < 0x0080)
4107 {
4108 buff[0] = static_cast<char>(code & 0x7F);
4109 return 1;
4110 }
4111 else if (code < 0x0800)
4112 {
4113 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
4114 buff[1] = static_cast<char>(0x80 | (code & 0x3F));
4115 return 2;
4116 }
4117 else if (code < 0xD800)
4118 {
4119 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4120 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4121 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
4122 return 3;
4123 }
4124 else if (code < 0xE000) // D800 - DFFF is invalid...
4125 {
4126 return 0;
4127 }
4128 else if (code < 0x10000)
4129 {
4130 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4131 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4132 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
4133 return 3;
4134 }
4135 else if (code < 0x110000)
4136 {
4137 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
4138 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
4139 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4140 buff[3] = static_cast<char>(0x80 | (code & 0x3F));
4141 return 4;
4142 }
4143
4144 // NOTREACHED
4145 return 0;
4146}
4147
4148// NOTE: This code came up with the following stackoverflow post:
4149// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
4150inline std::string base64_encode(const std::string &in)
4151{
4152 static const auto lookup =
4153 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4154
4155 std::string out;
4156 out.reserve(in.size());
4157
4158 auto val = 0;
4159 auto valb = -6;
4160
4161 for (auto c : in)
4162 {
4163 val = (val << 8) + static_cast<uint8_t>(c);
4164 valb += 8;
4165 while (valb >= 0)
4166 {
4167 out.push_back(lookup[(val >> valb) & 0x3F]);
4168 valb -= 6;
4169 }
4170 }
4171
4172 if (valb > -6)
4173 {
4174 out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]);
4175 }
4176
4177 while (out.size() % 4)
4178 {
4179 out.push_back('=');
4180 }
4181
4182 return out;
4183}
4184
4185inline bool is_valid_path(const std::string &path)
4186{
4187 size_t level = 0;
4188 size_t i = 0;
4189
4190 // Skip slash
4191 while (i < path.size() && path[i] == '/')
4192 {
4193 i++;
4194 }
4195
4196 while (i < path.size())
4197 {
4198 // Read component
4199 auto beg = i;
4200 while (i < path.size() && path[i] != '/')
4201 {
4202 if (path[i] == '\0')
4203 {
4204 return false;
4205 }
4206 else if (path[i] == '\\')
4207 {
4208 return false;
4209 }
4210 i++;
4211 }
4212
4213 auto len = i - beg;
4214 assert(len > 0);
4215
4216 if (!path.compare(beg, len, "."))
4217 {
4218 ;
4219 }
4220 else if (!path.compare(beg, len, ".."))
4221 {
4222 if (level == 0)
4223 {
4224 return false;
4225 }
4226 level--;
4227 }
4228 else
4229 {
4230 level++;
4231 }
4232
4233 // Skip slash
4234 while (i < path.size() && path[i] == '/')
4235 {
4236 i++;
4237 }
4238 }
4239
4240 return true;
4241}
4242
4243inline FileStat::FileStat(const std::string &path)
4244{
4245#if defined(_WIN32)
4246 auto wpath = u8string_to_wstring(path.c_str());
4247 ret_ = _wstat(wpath.c_str(), &st_);
4248#else
4249 ret_ = stat(path.c_str(), &st_);
4250#endif
4251}
4252inline bool FileStat::is_file() const
4253{
4254 return ret_ >= 0 && S_ISREG(st_.st_mode);
4255}
4256inline bool FileStat::is_dir() const
4257{
4258 return ret_ >= 0 && S_ISDIR(st_.st_mode);
4259}
4260
4261inline time_t FileStat::mtime() const
4262{
4263 return ret_ >= 0 ? static_cast<time_t>(st_.st_mtime)
4264 : static_cast<time_t>(-1);
4265}
4266
4267inline size_t FileStat::size() const
4268{
4269 return ret_ >= 0 ? static_cast<size_t>(st_.st_size) : 0;
4270}
4271
4272inline std::string encode_path(const std::string &s)
4273{
4274 std::string result;
4275 result.reserve(s.size());
4276
4277 for (size_t i = 0; s[i]; i++)
4278 {
4279 switch (s[i])
4280 {
4281 case ' ':
4282 result += "%20";
4283 break;
4284 case '+':
4285 result += "%2B";
4286 break;
4287 case '\r':
4288 result += "%0D";
4289 break;
4290 case '\n':
4291 result += "%0A";
4292 break;
4293 case '\'':
4294 result += "%27";
4295 break;
4296 case ',':
4297 result += "%2C";
4298 break;
4299 // case ':': result += "%3A"; break; // ok? probably...
4300 case ';':
4301 result += "%3B";
4302 break;
4303 default:
4304 auto c = static_cast<uint8_t>(s[i]);
4305 if (c >= 0x80)
4306 {
4307 result += '%';
4308 char hex[4];
4309 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
4310 assert(len == 2);
4311 result.append(hex, static_cast<size_t>(len));
4312 }
4313 else
4314 {
4315 result += s[i];
4316 }
4317 break;
4318 }
4319 }
4320
4321 return result;
4322}
4323
4324inline std::string file_extension(const std::string &path)
4325{
4326 std::smatch m;
4327 thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
4328 if (std::regex_search(path, m, re))
4329 {
4330 return m[1].str();
4331 }
4332 return std::string();
4333}
4334
4335inline bool is_space_or_tab(char c)
4336{
4337 return c == ' ' || c == '\t';
4338}
4339
4340template <typename T>
4341inline bool parse_header(const char *beg, const char *end, T fn);
4342
4343template <typename T>
4344inline bool parse_header(const char *beg, const char *end, T fn)
4345{
4346 // Skip trailing spaces and tabs.
4347 while (beg < end && is_space_or_tab(end[-1]))
4348 {
4349 end--;
4350 }
4351
4352 auto p = beg;
4353 while (p < end && *p != ':')
4354 {
4355 p++;
4356 }
4357
4358 auto name = std::string(beg, p);
4359 if (!detail::fields::is_field_name(name))
4360 {
4361 return false;
4362 }
4363
4364 if (p == end)
4365 {
4366 return false;
4367 }
4368
4369 auto key_end = p;
4370
4371 if (*p++ != ':')
4372 {
4373 return false;
4374 }
4375
4376 while (p < end && is_space_or_tab(*p))
4377 {
4378 p++;
4379 }
4380
4381 if (p <= end)
4382 {
4383 auto key_len = key_end - beg;
4384 if (!key_len)
4385 {
4386 return false;
4387 }
4388
4389 auto key = std::string(beg, key_end);
4390 auto val = std::string(p, end);
4391
4392 if (!detail::fields::is_field_value(val))
4393 {
4394 return false;
4395 }
4396
4397 if (case_ignore::equal(key, "Location") ||
4398 case_ignore::equal(key, "Referer"))
4399 {
4400 fn(key, val);
4401 }
4402 else
4403 {
4404 fn(key, decode_path_component(val));
4405 }
4406
4407 return true;
4408 }
4409
4410 return false;
4411}
4412
4413inline bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
4414 const Headers &src_headers)
4415{
4416 // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
4417 // transfer coding is complete when a chunk with a chunk-size of zero is
4418 // received, possibly followed by a trailer section, and finally terminated by
4419 // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
4420 //
4421 // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
4422 // doesn't care for the existence of the final CRLF. In other words, it seems
4423 // to be ok whether the final CRLF exists or not in the chunked data.
4424 // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4425 //
4426 // According to the reference code in RFC 9112, cpp-httplib now allows
4427 // chunked transfer coding data without the final CRLF.
4428
4429 // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
4430 thread_local case_ignore::unordered_set<std::string> prohibited_trailers =
4431 {
4432 "transfer-encoding",
4433 "content-length",
4434 "host",
4435 "authorization",
4436 "www-authenticate",
4437 "proxy-authenticate",
4438 "proxy-authorization",
4439 "cookie",
4440 "set-cookie",
4441 "cache-control",
4442 "expect",
4443 "max-forwards",
4444 "pragma",
4445 "range",
4446 "te",
4447 "age",
4448 "expires",
4449 "date",
4450 "location",
4451 "retry-after",
4452 "vary",
4453 "warning",
4454 "content-encoding",
4455 "content-type",
4456 "content-range",
4457 "trailer"
4458 };
4459
4460 case_ignore::unordered_set<std::string> declared_trailers;
4461 auto trailer_header = get_header_value(src_headers, "Trailer", "", 0);
4462 if (trailer_header && std::strlen(trailer_header))
4463 {
4464 auto len = std::strlen(trailer_header);
4465 split(trailer_header, trailer_header + len, ',',
4466 [&](const char *b, const char *e)
4467 {
4468 const char *kbeg = b;
4469 const char *kend = e;
4470 while (kbeg < kend && (*kbeg == ' ' || *kbeg == '\t'))
4471 {
4472 ++kbeg;
4473 }
4474 while (kend > kbeg && (kend[-1] == ' ' || kend[-1] == '\t'))
4475 {
4476 --kend;
4477 }
4478 std::string key(kbeg, static_cast<size_t>(kend - kbeg));
4479 if (!key.empty() &&
4480 prohibited_trailers.find(key) == prohibited_trailers.end())
4481 {
4482 declared_trailers.insert(key);
4483 }
4484 });
4485 }
4486
4487 size_t trailer_header_count = 0;
4488 while (strcmp(line_reader.ptr(), "\r\n") != 0)
4489 {
4490 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH)
4491 {
4492 return false;
4493 }
4494 if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT)
4495 {
4496 return false;
4497 }
4498
4499 constexpr auto line_terminator_len = 2;
4500 auto line_beg = line_reader.ptr();
4501 auto line_end =
4502 line_reader.ptr() + line_reader.size() - line_terminator_len;
4503
4504 if (!parse_header(line_beg, line_end,
4505 [&](const std::string & key, const std::string & val)
4506 {
4507 if (declared_trailers.find(key) !=
4508 declared_trailers.end())
4509 {
4510 dest.emplace(key, val);
4511 trailer_header_count++;
4512 }
4513 }))
4514 {
4515 return false;
4516 }
4517
4518 if (!line_reader.getline())
4519 {
4520 return false;
4521 }
4522 }
4523
4524 return true;
4525}
4526
4527inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
4528 size_t right)
4529{
4530 while (b + left < e && is_space_or_tab(b[left]))
4531 {
4532 left++;
4533 }
4534 while (right > 0 && is_space_or_tab(b[right - 1]))
4535 {
4536 right--;
4537 }
4538 return std::make_pair(left, right);
4539}
4540
4541inline std::string trim_copy(const std::string &s)
4542{
4543 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
4544 return s.substr(r.first, r.second - r.first);
4545}
4546
4547inline std::string trim_double_quotes_copy(const std::string &s)
4548{
4549 if (s.length() >= 2 && s.front() == '"' && s.back() == '"')
4550 {
4551 return s.substr(1, s.size() - 2);
4552 }
4553 return s;
4554}
4555
4556inline void
4557divide(const char *data, std::size_t size, char d,
4558 std::function<void(const char *, std::size_t, const char *, std::size_t)>
4559 fn)
4560{
4561 const auto it = std::find(data, data + size, d);
4562 const auto found = static_cast<std::size_t>(it != data + size);
4563 const auto lhs_data = data;
4564 const auto lhs_size = static_cast<std::size_t>(it - data);
4565 const auto rhs_data = it + found;
4566 const auto rhs_size = size - lhs_size - found;
4567
4568 fn(lhs_data, lhs_size, rhs_data, rhs_size);
4569}
4570
4571inline void
4572divide(const std::string &str, char d,
4573 std::function<void(const char *, std::size_t, const char *, std::size_t)>
4574 fn)
4575{
4576 divide(str.data(), str.size(), d, std::move(fn));
4577}
4578
4579inline void split(const char *b, const char *e, char d,
4580 std::function<void(const char *, const char *)> fn)
4581{
4582 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
4583}
4584
4585inline void split(const char *b, const char *e, char d, size_t m,
4586 std::function<void(const char *, const char *)> fn)
4587{
4588 size_t i = 0;
4589 size_t beg = 0;
4590 size_t count = 1;
4591
4592 while (e ? (b + i < e) : (b[i] != '\0'))
4593 {
4594 if (b[i] == d && count < m)
4595 {
4596 auto r = trim(b, e, beg, i);
4597 if (r.first < r.second)
4598 {
4599 fn(&b[r.first], &b[r.second]);
4600 }
4601 beg = i + 1;
4602 count++;
4603 }
4604 i++;
4605 }
4606
4607 if (i)
4608 {
4609 auto r = trim(b, e, beg, i);
4610 if (r.first < r.second)
4611 {
4612 fn(&b[r.first], &b[r.second]);
4613 }
4614 }
4615}
4616
4617inline bool split_find(const char *b, const char *e, char d, size_t m,
4618 std::function<bool(const char *, const char *)> fn)
4619{
4620 size_t i = 0;
4621 size_t beg = 0;
4622 size_t count = 1;
4623
4624 while (e ? (b + i < e) : (b[i] != '\0'))
4625 {
4626 if (b[i] == d && count < m)
4627 {
4628 auto r = trim(b, e, beg, i);
4629 if (r.first < r.second)
4630 {
4631 auto found = fn(&b[r.first], &b[r.second]);
4632 if (found)
4633 {
4634 return true;
4635 }
4636 }
4637 beg = i + 1;
4638 count++;
4639 }
4640 i++;
4641 }
4642
4643 if (i)
4644 {
4645 auto r = trim(b, e, beg, i);
4646 if (r.first < r.second)
4647 {
4648 auto found = fn(&b[r.first], &b[r.second]);
4649 if (found)
4650 {
4651 return true;
4652 }
4653 }
4654 }
4655
4656 return false;
4657}
4658
4659inline bool split_find(const char *b, const char *e, char d,
4660 std::function<bool(const char *, const char *)> fn)
4661{
4662 return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
4663 std::move(fn));
4664}
4665
4666inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
4667 size_t fixed_buffer_size)
4668 : strm_(strm), fixed_buffer_(fixed_buffer),
4669 fixed_buffer_size_(fixed_buffer_size) {}
4670
4671inline const char *stream_line_reader::ptr() const
4672{
4673 if (growable_buffer_.empty())
4674 {
4675 return fixed_buffer_;
4676 }
4677 else
4678 {
4679 return growable_buffer_.data();
4680 }
4681}
4682
4683inline size_t stream_line_reader::size() const
4684{
4685 if (growable_buffer_.empty())
4686 {
4687 return fixed_buffer_used_size_;
4688 }
4689 else
4690 {
4691 return growable_buffer_.size();
4692 }
4693}
4694
4695inline bool stream_line_reader::end_with_crlf() const
4696{
4697 auto end = ptr() + size();
4698 return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
4699}
4700
4701inline bool stream_line_reader::getline()
4702{
4703 fixed_buffer_used_size_ = 0;
4704 growable_buffer_.clear();
4705
4706#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4707 char prev_byte = 0;
4708#endif
4709
4710 for (size_t i = 0;; i++)
4711 {
4712 if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH)
4713 {
4714 // Treat exceptionally long lines as an error to
4715 // prevent infinite loops/memory exhaustion
4716 return false;
4717 }
4718 char byte;
4719 auto n = strm_.read(&byte, 1);
4720
4721 if (n < 0)
4722 {
4723 return false;
4724 }
4725 else if (n == 0)
4726 {
4727 if (i == 0)
4728 {
4729 return false;
4730 }
4731 else
4732 {
4733 break;
4734 }
4735 }
4736
4737 append(byte);
4738
4739#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4740 if (byte == '\n')
4741 {
4742 break;
4743 }
4744#else
4745 if (prev_byte == '\r' && byte == '\n')
4746 {
4747 break;
4748 }
4749 prev_byte = byte;
4750#endif
4751 }
4752
4753 return true;
4754}
4755
4756inline void stream_line_reader::append(char c)
4757{
4758 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1)
4759 {
4760 fixed_buffer_[fixed_buffer_used_size_++] = c;
4761 fixed_buffer_[fixed_buffer_used_size_] = '\0';
4762 }
4763 else
4764 {
4765 if (growable_buffer_.empty())
4766 {
4767 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
4768 growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
4769 }
4770 growable_buffer_ += c;
4771 }
4772}
4773
4774inline mmap::mmap(const char *path)
4775{
4776 open(path);
4777}
4778
4779inline mmap::~mmap()
4780{
4781 close();
4782}
4783
4784inline bool mmap::open(const char *path)
4785{
4786 close();
4787
4788#if defined(_WIN32)
4789 auto wpath = u8string_to_wstring(path);
4790 if (wpath.empty())
4791 {
4792 return false;
4793 }
4794
4795 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
4796 OPEN_EXISTING, NULL);
4797
4798 if (hFile_ == INVALID_HANDLE_VALUE)
4799 {
4800 return false;
4801 }
4802
4803 LARGE_INTEGER size{};
4804 if (!::GetFileSizeEx(hFile_, &size))
4805 {
4806 return false;
4807 }
4808 // If the following line doesn't compile due to QuadPart, update Windows SDK.
4809 // See:
4810 // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
4811 if (static_cast<ULONGLONG>(size.QuadPart) >
4812 (std::numeric_limits<decltype(size_)>::max)())
4813 {
4814 // `size_t` might be 32-bits, on 32-bits Windows.
4815 return false;
4816 }
4817 size_ = static_cast<size_t>(size.QuadPart);
4818
4819 hMapping_ =
4820 ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
4821
4822 // Special treatment for an empty file...
4823 if (hMapping_ == NULL && size_ == 0)
4824 {
4825 close();
4826 is_open_empty_file = true;
4827 return true;
4828 }
4829
4830 if (hMapping_ == NULL)
4831 {
4832 close();
4833 return false;
4834 }
4835
4836 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
4837
4838 if (addr_ == nullptr)
4839 {
4840 close();
4841 return false;
4842 }
4843#else
4844 fd_ = ::open(path, O_RDONLY);
4845 if (fd_ == -1)
4846 {
4847 return false;
4848 }
4849
4850 struct stat sb;
4851 if (fstat(fd_, &sb) == -1)
4852 {
4853 close();
4854 return false;
4855 }
4856 size_ = static_cast<size_t>(sb.st_size);
4857
4858 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
4859
4860 // Special treatment for an empty file...
4861 if (addr_ == MAP_FAILED && size_ == 0)
4862 {
4863 close();
4864 is_open_empty_file = true;
4865 return false;
4866 }
4867#endif
4868
4869 return true;
4870}
4871
4872inline bool mmap::is_open() const
4873{
4874 return is_open_empty_file ? true : addr_ != nullptr;
4875}
4876
4877inline size_t mmap::size() const
4878{
4879 return size_;
4880}
4881
4882inline const char *mmap::data() const
4883{
4884 return is_open_empty_file ? "" : static_cast<const char *>(addr_);
4885}
4886
4887inline void mmap::close()
4888{
4889#if defined(_WIN32)
4890 if (addr_)
4891 {
4892 ::UnmapViewOfFile(addr_);
4893 addr_ = nullptr;
4894 }
4895
4896 if (hMapping_)
4897 {
4898 ::CloseHandle(hMapping_);
4899 hMapping_ = NULL;
4900 }
4901
4902 if (hFile_ != INVALID_HANDLE_VALUE)
4903 {
4904 ::CloseHandle(hFile_);
4905 hFile_ = INVALID_HANDLE_VALUE;
4906 }
4907
4908 is_open_empty_file = false;
4909#else
4910 if (addr_ != nullptr)
4911 {
4912 munmap(addr_, size_);
4913 addr_ = nullptr;
4914 }
4915
4916 if (fd_ != -1)
4917 {
4918 ::close(fd_);
4919 fd_ = -1;
4920 }
4921#endif
4922 size_ = 0;
4923}
4924inline int close_socket(socket_t sock)
4925{
4926#ifdef _WIN32
4927 return closesocket(sock);
4928#else
4929 return close(sock);
4930#endif
4931}
4932
4933template <typename T> inline ssize_t handle_EINTR(T fn)
4934{
4935 ssize_t res = 0;
4936 while (true)
4937 {
4938 res = fn();
4939 if (res < 0 && errno == EINTR)
4940 {
4941 std::this_thread::sleep_for(std::chrono::microseconds{1});
4942 continue;
4943 }
4944 break;
4945 }
4946 return res;
4947}
4948
4949inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags)
4950{
4951 return handle_EINTR([&]()
4952 {
4953 return recv(sock,
4954#ifdef _WIN32
4955 static_cast<char *>(ptr), static_cast<int>(size),
4956#else
4957 ptr, size,
4958#endif
4959 flags);
4960 });
4961}
4962
4963inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
4964 int flags)
4965{
4966 return handle_EINTR([&]()
4967 {
4968 return send(sock,
4969#ifdef _WIN32
4970 static_cast<const char *>(ptr), static_cast<int>(size),
4971#else
4972 ptr, size,
4973#endif
4974 flags);
4975 });
4976}
4977
4978inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout)
4979{
4980#ifdef _WIN32
4981 return ::WSAPoll(fds, nfds, timeout);
4982#else
4983 return ::poll(fds, nfds, timeout);
4984#endif
4985}
4986
4987template <bool Read>
4988inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec)
4989{
4990#ifdef __APPLE__
4991 if (sock >= FD_SETSIZE)
4992 {
4993 return -1;
4994 }
4995
4996 fd_set fds, *rfds, *wfds;
4997 FD_ZERO(&fds);
4998 FD_SET(sock, &fds);
4999 rfds = (Read ? &fds : nullptr);
5000 wfds = (Read ? nullptr : &fds);
5001
5002 timeval tv;
5003 tv.tv_sec = static_cast<long>(sec);
5004 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
5005
5006 return handle_EINTR([&]()
5007 {
5008 return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
5009 });
5010#else
5011 struct pollfd pfd;
5012 pfd.fd = sock;
5013 pfd.events = (Read ? POLLIN : POLLOUT);
5014
5015 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
5016
5017 return handle_EINTR([&]()
5018 {
5019 return poll_wrapper(&pfd, 1, timeout);
5020 });
5021#endif
5022}
5023
5024inline ssize_t select_read(socket_t sock, time_t sec, time_t usec)
5025{
5026 return select_impl<true>(sock, sec, usec);
5027}
5028
5029inline ssize_t select_write(socket_t sock, time_t sec, time_t usec)
5030{
5031 return select_impl<false>(sock, sec, usec);
5032}
5033
5034inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
5035 time_t usec)
5036{
5037#ifdef __APPLE__
5038 if (sock >= FD_SETSIZE)
5039 {
5040 return Error::Connection;
5041 }
5042
5043 fd_set fdsr, fdsw;
5044 FD_ZERO(&fdsr);
5045 FD_ZERO(&fdsw);
5046 FD_SET(sock, &fdsr);
5047 FD_SET(sock, &fdsw);
5048
5049 timeval tv;
5050 tv.tv_sec = static_cast<long>(sec);
5051 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
5052
5053 auto ret = handle_EINTR([&]()
5054 {
5055 return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
5056 });
5057
5058 if (ret == 0)
5059 {
5061 }
5062
5063 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)))
5064 {
5065 auto error = 0;
5066 socklen_t len = sizeof(error);
5067 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5068 reinterpret_cast<char *>(&error), &len);
5069 auto successful = res >= 0 && !error;
5070 return successful ? Error::Success : Error::Connection;
5071 }
5072
5073 return Error::Connection;
5074#else
5075 struct pollfd pfd_read;
5076 pfd_read.fd = sock;
5077 pfd_read.events = POLLIN | POLLOUT;
5078
5079 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
5080
5081 auto poll_res =
5082 handle_EINTR([&]()
5083 {
5084 return poll_wrapper(&pfd_read, 1, timeout);
5085 });
5086
5087 if (poll_res == 0)
5088 {
5090 }
5091
5092 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT))
5093 {
5094 auto error = 0;
5095 socklen_t len = sizeof(error);
5096 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5097 reinterpret_cast<char *>(&error), &len);
5098 auto successful = res >= 0 && !error;
5099 return successful ? Error::Success : Error::Connection;
5100 }
5101
5102 return Error::Connection;
5103#endif
5104}
5105
5106inline bool is_socket_alive(socket_t sock)
5107{
5108 const auto val = detail::select_read(sock, 0, 0);
5109 if (val == 0)
5110 {
5111 return true;
5112 }
5113 else if (val < 0 && errno == EBADF)
5114 {
5115 return false;
5116 }
5117 char buf[1];
5118 return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
5119}
5120
5121class SocketStream final : public Stream
5122{
5123 public:
5124 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5125 time_t write_timeout_sec, time_t write_timeout_usec,
5126 time_t max_timeout_msec = 0,
5127 std::chrono::time_point<std::chrono::steady_clock> start_time =
5128 (std::chrono::steady_clock::time_point::min)());
5129 ~SocketStream() override;
5130
5131 bool is_readable() const override;
5132 bool wait_readable() const override;
5133 bool wait_writable() const override;
5134 ssize_t read(char *ptr, size_t size) override;
5135 ssize_t write(const char *ptr, size_t size) override;
5136 void get_remote_ip_and_port(std::string &ip, int &port) const override;
5137 void get_local_ip_and_port(std::string &ip, int &port) const override;
5138 socket_t socket() const override;
5139 time_t duration() const override;
5140
5141 private:
5142 socket_t sock_;
5143 time_t read_timeout_sec_;
5144 time_t read_timeout_usec_;
5145 time_t write_timeout_sec_;
5146 time_t write_timeout_usec_;
5147 time_t max_timeout_msec_;
5148 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5149
5150 std::vector<char> read_buff_;
5151 size_t read_buff_off_ = 0;
5152 size_t read_buff_content_size_ = 0;
5153
5154 static const size_t read_buff_size_ = 1024l * 4;
5155};
5156
5157#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5158class SSLSocketStream final : public Stream
5159{
5160 public:
5161 SSLSocketStream(
5162 socket_t sock, SSL *ssl, time_t read_timeout_sec,
5163 time_t read_timeout_usec, time_t write_timeout_sec,
5164 time_t write_timeout_usec, time_t max_timeout_msec = 0,
5165 std::chrono::time_point<std::chrono::steady_clock> start_time =
5166 (std::chrono::steady_clock::time_point::min)());
5167 ~SSLSocketStream() override;
5168
5169 bool is_readable() const override;
5170 bool wait_readable() const override;
5171 bool wait_writable() const override;
5172 ssize_t read(char *ptr, size_t size) override;
5173 ssize_t write(const char *ptr, size_t size) override;
5174 void get_remote_ip_and_port(std::string &ip, int &port) const override;
5175 void get_local_ip_and_port(std::string &ip, int &port) const override;
5176 socket_t socket() const override;
5177 time_t duration() const override;
5178
5179 private:
5180 socket_t sock_;
5181 SSL *ssl_;
5182 time_t read_timeout_sec_;
5183 time_t read_timeout_usec_;
5184 time_t write_timeout_sec_;
5185 time_t write_timeout_usec_;
5186 time_t max_timeout_msec_;
5187 const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5188};
5189#endif
5190
5191inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
5192 time_t keep_alive_timeout_sec)
5193{
5194 using namespace std::chrono;
5195
5196 const auto interval_usec =
5198
5199 // Avoid expensive `steady_clock::now()` call for the first time
5200 if (select_read(sock, 0, interval_usec) > 0)
5201 {
5202 return true;
5203 }
5204
5205 const auto start = steady_clock::now() - microseconds{interval_usec};
5206 const auto timeout = seconds{keep_alive_timeout_sec};
5207
5208 while (true)
5209 {
5210 if (svr_sock == INVALID_SOCKET)
5211 {
5212 break; // Server socket is closed
5213 }
5214
5215 auto val = select_read(sock, 0, interval_usec);
5216 if (val < 0)
5217 {
5218 break; // Ssocket error
5219 }
5220 else if (val == 0)
5221 {
5222 if (steady_clock::now() - start > timeout)
5223 {
5224 break; // Timeout
5225 }
5226 }
5227 else
5228 {
5229 return true; // Ready for read
5230 }
5231 }
5232
5233 return false;
5234}
5235
5236template <typename T>
5237inline bool
5238process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
5239 size_t keep_alive_max_count,
5240 time_t keep_alive_timeout_sec, T callback)
5241{
5242 assert(keep_alive_max_count > 0);
5243 auto ret = false;
5244 auto count = keep_alive_max_count;
5245 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec))
5246 {
5247 auto close_connection = count == 1;
5248 auto connection_closed = false;
5249 ret = callback(close_connection, connection_closed);
5250 if (!ret || connection_closed)
5251 {
5252 break;
5253 }
5254 count--;
5255 }
5256 return ret;
5257}
5258
5259template <typename T>
5260inline bool
5261process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
5262 size_t keep_alive_max_count,
5263 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
5264 time_t read_timeout_usec, time_t write_timeout_sec,
5265 time_t write_timeout_usec, T callback)
5266{
5267 return process_server_socket_core(
5268 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
5269 [&](bool close_connection, bool & connection_closed)
5270 {
5271 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5272 write_timeout_sec, write_timeout_usec);
5273 return callback(strm, close_connection, connection_closed);
5274 });
5275}
5276
5277inline bool process_client_socket(
5278 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5279 time_t write_timeout_sec, time_t write_timeout_usec,
5280 time_t max_timeout_msec,
5281 std::chrono::time_point<std::chrono::steady_clock> start_time,
5282 std::function<bool(Stream &)> callback)
5283{
5284 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5285 write_timeout_sec, write_timeout_usec, max_timeout_msec,
5286 start_time);
5287 return callback(strm);
5288}
5289
5290inline int shutdown_socket(socket_t sock)
5291{
5292#ifdef _WIN32
5293 return shutdown(sock, SD_BOTH);
5294#else
5295 return shutdown(sock, SHUT_RDWR);
5296#endif
5297}
5298
5299inline std::string escape_abstract_namespace_unix_domain(const std::string &s)
5300{
5301 if (s.size() > 1 && s[0] == '\0')
5302 {
5303 auto ret = s;
5304 ret[0] = '@';
5305 return ret;
5306 }
5307 return s;
5308}
5309
5310inline std::string
5311unescape_abstract_namespace_unix_domain(const std::string &s)
5312{
5313 if (s.size() > 1 && s[0] == '@')
5314 {
5315 auto ret = s;
5316 ret[0] = '\0';
5317 return ret;
5318 }
5319 return s;
5320}
5321
5322inline int getaddrinfo_with_timeout(const char *node, const char *service,
5323 const struct addrinfo *hints,
5324 struct addrinfo **res, time_t timeout_sec)
5325{
5326#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
5327 if (timeout_sec <= 0)
5328 {
5329 // No timeout specified, use standard getaddrinfo
5330 return getaddrinfo(node, service, hints, res);
5331 }
5332
5333#ifdef _WIN32
5334 // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
5335 OVERLAPPED overlapped = {0};
5336 HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
5337 if (!event)
5338 {
5339 return EAI_FAIL;
5340 }
5341
5342 overlapped.hEvent = event;
5343
5344 PADDRINFOEXW result_addrinfo = nullptr;
5345 HANDLE cancel_handle = nullptr;
5346
5347 ADDRINFOEXW hints_ex = {0};
5348 if (hints)
5349 {
5350 hints_ex.ai_flags = hints->ai_flags;
5351 hints_ex.ai_family = hints->ai_family;
5352 hints_ex.ai_socktype = hints->ai_socktype;
5353 hints_ex.ai_protocol = hints->ai_protocol;
5354 }
5355
5356 auto wnode = u8string_to_wstring(node);
5357 auto wservice = u8string_to_wstring(service);
5358
5359 auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
5360 hints ? &hints_ex : nullptr, &result_addrinfo,
5361 nullptr, &overlapped, nullptr, &cancel_handle);
5362
5363 if (ret == WSA_IO_PENDING)
5364 {
5365 auto wait_result =
5366 ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
5367 if (wait_result == WAIT_TIMEOUT)
5368 {
5369 if (cancel_handle)
5370 {
5371 ::GetAddrInfoExCancel(&cancel_handle);
5372 }
5373 ::CloseHandle(event);
5374 return EAI_AGAIN;
5375 }
5376
5377 DWORD bytes_returned;
5378 if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
5379 &bytes_returned, FALSE))
5380 {
5381 ::CloseHandle(event);
5382 return ::WSAGetLastError();
5383 }
5384 }
5385
5386 ::CloseHandle(event);
5387
5388 if (ret == NO_ERROR || ret == WSA_IO_PENDING)
5389 {
5390 *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
5391 return 0;
5392 }
5393
5394 return ret;
5395#elif TARGET_OS_MAC
5396 // macOS implementation using CFHost API for asynchronous DNS resolution
5397 CFStringRef hostname_ref = CFStringCreateWithCString(
5398 kCFAllocatorDefault, node, kCFStringEncodingUTF8);
5399 if (!hostname_ref)
5400 {
5401 return EAI_MEMORY;
5402 }
5403
5404 CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
5405 CFRelease(hostname_ref);
5406 if (!host_ref)
5407 {
5408 return EAI_MEMORY;
5409 }
5410
5411 // Set up context for callback
5412 struct CFHostContext
5413 {
5414 bool completed = false;
5415 bool success = false;
5416 CFArrayRef addresses = nullptr;
5417 std::mutex mutex;
5418 std::condition_variable cv;
5419 } context;
5420
5421 CFHostClientContext client_context;
5422 memset(&client_context, 0, sizeof(client_context));
5423 client_context.info = &context;
5424
5425 // Set callback
5426 auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
5427 const CFStreamError * error, void *info)
5428 {
5429 auto ctx = static_cast<CFHostContext *>(info);
5430 std::lock_guard<std::mutex> lock(ctx->mutex);
5431
5432 if (error && error->error != 0)
5433 {
5434 ctx->success = false;
5435 }
5436 else
5437 {
5438 Boolean hasBeenResolved;
5439 ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
5440 if (ctx->addresses && hasBeenResolved)
5441 {
5442 CFRetain(ctx->addresses);
5443 ctx->success = true;
5444 }
5445 else
5446 {
5447 ctx->success = false;
5448 }
5449 }
5450 ctx->completed = true;
5451 ctx->cv.notify_one();
5452 };
5453
5454 if (!CFHostSetClient(host_ref, callback, &client_context))
5455 {
5456 CFRelease(host_ref);
5457 return EAI_SYSTEM;
5458 }
5459
5460 // Schedule on run loop
5461 CFRunLoopRef run_loop = CFRunLoopGetCurrent();
5462 CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5463
5464 // Start resolution
5465 CFStreamError stream_error;
5466 if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error))
5467 {
5468 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5469 CFRelease(host_ref);
5470 return EAI_FAIL;
5471 }
5472
5473 // Wait for completion with timeout
5474 auto timeout_time =
5475 std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
5476 bool timed_out = false;
5477
5478 {
5479 std::unique_lock<std::mutex> lock(context.mutex);
5480
5481 while (!context.completed)
5482 {
5483 auto now = std::chrono::steady_clock::now();
5484 if (now >= timeout_time)
5485 {
5486 timed_out = true;
5487 break;
5488 }
5489
5490 // Run the runloop for a short time
5491 lock.unlock();
5492 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
5493 lock.lock();
5494 }
5495 }
5496
5497 // Clean up
5498 CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5499 CFHostSetClient(host_ref, nullptr, nullptr);
5500
5501 if (timed_out || !context.completed)
5502 {
5503 CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
5504 CFRelease(host_ref);
5505 return EAI_AGAIN;
5506 }
5507
5508 if (!context.success || !context.addresses)
5509 {
5510 CFRelease(host_ref);
5511 return EAI_NODATA;
5512 }
5513
5514 // Convert CFArray to addrinfo
5515 CFIndex count = CFArrayGetCount(context.addresses);
5516 if (count == 0)
5517 {
5518 CFRelease(context.addresses);
5519 CFRelease(host_ref);
5520 return EAI_NODATA;
5521 }
5522
5523 struct addrinfo *result_addrinfo = nullptr;
5524 struct addrinfo **current = &result_addrinfo;
5525
5526 for (CFIndex i = 0; i < count; i++)
5527 {
5528 CFDataRef addr_data =
5529 static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
5530 if (!addr_data) continue;
5531
5532 const struct sockaddr *sockaddr_ptr =
5533 reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
5534 socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
5535
5536 // Allocate addrinfo structure
5537 *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
5538 if (!*current)
5539 {
5540 freeaddrinfo(result_addrinfo);
5541 CFRelease(context.addresses);
5542 CFRelease(host_ref);
5543 return EAI_MEMORY;
5544 }
5545
5546 memset(*current, 0, sizeof(struct addrinfo));
5547
5548 // Set up addrinfo fields
5549 (*current)->ai_family = sockaddr_ptr->sa_family;
5550 (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
5551 (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
5552 (*current)->ai_addrlen = sockaddr_len;
5553
5554 // Copy sockaddr
5555 (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
5556 if (!(*current)->ai_addr)
5557 {
5558 freeaddrinfo(result_addrinfo);
5559 CFRelease(context.addresses);
5560 CFRelease(host_ref);
5561 return EAI_MEMORY;
5562 }
5563 memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
5564
5565 // Set port if service is specified
5566 if (service && strlen(service) > 0)
5567 {
5568 int port = atoi(service);
5569 if (port > 0)
5570 {
5571 if (sockaddr_ptr->sa_family == AF_INET)
5572 {
5573 reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
5574 ->sin_port = htons(static_cast<uint16_t>(port));
5575 }
5576 else if (sockaddr_ptr->sa_family == AF_INET6)
5577 {
5578 reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
5579 ->sin6_port = htons(static_cast<uint16_t>(port));
5580 }
5581 }
5582 }
5583
5584 current = &((*current)->ai_next);
5585 }
5586
5587 CFRelease(context.addresses);
5588 CFRelease(host_ref);
5589
5590 *res = result_addrinfo;
5591 return 0;
5592#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
5593 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
5594 // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
5595 struct gaicb request;
5596 struct gaicb *requests[1] = {&request};
5597 struct sigevent sevp;
5598 struct timespec timeout;
5599
5600 // Initialize the request structure
5601 memset(&request, 0, sizeof(request));
5602 request.ar_name = node;
5603 request.ar_service = service;
5604 request.ar_request = hints;
5605
5606 // Set up timeout
5607 timeout.tv_sec = timeout_sec;
5608 timeout.tv_nsec = 0;
5609
5610 // Initialize sigevent structure (not used, but required)
5611 memset(&sevp, 0, sizeof(sevp));
5612 sevp.sigev_notify = SIGEV_NONE;
5613
5614 // Start asynchronous resolution
5615 int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
5616 if (start_result != 0)
5617 {
5618 return start_result;
5619 }
5620
5621 // Wait for completion with timeout
5622 int wait_result =
5623 gai_suspend((const struct gaicb * const *)requests, 1, &timeout);
5624
5625 if (wait_result == 0 || wait_result == EAI_ALLDONE)
5626 {
5627 // Completed successfully, get the result
5628 int gai_result = gai_error(&request);
5629 if (gai_result == 0)
5630 {
5631 *res = request.ar_result;
5632 return 0;
5633 }
5634 else
5635 {
5636 // Clean up on error
5637 if (request.ar_result)
5638 {
5639 freeaddrinfo(request.ar_result);
5640 }
5641 return gai_result;
5642 }
5643 }
5644 else if (wait_result == EAI_AGAIN)
5645 {
5646 // Timeout occurred, cancel the request
5647 gai_cancel(&request);
5648 return EAI_AGAIN;
5649 }
5650 else
5651 {
5652 // Other error occurred
5653 gai_cancel(&request);
5654 return wait_result;
5655 }
5656#else
5657 // Fallback implementation using thread-based timeout for other Unix systems
5658
5659 struct GetAddrInfoState
5660 {
5661 ~GetAddrInfoState()
5662 {
5663 if (info)
5664 {
5665 freeaddrinfo(info);
5666 }
5667 }
5668
5669 std::mutex mutex;
5670 std::condition_variable result_cv;
5671 bool completed = false;
5672 int result = EAI_SYSTEM;
5673 std::string node;
5674 std::string service;
5675 struct addrinfo hints;
5676 struct addrinfo *info = nullptr;
5677 };
5678
5679 // Allocate on the heap, so the resolver thread can keep using the data.
5680 auto state = std::make_shared<GetAddrInfoState>();
5681 state->node = node;
5682 state->service = service;
5683 state->hints = *hints;
5684
5685 std::thread resolve_thread([state]()
5686 {
5687 auto thread_result =
5688 getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
5689 &state->info);
5690
5691 std::lock_guard<std::mutex> lock(state->mutex);
5692 state->result = thread_result;
5693 state->completed = true;
5694 state->result_cv.notify_one();
5695 });
5696
5697 // Wait for completion or timeout
5698 std::unique_lock<std::mutex> lock(state->mutex);
5699 auto finished =
5700 state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
5701 [&] { return state->completed; });
5702
5703 if (finished)
5704 {
5705 // Operation completed within timeout
5706 resolve_thread.join();
5707 *res = state->info;
5708 state->info = nullptr; // Pass ownership to caller
5709 return state->result;
5710 }
5711 else
5712 {
5713 // Timeout occurred
5714 resolve_thread.detach(); // Let the thread finish in background
5715 return EAI_AGAIN; // Return timeout error
5716 }
5717#endif
5718#else
5719 (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
5720 return getaddrinfo(node, service, hints, res);
5721#endif
5722}
5723
5724template <typename BindOrConnect>
5725socket_t create_socket(const std::string &host, const std::string &ip, int port,
5726 int address_family, int socket_flags, bool tcp_nodelay,
5727 bool ipv6_v6only, SocketOptions socket_options,
5728 BindOrConnect bind_or_connect, time_t timeout_sec = 0)
5729{
5730 // Get address info
5731 const char *node = nullptr;
5732 struct addrinfo hints;
5733 struct addrinfo *result;
5734
5735 memset(&hints, 0, sizeof(struct addrinfo));
5736 hints.ai_socktype = SOCK_STREAM;
5737 hints.ai_protocol = IPPROTO_IP;
5738
5739 if (!ip.empty())
5740 {
5741 node = ip.c_str();
5742 // Ask getaddrinfo to convert IP in c-string to address
5743 hints.ai_family = AF_UNSPEC;
5744 hints.ai_flags = AI_NUMERICHOST;
5745 }
5746 else
5747 {
5748 if (!host.empty())
5749 {
5750 node = host.c_str();
5751 }
5752 hints.ai_family = address_family;
5753 hints.ai_flags = socket_flags;
5754 }
5755
5756#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
5757 if (hints.ai_family == AF_UNIX)
5758 {
5759 const auto addrlen = host.length();
5760 if (addrlen > sizeof(sockaddr_un::sun_path))
5761 {
5762 return INVALID_SOCKET;
5763 }
5764
5765#ifdef SOCK_CLOEXEC
5766 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
5767 hints.ai_protocol);
5768#else
5769 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
5770#endif
5771
5772 if (sock != INVALID_SOCKET)
5773 {
5774 sockaddr_un addr{};
5775 addr.sun_family = AF_UNIX;
5776
5777 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
5778 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
5779
5780 hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
5781 hints.ai_addrlen = static_cast<socklen_t>(
5782 sizeof(addr) - sizeof(addr.sun_path) + addrlen);
5783
5784#ifndef SOCK_CLOEXEC
5785#ifndef _WIN32
5786 fcntl(sock, F_SETFD, FD_CLOEXEC);
5787#endif
5788#endif
5789
5790 if (socket_options)
5791 {
5792 socket_options(sock);
5793 }
5794
5795#ifdef _WIN32
5796 // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
5797 // remove the option.
5798 detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
5799#endif
5800
5801 bool dummy;
5802 if (!bind_or_connect(sock, hints, dummy))
5803 {
5804 close_socket(sock);
5805 sock = INVALID_SOCKET;
5806 }
5807 }
5808 return sock;
5809 }
5810#endif
5811
5812 auto service = std::to_string(port);
5813
5814 if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
5815 timeout_sec))
5816 {
5817#if defined __linux__ && !defined __ANDROID__
5818 res_init();
5819#endif
5820 return INVALID_SOCKET;
5821 }
5822 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5823
5824 for (auto rp = result; rp; rp = rp->ai_next)
5825 {
5826 // Create a socket
5827#ifdef _WIN32
5828 auto sock =
5829 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
5830 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
5845 if (sock == INVALID_SOCKET)
5846 {
5847 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
5848 }
5849#else
5850
5851#ifdef SOCK_CLOEXEC
5852 auto sock =
5853 socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
5854#else
5855 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
5856#endif
5857
5858#endif
5859 if (sock == INVALID_SOCKET)
5860 {
5861 continue;
5862 }
5863
5864#if !defined _WIN32 && !defined SOCK_CLOEXEC
5865 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1)
5866 {
5867 close_socket(sock);
5868 continue;
5869 }
5870#endif
5871
5872 if (tcp_nodelay)
5873 {
5874 set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
5875 }
5876
5877 if (rp->ai_family == AF_INET6)
5878 {
5879 set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
5880 }
5881
5882 if (socket_options)
5883 {
5884 socket_options(sock);
5885 }
5886
5887 // bind or connect
5888 auto quit = false;
5889 if (bind_or_connect(sock, *rp, quit))
5890 {
5891 return sock;
5892 }
5893
5894 close_socket(sock);
5895
5896 if (quit)
5897 {
5898 break;
5899 }
5900 }
5901
5902 return INVALID_SOCKET;
5903}
5904
5905inline void set_nonblocking(socket_t sock, bool nonblocking)
5906{
5907#ifdef _WIN32
5908 auto flags = nonblocking ? 1UL : 0UL;
5909 ioctlsocket(sock, FIONBIO, &flags);
5910#else
5911 auto flags = fcntl(sock, F_GETFL, 0);
5912 fcntl(sock, F_SETFL,
5913 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
5914#endif
5915}
5916
5917inline bool is_connection_error()
5918{
5919#ifdef _WIN32
5920 return WSAGetLastError() != WSAEWOULDBLOCK;
5921#else
5922 return errno != EINPROGRESS;
5923#endif
5924}
5925
5926inline bool bind_ip_address(socket_t sock, const std::string &host)
5927{
5928 struct addrinfo hints;
5929 struct addrinfo *result;
5930
5931 memset(&hints, 0, sizeof(struct addrinfo));
5932 hints.ai_family = AF_UNSPEC;
5933 hints.ai_socktype = SOCK_STREAM;
5934 hints.ai_protocol = 0;
5935
5936 if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0))
5937 {
5938 return false;
5939 }
5940
5941 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5942
5943 auto ret = false;
5944 for (auto rp = result; rp; rp = rp->ai_next)
5945 {
5946 const auto &ai = *rp;
5947 if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen)))
5948 {
5949 ret = true;
5950 break;
5951 }
5952 }
5953
5954 return ret;
5955}
5956
5957#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
5958#define USE_IF2IP
5959#endif
5960
5961#ifdef USE_IF2IP
5962inline std::string if2ip(int address_family, const std::string &ifn)
5963{
5964 struct ifaddrs *ifap;
5965 getifaddrs(&ifap);
5966 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
5967
5968 std::string addr_candidate;
5969 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next)
5970 {
5971 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
5972 (AF_UNSPEC == address_family ||
5973 ifa->ifa_addr->sa_family == address_family))
5974 {
5975 if (ifa->ifa_addr->sa_family == AF_INET)
5976 {
5977 auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
5978 char buf[INET_ADDRSTRLEN];
5979 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN))
5980 {
5981 return std::string(buf, INET_ADDRSTRLEN);
5982 }
5983 }
5984 else if (ifa->ifa_addr->sa_family == AF_INET6)
5985 {
5986 auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
5987 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr))
5988 {
5989 char buf[INET6_ADDRSTRLEN] = {};
5990 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN))
5991 {
5992 // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
5993 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
5994 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd)
5995 {
5996 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
5997 }
5998 else
5999 {
6000 return std::string(buf, INET6_ADDRSTRLEN);
6001 }
6002 }
6003 }
6004 }
6005 }
6006 }
6007 return addr_candidate;
6008}
6009#endif
6010
6011inline socket_t create_client_socket(
6012 const std::string &host, const std::string &ip, int port,
6013 int address_family, bool tcp_nodelay, bool ipv6_v6only,
6014 SocketOptions socket_options, time_t connection_timeout_sec,
6015 time_t connection_timeout_usec, time_t read_timeout_sec,
6016 time_t read_timeout_usec, time_t write_timeout_sec,
6017 time_t write_timeout_usec, const std::string &intf, Error &error)
6018{
6019 auto sock = create_socket(
6020 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
6021 std::move(socket_options),
6022 [&](socket_t sock2, struct addrinfo & ai, bool & quit) -> bool
6023 {
6024 if (!intf.empty())
6025 {
6026#ifdef USE_IF2IP
6027 auto ip_from_if = if2ip(address_family, intf);
6028 if (ip_from_if.empty())
6029 {
6030 ip_from_if = intf;
6031 }
6032 if (!bind_ip_address(sock2, ip_from_if))
6033 {
6035 return false;
6036 }
6037#endif
6038 }
6039
6040 set_nonblocking(sock2, true);
6041
6042 auto ret =
6043 ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
6044
6045 if (ret < 0)
6046 {
6047 if (is_connection_error())
6048 {
6050 return false;
6051 }
6052 error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
6053 connection_timeout_usec);
6054 if (error != Error::Success)
6055 {
6056 if (error == Error::ConnectionTimeout)
6057 {
6058 quit = true;
6059 }
6060 return false;
6061 }
6062 }
6063
6064 set_nonblocking(sock2, false);
6065 set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
6066 read_timeout_usec);
6067 set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
6068 write_timeout_usec);
6069
6071 return true;
6072 },
6073 connection_timeout_sec); // Pass DNS timeout
6074
6075 if (sock != INVALID_SOCKET)
6076 {
6078 }
6079 else
6080 {
6081 if (error == Error::Success)
6082 {
6084 }
6085 }
6086
6087 return sock;
6088}
6089
6090inline bool get_ip_and_port(const struct sockaddr_storage &addr,
6091 socklen_t addr_len, std::string &ip, int &port)
6092{
6093 if (addr.ss_family == AF_INET)
6094 {
6095 port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
6096 }
6097 else if (addr.ss_family == AF_INET6)
6098 {
6099 port =
6100 ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
6101 }
6102 else
6103 {
6104 return false;
6105 }
6106
6107 std::array<char, NI_MAXHOST> ipstr{};
6108 if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
6109 ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
6110 0, NI_NUMERICHOST))
6111 {
6112 return false;
6113 }
6114
6115 ip = ipstr.data();
6116 return true;
6117}
6118
6119inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port)
6120{
6121 struct sockaddr_storage addr;
6122 socklen_t addr_len = sizeof(addr);
6123 if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
6124 &addr_len))
6125 {
6126 get_ip_and_port(addr, addr_len, ip, port);
6127 }
6128}
6129
6130inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port)
6131{
6132 struct sockaddr_storage addr;
6133 socklen_t addr_len = sizeof(addr);
6134
6135 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
6136 &addr_len))
6137 {
6138#ifndef _WIN32
6139 if (addr.ss_family == AF_UNIX)
6140 {
6141#if defined(__linux__)
6142 struct ucred ucred;
6143 socklen_t len = sizeof(ucred);
6144 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0)
6145 {
6146 port = ucred.pid;
6147 }
6148#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
6149 pid_t pid;
6150 socklen_t len = sizeof(pid);
6151 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0)
6152 {
6153 port = pid;
6154 }
6155#endif
6156 return;
6157 }
6158#endif
6159 get_ip_and_port(addr, addr_len, ip, port);
6160 }
6161}
6162
6163inline constexpr unsigned int str2tag_core(const char *s, size_t l,
6164 unsigned int h)
6165{
6166 return (l == 0)
6167 ? h
6168 : str2tag_core(
6169 s + 1, l - 1,
6170 // Unsets the 6 high bits of h, therefore no overflow happens
6171 (((std::numeric_limits<unsigned int>::max)() >> 6) &
6172 h * 33) ^
6173 static_cast<unsigned char>(*s));
6174}
6175
6176inline unsigned int str2tag(const std::string &s)
6177{
6178 return str2tag_core(s.data(), s.size(), 0);
6179}
6180
6181namespace udl
6182{
6183
6184inline constexpr unsigned int operator""_t(const char *s, size_t l)
6185{
6186 return str2tag_core(s, l, 0);
6187}
6188
6189} // namespace udl
6190
6191inline std::string
6192find_content_type(const std::string &path,
6193 const std::map<std::string, std::string> &user_data,
6194 const std::string &default_content_type)
6195{
6196 auto ext = file_extension(path);
6197
6198 auto it = user_data.find(ext);
6199 if (it != user_data.end())
6200 {
6201 return it->second;
6202 }
6203
6204 using udl::operator""_t;
6205
6206 switch (str2tag(ext))
6207 {
6208 default:
6209 return default_content_type;
6210
6211 case "css"_t:
6212 return "text/css";
6213 case "csv"_t:
6214 return "text/csv";
6215 case "htm"_t:
6216 case "html"_t:
6217 return "text/html";
6218 case "js"_t:
6219 case "mjs"_t:
6220 return "text/javascript";
6221 case "txt"_t:
6222 return "text/plain";
6223 case "vtt"_t:
6224 return "text/vtt";
6225
6226 case "apng"_t:
6227 return "image/apng";
6228 case "avif"_t:
6229 return "image/avif";
6230 case "bmp"_t:
6231 return "image/bmp";
6232 case "gif"_t:
6233 return "image/gif";
6234 case "png"_t:
6235 return "image/png";
6236 case "svg"_t:
6237 return "image/svg+xml";
6238 case "webp"_t:
6239 return "image/webp";
6240 case "ico"_t:
6241 return "image/x-icon";
6242 case "tif"_t:
6243 return "image/tiff";
6244 case "tiff"_t:
6245 return "image/tiff";
6246 case "jpg"_t:
6247 case "jpeg"_t:
6248 return "image/jpeg";
6249
6250 case "mp4"_t:
6251 return "video/mp4";
6252 case "mpeg"_t:
6253 return "video/mpeg";
6254 case "webm"_t:
6255 return "video/webm";
6256
6257 case "mp3"_t:
6258 return "audio/mp3";
6259 case "mpga"_t:
6260 return "audio/mpeg";
6261 case "weba"_t:
6262 return "audio/webm";
6263 case "wav"_t:
6264 return "audio/wave";
6265
6266 case "otf"_t:
6267 return "font/otf";
6268 case "ttf"_t:
6269 return "font/ttf";
6270 case "woff"_t:
6271 return "font/woff";
6272 case "woff2"_t:
6273 return "font/woff2";
6274
6275 case "7z"_t:
6276 return "application/x-7z-compressed";
6277 case "atom"_t:
6278 return "application/atom+xml";
6279 case "pdf"_t:
6280 return "application/pdf";
6281 case "json"_t:
6282 return "application/json";
6283 case "rss"_t:
6284 return "application/rss+xml";
6285 case "tar"_t:
6286 return "application/x-tar";
6287 case "xht"_t:
6288 case "xhtml"_t:
6289 return "application/xhtml+xml";
6290 case "xslt"_t:
6291 return "application/xslt+xml";
6292 case "xml"_t:
6293 return "application/xml";
6294 case "gz"_t:
6295 return "application/gzip";
6296 case "zip"_t:
6297 return "application/zip";
6298 case "wasm"_t:
6299 return "application/wasm";
6300 }
6301}
6302
6303inline bool can_compress_content_type(const std::string &content_type)
6304{
6305 using udl::operator""_t;
6306
6307 auto tag = str2tag(content_type);
6308
6309 switch (tag)
6310 {
6311 case "image/svg+xml"_t:
6312 case "application/javascript"_t:
6313 case "application/json"_t:
6314 case "application/xml"_t:
6315 case "application/protobuf"_t:
6316 case "application/xhtml+xml"_t:
6317 return true;
6318
6319 case "text/event-stream"_t:
6320 return false;
6321
6322 default:
6323 return !content_type.rfind("text/", 0);
6324 }
6325}
6326
6327inline EncodingType encoding_type(const Request &req, const Response &res)
6328{
6329 auto ret =
6330 detail::can_compress_content_type(res.get_header_value("Content-Type"));
6331 if (!ret)
6332 {
6333 return EncodingType::None;
6334 }
6335
6336 const auto &s = req.get_header_value("Accept-Encoding");
6337 (void)(s);
6338
6339#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6340 // TODO: 'Accept-Encoding' has br, not br;q=0
6341 ret = s.find("br") != std::string::npos;
6342 if (ret)
6343 {
6344 return EncodingType::Brotli;
6345 }
6346#endif
6347
6348#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6349 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
6350 ret = s.find("gzip") != std::string::npos;
6351 if (ret)
6352 {
6353 return EncodingType::Gzip;
6354 }
6355#endif
6356
6357#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6358 // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
6359 ret = s.find("zstd") != std::string::npos;
6360 if (ret)
6361 {
6362 return EncodingType::Zstd;
6363 }
6364#endif
6365
6366 return EncodingType::None;
6367}
6368
6369inline bool nocompressor::compress(const char *data, size_t data_length,
6370 bool /*last*/, Callback callback)
6371{
6372 if (!data_length)
6373 {
6374 return true;
6375 }
6376 return callback(data, data_length);
6377}
6378
6379#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6380inline gzip_compressor::gzip_compressor()
6381{
6382 std::memset(&strm_, 0, sizeof(strm_));
6383 strm_.zalloc = Z_NULL;
6384 strm_.zfree = Z_NULL;
6385 strm_.opaque = Z_NULL;
6386
6387 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
6388 Z_DEFAULT_STRATEGY) == Z_OK;
6389}
6390
6391inline gzip_compressor::~gzip_compressor()
6392{
6393 deflateEnd(&strm_);
6394}
6395
6396inline bool gzip_compressor::compress(const char *data, size_t data_length,
6397 bool last, Callback callback)
6398{
6399 assert(is_valid_);
6400
6401 do
6402 {
6403 constexpr size_t max_avail_in =
6404 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
6405
6406 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
6407 (std::min)(data_length, max_avail_in));
6408 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
6409
6410 data_length -= strm_.avail_in;
6411 data += strm_.avail_in;
6412
6413 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
6414 auto ret = Z_OK;
6415
6416 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6417 do
6418 {
6419 strm_.avail_out = static_cast<uInt>(buff.size());
6420 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
6421
6422 ret = deflate(&strm_, flush);
6423 if (ret == Z_STREAM_ERROR)
6424 {
6425 return false;
6426 }
6427
6428 if (!callback(buff.data(), buff.size() - strm_.avail_out))
6429 {
6430 return false;
6431 }
6432 }
6433 while (strm_.avail_out == 0);
6434
6435 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
6436 (flush == Z_NO_FLUSH && ret == Z_OK));
6437 assert(strm_.avail_in == 0);
6438 }
6439 while (data_length > 0);
6440
6441 return true;
6442}
6443
6444inline gzip_decompressor::gzip_decompressor()
6445{
6446 std::memset(&strm_, 0, sizeof(strm_));
6447 strm_.zalloc = Z_NULL;
6448 strm_.zfree = Z_NULL;
6449 strm_.opaque = Z_NULL;
6450
6451 // 15 is the value of wbits, which should be at the maximum possible value
6452 // to ensure that any gzip stream can be decoded. The offset of 32 specifies
6453 // that the stream type should be automatically detected either gzip or
6454 // deflate.
6455 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
6456}
6457
6458inline gzip_decompressor::~gzip_decompressor()
6459{
6460 inflateEnd(&strm_);
6461}
6462
6463inline bool gzip_decompressor::is_valid() const
6464{
6465 return is_valid_;
6466}
6467
6468inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
6469 Callback callback)
6470{
6471 assert(is_valid_);
6472
6473 auto ret = Z_OK;
6474
6475 do
6476 {
6477 constexpr size_t max_avail_in =
6478 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
6479
6480 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
6481 (std::min)(data_length, max_avail_in));
6482 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
6483
6484 data_length -= strm_.avail_in;
6485 data += strm_.avail_in;
6486
6487 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6488 while (strm_.avail_in > 0 && ret == Z_OK)
6489 {
6490 strm_.avail_out = static_cast<uInt>(buff.size());
6491 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
6492
6493 ret = inflate(&strm_, Z_NO_FLUSH);
6494
6495 assert(ret != Z_STREAM_ERROR);
6496 switch (ret)
6497 {
6498 case Z_NEED_DICT:
6499 case Z_DATA_ERROR:
6500 case Z_MEM_ERROR:
6501 inflateEnd(&strm_);
6502 return false;
6503 }
6504
6505 if (!callback(buff.data(), buff.size() - strm_.avail_out))
6506 {
6507 return false;
6508 }
6509 }
6510
6511 if (ret != Z_OK && ret != Z_STREAM_END)
6512 {
6513 return false;
6514 }
6515
6516 }
6517 while (data_length > 0);
6518
6519 return true;
6520}
6521#endif
6522
6523#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6524inline brotli_compressor::brotli_compressor()
6525{
6526 state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
6527}
6528
6529inline brotli_compressor::~brotli_compressor()
6530{
6531 BrotliEncoderDestroyInstance(state_);
6532}
6533
6534inline bool brotli_compressor::compress(const char *data, size_t data_length,
6535 bool last, Callback callback)
6536{
6537 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6538
6539 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
6540 auto available_in = data_length;
6541 auto next_in = reinterpret_cast<const uint8_t *>(data);
6542
6543 for (;;)
6544 {
6545 if (last)
6546 {
6547 if (BrotliEncoderIsFinished(state_))
6548 {
6549 break;
6550 }
6551 }
6552 else
6553 {
6554 if (!available_in)
6555 {
6556 break;
6557 }
6558 }
6559
6560 auto available_out = buff.size();
6561 auto next_out = buff.data();
6562
6563 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
6564 &available_out, &next_out, nullptr))
6565 {
6566 return false;
6567 }
6568
6569 auto output_bytes = buff.size() - available_out;
6570 if (output_bytes)
6571 {
6572 callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
6573 }
6574 }
6575
6576 return true;
6577}
6578
6579inline brotli_decompressor::brotli_decompressor()
6580{
6581 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
6582 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
6583 : BROTLI_DECODER_RESULT_ERROR;
6584}
6585
6586inline brotli_decompressor::~brotli_decompressor()
6587{
6588 if (decoder_s)
6589 {
6590 BrotliDecoderDestroyInstance(decoder_s);
6591 }
6592}
6593
6594inline bool brotli_decompressor::is_valid() const
6595{
6596 return decoder_s;
6597}
6598
6599inline bool brotli_decompressor::decompress(const char *data,
6600 size_t data_length,
6601 Callback callback)
6602{
6603 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6604 decoder_r == BROTLI_DECODER_RESULT_ERROR)
6605 {
6606 return 0;
6607 }
6608
6609 auto next_in = reinterpret_cast<const uint8_t *>(data);
6610 size_t avail_in = data_length;
6611 size_t total_out;
6612
6613 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
6614
6615 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6616 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT)
6617 {
6618 char *next_out = buff.data();
6619 size_t avail_out = buff.size();
6620
6621 decoder_r = BrotliDecoderDecompressStream(
6622 decoder_s, &avail_in, &next_in, &avail_out,
6623 reinterpret_cast<uint8_t **>(&next_out), &total_out);
6624
6625 if (decoder_r == BROTLI_DECODER_RESULT_ERROR)
6626 {
6627 return false;
6628 }
6629
6630 if (!callback(buff.data(), buff.size() - avail_out))
6631 {
6632 return false;
6633 }
6634 }
6635
6636 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6637 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
6638}
6639#endif
6640
6641#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6642inline zstd_compressor::zstd_compressor()
6643{
6644 ctx_ = ZSTD_createCCtx();
6645 ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
6646}
6647
6648inline zstd_compressor::~zstd_compressor()
6649{
6650 ZSTD_freeCCtx(ctx_);
6651}
6652
6653inline bool zstd_compressor::compress(const char *data, size_t data_length,
6654 bool last, Callback callback)
6655{
6656 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6657
6658 ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
6659 ZSTD_inBuffer input = {data, data_length, 0};
6660
6661 bool finished;
6662 do
6663 {
6664 ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
6665 size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
6666
6667 if (ZSTD_isError(remaining))
6668 {
6669 return false;
6670 }
6671
6672 if (!callback(buff.data(), output.pos))
6673 {
6674 return false;
6675 }
6676
6677 finished = last ? (remaining == 0) : (input.pos == input.size);
6678
6679 }
6680 while (!finished);
6681
6682 return true;
6683}
6684
6685inline zstd_decompressor::zstd_decompressor()
6686{
6687 ctx_ = ZSTD_createDCtx();
6688}
6689
6690inline zstd_decompressor::~zstd_decompressor()
6691{
6692 ZSTD_freeDCtx(ctx_);
6693}
6694
6695inline bool zstd_decompressor::is_valid() const
6696{
6697 return ctx_ != nullptr;
6698}
6699
6700inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
6701 Callback callback)
6702{
6703 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6704 ZSTD_inBuffer input = {data, data_length, 0};
6705
6706 while (input.pos < input.size)
6707 {
6708 ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
6709 size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
6710
6711 if (ZSTD_isError(remaining))
6712 {
6713 return false;
6714 }
6715
6716 if (!callback(buff.data(), output.pos))
6717 {
6718 return false;
6719 }
6720 }
6721
6722 return true;
6723}
6724#endif
6725
6726inline std::unique_ptr<decompressor>
6727create_decompressor(const std::string &encoding)
6728{
6729 std::unique_ptr<decompressor> decompressor;
6730
6731 if (encoding == "gzip" || encoding == "deflate")
6732 {
6733#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6734 decompressor = detail::make_unique<gzip_decompressor>();
6735#endif
6736 }
6737 else if (encoding.find("br") != std::string::npos)
6738 {
6739#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6740 decompressor = detail::make_unique<brotli_decompressor>();
6741#endif
6742 }
6743 else if (encoding == "zstd" || encoding.find("zstd") != std::string::npos)
6744 {
6745#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6746 decompressor = detail::make_unique<zstd_decompressor>();
6747#endif
6748 }
6749
6750 return decompressor;
6751}
6752
6753inline bool is_prohibited_header_name(const std::string &name)
6754{
6755 using udl::operator""_t;
6756
6757 switch (str2tag(name))
6758 {
6759 case "REMOTE_ADDR"_t:
6760 case "REMOTE_PORT"_t:
6761 case "LOCAL_ADDR"_t:
6762 case "LOCAL_PORT"_t:
6763 return true;
6764 default:
6765 return false;
6766 }
6767}
6768
6769inline bool has_header(const Headers &headers, const std::string &key)
6770{
6771 if (is_prohibited_header_name(key))
6772 {
6773 return false;
6774 }
6775 return headers.find(key) != headers.end();
6776}
6777
6778inline const char *get_header_value(const Headers &headers,
6779 const std::string &key, const char *def,
6780 size_t id)
6781{
6782 if (is_prohibited_header_name(key))
6783 {
6784#ifndef CPPHTTPLIB_NO_EXCEPTIONS
6785 std::string msg = "Prohibited header name '" + key + "' is specified.";
6786 throw std::invalid_argument(msg);
6787#else
6788 return "";
6789#endif
6790 }
6791
6792 auto rng = headers.equal_range(key);
6793 auto it = rng.first;
6794 std::advance(it, static_cast<ssize_t>(id));
6795 if (it != rng.second)
6796 {
6797 return it->second.c_str();
6798 }
6799 return def;
6800}
6801
6802inline bool read_headers(Stream &strm, Headers &headers)
6803{
6804 const auto bufsiz = 2048;
6805 char buf[bufsiz];
6806 stream_line_reader line_reader(strm, buf, bufsiz);
6807
6808 size_t header_count = 0;
6809
6810 for (;;)
6811 {
6812 if (!line_reader.getline())
6813 {
6814 return false;
6815 }
6816
6817 // Check if the line ends with CRLF.
6818 auto line_terminator_len = 2;
6819 if (line_reader.end_with_crlf())
6820 {
6821 // Blank line indicates end of headers.
6822 if (line_reader.size() == 2)
6823 {
6824 break;
6825 }
6826 }
6827 else
6828 {
6829#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
6830 // Blank line indicates end of headers.
6831 if (line_reader.size() == 1)
6832 {
6833 break;
6834 }
6835 line_terminator_len = 1;
6836#else
6837 continue; // Skip invalid line.
6838#endif
6839 }
6840
6841 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH)
6842 {
6843 return false;
6844 }
6845
6846 // Check header count limit
6847 if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT)
6848 {
6849 return false;
6850 }
6851
6852 // Exclude line terminator
6853 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
6854
6855 if (!parse_header(line_reader.ptr(), end,
6856 [&](const std::string & key, const std::string & val)
6857 {
6858 headers.emplace(key, val);
6859 }))
6860 {
6861 return false;
6862 }
6863
6864 header_count++;
6865 }
6866
6867 return true;
6868}
6869
6870inline bool read_content_with_length(Stream &strm, size_t len,
6871 DownloadProgress progress,
6872 ContentReceiverWithProgress out)
6873{
6874 char buf[CPPHTTPLIB_RECV_BUFSIZ];
6875
6876 detail::BodyReader br;
6877 br.stream = &strm;
6878 br.content_length = len;
6879 br.chunked = false;
6880 br.bytes_read = 0;
6881 br.last_error = Error::Success;
6882
6883 size_t r = 0;
6884 while (r < len)
6885 {
6886 auto read_len = static_cast<size_t>(len - r);
6887 auto to_read = (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ);
6888 auto n = detail::read_body_content(&strm, br, buf, to_read);
6889 if (n <= 0)
6890 {
6891 return false;
6892 }
6893
6894 if (!out(buf, static_cast<size_t>(n), r, len))
6895 {
6896 return false;
6897 }
6898 r += static_cast<size_t>(n);
6899
6900 if (progress)
6901 {
6902 if (!progress(r, len))
6903 {
6904 return false;
6905 }
6906 }
6907 }
6908
6909 return true;
6910}
6911
6912inline void skip_content_with_length(Stream &strm, size_t len)
6913{
6914 char buf[CPPHTTPLIB_RECV_BUFSIZ];
6915 size_t r = 0;
6916 while (r < len)
6917 {
6918 auto read_len = static_cast<size_t>(len - r);
6919 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
6920 if (n <= 0)
6921 {
6922 return;
6923 }
6924 r += static_cast<size_t>(n);
6925 }
6926}
6927
6928enum class ReadContentResult
6929{
6930 Success, // Successfully read the content
6931 PayloadTooLarge, // The content exceeds the specified payload limit
6932 Error // An error occurred while reading the content
6933};
6934
6935inline ReadContentResult
6936read_content_without_length(Stream &strm, size_t payload_max_length,
6937 ContentReceiverWithProgress out)
6938{
6939 char buf[CPPHTTPLIB_RECV_BUFSIZ];
6940 size_t r = 0;
6941 for (;;)
6942 {
6943 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
6944 if (n == 0)
6945 {
6946 return ReadContentResult::Success;
6947 }
6948 if (n < 0)
6949 {
6950 return ReadContentResult::Error;
6951 }
6952
6953 // Check if adding this data would exceed the payload limit
6954 if (r > payload_max_length ||
6955 payload_max_length - r < static_cast<size_t>(n))
6956 {
6957 return ReadContentResult::PayloadTooLarge;
6958 }
6959
6960 if (!out(buf, static_cast<size_t>(n), r, 0))
6961 {
6962 return ReadContentResult::Error;
6963 }
6964 r += static_cast<size_t>(n);
6965 }
6966
6967 return ReadContentResult::Success;
6968}
6969
6970template <typename T>
6971inline ReadContentResult read_content_chunked(Stream &strm, T &x,
6972 size_t payload_max_length,
6973 ContentReceiverWithProgress out)
6974{
6975 detail::ChunkedDecoder dec(strm);
6976
6977 char buf[CPPHTTPLIB_RECV_BUFSIZ];
6978 size_t total_len = 0;
6979
6980 for (;;)
6981 {
6982 size_t chunk_offset = 0;
6983 size_t chunk_total = 0;
6984 auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
6985 if (n < 0)
6986 {
6987 return ReadContentResult::Error;
6988 }
6989
6990 if (n == 0)
6991 {
6992 if (!dec.parse_trailers_into(x.trailers, x.headers))
6993 {
6994 return ReadContentResult::Error;
6995 }
6996 return ReadContentResult::Success;
6997 }
6998
6999 if (total_len > payload_max_length ||
7000 payload_max_length - total_len < static_cast<size_t>(n))
7001 {
7002 return ReadContentResult::PayloadTooLarge;
7003 }
7004
7005 if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total))
7006 {
7007 return ReadContentResult::Error;
7008 }
7009
7010 total_len += static_cast<size_t>(n);
7011 }
7012}
7013
7014inline bool is_chunked_transfer_encoding(const Headers &headers)
7015{
7016 return case_ignore::equal(
7017 get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
7018}
7019
7020template <typename T, typename U>
7021bool prepare_content_receiver(T &x, int &status,
7022 ContentReceiverWithProgress receiver,
7023 bool decompress, U callback)
7024{
7025 if (decompress)
7026 {
7027 std::string encoding = x.get_header_value("Content-Encoding");
7028 std::unique_ptr<decompressor> decompressor;
7029
7030 if (!encoding.empty())
7031 {
7032 decompressor = detail::create_decompressor(encoding);
7033 if (!decompressor)
7034 {
7035 // Unsupported encoding or no support compiled in
7036 status = StatusCode::UnsupportedMediaType_415;
7037 return false;
7038 }
7039 }
7040
7041 if (decompressor)
7042 {
7043 if (decompressor->is_valid())
7044 {
7045 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
7046 size_t off, size_t len)
7047 {
7048 return decompressor->decompress(buf, n,
7049 [&](const char *buf2, size_t n2)
7050 {
7051 return receiver(buf2, n2, off, len);
7052 });
7053 };
7054 return callback(std::move(out));
7055 }
7056 else
7057 {
7058 status = StatusCode::InternalServerError_500;
7059 return false;
7060 }
7061 }
7062 }
7063
7064 ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
7065 size_t len)
7066 {
7067 return receiver(buf, n, off, len);
7068 };
7069 return callback(std::move(out));
7070}
7071
7072template <typename T>
7073bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
7074 DownloadProgress progress,
7075 ContentReceiverWithProgress receiver, bool decompress)
7076{
7077 return prepare_content_receiver(
7078 x, status, std::move(receiver), decompress,
7079 [&](const ContentReceiverWithProgress & out)
7080 {
7081 auto ret = true;
7082 auto exceed_payload_max_length = false;
7083
7084 if (is_chunked_transfer_encoding(x.headers))
7085 {
7086 auto result = read_content_chunked(strm, x, payload_max_length, out);
7087 if (result == ReadContentResult::Success)
7088 {
7089 ret = true;
7090 }
7091 else if (result == ReadContentResult::PayloadTooLarge)
7092 {
7093 exceed_payload_max_length = true;
7094 ret = false;
7095 }
7096 else
7097 {
7098 ret = false;
7099 }
7100 }
7101 else if (!has_header(x.headers, "Content-Length"))
7102 {
7103 auto result =
7104 read_content_without_length(strm, payload_max_length, out);
7105 if (result == ReadContentResult::Success)
7106 {
7107 ret = true;
7108 }
7109 else if (result == ReadContentResult::PayloadTooLarge)
7110 {
7111 exceed_payload_max_length = true;
7112 ret = false;
7113 }
7114 else
7115 {
7116 ret = false;
7117 }
7118 }
7119 else
7120 {
7121 auto is_invalid_value = false;
7122 auto len = get_header_value_u64(x.headers, "Content-Length",
7123 (std::numeric_limits<size_t>::max)(),
7124 0, is_invalid_value);
7125
7126 if (is_invalid_value)
7127 {
7128 ret = false;
7129 }
7130 else if (len > payload_max_length)
7131 {
7132 exceed_payload_max_length = true;
7133 skip_content_with_length(strm, len);
7134 ret = false;
7135 }
7136 else if (len > 0)
7137 {
7138 ret = read_content_with_length(strm, len, std::move(progress), out);
7139 }
7140 }
7141
7142 if (!ret)
7143 {
7144 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
7145 : StatusCode::BadRequest_400;
7146 }
7147 return ret;
7148 });
7149}
7150
7151inline ssize_t write_request_line(Stream &strm, const std::string &method,
7152 const std::string &path)
7153{
7154 std::string s = method;
7155 s += ' ';
7156 s += path;
7157 s += " HTTP/1.1\r\n";
7158 return strm.write(s.data(), s.size());
7159}
7160
7161inline ssize_t write_response_line(Stream &strm, int status)
7162{
7163 std::string s = "HTTP/1.1 ";
7164 s += std::to_string(status);
7165 s += ' ';
7166 s += httplib::status_message(status);
7167 s += "\r\n";
7168 return strm.write(s.data(), s.size());
7169}
7170
7171inline ssize_t write_headers(Stream &strm, const Headers &headers)
7172{
7173 ssize_t write_len = 0;
7174 for (const auto &x : headers)
7175 {
7176 std::string s;
7177 s = x.first;
7178 s += ": ";
7179 s += x.second;
7180 s += "\r\n";
7181
7182 auto len = strm.write(s.data(), s.size());
7183 if (len < 0)
7184 {
7185 return len;
7186 }
7187 write_len += len;
7188 }
7189 auto len = strm.write("\r\n");
7190 if (len < 0)
7191 {
7192 return len;
7193 }
7194 write_len += len;
7195 return write_len;
7196}
7197
7198inline bool write_data(Stream &strm, const char *d, size_t l)
7199{
7200 size_t offset = 0;
7201 while (offset < l)
7202 {
7203 auto length = strm.write(d + offset, l - offset);
7204 if (length < 0)
7205 {
7206 return false;
7207 }
7208 offset += static_cast<size_t>(length);
7209 }
7210 return true;
7211}
7212
7213template <typename T>
7214inline bool write_content_with_progress(Stream &strm,
7215 const ContentProvider &content_provider,
7216 size_t offset, size_t length,
7217 T is_shutting_down,
7218 const UploadProgress &upload_progress,
7219 Error &error)
7220{
7221 size_t end_offset = offset + length;
7222 size_t start_offset = offset;
7223 auto ok = true;
7224 DataSink data_sink;
7225
7226 data_sink.write = [&](const char *d, size_t l) -> bool
7227 {
7228 if (ok)
7229 {
7230 if (write_data(strm, d, l))
7231 {
7232 offset += l;
7233
7234 if (upload_progress && length > 0)
7235 {
7236 size_t current_written = offset - start_offset;
7237 if (!upload_progress(current_written, length))
7238 {
7239 ok = false;
7240 return false;
7241 }
7242 }
7243 }
7244 else
7245 {
7246 ok = false;
7247 }
7248 }
7249 return ok;
7250 };
7251
7252 data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
7253
7254 while (offset < end_offset && !is_shutting_down())
7255 {
7256 if (!strm.wait_writable())
7257 {
7258 error = Error::Write;
7259 return false;
7260 }
7261 else if (!content_provider(offset, end_offset - offset, data_sink))
7262 {
7263 error = Error::Canceled;
7264 return false;
7265 }
7266 else if (!ok)
7267 {
7268 error = Error::Write;
7269 return false;
7270 }
7271 }
7272
7273 error = Error::Success;
7274 return true;
7275}
7276
7277template <typename T>
7278inline bool write_content(Stream &strm, const ContentProvider &content_provider,
7279 size_t offset, size_t length, T is_shutting_down,
7280 Error &error)
7281{
7282 return write_content_with_progress<T>(strm, content_provider, offset, length,
7283 is_shutting_down, nullptr, error);
7284}
7285
7286template <typename T>
7287inline bool write_content(Stream &strm, const ContentProvider &content_provider,
7288 size_t offset, size_t length,
7289 const T &is_shutting_down)
7290{
7291 auto error = Error::Success;
7292 return write_content(strm, content_provider, offset, length, is_shutting_down,
7293 error);
7294}
7295
7296template <typename T>
7297inline bool
7298write_content_without_length(Stream &strm,
7299 const ContentProvider &content_provider,
7300 const T &is_shutting_down)
7301{
7302 size_t offset = 0;
7303 auto data_available = true;
7304 auto ok = true;
7305 DataSink data_sink;
7306
7307 data_sink.write = [&](const char *d, size_t l) -> bool
7308 {
7309 if (ok)
7310 {
7311 offset += l;
7312 if (!write_data(strm, d, l))
7313 {
7314 ok = false;
7315 }
7316 }
7317 return ok;
7318 };
7319
7320 data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
7321
7322 data_sink.done = [&](void)
7323 {
7324 data_available = false;
7325 };
7326
7327 while (data_available && !is_shutting_down())
7328 {
7329 if (!strm.wait_writable())
7330 {
7331 return false;
7332 }
7333 else if (!content_provider(offset, 0, data_sink))
7334 {
7335 return false;
7336 }
7337 else if (!ok)
7338 {
7339 return false;
7340 }
7341 }
7342 return true;
7343}
7344
7345template <typename T, typename U>
7346inline bool
7347write_content_chunked(Stream &strm, const ContentProvider &content_provider,
7348 const T &is_shutting_down, U &compressor, Error &error)
7349{
7350 size_t offset = 0;
7351 auto data_available = true;
7352 auto ok = true;
7353 DataSink data_sink;
7354
7355 data_sink.write = [&](const char *d, size_t l) -> bool
7356 {
7357 if (ok)
7358 {
7359 data_available = l > 0;
7360 offset += l;
7361
7362 std::string payload;
7363 if (compressor.compress(d, l, false,
7364 [&](const char *data, size_t data_len)
7365 {
7366 payload.append(data, data_len);
7367 return true;
7368 }))
7369 {
7370 if (!payload.empty())
7371 {
7372 // Emit chunked response header and footer for each chunk
7373 auto chunk =
7374 from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
7375 if (!write_data(strm, chunk.data(), chunk.size()))
7376 {
7377 ok = false;
7378 }
7379 }
7380 }
7381 else
7382 {
7383 ok = false;
7384 }
7385 }
7386 return ok;
7387 };
7388
7389 data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
7390
7391 auto done_with_trailer = [&](const Headers * trailer)
7392 {
7393 if (!ok)
7394 {
7395 return;
7396 }
7397
7398 data_available = false;
7399
7400 std::string payload;
7401 if (!compressor.compress(nullptr, 0, true,
7402 [&](const char *data, size_t data_len)
7403 {
7404 payload.append(data, data_len);
7405 return true;
7406 }))
7407 {
7408 ok = false;
7409 return;
7410 }
7411
7412 if (!payload.empty())
7413 {
7414 // Emit chunked response header and footer for each chunk
7415 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
7416 if (!write_data(strm, chunk.data(), chunk.size()))
7417 {
7418 ok = false;
7419 return;
7420 }
7421 }
7422
7423 constexpr const char done_marker[] = "0\r\n";
7424 if (!write_data(strm, done_marker, str_len(done_marker)))
7425 {
7426 ok = false;
7427 }
7428
7429 // Trailer
7430 if (trailer)
7431 {
7432 for (const auto &kv : *trailer)
7433 {
7434 std::string field_line = kv.first + ": " + kv.second + "\r\n";
7435 if (!write_data(strm, field_line.data(), field_line.size()))
7436 {
7437 ok = false;
7438 }
7439 }
7440 }
7441
7442 constexpr const char crlf[] = "\r\n";
7443 if (!write_data(strm, crlf, str_len(crlf)))
7444 {
7445 ok = false;
7446 }
7447 };
7448
7449 data_sink.done = [&](void)
7450 {
7451 done_with_trailer(nullptr);
7452 };
7453
7454 data_sink.done_with_trailer = [&](const Headers & trailer)
7455 {
7456 done_with_trailer(&trailer);
7457 };
7458
7459 while (data_available && !is_shutting_down())
7460 {
7461 if (!strm.wait_writable())
7462 {
7463 error = Error::Write;
7464 return false;
7465 }
7466 else if (!content_provider(offset, 0, data_sink))
7467 {
7468 error = Error::Canceled;
7469 return false;
7470 }
7471 else if (!ok)
7472 {
7473 error = Error::Write;
7474 return false;
7475 }
7476 }
7477
7478 error = Error::Success;
7479 return true;
7480}
7481
7482template <typename T, typename U>
7483inline bool write_content_chunked(Stream &strm,
7484 const ContentProvider &content_provider,
7485 const T &is_shutting_down, U &compressor)
7486{
7487 auto error = Error::Success;
7488 return write_content_chunked(strm, content_provider, is_shutting_down,
7489 compressor, error);
7490}
7491
7492template <typename T>
7493inline bool redirect(T &cli, Request &req, Response &res,
7494 const std::string &path, const std::string &location,
7495 Error &error)
7496{
7497 Request new_req = req;
7498 new_req.path = path;
7499 new_req.redirect_count_ -= 1;
7500
7501 if (res.status == StatusCode::SeeOther_303 &&
7502 (req.method != "GET" && req.method != "HEAD"))
7503 {
7504 new_req.method = "GET";
7505 new_req.body.clear();
7506 new_req.headers.clear();
7507 }
7508
7509 Response new_res;
7510
7511 auto ret = cli.send(new_req, new_res, error);
7512 if (ret)
7513 {
7514 req = std::move(new_req);
7515 res = std::move(new_res);
7516
7517 if (res.location.empty())
7518 {
7519 res.location = location;
7520 }
7521 }
7522 return ret;
7523}
7524
7525inline std::string params_to_query_str(const Params &params)
7526{
7527 std::string query;
7528
7529 for (auto it = params.begin(); it != params.end(); ++it)
7530 {
7531 if (it != params.begin())
7532 {
7533 query += '&';
7534 }
7535 query += encode_query_component(it->first);
7536 query += '=';
7537 query += encode_query_component(it->second);
7538 }
7539 return query;
7540}
7541
7542inline void parse_query_text(const char *data, std::size_t size,
7543 Params &params)
7544{
7545 std::set<std::string> cache;
7546 split(data, data + size, '&', [&](const char *b, const char *e)
7547 {
7548 std::string kv(b, e);
7549 if (cache.find(kv) != cache.end())
7550 {
7551 return;
7552 }
7553 cache.insert(std::move(kv));
7554
7555 std::string key;
7556 std::string val;
7557 divide(b, static_cast<std::size_t>(e - b), '=',
7558 [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
7559 std::size_t rhs_size)
7560 {
7561 key.assign(lhs_data, lhs_size);
7562 val.assign(rhs_data, rhs_size);
7563 });
7564
7565 if (!key.empty())
7566 {
7567 params.emplace(decode_query_component(key), decode_query_component(val));
7568 }
7569 });
7570}
7571
7572inline void parse_query_text(const std::string &s, Params &params)
7573{
7574 parse_query_text(s.data(), s.size(), params);
7575}
7576
7577// Normalize a query string by decoding and re-encoding each key/value pair
7578// while preserving the original parameter order. This avoids double-encoding
7579// and ensures consistent encoding without reordering (unlike Params which
7580// uses std::multimap and sorts keys).
7581inline std::string normalize_query_string(const std::string &query)
7582{
7583 std::string result;
7584 split(query.data(), query.data() + query.size(), '&',
7585 [&](const char *b, const char *e)
7586 {
7587 std::string key;
7588 std::string val;
7589 divide(b, static_cast<std::size_t>(e - b), '=',
7590 [&](const char *lhs_data, std::size_t lhs_size,
7591 const char *rhs_data, std::size_t rhs_size)
7592 {
7593 key.assign(lhs_data, lhs_size);
7594 val.assign(rhs_data, rhs_size);
7595 });
7596
7597 if (!key.empty())
7598 {
7599 auto dec_key = decode_query_component(key);
7600 auto dec_val = decode_query_component(val);
7601
7602 if (!result.empty())
7603 {
7604 result += '&';
7605 }
7606 result += encode_query_component(dec_key);
7607 if (!val.empty() || std::find(b, e, '=') != e)
7608 {
7609 result += '=';
7610 result += encode_query_component(dec_val);
7611 }
7612 }
7613 });
7614 return result;
7615}
7616
7617inline bool parse_multipart_boundary(const std::string &content_type,
7618 std::string &boundary)
7619{
7620 auto boundary_keyword = "boundary=";
7621 auto pos = content_type.find(boundary_keyword);
7622 if (pos == std::string::npos)
7623 {
7624 return false;
7625 }
7626 auto end = content_type.find(';', pos);
7627 auto beg = pos + strlen(boundary_keyword);
7628 boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
7629 return !boundary.empty();
7630}
7631
7632inline void parse_disposition_params(const std::string &s, Params &params)
7633{
7634 std::set<std::string> cache;
7635 split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e)
7636 {
7637 std::string kv(b, e);
7638 if (cache.find(kv) != cache.end())
7639 {
7640 return;
7641 }
7642 cache.insert(kv);
7643
7644 std::string key;
7645 std::string val;
7646 split(b, e, '=', [&](const char *b2, const char *e2)
7647 {
7648 if (key.empty())
7649 {
7650 key.assign(b2, e2);
7651 }
7652 else
7653 {
7654 val.assign(b2, e2);
7655 }
7656 });
7657
7658 if (!key.empty())
7659 {
7660 params.emplace(trim_double_quotes_copy((key)),
7661 trim_double_quotes_copy((val)));
7662 }
7663 });
7664}
7665
7666#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7667inline bool parse_range_header(const std::string &s, Ranges &ranges)
7668{
7669#else
7670inline bool parse_range_header(const std::string &s, Ranges &ranges) try
7671{
7672#endif
7673 auto is_valid = [](const std::string & str)
7674 {
7675 return std::all_of(str.cbegin(), str.cend(),
7676 [](unsigned char c)
7677 {
7678 return std::isdigit(c);
7679 });
7680 };
7681
7682 if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0)
7683 {
7684 const auto pos = static_cast<size_t>(6);
7685 const auto len = static_cast<size_t>(s.size() - 6);
7686 auto all_valid_ranges = true;
7687 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e)
7688 {
7689 if (!all_valid_ranges)
7690 {
7691 return;
7692 }
7693
7694 const auto it = std::find(b, e, '-');
7695 if (it == e)
7696 {
7697 all_valid_ranges = false;
7698 return;
7699 }
7700
7701 const auto lhs = std::string(b, it);
7702 const auto rhs = std::string(it + 1, e);
7703 if (!is_valid(lhs) || !is_valid(rhs))
7704 {
7705 all_valid_ranges = false;
7706 return;
7707 }
7708
7709 const auto first =
7710 static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
7711 const auto last =
7712 static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
7713 if ((first == -1 && last == -1) ||
7714 (first != -1 && last != -1 && first > last))
7715 {
7716 all_valid_ranges = false;
7717 return;
7718 }
7719
7720 ranges.emplace_back(first, last);
7721 });
7722 return all_valid_ranges && !ranges.empty();
7723 }
7724 return false;
7725#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7726}
7727#else
7728}
7729catch (...)
7730{
7731 return false;
7732}
7733#endif
7734
7735inline bool parse_accept_header(const std::string &s,
7736 std::vector<std::string> &content_types)
7737{
7738 content_types.clear();
7739
7740 // Empty string is considered valid (no preference)
7741 if (s.empty())
7742 {
7743 return true;
7744 }
7745
7746 // Check for invalid patterns: leading/trailing commas or consecutive commas
7747 if (s.front() == ',' || s.back() == ',' ||
7748 s.find(",,") != std::string::npos)
7749 {
7750 return false;
7751 }
7752
7753 struct AcceptEntry
7754 {
7755 std::string media_type;
7756 double quality;
7757 int order; // Original order in header
7758 };
7759
7760 std::vector<AcceptEntry> entries;
7761 int order = 0;
7762 bool has_invalid_entry = false;
7763
7764 // Split by comma and parse each entry
7765 split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e)
7766 {
7767 std::string entry(b, e);
7768 entry = trim_copy(entry);
7769
7770 if (entry.empty())
7771 {
7772 has_invalid_entry = true;
7773 return;
7774 }
7775
7776 AcceptEntry accept_entry;
7777 accept_entry.quality = 1.0; // Default quality
7778 accept_entry.order = order++;
7779
7780 // Find q= parameter
7781 auto q_pos = entry.find(";q=");
7782 if (q_pos == std::string::npos)
7783 {
7784 q_pos = entry.find("; q=");
7785 }
7786
7787 if (q_pos != std::string::npos)
7788 {
7789 // Extract media type (before q parameter)
7790 accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
7791
7792 // Extract quality value
7793 auto q_start = entry.find('=', q_pos) + 1;
7794 auto q_end = entry.find(';', q_start);
7795 if (q_end == std::string::npos)
7796 {
7797 q_end = entry.length();
7798 }
7799
7800 std::string quality_str =
7801 trim_copy(entry.substr(q_start, q_end - q_start));
7802 if (quality_str.empty())
7803 {
7804 has_invalid_entry = true;
7805 return;
7806 }
7807
7808#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7809 {
7810 std::istringstream iss(quality_str);
7811 iss >> accept_entry.quality;
7812
7813 // Check if conversion was successful and entire string was consumed
7814 if (iss.fail() || !iss.eof())
7815 {
7816 has_invalid_entry = true;
7817 return;
7818 }
7819 }
7820#else
7821 try
7822 {
7823 accept_entry.quality = std::stod(quality_str);
7824 }
7825 catch (...)
7826 {
7827 has_invalid_entry = true;
7828 return;
7829 }
7830#endif
7831 // Check if quality is in valid range [0.0, 1.0]
7832 if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0)
7833 {
7834 has_invalid_entry = true;
7835 return;
7836 }
7837 }
7838 else
7839 {
7840 // No quality parameter, use entire entry as media type
7841 accept_entry.media_type = entry;
7842 }
7843
7844 // Remove additional parameters from media type
7845 auto param_pos = accept_entry.media_type.find(';');
7846 if (param_pos != std::string::npos)
7847 {
7848 accept_entry.media_type =
7849 trim_copy(accept_entry.media_type.substr(0, param_pos));
7850 }
7851
7852 // Basic validation of media type format
7853 if (accept_entry.media_type.empty())
7854 {
7855 has_invalid_entry = true;
7856 return;
7857 }
7858
7859 // Check for basic media type format (should contain '/' or be '*')
7860 if (accept_entry.media_type != "*" &&
7861 accept_entry.media_type.find('/') == std::string::npos)
7862 {
7863 has_invalid_entry = true;
7864 return;
7865 }
7866
7867 entries.push_back(std::move(accept_entry));
7868 });
7869
7870 // Return false if any invalid entry was found
7871 if (has_invalid_entry)
7872 {
7873 return false;
7874 }
7875
7876 // Sort by quality (descending), then by original order (ascending)
7877 std::sort(entries.begin(), entries.end(),
7878 [](const AcceptEntry & a, const AcceptEntry & b)
7879 {
7880 if (a.quality != b.quality)
7881 {
7882 return a.quality > b.quality; // Higher quality first
7883 }
7884 return a.order < b.order; // Earlier order first for same quality
7885 });
7886
7887 // Extract sorted media types
7888 content_types.reserve(entries.size());
7889 for (auto &entry : entries)
7890 {
7891 content_types.push_back(std::move(entry.media_type));
7892 }
7893
7894 return true;
7895}
7896
7897class FormDataParser
7898{
7899 public:
7900 FormDataParser() = default;
7901
7902 void set_boundary(std::string &&boundary)
7903 {
7904 boundary_ = std::move(boundary);
7905 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
7906 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
7907 }
7908
7909 bool is_valid() const
7910 {
7911 return is_valid_;
7912 }
7913
7914 bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
7915 const ContentReceiver &content_callback)
7916 {
7917
7918 buf_append(buf, n);
7919
7920 while (buf_size() > 0)
7921 {
7922 switch (state_)
7923 {
7924 case 0: // Initial boundary
7925 {
7926 auto pos = buf_find(dash_boundary_crlf_);
7927 if (pos == buf_size())
7928 {
7929 return true;
7930 }
7931 buf_erase(pos + dash_boundary_crlf_.size());
7932 state_ = 1;
7933 break;
7934 }
7935 case 1: // New entry
7936 {
7937 clear_file_info();
7938 state_ = 2;
7939 break;
7940 }
7941 case 2: // Headers
7942 {
7943 auto pos = buf_find(crlf_);
7945 {
7946 return false;
7947 }
7948 while (pos < buf_size())
7949 {
7950 // Empty line
7951 if (pos == 0)
7952 {
7953 if (!header_callback(file_))
7954 {
7955 is_valid_ = false;
7956 return false;
7957 }
7958 buf_erase(crlf_.size());
7959 state_ = 3;
7960 break;
7961 }
7962
7963 const auto header = buf_head(pos);
7964
7965 if (!parse_header(header.data(), header.data() + header.size(),
7966 [&](const std::string &, const std::string &) {}))
7967 {
7968 is_valid_ = false;
7969 return false;
7970 }
7971
7972 // Parse and emplace space trimmed headers into a map
7973 if (!parse_header(
7974 header.data(), header.data() + header.size(),
7975 [&](const std::string & key, const std::string & val)
7976 {
7977 file_.headers.emplace(key, val);
7978 }))
7979 {
7980 is_valid_ = false;
7981 return false;
7982 }
7983
7984 constexpr const char header_content_type[] = "Content-Type:";
7985
7986 if (start_with_case_ignore(header, header_content_type))
7987 {
7988 file_.content_type =
7989 trim_copy(header.substr(str_len(header_content_type)));
7990 }
7991 else
7992 {
7993 thread_local const std::regex re_content_disposition(
7994 R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
7995 std::regex_constants::icase);
7996
7997 std::smatch m;
7998 if (std::regex_match(header, m, re_content_disposition))
7999 {
8000 Params params;
8001 parse_disposition_params(m[1], params);
8002
8003 auto it = params.find("name");
8004 if (it != params.end())
8005 {
8006 file_.name = it->second;
8007 }
8008 else
8009 {
8010 is_valid_ = false;
8011 return false;
8012 }
8013
8014 it = params.find("filename");
8015 if (it != params.end())
8016 {
8017 file_.filename = it->second;
8018 }
8019
8020 it = params.find("filename*");
8021 if (it != params.end())
8022 {
8023 // Only allow UTF-8 encoding...
8024 thread_local const std::regex re_rfc5987_encoding(
8025 R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
8026
8027 std::smatch m2;
8028 if (std::regex_match(it->second, m2, re_rfc5987_encoding))
8029 {
8030 file_.filename = decode_path_component(m2[1]); // override...
8031 }
8032 else
8033 {
8034 is_valid_ = false;
8035 return false;
8036 }
8037 }
8038 }
8039 }
8040 buf_erase(pos + crlf_.size());
8041 pos = buf_find(crlf_);
8042 }
8043 if (state_ != 3)
8044 {
8045 return true;
8046 }
8047 break;
8048 }
8049 case 3: // Body
8050 {
8051 if (crlf_dash_boundary_.size() > buf_size())
8052 {
8053 return true;
8054 }
8055 auto pos = buf_find(crlf_dash_boundary_);
8056 if (pos < buf_size())
8057 {
8058 if (!content_callback(buf_data(), pos))
8059 {
8060 is_valid_ = false;
8061 return false;
8062 }
8063 buf_erase(pos + crlf_dash_boundary_.size());
8064 state_ = 4;
8065 }
8066 else
8067 {
8068 auto len = buf_size() - crlf_dash_boundary_.size();
8069 if (len > 0)
8070 {
8071 if (!content_callback(buf_data(), len))
8072 {
8073 is_valid_ = false;
8074 return false;
8075 }
8076 buf_erase(len);
8077 }
8078 return true;
8079 }
8080 break;
8081 }
8082 case 4: // Boundary
8083 {
8084 if (crlf_.size() > buf_size())
8085 {
8086 return true;
8087 }
8088 if (buf_start_with(crlf_))
8089 {
8090 buf_erase(crlf_.size());
8091 state_ = 1;
8092 }
8093 else
8094 {
8095 if (dash_.size() > buf_size())
8096 {
8097 return true;
8098 }
8099 if (buf_start_with(dash_))
8100 {
8101 buf_erase(dash_.size());
8102 is_valid_ = true;
8103 buf_erase(buf_size()); // Remove epilogue
8104 }
8105 else
8106 {
8107 return true;
8108 }
8109 }
8110 break;
8111 }
8112 }
8113 }
8114
8115 return true;
8116 }
8117
8118 private:
8119 void clear_file_info()
8120 {
8121 file_.name.clear();
8122 file_.filename.clear();
8123 file_.content_type.clear();
8124 file_.headers.clear();
8125 }
8126
8127 bool start_with_case_ignore(const std::string &a, const char *b) const
8128 {
8129 const auto b_len = strlen(b);
8130 if (a.size() < b_len)
8131 {
8132 return false;
8133 }
8134 for (size_t i = 0; i < b_len; i++)
8135 {
8136 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i]))
8137 {
8138 return false;
8139 }
8140 }
8141 return true;
8142 }
8143
8144 const std::string dash_ = "--";
8145 const std::string crlf_ = "\r\n";
8146 std::string boundary_;
8147 std::string dash_boundary_crlf_;
8148 std::string crlf_dash_boundary_;
8149
8150 size_t state_ = 0;
8151 bool is_valid_ = false;
8152 FormData file_;
8153
8154 // Buffer
8155 bool start_with(const std::string &a, size_t spos, size_t epos,
8156 const std::string &b) const
8157 {
8158 if (epos - spos < b.size())
8159 {
8160 return false;
8161 }
8162 for (size_t i = 0; i < b.size(); i++)
8163 {
8164 if (a[i + spos] != b[i])
8165 {
8166 return false;
8167 }
8168 }
8169 return true;
8170 }
8171
8172 size_t buf_size() const
8173 {
8174 return buf_epos_ - buf_spos_;
8175 }
8176
8177 const char *buf_data() const
8178 {
8179 return &buf_[buf_spos_];
8180 }
8181
8182 std::string buf_head(size_t l) const
8183 {
8184 return buf_.substr(buf_spos_, l);
8185 }
8186
8187 bool buf_start_with(const std::string &s) const
8188 {
8189 return start_with(buf_, buf_spos_, buf_epos_, s);
8190 }
8191
8192 size_t buf_find(const std::string &s) const
8193 {
8194 auto c = s.front();
8195
8196 size_t off = buf_spos_;
8197 while (off < buf_epos_)
8198 {
8199 auto pos = off;
8200 while (true)
8201 {
8202 if (pos == buf_epos_)
8203 {
8204 return buf_size();
8205 }
8206 if (buf_[pos] == c)
8207 {
8208 break;
8209 }
8210 pos++;
8211 }
8212
8213 auto remaining_size = buf_epos_ - pos;
8214 if (s.size() > remaining_size)
8215 {
8216 return buf_size();
8217 }
8218
8219 if (start_with(buf_, pos, buf_epos_, s))
8220 {
8221 return pos - buf_spos_;
8222 }
8223
8224 off = pos + 1;
8225 }
8226
8227 return buf_size();
8228 }
8229
8230 void buf_append(const char *data, size_t n)
8231 {
8232 auto remaining_size = buf_size();
8233 if (remaining_size > 0 && buf_spos_ > 0)
8234 {
8235 for (size_t i = 0; i < remaining_size; i++)
8236 {
8237 buf_[i] = buf_[buf_spos_ + i];
8238 }
8239 }
8240 buf_spos_ = 0;
8241 buf_epos_ = remaining_size;
8242
8243 if (remaining_size + n > buf_.size())
8244 {
8245 buf_.resize(remaining_size + n);
8246 }
8247
8248 for (size_t i = 0; i < n; i++)
8249 {
8250 buf_[buf_epos_ + i] = data[i];
8251 }
8252 buf_epos_ += n;
8253 }
8254
8255 void buf_erase(size_t size)
8256 {
8257 buf_spos_ += size;
8258 }
8259
8260 std::string buf_;
8261 size_t buf_spos_ = 0;
8262 size_t buf_epos_ = 0;
8263};
8264
8265inline std::string random_string(size_t length)
8266{
8267 constexpr const char data[] =
8268 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8269
8270 thread_local auto engine([]()
8271 {
8272 // std::random_device might actually be deterministic on some
8273 // platforms, but due to lack of support in the c++ standard library,
8274 // doing better requires either some ugly hacks or breaking portability.
8275 std::random_device seed_gen;
8276 // Request 128 bits of entropy for initialization
8277 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
8278 return std::mt19937(seed_sequence);
8279 }
8280 ());
8281
8282 std::string result;
8283 for (size_t i = 0; i < length; i++)
8284 {
8285 result += data[engine() % (sizeof(data) - 1)];
8286 }
8287 return result;
8288}
8289
8290inline std::string make_multipart_data_boundary()
8291{
8292 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
8293}
8294
8295inline bool is_multipart_boundary_chars_valid(const std::string &boundary)
8296{
8297 auto valid = true;
8298 for (size_t i = 0; i < boundary.size(); i++)
8299 {
8300 auto c = boundary[i];
8301 if (!std::isalnum(c) && c != '-' && c != '_')
8302 {
8303 valid = false;
8304 break;
8305 }
8306 }
8307 return valid;
8308}
8309
8310template <typename T>
8311inline std::string
8312serialize_multipart_formdata_item_begin(const T &item,
8313 const std::string &boundary)
8314{
8315 std::string body = "--" + boundary + "\r\n";
8316 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
8317 if (!item.filename.empty())
8318 {
8319 body += "; filename=\"" + item.filename + "\"";
8320 }
8321 body += "\r\n";
8322 if (!item.content_type.empty())
8323 {
8324 body += "Content-Type: " + item.content_type + "\r\n";
8325 }
8326 body += "\r\n";
8327
8328 return body;
8329}
8330
8331inline std::string serialize_multipart_formdata_item_end()
8332{
8333 return "\r\n";
8334}
8335
8336inline std::string
8337serialize_multipart_formdata_finish(const std::string &boundary)
8338{
8339 return "--" + boundary + "--\r\n";
8340}
8341
8342inline std::string
8343serialize_multipart_formdata_get_content_type(const std::string &boundary)
8344{
8345 return "multipart/form-data; boundary=" + boundary;
8346}
8347
8348inline std::string
8349serialize_multipart_formdata(const UploadFormDataItems &items,
8350 const std::string &boundary, bool finish = true)
8351{
8352 std::string body;
8353
8354 for (const auto &item : items)
8355 {
8356 body += serialize_multipart_formdata_item_begin(item, boundary);
8357 body += item.content + serialize_multipart_formdata_item_end();
8358 }
8359
8360 if (finish)
8361 {
8362 body += serialize_multipart_formdata_finish(boundary);
8363 }
8364
8365 return body;
8366}
8367
8368inline void coalesce_ranges(Ranges &ranges, size_t content_length)
8369{
8370 if (ranges.size() <= 1) return;
8371
8372 // Sort ranges by start position
8373 std::sort(ranges.begin(), ranges.end(),
8374 [](const Range & a, const Range & b)
8375 {
8376 return a.first < b.first;
8377 });
8378
8379 Ranges coalesced;
8380 coalesced.reserve(ranges.size());
8381
8382 for (auto &r : ranges)
8383 {
8384 auto first_pos = r.first;
8385 auto last_pos = r.second;
8386
8387 // Handle special cases like in range_error
8388 if (first_pos == -1 && last_pos == -1)
8389 {
8390 first_pos = 0;
8391 last_pos = static_cast<ssize_t>(content_length);
8392 }
8393
8394 if (first_pos == -1)
8395 {
8396 first_pos = static_cast<ssize_t>(content_length) - last_pos;
8397 last_pos = static_cast<ssize_t>(content_length) - 1;
8398 }
8399
8400 if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length))
8401 {
8402 last_pos = static_cast<ssize_t>(content_length) - 1;
8403 }
8404
8405 // Skip invalid ranges
8406 if (!(0 <= first_pos && first_pos <= last_pos &&
8407 last_pos < static_cast<ssize_t>(content_length)))
8408 {
8409 continue;
8410 }
8411
8412 // Coalesce with previous range if overlapping or adjacent (but not
8413 // identical)
8414 if (!coalesced.empty())
8415 {
8416 auto &prev = coalesced.back();
8417 // Check if current range overlaps or is adjacent to previous range
8418 // but don't coalesce identical ranges (allow duplicates)
8419 if (first_pos <= prev.second + 1 &&
8420 !(first_pos == prev.first && last_pos == prev.second))
8421 {
8422 // Extend the previous range
8423 prev.second = (std::max)(prev.second, last_pos);
8424 continue;
8425 }
8426 }
8427
8428 // Add new range
8429 coalesced.emplace_back(first_pos, last_pos);
8430 }
8431
8432 ranges = std::move(coalesced);
8433}
8434
8435inline bool range_error(Request &req, Response &res)
8436{
8437 if (!req.ranges.empty() && 200 <= res.status && res.status < 300)
8438 {
8439 ssize_t content_len = static_cast<ssize_t>(
8440 res.content_length_ ? res.content_length_ : res.body.size());
8441
8442 std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
8443 size_t overwrapping_count = 0;
8444
8445 // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
8446 // 'HTTP Semantics' to avoid potential denial-of-service attacks.
8447 // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
8448
8449 // Too many ranges
8450 if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT)
8451 {
8452 return true;
8453 }
8454
8455 for (auto &r : req.ranges)
8456 {
8457 auto &first_pos = r.first;
8458 auto &last_pos = r.second;
8459
8460 if (first_pos == -1 && last_pos == -1)
8461 {
8462 first_pos = 0;
8463 last_pos = content_len;
8464 }
8465
8466 if (first_pos == -1)
8467 {
8468 first_pos = content_len - last_pos;
8469 last_pos = content_len - 1;
8470 }
8471
8472 // NOTE: RFC-9110 '14.1.2. Byte Ranges':
8473 // A client can limit the number of bytes requested without knowing the
8474 // size of the selected representation. If the last-pos value is absent,
8475 // or if the value is greater than or equal to the current length of the
8476 // representation data, the byte range is interpreted as the remainder of
8477 // the representation (i.e., the server replaces the value of last-pos
8478 // with a value that is one less than the current length of the selected
8479 // representation).
8480 // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
8481 if (last_pos == -1 || last_pos >= content_len)
8482 {
8483 last_pos = content_len - 1;
8484 }
8485
8486 // Range must be within content length
8487 if (!(0 <= first_pos && first_pos <= last_pos &&
8488 last_pos <= content_len - 1))
8489 {
8490 return true;
8491 }
8492
8493 // Request must not have more than two overlapping ranges
8494 for (const auto &processed_range : processed_ranges)
8495 {
8496 if (!(last_pos < processed_range.first ||
8497 first_pos > processed_range.second))
8498 {
8499 overwrapping_count++;
8500 if (overwrapping_count > 2)
8501 {
8502 return true;
8503 }
8504 break; // Only count once per range
8505 }
8506 }
8507
8508 processed_ranges.emplace_back(first_pos, last_pos);
8509 }
8510
8511 // After validation, coalesce overlapping ranges as per RFC 9110
8512 coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
8513 }
8514
8515 return false;
8516}
8517
8518inline std::pair<size_t, size_t>
8519get_range_offset_and_length(Range r, size_t content_length)
8520{
8521 assert(r.first != -1 && r.second != -1);
8522 assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
8523 assert(r.first <= r.second &&
8524 r.second < static_cast<ssize_t>(content_length));
8525 (void)(content_length);
8526 return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
8527}
8528
8529inline std::string make_content_range_header_field(
8530 const std::pair<size_t, size_t> &offset_and_length, size_t content_length)
8531{
8532 auto st = offset_and_length.first;
8533 auto ed = st + offset_and_length.second - 1;
8534
8535 std::string field = "bytes ";
8536 field += std::to_string(st);
8537 field += '-';
8538 field += std::to_string(ed);
8539 field += '/';
8540 field += std::to_string(content_length);
8541 return field;
8542}
8543
8544template <typename SToken, typename CToken, typename Content>
8545bool process_multipart_ranges_data(const Request &req,
8546 const std::string &boundary,
8547 const std::string &content_type,
8548 size_t content_length, SToken stoken,
8549 CToken ctoken, Content content)
8550{
8551 for (size_t i = 0; i < req.ranges.size(); i++)
8552 {
8553 ctoken("--");
8554 stoken(boundary);
8555 ctoken("\r\n");
8556 if (!content_type.empty())
8557 {
8558 ctoken("Content-Type: ");
8559 stoken(content_type);
8560 ctoken("\r\n");
8561 }
8562
8563 auto offset_and_length =
8564 get_range_offset_and_length(req.ranges[i], content_length);
8565
8566 ctoken("Content-Range: ");
8567 stoken(make_content_range_header_field(offset_and_length, content_length));
8568 ctoken("\r\n");
8569 ctoken("\r\n");
8570
8571 if (!content(offset_and_length.first, offset_and_length.second))
8572 {
8573 return false;
8574 }
8575 ctoken("\r\n");
8576 }
8577
8578 ctoken("--");
8579 stoken(boundary);
8580 ctoken("--");
8581
8582 return true;
8583}
8584
8585inline void make_multipart_ranges_data(const Request &req, Response &res,
8586 const std::string &boundary,
8587 const std::string &content_type,
8588 size_t content_length,
8589 std::string &data)
8590{
8591 process_multipart_ranges_data(
8592 req, boundary, content_type, content_length,
8593 [&](const std::string & token)
8594 {
8595 data += token;
8596 },
8597 [&](const std::string & token)
8598 {
8599 data += token;
8600 },
8601 [&](size_t offset, size_t length)
8602 {
8603 assert(offset + length <= content_length);
8604 data += res.body.substr(offset, length);
8605 return true;
8606 });
8607}
8608
8609inline size_t get_multipart_ranges_data_length(const Request &req,
8610 const std::string &boundary,
8611 const std::string &content_type,
8612 size_t content_length)
8613{
8614 size_t data_length = 0;
8615
8616 process_multipart_ranges_data(
8617 req, boundary, content_type, content_length,
8618 [&](const std::string & token)
8619 {
8620 data_length += token.size();
8621 },
8622 [&](const std::string & token)
8623 {
8624 data_length += token.size();
8625 },
8626 [&](size_t /*offset*/, size_t length)
8627 {
8628 data_length += length;
8629 return true;
8630 });
8631
8632 return data_length;
8633}
8634
8635template <typename T>
8636inline bool
8637write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
8638 const std::string &boundary,
8639 const std::string &content_type,
8640 size_t content_length, const T &is_shutting_down)
8641{
8642 return process_multipart_ranges_data(
8643 req, boundary, content_type, content_length,
8644 [&](const std::string & token)
8645 {
8646 strm.write(token);
8647 },
8648 [&](const std::string & token)
8649 {
8650 strm.write(token);
8651 },
8652 [&](size_t offset, size_t length)
8653 {
8654 return write_content(strm, res.content_provider_, offset, length,
8655 is_shutting_down);
8656 });
8657}
8658
8659inline bool expect_content(const Request &req)
8660{
8661 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
8662 req.method == "DELETE")
8663 {
8664 return true;
8665 }
8666 if (req.has_header("Content-Length") &&
8667 req.get_header_value_u64("Content-Length") > 0)
8668 {
8669 return true;
8670 }
8671 if (is_chunked_transfer_encoding(req.headers))
8672 {
8673 return true;
8674 }
8675 return false;
8676}
8677
8678inline bool has_crlf(const std::string &s)
8679{
8680 auto p = s.c_str();
8681 while (*p)
8682 {
8683 if (*p == '\r' || *p == '\n')
8684 {
8685 return true;
8686 }
8687 p++;
8688 }
8689 return false;
8690}
8691
8692#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8693inline std::string message_digest(const std::string &s, const EVP_MD *algo)
8694{
8695 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
8696 EVP_MD_CTX_new(), EVP_MD_CTX_free);
8697
8698 unsigned int hash_length = 0;
8699 unsigned char hash[EVP_MAX_MD_SIZE];
8700
8701 EVP_DigestInit_ex(context.get(), algo, nullptr);
8702 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
8703 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
8704
8705 std::stringstream ss;
8706 for (auto i = 0u; i < hash_length; ++i)
8707 {
8708 ss << std::hex << std::setw(2) << std::setfill('0')
8709 << static_cast<unsigned int>(hash[i]);
8710 }
8711
8712 return ss.str();
8713}
8714
8715inline std::string MD5(const std::string &s)
8716{
8717 return message_digest(s, EVP_md5());
8718}
8719
8720inline std::string SHA_256(const std::string &s)
8721{
8722 return message_digest(s, EVP_sha256());
8723}
8724
8725inline std::string SHA_512(const std::string &s)
8726{
8727 return message_digest(s, EVP_sha512());
8728}
8729
8730inline std::pair<std::string, std::string> make_digest_authentication_header(
8731 const Request &req, const std::map<std::string, std::string> &auth,
8732 size_t cnonce_count, const std::string &cnonce, const std::string &username,
8733 const std::string &password, bool is_proxy = false)
8734{
8735 std::string nc;
8736 {
8737 std::stringstream ss;
8738 ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
8739 nc = ss.str();
8740 }
8741
8742 std::string qop;
8743 if (auth.find("qop") != auth.end())
8744 {
8745 qop = auth.at("qop");
8746 if (qop.find("auth-int") != std::string::npos)
8747 {
8748 qop = "auth-int";
8749 }
8750 else if (qop.find("auth") != std::string::npos)
8751 {
8752 qop = "auth";
8753 }
8754 else
8755 {
8756 qop.clear();
8757 }
8758 }
8759
8760 std::string algo = "MD5";
8761 if (auth.find("algorithm") != auth.end())
8762 {
8763 algo = auth.at("algorithm");
8764 }
8765
8766 std::string response;
8767 {
8768 auto H = algo == "SHA-256" ? detail::SHA_256
8769 : algo == "SHA-512" ? detail::SHA_512
8770 : detail::MD5;
8771
8772 auto A1 = username + ":" + auth.at("realm") + ":" + password;
8773
8774 auto A2 = req.method + ":" + req.path;
8775 if (qop == "auth-int")
8776 {
8777 A2 += ":" + H(req.body);
8778 }
8779
8780 if (qop.empty())
8781 {
8782 response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
8783 }
8784 else
8785 {
8786 response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
8787 ":" + qop + ":" + H(A2));
8788 }
8789 }
8790
8791 auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
8792
8793 auto field = "Digest username=\"" + username + "\", realm=\"" +
8794 auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
8795 "\", uri=\"" + req.path + "\", algorithm=" + algo +
8796 (qop.empty() ? ", response=\""
8797 : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
8798 cnonce + "\", response=\"") +
8799 response + "\"" +
8800 (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
8801
8802 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
8803 return std::make_pair(key, field);
8804}
8805
8806inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock)
8807{
8808 detail::set_nonblocking(sock, true);
8809 auto se = detail::scope_exit([&]()
8810 {
8811 detail::set_nonblocking(sock, false);
8812 });
8813
8814 char buf[1];
8815 return !SSL_peek(ssl, buf, 1) &&
8816 SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
8817}
8818
8819#ifdef _WIN32
8820// NOTE: This code came up with the following stackoverflow post:
8821// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
8822inline bool load_system_certs_on_windows(X509_STORE *store)
8823{
8824 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
8825 if (!hStore)
8826 {
8827 return false;
8828 }
8829
8830 auto result = false;
8831 PCCERT_CONTEXT pContext = NULL;
8832 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
8833 nullptr)
8834 {
8835 auto encoded_cert =
8836 static_cast<const unsigned char *>(pContext->pbCertEncoded);
8837
8838 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
8839 if (x509)
8840 {
8841 X509_STORE_add_cert(store, x509);
8842 X509_free(x509);
8843 result = true;
8844 }
8845 }
8846
8847 CertFreeCertificateContext(pContext);
8848 CertCloseStore(hStore, 0);
8849
8850 return result;
8851}
8852#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
8853template <typename T>
8854using CFObjectPtr =
8855 std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
8856
8857inline void cf_object_ptr_deleter(CFTypeRef obj)
8858{
8859 if (obj)
8860 {
8861 CFRelease(obj);
8862 }
8863}
8864
8865inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs)
8866{
8867 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
8868 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
8869 kCFBooleanTrue
8870 };
8871
8872 CFObjectPtr<CFDictionaryRef> query(
8873 CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
8874 sizeof(keys) / sizeof(keys[0]),
8875 &kCFTypeDictionaryKeyCallBacks,
8876 &kCFTypeDictionaryValueCallBacks),
8877 cf_object_ptr_deleter);
8878
8879 if (!query)
8880 {
8881 return false;
8882 }
8883
8884 CFTypeRef security_items = nullptr;
8885 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
8886 CFArrayGetTypeID() != CFGetTypeID(security_items))
8887 {
8888 return false;
8889 }
8890
8891 certs.reset(reinterpret_cast<CFArrayRef>(security_items));
8892 return true;
8893}
8894
8895inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs)
8896{
8897 CFArrayRef root_security_items = nullptr;
8898 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess)
8899 {
8900 return false;
8901 }
8902
8903 certs.reset(root_security_items);
8904 return true;
8905}
8906
8907inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store)
8908{
8909 auto result = false;
8910 for (auto i = 0; i < CFArrayGetCount(certs); ++i)
8911 {
8912 const auto cert = reinterpret_cast<const __SecCertificate *>(
8913 CFArrayGetValueAtIndex(certs, i));
8914
8915 if (SecCertificateGetTypeID() != CFGetTypeID(cert))
8916 {
8917 continue;
8918 }
8919
8920 CFDataRef cert_data = nullptr;
8921 if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
8922 errSecSuccess)
8923 {
8924 continue;
8925 }
8926
8927 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
8928
8929 auto encoded_cert = static_cast<const unsigned char *>(
8930 CFDataGetBytePtr(cert_data_ptr.get()));
8931
8932 auto x509 =
8933 d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
8934
8935 if (x509)
8936 {
8937 X509_STORE_add_cert(store, x509);
8938 X509_free(x509);
8939 result = true;
8940 }
8941 }
8942
8943 return result;
8944}
8945
8946inline bool load_system_certs_on_macos(X509_STORE *store)
8947{
8948 auto result = false;
8949 CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
8950 if (retrieve_certs_from_keychain(certs) && certs)
8951 {
8952 result = add_certs_to_x509_store(certs.get(), store);
8953 }
8954
8955 if (retrieve_root_certs_from_keychain(certs) && certs)
8956 {
8957 result = add_certs_to_x509_store(certs.get(), store) || result;
8958 }
8959
8960 return result;
8961}
8962#endif // _WIN32
8963#endif // CPPHTTPLIB_OPENSSL_SUPPORT
8964
8965#ifdef _WIN32
8966class WSInit
8967{
8968 public:
8969 WSInit()
8970 {
8971 WSADATA wsaData;
8972 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
8973 }
8974
8975 ~WSInit()
8976 {
8977 if (is_valid_) WSACleanup();
8978 }
8979
8980 bool is_valid_ = false;
8981};
8982
8983static WSInit wsinit_;
8984#endif
8985
8986inline bool parse_www_authenticate(const Response &res,
8987 std::map<std::string, std::string> &auth,
8988 bool is_proxy)
8989{
8990 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
8991 if (res.has_header(auth_key))
8992 {
8993 thread_local auto re =
8994 std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
8995 auto s = res.get_header_value(auth_key);
8996 auto pos = s.find(' ');
8997 if (pos != std::string::npos)
8998 {
8999 auto type = s.substr(0, pos);
9000 if (type == "Basic")
9001 {
9002 return false;
9003 }
9004 else if (type == "Digest")
9005 {
9006 s = s.substr(pos + 1);
9007 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
9008 for (auto i = beg; i != std::sregex_iterator(); ++i)
9009 {
9010 const auto &m = *i;
9011 auto key = s.substr(static_cast<size_t>(m.position(1)),
9012 static_cast<size_t>(m.length(1)));
9013 auto val = m.length(2) > 0
9014 ? s.substr(static_cast<size_t>(m.position(2)),
9015 static_cast<size_t>(m.length(2)))
9016 : s.substr(static_cast<size_t>(m.position(3)),
9017 static_cast<size_t>(m.length(3)));
9018 auth[std::move(key)] = std::move(val);
9019 }
9020 return true;
9021 }
9022 }
9023 }
9024 return false;
9025}
9026
9027class ContentProviderAdapter
9028{
9029 public:
9030 explicit ContentProviderAdapter(
9031 ContentProviderWithoutLength &&content_provider)
9032 : content_provider_(std::move(content_provider)) {}
9033
9034 bool operator()(size_t offset, size_t, DataSink &sink)
9035 {
9036 return content_provider_(offset, sink);
9037 }
9038
9039 private:
9040 ContentProviderWithoutLength content_provider_;
9041};
9042
9043// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
9044namespace fields
9045{
9046
9047inline bool is_token_char(char c)
9048{
9049 return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
9050 c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
9051 c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
9052}
9053
9054inline bool is_token(const std::string &s)
9055{
9056 if (s.empty())
9057 {
9058 return false;
9059 }
9060 for (auto c : s)
9061 {
9062 if (!is_token_char(c))
9063 {
9064 return false;
9065 }
9066 }
9067 return true;
9068}
9069
9070inline bool is_field_name(const std::string &s)
9071{
9072 return is_token(s);
9073}
9074
9075inline bool is_vchar(char c)
9076{
9077 return c >= 33 && c <= 126;
9078}
9079
9080inline bool is_obs_text(char c)
9081{
9082 return 128 <= static_cast<unsigned char>(c);
9083}
9084
9085inline bool is_field_vchar(char c)
9086{
9087 return is_vchar(c) || is_obs_text(c);
9088}
9089
9090inline bool is_field_content(const std::string &s)
9091{
9092 if (s.empty())
9093 {
9094 return true;
9095 }
9096
9097 if (s.size() == 1)
9098 {
9099 return is_field_vchar(s[0]);
9100 }
9101 else if (s.size() == 2)
9102 {
9103 return is_field_vchar(s[0]) && is_field_vchar(s[1]);
9104 }
9105 else
9106 {
9107 size_t i = 0;
9108
9109 if (!is_field_vchar(s[i]))
9110 {
9111 return false;
9112 }
9113 i++;
9114
9115 while (i < s.size() - 1)
9116 {
9117 auto c = s[i++];
9118 if (c == ' ' || c == '\t' || is_field_vchar(c))
9119 {
9120 }
9121 else
9122 {
9123 return false;
9124 }
9125 }
9126
9127 return is_field_vchar(s[i]);
9128 }
9129}
9130
9131inline bool is_field_value(const std::string &s)
9132{
9133 return is_field_content(s);
9134}
9135
9136} // namespace fields
9137
9138} // namespace detail
9139
9140inline const char *status_message(int status)
9141{
9142 switch (status)
9143 {
9144 case StatusCode::Continue_100:
9145 return "Continue";
9146 case StatusCode::SwitchingProtocol_101:
9147 return "Switching Protocol";
9148 case StatusCode::Processing_102:
9149 return "Processing";
9150 case StatusCode::EarlyHints_103:
9151 return "Early Hints";
9152 case StatusCode::OK_200:
9153 return "OK";
9154 case StatusCode::Created_201:
9155 return "Created";
9156 case StatusCode::Accepted_202:
9157 return "Accepted";
9158 case StatusCode::NonAuthoritativeInformation_203:
9159 return "Non-Authoritative Information";
9160 case StatusCode::NoContent_204:
9161 return "No Content";
9162 case StatusCode::ResetContent_205:
9163 return "Reset Content";
9164 case StatusCode::PartialContent_206:
9165 return "Partial Content";
9166 case StatusCode::MultiStatus_207:
9167 return "Multi-Status";
9168 case StatusCode::AlreadyReported_208:
9169 return "Already Reported";
9170 case StatusCode::IMUsed_226:
9171 return "IM Used";
9172 case StatusCode::MultipleChoices_300:
9173 return "Multiple Choices";
9174 case StatusCode::MovedPermanently_301:
9175 return "Moved Permanently";
9176 case StatusCode::Found_302:
9177 return "Found";
9178 case StatusCode::SeeOther_303:
9179 return "See Other";
9180 case StatusCode::NotModified_304:
9181 return "Not Modified";
9182 case StatusCode::UseProxy_305:
9183 return "Use Proxy";
9184 case StatusCode::unused_306:
9185 return "unused";
9186 case StatusCode::TemporaryRedirect_307:
9187 return "Temporary Redirect";
9188 case StatusCode::PermanentRedirect_308:
9189 return "Permanent Redirect";
9190 case StatusCode::BadRequest_400:
9191 return "Bad Request";
9192 case StatusCode::Unauthorized_401:
9193 return "Unauthorized";
9194 case StatusCode::PaymentRequired_402:
9195 return "Payment Required";
9196 case StatusCode::Forbidden_403:
9197 return "Forbidden";
9198 case StatusCode::NotFound_404:
9199 return "Not Found";
9200 case StatusCode::MethodNotAllowed_405:
9201 return "Method Not Allowed";
9202 case StatusCode::NotAcceptable_406:
9203 return "Not Acceptable";
9204 case StatusCode::ProxyAuthenticationRequired_407:
9205 return "Proxy Authentication Required";
9206 case StatusCode::RequestTimeout_408:
9207 return "Request Timeout";
9208 case StatusCode::Conflict_409:
9209 return "Conflict";
9210 case StatusCode::Gone_410:
9211 return "Gone";
9212 case StatusCode::LengthRequired_411:
9213 return "Length Required";
9214 case StatusCode::PreconditionFailed_412:
9215 return "Precondition Failed";
9216 case StatusCode::PayloadTooLarge_413:
9217 return "Payload Too Large";
9218 case StatusCode::UriTooLong_414:
9219 return "URI Too Long";
9220 case StatusCode::UnsupportedMediaType_415:
9221 return "Unsupported Media Type";
9222 case StatusCode::RangeNotSatisfiable_416:
9223 return "Range Not Satisfiable";
9224 case StatusCode::ExpectationFailed_417:
9225 return "Expectation Failed";
9226 case StatusCode::ImATeapot_418:
9227 return "I'm a teapot";
9228 case StatusCode::MisdirectedRequest_421:
9229 return "Misdirected Request";
9230 case StatusCode::UnprocessableContent_422:
9231 return "Unprocessable Content";
9232 case StatusCode::Locked_423:
9233 return "Locked";
9234 case StatusCode::FailedDependency_424:
9235 return "Failed Dependency";
9236 case StatusCode::TooEarly_425:
9237 return "Too Early";
9238 case StatusCode::UpgradeRequired_426:
9239 return "Upgrade Required";
9240 case StatusCode::PreconditionRequired_428:
9241 return "Precondition Required";
9242 case StatusCode::TooManyRequests_429:
9243 return "Too Many Requests";
9244 case StatusCode::RequestHeaderFieldsTooLarge_431:
9245 return "Request Header Fields Too Large";
9246 case StatusCode::UnavailableForLegalReasons_451:
9247 return "Unavailable For Legal Reasons";
9248 case StatusCode::NotImplemented_501:
9249 return "Not Implemented";
9250 case StatusCode::BadGateway_502:
9251 return "Bad Gateway";
9252 case StatusCode::ServiceUnavailable_503:
9253 return "Service Unavailable";
9254 case StatusCode::GatewayTimeout_504:
9255 return "Gateway Timeout";
9256 case StatusCode::HttpVersionNotSupported_505:
9257 return "HTTP Version Not Supported";
9258 case StatusCode::VariantAlsoNegotiates_506:
9259 return "Variant Also Negotiates";
9260 case StatusCode::InsufficientStorage_507:
9261 return "Insufficient Storage";
9262 case StatusCode::LoopDetected_508:
9263 return "Loop Detected";
9264 case StatusCode::NotExtended_510:
9265 return "Not Extended";
9266 case StatusCode::NetworkAuthenticationRequired_511:
9267 return "Network Authentication Required";
9268
9269 default:
9270 case StatusCode::InternalServerError_500:
9271 return "Internal Server Error";
9272 }
9273}
9274
9275inline std::string to_string(const Error error)
9276{
9277 switch (error)
9278 {
9279 case Error::Success:
9280 return "Success (no error)";
9281 case Error::Unknown:
9282 return "Unknown";
9283 case Error::Connection:
9284 return "Could not establish connection";
9285 case Error::BindIPAddress:
9286 return "Failed to bind IP address";
9287 case Error::Read:
9288 return "Failed to read connection";
9289 case Error::Write:
9290 return "Failed to write connection";
9291 case Error::ExceedRedirectCount:
9292 return "Maximum redirect count exceeded";
9293 case Error::Canceled:
9294 return "Connection handling canceled";
9295 case Error::SSLConnection:
9296 return "SSL connection failed";
9297 case Error::SSLLoadingCerts:
9298 return "SSL certificate loading failed";
9299 case Error::SSLServerVerification:
9300 return "SSL server verification failed";
9301 case Error::SSLServerHostnameVerification:
9302 return "SSL server hostname verification failed";
9303 case Error::UnsupportedMultipartBoundaryChars:
9304 return "Unsupported HTTP multipart boundary characters";
9305 case Error::Compression:
9306 return "Compression failed";
9307 case Error::ConnectionTimeout:
9308 return "Connection timed out";
9309 case Error::ProxyConnection:
9310 return "Proxy connection failed";
9311 case Error::ConnectionClosed:
9312 return "Connection closed by server";
9313 case Error::Timeout:
9314 return "Read timeout";
9315 case Error::ResourceExhaustion:
9316 return "Resource exhaustion";
9317 case Error::TooManyFormDataFiles:
9318 return "Too many form data files";
9319 case Error::ExceedMaxPayloadSize:
9320 return "Exceeded maximum payload size";
9321 case Error::ExceedUriMaxLength:
9322 return "Exceeded maximum URI length";
9323 case Error::ExceedMaxSocketDescriptorCount:
9324 return "Exceeded maximum socket descriptor count";
9325 case Error::InvalidRequestLine:
9326 return "Invalid request line";
9327 case Error::InvalidHTTPMethod:
9328 return "Invalid HTTP method";
9329 case Error::InvalidHTTPVersion:
9330 return "Invalid HTTP version";
9331 case Error::InvalidHeaders:
9332 return "Invalid headers";
9333 case Error::MultipartParsing:
9334 return "Multipart parsing failed";
9335 case Error::OpenFile:
9336 return "Failed to open file";
9337 case Error::Listen:
9338 return "Failed to listen on socket";
9339 case Error::GetSockName:
9340 return "Failed to get socket name";
9341 case Error::UnsupportedAddressFamily:
9342 return "Unsupported address family";
9343 case Error::HTTPParsing:
9344 return "HTTP parsing failed";
9345 case Error::InvalidRangeHeader:
9346 return "Invalid Range header";
9347 default:
9348 break;
9349 }
9350
9351 return "Invalid";
9352}
9353
9354inline std::ostream &operator<<(std::ostream &os, const Error &obj)
9355{
9356 os << to_string(obj);
9357 os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
9358 return os;
9359}
9360
9361inline std::string hosted_at(const std::string &hostname)
9362{
9363 std::vector<std::string> addrs;
9364 hosted_at(hostname, addrs);
9365 if (addrs.empty())
9366 {
9367 return std::string();
9368 }
9369 return addrs[0];
9370}
9371
9372inline void hosted_at(const std::string &hostname,
9373 std::vector<std::string> &addrs)
9374{
9375 struct addrinfo hints;
9376 struct addrinfo *result;
9377
9378 memset(&hints, 0, sizeof(struct addrinfo));
9379 hints.ai_family = AF_UNSPEC;
9380 hints.ai_socktype = SOCK_STREAM;
9381 hints.ai_protocol = 0;
9382
9383 if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
9384 &result, 0))
9385 {
9386#if defined __linux__ && !defined __ANDROID__
9387 res_init();
9388#endif
9389 return;
9390 }
9391 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
9392
9393 for (auto rp = result; rp; rp = rp->ai_next)
9394 {
9395 const auto &addr =
9396 *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
9397 std::string ip;
9398 auto dummy = -1;
9399 if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
9400 dummy))
9401 {
9402 addrs.emplace_back(std::move(ip));
9403 }
9404 }
9405}
9406
9407inline std::string encode_uri_component(const std::string &value)
9408{
9409 std::ostringstream escaped;
9410 escaped.fill('0');
9411 escaped << std::hex;
9412
9413 for (auto c : value)
9414 {
9415 if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
9416 c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
9417 c == ')')
9418 {
9419 escaped << c;
9420 }
9421 else
9422 {
9423 escaped << std::uppercase;
9424 escaped << '%' << std::setw(2)
9425 << static_cast<int>(static_cast<unsigned char>(c));
9426 escaped << std::nouppercase;
9427 }
9428 }
9429
9430 return escaped.str();
9431}
9432
9433inline std::string encode_uri(const std::string &value)
9434{
9435 std::ostringstream escaped;
9436 escaped.fill('0');
9437 escaped << std::hex;
9438
9439 for (auto c : value)
9440 {
9441 if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
9442 c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
9443 c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
9444 c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#')
9445 {
9446 escaped << c;
9447 }
9448 else
9449 {
9450 escaped << std::uppercase;
9451 escaped << '%' << std::setw(2)
9452 << static_cast<int>(static_cast<unsigned char>(c));
9453 escaped << std::nouppercase;
9454 }
9455 }
9456
9457 return escaped.str();
9458}
9459
9460inline std::string decode_uri_component(const std::string &value)
9461{
9462 std::string result;
9463
9464 for (size_t i = 0; i < value.size(); i++)
9465 {
9466 if (value[i] == '%' && i + 2 < value.size())
9467 {
9468 auto val = 0;
9469 if (detail::from_hex_to_i(value, i + 1, 2, val))
9470 {
9471 result += static_cast<char>(val);
9472 i += 2;
9473 }
9474 else
9475 {
9476 result += value[i];
9477 }
9478 }
9479 else
9480 {
9481 result += value[i];
9482 }
9483 }
9484
9485 return result;
9486}
9487
9488inline std::string decode_uri(const std::string &value)
9489{
9490 std::string result;
9491
9492 for (size_t i = 0; i < value.size(); i++)
9493 {
9494 if (value[i] == '%' && i + 2 < value.size())
9495 {
9496 auto val = 0;
9497 if (detail::from_hex_to_i(value, i + 1, 2, val))
9498 {
9499 result += static_cast<char>(val);
9500 i += 2;
9501 }
9502 else
9503 {
9504 result += value[i];
9505 }
9506 }
9507 else
9508 {
9509 result += value[i];
9510 }
9511 }
9512
9513 return result;
9514}
9515
9516inline std::string encode_path_component(const std::string &component)
9517{
9518 std::string result;
9519 result.reserve(component.size() * 3);
9520
9521 for (size_t i = 0; i < component.size(); i++)
9522 {
9523 auto c = static_cast<unsigned char>(component[i]);
9524
9525 // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
9526 if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~')
9527 {
9528 result += static_cast<char>(c);
9529 }
9530 // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
9531 // "," / ";" / "="
9532 else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
9533 c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
9534 c == '=')
9535 {
9536 result += static_cast<char>(c);
9537 }
9538 // Colon is allowed in path segments except first segment
9539 else if (c == ':')
9540 {
9541 result += static_cast<char>(c);
9542 }
9543 // @ is allowed in path
9544 else if (c == '@')
9545 {
9546 result += static_cast<char>(c);
9547 }
9548 else
9549 {
9550 result += '%';
9551 char hex[3];
9552 snprintf(hex, sizeof(hex), "%02X", c);
9553 result.append(hex, 2);
9554 }
9555 }
9556 return result;
9557}
9558
9559inline std::string decode_path_component(const std::string &component)
9560{
9561 std::string result;
9562 result.reserve(component.size());
9563
9564 for (size_t i = 0; i < component.size(); i++)
9565 {
9566 if (component[i] == '%' && i + 1 < component.size())
9567 {
9568 if (component[i + 1] == 'u')
9569 {
9570 // Unicode %uXXXX encoding
9571 auto val = 0;
9572 if (detail::from_hex_to_i(component, i + 2, 4, val))
9573 {
9574 // 4 digits Unicode codes
9575 char buff[4];
9576 size_t len = detail::to_utf8(val, buff);
9577 if (len > 0)
9578 {
9579 result.append(buff, len);
9580 }
9581 i += 5; // 'u0000'
9582 }
9583 else
9584 {
9585 result += component[i];
9586 }
9587 }
9588 else
9589 {
9590 // Standard %XX encoding
9591 auto val = 0;
9592 if (detail::from_hex_to_i(component, i + 1, 2, val))
9593 {
9594 // 2 digits hex codes
9595 result += static_cast<char>(val);
9596 i += 2; // 'XX'
9597 }
9598 else
9599 {
9600 result += component[i];
9601 }
9602 }
9603 }
9604 else
9605 {
9606 result += component[i];
9607 }
9608 }
9609 return result;
9610}
9611
9612inline std::string encode_query_component(const std::string &component,
9613 bool space_as_plus)
9614{
9615 std::string result;
9616 result.reserve(component.size() * 3);
9617
9618 for (size_t i = 0; i < component.size(); i++)
9619 {
9620 auto c = static_cast<unsigned char>(component[i]);
9621
9622 // Unreserved characters per RFC 3986
9623 if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~')
9624 {
9625 result += static_cast<char>(c);
9626 }
9627 // Space handling
9628 else if (c == ' ')
9629 {
9630 if (space_as_plus)
9631 {
9632 result += '+';
9633 }
9634 else
9635 {
9636 result += "%20";
9637 }
9638 }
9639 // Plus sign handling
9640 else if (c == '+')
9641 {
9642 if (space_as_plus)
9643 {
9644 result += "%2B";
9645 }
9646 else
9647 {
9648 result += static_cast<char>(c);
9649 }
9650 }
9651 // Query-safe sub-delimiters (excluding & and = which are query delimiters)
9652 else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
9653 c == '*' || c == ',' || c == ';')
9654 {
9655 result += static_cast<char>(c);
9656 }
9657 // Colon and @ are allowed in query
9658 else if (c == ':' || c == '@')
9659 {
9660 result += static_cast<char>(c);
9661 }
9662 // Forward slash is allowed in query values
9663 else if (c == '/')
9664 {
9665 result += static_cast<char>(c);
9666 }
9667 // Question mark is allowed in query values (after first ?)
9668 else if (c == '?')
9669 {
9670 result += static_cast<char>(c);
9671 }
9672 else
9673 {
9674 result += '%';
9675 char hex[3];
9676 snprintf(hex, sizeof(hex), "%02X", c);
9677 result.append(hex, 2);
9678 }
9679 }
9680 return result;
9681}
9682
9683inline std::string decode_query_component(const std::string &component,
9684 bool plus_as_space)
9685{
9686 std::string result;
9687 result.reserve(component.size());
9688
9689 for (size_t i = 0; i < component.size(); i++)
9690 {
9691 if (component[i] == '%' && i + 2 < component.size())
9692 {
9693 std::string hex = component.substr(i + 1, 2);
9694 char *end;
9695 unsigned long value = std::strtoul(hex.c_str(), &end, 16);
9696 if (end == hex.c_str() + 2)
9697 {
9698 result += static_cast<char>(value);
9699 i += 2;
9700 }
9701 else
9702 {
9703 result += component[i];
9704 }
9705 }
9706 else if (component[i] == '+' && plus_as_space)
9707 {
9708 result += ' '; // + becomes space in form-urlencoded
9709 }
9710 else
9711 {
9712 result += component[i];
9713 }
9714 }
9715 return result;
9716}
9717
9718inline std::string append_query_params(const std::string &path,
9719 const Params &params)
9720{
9721 std::string path_with_query = path;
9722 thread_local const std::regex re("[^?]+\\?.*");
9723 auto delm = std::regex_match(path, re) ? '&' : '?';
9724 path_with_query += delm + detail::params_to_query_str(params);
9725 return path_with_query;
9726}
9727
9728// Header utilities
9729inline std::pair<std::string, std::string>
9731{
9732 std::string field = "bytes=";
9733 auto i = 0;
9734 for (const auto &r : ranges)
9735 {
9736 if (i != 0)
9737 {
9738 field += ", ";
9739 }
9740 if (r.first != -1)
9741 {
9742 field += std::to_string(r.first);
9743 }
9744 field += '-';
9745 if (r.second != -1)
9746 {
9747 field += std::to_string(r.second);
9748 }
9749 i++;
9750 }
9751 return std::make_pair("Range", std::move(field));
9752}
9753
9754inline std::pair<std::string, std::string>
9755make_basic_authentication_header(const std::string &username,
9756 const std::string &password, bool is_proxy)
9757{
9758 auto field = "Basic " + detail::base64_encode(username + ":" + password);
9759 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
9760 return std::make_pair(key, std::move(field));
9761}
9762
9763inline std::pair<std::string, std::string>
9765 bool is_proxy = false)
9766{
9767 auto field = "Bearer " + token;
9768 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
9769 return std::make_pair(key, std::move(field));
9770}
9771
9772// Request implementation
9773inline bool Request::has_header(const std::string &key) const
9774{
9775 return detail::has_header(headers, key);
9776}
9777
9778inline std::string Request::get_header_value(const std::string &key,
9779 const char *def, size_t id) const
9780{
9781 return detail::get_header_value(headers, key, def, id);
9782}
9783
9784inline size_t Request::get_header_value_count(const std::string &key) const
9785{
9786 auto r = headers.equal_range(key);
9787 return static_cast<size_t>(std::distance(r.first, r.second));
9788}
9789
9790inline void Request::set_header(const std::string &key,
9791 const std::string &val)
9792{
9793 if (detail::fields::is_field_name(key) &&
9794 detail::fields::is_field_value(val))
9795 {
9796 headers.emplace(key, val);
9797 }
9798}
9799
9800inline bool Request::has_trailer(const std::string &key) const
9801{
9802 return trailers.find(key) != trailers.end();
9803}
9804
9805inline std::string Request::get_trailer_value(const std::string &key,
9806 size_t id) const
9807{
9808 auto rng = trailers.equal_range(key);
9809 auto it = rng.first;
9810 std::advance(it, static_cast<ssize_t>(id));
9811 if (it != rng.second)
9812 {
9813 return it->second;
9814 }
9815 return std::string();
9816}
9817
9818inline size_t Request::get_trailer_value_count(const std::string &key) const
9819{
9820 auto r = trailers.equal_range(key);
9821 return static_cast<size_t>(std::distance(r.first, r.second));
9822}
9823
9824inline bool Request::has_param(const std::string &key) const
9825{
9826 return params.find(key) != params.end();
9827}
9828
9829inline std::string Request::get_param_value(const std::string &key,
9830 size_t id) const
9831{
9832 auto rng = params.equal_range(key);
9833 auto it = rng.first;
9834 std::advance(it, static_cast<ssize_t>(id));
9835 if (it != rng.second)
9836 {
9837 return it->second;
9838 }
9839 return std::string();
9840}
9841
9842inline size_t Request::get_param_value_count(const std::string &key) const
9843{
9844 auto r = params.equal_range(key);
9845 return static_cast<size_t>(std::distance(r.first, r.second));
9846}
9847
9848inline bool Request::is_multipart_form_data() const
9849{
9850 const auto &content_type = get_header_value("Content-Type");
9851 return !content_type.rfind("multipart/form-data", 0);
9852}
9853
9854// Multipart FormData implementation
9855inline std::string MultipartFormData::get_field(const std::string &key,
9856 size_t id) const
9857{
9858 auto rng = fields.equal_range(key);
9859 auto it = rng.first;
9860 std::advance(it, static_cast<ssize_t>(id));
9861 if (it != rng.second)
9862 {
9863 return it->second.content;
9864 }
9865 return std::string();
9866}
9867
9868inline std::vector<std::string>
9869MultipartFormData::get_fields(const std::string &key) const
9870{
9871 std::vector<std::string> values;
9872 auto rng = fields.equal_range(key);
9873 for (auto it = rng.first; it != rng.second; it++)
9874 {
9875 values.push_back(it->second.content);
9876 }
9877 return values;
9878}
9879
9880inline bool MultipartFormData::has_field(const std::string &key) const
9881{
9882 return fields.find(key) != fields.end();
9883}
9884
9885inline size_t MultipartFormData::get_field_count(const std::string &key) const
9886{
9887 auto r = fields.equal_range(key);
9888 return static_cast<size_t>(std::distance(r.first, r.second));
9889}
9890
9891inline FormData MultipartFormData::get_file(const std::string &key,
9892 size_t id) const
9893{
9894 auto rng = files.equal_range(key);
9895 auto it = rng.first;
9896 std::advance(it, static_cast<ssize_t>(id));
9897 if (it != rng.second)
9898 {
9899 return it->second;
9900 }
9901 return FormData();
9902}
9903
9904inline std::vector<FormData>
9905MultipartFormData::get_files(const std::string &key) const
9906{
9907 std::vector<FormData> values;
9908 auto rng = files.equal_range(key);
9909 for (auto it = rng.first; it != rng.second; it++)
9910 {
9911 values.push_back(it->second);
9912 }
9913 return values;
9914}
9915
9916inline bool MultipartFormData::has_file(const std::string &key) const
9917{
9918 return files.find(key) != files.end();
9919}
9920
9921inline size_t MultipartFormData::get_file_count(const std::string &key) const
9922{
9923 auto r = files.equal_range(key);
9924 return static_cast<size_t>(std::distance(r.first, r.second));
9925}
9926
9927// Response implementation
9928inline bool Response::has_header(const std::string &key) const
9929{
9930 return headers.find(key) != headers.end();
9931}
9932
9933inline std::string Response::get_header_value(const std::string &key,
9934 const char *def,
9935 size_t id) const
9936{
9937 return detail::get_header_value(headers, key, def, id);
9938}
9939
9940inline size_t Response::get_header_value_count(const std::string &key) const
9941{
9942 auto r = headers.equal_range(key);
9943 return static_cast<size_t>(std::distance(r.first, r.second));
9944}
9945
9946inline void Response::set_header(const std::string &key,
9947 const std::string &val)
9948{
9949 if (detail::fields::is_field_name(key) &&
9950 detail::fields::is_field_value(val))
9951 {
9952 headers.emplace(key, val);
9953 }
9954}
9955inline bool Response::has_trailer(const std::string &key) const
9956{
9957 return trailers.find(key) != trailers.end();
9958}
9959
9960inline std::string Response::get_trailer_value(const std::string &key,
9961 size_t id) const
9962{
9963 auto rng = trailers.equal_range(key);
9964 auto it = rng.first;
9965 std::advance(it, static_cast<ssize_t>(id));
9966 if (it != rng.second)
9967 {
9968 return it->second;
9969 }
9970 return std::string();
9971}
9972
9973inline size_t Response::get_trailer_value_count(const std::string &key) const
9974{
9975 auto r = trailers.equal_range(key);
9976 return static_cast<size_t>(std::distance(r.first, r.second));
9977}
9978
9979inline void Response::set_redirect(const std::string &url, int stat)
9980{
9981 if (detail::fields::is_field_value(url))
9982 {
9983 set_header("Location", url);
9984 if (300 <= stat && stat < 400)
9985 {
9986 this->status = stat;
9987 }
9988 else
9989 {
9990 this->status = StatusCode::Found_302;
9991 }
9992 }
9993}
9994
9995inline void Response::set_content(const char *s, size_t n,
9996 const std::string &content_type)
9997{
9998 body.assign(s, n);
9999
10000 auto rng = headers.equal_range("Content-Type");
10001 headers.erase(rng.first, rng.second);
10002 set_header("Content-Type", content_type);
10003}
10004
10005inline void Response::set_content(const std::string &s,
10006 const std::string &content_type)
10007{
10008 set_content(s.data(), s.size(), content_type);
10009}
10010
10011inline void Response::set_content(std::string &&s,
10012 const std::string &content_type)
10013{
10014 body = std::move(s);
10015
10016 auto rng = headers.equal_range("Content-Type");
10017 headers.erase(rng.first, rng.second);
10018 set_header("Content-Type", content_type);
10019}
10020
10021inline void Response::set_content_provider(
10022 size_t in_length, const std::string &content_type, ContentProvider provider,
10023 ContentProviderResourceReleaser resource_releaser)
10024{
10025 set_header("Content-Type", content_type);
10026 content_length_ = in_length;
10027 if (in_length > 0)
10028 {
10029 content_provider_ = std::move(provider);
10030 }
10031 content_provider_resource_releaser_ = std::move(resource_releaser);
10032 is_chunked_content_provider_ = false;
10033}
10034
10035inline void Response::set_content_provider(
10036 const std::string &content_type, ContentProviderWithoutLength provider,
10037 ContentProviderResourceReleaser resource_releaser)
10038{
10039 set_header("Content-Type", content_type);
10040 content_length_ = 0;
10041 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
10042 content_provider_resource_releaser_ = std::move(resource_releaser);
10043 is_chunked_content_provider_ = false;
10044}
10045
10046inline void Response::set_chunked_content_provider(
10047 const std::string &content_type, ContentProviderWithoutLength provider,
10048 ContentProviderResourceReleaser resource_releaser)
10049{
10050 set_header("Content-Type", content_type);
10051 content_length_ = 0;
10052 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
10053 content_provider_resource_releaser_ = std::move(resource_releaser);
10054 is_chunked_content_provider_ = true;
10055}
10056
10057inline void Response::set_file_content(const std::string &path,
10058 const std::string &content_type)
10059{
10060 file_content_path_ = path;
10061 file_content_content_type_ = content_type;
10062}
10063
10064inline void Response::set_file_content(const std::string &path)
10065{
10066 file_content_path_ = path;
10067}
10068
10069// Result implementation
10070inline bool Result::has_request_header(const std::string &key) const
10071{
10072 return request_headers_.find(key) != request_headers_.end();
10073}
10074
10075inline std::string Result::get_request_header_value(const std::string &key,
10076 const char *def,
10077 size_t id) const
10078{
10079 return detail::get_header_value(request_headers_, key, def, id);
10080}
10081
10082inline size_t
10083Result::get_request_header_value_count(const std::string &key) const
10084{
10085 auto r = request_headers_.equal_range(key);
10086 return static_cast<size_t>(std::distance(r.first, r.second));
10087}
10088
10089// Stream implementation
10090inline ssize_t Stream::write(const char *ptr)
10091{
10092 return write(ptr, strlen(ptr));
10093}
10094
10095inline ssize_t Stream::write(const std::string &s)
10096{
10097 return write(s.data(), s.size());
10098}
10099
10100// BodyReader implementation
10101inline ssize_t detail::BodyReader::read(char *buf, size_t len)
10102{
10103 if (!stream)
10104 {
10105 last_error = Error::Connection;
10106 return -1;
10107 }
10108 if (eof)
10109 {
10110 return 0;
10111 }
10112
10113 if (!chunked)
10114 {
10115 // Content-Length based reading
10116 if (bytes_read >= content_length)
10117 {
10118 eof = true;
10119 return 0;
10120 }
10121
10122 auto remaining = content_length - bytes_read;
10123 auto to_read = (std::min)(len, remaining);
10124 auto n = stream->read(buf, to_read);
10125
10126 if (n < 0)
10127 {
10128 last_error = stream->get_error();
10129 if (last_error == Error::Success)
10130 {
10131 last_error = Error::Read;
10132 }
10133 eof = true;
10134 return n;
10135 }
10136 if (n == 0)
10137 {
10138 // Unexpected EOF before content_length
10139 last_error = stream->get_error();
10140 if (last_error == Error::Success)
10141 {
10142 last_error = Error::Read;
10143 }
10144 eof = true;
10145 return 0;
10146 }
10147
10148 bytes_read += static_cast<size_t>(n);
10149 if (bytes_read >= content_length)
10150 {
10151 eof = true;
10152 }
10153 return n;
10154 }
10155
10156 // Chunked transfer encoding: delegate to shared decoder instance.
10157 if (!chunked_decoder)
10158 {
10159 chunked_decoder.reset(new ChunkedDecoder(*stream));
10160 }
10161
10162 size_t chunk_offset = 0;
10163 size_t chunk_total = 0;
10164 auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
10165 if (n < 0)
10166 {
10167 last_error = stream->get_error();
10168 if (last_error == Error::Success)
10169 {
10170 last_error = Error::Read;
10171 }
10172 eof = true;
10173 return n;
10174 }
10175
10176 if (n == 0)
10177 {
10178 // Final chunk observed. Leave trailer parsing to the caller (StreamHandle).
10179 eof = true;
10180 return 0;
10181 }
10182
10183 bytes_read += static_cast<size_t>(n);
10184 return n;
10185}
10186
10187namespace detail
10188{
10189
10190inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
10191 time_t timeout_sec, time_t timeout_usec,
10192 time_t &actual_timeout_sec,
10193 time_t &actual_timeout_usec)
10194{
10195 auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
10196
10197 auto actual_timeout_msec =
10198 (std::min)(max_timeout_msec - duration_msec, timeout_msec);
10199
10200 if (actual_timeout_msec < 0)
10201 {
10202 actual_timeout_msec = 0;
10203 }
10204
10205 actual_timeout_sec = actual_timeout_msec / 1000;
10206 actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
10207}
10208
10209// Socket stream implementation
10210inline SocketStream::SocketStream(
10211 socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
10212 time_t write_timeout_sec, time_t write_timeout_usec,
10213 time_t max_timeout_msec,
10214 std::chrono::time_point<std::chrono::steady_clock> start_time)
10215 : sock_(sock), read_timeout_sec_(read_timeout_sec),
10216 read_timeout_usec_(read_timeout_usec),
10217 write_timeout_sec_(write_timeout_sec),
10218 write_timeout_usec_(write_timeout_usec),
10219 max_timeout_msec_(max_timeout_msec), start_time_(start_time),
10220 read_buff_(read_buff_size_, 0) {}
10221
10222inline SocketStream::~SocketStream() = default;
10223
10224inline bool SocketStream::is_readable() const
10225{
10226 return read_buff_off_ < read_buff_content_size_;
10227}
10228
10229inline bool SocketStream::wait_readable() const
10230{
10231 if (max_timeout_msec_ <= 0)
10232 {
10233 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10234 }
10235
10236 time_t read_timeout_sec;
10237 time_t read_timeout_usec;
10238 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10239 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10240
10241 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10242}
10243
10244inline bool SocketStream::wait_writable() const
10245{
10246 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
10247 is_socket_alive(sock_);
10248}
10249
10250inline ssize_t SocketStream::read(char *ptr, size_t size)
10251{
10252#ifdef _WIN32
10253 size =
10254 (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
10255#else
10256 size = (std::min)(size,
10257 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
10258#endif
10259
10260 if (read_buff_off_ < read_buff_content_size_)
10261 {
10262 auto remaining_size = read_buff_content_size_ - read_buff_off_;
10263 if (size <= remaining_size)
10264 {
10265 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
10266 read_buff_off_ += size;
10267 return static_cast<ssize_t>(size);
10268 }
10269 else
10270 {
10271 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
10272 read_buff_off_ += remaining_size;
10273 return static_cast<ssize_t>(remaining_size);
10274 }
10275 }
10276
10277 if (!wait_readable())
10278 {
10279 error_ = Error::Timeout;
10280 return -1;
10281 }
10282
10283 read_buff_off_ = 0;
10284 read_buff_content_size_ = 0;
10285
10286 if (size < read_buff_size_)
10287 {
10288 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
10290 if (n <= 0)
10291 {
10292 if (n == 0)
10293 {
10294 error_ = Error::ConnectionClosed;
10295 }
10296 else
10297 {
10298 error_ = Error::Read;
10299 }
10300 return n;
10301 }
10302 else if (n <= static_cast<ssize_t>(size))
10303 {
10304 memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
10305 return n;
10306 }
10307 else
10308 {
10309 memcpy(ptr, read_buff_.data(), size);
10310 read_buff_off_ = size;
10311 read_buff_content_size_ = static_cast<size_t>(n);
10312 return static_cast<ssize_t>(size);
10313 }
10314 }
10315 else
10316 {
10317 auto n = read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
10318 if (n <= 0)
10319 {
10320 if (n == 0)
10321 {
10322 error_ = Error::ConnectionClosed;
10323 }
10324 else
10325 {
10326 error_ = Error::Read;
10327 }
10328 }
10329 return n;
10330 }
10331}
10332
10333inline ssize_t SocketStream::write(const char *ptr, size_t size)
10334{
10335 if (!wait_writable())
10336 {
10337 return -1;
10338 }
10339
10340#if defined(_WIN32) && !defined(_WIN64)
10341 size =
10342 (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
10343#endif
10344
10345 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
10346}
10347
10348inline void SocketStream::get_remote_ip_and_port(std::string &ip,
10349 int &port) const
10350{
10351 return detail::get_remote_ip_and_port(sock_, ip, port);
10352}
10353
10354inline void SocketStream::get_local_ip_and_port(std::string &ip,
10355 int &port) const
10356{
10357 return detail::get_local_ip_and_port(sock_, ip, port);
10358}
10359
10360inline socket_t SocketStream::socket() const
10361{
10362 return sock_;
10363}
10364
10365inline time_t SocketStream::duration() const
10366{
10367 return std::chrono::duration_cast<std::chrono::milliseconds>(
10368 std::chrono::steady_clock::now() - start_time_)
10369 .count();
10370}
10371
10372// Buffer stream implementation
10373inline bool BufferStream::is_readable() const
10374{
10375 return true;
10376}
10377
10378inline bool BufferStream::wait_readable() const
10379{
10380 return true;
10381}
10382
10383inline bool BufferStream::wait_writable() const
10384{
10385 return true;
10386}
10387
10388inline ssize_t BufferStream::read(char *ptr, size_t size)
10389{
10390#if defined(_MSC_VER) && _MSC_VER < 1910
10391 auto len_read = buffer._Copy_s(ptr, size, size, position);
10392#else
10393 auto len_read = buffer.copy(ptr, size, position);
10394#endif
10395 position += static_cast<size_t>(len_read);
10396 return static_cast<ssize_t>(len_read);
10397}
10398
10399inline ssize_t BufferStream::write(const char *ptr, size_t size)
10400{
10401 buffer.append(ptr, size);
10402 return static_cast<ssize_t>(size);
10403}
10404
10405inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
10406 int & /*port*/) const {}
10407
10408inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
10409 int & /*port*/) const {}
10410
10411inline socket_t BufferStream::socket() const
10412{
10413 return 0;
10414}
10415
10416inline time_t BufferStream::duration() const
10417{
10418 return 0;
10419}
10420
10421inline const std::string &BufferStream::get_buffer() const
10422{
10423 return buffer;
10424}
10425
10426inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
10427 : MatcherBase(pattern)
10428{
10429 constexpr const char marker[] = "/:";
10430
10431 // One past the last ending position of a path param substring
10432 std::size_t last_param_end = 0;
10433
10434#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10435 // Needed to ensure that parameter names are unique during matcher
10436 // construction
10437 // If exceptions are disabled, only last duplicate path
10438 // parameter will be set
10439 std::unordered_set<std::string> param_name_set;
10440#endif
10441
10442 while (true)
10443 {
10444 const auto marker_pos = pattern.find(
10445 marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
10446 if (marker_pos == std::string::npos)
10447 {
10448 break;
10449 }
10450
10451 static_fragments_.push_back(
10452 pattern.substr(last_param_end, marker_pos - last_param_end + 1));
10453
10454 const auto param_name_start = marker_pos + str_len(marker);
10455
10456 auto sep_pos = pattern.find(separator, param_name_start);
10457 if (sep_pos == std::string::npos)
10458 {
10459 sep_pos = pattern.length();
10460 }
10461
10462 auto param_name =
10463 pattern.substr(param_name_start, sep_pos - param_name_start);
10464
10465#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10466 if (param_name_set.find(param_name) != param_name_set.cend())
10467 {
10468 std::string msg = "Encountered path parameter '" + param_name +
10469 "' multiple times in route pattern '" + pattern + "'.";
10470 throw std::invalid_argument(msg);
10471 }
10472#endif
10473
10474 param_names_.push_back(std::move(param_name));
10475
10476 last_param_end = sep_pos + 1;
10477 }
10478
10479 if (last_param_end < pattern.length())
10480 {
10481 static_fragments_.push_back(pattern.substr(last_param_end));
10482 }
10483}
10484
10485inline bool PathParamsMatcher::match(Request &request) const
10486{
10487 request.matches = std::smatch();
10488 request.path_params.clear();
10489 request.path_params.reserve(param_names_.size());
10490
10491 // One past the position at which the path matched the pattern last time
10492 std::size_t starting_pos = 0;
10493 for (size_t i = 0; i < static_fragments_.size(); ++i)
10494 {
10495 const auto &fragment = static_fragments_[i];
10496
10497 if (starting_pos + fragment.length() > request.path.length())
10498 {
10499 return false;
10500 }
10501
10502 // Avoid unnecessary allocation by using strncmp instead of substr +
10503 // comparison
10504 if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
10505 fragment.length()) != 0)
10506 {
10507 return false;
10508 }
10509
10510 starting_pos += fragment.length();
10511
10512 // Should only happen when we have a static fragment after a param
10513 // Example: '/users/:id/subscriptions'
10514 // The 'subscriptions' fragment here does not have a corresponding param
10515 if (i >= param_names_.size())
10516 {
10517 continue;
10518 }
10519
10520 auto sep_pos = request.path.find(separator, starting_pos);
10521 if (sep_pos == std::string::npos)
10522 {
10523 sep_pos = request.path.length();
10524 }
10525
10526 const auto &param_name = param_names_[i];
10527
10528 request.path_params.emplace(
10529 param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
10530
10531 // Mark everything up to '/' as matched
10532 starting_pos = sep_pos + 1;
10533 }
10534 // Returns false if the path is longer than the pattern
10535 return starting_pos >= request.path.length();
10536}
10537
10538inline bool RegexMatcher::match(Request &request) const
10539{
10540 request.path_params.clear();
10541 return std::regex_match(request.path, request.matches, regex_);
10542}
10543
10544// Enclose IPv6 address in brackets if needed
10545inline std::string prepare_host_string(const std::string &host)
10546{
10547 // Enclose IPv6 address in brackets (but not if already enclosed)
10548 if (host.find(':') == std::string::npos ||
10549 (!host.empty() && host[0] == '['))
10550 {
10551 // IPv4, hostname, or already bracketed IPv6
10552 return host;
10553 }
10554 else
10555 {
10556 // IPv6 address without brackets
10557 return "[" + host + "]";
10558 }
10559}
10560
10561inline std::string make_host_and_port_string(const std::string &host, int port,
10562 bool is_ssl)
10563{
10564 auto result = prepare_host_string(host);
10565
10566 // Append port if not default
10567 if ((!is_ssl && port == 80) || (is_ssl && port == 443))
10568 {
10569 ; // do nothing
10570 }
10571 else
10572 {
10573 result += ":" + std::to_string(port);
10574 }
10575
10576 return result;
10577}
10578
10579// Create "host:port" string always including port number (for CONNECT method)
10580inline std::string
10581make_host_and_port_string_always_port(const std::string &host, int port)
10582{
10583 return prepare_host_string(host) + ":" + std::to_string(port);
10584}
10585
10586} // namespace detail
10587
10588// HTTP server implementation
10590 : new_task_queue(
10591 []
10592{
10594})
10595{
10596#ifndef _WIN32
10597 signal(SIGPIPE, SIG_IGN);
10598#endif
10599}
10600
10601inline Server::~Server() = default;
10602
10603inline std::unique_ptr<detail::MatcherBase>
10604Server::make_matcher(const std::string &pattern)
10605{
10606 if (pattern.find("/:") != std::string::npos)
10607 {
10608 return detail::make_unique<detail::PathParamsMatcher>(pattern);
10609 }
10610 else
10611 {
10612 return detail::make_unique<detail::RegexMatcher>(pattern);
10613 }
10614}
10615
10616inline Server &Server::Get(const std::string &pattern, Handler handler)
10617{
10618 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10619 return *this;
10620}
10621
10622inline Server &Server::Post(const std::string &pattern, Handler handler)
10623{
10624 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10625 return *this;
10626}
10627
10628inline Server &Server::Post(const std::string &pattern,
10630{
10631 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10632 std::move(handler));
10633 return *this;
10634}
10635
10636inline Server &Server::Put(const std::string &pattern, Handler handler)
10637{
10638 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10639 return *this;
10640}
10641
10642inline Server &Server::Put(const std::string &pattern,
10644{
10645 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10646 std::move(handler));
10647 return *this;
10648}
10649
10650inline Server &Server::Patch(const std::string &pattern, Handler handler)
10651{
10652 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10653 return *this;
10654}
10655
10656inline Server &Server::Patch(const std::string &pattern,
10658{
10659 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10660 std::move(handler));
10661 return *this;
10662}
10663
10664inline Server &Server::Delete(const std::string &pattern, Handler handler)
10665{
10666 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10667 return *this;
10668}
10669
10670inline Server &Server::Delete(const std::string &pattern,
10672{
10673 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
10674 std::move(handler));
10675 return *this;
10676}
10677
10678inline Server &Server::Options(const std::string &pattern, Handler handler)
10679{
10680 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
10681 return *this;
10682}
10683
10684inline bool Server::set_base_dir(const std::string &dir,
10685 const std::string &mount_point)
10686{
10687 return set_mount_point(mount_point, dir);
10688}
10689
10690inline bool Server::set_mount_point(const std::string &mount_point,
10691 const std::string &dir, Headers headers)
10692{
10693 detail::FileStat stat(dir);
10694 if (stat.is_dir())
10695 {
10696 std::string mnt = !mount_point.empty() ? mount_point : "/";
10697 if (!mnt.empty() && mnt[0] == '/')
10698 {
10699 base_dirs_.push_back({std::move(mnt), dir, std::move(headers)});
10700 return true;
10701 }
10702 }
10703 return false;
10704}
10705
10706inline bool Server::remove_mount_point(const std::string &mount_point)
10707{
10708 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it)
10709 {
10710 if (it->mount_point == mount_point)
10711 {
10712 base_dirs_.erase(it);
10713 return true;
10714 }
10715 }
10716 return false;
10717}
10718
10719inline Server &
10721 const std::string &mime)
10722{
10723 file_extension_and_mimetype_map_[ext] = mime;
10724 return *this;
10725}
10726
10727inline Server &Server::set_default_file_mimetype(const std::string &mime)
10728{
10729 default_file_mimetype_ = mime;
10730 return *this;
10731}
10732
10734{
10735 file_request_handler_ = std::move(handler);
10736 return *this;
10737}
10738
10739inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
10740 std::true_type)
10741{
10742 error_handler_ = std::move(handler);
10743 return *this;
10744}
10745
10746inline Server &Server::set_error_handler_core(Handler handler,
10747 std::false_type)
10748{
10749 error_handler_ = [handler](const Request & req, Response & res)
10750 {
10751 handler(req, res);
10753 };
10754 return *this;
10755}
10756
10758{
10759 exception_handler_ = std::move(handler);
10760 return *this;
10761}
10762
10764{
10765 pre_routing_handler_ = std::move(handler);
10766 return *this;
10767}
10768
10770{
10771 post_routing_handler_ = std::move(handler);
10772 return *this;
10773}
10774
10776{
10777 pre_request_handler_ = std::move(handler);
10778 return *this;
10779}
10780
10782{
10783 logger_ = std::move(logger);
10784 return *this;
10785}
10786
10788{
10789 error_logger_ = std::move(error_logger);
10790 return *this;
10791}
10792
10794{
10795 pre_compression_logger_ = std::move(logger);
10796 return *this;
10797}
10798
10799inline Server &
10801{
10802 expect_100_continue_handler_ = std::move(handler);
10803 return *this;
10804}
10805
10807{
10808 address_family_ = family;
10809 return *this;
10810}
10811
10813{
10814 tcp_nodelay_ = on;
10815 return *this;
10816}
10817
10819{
10820 ipv6_v6only_ = on;
10821 return *this;
10822}
10823
10825{
10826 socket_options_ = std::move(socket_options);
10827 return *this;
10828}
10829
10831{
10832 default_headers_ = std::move(headers);
10833 return *this;
10834}
10835
10837 std::function<ssize_t(Stream &, Headers &)> const &writer)
10838{
10839 header_writer_ = writer;
10840 return *this;
10841}
10842
10843inline Server &
10844Server::set_trusted_proxies(const std::vector<std::string> &proxies)
10845{
10846 trusted_proxies_ = proxies;
10847 return *this;
10848}
10849
10851{
10852 keep_alive_max_count_ = count;
10853 return *this;
10854}
10855
10857{
10859 return *this;
10860}
10861
10862inline Server &Server::set_read_timeout(time_t sec, time_t usec)
10863{
10864 read_timeout_sec_ = sec;
10865 read_timeout_usec_ = usec;
10866 return *this;
10867}
10868
10869inline Server &Server::set_write_timeout(time_t sec, time_t usec)
10870{
10871 write_timeout_sec_ = sec;
10872 write_timeout_usec_ = usec;
10873 return *this;
10874}
10875
10876inline Server &Server::set_idle_interval(time_t sec, time_t usec)
10877{
10878 idle_interval_sec_ = sec;
10879 idle_interval_usec_ = usec;
10880 return *this;
10881}
10882
10884{
10885 payload_max_length_ = length;
10886 return *this;
10887}
10888
10889inline bool Server::bind_to_port(const std::string &host, int port,
10890 int socket_flags)
10891{
10892 auto ret = bind_internal(host, port, socket_flags);
10893 if (ret == -1)
10894 {
10895 is_decommissioned = true;
10896 }
10897 return ret >= 0;
10898}
10899inline int Server::bind_to_any_port(const std::string &host, int socket_flags)
10900{
10901 auto ret = bind_internal(host, 0, socket_flags);
10902 if (ret == -1)
10903 {
10904 is_decommissioned = true;
10905 }
10906 return ret;
10907}
10908
10910{
10911 return listen_internal();
10912}
10913
10914inline bool Server::listen(const std::string &host, int port,
10915 int socket_flags)
10916{
10917 return bind_to_port(host, port, socket_flags) && listen_internal();
10918}
10919
10920inline bool Server::is_running() const
10921{
10922 return is_running_;
10923}
10924
10925inline void Server::wait_until_ready() const
10926{
10927 while (!is_running_ && !is_decommissioned)
10928 {
10929 std::this_thread::sleep_for(std::chrono::milliseconds{1});
10930 }
10931}
10932
10933inline void Server::stop()
10934{
10935 if (is_running_)
10936 {
10937 assert(svr_sock_ != INVALID_SOCKET);
10938 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
10939 detail::shutdown_socket(sock);
10941 }
10942 is_decommissioned = false;
10943}
10944
10946{
10947 is_decommissioned = true;
10948}
10949
10950inline bool Server::parse_request_line(const char *s, Request &req) const
10951{
10952 auto len = strlen(s);
10953 if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n')
10954 {
10955 return false;
10956 }
10957 len -= 2;
10958
10959 {
10960 size_t count = 0;
10961
10962 detail::split(s, s + len, ' ', [&](const char *b, const char *e)
10963 {
10964 switch (count)
10965 {
10966 case 0:
10967 req.method = std::string(b, e);
10968 break;
10969 case 1:
10970 req.target = std::string(b, e);
10971 break;
10972 case 2:
10973 req.version = std::string(b, e);
10974 break;
10975 default:
10976 break;
10977 }
10978 count++;
10979 });
10980
10981 if (count != 3)
10982 {
10983 return false;
10984 }
10985 }
10986
10987 thread_local const std::set<std::string> methods
10988 {
10989 "GET", "HEAD", "POST", "PUT", "DELETE",
10990 "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
10991
10992 if (methods.find(req.method) == methods.end())
10993 {
10994 output_error_log(Error::InvalidHTTPMethod, &req);
10995 return false;
10996 }
10997
10998 if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0")
10999 {
11000 output_error_log(Error::InvalidHTTPVersion, &req);
11001 return false;
11002 }
11003
11004 {
11005 // Skip URL fragment
11006 for (size_t i = 0; i < req.target.size(); i++)
11007 {
11008 if (req.target[i] == '#')
11009 {
11010 req.target.erase(i);
11011 break;
11012 }
11013 }
11014
11015 detail::divide(req.target, '?',
11016 [&](const char *lhs_data, std::size_t lhs_size,
11017 const char *rhs_data, std::size_t rhs_size)
11018 {
11019 req.path =
11020 decode_path_component(std::string(lhs_data, lhs_size));
11021 detail::parse_query_text(rhs_data, rhs_size, req.params);
11022 });
11023 }
11024
11025 return true;
11026}
11027
11028inline bool Server::write_response(Stream &strm, bool close_connection,
11029 Request &req, Response &res)
11030{
11031 // NOTE: `req.ranges` should be empty, otherwise it will be applied
11032 // incorrectly to the error content.
11033 req.ranges.clear();
11034 return write_response_core(strm, close_connection, req, res, false);
11035}
11036
11037inline bool Server::write_response_with_content(Stream &strm,
11038 bool close_connection,
11039 const Request &req,
11040 Response &res)
11041{
11042 return write_response_core(strm, close_connection, req, res, true);
11043}
11044
11045inline bool Server::write_response_core(Stream &strm, bool close_connection,
11046 const Request &req, Response &res,
11047 bool need_apply_ranges)
11048{
11049 assert(res.status != -1);
11050
11051 if (400 <= res.status && error_handler_ &&
11052 error_handler_(req, res) == HandlerResponse::Handled)
11053 {
11054 need_apply_ranges = true;
11055 }
11056
11057 std::string content_type;
11058 std::string boundary;
11059 if (need_apply_ranges)
11060 {
11061 apply_ranges(req, res, content_type, boundary);
11062 }
11063
11064 // Prepare additional headers
11065 if (close_connection || req.get_header_value("Connection") == "close" ||
11066 400 <= res.status) // Don't leave connections open after errors
11067 {
11068 res.set_header("Connection", "close");
11069 }
11070 else
11071 {
11072 std::string s = "timeout=";
11073 s += std::to_string(keep_alive_timeout_sec_);
11074 s += ", max=";
11075 s += std::to_string(keep_alive_max_count_);
11076 res.set_header("Keep-Alive", s);
11077 }
11078
11079 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
11080 !res.has_header("Content-Type"))
11081 {
11082 res.set_header("Content-Type", "text/plain");
11083 }
11084
11085 if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
11086 !res.has_header("Content-Length"))
11087 {
11088 res.set_header("Content-Length", "0");
11089 }
11090
11091 if (req.method == "HEAD" && !res.has_header("Accept-Ranges"))
11092 {
11093 res.set_header("Accept-Ranges", "bytes");
11094 }
11095
11096 if (post_routing_handler_)
11097 {
11098 post_routing_handler_(req, res);
11099 }
11100
11101 // Response line and headers
11102 {
11103 detail::BufferStream bstrm;
11104 if (!detail::write_response_line(bstrm, res.status))
11105 {
11106 return false;
11107 }
11108 if (!header_writer_(bstrm, res.headers))
11109 {
11110 return false;
11111 }
11112
11113 // Flush buffer
11114 auto &data = bstrm.get_buffer();
11115 detail::write_data(strm, data.data(), data.size());
11116 }
11117
11118 // Body
11119 auto ret = true;
11120 if (req.method != "HEAD")
11121 {
11122 if (!res.body.empty())
11123 {
11124 if (!detail::write_data(strm, res.body.data(), res.body.size()))
11125 {
11126 ret = false;
11127 }
11128 }
11129 else if (res.content_provider_)
11130 {
11131 if (write_content_with_provider(strm, req, res, boundary, content_type))
11132 {
11133 res.content_provider_success_ = true;
11134 }
11135 else
11136 {
11137 ret = false;
11138 }
11139 }
11140 }
11141
11142 // Log
11143 output_log(req, res);
11144
11145 return ret;
11146}
11147
11148inline bool
11149Server::write_content_with_provider(Stream &strm, const Request &req,
11150 Response &res, const std::string &boundary,
11151 const std::string &content_type)
11152{
11153 auto is_shutting_down = [this]()
11154 {
11155 return this->svr_sock_ == INVALID_SOCKET;
11156 };
11157
11158 if (res.content_length_ > 0)
11159 {
11160 if (req.ranges.empty())
11161 {
11162 return detail::write_content(strm, res.content_provider_, 0,
11163 res.content_length_, is_shutting_down);
11164 }
11165 else if (req.ranges.size() == 1)
11166 {
11167 auto offset_and_length = detail::get_range_offset_and_length(
11168 req.ranges[0], res.content_length_);
11169
11170 return detail::write_content(strm, res.content_provider_,
11171 offset_and_length.first,
11172 offset_and_length.second, is_shutting_down);
11173 }
11174 else
11175 {
11176 return detail::write_multipart_ranges_data(
11177 strm, req, res, boundary, content_type, res.content_length_,
11178 is_shutting_down);
11179 }
11180 }
11181 else
11182 {
11183 if (res.is_chunked_content_provider_)
11184 {
11185 auto type = detail::encoding_type(req, res);
11186
11187 std::unique_ptr<detail::compressor> compressor;
11188 if (type == detail::EncodingType::Gzip)
11189 {
11190#ifdef CPPHTTPLIB_ZLIB_SUPPORT
11191 compressor = detail::make_unique<detail::gzip_compressor>();
11192#endif
11193 }
11194 else if (type == detail::EncodingType::Brotli)
11195 {
11196#ifdef CPPHTTPLIB_BROTLI_SUPPORT
11197 compressor = detail::make_unique<detail::brotli_compressor>();
11198#endif
11199 }
11200 else if (type == detail::EncodingType::Zstd)
11201 {
11202#ifdef CPPHTTPLIB_ZSTD_SUPPORT
11203 compressor = detail::make_unique<detail::zstd_compressor>();
11204#endif
11205 }
11206 else
11207 {
11208 compressor = detail::make_unique<detail::nocompressor>();
11209 }
11210 assert(compressor != nullptr);
11211
11212 return detail::write_content_chunked(strm, res.content_provider_,
11213 is_shutting_down, *compressor);
11214 }
11215 else
11216 {
11217 return detail::write_content_without_length(strm, res.content_provider_,
11218 is_shutting_down);
11219 }
11220 }
11221}
11222
11223inline bool Server::read_content(Stream &strm, Request &req, Response &res)
11224{
11225 FormFields::iterator cur_field;
11226 FormFiles::iterator cur_file;
11227 auto is_text_field = false;
11228 size_t count = 0;
11229 if (read_content_core(
11230 strm, req, res,
11231 // Regular
11232 [&](const char *buf, size_t n)
11233{
11234 // Prevent arithmetic overflow when checking sizes.
11235 // Avoid computing (req.body.size() + n) directly because
11236 // adding two unsigned `size_t` values can wrap around and
11237 // produce a small result instead of indicating overflow.
11238 // Instead, check using subtraction: ensure `n` does not
11239 // exceed the remaining capacity `max_size() - size()`.
11240 if (req.body.size() >= req.body.max_size() ||
11241 n > req.body.max_size() - req.body.size())
11242 {
11243 return false;
11244 }
11245 req.body.append(buf, n);
11246 return true;
11247 },
11248 // Multipart FormData
11249 [&](const FormData & file)
11250 {
11252 {
11253 output_error_log(Error::TooManyFormDataFiles, &req);
11254 return false;
11255 }
11256
11257 if (file.filename.empty())
11258 {
11259 cur_field = req.form.fields.emplace(
11260 file.name, FormField{file.name, file.content, file.headers});
11261 is_text_field = true;
11262 }
11263 else
11264 {
11265 cur_file = req.form.files.emplace(file.name, file);
11266 is_text_field = false;
11267 }
11268 return true;
11269 },
11270 [&](const char *buf, size_t n)
11271 {
11272 if (is_text_field)
11273 {
11274 auto &content = cur_field->second.content;
11275 if (content.size() + n > content.max_size())
11276 {
11277 return false;
11278 }
11279 content.append(buf, n);
11280 }
11281 else
11282 {
11283 auto &content = cur_file->second.content;
11284 if (content.size() + n > content.max_size())
11285 {
11286 return false;
11287 }
11288 content.append(buf, n);
11289 }
11290 return true;
11291 }))
11292 {
11293 const auto &content_type = req.get_header_value("Content-Type");
11294 if (!content_type.find("application/x-www-form-urlencoded"))
11295 {
11297 {
11298 res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
11299 output_error_log(Error::ExceedMaxPayloadSize, &req);
11300 return false;
11301 }
11302 detail::parse_query_text(req.body, req.params);
11303 }
11304 return true;
11305 }
11306 return false;
11307}
11308
11309inline bool Server::read_content_with_content_receiver(
11310 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
11311 FormDataHeader multipart_header, ContentReceiver multipart_receiver)
11312{
11313 return read_content_core(strm, req, res, std::move(receiver),
11314 std::move(multipart_header),
11315 std::move(multipart_receiver));
11316}
11317
11318inline bool Server::read_content_core(
11319 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
11320 FormDataHeader multipart_header, ContentReceiver multipart_receiver) const
11321{
11322 detail::FormDataParser multipart_form_data_parser;
11324
11325 if (req.is_multipart_form_data())
11326 {
11327 const auto &content_type = req.get_header_value("Content-Type");
11328 std::string boundary;
11329 if (!detail::parse_multipart_boundary(content_type, boundary))
11330 {
11331 res.status = StatusCode::BadRequest_400;
11332 output_error_log(Error::MultipartParsing, &req);
11333 return false;
11334 }
11335
11336 multipart_form_data_parser.set_boundary(std::move(boundary));
11337 out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/)
11338 {
11339 return multipart_form_data_parser.parse(buf, n, multipart_header,
11340 multipart_receiver);
11341 };
11342 }
11343 else
11344 {
11345 out = [receiver](const char *buf, size_t n, size_t /*off*/,
11346 size_t /*len*/)
11347 {
11348 return receiver(buf, n);
11349 };
11350 }
11351
11352 // RFC 7230 Section 3.3.3: If this is a request message and none of the above
11353 // are true (no Transfer-Encoding and no Content-Length), then the message
11354 // body length is zero (no message body is present).
11355 //
11356 // For non-SSL builds, peek into the socket to detect clients that send a
11357 // body without a Content-Length header (raw HTTP over TCP). If there is
11358 // pending data that exceeds the configured payload limit, treat this as an
11359 // oversized request and fail early (causing connection close). For SSL
11360 // builds we cannot reliably peek the decrypted application bytes, so keep
11361 // the original behaviour.
11362#if !defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(_WIN32)
11363 if (!req.has_header("Content-Length") &&
11364 !detail::is_chunked_transfer_encoding(req.headers))
11365 {
11366 socket_t s = strm.socket();
11367 if (s != INVALID_SOCKET)
11368 {
11369 // Peek up to payload_max_length_ + 1 bytes. If more than
11370 // payload_max_length_ bytes are pending, reject the request.
11371 size_t to_peek =
11373 ? (std::min)(payload_max_length_ + 1, static_cast<size_t>(4096))
11374 : 1;
11375 std::vector<char> peekbuf(to_peek);
11376 ssize_t n = ::recv(s, peekbuf.data(), to_peek, MSG_PEEK);
11377 if (n > 0 && static_cast<size_t>(n) > payload_max_length_)
11378 {
11379 // Indicate failure so connection will be closed.
11380 return false;
11381 }
11382 }
11383 return true;
11384 }
11385#else
11386 if (!req.has_header("Content-Length") &&
11387 !detail::is_chunked_transfer_encoding(req.headers))
11388 {
11389 return true;
11390 }
11391#endif
11392
11393 if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
11394 out, true))
11395 {
11396 return false;
11397 }
11398
11399 if (req.is_multipart_form_data())
11400 {
11401 if (!multipart_form_data_parser.is_valid())
11402 {
11403 res.status = StatusCode::BadRequest_400;
11404 output_error_log(Error::MultipartParsing, &req);
11405 return false;
11406 }
11407 }
11408
11409 return true;
11410}
11411
11412inline bool Server::handle_file_request(Request &req, Response &res)
11413{
11414 for (const auto &entry : base_dirs_)
11415 {
11416 // Prefix match
11417 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point))
11418 {
11419 std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
11420 if (detail::is_valid_path(sub_path))
11421 {
11422 auto path = entry.base_dir + sub_path;
11423 if (path.back() == '/')
11424 {
11425 path += "index.html";
11426 }
11427
11428 detail::FileStat stat(path);
11429
11430 if (stat.is_dir())
11431 {
11432 res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
11433 return true;
11434 }
11435
11436 if (stat.is_file())
11437 {
11438 for (const auto &kv : entry.headers)
11439 {
11440 res.set_header(kv.first, kv.second);
11441 }
11442
11443 auto etag = detail::compute_etag(stat);
11444 if (!etag.empty())
11445 {
11446 res.set_header("ETag", etag);
11447 }
11448
11449 auto mtime = stat.mtime();
11450
11451 auto last_modified = detail::file_mtime_to_http_date(mtime);
11452 if (!last_modified.empty())
11453 {
11454 res.set_header("Last-Modified", last_modified);
11455 }
11456
11457 if (check_if_not_modified(req, res, etag, mtime))
11458 {
11459 return true;
11460 }
11461
11462 check_if_range(req, etag, mtime);
11463
11464 auto mm = std::make_shared<detail::mmap>(path.c_str());
11465 if (!mm->is_open())
11466 {
11467 output_error_log(Error::OpenFile, &req);
11468 return false;
11469 }
11470
11471 res.set_content_provider(
11472 mm->size(),
11473 detail::find_content_type(path, file_extension_and_mimetype_map_,
11474 default_file_mimetype_),
11475 [mm](size_t offset, size_t length, DataSink & sink) -> bool
11476 {
11477 sink.write(mm->data() + offset, length);
11478 return true;
11479 });
11480
11481 if (req.method != "HEAD" && file_request_handler_)
11482 {
11483 file_request_handler_(req, res);
11484 }
11485
11486 return true;
11487 }
11488 else
11489 {
11490 output_error_log(Error::OpenFile, &req);
11491 }
11492 }
11493 }
11494 }
11495 return false;
11496}
11497
11498inline bool Server::check_if_not_modified(const Request &req, Response &res,
11499 const std::string &etag,
11500 time_t mtime) const
11501{
11502 // Handle conditional GET:
11503 // 1. If-None-Match takes precedence (RFC 9110 Section 13.1.2)
11504 // 2. If-Modified-Since is checked only when If-None-Match is absent
11505 if (req.has_header("If-None-Match"))
11506 {
11507 if (!etag.empty())
11508 {
11509 auto val = req.get_header_value("If-None-Match");
11510
11511 // NOTE: We use exact string matching here. This works correctly
11512 // because our server always generates weak ETags (W/"..."), and
11513 // clients typically send back the same ETag they received.
11514 // RFC 9110 Section 8.8.3.2 allows weak comparison for
11515 // If-None-Match, where W/"x" and "x" would match, but this
11516 // simplified implementation requires exact matches.
11517 auto ret = detail::split_find(val.data(), val.data() + val.size(), ',',
11518 [&](const char *b, const char *e)
11519 {
11520 return std::equal(b, e, "*") ||
11521 std::equal(b, e, etag.begin());
11522 });
11523
11524 if (ret)
11525 {
11526 res.status = StatusCode::NotModified_304;
11527 return true;
11528 }
11529 }
11530 }
11531 else if (req.has_header("If-Modified-Since"))
11532 {
11533 auto val = req.get_header_value("If-Modified-Since");
11534 auto t = detail::parse_http_date(val);
11535
11536 if (t != static_cast<time_t>(-1) && mtime <= t)
11537 {
11538 res.status = StatusCode::NotModified_304;
11539 return true;
11540 }
11541 }
11542 return false;
11543}
11544
11545inline bool Server::check_if_range(Request &req, const std::string &etag,
11546 time_t mtime) const
11547{
11548 // Handle If-Range for partial content requests (RFC 9110
11549 // Section 13.1.5). If-Range is only evaluated when Range header is
11550 // present. If the validator matches, serve partial content; otherwise
11551 // serve full content.
11552 if (!req.ranges.empty() && req.has_header("If-Range"))
11553 {
11554 auto val = req.get_header_value("If-Range");
11555
11556 auto is_valid_range = [&]()
11557 {
11558 if (detail::is_strong_etag(val))
11559 {
11560 // RFC 9110 Section 13.1.5: If-Range requires strong ETag
11561 // comparison.
11562 return (!etag.empty() && val == etag);
11563 }
11564 else if (detail::is_weak_etag(val))
11565 {
11566 // Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
11567 return false;
11568 }
11569 else
11570 {
11571 // HTTP-date comparison
11572 auto t = detail::parse_http_date(val);
11573 return (t != static_cast<time_t>(-1) && mtime <= t);
11574 }
11575 };
11576
11577 if (!is_valid_range())
11578 {
11579 // Validator doesn't match: ignore Range and serve full content
11580 req.ranges.clear();
11581 return false;
11582 }
11583 }
11584
11585 return true;
11586}
11587
11588inline socket_t
11589Server::create_server_socket(const std::string &host, int port,
11590 int socket_flags,
11591 SocketOptions socket_options) const
11592{
11593 return detail::create_socket(
11594 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
11595 ipv6_v6only_, std::move(socket_options),
11596 [&](socket_t sock, struct addrinfo & ai, bool & /*quit*/) -> bool
11597 {
11598 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen)))
11599 {
11600 output_error_log(Error::BindIPAddress, nullptr);
11601 return false;
11602 }
11604 {
11605 output_error_log(Error::Listen, nullptr);
11606 return false;
11607 }
11608 return true;
11609 });
11610}
11611
11612inline int Server::bind_internal(const std::string &host, int port,
11613 int socket_flags)
11614{
11615 if (is_decommissioned)
11616 {
11617 return -1;
11618 }
11619
11620 if (!is_valid())
11621 {
11622 return -1;
11623 }
11624
11625 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
11627 {
11628 return -1;
11629 }
11630
11631 if (port == 0)
11632 {
11633 struct sockaddr_storage addr;
11634 socklen_t addr_len = sizeof(addr);
11635 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
11636 &addr_len) == -1)
11637 {
11638 output_error_log(Error::GetSockName, nullptr);
11639 return -1;
11640 }
11641 if (addr.ss_family == AF_INET)
11642 {
11643 return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
11644 }
11645 else if (addr.ss_family == AF_INET6)
11646 {
11647 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
11648 }
11649 else
11650 {
11651 output_error_log(Error::UnsupportedAddressFamily, nullptr);
11652 return -1;
11653 }
11654 }
11655 else
11656 {
11657 return port;
11658 }
11659}
11660
11661inline bool Server::listen_internal()
11662{
11663 if (is_decommissioned)
11664 {
11665 return false;
11666 }
11667
11668 auto ret = true;
11669 is_running_ = true;
11670 auto se = detail::scope_exit([&]()
11671 {
11672 is_running_ = false;
11673 });
11674
11675 {
11676 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
11677
11678 while (svr_sock_ != INVALID_SOCKET)
11679 {
11680#ifndef _WIN32
11682 {
11683#endif
11684 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
11686 if (val == 0) // Timeout
11687 {
11688 task_queue->on_idle();
11689 continue;
11690 }
11691#ifndef _WIN32
11692 }
11693#endif
11694
11695#if defined _WIN32
11696 // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
11697 // OVERLAPPED
11698 socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
11699#elif defined SOCK_CLOEXEC
11700 socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
11701#else
11702 socket_t sock = accept(svr_sock_, nullptr, nullptr);
11703#endif
11704
11705 if (sock == INVALID_SOCKET)
11706 {
11707 if (errno == EMFILE)
11708 {
11709 // The per-process limit of open file descriptors has been reached.
11710 // Try to accept new connections after a short sleep.
11711 std::this_thread::sleep_for(std::chrono::microseconds{1});
11712 continue;
11713 }
11714 else if (errno == EINTR || errno == EAGAIN)
11715 {
11716 continue;
11717 }
11719 {
11721 ret = false;
11722 output_error_log(Error::Connection, nullptr);
11723 }
11724 else
11725 {
11726 ; // The server socket was closed by user.
11727 }
11728 break;
11729 }
11730
11731 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
11733 detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
11735
11736 if (!task_queue->enqueue(
11737 [this, sock]()
11738 {
11739 process_and_close_socket(sock);
11740 }))
11741 {
11742 output_error_log(Error::ResourceExhaustion, nullptr);
11743 detail::shutdown_socket(sock);
11745 }
11746 }
11747
11748 task_queue->shutdown();
11749 }
11750
11751 is_decommissioned = !ret;
11752 return ret;
11753}
11754
11755inline bool Server::routing(Request &req, Response &res, Stream &strm)
11756{
11757 if (pre_routing_handler_ &&
11758 pre_routing_handler_(req, res) == HandlerResponse::Handled)
11759 {
11760 return true;
11761 }
11762
11763 // File handler
11764 if ((req.method == "GET" || req.method == "HEAD") &&
11765 handle_file_request(req, res))
11766 {
11767 return true;
11768 }
11769
11770 if (detail::expect_content(req))
11771 {
11772 // Content reader handler
11773 {
11774 ContentReader reader(
11776 {
11777 auto result = read_content_with_content_receiver(
11778 strm, req, res, std::move(receiver), nullptr, nullptr);
11779 if (!result)
11780 {
11781 output_error_log(Error::Read, &req);
11782 }
11783 return result;
11784 },
11786 {
11787 auto result = read_content_with_content_receiver(
11788 strm, req, res, nullptr, std::move(header),
11789 std::move(receiver));
11790 if (!result)
11791 {
11792 output_error_log(Error::Read, &req);
11793 }
11794 return result;
11795 });
11796
11797 if (req.method == "POST")
11798 {
11799 if (dispatch_request_for_content_reader(
11800 req, res, std::move(reader),
11801 post_handlers_for_content_reader_))
11802 {
11803 return true;
11804 }
11805 }
11806 else if (req.method == "PUT")
11807 {
11808 if (dispatch_request_for_content_reader(
11809 req, res, std::move(reader),
11810 put_handlers_for_content_reader_))
11811 {
11812 return true;
11813 }
11814 }
11815 else if (req.method == "PATCH")
11816 {
11817 if (dispatch_request_for_content_reader(
11818 req, res, std::move(reader),
11819 patch_handlers_for_content_reader_))
11820 {
11821 return true;
11822 }
11823 }
11824 else if (req.method == "DELETE")
11825 {
11826 if (dispatch_request_for_content_reader(
11827 req, res, std::move(reader),
11828 delete_handlers_for_content_reader_))
11829 {
11830 return true;
11831 }
11832 }
11833 }
11834
11835 // Read content into `req.body`
11836 if (!read_content(strm, req, res))
11837 {
11838 output_error_log(Error::Read, &req);
11839 return false;
11840 }
11841 }
11842
11843 // Regular handler
11844 if (req.method == "GET" || req.method == "HEAD")
11845 {
11846 return dispatch_request(req, res, get_handlers_);
11847 }
11848 else if (req.method == "POST")
11849 {
11850 return dispatch_request(req, res, post_handlers_);
11851 }
11852 else if (req.method == "PUT")
11853 {
11854 return dispatch_request(req, res, put_handlers_);
11855 }
11856 else if (req.method == "DELETE")
11857 {
11858 return dispatch_request(req, res, delete_handlers_);
11859 }
11860 else if (req.method == "OPTIONS")
11861 {
11862 return dispatch_request(req, res, options_handlers_);
11863 }
11864 else if (req.method == "PATCH")
11865 {
11866 return dispatch_request(req, res, patch_handlers_);
11867 }
11868
11869 res.status = StatusCode::BadRequest_400;
11870 return false;
11871}
11872
11873inline bool Server::dispatch_request(Request &req, Response &res,
11874 const Handlers &handlers) const
11875{
11876 for (const auto &x : handlers)
11877 {
11878 const auto &matcher = x.first;
11879 const auto &handler = x.second;
11880
11881 if (matcher->match(req))
11882 {
11883 req.matched_route = matcher->pattern();
11884 if (!pre_request_handler_ ||
11885 pre_request_handler_(req, res) != HandlerResponse::Handled)
11886 {
11887 handler(req, res);
11888 }
11889 return true;
11890 }
11891 }
11892 return false;
11893}
11894
11895inline void Server::apply_ranges(const Request &req, Response &res,
11896 std::string &content_type,
11897 std::string &boundary) const
11898{
11899 if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206)
11900 {
11901 auto it = res.headers.find("Content-Type");
11902 if (it != res.headers.end())
11903 {
11904 content_type = it->second;
11905 res.headers.erase(it);
11906 }
11907
11908 boundary = detail::make_multipart_data_boundary();
11909
11910 res.set_header("Content-Type",
11911 "multipart/byteranges; boundary=" + boundary);
11912 }
11913
11914 auto type = detail::encoding_type(req, res);
11915
11916 if (res.body.empty())
11917 {
11918 if (res.content_length_ > 0)
11919 {
11920 size_t length = 0;
11921 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206)
11922 {
11923 length = res.content_length_;
11924 }
11925 else if (req.ranges.size() == 1)
11926 {
11927 auto offset_and_length = detail::get_range_offset_and_length(
11928 req.ranges[0], res.content_length_);
11929
11930 length = offset_and_length.second;
11931
11932 auto content_range = detail::make_content_range_header_field(
11933 offset_and_length, res.content_length_);
11934 res.set_header("Content-Range", content_range);
11935 }
11936 else
11937 {
11938 length = detail::get_multipart_ranges_data_length(
11939 req, boundary, content_type, res.content_length_);
11940 }
11941 res.set_header("Content-Length", std::to_string(length));
11942 }
11943 else
11944 {
11945 if (res.content_provider_)
11946 {
11947 if (res.is_chunked_content_provider_)
11948 {
11949 res.set_header("Transfer-Encoding", "chunked");
11950 if (type == detail::EncodingType::Gzip)
11951 {
11952 res.set_header("Content-Encoding", "gzip");
11953 res.set_header("Vary", "Accept-Encoding");
11954 }
11955 else if (type == detail::EncodingType::Brotli)
11956 {
11957 res.set_header("Content-Encoding", "br");
11958 res.set_header("Vary", "Accept-Encoding");
11959 }
11960 else if (type == detail::EncodingType::Zstd)
11961 {
11962 res.set_header("Content-Encoding", "zstd");
11963 res.set_header("Vary", "Accept-Encoding");
11964 }
11965 }
11966 }
11967 }
11968 }
11969 else
11970 {
11971 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206)
11972 {
11973 ;
11974 }
11975 else if (req.ranges.size() == 1)
11976 {
11977 auto offset_and_length =
11978 detail::get_range_offset_and_length(req.ranges[0], res.body.size());
11979 auto offset = offset_and_length.first;
11980 auto length = offset_and_length.second;
11981
11982 auto content_range = detail::make_content_range_header_field(
11983 offset_and_length, res.body.size());
11984 res.set_header("Content-Range", content_range);
11985
11986 assert(offset + length <= res.body.size());
11987 res.body = res.body.substr(offset, length);
11988 }
11989 else
11990 {
11991 std::string data;
11992 detail::make_multipart_ranges_data(req, res, boundary, content_type,
11993 res.body.size(), data);
11994 res.body.swap(data);
11995 }
11996
11997 if (type != detail::EncodingType::None)
11998 {
11999 output_pre_compression_log(req, res);
12000
12001 std::unique_ptr<detail::compressor> compressor;
12002 std::string content_encoding;
12003
12004 if (type == detail::EncodingType::Gzip)
12005 {
12006#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12007 compressor = detail::make_unique<detail::gzip_compressor>();
12008 content_encoding = "gzip";
12009#endif
12010 }
12011 else if (type == detail::EncodingType::Brotli)
12012 {
12013#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12014 compressor = detail::make_unique<detail::brotli_compressor>();
12015 content_encoding = "br";
12016#endif
12017 }
12018 else if (type == detail::EncodingType::Zstd)
12019 {
12020#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12021 compressor = detail::make_unique<detail::zstd_compressor>();
12022 content_encoding = "zstd";
12023#endif
12024 }
12025
12026 if (compressor)
12027 {
12028 std::string compressed;
12029 if (compressor->compress(res.body.data(), res.body.size(), true,
12030 [&](const char *data, size_t data_len)
12031 {
12032 compressed.append(data, data_len);
12033 return true;
12034 }))
12035 {
12036 res.body.swap(compressed);
12037 res.set_header("Content-Encoding", content_encoding);
12038 res.set_header("Vary", "Accept-Encoding");
12039 }
12040 }
12041 }
12042
12043 auto length = std::to_string(res.body.size());
12044 res.set_header("Content-Length", length);
12045 }
12046}
12047
12048inline bool Server::dispatch_request_for_content_reader(
12049 Request &req, Response &res, ContentReader content_reader,
12050 const HandlersForContentReader &handlers) const
12051{
12052 for (const auto &x : handlers)
12053 {
12054 const auto &matcher = x.first;
12055 const auto &handler = x.second;
12056
12057 if (matcher->match(req))
12058 {
12059 req.matched_route = matcher->pattern();
12060 if (!pre_request_handler_ ||
12061 pre_request_handler_(req, res) != HandlerResponse::Handled)
12062 {
12063 handler(req, res, content_reader);
12064 }
12065 return true;
12066 }
12067 }
12068 return false;
12069}
12070
12071inline std::string
12072get_client_ip(const std::string &x_forwarded_for,
12073 const std::vector<std::string> &trusted_proxies)
12074{
12075 // X-Forwarded-For is a comma-separated list per RFC 7239
12076 std::vector<std::string> ip_list;
12077 detail::split(x_forwarded_for.data(),
12078 x_forwarded_for.data() + x_forwarded_for.size(), ',',
12079 [&](const char *b, const char *e)
12080 {
12081 auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
12082 ip_list.emplace_back(std::string(b + r.first, b + r.second));
12083 });
12084
12085 for (size_t i = 0; i < ip_list.size(); ++i)
12086 {
12087 auto ip = ip_list[i];
12088
12089 auto is_trusted_proxy =
12090 std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
12091 [&](const std::string & proxy)
12092 {
12093 return ip == proxy;
12094 });
12095
12096 if (is_trusted_proxy)
12097 {
12098 if (i == 0)
12099 {
12100 // If the trusted proxy is the first IP, there's no preceding client IP
12101 return ip;
12102 }
12103 else
12104 {
12105 // Return the IP immediately before the trusted proxy
12106 return ip_list[i - 1];
12107 }
12108 }
12109 }
12110
12111 // If no trusted proxy is found, return the first IP in the list
12112 return ip_list.front();
12113}
12114
12115inline bool
12116Server::process_request(Stream &strm, const std::string &remote_addr,
12117 int remote_port, const std::string &local_addr,
12118 int local_port, bool close_connection,
12119 bool &connection_closed,
12120 const std::function<void(Request &)> &setup_request)
12121{
12122 std::array<char, 2048> buf{};
12123
12124 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12125
12126 // Connection has been closed on client
12127 if (!line_reader.getline())
12128 {
12129 return false;
12130 }
12131
12132 Request req;
12133 req.start_time_ = std::chrono::steady_clock::now();
12134 req.remote_addr = remote_addr;
12135 req.remote_port = remote_port;
12136 req.local_addr = local_addr;
12137 req.local_port = local_port;
12138
12139 Response res;
12140 res.version = "HTTP/1.1";
12141 res.headers = default_headers_;
12142
12143#ifdef __APPLE__
12144 // Socket file descriptor exceeded FD_SETSIZE...
12145 if (strm.socket() >= FD_SETSIZE)
12146 {
12147 Headers dummy;
12148 detail::read_headers(strm, dummy);
12150 output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
12151 return write_response(strm, close_connection, req, res);
12152 }
12153#endif
12154
12155 // Request line and headers
12156 if (!parse_request_line(line_reader.ptr(), req))
12157 {
12159 output_error_log(Error::InvalidRequestLine, &req);
12160 return write_response(strm, close_connection, req, res);
12161 }
12162
12163 // Request headers
12164 if (!detail::read_headers(strm, req.headers))
12165 {
12167 output_error_log(Error::InvalidHeaders, &req);
12168 return write_response(strm, close_connection, req, res);
12169 }
12170
12171 // Check if the request URI doesn't exceed the limit
12173 {
12175 output_error_log(Error::ExceedUriMaxLength, &req);
12176 return write_response(strm, close_connection, req, res);
12177 }
12178
12179 if (req.get_header_value("Connection") == "close")
12180 {
12181 connection_closed = true;
12182 }
12183
12184 if (req.version == "HTTP/1.0" &&
12185 req.get_header_value("Connection") != "Keep-Alive")
12186 {
12187 connection_closed = true;
12188 }
12189
12190 if (!trusted_proxies_.empty() && req.has_header("X-Forwarded-For"))
12191 {
12192 auto x_forwarded_for = req.get_header_value("X-Forwarded-For");
12193 req.remote_addr = get_client_ip(x_forwarded_for, trusted_proxies_);
12194 }
12195 else
12196 {
12197 req.remote_addr = remote_addr;
12198 }
12199 req.remote_port = remote_port;
12200
12201 req.local_addr = local_addr;
12202 req.local_port = local_port;
12203
12204 if (req.has_header("Accept"))
12205 {
12206 const auto &accept_header = req.get_header_value("Accept");
12207 if (!detail::parse_accept_header(accept_header, req.accept_content_types))
12208 {
12210 output_error_log(Error::HTTPParsing, &req);
12211 return write_response(strm, close_connection, req, res);
12212 }
12213 }
12214
12215 if (req.has_header("Range"))
12216 {
12217 const auto &range_header_value = req.get_header_value("Range");
12218 if (!detail::parse_range_header(range_header_value, req.ranges))
12219 {
12221 output_error_log(Error::InvalidRangeHeader, &req);
12222 return write_response(strm, close_connection, req, res);
12223 }
12224 }
12225
12226 if (setup_request)
12227 {
12228 setup_request(req);
12229 }
12230
12231 if (req.get_header_value("Expect") == "100-continue")
12232 {
12233 int status = StatusCode::Continue_100;
12234 if (expect_100_continue_handler_)
12235 {
12236 status = expect_100_continue_handler_(req, res);
12237 }
12238 switch (status)
12239 {
12242 detail::write_response_line(strm, status);
12243 strm.write("\r\n");
12244 break;
12245 default:
12246 connection_closed = true;
12247 return write_response(strm, true, req, res);
12248 }
12249 }
12250
12251 // Setup `is_connection_closed` method
12252 auto sock = strm.socket();
12253 req.is_connection_closed = [sock]()
12254 {
12255 return !detail::is_socket_alive(sock);
12256 };
12257
12258 // Routing
12259 auto routed = false;
12260#ifdef CPPHTTPLIB_NO_EXCEPTIONS
12261 routed = routing(req, res, strm);
12262#else
12263 try
12264 {
12265 routed = routing(req, res, strm);
12266 }
12267 catch (std::exception &e)
12268 {
12269 if (exception_handler_)
12270 {
12271 auto ep = std::current_exception();
12272 exception_handler_(req, res, ep);
12273 routed = true;
12274 }
12275 else
12276 {
12278 std::string val;
12279 auto s = e.what();
12280 for (size_t i = 0; s[i]; i++)
12281 {
12282 switch (s[i])
12283 {
12284 case '\r':
12285 val += "\\r";
12286 break;
12287 case '\n':
12288 val += "\\n";
12289 break;
12290 default:
12291 val += s[i];
12292 break;
12293 }
12294 }
12295 res.set_header("EXCEPTION_WHAT", val);
12296 }
12297 }
12298 catch (...)
12299 {
12300 if (exception_handler_)
12301 {
12302 auto ep = std::current_exception();
12303 exception_handler_(req, res, ep);
12304 routed = true;
12305 }
12306 else
12307 {
12309 res.set_header("EXCEPTION_WHAT", "UNKNOWN");
12310 }
12311 }
12312#endif
12313 if (routed)
12314 {
12315 if (res.status == -1)
12316 {
12317 res.status = req.ranges.empty() ? StatusCode::OK_200
12319 }
12320
12321 // Serve file content by using a content provider
12322 if (!res.file_content_path_.empty())
12323 {
12324 const auto &path = res.file_content_path_;
12325 auto mm = std::make_shared<detail::mmap>(path.c_str());
12326 if (!mm->is_open())
12327 {
12328 res.body.clear();
12329 res.content_length_ = 0;
12330 res.content_provider_ = nullptr;
12332 output_error_log(Error::OpenFile, &req);
12333 return write_response(strm, close_connection, req, res);
12334 }
12335
12336 auto content_type = res.file_content_content_type_;
12337 if (content_type.empty())
12338 {
12339 content_type = detail::find_content_type(
12340 path, file_extension_and_mimetype_map_, default_file_mimetype_);
12341 }
12342
12344 mm->size(), content_type,
12345 [mm](size_t offset, size_t length, DataSink & sink) -> bool
12346 {
12347 sink.write(mm->data() + offset, length);
12348 return true;
12349 });
12350 }
12351
12352 if (detail::range_error(req, res))
12353 {
12354 res.body.clear();
12355 res.content_length_ = 0;
12356 res.content_provider_ = nullptr;
12358 return write_response(strm, close_connection, req, res);
12359 }
12360
12361 return write_response_with_content(strm, close_connection, req, res);
12362 }
12363 else
12364 {
12365 if (res.status == -1)
12366 {
12368 }
12369
12370 return write_response(strm, close_connection, req, res);
12371 }
12372}
12373
12374inline bool Server::is_valid() const
12375{
12376 return true;
12377}
12378
12379inline bool Server::process_and_close_socket(socket_t sock)
12380{
12381 std::string remote_addr;
12382 int remote_port = 0;
12383 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
12384
12385 std::string local_addr;
12386 int local_port = 0;
12387 detail::get_local_ip_and_port(sock, local_addr, local_port);
12388
12389 auto ret = detail::process_server_socket(
12393 [&](Stream & strm, bool close_connection, bool & connection_closed)
12394 {
12395 return process_request(strm, remote_addr, remote_port, local_addr,
12396 local_port, close_connection, connection_closed,
12397 nullptr);
12398 });
12399
12400 detail::shutdown_socket(sock);
12402 return ret;
12403}
12404
12405inline void Server::output_log(const Request &req, const Response &res) const
12406{
12407 if (logger_)
12408 {
12409 std::lock_guard<std::mutex> guard(logger_mutex_);
12410 logger_(req, res);
12411 }
12412}
12413
12414inline void Server::output_pre_compression_log(const Request &req,
12415 const Response &res) const
12416{
12417 if (pre_compression_logger_)
12418 {
12419 std::lock_guard<std::mutex> guard(logger_mutex_);
12420 pre_compression_logger_(req, res);
12421 }
12422}
12423
12424inline void Server::output_error_log(const Error &err,
12425 const Request *req) const
12426{
12427 if (error_logger_)
12428 {
12429 std::lock_guard<std::mutex> guard(logger_mutex_);
12430 error_logger_(err, req);
12431 }
12432}
12433
12434// HTTP client implementation
12435inline ClientImpl::ClientImpl(const std::string &host)
12436 : ClientImpl(host, 80, std::string(), std::string()) {}
12437
12438inline ClientImpl::ClientImpl(const std::string &host, int port)
12439 : ClientImpl(host, port, std::string(), std::string()) {}
12440
12441inline ClientImpl::ClientImpl(const std::string &host, int port,
12442 const std::string &client_cert_path,
12443 const std::string &client_key_path)
12444 : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
12445 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
12446
12448{
12449 // Wait until all the requests in flight are handled.
12450 size_t retry_count = 10;
12451 while (retry_count-- > 0)
12452 {
12453 {
12454 std::lock_guard<std::mutex> guard(socket_mutex_);
12456 {
12457 break;
12458 }
12459 }
12460 std::this_thread::sleep_for(std::chrono::milliseconds{1});
12461 }
12462
12463 std::lock_guard<std::mutex> guard(socket_mutex_);
12466}
12467
12468inline bool ClientImpl::is_valid() const
12469{
12470 return true;
12471}
12472
12474{
12486#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12487 digest_auth_username_ = rhs.digest_auth_username_;
12488 digest_auth_password_ = rhs.digest_auth_password_;
12489#endif
12497 compress_ = rhs.compress_;
12499 interface_ = rhs.interface_;
12505#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12506 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
12507 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
12508#endif
12509#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12510 ca_cert_file_path_ = rhs.ca_cert_file_path_;
12511 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
12512 ca_cert_store_ = rhs.ca_cert_store_;
12513#endif
12514#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12515 server_certificate_verification_ = rhs.server_certificate_verification_;
12516 server_hostname_verification_ = rhs.server_hostname_verification_;
12517 server_certificate_verifier_ = rhs.server_certificate_verifier_;
12518#endif
12519 logger_ = rhs.logger_;
12521}
12522
12523inline socket_t ClientImpl::create_client_socket(Error &error) const
12524{
12525 if (!proxy_host_.empty() && proxy_port_ != -1)
12526 {
12527 return detail::create_client_socket(
12532 }
12533
12534 // Check is custom IP specified for host_
12535 std::string ip;
12536 auto it = addr_map_.find(host_);
12537 if (it != addr_map_.end())
12538 {
12539 ip = it->second;
12540 }
12541
12542 return detail::create_client_socket(
12547}
12548
12550 Error &error)
12551{
12552 auto sock = create_client_socket(error);
12553 if (sock == INVALID_SOCKET)
12554 {
12555 return false;
12556 }
12557 socket.sock = sock;
12558 return true;
12559}
12560
12562{
12563 return create_and_connect_socket(socket, error);
12564}
12565
12566#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12567inline bool SSLClient::ensure_socket_connection(Socket &socket, Error &error)
12568{
12569 if (!ClientImpl::ensure_socket_connection(socket, error))
12570 {
12571 return false;
12572 }
12573
12574 if (!proxy_host_.empty() && proxy_port_ != -1)
12575 {
12576 return true;
12577 }
12578
12579 if (!initialize_ssl(socket, error))
12580 {
12581 shutdown_socket(socket);
12582 close_socket(socket);
12583 return false;
12584 }
12585
12586 return true;
12587}
12588#endif
12589
12590inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
12591 bool /*shutdown_gracefully*/)
12592{
12593 // If there are any requests in flight from threads other than us, then it's
12594 // a thread-unsafe race because individual ssl* objects are not thread-safe.
12595 assert(socket_requests_in_flight_ == 0 ||
12596 socket_requests_are_from_thread_ == std::this_thread::get_id());
12597}
12598
12599inline void ClientImpl::shutdown_socket(Socket &socket) const
12600{
12601 if (socket.sock == INVALID_SOCKET)
12602 {
12603 return;
12604 }
12605 detail::shutdown_socket(socket.sock);
12606}
12607
12609{
12610 // If there are requests in flight in another thread, usually closing
12611 // the socket will be fine and they will simply receive an error when
12612 // using the closed socket, but it is still a bug since rarely the OS
12613 // may reassign the socket id to be used for a new socket, and then
12614 // suddenly they will be operating on a live socket that is different
12615 // than the one they intended!
12616 assert(socket_requests_in_flight_ == 0 ||
12617 socket_requests_are_from_thread_ == std::this_thread::get_id());
12618
12619 // It is also a bug if this happens while SSL is still active
12620#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12621 assert(socket.ssl == nullptr);
12622#endif
12623 if (socket.sock == INVALID_SOCKET)
12624 {
12625 return;
12626 }
12628 socket.sock = INVALID_SOCKET;
12629}
12630
12631inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
12632 Response &res) const
12633{
12634 std::array<char, 2048> buf{};
12635
12636 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12637
12638 if (!line_reader.getline())
12639 {
12640 return false;
12641 }
12642
12643#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
12644 thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
12645#else
12646 thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
12647#endif
12648
12649 std::cmatch m;
12650 if (!std::regex_match(line_reader.ptr(), m, re))
12651 {
12652 return req.method == "CONNECT";
12653 }
12654 res.version = std::string(m[1]);
12655 res.status = std::stoi(std::string(m[2]));
12656 res.reason = std::string(m[3]);
12657
12658 // Ignore '100 Continue'
12659 while (res.status == StatusCode::Continue_100)
12660 {
12661 if (!line_reader.getline())
12662 {
12663 return false; // CRLF
12664 }
12665 if (!line_reader.getline())
12666 {
12667 return false; // next response line
12668 }
12669
12670 if (!std::regex_match(line_reader.ptr(), m, re))
12671 {
12672 return false;
12673 }
12674 res.version = std::string(m[1]);
12675 res.status = std::stoi(std::string(m[2]));
12676 res.reason = std::string(m[3]);
12677 }
12678
12679 return true;
12680}
12681
12682inline bool ClientImpl::send(Request &req, Response &res, Error &error)
12683{
12684 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
12685 auto ret = send_(req, res, error);
12686 if (error == Error::SSLPeerCouldBeClosed_)
12687 {
12688 assert(!ret);
12689 ret = send_(req, res, error);
12690 }
12691 return ret;
12692}
12693
12694inline bool ClientImpl::send_(Request &req, Response &res, Error &error)
12695{
12696 {
12697 std::lock_guard<std::mutex> guard(socket_mutex_);
12698
12699 // Set this to false immediately - if it ever gets set to true by the end
12700 // of the request, we know another thread instructed us to close the
12701 // socket.
12703
12704 auto is_alive = false;
12705 if (socket_.is_open())
12706 {
12707 is_alive = detail::is_socket_alive(socket_.sock);
12708
12709#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12710 if (is_alive && is_ssl())
12711 {
12712 if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock))
12713 {
12714 is_alive = false;
12715 }
12716 }
12717#endif
12718
12719 if (!is_alive)
12720 {
12721 // Attempt to avoid sigpipe by shutting down non-gracefully if it
12722 // seems like the other side has already closed the connection Also,
12723 // there cannot be any requests in flight from other threads since we
12724 // locked request_mutex_, so safe to close everything immediately
12725 const bool shutdown_gracefully = false;
12726 shutdown_ssl(socket_, shutdown_gracefully);
12729 }
12730 }
12731
12732 if (!is_alive)
12733 {
12734 if (!ensure_socket_connection(socket_, error))
12735 {
12736 output_error_log(error, &req);
12737 return false;
12738 }
12739
12740#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12741 // TODO: refactoring
12742 if (is_ssl())
12743 {
12744 auto &scli = static_cast<SSLClient &>(*this);
12745 if (!proxy_host_.empty() && proxy_port_ != -1)
12746 {
12747 auto success = false;
12748 if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
12749 error))
12750 {
12751 if (!success)
12752 {
12753 output_error_log(error, &req);
12754 }
12755 return success;
12756 }
12757 }
12758
12759 if (!proxy_host_.empty() && proxy_port_ != -1)
12760 {
12761 if (!scli.initialize_ssl(socket_, error))
12762 {
12763 output_error_log(error, &req);
12764 return false;
12765 }
12766 }
12767 }
12768#endif
12769 }
12770
12771 // Mark the current socket as being in use so that it cannot be closed by
12772 // anyone else while this request is ongoing, even though we will be
12773 // releasing the mutex.
12775 {
12776 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
12777 }
12779 socket_requests_are_from_thread_ = std::this_thread::get_id();
12780 }
12781
12782 for (const auto &header : default_headers_)
12783 {
12784 if (req.headers.find(header.first) == req.headers.end())
12785 {
12786 req.headers.insert(header);
12787 }
12788 }
12789
12790 auto ret = false;
12791 auto close_connection = !keep_alive_;
12792
12793 auto se = detail::scope_exit([&]()
12794 {
12795 // Briefly lock mutex in order to mark that a request is no longer ongoing
12796 std::lock_guard<std::mutex> guard(socket_mutex_);
12799 {
12800 assert(socket_requests_in_flight_ == 0);
12801 socket_requests_are_from_thread_ = std::thread::id();
12802 }
12803
12804 if (socket_should_be_closed_when_request_is_done_ || close_connection ||
12805 !ret)
12806 {
12807 shutdown_ssl(socket_, true);
12810 }
12811 });
12812
12813 ret = process_socket(socket_, req.start_time_, [&](Stream & strm)
12814 {
12815 return handle_request(strm, req, res, close_connection, error);
12816 });
12817
12818 if (!ret)
12819 {
12820 if (error == Error::Success)
12821 {
12823 output_error_log(error, &req);
12824 }
12825 }
12826
12827 return ret;
12828}
12829
12831{
12832 auto req2 = req;
12833 return send_(std::move(req2));
12834}
12835
12836inline Result ClientImpl::send_(Request &&req)
12837{
12838 auto res = detail::make_unique<Response>();
12839 auto error = Error::Success;
12840 auto ret = send(req, *res, error);
12841#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12842 return Result {ret ? std::move(res) : nullptr, error, std::move(req.headers),
12843 last_ssl_error_, last_openssl_error_
12844 };
12845#else
12846 return Result {ret ? std::move(res) : nullptr, error, std::move(req.headers)};
12847#endif
12848}
12849
12850inline void ClientImpl::prepare_default_headers(Request &r, bool for_stream,
12851 const std::string &ct)
12852{
12853 (void)for_stream;
12854 for (const auto &header : default_headers_)
12855 {
12856 if (!r.has_header(header.first))
12857 {
12858 r.headers.insert(header);
12859 }
12860 }
12861
12862 if (!r.has_header("Host"))
12863 {
12864 if (address_family_ == AF_UNIX)
12865 {
12866 r.headers.emplace("Host", "localhost");
12867 }
12868 else
12869 {
12870 r.headers.emplace(
12871 "Host", detail::make_host_and_port_string(host_, port_, is_ssl()));
12872 }
12873 }
12874
12875 if (!r.has_header("Accept"))
12876 {
12877 r.headers.emplace("Accept", "*/*");
12878 }
12879
12880 if (!r.content_receiver)
12881 {
12882 if (!r.has_header("Accept-Encoding"))
12883 {
12884 std::string accept_encoding;
12885#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12886 accept_encoding = "br";
12887#endif
12888#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12889 if (!accept_encoding.empty())
12890 {
12891 accept_encoding += ", ";
12892 }
12893 accept_encoding += "gzip, deflate";
12894#endif
12895#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12896 if (!accept_encoding.empty())
12897 {
12898 accept_encoding += ", ";
12899 }
12900 accept_encoding += "zstd";
12901#endif
12902 r.set_header("Accept-Encoding", accept_encoding);
12903 }
12904
12905#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
12906 if (!r.has_header("User-Agent"))
12907 {
12908 auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
12909 r.set_header("User-Agent", agent);
12910 }
12911#endif
12912 }
12913
12914 if (!r.body.empty())
12915 {
12916 if (!ct.empty() && !r.has_header("Content-Type"))
12917 {
12918 r.headers.emplace("Content-Type", ct);
12919 }
12920 if (!r.has_header("Content-Length"))
12921 {
12922 r.headers.emplace("Content-Length", std::to_string(r.body.size()));
12923 }
12924 }
12925}
12926
12927inline ClientImpl::StreamHandle
12928ClientImpl::open_stream(const std::string &method, const std::string &path,
12929 const Params &params, const Headers &headers,
12930 const std::string &body,
12931 const std::string &content_type)
12932{
12933 StreamHandle handle;
12934 handle.response = detail::make_unique<Response>();
12935 handle.error = Error::Success;
12936
12937 auto query_path = params.empty() ? path : append_query_params(path, params);
12938 handle.connection_ = detail::make_unique<ClientConnection>();
12939
12940 {
12941 std::lock_guard<std::mutex> guard(socket_mutex_);
12942
12943 auto is_alive = false;
12944 if (socket_.is_open())
12945 {
12946 is_alive = detail::is_socket_alive(socket_.sock);
12947#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12948 if (is_alive && is_ssl())
12949 {
12950 if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock))
12951 {
12952 is_alive = false;
12953 }
12954 }
12955#endif
12956 if (!is_alive)
12957 {
12958 shutdown_ssl(socket_, false);
12961 }
12962 }
12963
12964 if (!is_alive)
12965 {
12967 {
12968 handle.response.reset();
12969 return handle;
12970 }
12971
12972#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12973 if (is_ssl())
12974 {
12975 auto &scli = static_cast<SSLClient &>(*this);
12976 if (!proxy_host_.empty() && proxy_port_ != -1)
12977 {
12978 if (!scli.initialize_ssl(socket_, handle.error))
12979 {
12980 handle.response.reset();
12981 return handle;
12982 }
12983 }
12984 }
12985#endif
12986 }
12987
12988 transfer_socket_ownership_to_handle(handle);
12989 }
12990
12991#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12992 if (is_ssl() && handle.connection_->ssl)
12993 {
12994 handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
12995 handle.connection_->sock, handle.connection_->ssl, read_timeout_sec_,
12997 }
12998 else
12999 {
13000 handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
13001 handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
13003 }
13004#else
13005 handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
13006 handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
13008#endif
13009 handle.stream_ = handle.socket_stream_.get();
13010
13011 Request req;
13012 req.method = method;
13013 req.path = query_path;
13014 req.headers = headers;
13015 req.body = body;
13016
13017 prepare_default_headers(req, true, content_type);
13018
13019 auto &strm = *handle.stream_;
13020 if (detail::write_request_line(strm, req.method, req.path) < 0)
13021 {
13022 handle.error = Error::Write;
13023 handle.response.reset();
13024 return handle;
13025 }
13026
13027 if (!detail::write_headers(strm, req.headers))
13028 {
13029 handle.error = Error::Write;
13030 handle.response.reset();
13031 return handle;
13032 }
13033
13034 if (!body.empty())
13035 {
13036 if (strm.write(body.data(), body.size()) < 0)
13037 {
13038 handle.error = Error::Write;
13039 handle.response.reset();
13040 return handle;
13041 }
13042 }
13043
13044 if (!read_response_line(strm, req, *handle.response) ||
13045 !detail::read_headers(strm, handle.response->headers))
13046 {
13047 handle.error = Error::Read;
13048 handle.response.reset();
13049 return handle;
13050 }
13051
13052 handle.body_reader_.stream = handle.stream_;
13053
13054 auto content_length_str = handle.response->get_header_value("Content-Length");
13055 if (!content_length_str.empty())
13056 {
13057 handle.body_reader_.content_length =
13058 static_cast<size_t>(std::stoull(content_length_str));
13059 }
13060
13061 auto transfer_encoding =
13062 handle.response->get_header_value("Transfer-Encoding");
13063 handle.body_reader_.chunked = (transfer_encoding == "chunked");
13064
13065 auto content_encoding = handle.response->get_header_value("Content-Encoding");
13066 if (!content_encoding.empty())
13067 {
13068 handle.decompressor_ = detail::create_decompressor(content_encoding);
13069 }
13070
13071 return handle;
13072}
13073
13074inline ssize_t ClientImpl::StreamHandle::read(char *buf, size_t len)
13075{
13076 if (!is_valid() || !response)
13077 {
13078 return -1;
13079 }
13080
13081 if (decompressor_)
13082 {
13083 return read_with_decompression(buf, len);
13084 }
13085 auto n = detail::read_body_content(stream_, body_reader_, buf, len);
13086
13087 if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_)
13088 {
13089 trailers_parsed_ = true;
13090 if (body_reader_.chunked_decoder)
13091 {
13092 if (!body_reader_.chunked_decoder->parse_trailers_into(
13093 response->trailers, response->headers))
13094 {
13095 return n;
13096 }
13097 }
13098 else
13099 {
13100 detail::ChunkedDecoder dec(*stream_);
13101 if (!dec.parse_trailers_into(response->trailers, response->headers))
13102 {
13103 return n;
13104 }
13105 }
13106 }
13107
13108 return n;
13109}
13110
13111inline ssize_t ClientImpl::StreamHandle::read_with_decompression(char *buf,
13112 size_t len)
13113{
13114 if (decompress_offset_ < decompress_buffer_.size())
13115 {
13116 auto available = decompress_buffer_.size() - decompress_offset_;
13117 auto to_copy = (std::min)(len, available);
13118 std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
13119 decompress_offset_ += to_copy;
13120 return static_cast<ssize_t>(to_copy);
13121 }
13122
13123 decompress_buffer_.clear();
13124 decompress_offset_ = 0;
13125
13126 constexpr size_t kDecompressionBufferSize = 8192;
13127 char compressed_buf[kDecompressionBufferSize];
13128
13129 while (true)
13130 {
13131 auto n = detail::read_body_content(stream_, body_reader_, compressed_buf,
13132 sizeof(compressed_buf));
13133
13134 if (n <= 0)
13135 {
13136 return n;
13137 }
13138
13139 bool decompress_ok =
13140 decompressor_->decompress(compressed_buf, static_cast<size_t>(n),
13141 [this](const char *data, size_t data_len)
13142 {
13143 decompress_buffer_.append(data, data_len);
13144 return true;
13145 });
13146
13147 if (!decompress_ok)
13148 {
13149 body_reader_.last_error = Error::Read;
13150 return -1;
13151 }
13152
13153 if (!decompress_buffer_.empty())
13154 {
13155 break;
13156 }
13157 }
13158
13159 auto to_copy = (std::min)(len, decompress_buffer_.size());
13160 std::memcpy(buf, decompress_buffer_.data(), to_copy);
13161 decompress_offset_ = to_copy;
13162 return static_cast<ssize_t>(to_copy);
13163}
13164
13166{
13167 if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_)
13168 {
13169 return;
13170 }
13171
13172 trailers_parsed_ = true;
13173
13174 const auto bufsiz = 128;
13175 char line_buf[bufsiz];
13176 detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
13177
13178 if (!line_reader.getline())
13179 {
13180 return;
13181 }
13182
13183 if (!detail::parse_trailers(line_reader, response->trailers,
13184 response->headers))
13185 {
13186 return;
13187 }
13188}
13189
13190// Inline method implementations for `ChunkedDecoder`.
13191namespace detail
13192{
13193
13194inline ChunkedDecoder::ChunkedDecoder(Stream &s) : strm(s) {}
13195
13196inline ssize_t ChunkedDecoder::read_payload(char *buf, size_t len,
13197 size_t &out_chunk_offset,
13198 size_t &out_chunk_total)
13199{
13200 if (finished)
13201 {
13202 return 0;
13203 }
13204
13205 if (chunk_remaining == 0)
13206 {
13207 stream_line_reader lr(strm, line_buf, sizeof(line_buf));
13208 if (!lr.getline())
13209 {
13210 return -1;
13211 }
13212
13213 char *endptr = nullptr;
13214 unsigned long chunk_len = std::strtoul(lr.ptr(), &endptr, 16);
13215 if (endptr == lr.ptr())
13216 {
13217 return -1;
13218 }
13219 if (chunk_len == ULONG_MAX)
13220 {
13221 return -1;
13222 }
13223
13224 if (chunk_len == 0)
13225 {
13226 chunk_remaining = 0;
13227 finished = true;
13228 out_chunk_offset = 0;
13229 out_chunk_total = 0;
13230 return 0;
13231 }
13232
13233 chunk_remaining = static_cast<size_t>(chunk_len);
13234 last_chunk_total = chunk_remaining;
13235 last_chunk_offset = 0;
13236 }
13237
13238 auto to_read = (std::min)(chunk_remaining, len);
13239 auto n = strm.read(buf, to_read);
13240 if (n <= 0)
13241 {
13242 return -1;
13243 }
13244
13245 auto offset_before = last_chunk_offset;
13246 last_chunk_offset += static_cast<size_t>(n);
13247 chunk_remaining -= static_cast<size_t>(n);
13248
13249 out_chunk_offset = offset_before;
13250 out_chunk_total = last_chunk_total;
13251
13252 if (chunk_remaining == 0)
13253 {
13254 stream_line_reader lr(strm, line_buf, sizeof(line_buf));
13255 if (!lr.getline())
13256 {
13257 return -1;
13258 }
13259 if (std::strcmp(lr.ptr(), "\r\n") != 0)
13260 {
13261 return -1;
13262 }
13263 }
13264
13265 return n;
13266}
13267
13268inline bool ChunkedDecoder::parse_trailers_into(Headers &dest,
13269 const Headers &src_headers)
13270{
13271 stream_line_reader lr(strm, line_buf, sizeof(line_buf));
13272 if (!lr.getline())
13273 {
13274 return false;
13275 }
13276 return parse_trailers(lr, dest, src_headers);
13277}
13278
13279} // namespace detail
13280
13281inline void
13282ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle)
13283{
13284 handle.connection_->sock = socket_.sock;
13285#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13286 handle.connection_->ssl = socket_.ssl;
13287 socket_.ssl = nullptr;
13288#endif
13290}
13291
13292inline bool ClientImpl::handle_request(Stream &strm, Request &req,
13293 Response &res, bool close_connection,
13294 Error &error)
13295{
13296 if (req.path.empty())
13297 {
13299 output_error_log(error, &req);
13300 return false;
13301 }
13302
13303 auto req_save = req;
13304
13305 bool ret;
13306
13307 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1)
13308 {
13309 auto req2 = req;
13310 req2.path = "http://" +
13311 detail::make_host_and_port_string(host_, port_, false) +
13312 req.path;
13313 ret = process_request(strm, req2, res, close_connection, error);
13314 req = std::move(req2);
13315 req.path = req_save.path;
13316 }
13317 else
13318 {
13319 ret = process_request(strm, req, res, close_connection, error);
13320 }
13321
13322 if (!ret)
13323 {
13324 return false;
13325 }
13326
13327 if (res.get_header_value("Connection") == "close" ||
13328 (res.version == "HTTP/1.0" && res.reason != "Connection established"))
13329 {
13330 // TODO this requires a not-entirely-obvious chain of calls to be correct
13331 // for this to be safe.
13332
13333 // This is safe to call because handle_request is only called by send_
13334 // which locks the request mutex during the process. It would be a bug
13335 // to call it from a different thread since it's a thread-safety issue
13336 // to do these things to the socket if another thread is using the socket.
13337 std::lock_guard<std::mutex> guard(socket_mutex_);
13338 shutdown_ssl(socket_, true);
13341 }
13342
13343 if (300 < res.status && res.status < 400 && follow_location_)
13344 {
13345 req = std::move(req_save);
13346 ret = redirect(req, res, error);
13347 }
13348
13349#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13350 if ((res.status == StatusCode::Unauthorized_401 ||
13352 req.authorization_count_ < 5)
13353 {
13354 auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
13355 const auto &username =
13356 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
13357 const auto &password =
13358 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
13359
13360 if (!username.empty() && !password.empty())
13361 {
13362 std::map<std::string, std::string> auth;
13363 if (detail::parse_www_authenticate(res, auth, is_proxy))
13364 {
13365 Request new_req = req;
13366 new_req.authorization_count_ += 1;
13367 new_req.headers.erase(is_proxy ? "Proxy-Authorization"
13368 : "Authorization");
13369 new_req.headers.insert(detail::make_digest_authentication_header(
13370 req, auth, new_req.authorization_count_, detail::random_string(10),
13371 username, password, is_proxy));
13372
13373 Response new_res;
13374
13375 ret = send(new_req, new_res, error);
13376 if (ret)
13377 {
13378 res = std::move(new_res);
13379 }
13380 }
13381 }
13382 }
13383#endif
13384
13385 return ret;
13386}
13387
13388inline bool ClientImpl::redirect(Request &req, Response &res, Error &error)
13389{
13390 if (req.redirect_count_ == 0)
13391 {
13393 output_error_log(error, &req);
13394 return false;
13395 }
13396
13397 auto location = res.get_header_value("location");
13398 if (location.empty())
13399 {
13400 return false;
13401 }
13402
13403 thread_local const std::regex re(
13404 R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
13405
13406 std::smatch m;
13407 if (!std::regex_match(location, m, re))
13408 {
13409 return false;
13410 }
13411
13412 auto scheme = is_ssl() ? "https" : "http";
13413
13414 auto next_scheme = m[1].str();
13415 auto next_host = m[2].str();
13416 if (next_host.empty())
13417 {
13418 next_host = m[3].str();
13419 }
13420 auto port_str = m[4].str();
13421 auto next_path = m[5].str();
13422 auto next_query = m[6].str();
13423
13424 auto next_port = port_;
13425 if (!port_str.empty())
13426 {
13427 next_port = std::stoi(port_str);
13428 }
13429 else if (!next_scheme.empty())
13430 {
13431 next_port = next_scheme == "https" ? 443 : 80;
13432 }
13433
13434 if (next_scheme.empty())
13435 {
13436 next_scheme = scheme;
13437 }
13438 if (next_host.empty())
13439 {
13440 next_host = host_;
13441 }
13442 if (next_path.empty())
13443 {
13444 next_path = "/";
13445 }
13446
13447 auto path = decode_query_component(next_path, true) + next_query;
13448
13449 // Same host redirect - use current client
13450 if (next_scheme == scheme && next_host == host_ && next_port == port_)
13451 {
13452 return detail::redirect(*this, req, res, path, location, error);
13453 }
13454
13455 // Cross-host/scheme redirect - create new client with robust setup
13456 return create_redirect_client(next_scheme, next_host, next_port, req, res,
13457 path, location, error);
13458}
13459
13460// New method for robust redirect client creation
13461inline bool ClientImpl::create_redirect_client(
13462 const std::string &scheme, const std::string &host, int port, Request &req,
13463 Response &res, const std::string &path, const std::string &location,
13464 Error &error)
13465{
13466 // Determine if we need SSL
13467 auto need_ssl = (scheme == "https");
13468
13469 // Clean up request headers that are host/client specific
13470 // Remove headers that should not be carried over to new host
13471 auto headers_to_remove =
13472 std::vector<std::string> {"Host", "Proxy-Authorization", "Authorization"};
13473
13474 for (const auto &header_name : headers_to_remove)
13475 {
13476 auto it = req.headers.find(header_name);
13477 while (it != req.headers.end())
13478 {
13479 it = req.headers.erase(it);
13480 it = req.headers.find(header_name);
13481 }
13482 }
13483
13484 // Create appropriate client type and handle redirect
13485 if (need_ssl)
13486 {
13487#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13488 // Create SSL client for HTTPS redirect
13489 SSLClient redirect_client(host, port);
13490
13491 // Setup basic client configuration first
13492 setup_redirect_client(redirect_client);
13493
13494 // SSL-specific configuration for proxy environments
13495 if (!proxy_host_.empty() && proxy_port_ != -1)
13496 {
13497 // Critical: Disable SSL verification for proxy environments
13498 redirect_client.enable_server_certificate_verification(false);
13499 redirect_client.enable_server_hostname_verification(false);
13500 }
13501 else
13502 {
13503 // For direct SSL connections, copy SSL verification settings
13504 redirect_client.enable_server_certificate_verification(
13505 server_certificate_verification_);
13506 redirect_client.enable_server_hostname_verification(
13507 server_hostname_verification_);
13508 }
13509
13510 // Handle CA certificate store and paths if available
13511 if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_))
13512 {
13513 redirect_client.set_ca_cert_store(ca_cert_store_);
13514 }
13515 if (!ca_cert_file_path_.empty())
13516 {
13517 redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
13518 }
13519
13520 // Client certificates are set through constructor for SSLClient
13521 // NOTE: SSLClient constructor already takes client_cert_path and
13522 // client_key_path so we need to create it properly if client certs are
13523 // needed
13524
13525 // Execute the redirect
13526 return detail::redirect(redirect_client, req, res, path, location, error);
13527#else
13528 // SSL not supported - set appropriate error
13530 output_error_log(error, &req);
13531 return false;
13532#endif
13533 }
13534 else
13535 {
13536 // HTTP redirect
13537 ClientImpl redirect_client(host, port);
13538
13539 // Setup client with robust configuration
13540 setup_redirect_client(redirect_client);
13541
13542 // Execute the redirect
13543 return detail::redirect(redirect_client, req, res, path, location, error);
13544 }
13545}
13546
13547// New method for robust client setup (based on basic_manual_redirect.cpp
13548// logic)
13549template <typename ClientType>
13550inline void ClientImpl::setup_redirect_client(ClientType &client)
13551{
13552 // Copy basic settings first
13553 client.set_connection_timeout(connection_timeout_sec_);
13554 client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
13555 client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
13556 client.set_keep_alive(keep_alive_);
13557 client.set_follow_location(
13558 true); // Enable redirects to handle multi-step redirects
13559 client.set_path_encode(path_encode_);
13560 client.set_compress(compress_);
13561 client.set_decompress(decompress_);
13562
13563 // Copy authentication settings BEFORE proxy setup
13564 if (!basic_auth_username_.empty())
13565 {
13566 client.set_basic_auth(basic_auth_username_, basic_auth_password_);
13567 }
13568 if (!bearer_token_auth_token_.empty())
13569 {
13570 client.set_bearer_token_auth(bearer_token_auth_token_);
13571 }
13572#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13573 if (!digest_auth_username_.empty())
13574 {
13575 client.set_digest_auth(digest_auth_username_, digest_auth_password_);
13576 }
13577#endif
13578
13579 // Setup proxy configuration (CRITICAL ORDER - proxy must be set
13580 // before proxy auth)
13581 if (!proxy_host_.empty() && proxy_port_ != -1)
13582 {
13583 // First set proxy host and port
13584 client.set_proxy(proxy_host_, proxy_port_);
13585
13586 // Then set proxy authentication (order matters!)
13587 if (!proxy_basic_auth_username_.empty())
13588 {
13589 client.set_proxy_basic_auth(proxy_basic_auth_username_,
13591 }
13592 if (!proxy_bearer_token_auth_token_.empty())
13593 {
13594 client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
13595 }
13596#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13597 if (!proxy_digest_auth_username_.empty())
13598 {
13599 client.set_proxy_digest_auth(proxy_digest_auth_username_,
13600 proxy_digest_auth_password_);
13601 }
13602#endif
13603 }
13604
13605 // Copy network and socket settings
13606 client.set_address_family(address_family_);
13607 client.set_tcp_nodelay(tcp_nodelay_);
13608 client.set_ipv6_v6only(ipv6_v6only_);
13609 if (socket_options_)
13610 {
13611 client.set_socket_options(socket_options_);
13612 }
13613 if (!interface_.empty())
13614 {
13615 client.set_interface(interface_);
13616 }
13617
13618 // Copy logging and headers
13619 if (logger_)
13620 {
13621 client.set_logger(logger_);
13622 }
13623 if (error_logger_)
13624 {
13625 client.set_error_logger(error_logger_);
13626 }
13627
13628 // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
13629 // Each new client should generate its own headers based on its target host
13630}
13631
13633 const Request &req,
13634 Error &error) const
13635{
13636 auto is_shutting_down = []()
13637 {
13638 return false;
13639 };
13640
13642 {
13643 // TODO: Brotli support
13644 std::unique_ptr<detail::compressor> compressor;
13645#ifdef CPPHTTPLIB_ZLIB_SUPPORT
13646 if (compress_)
13647 {
13648 compressor = detail::make_unique<detail::gzip_compressor>();
13649 }
13650 else
13651#endif
13652 {
13653 compressor = detail::make_unique<detail::nocompressor>();
13654 }
13655
13656 return detail::write_content_chunked(strm, req.content_provider_,
13657 is_shutting_down, *compressor, error);
13658 }
13659 else
13660 {
13661 return detail::write_content_with_progress(
13662 strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
13663 req.upload_progress, error);
13664 }
13665}
13666
13667inline bool ClientImpl::write_request(Stream &strm, Request &req,
13668 bool close_connection, Error &error)
13669{
13670 // Prepare additional headers
13671 if (close_connection)
13672 {
13673 if (!req.has_header("Connection"))
13674 {
13675 req.set_header("Connection", "close");
13676 }
13677 }
13678
13679 std::string ct_for_defaults;
13680 if (!req.has_header("Content-Type") && !req.body.empty())
13681 {
13682 ct_for_defaults = "text/plain";
13683 }
13684 prepare_default_headers(req, false, ct_for_defaults);
13685
13686 if (req.body.empty())
13687 {
13688 if (req.content_provider_)
13689 {
13691 {
13692 if (!req.has_header("Content-Length"))
13693 {
13694 auto length = std::to_string(req.content_length_);
13695 req.set_header("Content-Length", length);
13696 }
13697 }
13698 }
13699 else
13700 {
13701 if (req.method == "POST" || req.method == "PUT" ||
13702 req.method == "PATCH")
13703 {
13704 req.set_header("Content-Length", "0");
13705 }
13706 }
13707 }
13708
13709 if (!basic_auth_password_.empty() || !basic_auth_username_.empty())
13710 {
13711 if (!req.has_header("Authorization"))
13712 {
13715 }
13716 }
13717
13718 if (!proxy_basic_auth_username_.empty() &&
13720 {
13721 if (!req.has_header("Proxy-Authorization"))
13722 {
13725 }
13726 }
13727
13728 if (!bearer_token_auth_token_.empty())
13729 {
13730 if (!req.has_header("Authorization"))
13731 {
13733 bearer_token_auth_token_, false));
13734 }
13735 }
13736
13737 if (!proxy_bearer_token_auth_token_.empty())
13738 {
13739 if (!req.has_header("Proxy-Authorization"))
13740 {
13743 }
13744 }
13745
13746 // Request line and headers
13747 {
13748 detail::BufferStream bstrm;
13749
13750 // Extract path and query from req.path
13751 std::string path_part, query_part;
13752 auto query_pos = req.path.find('?');
13753 if (query_pos != std::string::npos)
13754 {
13755 path_part = req.path.substr(0, query_pos);
13756 query_part = req.path.substr(query_pos + 1);
13757 }
13758 else
13759 {
13760 path_part = req.path;
13761 query_part = "";
13762 }
13763
13764 // Encode path part. If the original `req.path` already contained a
13765 // query component, preserve its raw query string (including parameter
13766 // order) instead of reparsing and reassembling it which may reorder
13767 // parameters due to container ordering (e.g. `Params` uses
13768 // `std::multimap`). When there is no query in `req.path`, fall back to
13769 // building a query from `req.params` so existing callers that pass
13770 // `Params` continue to work.
13771 auto path_with_query =
13772 path_encode_ ? detail::encode_path(path_part) : path_part;
13773
13774 if (!query_part.empty())
13775 {
13776 // Normalize the query string (decode then re-encode) while preserving
13777 // the original parameter order.
13778 auto normalized = detail::normalize_query_string(query_part);
13779 if (!normalized.empty())
13780 {
13781 path_with_query += '?' + normalized;
13782 }
13783
13784 // Still populate req.params for handlers/users who read them.
13785 detail::parse_query_text(query_part, req.params);
13786 }
13787 else
13788 {
13789 // No query in path; parse any query_part (empty) and append params
13790 // from `req.params` when present (preserves prior behavior for
13791 // callers who provide Params separately).
13792 detail::parse_query_text(query_part, req.params);
13793 if (!req.params.empty())
13794 {
13795 path_with_query = append_query_params(path_with_query, req.params);
13796 }
13797 }
13798
13799 // Write request line and headers
13800 detail::write_request_line(bstrm, req.method, path_with_query);
13801 header_writer_(bstrm, req.headers);
13802
13803 // Flush buffer
13804 auto &data = bstrm.get_buffer();
13805 if (!detail::write_data(strm, data.data(), data.size()))
13806 {
13808 output_error_log(error, &req);
13809 return false;
13810 }
13811 }
13812
13813 // Body
13814 if (req.body.empty())
13815 {
13816 return write_content_with_provider(strm, req, error);
13817 }
13818
13819 if (req.upload_progress)
13820 {
13821 auto body_size = req.body.size();
13822 size_t written = 0;
13823 auto data = req.body.data();
13824
13825 while (written < body_size)
13826 {
13827 size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
13828 if (!detail::write_data(strm, data + written, to_write))
13829 {
13831 output_error_log(error, &req);
13832 return false;
13833 }
13834 written += to_write;
13835
13836 if (!req.upload_progress(written, body_size))
13837 {
13839 output_error_log(error, &req);
13840 return false;
13841 }
13842 }
13843 }
13844 else
13845 {
13846 if (!detail::write_data(strm, req.body.data(), req.body.size()))
13847 {
13849 output_error_log(error, &req);
13850 return false;
13851 }
13852 }
13853
13854 return true;
13855}
13856
13857inline std::unique_ptr<Response>
13858ClientImpl::send_with_content_provider_and_receiver(
13859 Request &req, const char *body, size_t content_length,
13860 ContentProvider content_provider,
13861 ContentProviderWithoutLength content_provider_without_length,
13862 const std::string &content_type, ContentReceiver content_receiver,
13863 Error &error)
13864{
13865 if (!content_type.empty())
13866 {
13867 req.set_header("Content-Type", content_type);
13868 }
13869
13870#ifdef CPPHTTPLIB_ZLIB_SUPPORT
13871 if (compress_)
13872 {
13873 req.set_header("Content-Encoding", "gzip");
13874 }
13875#endif
13876
13877#ifdef CPPHTTPLIB_ZLIB_SUPPORT
13878 if (compress_ && !content_provider_without_length)
13879 {
13880 // TODO: Brotli support
13881 detail::gzip_compressor compressor;
13882
13883 if (content_provider)
13884 {
13885 auto ok = true;
13886 size_t offset = 0;
13887 DataSink data_sink;
13888
13889 data_sink.write = [&](const char *data, size_t data_len) -> bool
13890 {
13891 if (ok)
13892 {
13893 auto last = offset + data_len == content_length;
13894
13895 auto ret = compressor.compress(
13896 data, data_len, last,
13897 [&](const char *compressed_data, size_t compressed_data_len)
13898 {
13899 req.body.append(compressed_data, compressed_data_len);
13900 return true;
13901 });
13902
13903 if (ret)
13904 {
13905 offset += data_len;
13906 }
13907 else
13908 {
13909 ok = false;
13910 }
13911 }
13912 return ok;
13913 };
13914
13915 while (ok && offset < content_length)
13916 {
13917 if (!content_provider(offset, content_length - offset, data_sink))
13918 {
13920 output_error_log(error, &req);
13921 return nullptr;
13922 }
13923 }
13924 }
13925 else
13926 {
13927 if (!compressor.compress(body, content_length, true,
13928 [&](const char *data, size_t data_len)
13929 {
13930 req.body.append(data, data_len);
13931 return true;
13932 }))
13933 {
13935 output_error_log(error, &req);
13936 return nullptr;
13937 }
13938 }
13939 }
13940 else
13941#endif
13942 {
13943 if (content_provider)
13944 {
13945 req.content_length_ = content_length;
13946 req.content_provider_ = std::move(content_provider);
13947 req.is_chunked_content_provider_ = false;
13948 }
13949 else if (content_provider_without_length)
13950 {
13951 req.content_length_ = 0;
13952 req.content_provider_ = detail::ContentProviderAdapter(
13953 std::move(content_provider_without_length));
13954 req.is_chunked_content_provider_ = true;
13955 req.set_header("Transfer-Encoding", "chunked");
13956 }
13957 else
13958 {
13959 req.body.assign(body, content_length);
13960 }
13961 }
13962
13963 if (content_receiver)
13964 {
13965 req.content_receiver =
13966 [content_receiver](const char *data, size_t data_length,
13967 size_t /*offset*/, size_t /*total_length*/)
13968 {
13969 return content_receiver(data, data_length);
13970 };
13971 }
13972
13973 auto res = detail::make_unique<Response>();
13974 return send(req, *res, error) ? std::move(res) : nullptr;
13975}
13976
13977inline Result ClientImpl::send_with_content_provider_and_receiver(
13978 const std::string &method, const std::string &path, const Headers &headers,
13979 const char *body, size_t content_length, ContentProvider content_provider,
13980 ContentProviderWithoutLength content_provider_without_length,
13981 const std::string &content_type, ContentReceiver content_receiver,
13982 UploadProgress progress)
13983{
13984 Request req;
13985 req.method = method;
13986 req.headers = headers;
13987 req.path = path;
13988 req.upload_progress = std::move(progress);
13989 if (max_timeout_msec_ > 0)
13990 {
13991 req.start_time_ = std::chrono::steady_clock::now();
13992 }
13993
13994 auto error = Error::Success;
13995
13996 auto res = send_with_content_provider_and_receiver(
13997 req, body, content_length, std::move(content_provider),
13998 std::move(content_provider_without_length), content_type,
13999 std::move(content_receiver), error);
14000
14001#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14002 return Result {std::move(res), error, std::move(req.headers), last_ssl_error_,
14003 last_openssl_error_
14004 };
14005#else
14006 return Result {std::move(res), error, std::move(req.headers)};
14007#endif
14008}
14009
14010inline void ClientImpl::output_log(const Request &req,
14011 const Response &res) const
14012{
14013 if (logger_)
14014 {
14015 std::lock_guard<std::mutex> guard(logger_mutex_);
14016 logger_(req, res);
14017 }
14018}
14019
14021 const Request *req) const
14022{
14023 if (error_logger_)
14024 {
14025 std::lock_guard<std::mutex> guard(logger_mutex_);
14026 error_logger_(err, req);
14027 }
14028}
14029
14031 Response &res, bool close_connection,
14032 Error &error)
14033{
14034 // Send request
14035 if (!write_request(strm, req, close_connection, error))
14036 {
14037 return false;
14038 }
14039
14040#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14041 if (is_ssl())
14042 {
14043 auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
14044 if (!is_proxy_enabled)
14045 {
14046 if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock))
14047 {
14049 output_error_log(error, &req);
14050 return false;
14051 }
14052 }
14053 }
14054#endif
14055
14056 // Receive response and headers
14057 if (!read_response_line(strm, req, res) ||
14058 !detail::read_headers(strm, res.headers))
14059 {
14060 error = Error::Read;
14061 output_error_log(error, &req);
14062 return false;
14063 }
14064
14065 // Body
14066 if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
14067 req.method != "CONNECT")
14068 {
14069 auto redirect = 300 < res.status && res.status < 400 &&
14072
14073 if (req.response_handler && !redirect)
14074 {
14075 if (!req.response_handler(res))
14076 {
14077 error = Error::Canceled;
14078 output_error_log(error, &req);
14079 return false;
14080 }
14081 }
14082
14083 auto out =
14085 ? static_cast<ContentReceiverWithProgress>(
14086 [&](const char *buf, size_t n, size_t off, size_t len)
14087 {
14088 if (redirect)
14089 {
14090 return true;
14091 }
14092 auto ret = req.content_receiver(buf, n, off, len);
14093 if (!ret)
14094 {
14095 error = Error::Canceled;
14096 output_error_log(error, &req);
14097 }
14098 return ret;
14099 })
14100 : static_cast<ContentReceiverWithProgress>(
14101 [&](const char *buf, size_t n, size_t /*off*/,
14102 size_t /*len*/)
14103 {
14104 assert(res.body.size() + n <= res.body.max_size());
14105 res.body.append(buf, n);
14106 return true;
14107 });
14108
14109 auto progress = [&](size_t current, size_t total)
14110 {
14111 if (!req.download_progress || redirect)
14112 {
14113 return true;
14114 }
14115 auto ret = req.download_progress(current, total);
14116 if (!ret)
14117 {
14118 error = Error::Canceled;
14119 output_error_log(error, &req);
14120 }
14121 return ret;
14122 };
14123
14124 if (res.has_header("Content-Length"))
14125 {
14126 if (!req.content_receiver)
14127 {
14128 auto len = res.get_header_value_u64("Content-Length");
14129 if (len > res.body.max_size())
14130 {
14131 error = Error::Read;
14132 output_error_log(error, &req);
14133 return false;
14134 }
14135 res.body.reserve(static_cast<size_t>(len));
14136 }
14137 }
14138
14140 {
14141 int dummy_status;
14142 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
14143 dummy_status, std::move(progress),
14144 std::move(out), decompress_))
14145 {
14146 if (error != Error::Canceled)
14147 {
14148 error = Error::Read;
14149 }
14150 output_error_log(error, &req);
14151 return false;
14152 }
14153 }
14154 }
14155
14156 // Log
14157 output_log(req, res);
14158
14159 return true;
14160}
14161
14162inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
14163 const std::string &boundary, const UploadFormDataItems &items,
14164 const FormDataProviderItems &provider_items) const
14165{
14166 size_t cur_item = 0;
14167 size_t cur_start = 0;
14168 // cur_item and cur_start are copied to within the std::function and
14169 // maintain state between successive calls
14170 return [ &, cur_item, cur_start](size_t offset,
14171 DataSink & sink) mutable -> bool
14172 {
14173 if (!offset && !items.empty())
14174 {
14175 sink.os << detail::serialize_multipart_formdata(items, boundary, false);
14176 return true;
14177 }
14178 else if (cur_item < provider_items.size())
14179 {
14180 if (!cur_start)
14181 {
14182 const auto &begin = detail::serialize_multipart_formdata_item_begin(
14183 provider_items[cur_item], boundary);
14184 offset += begin.size();
14185 cur_start = offset;
14186 sink.os << begin;
14187 }
14188
14189 DataSink cur_sink;
14190 auto has_data = true;
14191 cur_sink.write = sink.write;
14192 cur_sink.done = [&]()
14193 {
14194 has_data = false;
14195 };
14196
14197 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
14198 {
14199 return false;
14200 }
14201
14202 if (!has_data)
14203 {
14204 sink.os << detail::serialize_multipart_formdata_item_end();
14205 cur_item++;
14206 cur_start = 0;
14207 }
14208 return true;
14209 }
14210 else
14211 {
14212 sink.os << detail::serialize_multipart_formdata_finish(boundary);
14213 sink.done();
14214 return true;
14215 }
14216 };
14217}
14218
14219inline bool ClientImpl::process_socket(
14220 const Socket &socket,
14221 std::chrono::time_point<std::chrono::steady_clock> start_time,
14222 std::function<bool(Stream &strm)> callback)
14223{
14224 return detail::process_client_socket(
14226 write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
14227}
14228
14229inline bool ClientImpl::is_ssl() const
14230{
14231 return false;
14232}
14233
14234inline Result ClientImpl::Get(const std::string &path,
14235 DownloadProgress progress)
14236{
14237 return Get(path, Headers(), std::move(progress));
14238}
14239
14240inline Result ClientImpl::Get(const std::string &path, const Params &params,
14241 const Headers &headers,
14242 DownloadProgress progress)
14243{
14244 if (params.empty())
14245 {
14246 return Get(path, headers);
14247 }
14248
14249 std::string path_with_query = append_query_params(path, params);
14250 return Get(path_with_query, headers, std::move(progress));
14251}
14252
14253inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
14254 DownloadProgress progress)
14255{
14256 Request req;
14257 req.method = "GET";
14258 req.path = path;
14259 req.headers = headers;
14260 req.download_progress = std::move(progress);
14261 if (max_timeout_msec_ > 0)
14262 {
14263 req.start_time_ = std::chrono::steady_clock::now();
14264 }
14265
14266 return send_(std::move(req));
14267}
14268
14269inline Result ClientImpl::Get(const std::string &path,
14270 ContentReceiver content_receiver,
14271 DownloadProgress progress)
14272{
14273 return Get(path, Headers(), nullptr, std::move(content_receiver),
14274 std::move(progress));
14275}
14276
14277inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
14278 ContentReceiver content_receiver,
14279 DownloadProgress progress)
14280{
14281 return Get(path, headers, nullptr, std::move(content_receiver),
14282 std::move(progress));
14283}
14284
14285inline Result ClientImpl::Get(const std::string &path,
14286 ResponseHandler response_handler,
14287 ContentReceiver content_receiver,
14288 DownloadProgress progress)
14289{
14290 return Get(path, Headers(), std::move(response_handler),
14291 std::move(content_receiver), std::move(progress));
14292}
14293
14294inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
14295 ResponseHandler response_handler,
14296 ContentReceiver content_receiver,
14297 DownloadProgress progress)
14298{
14299 Request req;
14300 req.method = "GET";
14301 req.path = path;
14302 req.headers = headers;
14303 req.response_handler = std::move(response_handler);
14304 req.content_receiver =
14305 [content_receiver](const char *data, size_t data_length,
14306 size_t /*offset*/, size_t /*total_length*/)
14307 {
14308 return content_receiver(data, data_length);
14309 };
14310 req.download_progress = std::move(progress);
14311 if (max_timeout_msec_ > 0)
14312 {
14313 req.start_time_ = std::chrono::steady_clock::now();
14314 }
14315
14316 return send_(std::move(req));
14317}
14318
14319inline Result ClientImpl::Get(const std::string &path, const Params &params,
14320 const Headers &headers,
14321 ContentReceiver content_receiver,
14322 DownloadProgress progress)
14323{
14324 return Get(path, params, headers, nullptr, std::move(content_receiver),
14325 std::move(progress));
14326}
14327
14328inline Result ClientImpl::Get(const std::string &path, const Params &params,
14329 const Headers &headers,
14330 ResponseHandler response_handler,
14331 ContentReceiver content_receiver,
14332 DownloadProgress progress)
14333{
14334 if (params.empty())
14335 {
14336 return Get(path, headers, std::move(response_handler),
14337 std::move(content_receiver), std::move(progress));
14338 }
14339
14340 std::string path_with_query = append_query_params(path, params);
14341 return Get(path_with_query, headers, std::move(response_handler),
14342 std::move(content_receiver), std::move(progress));
14343}
14344
14345inline Result ClientImpl::Head(const std::string &path)
14346{
14347 return Head(path, Headers());
14348}
14349
14350inline Result ClientImpl::Head(const std::string &path,
14351 const Headers &headers)
14352{
14353 Request req;
14354 req.method = "HEAD";
14355 req.headers = headers;
14356 req.path = path;
14357 if (max_timeout_msec_ > 0)
14358 {
14359 req.start_time_ = std::chrono::steady_clock::now();
14360 }
14361
14362 return send_(std::move(req));
14363}
14364
14365inline Result ClientImpl::Post(const std::string &path)
14366{
14367 return Post(path, std::string(), std::string());
14368}
14369
14370inline Result ClientImpl::Post(const std::string &path,
14371 const Headers &headers)
14372{
14373 return Post(path, headers, nullptr, 0, std::string());
14374}
14375
14376inline Result ClientImpl::Post(const std::string &path, const char *body,
14377 size_t content_length,
14378 const std::string &content_type,
14379 UploadProgress progress)
14380{
14381 return Post(path, Headers(), body, content_length, content_type, progress);
14382}
14383
14384inline Result ClientImpl::Post(const std::string &path, const std::string &body,
14385 const std::string &content_type,
14386 UploadProgress progress)
14387{
14388 return Post(path, Headers(), body, content_type, progress);
14389}
14390
14391inline Result ClientImpl::Post(const std::string &path, const Params &params)
14392{
14393 return Post(path, Headers(), params);
14394}
14395
14396inline Result ClientImpl::Post(const std::string &path, size_t content_length,
14397 ContentProvider content_provider,
14398 const std::string &content_type,
14399 UploadProgress progress)
14400{
14401 return Post(path, Headers(), content_length, std::move(content_provider),
14402 content_type, progress);
14403}
14404
14405inline Result ClientImpl::Post(const std::string &path, size_t content_length,
14406 ContentProvider content_provider,
14407 const std::string &content_type,
14408 ContentReceiver content_receiver,
14409 UploadProgress progress)
14410{
14411 return Post(path, Headers(), content_length, std::move(content_provider),
14412 content_type, std::move(content_receiver), progress);
14413}
14414
14415inline Result ClientImpl::Post(const std::string &path,
14416 ContentProviderWithoutLength content_provider,
14417 const std::string &content_type,
14418 UploadProgress progress)
14419{
14420 return Post(path, Headers(), std::move(content_provider), content_type,
14421 progress);
14422}
14423
14424inline Result ClientImpl::Post(const std::string &path,
14425 ContentProviderWithoutLength content_provider,
14426 const std::string &content_type,
14427 ContentReceiver content_receiver,
14428 UploadProgress progress)
14429{
14430 return Post(path, Headers(), std::move(content_provider), content_type,
14431 std::move(content_receiver), progress);
14432}
14433
14434inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14435 const Params &params)
14436{
14437 auto query = detail::params_to_query_str(params);
14438 return Post(path, headers, query, "application/x-www-form-urlencoded");
14439}
14440
14441inline Result ClientImpl::Post(const std::string &path,
14442 const UploadFormDataItems &items,
14443 UploadProgress progress)
14444{
14445 return Post(path, Headers(), items, progress);
14446}
14447
14448inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14449 const UploadFormDataItems &items,
14450 UploadProgress progress)
14451{
14452 const auto &boundary = detail::make_multipart_data_boundary();
14453 const auto &content_type =
14454 detail::serialize_multipart_formdata_get_content_type(boundary);
14455 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14456 return Post(path, headers, body, content_type, progress);
14457}
14458
14459inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14460 const UploadFormDataItems &items,
14461 const std::string &boundary,
14462 UploadProgress progress)
14463{
14464 if (!detail::is_multipart_boundary_chars_valid(boundary))
14465 {
14467 }
14468
14469 const auto &content_type =
14470 detail::serialize_multipart_formdata_get_content_type(boundary);
14471 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14472 return Post(path, headers, body, content_type, progress);
14473}
14474
14475inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14476 const char *body, size_t content_length,
14477 const std::string &content_type,
14478 UploadProgress progress)
14479{
14480 return send_with_content_provider_and_receiver(
14481 "POST", path, headers, body, content_length, nullptr, nullptr,
14482 content_type, nullptr, progress);
14483}
14484
14485inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14486 const std::string &body,
14487 const std::string &content_type,
14488 UploadProgress progress)
14489{
14490 return send_with_content_provider_and_receiver(
14491 "POST", path, headers, body.data(), body.size(), nullptr, nullptr,
14492 content_type, nullptr, progress);
14493}
14494
14495inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14496 size_t content_length,
14497 ContentProvider content_provider,
14498 const std::string &content_type,
14499 UploadProgress progress)
14500{
14501 return send_with_content_provider_and_receiver(
14502 "POST", path, headers, nullptr, content_length,
14503 std::move(content_provider), nullptr, content_type, nullptr, progress);
14504}
14505
14506inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14507 size_t content_length,
14508 ContentProvider content_provider,
14509 const std::string &content_type,
14510 ContentReceiver content_receiver,
14511 DownloadProgress progress)
14512{
14513 return send_with_content_provider_and_receiver(
14514 "POST", path, headers, nullptr, content_length,
14515 std::move(content_provider), nullptr, content_type,
14516 std::move(content_receiver), std::move(progress));
14517}
14518
14519inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14520 ContentProviderWithoutLength content_provider,
14521 const std::string &content_type,
14522 UploadProgress progress)
14523{
14524 return send_with_content_provider_and_receiver(
14525 "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14526 content_type, nullptr, progress);
14527}
14528
14529inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14530 ContentProviderWithoutLength content_provider,
14531 const std::string &content_type,
14532 ContentReceiver content_receiver,
14533 DownloadProgress progress)
14534{
14535 return send_with_content_provider_and_receiver(
14536 "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14537 content_type, std::move(content_receiver), std::move(progress));
14538}
14539
14540inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14541 const UploadFormDataItems &items,
14542 const FormDataProviderItems &provider_items,
14543 UploadProgress progress)
14544{
14545 const auto &boundary = detail::make_multipart_data_boundary();
14546 const auto &content_type =
14547 detail::serialize_multipart_formdata_get_content_type(boundary);
14548 return send_with_content_provider_and_receiver(
14549 "POST", path, headers, nullptr, 0, nullptr,
14550 get_multipart_content_provider(boundary, items, provider_items),
14551 content_type, nullptr, progress);
14552}
14553
14554inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14555 const std::string &body,
14556 const std::string &content_type,
14557 ContentReceiver content_receiver,
14558 DownloadProgress progress)
14559{
14560 Request req;
14561 req.method = "POST";
14562 req.path = path;
14563 req.headers = headers;
14564 req.body = body;
14565 req.content_receiver =
14566 [content_receiver](const char *data, size_t data_length,
14567 size_t /*offset*/, size_t /*total_length*/)
14568 {
14569 return content_receiver(data, data_length);
14570 };
14571 req.download_progress = std::move(progress);
14572
14573 if (max_timeout_msec_ > 0)
14574 {
14575 req.start_time_ = std::chrono::steady_clock::now();
14576 }
14577
14578 if (!content_type.empty())
14579 {
14580 req.set_header("Content-Type", content_type);
14581 }
14582
14583 return send_(std::move(req));
14584}
14585
14586inline Result ClientImpl::Put(const std::string &path)
14587{
14588 return Put(path, std::string(), std::string());
14589}
14590
14591inline Result ClientImpl::Put(const std::string &path, const Headers &headers)
14592{
14593 return Put(path, headers, nullptr, 0, std::string());
14594}
14595
14596inline Result ClientImpl::Put(const std::string &path, const char *body,
14597 size_t content_length,
14598 const std::string &content_type,
14599 UploadProgress progress)
14600{
14601 return Put(path, Headers(), body, content_length, content_type, progress);
14602}
14603
14604inline Result ClientImpl::Put(const std::string &path, const std::string &body,
14605 const std::string &content_type,
14606 UploadProgress progress)
14607{
14608 return Put(path, Headers(), body, content_type, progress);
14609}
14610
14611inline Result ClientImpl::Put(const std::string &path, const Params &params)
14612{
14613 return Put(path, Headers(), params);
14614}
14615
14616inline Result ClientImpl::Put(const std::string &path, size_t content_length,
14617 ContentProvider content_provider,
14618 const std::string &content_type,
14619 UploadProgress progress)
14620{
14621 return Put(path, Headers(), content_length, std::move(content_provider),
14622 content_type, progress);
14623}
14624
14625inline Result ClientImpl::Put(const std::string &path, size_t content_length,
14626 ContentProvider content_provider,
14627 const std::string &content_type,
14628 ContentReceiver content_receiver,
14629 UploadProgress progress)
14630{
14631 return Put(path, Headers(), content_length, std::move(content_provider),
14632 content_type, std::move(content_receiver), progress);
14633}
14634
14635inline Result ClientImpl::Put(const std::string &path,
14636 ContentProviderWithoutLength content_provider,
14637 const std::string &content_type,
14638 UploadProgress progress)
14639{
14640 return Put(path, Headers(), std::move(content_provider), content_type,
14641 progress);
14642}
14643
14644inline Result ClientImpl::Put(const std::string &path,
14645 ContentProviderWithoutLength content_provider,
14646 const std::string &content_type,
14647 ContentReceiver content_receiver,
14648 UploadProgress progress)
14649{
14650 return Put(path, Headers(), std::move(content_provider), content_type,
14651 std::move(content_receiver), progress);
14652}
14653
14654inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14655 const Params &params)
14656{
14657 auto query = detail::params_to_query_str(params);
14658 return Put(path, headers, query, "application/x-www-form-urlencoded");
14659}
14660
14661inline Result ClientImpl::Put(const std::string &path,
14662 const UploadFormDataItems &items,
14663 UploadProgress progress)
14664{
14665 return Put(path, Headers(), items, progress);
14666}
14667
14668inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14669 const UploadFormDataItems &items,
14670 UploadProgress progress)
14671{
14672 const auto &boundary = detail::make_multipart_data_boundary();
14673 const auto &content_type =
14674 detail::serialize_multipart_formdata_get_content_type(boundary);
14675 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14676 return Put(path, headers, body, content_type, progress);
14677}
14678
14679inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14680 const UploadFormDataItems &items,
14681 const std::string &boundary,
14682 UploadProgress progress)
14683{
14684 if (!detail::is_multipart_boundary_chars_valid(boundary))
14685 {
14687 }
14688
14689 const auto &content_type =
14690 detail::serialize_multipart_formdata_get_content_type(boundary);
14691 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14692 return Put(path, headers, body, content_type, progress);
14693}
14694
14695inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14696 const char *body, size_t content_length,
14697 const std::string &content_type,
14698 UploadProgress progress)
14699{
14700 return send_with_content_provider_and_receiver(
14701 "PUT", path, headers, body, content_length, nullptr, nullptr,
14702 content_type, nullptr, progress);
14703}
14704
14705inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14706 const std::string &body,
14707 const std::string &content_type,
14708 UploadProgress progress)
14709{
14710 return send_with_content_provider_and_receiver(
14711 "PUT", path, headers, body.data(), body.size(), nullptr, nullptr,
14712 content_type, nullptr, progress);
14713}
14714
14715inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14716 size_t content_length,
14717 ContentProvider content_provider,
14718 const std::string &content_type,
14719 UploadProgress progress)
14720{
14721 return send_with_content_provider_and_receiver(
14722 "PUT", path, headers, nullptr, content_length,
14723 std::move(content_provider), nullptr, content_type, nullptr, progress);
14724}
14725
14726inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14727 size_t content_length,
14728 ContentProvider content_provider,
14729 const std::string &content_type,
14730 ContentReceiver content_receiver,
14731 UploadProgress progress)
14732{
14733 return send_with_content_provider_and_receiver(
14734 "PUT", path, headers, nullptr, content_length,
14735 std::move(content_provider), nullptr, content_type,
14736 std::move(content_receiver), progress);
14737}
14738
14739inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14740 ContentProviderWithoutLength content_provider,
14741 const std::string &content_type,
14742 UploadProgress progress)
14743{
14744 return send_with_content_provider_and_receiver(
14745 "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14746 content_type, nullptr, progress);
14747}
14748
14749inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14750 ContentProviderWithoutLength content_provider,
14751 const std::string &content_type,
14752 ContentReceiver content_receiver,
14753 UploadProgress progress)
14754{
14755 return send_with_content_provider_and_receiver(
14756 "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14757 content_type, std::move(content_receiver), progress);
14758}
14759
14760inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14761 const UploadFormDataItems &items,
14762 const FormDataProviderItems &provider_items,
14763 UploadProgress progress)
14764{
14765 const auto &boundary = detail::make_multipart_data_boundary();
14766 const auto &content_type =
14767 detail::serialize_multipart_formdata_get_content_type(boundary);
14768 return send_with_content_provider_and_receiver(
14769 "PUT", path, headers, nullptr, 0, nullptr,
14770 get_multipart_content_provider(boundary, items, provider_items),
14771 content_type, nullptr, progress);
14772}
14773
14774inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14775 const std::string &body,
14776 const std::string &content_type,
14777 ContentReceiver content_receiver,
14778 DownloadProgress progress)
14779{
14780 Request req;
14781 req.method = "PUT";
14782 req.path = path;
14783 req.headers = headers;
14784 req.body = body;
14785 req.content_receiver =
14786 [content_receiver](const char *data, size_t data_length,
14787 size_t /*offset*/, size_t /*total_length*/)
14788 {
14789 return content_receiver(data, data_length);
14790 };
14791 req.download_progress = std::move(progress);
14792
14793 if (max_timeout_msec_ > 0)
14794 {
14795 req.start_time_ = std::chrono::steady_clock::now();
14796 }
14797
14798 if (!content_type.empty())
14799 {
14800 req.set_header("Content-Type", content_type);
14801 }
14802
14803 return send_(std::move(req));
14804}
14805
14806inline Result ClientImpl::Patch(const std::string &path)
14807{
14808 return Patch(path, std::string(), std::string());
14809}
14810
14811inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14812 UploadProgress progress)
14813{
14814 return Patch(path, headers, nullptr, 0, std::string(), progress);
14815}
14816
14817inline Result ClientImpl::Patch(const std::string &path, const char *body,
14818 size_t content_length,
14819 const std::string &content_type,
14820 UploadProgress progress)
14821{
14822 return Patch(path, Headers(), body, content_length, content_type, progress);
14823}
14824
14825inline Result ClientImpl::Patch(const std::string &path,
14826 const std::string &body,
14827 const std::string &content_type,
14828 UploadProgress progress)
14829{
14830 return Patch(path, Headers(), body, content_type, progress);
14831}
14832
14833inline Result ClientImpl::Patch(const std::string &path, const Params &params)
14834{
14835 return Patch(path, Headers(), params);
14836}
14837
14838inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
14839 ContentProvider content_provider,
14840 const std::string &content_type,
14841 UploadProgress progress)
14842{
14843 return Patch(path, Headers(), content_length, std::move(content_provider),
14844 content_type, progress);
14845}
14846
14847inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
14848 ContentProvider content_provider,
14849 const std::string &content_type,
14850 ContentReceiver content_receiver,
14851 UploadProgress progress)
14852{
14853 return Patch(path, Headers(), content_length, std::move(content_provider),
14854 content_type, std::move(content_receiver), progress);
14855}
14856
14857inline Result ClientImpl::Patch(const std::string &path,
14858 ContentProviderWithoutLength content_provider,
14859 const std::string &content_type,
14860 UploadProgress progress)
14861{
14862 return Patch(path, Headers(), std::move(content_provider), content_type,
14863 progress);
14864}
14865
14866inline Result ClientImpl::Patch(const std::string &path,
14867 ContentProviderWithoutLength content_provider,
14868 const std::string &content_type,
14869 ContentReceiver content_receiver,
14870 UploadProgress progress)
14871{
14872 return Patch(path, Headers(), std::move(content_provider), content_type,
14873 std::move(content_receiver), progress);
14874}
14875
14876inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14877 const Params &params)
14878{
14879 auto query = detail::params_to_query_str(params);
14880 return Patch(path, headers, query, "application/x-www-form-urlencoded");
14881}
14882
14883inline Result ClientImpl::Patch(const std::string &path,
14884 const UploadFormDataItems &items,
14885 UploadProgress progress)
14886{
14887 return Patch(path, Headers(), items, progress);
14888}
14889
14890inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14891 const UploadFormDataItems &items,
14892 UploadProgress progress)
14893{
14894 const auto &boundary = detail::make_multipart_data_boundary();
14895 const auto &content_type =
14896 detail::serialize_multipart_formdata_get_content_type(boundary);
14897 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14898 return Patch(path, headers, body, content_type, progress);
14899}
14900
14901inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14902 const UploadFormDataItems &items,
14903 const std::string &boundary,
14904 UploadProgress progress)
14905{
14906 if (!detail::is_multipart_boundary_chars_valid(boundary))
14907 {
14909 }
14910
14911 const auto &content_type =
14912 detail::serialize_multipart_formdata_get_content_type(boundary);
14913 const auto &body = detail::serialize_multipart_formdata(items, boundary);
14914 return Patch(path, headers, body, content_type, progress);
14915}
14916
14917inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14918 const char *body, size_t content_length,
14919 const std::string &content_type,
14920 UploadProgress progress)
14921{
14922 return send_with_content_provider_and_receiver(
14923 "PATCH", path, headers, body, content_length, nullptr, nullptr,
14924 content_type, nullptr, progress);
14925}
14926
14927inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14928 const std::string &body,
14929 const std::string &content_type,
14930 UploadProgress progress)
14931{
14932 return send_with_content_provider_and_receiver(
14933 "PATCH", path, headers, body.data(), body.size(), nullptr, nullptr,
14934 content_type, nullptr, progress);
14935}
14936
14937inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14938 size_t content_length,
14939 ContentProvider content_provider,
14940 const std::string &content_type,
14941 UploadProgress progress)
14942{
14943 return send_with_content_provider_and_receiver(
14944 "PATCH", path, headers, nullptr, content_length,
14945 std::move(content_provider), nullptr, content_type, nullptr, progress);
14946}
14947
14948inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14949 size_t content_length,
14950 ContentProvider content_provider,
14951 const std::string &content_type,
14952 ContentReceiver content_receiver,
14953 UploadProgress progress)
14954{
14955 return send_with_content_provider_and_receiver(
14956 "PATCH", path, headers, nullptr, content_length,
14957 std::move(content_provider), nullptr, content_type,
14958 std::move(content_receiver), progress);
14959}
14960
14961inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14962 ContentProviderWithoutLength content_provider,
14963 const std::string &content_type,
14964 UploadProgress progress)
14965{
14966 return send_with_content_provider_and_receiver(
14967 "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14968 content_type, nullptr, progress);
14969}
14970
14971inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14972 ContentProviderWithoutLength content_provider,
14973 const std::string &content_type,
14974 ContentReceiver content_receiver,
14975 UploadProgress progress)
14976{
14977 return send_with_content_provider_and_receiver(
14978 "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14979 content_type, std::move(content_receiver), progress);
14980}
14981
14982inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14983 const UploadFormDataItems &items,
14984 const FormDataProviderItems &provider_items,
14985 UploadProgress progress)
14986{
14987 const auto &boundary = detail::make_multipart_data_boundary();
14988 const auto &content_type =
14989 detail::serialize_multipart_formdata_get_content_type(boundary);
14990 return send_with_content_provider_and_receiver(
14991 "PATCH", path, headers, nullptr, 0, nullptr,
14992 get_multipart_content_provider(boundary, items, provider_items),
14993 content_type, nullptr, progress);
14994}
14995
14996inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14997 const std::string &body,
14998 const std::string &content_type,
14999 ContentReceiver content_receiver,
15000 DownloadProgress progress)
15001{
15002 Request req;
15003 req.method = "PATCH";
15004 req.path = path;
15005 req.headers = headers;
15006 req.body = body;
15007 req.content_receiver =
15008 [content_receiver](const char *data, size_t data_length,
15009 size_t /*offset*/, size_t /*total_length*/)
15010 {
15011 return content_receiver(data, data_length);
15012 };
15013 req.download_progress = std::move(progress);
15014
15015 if (max_timeout_msec_ > 0)
15016 {
15017 req.start_time_ = std::chrono::steady_clock::now();
15018 }
15019
15020 if (!content_type.empty())
15021 {
15022 req.set_header("Content-Type", content_type);
15023 }
15024
15025 return send_(std::move(req));
15026}
15027
15028inline Result ClientImpl::Delete(const std::string &path,
15029 DownloadProgress progress)
15030{
15031 return Delete(path, Headers(), std::string(), std::string(), progress);
15032}
15033
15034inline Result ClientImpl::Delete(const std::string &path,
15035 const Headers &headers,
15036 DownloadProgress progress)
15037{
15038 return Delete(path, headers, std::string(), std::string(), progress);
15039}
15040
15041inline Result ClientImpl::Delete(const std::string &path, const char *body,
15042 size_t content_length,
15043 const std::string &content_type,
15044 DownloadProgress progress)
15045{
15046 return Delete(path, Headers(), body, content_length, content_type, progress);
15047}
15048
15049inline Result ClientImpl::Delete(const std::string &path,
15050 const std::string &body,
15051 const std::string &content_type,
15052 DownloadProgress progress)
15053{
15054 return Delete(path, Headers(), body.data(), body.size(), content_type,
15055 progress);
15056}
15057
15058inline Result ClientImpl::Delete(const std::string &path,
15059 const Headers &headers,
15060 const std::string &body,
15061 const std::string &content_type,
15062 DownloadProgress progress)
15063{
15064 return Delete(path, headers, body.data(), body.size(), content_type,
15065 progress);
15066}
15067
15068inline Result ClientImpl::Delete(const std::string &path, const Params &params,
15069 DownloadProgress progress)
15070{
15071 return Delete(path, Headers(), params, progress);
15072}
15073
15074inline Result ClientImpl::Delete(const std::string &path,
15075 const Headers &headers, const Params &params,
15076 DownloadProgress progress)
15077{
15078 auto query = detail::params_to_query_str(params);
15079 return Delete(path, headers, query, "application/x-www-form-urlencoded",
15080 progress);
15081}
15082
15083inline Result ClientImpl::Delete(const std::string &path,
15084 const Headers &headers, const char *body,
15085 size_t content_length,
15086 const std::string &content_type,
15087 DownloadProgress progress)
15088{
15089 Request req;
15090 req.method = "DELETE";
15091 req.headers = headers;
15092 req.path = path;
15093 req.download_progress = std::move(progress);
15094 if (max_timeout_msec_ > 0)
15095 {
15096 req.start_time_ = std::chrono::steady_clock::now();
15097 }
15098
15099 if (!content_type.empty())
15100 {
15101 req.set_header("Content-Type", content_type);
15102 }
15103 req.body.assign(body, content_length);
15104
15105 return send_(std::move(req));
15106}
15107
15108inline Result ClientImpl::Options(const std::string &path)
15109{
15110 return Options(path, Headers());
15111}
15112
15113inline Result ClientImpl::Options(const std::string &path,
15114 const Headers &headers)
15115{
15116 Request req;
15117 req.method = "OPTIONS";
15118 req.headers = headers;
15119 req.path = path;
15120 if (max_timeout_msec_ > 0)
15121 {
15122 req.start_time_ = std::chrono::steady_clock::now();
15123 }
15124
15125 return send_(std::move(req));
15126}
15127
15128inline void ClientImpl::stop()
15129{
15130 std::lock_guard<std::mutex> guard(socket_mutex_);
15131
15132 // If there is anything ongoing right now, the ONLY thread-safe thing we can
15133 // do is to shutdown_socket, so that threads using this socket suddenly
15134 // discover they can't read/write any more and error out. Everything else
15135 // (closing the socket, shutting ssl down) is unsafe because these actions
15136 // are not thread-safe.
15138 {
15140
15141 // Aside from that, we set a flag for the socket to be closed when we're
15142 // done.
15144 return;
15145 }
15146
15147 // Otherwise, still holding the mutex, we can shut everything down ourselves
15148 shutdown_ssl(socket_, true);
15151}
15152
15153inline std::string ClientImpl::host() const
15154{
15155 return host_;
15156}
15157
15158inline int ClientImpl::port() const
15159{
15160 return port_;
15161}
15162
15163inline size_t ClientImpl::is_socket_open() const
15164{
15165 std::lock_guard<std::mutex> guard(socket_mutex_);
15166 return socket_.is_open();
15167}
15168
15170{
15171 return socket_.sock;
15172}
15173
15174inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec)
15175{
15178}
15179
15180inline void ClientImpl::set_read_timeout(time_t sec, time_t usec)
15181{
15182 read_timeout_sec_ = sec;
15183 read_timeout_usec_ = usec;
15184}
15185
15186inline void ClientImpl::set_write_timeout(time_t sec, time_t usec)
15187{
15188 write_timeout_sec_ = sec;
15189 write_timeout_usec_ = usec;
15190}
15191
15192inline void ClientImpl::set_max_timeout(time_t msec)
15193{
15194 max_timeout_msec_ = msec;
15195}
15196
15197inline void ClientImpl::set_basic_auth(const std::string &username,
15198 const std::string &password)
15199{
15200 basic_auth_username_ = username;
15201 basic_auth_password_ = password;
15202}
15203
15204inline void ClientImpl::set_bearer_token_auth(const std::string &token)
15205{
15207}
15208
15209#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15210inline void ClientImpl::set_digest_auth(const std::string &username,
15211 const std::string &password)
15212{
15213 digest_auth_username_ = username;
15214 digest_auth_password_ = password;
15215}
15216#endif
15217
15218inline void ClientImpl::set_keep_alive(bool on)
15219{
15220 keep_alive_ = on;
15221}
15222
15224{
15225 follow_location_ = on;
15226}
15227
15229{
15230 path_encode_ = on;
15231}
15232
15233inline void
15234ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map)
15235{
15236 addr_map_ = std::move(addr_map);
15237}
15238
15240{
15241 default_headers_ = std::move(headers);
15242}
15243
15245 std::function<ssize_t(Stream &, Headers &)> const &writer)
15246{
15247 header_writer_ = writer;
15248}
15249
15250inline void ClientImpl::set_address_family(int family)
15251{
15252 address_family_ = family;
15253}
15254
15256{
15257 tcp_nodelay_ = on;
15258}
15259
15261{
15262 ipv6_v6only_ = on;
15263}
15264
15266{
15267 socket_options_ = std::move(socket_options);
15268}
15269
15270inline void ClientImpl::set_compress(bool on)
15271{
15272 compress_ = on;
15273}
15274
15275inline void ClientImpl::set_decompress(bool on)
15276{
15277 decompress_ = on;
15278}
15279
15280inline void ClientImpl::set_interface(const std::string &intf)
15281{
15282 interface_ = intf;
15283}
15284
15285inline void ClientImpl::set_proxy(const std::string &host, int port)
15286{
15287 proxy_host_ = host;
15288 proxy_port_ = port;
15289}
15290
15291inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
15292 const std::string &password)
15293{
15294 proxy_basic_auth_username_ = username;
15295 proxy_basic_auth_password_ = password;
15296}
15297
15298inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token)
15299{
15301}
15302
15303#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15304inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
15305 const std::string &password)
15306{
15307 proxy_digest_auth_username_ = username;
15308 proxy_digest_auth_password_ = password;
15309}
15310
15311inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
15312 const std::string &ca_cert_dir_path)
15313{
15314 ca_cert_file_path_ = ca_cert_file_path;
15315 ca_cert_dir_path_ = ca_cert_dir_path;
15316}
15317
15318inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store)
15319{
15320 if (ca_cert_store && ca_cert_store != ca_cert_store_)
15321 {
15322 ca_cert_store_ = ca_cert_store;
15323 }
15324}
15325
15326inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
15327 std::size_t size) const
15328{
15329 auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
15330 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
15331 if (!mem)
15332 {
15333 return nullptr;
15334 }
15335
15336 auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
15337 if (!inf)
15338 {
15339 return nullptr;
15340 }
15341
15342 auto cts = X509_STORE_new();
15343 if (cts)
15344 {
15345 for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++)
15346 {
15347 auto itmp = sk_X509_INFO_value(inf, i);
15348 if (!itmp)
15349 {
15350 continue;
15351 }
15352
15353 if (itmp->x509)
15354 {
15355 X509_STORE_add_cert(cts, itmp->x509);
15356 }
15357 if (itmp->crl)
15358 {
15359 X509_STORE_add_crl(cts, itmp->crl);
15360 }
15361 }
15362 }
15363
15364 sk_X509_INFO_pop_free(inf, X509_INFO_free);
15365 return cts;
15366}
15367
15368inline void ClientImpl::enable_server_certificate_verification(bool enabled)
15369{
15370 server_certificate_verification_ = enabled;
15371}
15372
15373inline void ClientImpl::enable_server_hostname_verification(bool enabled)
15374{
15375 server_hostname_verification_ = enabled;
15376}
15377
15378inline void ClientImpl::set_server_certificate_verifier(
15379 std::function<SSLVerifierResponse(SSL *ssl)> verifier)
15380{
15381 server_certificate_verifier_ = verifier;
15382}
15383#endif
15384
15386{
15387 logger_ = std::move(logger);
15388}
15389
15391{
15392 error_logger_ = std::move(error_logger);
15393}
15394
15395/*
15396 * SSL Implementation
15397 */
15398#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15399namespace detail
15400{
15401
15402inline bool is_ip_address(const std::string &host)
15403{
15404 struct in_addr addr4;
15405 struct in6_addr addr6;
15406 return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
15407 inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
15408}
15409
15410template <typename U, typename V>
15411inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
15412 U SSL_connect_or_accept, V setup)
15413{
15414 SSL *ssl = nullptr;
15415 {
15416 std::lock_guard<std::mutex> guard(ctx_mutex);
15417 ssl = SSL_new(ctx);
15418 }
15419
15420 if (ssl)
15421 {
15422 set_nonblocking(sock, true);
15423 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
15424 BIO_set_nbio(bio, 1);
15425 SSL_set_bio(ssl, bio, bio);
15426
15427 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1)
15428 {
15429 SSL_shutdown(ssl);
15430 {
15431 std::lock_guard<std::mutex> guard(ctx_mutex);
15432 SSL_free(ssl);
15433 }
15434 set_nonblocking(sock, false);
15435 return nullptr;
15436 }
15437 BIO_set_nbio(bio, 0);
15438 set_nonblocking(sock, false);
15439 }
15440
15441 return ssl;
15442}
15443
15444inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
15445 bool shutdown_gracefully)
15446{
15447 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
15448 // the remote has closed the network connection
15449 // Note that it is not always possible to avoid SIGPIPE, this is merely a
15450 // best-efforts.
15451 if (shutdown_gracefully)
15452 {
15453 (void)(sock);
15454 // SSL_shutdown() returns 0 on first call (indicating close_notify alert
15455 // sent) and 1 on subsequent call (indicating close_notify alert received)
15456 if (SSL_shutdown(ssl) == 0)
15457 {
15458 // Expected to return 1, but even if it doesn't, we free ssl
15459 SSL_shutdown(ssl);
15460 }
15461 }
15462
15463 std::lock_guard<std::mutex> guard(ctx_mutex);
15464 SSL_free(ssl);
15465}
15466
15467template <typename U>
15468bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
15469 U ssl_connect_or_accept,
15470 time_t timeout_sec, time_t timeout_usec,
15471 int *ssl_error)
15472{
15473 auto res = 0;
15474 while ((res = ssl_connect_or_accept(ssl)) != 1)
15475 {
15476 auto err = SSL_get_error(ssl, res);
15477 switch (err)
15478 {
15479 case SSL_ERROR_WANT_READ:
15480 if (select_read(sock, timeout_sec, timeout_usec) > 0)
15481 {
15482 continue;
15483 }
15484 break;
15485 case SSL_ERROR_WANT_WRITE:
15486 if (select_write(sock, timeout_sec, timeout_usec) > 0)
15487 {
15488 continue;
15489 }
15490 break;
15491 default:
15492 break;
15493 }
15494 if (ssl_error)
15495 {
15496 *ssl_error = err;
15497 }
15498 return false;
15499 }
15500 return true;
15501}
15502
15503template <typename T>
15504inline bool process_server_socket_ssl(
15505 const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
15506 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
15507 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
15508 time_t write_timeout_usec, T callback)
15509{
15510 return process_server_socket_core(
15511 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
15512 [&](bool close_connection, bool & connection_closed)
15513 {
15514 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
15515 write_timeout_sec, write_timeout_usec);
15516 return callback(strm, close_connection, connection_closed);
15517 });
15518}
15519
15520template <typename T>
15521inline bool process_client_socket_ssl(
15522 SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
15523 time_t write_timeout_sec, time_t write_timeout_usec,
15524 time_t max_timeout_msec,
15525 std::chrono::time_point<std::chrono::steady_clock> start_time, T callback)
15526{
15527 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
15528 write_timeout_sec, write_timeout_usec, max_timeout_msec,
15529 start_time);
15530 return callback(strm);
15531}
15532
15533// SSL socket stream implementation
15534inline SSLSocketStream::SSLSocketStream(
15535 socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
15536 time_t write_timeout_sec, time_t write_timeout_usec,
15537 time_t max_timeout_msec,
15538 std::chrono::time_point<std::chrono::steady_clock> start_time)
15539 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
15540 read_timeout_usec_(read_timeout_usec),
15541 write_timeout_sec_(write_timeout_sec),
15542 write_timeout_usec_(write_timeout_usec),
15543 max_timeout_msec_(max_timeout_msec), start_time_(start_time)
15544{
15545 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
15546}
15547
15548inline SSLSocketStream::~SSLSocketStream() = default;
15549
15550inline bool SSLSocketStream::is_readable() const
15551{
15552 return SSL_pending(ssl_) > 0;
15553}
15554
15555inline bool SSLSocketStream::wait_readable() const
15556{
15557 if (max_timeout_msec_ <= 0)
15558 {
15559 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
15560 }
15561
15562 time_t read_timeout_sec;
15563 time_t read_timeout_usec;
15564 calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
15565 read_timeout_usec_, read_timeout_sec, read_timeout_usec);
15566
15567 return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
15568}
15569
15570inline bool SSLSocketStream::wait_writable() const
15571{
15572 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
15573 is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
15574}
15575
15576inline ssize_t SSLSocketStream::read(char *ptr, size_t size)
15577{
15578 if (SSL_pending(ssl_) > 0)
15579 {
15580 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
15581 if (ret == 0)
15582 {
15583 error_ = Error::ConnectionClosed;
15584 }
15585 return ret;
15586 }
15587 else if (wait_readable())
15588 {
15589 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
15590 if (ret < 0)
15591 {
15592 auto err = SSL_get_error(ssl_, ret);
15593 auto n = 1000;
15594#ifdef _WIN32
15595 while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
15596 (err == SSL_ERROR_SYSCALL &&
15597 WSAGetLastError() == WSAETIMEDOUT)))
15598 {
15599#else
15600 while (--n >= 0 && err == SSL_ERROR_WANT_READ)
15601 {
15602#endif
15603 if (SSL_pending(ssl_) > 0)
15604 {
15605 return SSL_read(ssl_, ptr, static_cast<int>(size));
15606 }
15607 else if (wait_readable())
15608 {
15609 std::this_thread::sleep_for(std::chrono::microseconds{10});
15610 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
15611 if (ret >= 0)
15612 {
15613 return ret;
15614 }
15615 err = SSL_get_error(ssl_, ret);
15616 }
15617 else
15618 {
15619 break;
15620 }
15621 }
15622 assert(ret < 0);
15623 }
15624 else if (ret == 0)
15625 {
15626 error_ = Error::ConnectionClosed;
15627 }
15628 return ret;
15629 }
15630 else
15631 {
15632 error_ = Error::Timeout;
15633 return -1;
15634 }
15635}
15636
15637inline ssize_t SSLSocketStream::write(const char *ptr, size_t size)
15638{
15639 if (wait_writable())
15640 {
15641 auto handle_size = static_cast<int>(
15642 std::min<size_t>(size, (std::numeric_limits<int>::max)()));
15643
15644 auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
15645 if (ret < 0)
15646 {
15647 auto err = SSL_get_error(ssl_, ret);
15648 auto n = 1000;
15649#ifdef _WIN32
15650 while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
15651 (err == SSL_ERROR_SYSCALL &&
15652 WSAGetLastError() == WSAETIMEDOUT)))
15653 {
15654#else
15655 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE)
15656 {
15657#endif
15658 if (wait_writable())
15659 {
15660 std::this_thread::sleep_for(std::chrono::microseconds{10});
15661 ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
15662 if (ret >= 0)
15663 {
15664 return ret;
15665 }
15666 err = SSL_get_error(ssl_, ret);
15667 }
15668 else
15669 {
15670 break;
15671 }
15672 }
15673 assert(ret < 0);
15674 }
15675 return ret;
15676 }
15677 return -1;
15678}
15679
15680inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
15681 int &port) const
15682{
15683 detail::get_remote_ip_and_port(sock_, ip, port);
15684}
15685
15686inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
15687 int &port) const
15688{
15689 detail::get_local_ip_and_port(sock_, ip, port);
15690}
15691
15692inline socket_t SSLSocketStream::socket() const
15693{
15694 return sock_;
15695}
15696
15697inline time_t SSLSocketStream::duration() const
15698{
15699 return std::chrono::duration_cast<std::chrono::milliseconds>(
15700 std::chrono::steady_clock::now() - start_time_)
15701 .count();
15702}
15703
15704} // namespace detail
15705
15706// SSL HTTP server implementation
15707inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
15708 const char *client_ca_cert_file_path,
15709 const char *client_ca_cert_dir_path,
15710 const char *private_key_password)
15711{
15712 ctx_ = SSL_CTX_new(TLS_server_method());
15713
15714 if (ctx_)
15715 {
15716 SSL_CTX_set_options(ctx_,
15717 SSL_OP_NO_COMPRESSION |
15718 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
15719
15720 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
15721
15722 if (private_key_password != nullptr && (private_key_password[0] != '\0'))
15723 {
15724 SSL_CTX_set_default_passwd_cb_userdata(
15725 ctx_,
15726 reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
15727 }
15728
15729 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
15730 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
15731 1 ||
15732 SSL_CTX_check_private_key(ctx_) != 1)
15733 {
15734 last_ssl_error_ = static_cast<int>(ERR_get_error());
15735 SSL_CTX_free(ctx_);
15736 ctx_ = nullptr;
15737 }
15738 else if (client_ca_cert_file_path || client_ca_cert_dir_path)
15739 {
15740 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
15741 client_ca_cert_dir_path);
15742
15743 // Set client CA list to be sent to clients during TLS handshake
15744 if (client_ca_cert_file_path)
15745 {
15746 auto ca_list = SSL_load_client_CA_file(client_ca_cert_file_path);
15747 if (ca_list != nullptr)
15748 {
15749 SSL_CTX_set_client_CA_list(ctx_, ca_list);
15750 }
15751 else
15752 {
15753 // Failed to load client CA list, but we continue since
15754 // SSL_CTX_load_verify_locations already succeeded and
15755 // certificate verification will still work
15756 last_ssl_error_ = static_cast<int>(ERR_get_error());
15757 }
15758 }
15759
15760 SSL_CTX_set_verify(
15761 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
15762 }
15763 }
15764}
15765
15766inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
15767 X509_STORE *client_ca_cert_store)
15768{
15769 ctx_ = SSL_CTX_new(TLS_server_method());
15770
15771 if (ctx_)
15772 {
15773 SSL_CTX_set_options(ctx_,
15774 SSL_OP_NO_COMPRESSION |
15775 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
15776
15777 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
15778
15779 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
15780 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1)
15781 {
15782 SSL_CTX_free(ctx_);
15783 ctx_ = nullptr;
15784 }
15785 else if (client_ca_cert_store)
15786 {
15787 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
15788
15789 // Extract CA names from the store and set them as the client CA list
15790 auto ca_list = extract_ca_names_from_x509_store(client_ca_cert_store);
15791 if (ca_list)
15792 {
15793 SSL_CTX_set_client_CA_list(ctx_, ca_list);
15794 }
15795 else
15796 {
15797 // Failed to extract CA names, record the error
15798 last_ssl_error_ = static_cast<int>(ERR_get_error());
15799 }
15800
15801 SSL_CTX_set_verify(
15802 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
15803 }
15804 }
15805}
15806
15807inline SSLServer::SSLServer(
15808 const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback)
15809{
15810 ctx_ = SSL_CTX_new(TLS_method());
15811 if (ctx_)
15812 {
15813 if (!setup_ssl_ctx_callback(*ctx_))
15814 {
15815 SSL_CTX_free(ctx_);
15816 ctx_ = nullptr;
15817 }
15818 }
15819}
15820
15821inline SSLServer::~SSLServer()
15822{
15823 if (ctx_)
15824 {
15825 SSL_CTX_free(ctx_);
15826 }
15827}
15828
15829inline bool SSLServer::is_valid() const
15830{
15831 return ctx_;
15832}
15833
15834inline SSL_CTX *SSLServer::ssl_context() const
15835{
15836 return ctx_;
15837}
15838
15839inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
15840 X509_STORE *client_ca_cert_store)
15841{
15842
15843 std::lock_guard<std::mutex> guard(ctx_mutex_);
15844
15845 SSL_CTX_use_certificate(ctx_, cert);
15846 SSL_CTX_use_PrivateKey(ctx_, private_key);
15847
15848 if (client_ca_cert_store != nullptr)
15849 {
15850 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
15851 }
15852}
15853
15854inline bool SSLServer::process_and_close_socket(socket_t sock)
15855{
15856 auto ssl = detail::ssl_new(
15857 sock, ctx_, ctx_mutex_,
15858 [&](SSL * ssl2)
15859 {
15860 return detail::ssl_connect_or_accept_nonblocking(
15861 sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
15862 &last_ssl_error_);
15863 },
15864 [](SSL * /*ssl2*/)
15865 {
15866 return true;
15867 });
15868
15869 auto ret = false;
15870 if (ssl)
15871 {
15872 std::string remote_addr;
15873 int remote_port = 0;
15874 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
15875
15876 std::string local_addr;
15877 int local_port = 0;
15878 detail::get_local_ip_and_port(sock, local_addr, local_port);
15879
15880 ret = detail::process_server_socket_ssl(
15881 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
15882 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
15883 write_timeout_usec_,
15884 [&](Stream & strm, bool close_connection, bool & connection_closed)
15885 {
15886 return process_request(strm, remote_addr, remote_port, local_addr,
15887 local_port, close_connection,
15888 connection_closed,
15889 [&](Request & req)
15890 {
15891 req.ssl = ssl;
15892 });
15893 });
15894
15895 // Shutdown gracefully if the result seemed successful, non-gracefully if
15896 // the connection appeared to be closed.
15897 const bool shutdown_gracefully = ret;
15898 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
15899 }
15900
15901 detail::shutdown_socket(sock);
15903 return ret;
15904}
15905
15906inline STACK_OF(X509_NAME) * SSLServer::extract_ca_names_from_x509_store(
15907 X509_STORE *store)
15908{
15909 if (!store)
15910 {
15911 return nullptr;
15912 }
15913
15914 auto ca_list = sk_X509_NAME_new_null();
15915 if (!ca_list)
15916 {
15917 return nullptr;
15918 }
15919
15920 // Get all objects from the store
15921 auto objs = X509_STORE_get0_objects(store);
15922 if (!objs)
15923 {
15924 sk_X509_NAME_free(ca_list);
15925 return nullptr;
15926 }
15927
15928 // Iterate through objects and extract certificate subject names
15929 for (int i = 0; i < sk_X509_OBJECT_num(objs); i++)
15930 {
15931 auto obj = sk_X509_OBJECT_value(objs, i);
15932 if (X509_OBJECT_get_type(obj) == X509_LU_X509)
15933 {
15934 auto cert = X509_OBJECT_get0_X509(obj);
15935 if (cert)
15936 {
15937 auto subject = X509_get_subject_name(cert);
15938 if (subject)
15939 {
15940 auto name_dup = X509_NAME_dup(subject);
15941 if (name_dup)
15942 {
15943 sk_X509_NAME_push(ca_list, name_dup);
15944 }
15945 }
15946 }
15947 }
15948 }
15949
15950 // If no names were extracted, free the list and return nullptr
15951 if (sk_X509_NAME_num(ca_list) == 0)
15952 {
15953 sk_X509_NAME_free(ca_list);
15954 return nullptr;
15955 }
15956
15957 return ca_list;
15958}
15959
15960// SSL HTTP client implementation
15961inline SSLClient::SSLClient(const std::string &host)
15962 : SSLClient(host, 443, std::string(), std::string()) {}
15963
15964inline SSLClient::SSLClient(const std::string &host, int port)
15965 : SSLClient(host, port, std::string(), std::string()) {}
15966
15967inline SSLClient::SSLClient(const std::string &host, int port,
15968 const std::string &client_cert_path,
15969 const std::string &client_key_path,
15970 const std::string &private_key_password)
15971 : ClientImpl(host, port, client_cert_path, client_key_path)
15972{
15973 ctx_ = SSL_CTX_new(TLS_client_method());
15974
15975 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
15976
15977 detail::split(&host_[0], &host_[host_.size()], '.',
15978 [&](const char *b, const char *e)
15979 {
15980 host_components_.emplace_back(b, e);
15981 });
15982
15983 if (!client_cert_path.empty() && !client_key_path.empty())
15984 {
15985 if (!private_key_password.empty())
15986 {
15987 SSL_CTX_set_default_passwd_cb_userdata(
15988 ctx_, reinterpret_cast<void *>(
15989 const_cast<char *>(private_key_password.c_str())));
15990 }
15991
15992 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
15993 SSL_FILETYPE_PEM) != 1 ||
15994 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
15995 SSL_FILETYPE_PEM) != 1)
15996 {
15997 last_openssl_error_ = ERR_get_error();
15998 SSL_CTX_free(ctx_);
15999 ctx_ = nullptr;
16000 }
16001 }
16002}
16003
16004inline SSLClient::SSLClient(const std::string &host, int port,
16005 X509 *client_cert, EVP_PKEY *client_key,
16006 const std::string &private_key_password)
16007 : ClientImpl(host, port)
16008{
16009 ctx_ = SSL_CTX_new(TLS_client_method());
16010
16011 detail::split(&host_[0], &host_[host_.size()], '.',
16012 [&](const char *b, const char *e)
16013 {
16014 host_components_.emplace_back(b, e);
16015 });
16016
16017 if (client_cert != nullptr && client_key != nullptr)
16018 {
16019 if (!private_key_password.empty())
16020 {
16021 SSL_CTX_set_default_passwd_cb_userdata(
16022 ctx_, reinterpret_cast<void *>(
16023 const_cast<char *>(private_key_password.c_str())));
16024 }
16025
16026 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
16027 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1)
16028 {
16029 last_openssl_error_ = ERR_get_error();
16030 SSL_CTX_free(ctx_);
16031 ctx_ = nullptr;
16032 }
16033 }
16034}
16035
16036inline SSLClient::~SSLClient()
16037{
16038 if (ctx_)
16039 {
16040 SSL_CTX_free(ctx_);
16041 }
16042 // Make sure to shut down SSL since shutdown_ssl will resolve to the
16043 // base function rather than the derived function once we get to the
16044 // base class destructor, and won't free the SSL (causing a leak).
16045 shutdown_ssl_impl(socket_, true);
16046}
16047
16048inline bool SSLClient::is_valid() const
16049{
16050 return ctx_;
16051}
16052
16053inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store)
16054{
16055 if (ca_cert_store)
16056 {
16057 if (ctx_)
16058 {
16059 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store)
16060 {
16061 // Free memory allocated for old cert and use new store
16062 // `ca_cert_store`
16063 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
16064 ca_cert_store_ = ca_cert_store;
16065 }
16066 }
16067 else
16068 {
16069 X509_STORE_free(ca_cert_store);
16070 }
16071 }
16072}
16073
16074inline void SSLClient::load_ca_cert_store(const char *ca_cert,
16075 std::size_t size)
16076{
16077 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
16078}
16079
16080inline long SSLClient::get_openssl_verify_result() const
16081{
16082 return verify_result_;
16083}
16084
16085inline SSL_CTX *SSLClient::ssl_context() const
16086{
16087 return ctx_;
16088}
16089
16090inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error)
16091{
16092 if (!is_valid())
16093 {
16095 return false;
16096 }
16097 return ClientImpl::create_and_connect_socket(socket, error);
16098}
16099
16100// Assumes that socket_mutex_ is locked and that there are no requests in
16101// flight
16102inline bool SSLClient::connect_with_proxy(
16103 Socket &socket,
16104 std::chrono::time_point<std::chrono::steady_clock> start_time,
16105 Response &res, bool &success, Error &error)
16106{
16107 success = true;
16108 Response proxy_res;
16109 if (!detail::process_client_socket(
16110 socket.sock, read_timeout_sec_, read_timeout_usec_,
16111 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
16112 start_time, [&](Stream & strm)
16113{
16114 Request req2;
16115 req2.method = "CONNECT";
16116 req2.path =
16117 detail::make_host_and_port_string_always_port(host_, port_);
16118 if (max_timeout_msec_ > 0)
16119 {
16120 req2.start_time_ = std::chrono::steady_clock::now();
16121 }
16122 return process_request(strm, req2, proxy_res, false, error);
16123 }))
16124 {
16125 // Thread-safe to close everything because we are assuming there are no
16126 // requests in flight
16127 shutdown_ssl(socket, true);
16128 shutdown_socket(socket);
16129 close_socket(socket);
16130 success = false;
16131 return false;
16132 }
16133
16134 if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407)
16135 {
16136 if (!proxy_digest_auth_username_.empty() &&
16137 !proxy_digest_auth_password_.empty())
16138 {
16139 std::map<std::string, std::string> auth;
16140 if (detail::parse_www_authenticate(proxy_res, auth, true))
16141 {
16142 // Close the current socket and create a new one for the authenticated
16143 // request
16144 shutdown_ssl(socket, true);
16145 shutdown_socket(socket);
16146 close_socket(socket);
16147
16148 // Create a new socket for the authenticated CONNECT request
16149 if (!ensure_socket_connection(socket, error))
16150 {
16151 success = false;
16152 output_error_log(error, nullptr);
16153 return false;
16154 }
16155
16156 proxy_res = Response();
16157 if (!detail::process_client_socket(
16158 socket.sock, read_timeout_sec_, read_timeout_usec_,
16159 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
16160 start_time, [&](Stream & strm)
16161 {
16162 Request req3;
16163 req3.method = "CONNECT";
16164 req3.path = detail::make_host_and_port_string_always_port(
16165 host_, port_);
16166 req3.headers.insert(detail::make_digest_authentication_header(
16167 req3, auth, 1, detail::random_string(10),
16168 proxy_digest_auth_username_, proxy_digest_auth_password_,
16169 true));
16170 if (max_timeout_msec_ > 0)
16171 {
16172 req3.start_time_ = std::chrono::steady_clock::now();
16173 }
16174 return process_request(strm, req3, proxy_res, false, error);
16175 }))
16176 {
16177 // Thread-safe to close everything because we are assuming there are
16178 // no requests in flight
16179 shutdown_ssl(socket, true);
16180 shutdown_socket(socket);
16181 close_socket(socket);
16182 success = false;
16183 return false;
16184 }
16185 }
16186 }
16187 }
16188
16189 // If status code is not 200, proxy request is failed.
16190 // Set error to ProxyConnection and return proxy response
16191 // as the response of the request
16192 if (proxy_res.status != StatusCode::OK_200)
16193 {
16195 output_error_log(error, nullptr);
16196 res = std::move(proxy_res);
16197 // Thread-safe to close everything because we are assuming there are
16198 // no requests in flight
16199 shutdown_ssl(socket, true);
16200 shutdown_socket(socket);
16201 close_socket(socket);
16202 return false;
16203 }
16204
16205 return true;
16206}
16207
16208inline bool SSLClient::load_certs()
16209{
16210 auto ret = true;
16211
16212 std::call_once(initialize_cert_, [&]()
16213 {
16214 std::lock_guard<std::mutex> guard(ctx_mutex_);
16215 if (!ca_cert_file_path_.empty())
16216 {
16217 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
16218 nullptr))
16219 {
16220 last_openssl_error_ = ERR_get_error();
16221 ret = false;
16222 }
16223 }
16224 else if (!ca_cert_dir_path_.empty())
16225 {
16226 if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
16227 ca_cert_dir_path_.c_str()))
16228 {
16229 last_openssl_error_ = ERR_get_error();
16230 ret = false;
16231 }
16232 }
16233 else
16234 {
16235 auto loaded = false;
16236#ifdef _WIN32
16237 loaded =
16238 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
16239#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
16240 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
16241#endif // _WIN32
16242 if (!loaded)
16243 {
16244 SSL_CTX_set_default_verify_paths(ctx_);
16245 }
16246 }
16247 });
16248
16249 return ret;
16250}
16251
16252inline bool SSLClient::initialize_ssl(Socket &socket, Error &error)
16253{
16254 auto ssl = detail::ssl_new(
16255 socket.sock, ctx_, ctx_mutex_,
16256 [&](SSL * ssl2)
16257 {
16258 if (server_certificate_verification_)
16259 {
16260 if (!load_certs())
16261 {
16262 error = Error::SSLLoadingCerts;
16263 output_error_log(error, nullptr);
16264 return false;
16265 }
16266 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
16267 }
16268
16269 if (!detail::ssl_connect_or_accept_nonblocking(
16270 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
16271 connection_timeout_usec_, &last_ssl_error_))
16272 {
16273 error = Error::SSLConnection;
16274 output_error_log(error, nullptr);
16275 return false;
16276 }
16277
16278 if (server_certificate_verification_)
16279 {
16280 auto verification_status = SSLVerifierResponse::NoDecisionMade;
16281
16282 if (server_certificate_verifier_)
16283 {
16284 verification_status = server_certificate_verifier_(ssl2);
16285 }
16286
16287 if (verification_status == SSLVerifierResponse::CertificateRejected)
16288 {
16289 last_openssl_error_ = ERR_get_error();
16290 error = Error::SSLServerVerification;
16291 output_error_log(error, nullptr);
16292 return false;
16293 }
16294
16295 if (verification_status == SSLVerifierResponse::NoDecisionMade)
16296 {
16297 verify_result_ = SSL_get_verify_result(ssl2);
16298
16299 if (verify_result_ != X509_V_OK)
16300 {
16301 last_openssl_error_ = static_cast<unsigned long>(verify_result_);
16302 error = Error::SSLServerVerification;
16303 output_error_log(error, nullptr);
16304 return false;
16305 }
16306
16307 auto server_cert = SSL_get1_peer_certificate(ssl2);
16308 auto se = detail::scope_exit([&] { X509_free(server_cert); });
16309
16310 if (server_cert == nullptr)
16311 {
16312 last_openssl_error_ = ERR_get_error();
16313 error = Error::SSLServerVerification;
16314 output_error_log(error, nullptr);
16315 return false;
16316 }
16317
16318 if (server_hostname_verification_)
16319 {
16320 if (!verify_host(server_cert))
16321 {
16322 last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
16323 error = Error::SSLServerHostnameVerification;
16324 output_error_log(error, nullptr);
16325 return false;
16326 }
16327 }
16328 }
16329 }
16330
16331 return true;
16332 },
16333 [&](SSL * ssl2)
16334 {
16335 // Set SNI only if host is not IP address
16336 if (!detail::is_ip_address(host_))
16337 {
16338#if defined(OPENSSL_IS_BORINGSSL)
16339 SSL_set_tlsext_host_name(ssl2, host_.c_str());
16340#else
16341 // NOTE: Direct call instead of using the OpenSSL macro to suppress
16342 // -Wold-style-cast warning
16343 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,
16344 TLSEXT_NAMETYPE_host_name,
16345 static_cast<void *>(const_cast<char *>(host_.c_str())));
16346#endif
16347 }
16348 return true;
16349 });
16350
16351 if (ssl)
16352 {
16353 socket.ssl = ssl;
16354 return true;
16355 }
16356
16357 if (ctx_ == nullptr)
16358 {
16359 error = Error::SSLConnection;
16360 last_openssl_error_ = ERR_get_error();
16361 }
16362
16363 shutdown_socket(socket);
16364 close_socket(socket);
16365 return false;
16366}
16367
16368inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully)
16369{
16370 shutdown_ssl_impl(socket, shutdown_gracefully);
16371}
16372
16373inline void SSLClient::shutdown_ssl_impl(Socket &socket,
16374 bool shutdown_gracefully)
16375{
16376 if (socket.sock == INVALID_SOCKET)
16377 {
16378 assert(socket.ssl == nullptr);
16379 return;
16380 }
16381 if (socket.ssl)
16382 {
16383 detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
16384 shutdown_gracefully);
16385 socket.ssl = nullptr;
16386 }
16387 assert(socket.ssl == nullptr);
16388}
16389
16390inline bool SSLClient::process_socket(
16391 const Socket &socket,
16392 std::chrono::time_point<std::chrono::steady_clock> start_time,
16393 std::function<bool(Stream &strm)> callback)
16394{
16395 assert(socket.ssl);
16396 return detail::process_client_socket_ssl(
16397 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
16398 write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
16399 std::move(callback));
16400}
16401
16402inline bool SSLClient::is_ssl() const
16403{
16404 return true;
16405}
16406
16407inline bool SSLClient::verify_host(X509 *server_cert) const
16408{
16409 /* Quote from RFC2818 section 3.1 "Server Identity"
16410
16411 If a subjectAltName extension of type dNSName is present, that MUST
16412 be used as the identity. Otherwise, the (most specific) Common Name
16413 field in the Subject field of the certificate MUST be used. Although
16414 the use of the Common Name is existing practice, it is deprecated and
16415 Certification Authorities are encouraged to use the dNSName instead.
16416
16417 Matching is performed using the matching rules specified by
16418 [RFC2459]. If more than one identity of a given type is present in
16419 the certificate (e.g., more than one dNSName name, a match in any one
16420 of the set is considered acceptable.) Names may contain the wildcard
16421 character * which is considered to match any single domain name
16422 component or component fragment. E.g., *.a.com matches foo.a.com but
16423 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
16424
16425 In some cases, the URI is specified as an IP address rather than a
16426 hostname. In this case, the iPAddress subjectAltName must be present
16427 in the certificate and must exactly match the IP in the URI.
16428
16429 */
16430 return verify_host_with_subject_alt_name(server_cert) ||
16431 verify_host_with_common_name(server_cert);
16432}
16433
16434inline bool
16435SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const
16436{
16437 auto ret = false;
16438
16439 auto type = GEN_DNS;
16440
16441 struct in6_addr addr6 = {};
16442 struct in_addr addr = {};
16443 size_t addr_len = 0;
16444
16445#ifndef __MINGW32__
16446 if (inet_pton(AF_INET6, host_.c_str(), &addr6))
16447 {
16448 type = GEN_IPADD;
16449 addr_len = sizeof(struct in6_addr);
16450 }
16451 else if (inet_pton(AF_INET, host_.c_str(), &addr))
16452 {
16453 type = GEN_IPADD;
16454 addr_len = sizeof(struct in_addr);
16455 }
16456#endif
16457
16458 auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
16459 X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
16460
16461 if (alt_names)
16462 {
16463 auto dsn_matched = false;
16464 auto ip_matched = false;
16465
16466 auto count = sk_GENERAL_NAME_num(alt_names);
16467
16468 for (decltype(count) i = 0; i < count && !dsn_matched; i++)
16469 {
16470 auto val = sk_GENERAL_NAME_value(alt_names, i);
16471 if (!val || val->type != type)
16472 {
16473 continue;
16474 }
16475
16476 auto name =
16477 reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
16478 if (name == nullptr)
16479 {
16480 continue;
16481 }
16482
16483 auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
16484
16485 switch (type)
16486 {
16487 case GEN_DNS:
16488 dsn_matched = check_host_name(name, name_len);
16489 break;
16490
16491 case GEN_IPADD:
16492 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len))
16493 {
16494 ip_matched = true;
16495 }
16496 break;
16497 }
16498 }
16499
16500 if (dsn_matched || ip_matched)
16501 {
16502 ret = true;
16503 }
16504 }
16505
16506 GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
16507 reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
16508 return ret;
16509}
16510
16511inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const
16512{
16513 const auto subject_name = X509_get_subject_name(server_cert);
16514
16515 if (subject_name != nullptr)
16516 {
16517 char name[BUFSIZ];
16518 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
16519 name, sizeof(name));
16520
16521 if (name_len != -1)
16522 {
16523 return check_host_name(name, static_cast<size_t>(name_len));
16524 }
16525 }
16526
16527 return false;
16528}
16529
16530inline bool SSLClient::check_host_name(const char *pattern,
16531 size_t pattern_len) const
16532{
16533 if (host_.size() == pattern_len && host_ == pattern)
16534 {
16535 return true;
16536 }
16537
16538 // Wildcard match
16539 // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
16540 std::vector<std::string> pattern_components;
16541 detail::split(&pattern[0], &pattern[pattern_len], '.',
16542 [&](const char *b, const char *e)
16543 {
16544 pattern_components.emplace_back(b, e);
16545 });
16546
16547 if (host_components_.size() != pattern_components.size())
16548 {
16549 return false;
16550 }
16551
16552 auto itr = pattern_components.begin();
16553 for (const auto &h : host_components_)
16554 {
16555 auto &p = *itr;
16556 if (p != h && p != "*")
16557 {
16558 auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
16559 !p.compare(0, p.size() - 1, h));
16560 if (!partial_match)
16561 {
16562 return false;
16563 }
16564 }
16565 ++itr;
16566 }
16567
16568 return true;
16569}
16570#endif
16571
16572// Universal client implementation
16573inline Client::Client(const std::string &scheme_host_port)
16574 : Client(scheme_host_port, std::string(), std::string()) {}
16575
16576inline Client::Client(const std::string &scheme_host_port,
16577 const std::string &client_cert_path,
16578 const std::string &client_key_path)
16579{
16580 const static std::regex re(
16581 R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
16582
16583 std::smatch m;
16584 if (std::regex_match(scheme_host_port, m, re))
16585 {
16586 auto scheme = m[1].str();
16587
16588#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16589 if (!scheme.empty() && (scheme != "http" && scheme != "https"))
16590 {
16591#else
16592 if (!scheme.empty() && scheme != "http")
16593 {
16594#endif
16595#ifndef CPPHTTPLIB_NO_EXCEPTIONS
16596 std::string msg = "'" + scheme + "' scheme is not supported.";
16597 throw std::invalid_argument(msg);
16598#endif
16599 return;
16600 }
16601
16602 auto is_ssl = scheme == "https";
16603
16604 auto host = m[2].str();
16605 if (host.empty())
16606 {
16607 host = m[3].str();
16608 }
16609
16610 auto port_str = m[4].str();
16611 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
16612
16613 if (is_ssl)
16614 {
16615#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16616 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
16617 client_key_path);
16618 is_ssl_ = is_ssl;
16619#endif
16620 }
16621 else
16622 {
16623 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
16624 client_key_path);
16625 }
16626 }
16627 else
16628 {
16629 // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
16630 // if port param below changes.
16631 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
16632 client_cert_path, client_key_path);
16633 }
16634} // namespace detail
16635
16636inline Client::Client(const std::string &host, int port)
16637 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
16638
16639inline Client::Client(const std::string &host, int port,
16640 const std::string &client_cert_path,
16641 const std::string &client_key_path)
16642 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
16643 client_key_path)) {}
16644
16645inline Client::~Client() = default;
16646
16647inline bool Client::is_valid() const
16648{
16649 return cli_ != nullptr && cli_->is_valid();
16650}
16651
16652inline Result Client::Get(const std::string &path, DownloadProgress progress)
16653{
16654 return cli_->Get(path, std::move(progress));
16655}
16656inline Result Client::Get(const std::string &path, const Headers &headers,
16657 DownloadProgress progress)
16658{
16659 return cli_->Get(path, headers, std::move(progress));
16660}
16661inline Result Client::Get(const std::string &path,
16662 ContentReceiver content_receiver,
16663 DownloadProgress progress)
16664{
16665 return cli_->Get(path, std::move(content_receiver), std::move(progress));
16666}
16667inline Result Client::Get(const std::string &path, const Headers &headers,
16668 ContentReceiver content_receiver,
16669 DownloadProgress progress)
16670{
16671 return cli_->Get(path, headers, std::move(content_receiver),
16672 std::move(progress));
16673}
16674inline Result Client::Get(const std::string &path,
16675 ResponseHandler response_handler,
16676 ContentReceiver content_receiver,
16677 DownloadProgress progress)
16678{
16679 return cli_->Get(path, std::move(response_handler),
16680 std::move(content_receiver), std::move(progress));
16681}
16682inline Result Client::Get(const std::string &path, const Headers &headers,
16683 ResponseHandler response_handler,
16684 ContentReceiver content_receiver,
16685 DownloadProgress progress)
16686{
16687 return cli_->Get(path, headers, std::move(response_handler),
16688 std::move(content_receiver), std::move(progress));
16689}
16690inline Result Client::Get(const std::string &path, const Params &params,
16691 const Headers &headers, DownloadProgress progress)
16692{
16693 return cli_->Get(path, params, headers, std::move(progress));
16694}
16695inline Result Client::Get(const std::string &path, const Params &params,
16696 const Headers &headers,
16697 ContentReceiver content_receiver,
16698 DownloadProgress progress)
16699{
16700 return cli_->Get(path, params, headers, std::move(content_receiver),
16701 std::move(progress));
16702}
16703inline Result Client::Get(const std::string &path, const Params &params,
16704 const Headers &headers,
16705 ResponseHandler response_handler,
16706 ContentReceiver content_receiver,
16707 DownloadProgress progress)
16708{
16709 return cli_->Get(path, params, headers, std::move(response_handler),
16710 std::move(content_receiver), std::move(progress));
16711}
16712
16713inline Result Client::Head(const std::string &path)
16714{
16715 return cli_->Head(path);
16716}
16717inline Result Client::Head(const std::string &path, const Headers &headers)
16718{
16719 return cli_->Head(path, headers);
16720}
16721
16722inline Result Client::Post(const std::string &path)
16723{
16724 return cli_->Post(path);
16725}
16726inline Result Client::Post(const std::string &path, const Headers &headers)
16727{
16728 return cli_->Post(path, headers);
16729}
16730inline Result Client::Post(const std::string &path, const char *body,
16731 size_t content_length,
16732 const std::string &content_type,
16733 UploadProgress progress)
16734{
16735 return cli_->Post(path, body, content_length, content_type, progress);
16736}
16737inline Result Client::Post(const std::string &path, const Headers &headers,
16738 const char *body, size_t content_length,
16739 const std::string &content_type,
16740 UploadProgress progress)
16741{
16742 return cli_->Post(path, headers, body, content_length, content_type,
16743 progress);
16744}
16745inline Result Client::Post(const std::string &path, const std::string &body,
16746 const std::string &content_type,
16747 UploadProgress progress)
16748{
16749 return cli_->Post(path, body, content_type, progress);
16750}
16751inline Result Client::Post(const std::string &path, const Headers &headers,
16752 const std::string &body,
16753 const std::string &content_type,
16754 UploadProgress progress)
16755{
16756 return cli_->Post(path, headers, body, content_type, progress);
16757}
16758inline Result Client::Post(const std::string &path, size_t content_length,
16759 ContentProvider content_provider,
16760 const std::string &content_type,
16761 UploadProgress progress)
16762{
16763 return cli_->Post(path, content_length, std::move(content_provider),
16764 content_type, progress);
16765}
16766inline Result Client::Post(const std::string &path, size_t content_length,
16767 ContentProvider content_provider,
16768 const std::string &content_type,
16769 ContentReceiver content_receiver,
16770 UploadProgress progress)
16771{
16772 return cli_->Post(path, content_length, std::move(content_provider),
16773 content_type, std::move(content_receiver), progress);
16774}
16775inline Result Client::Post(const std::string &path,
16776 ContentProviderWithoutLength content_provider,
16777 const std::string &content_type,
16778 UploadProgress progress)
16779{
16780 return cli_->Post(path, std::move(content_provider), content_type, progress);
16781}
16782inline Result Client::Post(const std::string &path,
16783 ContentProviderWithoutLength content_provider,
16784 const std::string &content_type,
16785 ContentReceiver content_receiver,
16786 UploadProgress progress)
16787{
16788 return cli_->Post(path, std::move(content_provider), content_type,
16789 std::move(content_receiver), progress);
16790}
16791inline Result Client::Post(const std::string &path, const Headers &headers,
16792 size_t content_length,
16793 ContentProvider content_provider,
16794 const std::string &content_type,
16795 UploadProgress progress)
16796{
16797 return cli_->Post(path, headers, content_length, std::move(content_provider),
16798 content_type, progress);
16799}
16800inline Result Client::Post(const std::string &path, const Headers &headers,
16801 size_t content_length,
16802 ContentProvider content_provider,
16803 const std::string &content_type,
16804 ContentReceiver content_receiver,
16805 DownloadProgress progress)
16806{
16807 return cli_->Post(path, headers, content_length, std::move(content_provider),
16808 content_type, std::move(content_receiver), progress);
16809}
16810inline Result Client::Post(const std::string &path, const Headers &headers,
16811 ContentProviderWithoutLength content_provider,
16812 const std::string &content_type,
16813 UploadProgress progress)
16814{
16815 return cli_->Post(path, headers, std::move(content_provider), content_type,
16816 progress);
16817}
16818inline Result Client::Post(const std::string &path, const Headers &headers,
16819 ContentProviderWithoutLength content_provider,
16820 const std::string &content_type,
16821 ContentReceiver content_receiver,
16822 DownloadProgress progress)
16823{
16824 return cli_->Post(path, headers, std::move(content_provider), content_type,
16825 std::move(content_receiver), progress);
16826}
16827inline Result Client::Post(const std::string &path, const Params &params)
16828{
16829 return cli_->Post(path, params);
16830}
16831inline Result Client::Post(const std::string &path, const Headers &headers,
16832 const Params &params)
16833{
16834 return cli_->Post(path, headers, params);
16835}
16836inline Result Client::Post(const std::string &path,
16837 const UploadFormDataItems &items,
16838 UploadProgress progress)
16839{
16840 return cli_->Post(path, items, progress);
16841}
16842inline Result Client::Post(const std::string &path, const Headers &headers,
16843 const UploadFormDataItems &items,
16844 UploadProgress progress)
16845{
16846 return cli_->Post(path, headers, items, progress);
16847}
16848inline Result Client::Post(const std::string &path, const Headers &headers,
16849 const UploadFormDataItems &items,
16850 const std::string &boundary,
16851 UploadProgress progress)
16852{
16853 return cli_->Post(path, headers, items, boundary, progress);
16854}
16855inline Result Client::Post(const std::string &path, const Headers &headers,
16856 const UploadFormDataItems &items,
16857 const FormDataProviderItems &provider_items,
16858 UploadProgress progress)
16859{
16860 return cli_->Post(path, headers, items, provider_items, progress);
16861}
16862inline Result Client::Post(const std::string &path, const Headers &headers,
16863 const std::string &body,
16864 const std::string &content_type,
16865 ContentReceiver content_receiver,
16866 DownloadProgress progress)
16867{
16868 return cli_->Post(path, headers, body, content_type,
16869 std::move(content_receiver), progress);
16870}
16871
16872inline Result Client::Put(const std::string &path)
16873{
16874 return cli_->Put(path);
16875}
16876inline Result Client::Put(const std::string &path, const Headers &headers)
16877{
16878 return cli_->Put(path, headers);
16879}
16880inline Result Client::Put(const std::string &path, const char *body,
16881 size_t content_length,
16882 const std::string &content_type,
16883 UploadProgress progress)
16884{
16885 return cli_->Put(path, body, content_length, content_type, progress);
16886}
16887inline Result Client::Put(const std::string &path, const Headers &headers,
16888 const char *body, size_t content_length,
16889 const std::string &content_type,
16890 UploadProgress progress)
16891{
16892 return cli_->Put(path, headers, body, content_length, content_type, progress);
16893}
16894inline Result Client::Put(const std::string &path, const std::string &body,
16895 const std::string &content_type,
16896 UploadProgress progress)
16897{
16898 return cli_->Put(path, body, content_type, progress);
16899}
16900inline Result Client::Put(const std::string &path, const Headers &headers,
16901 const std::string &body,
16902 const std::string &content_type,
16903 UploadProgress progress)
16904{
16905 return cli_->Put(path, headers, body, content_type, progress);
16906}
16907inline Result Client::Put(const std::string &path, size_t content_length,
16908 ContentProvider content_provider,
16909 const std::string &content_type,
16910 UploadProgress progress)
16911{
16912 return cli_->Put(path, content_length, std::move(content_provider),
16913 content_type, progress);
16914}
16915inline Result Client::Put(const std::string &path, size_t content_length,
16916 ContentProvider content_provider,
16917 const std::string &content_type,
16918 ContentReceiver content_receiver,
16919 UploadProgress progress)
16920{
16921 return cli_->Put(path, content_length, std::move(content_provider),
16922 content_type, std::move(content_receiver), progress);
16923}
16924inline Result Client::Put(const std::string &path,
16925 ContentProviderWithoutLength content_provider,
16926 const std::string &content_type,
16927 UploadProgress progress)
16928{
16929 return cli_->Put(path, std::move(content_provider), content_type, progress);
16930}
16931inline Result Client::Put(const std::string &path,
16932 ContentProviderWithoutLength content_provider,
16933 const std::string &content_type,
16934 ContentReceiver content_receiver,
16935 UploadProgress progress)
16936{
16937 return cli_->Put(path, std::move(content_provider), content_type,
16938 std::move(content_receiver), progress);
16939}
16940inline Result Client::Put(const std::string &path, const Headers &headers,
16941 size_t content_length,
16942 ContentProvider content_provider,
16943 const std::string &content_type,
16944 UploadProgress progress)
16945{
16946 return cli_->Put(path, headers, content_length, std::move(content_provider),
16947 content_type, progress);
16948}
16949inline Result Client::Put(const std::string &path, const Headers &headers,
16950 size_t content_length,
16951 ContentProvider content_provider,
16952 const std::string &content_type,
16953 ContentReceiver content_receiver,
16954 UploadProgress progress)
16955{
16956 return cli_->Put(path, headers, content_length, std::move(content_provider),
16957 content_type, std::move(content_receiver), progress);
16958}
16959inline Result Client::Put(const std::string &path, const Headers &headers,
16960 ContentProviderWithoutLength content_provider,
16961 const std::string &content_type,
16962 UploadProgress progress)
16963{
16964 return cli_->Put(path, headers, std::move(content_provider), content_type,
16965 progress);
16966}
16967inline Result Client::Put(const std::string &path, const Headers &headers,
16968 ContentProviderWithoutLength content_provider,
16969 const std::string &content_type,
16970 ContentReceiver content_receiver,
16971 UploadProgress progress)
16972{
16973 return cli_->Put(path, headers, std::move(content_provider), content_type,
16974 std::move(content_receiver), progress);
16975}
16976inline Result Client::Put(const std::string &path, const Params &params)
16977{
16978 return cli_->Put(path, params);
16979}
16980inline Result Client::Put(const std::string &path, const Headers &headers,
16981 const Params &params)
16982{
16983 return cli_->Put(path, headers, params);
16984}
16985inline Result Client::Put(const std::string &path,
16986 const UploadFormDataItems &items,
16987 UploadProgress progress)
16988{
16989 return cli_->Put(path, items, progress);
16990}
16991inline Result Client::Put(const std::string &path, const Headers &headers,
16992 const UploadFormDataItems &items,
16993 UploadProgress progress)
16994{
16995 return cli_->Put(path, headers, items, progress);
16996}
16997inline Result Client::Put(const std::string &path, const Headers &headers,
16998 const UploadFormDataItems &items,
16999 const std::string &boundary,
17000 UploadProgress progress)
17001{
17002 return cli_->Put(path, headers, items, boundary, progress);
17003}
17004inline Result Client::Put(const std::string &path, const Headers &headers,
17005 const UploadFormDataItems &items,
17006 const FormDataProviderItems &provider_items,
17007 UploadProgress progress)
17008{
17009 return cli_->Put(path, headers, items, provider_items, progress);
17010}
17011inline Result Client::Put(const std::string &path, const Headers &headers,
17012 const std::string &body,
17013 const std::string &content_type,
17014 ContentReceiver content_receiver,
17015 DownloadProgress progress)
17016{
17017 return cli_->Put(path, headers, body, content_type, content_receiver,
17018 progress);
17019}
17020
17021inline Result Client::Patch(const std::string &path)
17022{
17023 return cli_->Patch(path);
17024}
17025inline Result Client::Patch(const std::string &path, const Headers &headers)
17026{
17027 return cli_->Patch(path, headers);
17028}
17029inline Result Client::Patch(const std::string &path, const char *body,
17030 size_t content_length,
17031 const std::string &content_type,
17032 UploadProgress progress)
17033{
17034 return cli_->Patch(path, body, content_length, content_type, progress);
17035}
17036inline Result Client::Patch(const std::string &path, const Headers &headers,
17037 const char *body, size_t content_length,
17038 const std::string &content_type,
17039 UploadProgress progress)
17040{
17041 return cli_->Patch(path, headers, body, content_length, content_type,
17042 progress);
17043}
17044inline Result Client::Patch(const std::string &path, const std::string &body,
17045 const std::string &content_type,
17046 UploadProgress progress)
17047{
17048 return cli_->Patch(path, body, content_type, progress);
17049}
17050inline Result Client::Patch(const std::string &path, const Headers &headers,
17051 const std::string &body,
17052 const std::string &content_type,
17053 UploadProgress progress)
17054{
17055 return cli_->Patch(path, headers, body, content_type, progress);
17056}
17057inline Result Client::Patch(const std::string &path, size_t content_length,
17058 ContentProvider content_provider,
17059 const std::string &content_type,
17060 UploadProgress progress)
17061{
17062 return cli_->Patch(path, content_length, std::move(content_provider),
17063 content_type, progress);
17064}
17065inline Result Client::Patch(const std::string &path, size_t content_length,
17066 ContentProvider content_provider,
17067 const std::string &content_type,
17068 ContentReceiver content_receiver,
17069 UploadProgress progress)
17070{
17071 return cli_->Patch(path, content_length, std::move(content_provider),
17072 content_type, std::move(content_receiver), progress);
17073}
17074inline Result Client::Patch(const std::string &path,
17075 ContentProviderWithoutLength content_provider,
17076 const std::string &content_type,
17077 UploadProgress progress)
17078{
17079 return cli_->Patch(path, std::move(content_provider), content_type, progress);
17080}
17081inline Result Client::Patch(const std::string &path,
17082 ContentProviderWithoutLength content_provider,
17083 const std::string &content_type,
17084 ContentReceiver content_receiver,
17085 UploadProgress progress)
17086{
17087 return cli_->Patch(path, std::move(content_provider), content_type,
17088 std::move(content_receiver), progress);
17089}
17090inline Result Client::Patch(const std::string &path, const Headers &headers,
17091 size_t content_length,
17092 ContentProvider content_provider,
17093 const std::string &content_type,
17094 UploadProgress progress)
17095{
17096 return cli_->Patch(path, headers, content_length, std::move(content_provider),
17097 content_type, progress);
17098}
17099inline Result Client::Patch(const std::string &path, const Headers &headers,
17100 size_t content_length,
17101 ContentProvider content_provider,
17102 const std::string &content_type,
17103 ContentReceiver content_receiver,
17104 UploadProgress progress)
17105{
17106 return cli_->Patch(path, headers, content_length, std::move(content_provider),
17107 content_type, std::move(content_receiver), progress);
17108}
17109inline Result Client::Patch(const std::string &path, const Headers &headers,
17110 ContentProviderWithoutLength content_provider,
17111 const std::string &content_type,
17112 UploadProgress progress)
17113{
17114 return cli_->Patch(path, headers, std::move(content_provider), content_type,
17115 progress);
17116}
17117inline Result Client::Patch(const std::string &path, const Headers &headers,
17118 ContentProviderWithoutLength content_provider,
17119 const std::string &content_type,
17120 ContentReceiver content_receiver,
17121 UploadProgress progress)
17122{
17123 return cli_->Patch(path, headers, std::move(content_provider), content_type,
17124 std::move(content_receiver), progress);
17125}
17126inline Result Client::Patch(const std::string &path, const Params &params)
17127{
17128 return cli_->Patch(path, params);
17129}
17130inline Result Client::Patch(const std::string &path, const Headers &headers,
17131 const Params &params)
17132{
17133 return cli_->Patch(path, headers, params);
17134}
17135inline Result Client::Patch(const std::string &path,
17136 const UploadFormDataItems &items,
17137 UploadProgress progress)
17138{
17139 return cli_->Patch(path, items, progress);
17140}
17141inline Result Client::Patch(const std::string &path, const Headers &headers,
17142 const UploadFormDataItems &items,
17143 UploadProgress progress)
17144{
17145 return cli_->Patch(path, headers, items, progress);
17146}
17147inline Result Client::Patch(const std::string &path, const Headers &headers,
17148 const UploadFormDataItems &items,
17149 const std::string &boundary,
17150 UploadProgress progress)
17151{
17152 return cli_->Patch(path, headers, items, boundary, progress);
17153}
17154inline Result Client::Patch(const std::string &path, const Headers &headers,
17155 const UploadFormDataItems &items,
17156 const FormDataProviderItems &provider_items,
17157 UploadProgress progress)
17158{
17159 return cli_->Patch(path, headers, items, provider_items, progress);
17160}
17161inline Result Client::Patch(const std::string &path, const Headers &headers,
17162 const std::string &body,
17163 const std::string &content_type,
17164 ContentReceiver content_receiver,
17165 DownloadProgress progress)
17166{
17167 return cli_->Patch(path, headers, body, content_type, content_receiver,
17168 progress);
17169}
17170
17171inline Result Client::Delete(const std::string &path,
17172 DownloadProgress progress)
17173{
17174 return cli_->Delete(path, progress);
17175}
17176inline Result Client::Delete(const std::string &path, const Headers &headers,
17177 DownloadProgress progress)
17178{
17179 return cli_->Delete(path, headers, progress);
17180}
17181inline Result Client::Delete(const std::string &path, const char *body,
17182 size_t content_length,
17183 const std::string &content_type,
17184 DownloadProgress progress)
17185{
17186 return cli_->Delete(path, body, content_length, content_type, progress);
17187}
17188inline Result Client::Delete(const std::string &path, const Headers &headers,
17189 const char *body, size_t content_length,
17190 const std::string &content_type,
17191 DownloadProgress progress)
17192{
17193 return cli_->Delete(path, headers, body, content_length, content_type,
17194 progress);
17195}
17196inline Result Client::Delete(const std::string &path, const std::string &body,
17197 const std::string &content_type,
17198 DownloadProgress progress)
17199{
17200 return cli_->Delete(path, body, content_type, progress);
17201}
17202inline Result Client::Delete(const std::string &path, const Headers &headers,
17203 const std::string &body,
17204 const std::string &content_type,
17205 DownloadProgress progress)
17206{
17207 return cli_->Delete(path, headers, body, content_type, progress);
17208}
17209inline Result Client::Delete(const std::string &path, const Params &params,
17210 DownloadProgress progress)
17211{
17212 return cli_->Delete(path, params, progress);
17213}
17214inline Result Client::Delete(const std::string &path, const Headers &headers,
17215 const Params &params, DownloadProgress progress)
17216{
17217 return cli_->Delete(path, headers, params, progress);
17218}
17219
17220inline Result Client::Options(const std::string &path)
17221{
17222 return cli_->Options(path);
17223}
17224inline Result Client::Options(const std::string &path, const Headers &headers)
17225{
17226 return cli_->Options(path, headers);
17227}
17228
17230Client::open_stream(const std::string &method, const std::string &path,
17231 const Params &params, const Headers &headers,
17232 const std::string &body, const std::string &content_type)
17233{
17234 return cli_->open_stream(method, path, params, headers, body, content_type);
17235}
17236
17237inline bool Client::send(Request &req, Response &res, Error &error)
17238{
17239 return cli_->send(req, res, error);
17240}
17241
17242inline Result Client::send(const Request &req)
17243{
17244 return cli_->send(req);
17245}
17246
17247inline void Client::stop()
17248{
17249 cli_->stop();
17250}
17251
17252inline std::string Client::host() const
17253{
17254 return cli_->host();
17255}
17256
17257inline int Client::port() const
17258{
17259 return cli_->port();
17260}
17261
17262inline size_t Client::is_socket_open() const
17263{
17264 return cli_->is_socket_open();
17265}
17266
17268{
17269 return cli_->socket();
17270}
17271
17272inline void
17273Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map)
17274{
17275 cli_->set_hostname_addr_map(std::move(addr_map));
17276}
17277
17279{
17280 cli_->set_default_headers(std::move(headers));
17281}
17282
17284 std::function<ssize_t(Stream &, Headers &)> const &writer)
17285{
17286 cli_->set_header_writer(writer);
17287}
17288
17289inline void Client::set_address_family(int family)
17290{
17291 cli_->set_address_family(family);
17292}
17293
17294inline void Client::set_tcp_nodelay(bool on)
17295{
17296 cli_->set_tcp_nodelay(on);
17297}
17298
17300{
17301 cli_->set_socket_options(std::move(socket_options));
17302}
17303
17304inline void Client::set_connection_timeout(time_t sec, time_t usec)
17305{
17306 cli_->set_connection_timeout(sec, usec);
17307}
17308
17309inline void Client::set_read_timeout(time_t sec, time_t usec)
17310{
17311 cli_->set_read_timeout(sec, usec);
17312}
17313
17314inline void Client::set_write_timeout(time_t sec, time_t usec)
17315{
17316 cli_->set_write_timeout(sec, usec);
17317}
17318
17319inline void Client::set_basic_auth(const std::string &username,
17320 const std::string &password)
17321{
17322 cli_->set_basic_auth(username, password);
17323}
17324inline void Client::set_bearer_token_auth(const std::string &token)
17325{
17326 cli_->set_bearer_token_auth(token);
17327}
17328#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17329inline void Client::set_digest_auth(const std::string &username,
17330 const std::string &password)
17331{
17332 cli_->set_digest_auth(username, password);
17333}
17334#endif
17335
17336inline void Client::set_keep_alive(bool on)
17337{
17338 cli_->set_keep_alive(on);
17339}
17341{
17342 cli_->set_follow_location(on);
17343}
17344
17345inline void Client::set_path_encode(bool on)
17346{
17347 cli_->set_path_encode(on);
17348}
17349
17350[[deprecated("Use set_path_encode instead")]]
17351inline void Client::set_url_encode(bool on)
17352{
17353 cli_->set_path_encode(on);
17354}
17355
17356inline void Client::set_compress(bool on)
17357{
17358 cli_->set_compress(on);
17359}
17360
17361inline void Client::set_decompress(bool on)
17362{
17363 cli_->set_decompress(on);
17364}
17365
17366inline void Client::set_interface(const std::string &intf)
17367{
17368 cli_->set_interface(intf);
17369}
17370
17371inline void Client::set_proxy(const std::string &host, int port)
17372{
17373 cli_->set_proxy(host, port);
17374}
17375inline void Client::set_proxy_basic_auth(const std::string &username,
17376 const std::string &password)
17377{
17378 cli_->set_proxy_basic_auth(username, password);
17379}
17380inline void Client::set_proxy_bearer_token_auth(const std::string &token)
17381{
17382 cli_->set_proxy_bearer_token_auth(token);
17383}
17384#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17385inline void Client::set_proxy_digest_auth(const std::string &username,
17386 const std::string &password)
17387{
17388 cli_->set_proxy_digest_auth(username, password);
17389}
17390#endif
17391
17392#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17393inline void Client::enable_server_certificate_verification(bool enabled)
17394{
17395 cli_->enable_server_certificate_verification(enabled);
17396}
17397
17398inline void Client::enable_server_hostname_verification(bool enabled)
17399{
17400 cli_->enable_server_hostname_verification(enabled);
17401}
17402
17403inline void Client::set_server_certificate_verifier(
17404 std::function<SSLVerifierResponse(SSL *ssl)> verifier)
17405{
17406 cli_->set_server_certificate_verifier(verifier);
17407}
17408#endif
17409
17410inline void Client::set_logger(Logger logger)
17411{
17412 cli_->set_logger(std::move(logger));
17413}
17414
17415inline void Client::set_error_logger(ErrorLogger error_logger)
17416{
17417 cli_->set_error_logger(std::move(error_logger));
17418}
17419
17420#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
17421inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
17422 const std::string &ca_cert_dir_path)
17423{
17424 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
17425}
17426
17427inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store)
17428{
17429 if (is_ssl_)
17430 {
17431 static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
17432 }
17433 else
17434 {
17435 cli_->set_ca_cert_store(ca_cert_store);
17436 }
17437}
17438
17439inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size)
17440{
17441 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
17442}
17443
17444inline long Client::get_openssl_verify_result() const
17445{
17446 if (is_ssl_)
17447 {
17448 return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
17449 }
17450 return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
17451}
17452
17453inline SSL_CTX *Client::ssl_context() const
17454{
17455 if (is_ssl_)
17456 {
17457 return static_cast<SSLClient &>(*cli_).ssl_context();
17458 }
17459 return nullptr;
17460}
17461#endif
17462
17463// ----------------------------------------------------------------------------
17464
17465} // namespace httplib
17466
17467#endif // CPPHTTPLIB_HTTPLIB_H
std::string fmt(const char *fmt,...)
Definition Utils.cpp:241
Options
Definition ccvt.h:199
void set_proxy_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:15291
void set_socket_options(SocketOptions socket_options)
Definition httplib.h:15265
time_t write_timeout_sec_
Definition httplib.h:1978
std::string host() const
Definition httplib.h:15153
virtual bool ensure_socket_connection(Socket &socket, Error &error)
Definition httplib.h:12561
void set_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:15197
time_t read_timeout_sec_
Definition httplib.h:1976
void set_error_logger(ErrorLogger error_logger)
Definition httplib.h:15390
size_t socket_requests_in_flight_
Definition httplib.h:1956
void close_socket(Socket &socket)
Definition httplib.h:12608
std::string proxy_bearer_token_auth_token_
Definition httplib.h:2010
bool write_content_with_provider(Stream &strm, const Request &req, Error &error) const
Definition httplib.h:13632
void set_decompress(bool on)
Definition httplib.h:15275
socket_t socket() const
Definition httplib.h:15169
void set_proxy(const std::string &host, int port)
Definition httplib.h:15285
void set_interface(const std::string &intf)
Definition httplib.h:15280
time_t connection_timeout_sec_
Definition httplib.h:1974
Result Patch(const std::string &path)
Definition httplib.h:14806
const std::string host_
Definition httplib.h:1947
time_t max_timeout_msec_
Definition httplib.h:1980
time_t read_timeout_usec_
Definition httplib.h:1977
std::string proxy_host_
Definition httplib.h:2005
Result Delete(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:15028
void set_tcp_nodelay(bool on)
Definition httplib.h:15255
void set_proxy_bearer_token_auth(const std::string &token)
Definition httplib.h:15298
std::string basic_auth_password_
Definition httplib.h:1983
bool send(Request &req, Response &res, Error &error)
Definition httplib.h:12682
virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully)
Definition httplib.h:12590
void set_connection_timeout(time_t sec, time_t usec=0)
Definition httplib.h:15174
std::string bearer_token_auth_token_
Definition httplib.h:1984
void set_hostname_addr_map(std::map< std::string, std::string > addr_map)
Definition httplib.h:15234
void set_logger(Logger logger)
Definition httplib.h:15385
ClientImpl(const std::string &host)
Definition httplib.h:12435
void set_compress(bool on)
Definition httplib.h:15270
void output_log(const Request &req, const Response &res) const
Definition httplib.h:14010
int port() const
Definition httplib.h:15158
void set_address_family(int family)
Definition httplib.h:15250
std::string client_key_path_
Definition httplib.h:1972
std::string interface_
Definition httplib.h:2003
Result Put(const std::string &path)
Definition httplib.h:14586
Headers default_headers_
Definition httplib.h:1964
std::string client_cert_path_
Definition httplib.h:1971
void set_ipv6_v6only(bool on)
Definition httplib.h:15260
Result Post(const std::string &path)
Definition httplib.h:14365
time_t connection_timeout_usec_
Definition httplib.h:1975
std::string proxy_basic_auth_username_
Definition httplib.h:2008
void shutdown_socket(Socket &socket) const
Definition httplib.h:12599
std::function< ssize_t(Stream &, Headers &)> header_writer_
Definition httplib.h:1967
bool process_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error)
Definition httplib.h:14030
std::thread::id socket_requests_are_from_thread_
Definition httplib.h:1957
std::mutex logger_mutex_
Definition httplib.h:2029
std::map< std::string, std::string > addr_map_
Definition httplib.h:1961
ErrorLogger error_logger_
Definition httplib.h:2031
time_t write_timeout_usec_
Definition httplib.h:1979
std::string proxy_basic_auth_password_
Definition httplib.h:2009
void set_write_timeout(time_t sec, time_t usec=0)
Definition httplib.h:15186
void copy_settings(const ClientImpl &rhs)
Definition httplib.h:12473
void output_error_log(const Error &err, const Request *req) const
Definition httplib.h:14020
void set_max_timeout(time_t msec)
Definition httplib.h:15192
void set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Definition httplib.h:15244
std::recursive_mutex request_mutex_
Definition httplib.h:1953
Result Options(const std::string &path)
Definition httplib.h:15108
size_t is_socket_open() const
Definition httplib.h:15163
void set_default_headers(Headers headers)
Definition httplib.h:15239
void set_follow_location(bool on)
Definition httplib.h:15223
SocketOptions socket_options_
Definition httplib.h:1998
void set_keep_alive(bool on)
Definition httplib.h:15218
void set_path_encode(bool on)
Definition httplib.h:15228
bool socket_should_be_closed_when_request_is_done_
Definition httplib.h:1958
Result Head(const std::string &path)
Definition httplib.h:14345
std::mutex socket_mutex_
Definition httplib.h:1952
virtual ~ClientImpl()
Definition httplib.h:12447
StreamHandle open_stream(const std::string &method, const std::string &path, const Params &params={}, const Headers &headers={}, const std::string &body={}, const std::string &content_type={})
Definition httplib.h:12928
std::string basic_auth_username_
Definition httplib.h:1982
virtual bool create_and_connect_socket(Socket &socket, Error &error)
Definition httplib.h:12549
Result Get(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:14234
virtual bool is_valid() const
Definition httplib.h:12468
void set_read_timeout(time_t sec, time_t usec=0)
Definition httplib.h:15180
void set_bearer_token_auth(const std::string &token)
Definition httplib.h:15204
ClientImpl::StreamHandle open_stream(const std::string &method, const std::string &path, const Params &params={}, const Headers &headers={}, const std::string &body={}, const std::string &content_type={})
Definition httplib.h:17230
void set_read_timeout(time_t sec, time_t usec=0)
Definition httplib.h:17309
Result Post(const std::string &path)
Definition httplib.h:16722
Client(Client &&)=default
Result Put(const std::string &path)
Definition httplib.h:16872
void set_connection_timeout(time_t sec, time_t usec=0)
Definition httplib.h:17304
bool send(Request &req, Response &res, Error &error)
Definition httplib.h:17237
Result Options(const std::string &path)
Definition httplib.h:17220
void set_default_headers(Headers headers)
Definition httplib.h:17278
void set_proxy_bearer_token_auth(const std::string &token)
Definition httplib.h:17380
void set_hostname_addr_map(std::map< std::string, std::string > addr_map)
Definition httplib.h:17273
Client & operator=(Client &&)=default
void set_proxy_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:17375
void set_tcp_nodelay(bool on)
Definition httplib.h:17294
void set_keep_alive(bool on)
Definition httplib.h:17336
void set_max_timeout(time_t msec)
Definition httplib.h:2707
void set_interface(const std::string &intf)
Definition httplib.h:17366
socket_t socket() const
Definition httplib.h:17267
Result Get(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:16652
void set_decompress(bool on)
Definition httplib.h:17361
size_t is_socket_open() const
Definition httplib.h:17262
bool is_valid() const
Definition httplib.h:16647
void set_follow_location(bool on)
Definition httplib.h:17340
void set_path_encode(bool on)
Definition httplib.h:17345
void set_proxy(const std::string &host, int port)
Definition httplib.h:17371
void set_write_timeout(time_t sec, time_t usec=0)
Definition httplib.h:17314
Client(const std::string &scheme_host_port)
Definition httplib.h:16573
void set_address_family(int family)
Definition httplib.h:17289
Result Head(const std::string &path)
Definition httplib.h:16713
std::string host() const
Definition httplib.h:17252
void set_error_logger(ErrorLogger error_logger)
Definition httplib.h:17415
void set_bearer_token_auth(const std::string &token)
Definition httplib.h:17324
void set_url_encode(bool on)
Definition httplib.h:17351
void set_logger(Logger logger)
Definition httplib.h:17410
void set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Definition httplib.h:17283
Result Patch(const std::string &path)
Definition httplib.h:17021
void set_socket_options(SocketOptions socket_options)
Definition httplib.h:17299
void set_compress(bool on)
Definition httplib.h:17356
Result Delete(const std::string &path, DownloadProgress progress=nullptr)
Definition httplib.h:17171
void set_basic_auth(const std::string &username, const std::string &password)
Definition httplib.h:17319
int port() const
Definition httplib.h:17257
ContentReader(Reader reader, FormDataReader multipart_reader)
Definition httplib.h:732
bool operator()(FormDataHeader header, ContentReceiver receiver) const
Definition httplib.h:736
bool operator()(ContentReceiver receiver) const
Definition httplib.h:741
std::function< bool(ContentReceiver receiver)> Reader
Definition httplib.h:728
FormDataReader formdata_reader_
Definition httplib.h:747
std::function< bool(FormDataHeader header, ContentReceiver receiver)> FormDataReader
Definition httplib.h:730
std::function< bool(const char *data, size_t data_len)> write
Definition httplib.h:674
DataSink(DataSink &&)=delete
DataSink(const DataSink &)=delete
DataSink & operator=(DataSink &&)=delete
DataSink & operator=(const DataSink &)=delete
std::function< void()> done
Definition httplib.h:676
std::function< bool()> is_writable
Definition httplib.h:675
std::ostream os
Definition httplib.h:678
std::function< void(const Headers &trailer)> done_with_trailer
Definition httplib.h:677
bool has_request_header(const std::string &key) const
Definition httplib.h:10070
Response & value()
Definition httplib.h:1449
Result()=default
const Response & value() const
Definition httplib.h:1445
size_t get_request_header_value_count(const std::string &key) const
Definition httplib.h:10083
const Response & operator*() const
Definition httplib.h:1453
bool operator==(std::nullptr_t) const
Definition httplib.h:1437
Result(std::unique_ptr< Response > &&res, Error err, Headers &&request_headers=Headers{})
Definition httplib.h:1417
bool operator!=(std::nullptr_t) const
Definition httplib.h:1441
const Response * operator->() const
Definition httplib.h:1461
Response * operator->()
Definition httplib.h:1465
size_t get_request_header_value_u64(const std::string &key, size_t def=0, size_t id=0) const
Definition httplib.h:2638
Error error() const
Definition httplib.h:1471
Response & operator*()
Definition httplib.h:1457
std::string get_request_header_value(const std::string &key, const char *def="", size_t id=0) const
Definition httplib.h:10075
time_t read_timeout_usec_
Definition httplib.h:1297
Server & set_default_file_mimetype(const std::string &mime)
Definition httplib.h:10727
Server & Put(const std::string &pattern, Handler handler)
Definition httplib.h:10636
std::function< HandlerResponse(const Request &, Response &)> HandlerWithResponse
Definition httplib.h:1187
Server & Delete(const std::string &pattern, Handler handler)
Definition httplib.h:10664
Server & set_pre_routing_handler(HandlerWithResponse handler)
Definition httplib.h:10763
Server & set_write_timeout(time_t sec, time_t usec=0)
Definition httplib.h:10869
time_t idle_interval_usec_
Definition httplib.h:1301
size_t keep_alive_max_count_
Definition httplib.h:1294
time_t write_timeout_sec_
Definition httplib.h:1298
Server & set_error_handler(ErrorHandlerFunc &&handler)
Definition httplib.h:1223
bool listen(const std::string &host, int port, int socket_flags=0)
Definition httplib.h:10914
bool set_base_dir(const std::string &dir, const std::string &mount_point=std::string())
Definition httplib.h:10684
void decommission()
Definition httplib.h:10945
bool listen_after_bind()
Definition httplib.h:10909
std::vector< std::string > trusted_proxies_
Definition httplib.h:1292
Server & set_tcp_nodelay(bool on)
Definition httplib.h:10812
std::function< void(const Request &, Response &, const ContentReader &content_reader)> HandlerWithContentReader
Definition httplib.h:1190
Server & set_header_writer(std::function< ssize_t(Stream &, Headers &)> const &writer)
Definition httplib.h:10836
Server & set_payload_max_length(size_t length)
Definition httplib.h:10883
Server & Options(const std::string &pattern, Handler handler)
Definition httplib.h:10678
virtual bool is_valid() const
Definition httplib.h:12374
int bind_to_any_port(const std::string &host, int socket_flags=0)
Definition httplib.h:10899
time_t idle_interval_sec_
Definition httplib.h:1300
bool bind_to_port(const std::string &host, int port, int socket_flags=0)
Definition httplib.h:10889
time_t write_timeout_usec_
Definition httplib.h:1299
time_t keep_alive_timeout_sec_
Definition httplib.h:1295
Server & Patch(const std::string &pattern, Handler handler)
Definition httplib.h:10650
Server & set_default_headers(Headers headers)
Definition httplib.h:10830
Server & Get(const std::string &pattern, Handler handler)
Definition httplib.h:10616
std::function< int(const Request &, Response &)> Expect100ContinueHandler
Definition httplib.h:1193
Server & set_trusted_proxies(const std::vector< std::string > &proxies)
Definition httplib.h:10844
Server & set_file_request_handler(Handler handler)
Definition httplib.h:10733
std::function< void(const Request &, Response &)> Handler
Definition httplib.h:1176
std::atomic< socket_t > svr_sock_
Definition httplib.h:1290
void wait_until_ready() const
Definition httplib.h:10925
Server & set_post_routing_handler(Handler handler)
Definition httplib.h:10769
bool is_running() const
Definition httplib.h:10920
Server & Post(const std::string &pattern, Handler handler)
Definition httplib.h:10622
Server & set_idle_interval(time_t sec, time_t usec=0)
Definition httplib.h:10876
Server & set_address_family(int family)
Definition httplib.h:10806
std::function< void(const Request &, Response &, std::exception_ptr ep)> ExceptionHandler
Definition httplib.h:1179
bool remove_mount_point(const std::string &mount_point)
Definition httplib.h:10706
Server & set_keep_alive_timeout(time_t sec)
Definition httplib.h:10856
std::function< TaskQueue *(void)> new_task_queue
Definition httplib.h:1281
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:12116
Server & set_logger(Logger logger)
Definition httplib.h:10781
bool set_mount_point(const std::string &mount_point, const std::string &dir, Headers headers=Headers())
Definition httplib.h:10690
Server & set_socket_options(SocketOptions socket_options)
Definition httplib.h:10824
Server & set_pre_compression_logger(Logger logger)
Definition httplib.h:10793
time_t read_timeout_sec_
Definition httplib.h:1296
Server & set_pre_request_handler(HandlerWithResponse handler)
Definition httplib.h:10775
Server & set_exception_handler(ExceptionHandler handler)
Definition httplib.h:10757
Server & set_error_logger(ErrorLogger error_logger)
Definition httplib.h:10787
Server & set_expect_100_continue_handler(Expect100ContinueHandler handler)
Definition httplib.h:10800
Server & set_ipv6_v6only(bool on)
Definition httplib.h:10818
size_t payload_max_length_
Definition httplib.h:1302
virtual ~Server()
Server & set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime)
Definition httplib.h:10720
Server & set_keep_alive_max_count(size_t count)
Definition httplib.h:10850
Server & set_read_timeout(time_t sec, time_t usec=0)
Definition httplib.h:10862
Error get_error() const
Definition httplib.h:949
virtual bool wait_writable() const =0
ssize_t write(const char *ptr)
Definition httplib.h:10090
virtual bool wait_readable() const =0
virtual ssize_t write(const char *ptr, size_t size)=0
virtual ssize_t read(char *ptr, size_t size)=0
virtual void get_remote_ip_and_port(std::string &ip, int &port) const =0
virtual void get_local_ip_and_port(std::string &ip, int &port) const =0
virtual time_t duration() const =0
virtual ~Stream()=default
virtual socket_t socket() const =0
virtual bool is_readable() const =0
virtual bool enqueue(std::function< void()> fn)=0
virtual void on_idle()
Definition httplib.h:967
virtual ~TaskQueue()=default
virtual void shutdown()=0
friend struct worker
Definition httplib.h:1056
ThreadPool(size_t n, size_t mqr=0)
Definition httplib.h:973
void shutdown() override
Definition httplib.h:1002
ThreadPool(const ThreadPool &)=delete
~ThreadPool() override=default
bool enqueue(std::function< void()> fn) override
Definition httplib.h:987
virtual bool match(Request &request) const =0
virtual ~MatcherBase()=default
MatcherBase(std::string pattern)
Definition httplib.h:1092
const std::string & pattern() const
Definition httplib.h:1095
PathParamsMatcher(const std::string &pattern)
Definition httplib.h:10426
bool match(Request &request) const override
Definition httplib.h:10485
bool match(Request &request) const override
Definition httplib.h:10538
RegexMatcher(const std::string &pattern)
Definition httplib.h:1158
double max(void)
int errno
#define CPPHTTPLIB_VERSION
Definition httplib.h:11
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
Definition httplib.h:130
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH
Definition httplib.h:134
#define CPPHTTPLIB_HEADER_MAX_COUNT
Definition httplib.h:122
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
Definition httplib.h:66
#define INVALID_SOCKET
Definition httplib.h:273
#define CPPHTTPLIB_SEND_FLAGS
Definition httplib.h:177
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
Definition httplib.h:50
#define CPPHTTPLIB_SEND_BUFSIZ
Definition httplib.h:158
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
Definition httplib.h:70
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
Definition httplib.h:74
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
Definition httplib.h:94
#define CPPHTTPLIB_LISTEN_BACKLOG
Definition httplib.h:181
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND
Definition httplib.h:102
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT
Definition httplib.h:54
#define CPPHTTPLIB_RANGE_MAX_COUNT
Definition httplib.h:142
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
Definition httplib.h:138
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND
Definition httplib.h:109
#define CPPHTTPLIB_IPV6_V6ONLY
Definition httplib.h:150
#define CPPHTTPLIB_RECV_FLAGS
Definition httplib.h:173
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
Definition httplib.h:82
int socket_t
Definition httplib.h:271
#define CPPHTTPLIB_HEADER_MAX_LENGTH
Definition httplib.h:118
#define CPPHTTPLIB_COMPRESSION_BUFSIZ
Definition httplib.h:162
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
Definition httplib.h:46
#define CPPHTTPLIB_THREAD_POOL_COUNT
Definition httplib.h:166
#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
Definition httplib.h:98
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
Definition httplib.h:78
#define CPPHTTPLIB_RECV_BUFSIZ
Definition httplib.h:154
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
Definition httplib.h:114
#define CPPHTTPLIB_REDIRECT_MAX_COUNT
Definition httplib.h:126
#define CPPHTTPLIB_MAX_LINE_LENGTH
Definition httplib.h:185
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
Definition httplib.h:86
#define CPPHTTPLIB_TCP_NODELAY
Definition httplib.h:146
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
Definition httplib.h:90
#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
Definition httplib.h:58
#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
Definition httplib.h:62
double dec
std::vector< uint8_t > buffer
std::vector< std::string > split(const std::string &input, const std::string &regex)
std::unordered_set< T, detail::case_ignore::hash, detail::case_ignore::equal_to > unordered_set
Definition httplib.h:476
bool equal(const std::string &a, const std::string &b)
Definition httplib.h:438
unsigned char to_lower(int c)
Definition httplib.h:412
ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf, size_t len)
Definition httplib.h:1593
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
Definition httplib.h:396
int close_socket(socket_t sock)
Definition httplib.h:4924
ssize_t write_headers(Stream &strm, const Headers &headers)
Definition httplib.h:7171
std::function< void(socket_t sock)> SocketOptions
Definition httplib.h:1074
std::ostream & operator<<(std::ostream &os, const Error &obj)
Definition httplib.h:9354
std::pair< std::string, std::string > make_range_header(const Ranges &ranges)
Definition httplib.h:9730
std::string encode_path_component(const std::string &component)
Definition httplib.h:9516
std::string get_client_ip(const std::string &x_forwarded_for, const std::vector< std::string > &trusted_proxies)
Definition httplib.h:12072
std::vector< Range > Ranges
Definition httplib.h:751
std::smatch Match
Definition httplib.h:610
std::string hosted_at(const std::string &hostname)
Definition httplib.h:9361
std::string decode_uri_component(const std::string &value)
Definition httplib.h:9460
std::function< bool(size_t offset, DataSink &sink)> ContentProviderWithoutLength
Definition httplib.h:704
std::function< bool(size_t current, size_t total)> DownloadProgress
Definition httplib.h:612
std::function< void(const Request &, const Response &)> Logger
Definition httplib.h:1068
@ SSLServerHostnameVerification
@ ExceedMaxSocketDescriptorCount
@ UnsupportedMultipartBoundaryChars
std::pair< ssize_t, ssize_t > Range
Definition httplib.h:750
std::string to_string(Error error)
Definition httplib.h:9275
std::function< void(const Error &, const Request *)> ErrorLogger
Definition httplib.h:1072
std::multimap< std::string, std::string > Params
Definition httplib.h:609
std::unordered_multimap< std::string, std::string, detail::case_ignore::hash, detail::case_ignore::equal_to > Headers
Definition httplib.h:607
std::string encode_uri_component(const std::string &value)
Definition httplib.h:9407
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:9755
const char * status_message(int status)
Definition httplib.h:9140
SSLVerifierResponse
Definition httplib.h:520
@ CertificateAccepted
Definition httplib.h:524
@ CertificateRejected
Definition httplib.h:526
@ NoDecisionMade
Definition httplib.h:522
std::string get_bearer_token_auth(const Request &req)
Definition httplib.h:2591
std::pair< std::string, std::string > make_bearer_token_authentication_header(const std::string &token, bool is_proxy=false)
Definition httplib.h:9764
std::function< bool(const char *data, size_t data_length, size_t offset, size_t total_length)> ContentReceiverWithProgress
Definition httplib.h:718
std::function< bool(size_t offset, size_t length, DataSink &sink)> ContentProvider
Definition httplib.h:701
std::vector< FormDataProvider > FormDataProviderItems
Definition httplib.h:715
std::function< bool(size_t current, size_t total)> UploadProgress
Definition httplib.h:613
std::multimap< std::string, FormData > FormFiles
Definition httplib.h:635
std::function< bool(const FormData &file)> FormDataHeader
Definition httplib.h:723
std::string decode_path_component(const std::string &component)
Definition httplib.h:9559
std::string decode_uri(const std::string &value)
Definition httplib.h:9488
std::vector< UploadFormData > UploadFormDataItems
Definition httplib.h:662
std::multimap< std::string, FormField > FormFields
Definition httplib.h:633
std::string encode_query_component(const std::string &component, bool space_as_plus=true)
Definition httplib.h:9612
std::function< bool(const char *data, size_t data_length)> ContentReceiver
Definition httplib.h:721
std::function< bool(const Response &response)> ResponseHandler
Definition httplib.h:616
std::string decode_query_component(const std::string &component, bool plus_as_space=true)
Definition httplib.h:9683
@ Conflict_409
Definition httplib.h:570
@ IMUsed_226
Definition httplib.h:547
@ SwitchingProtocol_101
Definition httplib.h:533
@ MisdirectedRequest_421
Definition httplib.h:580
@ FailedDependency_424
Definition httplib.h:583
@ Found_302
Definition httplib.h:552
@ MultipleChoices_300
Definition httplib.h:550
@ UnavailableForLegalReasons_451
Definition httplib.h:589
@ LoopDetected_508
Definition httplib.h:600
@ ServiceUnavailable_503
Definition httplib.h:595
@ EarlyHints_103
Definition httplib.h:535
@ ProxyAuthenticationRequired_407
Definition httplib.h:568
@ Processing_102
Definition httplib.h:534
@ Locked_423
Definition httplib.h:582
@ PaymentRequired_402
Definition httplib.h:563
@ NonAuthoritativeInformation_203
Definition httplib.h:541
@ UriTooLong_414
Definition httplib.h:575
@ Accepted_202
Definition httplib.h:540
@ ResetContent_205
Definition httplib.h:543
@ Forbidden_403
Definition httplib.h:564
@ Gone_410
Definition httplib.h:571
@ UnsupportedMediaType_415
Definition httplib.h:576
@ NotExtended_510
Definition httplib.h:601
@ NotModified_304
Definition httplib.h:554
@ VariantAlsoNegotiates_506
Definition httplib.h:598
@ unused_306
Definition httplib.h:556
@ TooManyRequests_429
Definition httplib.h:587
@ RequestTimeout_408
Definition httplib.h:569
@ OK_200
Definition httplib.h:538
@ NetworkAuthenticationRequired_511
Definition httplib.h:602
@ AlreadyReported_208
Definition httplib.h:546
@ MultiStatus_207
Definition httplib.h:545
@ ImATeapot_418
Definition httplib.h:579
@ NotAcceptable_406
Definition httplib.h:567
@ InternalServerError_500
Definition httplib.h:592
@ ExpectationFailed_417
Definition httplib.h:578
@ MethodNotAllowed_405
Definition httplib.h:566
@ NotFound_404
Definition httplib.h:565
@ TemporaryRedirect_307
Definition httplib.h:557
@ PermanentRedirect_308
Definition httplib.h:558
@ SeeOther_303
Definition httplib.h:553
@ Unauthorized_401
Definition httplib.h:562
@ PreconditionFailed_412
Definition httplib.h:573
@ Created_201
Definition httplib.h:539
@ NotImplemented_501
Definition httplib.h:593
@ UseProxy_305
Definition httplib.h:555
@ NoContent_204
Definition httplib.h:542
@ InsufficientStorage_507
Definition httplib.h:599
@ TooEarly_425
Definition httplib.h:584
@ BadGateway_502
Definition httplib.h:594
@ MovedPermanently_301
Definition httplib.h:551
@ PreconditionRequired_428
Definition httplib.h:586
@ PartialContent_206
Definition httplib.h:544
@ LengthRequired_411
Definition httplib.h:572
@ PayloadTooLarge_413
Definition httplib.h:574
@ RangeNotSatisfiable_416
Definition httplib.h:577
@ UnprocessableContent_422
Definition httplib.h:581
@ RequestHeaderFieldsTooLarge_431
Definition httplib.h:588
@ Continue_100
Definition httplib.h:532
@ BadRequest_400
Definition httplib.h:561
@ UpgradeRequired_426
Definition httplib.h:585
@ GatewayTimeout_504
Definition httplib.h:596
@ HttpVersionNotSupported_505
Definition httplib.h:597
std::string encode_uri(const std::string &value)
Definition httplib.h:9433
void default_socket_options(socket_t sock)
Definition httplib.h:2580
std::string append_query_params(const std::string &path, const Params &params)
Definition httplib.h:9718
std::function< void(bool success)> ContentProviderResourceReleaser
Definition httplib.h:706
@ error
throw a parse_error exception in case of a tag
uint16_t type
Definition pwc-ioctl.h:0
std::unique_ptr< RadioSim > receiver(new RadioSim())
ClientConnection & operator=(ClientConnection &&other) noexcept
Definition httplib.h:1554
ClientConnection(ClientConnection &&other) noexcept
Definition httplib.h:1541
ClientConnection & operator=(const ClientConnection &)=delete
ClientConnection(const ClientConnection &)=delete
StreamHandle & operator=(StreamHandle &&)=default
ssize_t read(char *buf, size_t len)
Definition httplib.h:13074
StreamHandle(const StreamHandle &)=delete
StreamHandle & operator=(const StreamHandle &)=delete
StreamHandle(StreamHandle &&)=default
std::unique_ptr< Response > response
Definition httplib.h:1621
std::string content_type
Definition httplib.h:713
ContentProviderWithoutLength provider
Definition httplib.h:711
std::string content
Definition httplib.h:621
std::string filename
Definition httplib.h:622
std::string content_type
Definition httplib.h:623
Headers headers
Definition httplib.h:624
std::string name
Definition httplib.h:620
std::string content
Definition httplib.h:630
std::string name
Definition httplib.h:629
std::string get_field(const std::string &key, size_t id=0) const
Definition httplib.h:9855
bool has_file(const std::string &key) const
Definition httplib.h:9916
FormData get_file(const std::string &key, size_t id=0) const
Definition httplib.h:9891
bool has_field(const std::string &key) const
Definition httplib.h:9880
size_t get_file_count(const std::string &key) const
Definition httplib.h:9921
std::vector< FormData > get_files(const std::string &key) const
Definition httplib.h:9905
std::vector< std::string > get_fields(const std::string &key) const
Definition httplib.h:9869
size_t get_field_count(const std::string &key) const
Definition httplib.h:9885
MultipartFormData form
Definition httplib.h:771
bool has_param(const std::string &key) const
Definition httplib.h:9824
bool has_header(const std::string &key) const
Definition httplib.h:9773
DownloadProgress download_progress
Definition httplib.h:784
size_t get_param_value_count(const std::string &key) const
Definition httplib.h:9842
std::vector< std::string > accept_content_types
Definition httplib.h:781
size_t get_trailer_value_count(const std::string &key) const
Definition httplib.h:9818
ContentReceiverWithProgress content_receiver
Definition httplib.h:783
std::string local_addr
Definition httplib.h:765
std::unordered_map< std::string, std::string > path_params
Definition httplib.h:774
UploadProgress upload_progress
Definition httplib.h:785
Headers trailers
Definition httplib.h:760
std::string remote_addr
Definition httplib.h:763
std::string get_header_value(const std::string &key, const char *def="", size_t id=0) const
Definition httplib.h:9778
std::string target
Definition httplib.h:770
size_t get_header_value_u64(const std::string &key, size_t def=0, size_t id=0) const
Definition httplib.h:2533
std::string get_param_value(const std::string &key, size_t id=0) const
Definition httplib.h:9829
size_t authorization_count_
Definition httplib.h:813
bool is_multipart_form_data() const
Definition httplib.h:9848
std::string version
Definition httplib.h:769
bool has_trailer(const std::string &key) const
Definition httplib.h:9800
std::chrono::time_point< std::chrono::steady_clock > start_time_
Definition httplib.h:814
ContentProvider content_provider_
Definition httplib.h:811
std::string get_trailer_value(const std::string &key, size_t id=0) const
Definition httplib.h:9805
size_t content_length_
Definition httplib.h:810
std::string path
Definition httplib.h:756
bool is_chunked_content_provider_
Definition httplib.h:812
std::function< bool()> is_connection_closed
Definition httplib.h:775
std::string body
Definition httplib.h:761
size_t redirect_count_
Definition httplib.h:809
std::string matched_route
Definition httplib.h:757
size_t get_header_value_count(const std::string &key) const
Definition httplib.h:9784
ResponseHandler response_handler
Definition httplib.h:782
std::string method
Definition httplib.h:755
void set_header(const std::string &key, const std::string &val)
Definition httplib.h:9790
Headers headers
Definition httplib.h:759
ContentProvider content_provider_
Definition httplib.h:876
bool is_chunked_content_provider_
Definition httplib.h:878
Headers trailers
Definition httplib.h:824
void set_redirect(const std::string &url, int status=StatusCode::Found_302)
Definition httplib.h:9979
Response & operator=(Response &&)=default
std::string file_content_path_
Definition httplib.h:880
size_t get_header_value_u64(const std::string &key, size_t def=0, size_t id=0) const
Definition httplib.h:2539
Response()=default
size_t content_length_
Definition httplib.h:875
void set_content_provider(size_t length, const std::string &content_type, ContentProvider provider, ContentProviderResourceReleaser resource_releaser=nullptr)
Definition httplib.h:10021
std::string version
Definition httplib.h:820
Headers headers
Definition httplib.h:823
void set_content(const char *s, size_t n, const std::string &content_type)
Definition httplib.h:9995
bool has_header(const std::string &key) const
Definition httplib.h:9928
bool content_provider_success_
Definition httplib.h:879
ContentProviderResourceReleaser content_provider_resource_releaser_
Definition httplib.h:877
std::string get_header_value(const std::string &key, const char *def="", size_t id=0) const
Definition httplib.h:9933
void set_chunked_content_provider(const std::string &content_type, ContentProviderWithoutLength provider, ContentProviderResourceReleaser resource_releaser=nullptr)
Definition httplib.h:10046
std::string get_trailer_value(const std::string &key, size_t id=0) const
Definition httplib.h:9960
std::string reason
Definition httplib.h:822
size_t get_trailer_value_count(const std::string &key) const
Definition httplib.h:9973
std::string body
Definition httplib.h:825
void set_file_content(const std::string &path, const std::string &content_type)
Definition httplib.h:10057
bool has_trailer(const std::string &key) const
Definition httplib.h:9955
Response & operator=(const Response &)=default
Response(const Response &)=default
std::string file_content_content_type_
Definition httplib.h:881
Response(Response &&)=default
void set_header(const std::string &key, const std::string &val)
Definition httplib.h:9946
size_t get_header_value_count(const std::string &key) const
Definition httplib.h:9940
std::string location
Definition httplib.h:826
std::string filename
Definition httplib.h:659
std::string content_type
Definition httplib.h:660
std::string content
Definition httplib.h:658
std::unique_ptr< ChunkedDecoder > chunked_decoder
Definition httplib.h:1583
ssize_t read(char *buf, size_t len)
Definition httplib.h:10101
bool operator()(const std::string &a, const std::string &b) const
Definition httplib.h:449
size_t operator()(const std::string &key) const
Definition httplib.h:457
size_t hash_core(const char *s, size_t l, size_t h) const
Definition httplib.h:462
scope_exit(std::function< void(void)> &&f)
Definition httplib.h:485
scope_exit(scope_exit &&rhs) noexcept
Definition httplib.h:488
#define TRUE
Definition stvdriver.c:54
#define FALSE
Definition stvdriver.c:53
unsigned char byte
Definition stvdriver.h:175