Coverage Report

Created: 2026-01-21 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpp-httplib/httplib.h
Line
Count
Source
1
//
2
//  httplib.h
3
//
4
//  Copyright (c) 2026 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.30.1"
12
#define CPPHTTPLIB_VERSION_NUM "0x001E01"
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
0
#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_EXPECT_100_THRESHOLD
102
#define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024
103
#endif
104
105
#ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
106
#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000
107
#endif
108
109
#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
110
#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024)
111
#endif
112
113
#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND
114
#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50
115
#endif
116
117
#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
118
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
119
#endif
120
121
#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
122
#ifdef _WIN32
123
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
124
#else
125
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
126
#endif
127
#endif
128
129
#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
130
4.83k
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
131
#endif
132
133
#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
134
36.0k
#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
135
#endif
136
137
#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
138
15.4k
#define CPPHTTPLIB_HEADER_MAX_COUNT 100
139
#endif
140
141
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
142
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
143
#endif
144
145
#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
146
19.3k
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
147
#endif
148
149
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
150
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
151
#endif
152
153
#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
154
18
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
155
#endif
156
157
#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
158
700
#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
159
#endif
160
161
#ifndef CPPHTTPLIB_TCP_NODELAY
162
#define CPPHTTPLIB_TCP_NODELAY false
163
#endif
164
165
#ifndef CPPHTTPLIB_IPV6_V6ONLY
166
#define CPPHTTPLIB_IPV6_V6ONLY false
167
#endif
168
169
#ifndef CPPHTTPLIB_RECV_BUFSIZ
170
3.84k
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
171
#endif
172
173
#ifndef CPPHTTPLIB_SEND_BUFSIZ
174
#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
175
#endif
176
177
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
178
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
179
#endif
180
181
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
182
#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
183
0
  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \
184
0
                      ? std::thread::hardware_concurrency() - 1                \
185
0
                      : 0))
186
#endif
187
188
#ifndef CPPHTTPLIB_RECV_FLAGS
189
0
#define CPPHTTPLIB_RECV_FLAGS 0
190
#endif
191
192
#ifndef CPPHTTPLIB_SEND_FLAGS
193
0
#define CPPHTTPLIB_SEND_FLAGS 0
194
#endif
195
196
#ifndef CPPHTTPLIB_LISTEN_BACKLOG
197
#define CPPHTTPLIB_LISTEN_BACKLOG 5
198
#endif
199
200
#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
201
2.67M
#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
202
#endif
203
204
/*
205
 * Headers
206
 */
207
208
#ifdef _WIN32
209
#ifndef _CRT_SECURE_NO_WARNINGS
210
#define _CRT_SECURE_NO_WARNINGS
211
#endif //_CRT_SECURE_NO_WARNINGS
212
213
#ifndef _CRT_NONSTDC_NO_DEPRECATE
214
#define _CRT_NONSTDC_NO_DEPRECATE
215
#endif //_CRT_NONSTDC_NO_DEPRECATE
216
217
#if defined(_MSC_VER)
218
#if _MSC_VER < 1900
219
#error Sorry, Visual Studio versions prior to 2015 are not supported
220
#endif
221
222
#pragma comment(lib, "ws2_32.lib")
223
224
#ifndef _SSIZE_T_DEFINED
225
using ssize_t = __int64;
226
#define _SSIZE_T_DEFINED
227
#endif
228
#endif // _MSC_VER
229
230
#ifndef S_ISREG
231
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
232
#endif // S_ISREG
233
234
#ifndef S_ISDIR
235
#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
236
#endif // S_ISDIR
237
238
#ifndef NOMINMAX
239
#define NOMINMAX
240
#endif // NOMINMAX
241
242
#include <io.h>
243
#include <winsock2.h>
244
#include <ws2tcpip.h>
245
246
#if defined(__has_include)
247
#if __has_include(<afunix.h>)
248
// afunix.h uses types declared in winsock2.h, so has to be included after it.
249
#include <afunix.h>
250
#define CPPHTTPLIB_HAVE_AFUNIX_H 1
251
#endif
252
#endif
253
254
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
255
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
256
#endif
257
258
using nfds_t = unsigned long;
259
using socket_t = SOCKET;
260
using socklen_t = int;
261
262
#else // not _WIN32
263
264
#include <arpa/inet.h>
265
#if !defined(_AIX) && !defined(__MVS__)
266
#include <ifaddrs.h>
267
#endif
268
#ifdef __MVS__
269
#include <strings.h>
270
#ifndef NI_MAXHOST
271
#define NI_MAXHOST 1025
272
#endif
273
#endif
274
#include <net/if.h>
275
#include <netdb.h>
276
#include <netinet/in.h>
277
#ifdef __linux__
278
#include <resolv.h>
279
#undef _res // Undefine _res macro to avoid conflicts with user code (#2278)
280
#endif
281
#include <csignal>
282
#include <netinet/tcp.h>
283
#include <poll.h>
284
#include <pthread.h>
285
#include <sys/mman.h>
286
#include <sys/socket.h>
287
#include <sys/un.h>
288
#include <unistd.h>
289
290
using socket_t = int;
291
#ifndef INVALID_SOCKET
292
0
#define INVALID_SOCKET (-1)
293
#endif
294
#endif //_WIN32
295
296
#if defined(__APPLE__)
297
#include <TargetConditionals.h>
298
#endif
299
300
#include <algorithm>
301
#include <array>
302
#include <atomic>
303
#include <cassert>
304
#include <cctype>
305
#include <chrono>
306
#include <climits>
307
#include <condition_variable>
308
#include <cstring>
309
#include <errno.h>
310
#include <exception>
311
#include <fcntl.h>
312
#include <functional>
313
#include <iomanip>
314
#include <iostream>
315
#include <list>
316
#include <map>
317
#include <memory>
318
#include <mutex>
319
#include <random>
320
#include <regex>
321
#include <set>
322
#include <sstream>
323
#include <string>
324
#include <sys/stat.h>
325
#include <thread>
326
#include <unordered_map>
327
#include <unordered_set>
328
#include <utility>
329
330
#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) ||                        \
331
    defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
332
#if TARGET_OS_MAC
333
#include <CFNetwork/CFHost.h>
334
#include <CoreFoundation/CoreFoundation.h>
335
#endif
336
#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or
337
       // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
338
339
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
340
#ifdef _WIN32
341
#include <wincrypt.h>
342
343
// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
344
// used
345
#undef X509_NAME
346
#undef X509_CERT_PAIR
347
#undef X509_EXTENSIONS
348
#undef PKCS7_SIGNER_INFO
349
350
#ifdef _MSC_VER
351
#pragma comment(lib, "crypt32.lib")
352
#endif
353
#endif // _WIN32
354
355
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
356
#if TARGET_OS_MAC
357
#include <Security/Security.h>
358
#endif
359
#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
360
361
#include <openssl/err.h>
362
#include <openssl/evp.h>
363
#include <openssl/ssl.h>
364
#include <openssl/x509v3.h>
365
366
#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
367
#include <openssl/applink.c>
368
#endif
369
370
#include <iostream>
371
#include <sstream>
372
373
#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
374
#if OPENSSL_VERSION_NUMBER < 0x1010107f
375
#error Please use OpenSSL or a current version of BoringSSL
376
#endif
377
#define SSL_get1_peer_certificate SSL_get_peer_certificate
378
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
379
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
380
#endif
381
382
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
383
384
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
385
#include <zlib.h>
386
#endif
387
388
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
389
#include <brotli/decode.h>
390
#include <brotli/encode.h>
391
#endif
392
393
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
394
#include <zstd.h>
395
#endif
396
397
/*
398
 * Declaration
399
 */
400
namespace httplib {
401
402
namespace detail {
403
404
/*
405
 * Backport std::make_unique from C++14.
406
 *
407
 * NOTE: This code came up with the following stackoverflow post:
408
 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
409
 *
410
 */
411
412
template <class T, class... Args>
413
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
414
702
make_unique(Args &&...args) {
415
702
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
416
702
}
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_17PathParamsMatcherEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISD_NS3_14default_deleteISD_EEEEE4typeEDpOT0_
_ZN7httplib6detail11make_uniqueINS0_12RegexMatcherEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISD_NS3_14default_deleteISD_EEEEE4typeEDpOT0_
Line
Count
Source
414
12
make_unique(Args &&...args) {
415
12
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
416
12
}
_ZN7httplib6detail11make_uniqueINS0_15gzip_compressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Line
Count
Source
414
18
make_unique(Args &&...args) {
415
18
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
416
18
}
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_12nocompressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
_ZN7httplib6detail11make_uniqueINS0_17gzip_decompressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Line
Count
Source
414
672
make_unique(Args &&...args) {
415
672
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
416
672
}
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_8ResponseEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_16ClientConnectionEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_12SocketStreamEJRiRlS4_S4_S4_EEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS5_10unique_ptrIS7_NS5_14default_deleteIS7_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEERiRKS9_SD_EEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISF_NS3_14default_deleteISF_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEiSB_SB_EEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISD_NS3_14default_deleteISD_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEERiEEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISE_NS3_14default_deleteISE_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEERiSB_SB_EEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISE_NS3_14default_deleteISE_EEEEE4typeEDpOT0_
417
418
template <class T>
419
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
420
make_unique(std::size_t n) {
421
  typedef typename std::remove_extent<T>::type RT;
422
  return std::unique_ptr<T>(new RT[n]);
423
}
424
425
namespace case_ignore {
426
427
169M
inline unsigned char to_lower(int c) {
428
169M
  const static unsigned char table[256] = {
429
169M
      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
430
169M
      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
431
169M
      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
432
169M
      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
433
169M
      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
434
169M
      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
435
169M
      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
436
169M
      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
437
169M
      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
438
169M
      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
439
169M
      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
440
169M
      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
441
169M
      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
442
169M
      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
443
169M
      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
444
169M
      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
445
169M
      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
446
169M
      255,
447
169M
  };
448
169M
  return table[(unsigned char)(char)c];
449
169M
}
450
451
43.8M
inline bool equal(const std::string &a, const std::string &b) {
452
43.8M
  return a.size() == b.size() &&
453
82.9M
         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
454
82.9M
           return to_lower(ca) == to_lower(cb);
455
82.9M
         });
456
43.8M
}
457
458
struct equal_to {
459
43.3M
  bool operator()(const std::string &a, const std::string &b) const {
460
43.3M
    return equal(a, b);
461
43.3M
  }
462
};
463
464
struct hash {
465
487k
  size_t operator()(const std::string &key) const {
466
487k
    return hash_core(key.data(), key.size(), 0);
467
487k
  }
468
469
4.00M
  size_t hash_core(const char *s, size_t l, size_t h) const {
470
4.00M
    return (l == 0) ? h
471
4.00M
                    : hash_core(s + 1, l - 1,
472
                                // Unsets the 6 high bits of h, therefore no
473
                                // overflow happens
474
3.52M
                                (((std::numeric_limits<size_t>::max)() >> 6) &
475
3.52M
                                 h * 33) ^
476
3.52M
                                    static_cast<unsigned char>(to_lower(*s)));
477
4.00M
  }
478
};
479
480
template <typename T>
481
using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
482
                                         detail::case_ignore::equal_to>;
483
484
} // namespace case_ignore
485
486
// This is based on
487
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
488
489
struct scope_exit {
490
  explicit scope_exit(std::function<void(void)> &&f)
491
0
      : exit_function(std::move(f)), execute_on_destruction{true} {}
492
493
  scope_exit(scope_exit &&rhs) noexcept
494
      : exit_function(std::move(rhs.exit_function)),
495
0
        execute_on_destruction{rhs.execute_on_destruction} {
496
0
    rhs.release();
497
0
  }
498
499
0
  ~scope_exit() {
500
0
    if (execute_on_destruction) { this->exit_function(); }
501
0
  }
502
503
0
  void release() { this->execute_on_destruction = false; }
504
505
private:
506
  scope_exit(const scope_exit &) = delete;
507
  void operator=(const scope_exit &) = delete;
508
  scope_exit &operator=(scope_exit &&) = delete;
509
510
  std::function<void(void)> exit_function;
511
  bool execute_on_destruction;
512
};
513
514
} // namespace detail
515
516
enum SSLVerifierResponse {
517
  // no decision has been made, use the built-in certificate verifier
518
  NoDecisionMade,
519
  // connection certificate is verified and accepted
520
  CertificateAccepted,
521
  // connection certificate was processed but is rejected
522
  CertificateRejected
523
};
524
525
enum StatusCode {
526
  // Information responses
527
  Continue_100 = 100,
528
  SwitchingProtocol_101 = 101,
529
  Processing_102 = 102,
530
  EarlyHints_103 = 103,
531
532
  // Successful responses
533
  OK_200 = 200,
534
  Created_201 = 201,
535
  Accepted_202 = 202,
536
  NonAuthoritativeInformation_203 = 203,
537
  NoContent_204 = 204,
538
  ResetContent_205 = 205,
539
  PartialContent_206 = 206,
540
  MultiStatus_207 = 207,
541
  AlreadyReported_208 = 208,
542
  IMUsed_226 = 226,
543
544
  // Redirection messages
545
  MultipleChoices_300 = 300,
546
  MovedPermanently_301 = 301,
547
  Found_302 = 302,
548
  SeeOther_303 = 303,
549
  NotModified_304 = 304,
550
  UseProxy_305 = 305,
551
  unused_306 = 306,
552
  TemporaryRedirect_307 = 307,
553
  PermanentRedirect_308 = 308,
554
555
  // Client error responses
556
  BadRequest_400 = 400,
557
  Unauthorized_401 = 401,
558
  PaymentRequired_402 = 402,
559
  Forbidden_403 = 403,
560
  NotFound_404 = 404,
561
  MethodNotAllowed_405 = 405,
562
  NotAcceptable_406 = 406,
563
  ProxyAuthenticationRequired_407 = 407,
564
  RequestTimeout_408 = 408,
565
  Conflict_409 = 409,
566
  Gone_410 = 410,
567
  LengthRequired_411 = 411,
568
  PreconditionFailed_412 = 412,
569
  PayloadTooLarge_413 = 413,
570
  UriTooLong_414 = 414,
571
  UnsupportedMediaType_415 = 415,
572
  RangeNotSatisfiable_416 = 416,
573
  ExpectationFailed_417 = 417,
574
  ImATeapot_418 = 418,
575
  MisdirectedRequest_421 = 421,
576
  UnprocessableContent_422 = 422,
577
  Locked_423 = 423,
578
  FailedDependency_424 = 424,
579
  TooEarly_425 = 425,
580
  UpgradeRequired_426 = 426,
581
  PreconditionRequired_428 = 428,
582
  TooManyRequests_429 = 429,
583
  RequestHeaderFieldsTooLarge_431 = 431,
584
  UnavailableForLegalReasons_451 = 451,
585
586
  // Server error responses
587
  InternalServerError_500 = 500,
588
  NotImplemented_501 = 501,
589
  BadGateway_502 = 502,
590
  ServiceUnavailable_503 = 503,
591
  GatewayTimeout_504 = 504,
592
  HttpVersionNotSupported_505 = 505,
593
  VariantAlsoNegotiates_506 = 506,
594
  InsufficientStorage_507 = 507,
595
  LoopDetected_508 = 508,
596
  NotExtended_510 = 510,
597
  NetworkAuthenticationRequired_511 = 511,
598
};
599
600
using Headers =
601
    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
602
                            detail::case_ignore::equal_to>;
603
604
using Params = std::multimap<std::string, std::string>;
605
using Match = std::smatch;
606
607
using DownloadProgress = std::function<bool(size_t current, size_t total)>;
608
using UploadProgress = std::function<bool(size_t current, size_t total)>;
609
610
struct Response;
611
using ResponseHandler = std::function<bool(const Response &response)>;
612
613
struct FormData {
614
  std::string name;
615
  std::string content;
616
  std::string filename;
617
  std::string content_type;
618
  Headers headers;
619
};
620
621
struct FormField {
622
  std::string name;
623
  std::string content;
624
  Headers headers;
625
};
626
using FormFields = std::multimap<std::string, FormField>;
627
628
using FormFiles = std::multimap<std::string, FormData>;
629
630
struct MultipartFormData {
631
  FormFields fields; // Text fields from multipart
632
  FormFiles files;   // Files from multipart
633
634
  // Text field access
635
  std::string get_field(const std::string &key, size_t id = 0) const;
636
  std::vector<std::string> get_fields(const std::string &key) const;
637
  bool has_field(const std::string &key) const;
638
  size_t get_field_count(const std::string &key) const;
639
640
  // File access
641
  FormData get_file(const std::string &key, size_t id = 0) const;
642
  std::vector<FormData> get_files(const std::string &key) const;
643
  bool has_file(const std::string &key) const;
644
  size_t get_file_count(const std::string &key) const;
645
};
646
647
struct UploadFormData {
648
  std::string name;
649
  std::string content;
650
  std::string filename;
651
  std::string content_type;
652
};
653
using UploadFormDataItems = std::vector<UploadFormData>;
654
655
class DataSink {
656
public:
657
0
  DataSink() : os(&sb_), sb_(*this) {}
658
659
  DataSink(const DataSink &) = delete;
660
  DataSink &operator=(const DataSink &) = delete;
661
  DataSink(DataSink &&) = delete;
662
  DataSink &operator=(DataSink &&) = delete;
663
664
  std::function<bool(const char *data, size_t data_len)> write;
665
  std::function<bool()> is_writable;
666
  std::function<void()> done;
667
  std::function<void(const Headers &trailer)> done_with_trailer;
668
  std::ostream os;
669
670
private:
671
  class data_sink_streambuf final : public std::streambuf {
672
  public:
673
0
    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
674
675
  protected:
676
0
    std::streamsize xsputn(const char *s, std::streamsize n) override {
677
0
      sink_.write(s, static_cast<size_t>(n));
678
0
      return n;
679
0
    }
680
681
  private:
682
    DataSink &sink_;
683
  };
684
685
  data_sink_streambuf sb_;
686
};
687
688
using ContentProvider =
689
    std::function<bool(size_t offset, size_t length, DataSink &sink)>;
690
691
using ContentProviderWithoutLength =
692
    std::function<bool(size_t offset, DataSink &sink)>;
693
694
using ContentProviderResourceReleaser = std::function<void(bool success)>;
695
696
struct FormDataProvider {
697
  std::string name;
698
  ContentProviderWithoutLength provider;
699
  std::string filename;
700
  std::string content_type;
701
};
702
using FormDataProviderItems = std::vector<FormDataProvider>;
703
704
using ContentReceiverWithProgress = std::function<bool(
705
    const char *data, size_t data_length, size_t offset, size_t total_length)>;
706
707
using ContentReceiver =
708
    std::function<bool(const char *data, size_t data_length)>;
709
710
using FormDataHeader = std::function<bool(const FormData &file)>;
711
712
class ContentReader {
713
public:
714
  using Reader = std::function<bool(ContentReceiver receiver)>;
715
  using FormDataReader =
716
      std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
717
718
  ContentReader(Reader reader, FormDataReader multipart_reader)
719
3.72k
      : reader_(std::move(reader)),
720
3.72k
        formdata_reader_(std::move(multipart_reader)) {}
721
722
0
  bool operator()(FormDataHeader header, ContentReceiver receiver) const {
723
0
    return formdata_reader_(std::move(header), std::move(receiver));
724
0
  }
725
726
0
  bool operator()(ContentReceiver receiver) const {
727
0
    return reader_(std::move(receiver));
728
0
  }
729
730
  Reader reader_;
731
  FormDataReader formdata_reader_;
732
};
733
734
using Range = std::pair<ssize_t, ssize_t>;
735
using Ranges = std::vector<Range>;
736
737
struct Request {
738
  std::string method;
739
  std::string path;
740
  std::string matched_route;
741
  Params params;
742
  Headers headers;
743
  Headers trailers;
744
  std::string body;
745
746
  std::string remote_addr;
747
  int remote_port = -1;
748
  std::string local_addr;
749
  int local_port = -1;
750
751
  // for server
752
  std::string version;
753
  std::string target;
754
  MultipartFormData form;
755
  Ranges ranges;
756
  Match matches;
757
  std::unordered_map<std::string, std::string> path_params;
758
0
  std::function<bool()> is_connection_closed = []() { return true; };
759
760
  // for client
761
  std::vector<std::string> accept_content_types;
762
  ResponseHandler response_handler;
763
  ContentReceiverWithProgress content_receiver;
764
  DownloadProgress download_progress;
765
  UploadProgress upload_progress;
766
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
767
  const SSL *ssl = nullptr;
768
#endif
769
770
  bool has_header(const std::string &key) const;
771
  std::string get_header_value(const std::string &key, const char *def = "",
772
                               size_t id = 0) const;
773
  size_t get_header_value_u64(const std::string &key, size_t def = 0,
774
                              size_t id = 0) const;
775
  size_t get_header_value_count(const std::string &key) const;
776
  void set_header(const std::string &key, const std::string &val);
777
778
  bool has_trailer(const std::string &key) const;
779
  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
780
  size_t get_trailer_value_count(const std::string &key) const;
781
782
  bool has_param(const std::string &key) const;
783
  std::string get_param_value(const std::string &key, size_t id = 0) const;
784
  size_t get_param_value_count(const std::string &key) const;
785
786
  bool is_multipart_form_data() const;
787
788
  // private members...
789
  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
790
  size_t content_length_ = 0;
791
  ContentProvider content_provider_;
792
  bool is_chunked_content_provider_ = false;
793
  size_t authorization_count_ = 0;
794
  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
795
      (std::chrono::steady_clock::time_point::min)();
796
};
797
798
struct Response {
799
  std::string version;
800
  int status = -1;
801
  std::string reason;
802
  Headers headers;
803
  Headers trailers;
804
  std::string body;
805
  std::string location; // Redirect location
806
807
  bool has_header(const std::string &key) const;
808
  std::string get_header_value(const std::string &key, const char *def = "",
809
                               size_t id = 0) const;
810
  size_t get_header_value_u64(const std::string &key, size_t def = 0,
811
                              size_t id = 0) const;
812
  size_t get_header_value_count(const std::string &key) const;
813
  void set_header(const std::string &key, const std::string &val);
814
815
  bool has_trailer(const std::string &key) const;
816
  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
817
  size_t get_trailer_value_count(const std::string &key) const;
818
819
  void set_redirect(const std::string &url, int status = StatusCode::Found_302);
820
  void set_content(const char *s, size_t n, const std::string &content_type);
821
  void set_content(const std::string &s, const std::string &content_type);
822
  void set_content(std::string &&s, const std::string &content_type);
823
824
  void set_content_provider(
825
      size_t length, const std::string &content_type, ContentProvider provider,
826
      ContentProviderResourceReleaser resource_releaser = nullptr);
827
828
  void set_content_provider(
829
      const std::string &content_type, ContentProviderWithoutLength provider,
830
      ContentProviderResourceReleaser resource_releaser = nullptr);
831
832
  void set_chunked_content_provider(
833
      const std::string &content_type, ContentProviderWithoutLength provider,
834
      ContentProviderResourceReleaser resource_releaser = nullptr);
835
836
  void set_file_content(const std::string &path,
837
                        const std::string &content_type);
838
  void set_file_content(const std::string &path);
839
840
6.53k
  Response() = default;
841
  Response(const Response &) = default;
842
  Response &operator=(const Response &) = default;
843
  Response(Response &&) = default;
844
  Response &operator=(Response &&) = default;
845
6.53k
  ~Response() {
846
6.53k
    if (content_provider_resource_releaser_) {
847
0
      content_provider_resource_releaser_(content_provider_success_);
848
0
    }
849
6.53k
  }
850
851
  // private members...
852
  size_t content_length_ = 0;
853
  ContentProvider content_provider_;
854
  ContentProviderResourceReleaser content_provider_resource_releaser_;
855
  bool is_chunked_content_provider_ = false;
856
  bool content_provider_success_ = false;
857
  std::string file_content_path_;
858
  std::string file_content_content_type_;
859
};
860
861
enum class Error {
862
  Success = 0,
863
  Unknown,
864
  Connection,
865
  BindIPAddress,
866
  Read,
867
  Write,
868
  ExceedRedirectCount,
869
  Canceled,
870
  SSLConnection,
871
  SSLLoadingCerts,
872
  SSLServerVerification,
873
  SSLServerHostnameVerification,
874
  UnsupportedMultipartBoundaryChars,
875
  Compression,
876
  ConnectionTimeout,
877
  ProxyConnection,
878
  ConnectionClosed,
879
  Timeout,
880
  ResourceExhaustion,
881
  TooManyFormDataFiles,
882
  ExceedMaxPayloadSize,
883
  ExceedUriMaxLength,
884
  ExceedMaxSocketDescriptorCount,
885
  InvalidRequestLine,
886
  InvalidHTTPMethod,
887
  InvalidHTTPVersion,
888
  InvalidHeaders,
889
  MultipartParsing,
890
  OpenFile,
891
  Listen,
892
  GetSockName,
893
  UnsupportedAddressFamily,
894
  HTTPParsing,
895
  InvalidRangeHeader,
896
897
  // For internal use only
898
  SSLPeerCouldBeClosed_,
899
};
900
901
std::string to_string(Error error);
902
903
std::ostream &operator<<(std::ostream &os, const Error &obj);
904
905
class Stream {
906
public:
907
13.0k
  virtual ~Stream() = default;
908
909
  virtual bool is_readable() const = 0;
910
  virtual bool wait_readable() const = 0;
911
  virtual bool wait_writable() const = 0;
912
913
  virtual ssize_t read(char *ptr, size_t size) = 0;
914
  virtual ssize_t write(const char *ptr, size_t size) = 0;
915
  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
916
  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
917
  virtual socket_t socket() const = 0;
918
919
  virtual time_t duration() const = 0;
920
921
  ssize_t write(const char *ptr);
922
  ssize_t write(const std::string &s);
923
924
1.26k
  Error get_error() const { return error_; }
925
926
protected:
927
  Error error_ = Error::Success;
928
};
929
930
class TaskQueue {
931
public:
932
0
  TaskQueue() = default;
933
0
  virtual ~TaskQueue() = default;
934
935
  virtual bool enqueue(std::function<void()> fn) = 0;
936
  virtual void shutdown() = 0;
937
938
0
  virtual void on_idle() {}
939
};
940
941
class ThreadPool final : public TaskQueue {
942
public:
943
  explicit ThreadPool(size_t n, size_t mqr = 0)
944
0
      : shutdown_(false), max_queued_requests_(mqr) {
945
0
    threads_.reserve(n);
946
0
    while (n) {
947
0
      threads_.emplace_back(worker(*this));
948
0
      n--;
949
0
    }
950
0
  }
951
952
  ThreadPool(const ThreadPool &) = delete;
953
0
  ~ThreadPool() override = default;
954
955
0
  bool enqueue(std::function<void()> fn) override {
956
0
    {
957
0
      std::unique_lock<std::mutex> lock(mutex_);
958
0
      if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
959
0
        return false;
960
0
      }
961
0
      jobs_.push_back(std::move(fn));
962
0
    }
963
964
0
    cond_.notify_one();
965
0
    return true;
966
0
  }
967
968
0
  void shutdown() override {
969
    // Stop all worker threads...
970
0
    {
971
0
      std::unique_lock<std::mutex> lock(mutex_);
972
0
      shutdown_ = true;
973
0
    }
974
975
0
    cond_.notify_all();
976
977
    // Join...
978
0
    for (auto &t : threads_) {
979
0
      t.join();
980
0
    }
981
0
  }
982
983
private:
984
  struct worker {
985
0
    explicit worker(ThreadPool &pool) : pool_(pool) {}
986
987
0
    void operator()() {
988
0
      for (;;) {
989
0
        std::function<void()> fn;
990
0
        {
991
0
          std::unique_lock<std::mutex> lock(pool_.mutex_);
992
993
0
          pool_.cond_.wait(
994
0
              lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
995
996
0
          if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
997
998
0
          fn = pool_.jobs_.front();
999
0
          pool_.jobs_.pop_front();
1000
0
        }
1001
1002
0
        assert(true == static_cast<bool>(fn));
1003
0
        fn();
1004
0
      }
1005
1006
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \
1007
    !defined(LIBRESSL_VERSION_NUMBER)
1008
      OPENSSL_thread_stop();
1009
#endif
1010
0
    }
1011
1012
    ThreadPool &pool_;
1013
  };
1014
  friend struct worker;
1015
1016
  std::vector<std::thread> threads_;
1017
  std::list<std::function<void()>> jobs_;
1018
1019
  bool shutdown_;
1020
  size_t max_queued_requests_ = 0;
1021
1022
  std::condition_variable cond_;
1023
  std::mutex mutex_;
1024
};
1025
1026
using Logger = std::function<void(const Request &, const Response &)>;
1027
1028
// Forward declaration for Error type
1029
enum class Error;
1030
using ErrorLogger = std::function<void(const Error &, const Request *)>;
1031
1032
using SocketOptions = std::function<void(socket_t sock)>;
1033
1034
void default_socket_options(socket_t sock);
1035
1036
const char *status_message(int status);
1037
1038
std::string to_string(Error error);
1039
1040
std::ostream &operator<<(std::ostream &os, const Error &obj);
1041
1042
std::string get_bearer_token_auth(const Request &req);
1043
1044
namespace detail {
1045
1046
class MatcherBase {
1047
public:
1048
12
  MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
1049
0
  virtual ~MatcherBase() = default;
1050
1051
1.83k
  const std::string &pattern() const { return pattern_; }
1052
1053
  // Match request path and populate its matches and
1054
  virtual bool match(Request &request) const = 0;
1055
1056
private:
1057
  std::string pattern_;
1058
};
1059
1060
/**
1061
 * Captures parameters in request path and stores them in Request::path_params
1062
 *
1063
 * Capture name is a substring of a pattern from : to /.
1064
 * The rest of the pattern is matched against the request path directly
1065
 * Parameters are captured starting from the next character after
1066
 * the end of the last matched static pattern fragment until the next /.
1067
 *
1068
 * Example pattern:
1069
 * "/path/fragments/:capture/more/fragments/:second_capture"
1070
 * Static fragments:
1071
 * "/path/fragments/", "more/fragments/"
1072
 *
1073
 * Given the following request path:
1074
 * "/path/fragments/:1/more/fragments/:2"
1075
 * the resulting capture will be
1076
 * {{"capture", "1"}, {"second_capture", "2"}}
1077
 */
1078
class PathParamsMatcher final : public MatcherBase {
1079
public:
1080
  PathParamsMatcher(const std::string &pattern);
1081
1082
  bool match(Request &request) const override;
1083
1084
private:
1085
  // Treat segment separators as the end of path parameter capture
1086
  // Does not need to handle query parameters as they are parsed before path
1087
  // matching
1088
  static constexpr char separator = '/';
1089
1090
  // Contains static path fragments to match against, excluding the '/' after
1091
  // path params
1092
  // Fragments are separated by path params
1093
  std::vector<std::string> static_fragments_;
1094
  // Stores the names of the path parameters to be used as keys in the
1095
  // Request::path_params map
1096
  std::vector<std::string> param_names_;
1097
};
1098
1099
/**
1100
 * Performs std::regex_match on request path
1101
 * and stores the result in Request::matches
1102
 *
1103
 * Note that regex match is performed directly on the whole request.
1104
 * This means that wildcard patterns may match multiple path segments with /:
1105
 * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".
1106
 */
1107
class RegexMatcher final : public MatcherBase {
1108
public:
1109
  RegexMatcher(const std::string &pattern)
1110
12
      : MatcherBase(pattern), regex_(pattern) {}
1111
1112
  bool match(Request &request) const override;
1113
1114
private:
1115
  std::regex regex_;
1116
};
1117
1118
int close_socket(socket_t sock);
1119
1120
ssize_t write_headers(Stream &strm, const Headers &headers);
1121
1122
} // namespace detail
1123
1124
class Server {
1125
public:
1126
  using Handler = std::function<void(const Request &, Response &)>;
1127
1128
  using ExceptionHandler =
1129
      std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1130
1131
  enum class HandlerResponse {
1132
    Handled,
1133
    Unhandled,
1134
  };
1135
  using HandlerWithResponse =
1136
      std::function<HandlerResponse(const Request &, Response &)>;
1137
1138
  using HandlerWithContentReader = std::function<void(
1139
      const Request &, Response &, const ContentReader &content_reader)>;
1140
1141
  using Expect100ContinueHandler =
1142
      std::function<int(const Request &, Response &)>;
1143
1144
  Server();
1145
1146
  virtual ~Server();
1147
1148
  virtual bool is_valid() const;
1149
1150
  Server &Get(const std::string &pattern, Handler handler);
1151
  Server &Post(const std::string &pattern, Handler handler);
1152
  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1153
  Server &Put(const std::string &pattern, Handler handler);
1154
  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1155
  Server &Patch(const std::string &pattern, Handler handler);
1156
  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1157
  Server &Delete(const std::string &pattern, Handler handler);
1158
  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1159
  Server &Options(const std::string &pattern, Handler handler);
1160
1161
  bool set_base_dir(const std::string &dir,
1162
                    const std::string &mount_point = std::string());
1163
  bool set_mount_point(const std::string &mount_point, const std::string &dir,
1164
                       Headers headers = Headers());
1165
  bool remove_mount_point(const std::string &mount_point);
1166
  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1167
                                                  const std::string &mime);
1168
  Server &set_default_file_mimetype(const std::string &mime);
1169
  Server &set_file_request_handler(Handler handler);
1170
1171
  template <class ErrorHandlerFunc>
1172
  Server &set_error_handler(ErrorHandlerFunc &&handler) {
1173
    return set_error_handler_core(
1174
        std::forward<ErrorHandlerFunc>(handler),
1175
        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1176
  }
1177
1178
  Server &set_exception_handler(ExceptionHandler handler);
1179
1180
  Server &set_pre_routing_handler(HandlerWithResponse handler);
1181
  Server &set_post_routing_handler(Handler handler);
1182
1183
  Server &set_pre_request_handler(HandlerWithResponse handler);
1184
1185
  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1186
  Server &set_logger(Logger logger);
1187
  Server &set_pre_compression_logger(Logger logger);
1188
  Server &set_error_logger(ErrorLogger error_logger);
1189
1190
  Server &set_address_family(int family);
1191
  Server &set_tcp_nodelay(bool on);
1192
  Server &set_ipv6_v6only(bool on);
1193
  Server &set_socket_options(SocketOptions socket_options);
1194
1195
  Server &set_default_headers(Headers headers);
1196
  Server &
1197
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1198
1199
  Server &set_trusted_proxies(const std::vector<std::string> &proxies);
1200
1201
  Server &set_keep_alive_max_count(size_t count);
1202
  Server &set_keep_alive_timeout(time_t sec);
1203
1204
  Server &set_read_timeout(time_t sec, time_t usec = 0);
1205
  template <class Rep, class Period>
1206
  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1207
1208
  Server &set_write_timeout(time_t sec, time_t usec = 0);
1209
  template <class Rep, class Period>
1210
  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1211
1212
  Server &set_idle_interval(time_t sec, time_t usec = 0);
1213
  template <class Rep, class Period>
1214
  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1215
1216
  Server &set_payload_max_length(size_t length);
1217
1218
  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1219
  int bind_to_any_port(const std::string &host, int socket_flags = 0);
1220
  bool listen_after_bind();
1221
1222
  bool listen(const std::string &host, int port, int socket_flags = 0);
1223
1224
  bool is_running() const;
1225
  void wait_until_ready() const;
1226
  void stop();
1227
  void decommission();
1228
1229
  std::function<TaskQueue *(void)> new_task_queue;
1230
1231
protected:
1232
  bool process_request(Stream &strm, const std::string &remote_addr,
1233
                       int remote_port, const std::string &local_addr,
1234
                       int local_port, bool close_connection,
1235
                       bool &connection_closed,
1236
                       const std::function<void(Request &)> &setup_request);
1237
1238
  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1239
1240
  std::vector<std::string> trusted_proxies_;
1241
1242
  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
1243
  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
1244
  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1245
  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1246
  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1247
  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
1248
  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
1249
  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
1250
  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1251
1252
private:
1253
  using Handlers =
1254
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1255
  using HandlersForContentReader =
1256
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1257
                            HandlerWithContentReader>>;
1258
1259
  static std::unique_ptr<detail::MatcherBase>
1260
  make_matcher(const std::string &pattern);
1261
1262
  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1263
  Server &set_error_handler_core(Handler handler, std::false_type);
1264
1265
  socket_t create_server_socket(const std::string &host, int port,
1266
                                int socket_flags,
1267
                                SocketOptions socket_options) const;
1268
  int bind_internal(const std::string &host, int port, int socket_flags);
1269
  bool listen_internal();
1270
1271
  bool routing(Request &req, Response &res, Stream &strm);
1272
  bool handle_file_request(Request &req, Response &res);
1273
  bool check_if_not_modified(const Request &req, Response &res,
1274
                             const std::string &etag, time_t mtime) const;
1275
  bool check_if_range(Request &req, const std::string &etag,
1276
                      time_t mtime) const;
1277
  bool dispatch_request(Request &req, Response &res,
1278
                        const Handlers &handlers) const;
1279
  bool dispatch_request_for_content_reader(
1280
      Request &req, Response &res, ContentReader content_reader,
1281
      const HandlersForContentReader &handlers) const;
1282
1283
  bool parse_request_line(const char *s, Request &req) const;
1284
  void apply_ranges(const Request &req, Response &res,
1285
                    std::string &content_type, std::string &boundary) const;
1286
  bool write_response(Stream &strm, bool close_connection, Request &req,
1287
                      Response &res);
1288
  bool write_response_with_content(Stream &strm, bool close_connection,
1289
                                   const Request &req, Response &res);
1290
  bool write_response_core(Stream &strm, bool close_connection,
1291
                           const Request &req, Response &res,
1292
                           bool need_apply_ranges);
1293
  bool write_content_with_provider(Stream &strm, const Request &req,
1294
                                   Response &res, const std::string &boundary,
1295
                                   const std::string &content_type);
1296
  bool read_content(Stream &strm, Request &req, Response &res);
1297
  bool read_content_with_content_receiver(Stream &strm, Request &req,
1298
                                          Response &res,
1299
                                          ContentReceiver receiver,
1300
                                          FormDataHeader multipart_header,
1301
                                          ContentReceiver multipart_receiver);
1302
  bool read_content_core(Stream &strm, Request &req, Response &res,
1303
                         ContentReceiver receiver,
1304
                         FormDataHeader multipart_header,
1305
                         ContentReceiver multipart_receiver) const;
1306
1307
  virtual bool process_and_close_socket(socket_t sock);
1308
1309
  void output_log(const Request &req, const Response &res) const;
1310
  void output_pre_compression_log(const Request &req,
1311
                                  const Response &res) const;
1312
  void output_error_log(const Error &err, const Request *req) const;
1313
1314
  std::atomic<bool> is_running_{false};
1315
  std::atomic<bool> is_decommissioned{false};
1316
1317
  struct MountPointEntry {
1318
    std::string mount_point;
1319
    std::string base_dir;
1320
    Headers headers;
1321
  };
1322
  std::vector<MountPointEntry> base_dirs_;
1323
  std::map<std::string, std::string> file_extension_and_mimetype_map_;
1324
  std::string default_file_mimetype_ = "application/octet-stream";
1325
  Handler file_request_handler_;
1326
1327
  Handlers get_handlers_;
1328
  Handlers post_handlers_;
1329
  HandlersForContentReader post_handlers_for_content_reader_;
1330
  Handlers put_handlers_;
1331
  HandlersForContentReader put_handlers_for_content_reader_;
1332
  Handlers patch_handlers_;
1333
  HandlersForContentReader patch_handlers_for_content_reader_;
1334
  Handlers delete_handlers_;
1335
  HandlersForContentReader delete_handlers_for_content_reader_;
1336
  Handlers options_handlers_;
1337
1338
  HandlerWithResponse error_handler_;
1339
  ExceptionHandler exception_handler_;
1340
  HandlerWithResponse pre_routing_handler_;
1341
  Handler post_routing_handler_;
1342
  HandlerWithResponse pre_request_handler_;
1343
  Expect100ContinueHandler expect_100_continue_handler_;
1344
1345
  mutable std::mutex logger_mutex_;
1346
  Logger logger_;
1347
  Logger pre_compression_logger_;
1348
  ErrorLogger error_logger_;
1349
1350
  int address_family_ = AF_UNSPEC;
1351
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1352
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1353
  SocketOptions socket_options_ = default_socket_options;
1354
1355
  Headers default_headers_;
1356
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1357
      detail::write_headers;
1358
};
1359
1360
class Result {
1361
public:
1362
  Result() = default;
1363
  Result(std::unique_ptr<Response> &&res, Error err,
1364
         Headers &&request_headers = Headers{})
1365
      : res_(std::move(res)), err_(err),
1366
0
        request_headers_(std::move(request_headers)) {}
1367
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1368
  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1369
         int ssl_error)
1370
      : res_(std::move(res)), err_(err),
1371
        request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
1372
  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1373
         int ssl_error, unsigned long ssl_openssl_error)
1374
      : res_(std::move(res)), err_(err),
1375
        request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1376
        ssl_openssl_error_(ssl_openssl_error) {}
1377
#endif
1378
  // Response
1379
0
  operator bool() const { return res_ != nullptr; }
1380
0
  bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1381
0
  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1382
0
  const Response &value() const { return *res_; }
1383
0
  Response &value() { return *res_; }
1384
0
  const Response &operator*() const { return *res_; }
1385
0
  Response &operator*() { return *res_; }
1386
0
  const Response *operator->() const { return res_.get(); }
1387
0
  Response *operator->() { return res_.get(); }
1388
1389
  // Error
1390
0
  Error error() const { return err_; }
1391
1392
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1393
  // SSL Error
1394
  int ssl_error() const { return ssl_error_; }
1395
  // OpenSSL Error
1396
  unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }
1397
#endif
1398
1399
  // Request Headers
1400
  bool has_request_header(const std::string &key) const;
1401
  std::string get_request_header_value(const std::string &key,
1402
                                       const char *def = "",
1403
                                       size_t id = 0) const;
1404
  size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
1405
                                      size_t id = 0) const;
1406
  size_t get_request_header_value_count(const std::string &key) const;
1407
1408
private:
1409
  std::unique_ptr<Response> res_;
1410
  Error err_ = Error::Unknown;
1411
  Headers request_headers_;
1412
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1413
  int ssl_error_ = 0;
1414
  unsigned long ssl_openssl_error_ = 0;
1415
#endif
1416
};
1417
1418
struct ClientConnection {
1419
  socket_t sock = INVALID_SOCKET;
1420
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1421
  SSL *ssl = nullptr;
1422
#endif
1423
1424
0
  bool is_open() const { return sock != INVALID_SOCKET; }
1425
1426
  ClientConnection() = default;
1427
1428
0
  ~ClientConnection() {
1429
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1430
0
    if (ssl) {
1431
0
      SSL_free(ssl);
1432
0
      ssl = nullptr;
1433
0
    }
1434
0
#endif
1435
0
    if (sock != INVALID_SOCKET) {
1436
0
      detail::close_socket(sock);
1437
0
      sock = INVALID_SOCKET;
1438
0
    }
1439
0
  }
1440
1441
  ClientConnection(const ClientConnection &) = delete;
1442
  ClientConnection &operator=(const ClientConnection &) = delete;
1443
1444
  ClientConnection(ClientConnection &&other) noexcept
1445
      : sock(other.sock)
1446
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1447
        ,
1448
        ssl(other.ssl)
1449
#endif
1450
0
  {
1451
0
    other.sock = INVALID_SOCKET;
1452
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1453
0
    other.ssl = nullptr;
1454
0
#endif
1455
0
  }
1456
1457
0
  ClientConnection &operator=(ClientConnection &&other) noexcept {
1458
0
    if (this != &other) {
1459
0
      sock = other.sock;
1460
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1461
0
      ssl = other.ssl;
1462
0
#endif
1463
0
      other.sock = INVALID_SOCKET;
1464
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1465
0
      other.ssl = nullptr;
1466
0
#endif
1467
0
    }
1468
0
    return *this;
1469
0
  }
1470
};
1471
1472
namespace detail {
1473
1474
struct ChunkedDecoder;
1475
1476
struct BodyReader {
1477
  Stream *stream = nullptr;
1478
  size_t content_length = 0;
1479
  size_t bytes_read = 0;
1480
  bool chunked = false;
1481
  bool eof = false;
1482
  std::unique_ptr<ChunkedDecoder> chunked_decoder;
1483
  Error last_error = Error::Success;
1484
1485
  ssize_t read(char *buf, size_t len);
1486
0
  bool has_error() const { return last_error != Error::Success; }
1487
};
1488
1489
inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
1490
3.84k
                                 size_t len) {
1491
3.84k
  (void)stream;
1492
3.84k
  return br.read(buf, len);
1493
3.84k
}
1494
1495
class decompressor;
1496
1497
} // namespace detail
1498
1499
class ClientImpl {
1500
public:
1501
  explicit ClientImpl(const std::string &host);
1502
1503
  explicit ClientImpl(const std::string &host, int port);
1504
1505
  explicit ClientImpl(const std::string &host, int port,
1506
                      const std::string &client_cert_path,
1507
                      const std::string &client_key_path);
1508
1509
  virtual ~ClientImpl();
1510
1511
  virtual bool is_valid() const;
1512
1513
  struct StreamHandle {
1514
    std::unique_ptr<Response> response;
1515
    Error error = Error::Success;
1516
1517
    StreamHandle() = default;
1518
    StreamHandle(const StreamHandle &) = delete;
1519
    StreamHandle &operator=(const StreamHandle &) = delete;
1520
    StreamHandle(StreamHandle &&) = default;
1521
    StreamHandle &operator=(StreamHandle &&) = default;
1522
    ~StreamHandle() = default;
1523
1524
0
    bool is_valid() const {
1525
0
      return response != nullptr && error == Error::Success;
1526
0
    }
1527
1528
    ssize_t read(char *buf, size_t len);
1529
    void parse_trailers_if_needed();
1530
0
    Error get_read_error() const { return body_reader_.last_error; }
1531
0
    bool has_read_error() const { return body_reader_.has_error(); }
1532
1533
    bool trailers_parsed_ = false;
1534
1535
  private:
1536
    friend class ClientImpl;
1537
1538
    ssize_t read_with_decompression(char *buf, size_t len);
1539
1540
    std::unique_ptr<ClientConnection> connection_;
1541
    std::unique_ptr<Stream> socket_stream_;
1542
    Stream *stream_ = nullptr;
1543
    detail::BodyReader body_reader_;
1544
1545
    std::unique_ptr<detail::decompressor> decompressor_;
1546
    std::string decompress_buffer_;
1547
    size_t decompress_offset_ = 0;
1548
  };
1549
1550
  // clang-format off
1551
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1552
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1553
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1554
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1555
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1556
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1557
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1558
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1559
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1560
1561
  Result Head(const std::string &path);
1562
  Result Head(const std::string &path, const Headers &headers);
1563
1564
  Result Post(const std::string &path);
1565
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1566
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1567
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1568
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1569
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1570
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1571
  Result Post(const std::string &path, const Params &params);
1572
  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1573
  Result Post(const std::string &path, const Headers &headers);
1574
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1575
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1576
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1577
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1578
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1579
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1580
  Result Post(const std::string &path, const Headers &headers, const Params &params);
1581
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1582
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1583
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1584
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1585
1586
  Result Put(const std::string &path);
1587
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1588
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1589
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1590
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1591
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1592
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1593
  Result Put(const std::string &path, const Params &params);
1594
  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1595
  Result Put(const std::string &path, const Headers &headers);
1596
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1597
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1598
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1599
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1600
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1601
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1602
  Result Put(const std::string &path, const Headers &headers, const Params &params);
1603
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1604
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1605
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1606
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1607
1608
  Result Patch(const std::string &path);
1609
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1610
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1611
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1612
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1613
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1614
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1615
  Result Patch(const std::string &path, const Params &params);
1616
  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1617
  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
1618
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1619
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1620
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1621
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1622
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1623
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1624
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
1625
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1626
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1627
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1628
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1629
1630
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1631
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1632
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1633
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1634
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1635
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1636
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1637
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1638
1639
  Result Options(const std::string &path);
1640
  Result Options(const std::string &path, const Headers &headers);
1641
  // clang-format on
1642
1643
  // Streaming API: Open a stream for reading response body incrementally
1644
  // Socket ownership is transferred to StreamHandle for true streaming
1645
  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
1646
  StreamHandle open_stream(const std::string &method, const std::string &path,
1647
                           const Params &params = {},
1648
                           const Headers &headers = {},
1649
                           const std::string &body = {},
1650
                           const std::string &content_type = {});
1651
1652
  bool send(Request &req, Response &res, Error &error);
1653
  Result send(const Request &req);
1654
1655
  void stop();
1656
1657
  std::string host() const;
1658
  int port() const;
1659
1660
  size_t is_socket_open() const;
1661
  socket_t socket() const;
1662
1663
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1664
1665
  void set_default_headers(Headers headers);
1666
1667
  void
1668
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1669
1670
  void set_address_family(int family);
1671
  void set_tcp_nodelay(bool on);
1672
  void set_ipv6_v6only(bool on);
1673
  void set_socket_options(SocketOptions socket_options);
1674
1675
  void set_connection_timeout(time_t sec, time_t usec = 0);
1676
  template <class Rep, class Period>
1677
  void
1678
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1679
1680
  void set_read_timeout(time_t sec, time_t usec = 0);
1681
  template <class Rep, class Period>
1682
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1683
1684
  void set_write_timeout(time_t sec, time_t usec = 0);
1685
  template <class Rep, class Period>
1686
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1687
1688
  void set_max_timeout(time_t msec);
1689
  template <class Rep, class Period>
1690
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1691
1692
  void set_basic_auth(const std::string &username, const std::string &password);
1693
  void set_bearer_token_auth(const std::string &token);
1694
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1695
  void set_digest_auth(const std::string &username,
1696
                       const std::string &password);
1697
#endif
1698
1699
  void set_keep_alive(bool on);
1700
  void set_follow_location(bool on);
1701
1702
  void set_path_encode(bool on);
1703
1704
  void set_compress(bool on);
1705
1706
  void set_decompress(bool on);
1707
1708
  void set_interface(const std::string &intf);
1709
1710
  void set_proxy(const std::string &host, int port);
1711
  void set_proxy_basic_auth(const std::string &username,
1712
                            const std::string &password);
1713
  void set_proxy_bearer_token_auth(const std::string &token);
1714
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1715
  void set_proxy_digest_auth(const std::string &username,
1716
                             const std::string &password);
1717
#endif
1718
1719
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1720
  void set_ca_cert_path(const std::string &ca_cert_file_path,
1721
                        const std::string &ca_cert_dir_path = std::string());
1722
  void set_ca_cert_store(X509_STORE *ca_cert_store);
1723
  X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1724
#endif
1725
1726
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1727
  void enable_server_certificate_verification(bool enabled);
1728
  void enable_server_hostname_verification(bool enabled);
1729
  void set_server_certificate_verifier(
1730
      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1731
#endif
1732
1733
  void set_logger(Logger logger);
1734
  void set_error_logger(ErrorLogger error_logger);
1735
1736
protected:
1737
  struct Socket {
1738
    socket_t sock = INVALID_SOCKET;
1739
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1740
    SSL *ssl = nullptr;
1741
#endif
1742
1743
0
    bool is_open() const { return sock != INVALID_SOCKET; }
1744
  };
1745
1746
  virtual bool create_and_connect_socket(Socket &socket, Error &error);
1747
  virtual bool ensure_socket_connection(Socket &socket, Error &error);
1748
1749
  // All of:
1750
  //   shutdown_ssl
1751
  //   shutdown_socket
1752
  //   close_socket
1753
  // should ONLY be called when socket_mutex_ is locked.
1754
  // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1755
  // with a DIFFERENT thread sending requests using that socket.
1756
  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1757
  void shutdown_socket(Socket &socket) const;
1758
  void close_socket(Socket &socket);
1759
1760
  bool process_request(Stream &strm, Request &req, Response &res,
1761
                       bool close_connection, Error &error);
1762
1763
  bool write_content_with_provider(Stream &strm, const Request &req,
1764
                                   Error &error) const;
1765
1766
  void copy_settings(const ClientImpl &rhs);
1767
1768
  void output_log(const Request &req, const Response &res) const;
1769
  void output_error_log(const Error &err, const Request *req) const;
1770
1771
  // Socket endpoint information
1772
  const std::string host_;
1773
  const int port_;
1774
1775
  // Current open socket
1776
  Socket socket_;
1777
  mutable std::mutex socket_mutex_;
1778
  std::recursive_mutex request_mutex_;
1779
1780
  // These are all protected under socket_mutex
1781
  size_t socket_requests_in_flight_ = 0;
1782
  std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1783
  bool socket_should_be_closed_when_request_is_done_ = false;
1784
1785
  // Hostname-IP map
1786
  std::map<std::string, std::string> addr_map_;
1787
1788
  // Default headers
1789
  Headers default_headers_;
1790
1791
  // Header writer
1792
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1793
      detail::write_headers;
1794
1795
  // Settings
1796
  std::string client_cert_path_;
1797
  std::string client_key_path_;
1798
1799
  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1800
  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1801
  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1802
  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1803
  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1804
  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1805
  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
1806
1807
  std::string basic_auth_username_;
1808
  std::string basic_auth_password_;
1809
  std::string bearer_token_auth_token_;
1810
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1811
  std::string digest_auth_username_;
1812
  std::string digest_auth_password_;
1813
#endif
1814
1815
  bool keep_alive_ = false;
1816
  bool follow_location_ = false;
1817
1818
  bool path_encode_ = true;
1819
1820
  int address_family_ = AF_UNSPEC;
1821
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1822
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1823
  SocketOptions socket_options_ = nullptr;
1824
1825
  bool compress_ = false;
1826
  bool decompress_ = true;
1827
1828
  std::string interface_;
1829
1830
  std::string proxy_host_;
1831
  int proxy_port_ = -1;
1832
1833
  std::string proxy_basic_auth_username_;
1834
  std::string proxy_basic_auth_password_;
1835
  std::string proxy_bearer_token_auth_token_;
1836
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1837
  std::string proxy_digest_auth_username_;
1838
  std::string proxy_digest_auth_password_;
1839
#endif
1840
1841
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1842
  std::string ca_cert_file_path_;
1843
  std::string ca_cert_dir_path_;
1844
1845
  X509_STORE *ca_cert_store_ = nullptr;
1846
#endif
1847
1848
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1849
  bool server_certificate_verification_ = true;
1850
  bool server_hostname_verification_ = true;
1851
  std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
1852
#endif
1853
1854
  mutable std::mutex logger_mutex_;
1855
  Logger logger_;
1856
  ErrorLogger error_logger_;
1857
1858
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1859
  int last_ssl_error_ = 0;
1860
  unsigned long last_openssl_error_ = 0;
1861
#endif
1862
1863
private:
1864
  bool send_(Request &req, Response &res, Error &error);
1865
  Result send_(Request &&req);
1866
1867
  socket_t create_client_socket(Error &error) const;
1868
  bool read_response_line(Stream &strm, const Request &req, Response &res,
1869
                          bool skip_100_continue = true) const;
1870
  bool write_request(Stream &strm, Request &req, bool close_connection,
1871
                     Error &error, bool skip_body = false);
1872
  bool write_request_body(Stream &strm, Request &req, Error &error);
1873
  void prepare_default_headers(Request &r, bool for_stream,
1874
                               const std::string &ct);
1875
  bool redirect(Request &req, Response &res, Error &error);
1876
  bool create_redirect_client(const std::string &scheme,
1877
                              const std::string &host, int port, Request &req,
1878
                              Response &res, const std::string &path,
1879
                              const std::string &location, Error &error);
1880
  template <typename ClientType> void setup_redirect_client(ClientType &client);
1881
  bool handle_request(Stream &strm, Request &req, Response &res,
1882
                      bool close_connection, Error &error);
1883
  std::unique_ptr<Response> send_with_content_provider_and_receiver(
1884
      Request &req, const char *body, size_t content_length,
1885
      ContentProvider content_provider,
1886
      ContentProviderWithoutLength content_provider_without_length,
1887
      const std::string &content_type, ContentReceiver content_receiver,
1888
      Error &error);
1889
  Result send_with_content_provider_and_receiver(
1890
      const std::string &method, const std::string &path,
1891
      const Headers &headers, const char *body, size_t content_length,
1892
      ContentProvider content_provider,
1893
      ContentProviderWithoutLength content_provider_without_length,
1894
      const std::string &content_type, ContentReceiver content_receiver,
1895
      UploadProgress progress);
1896
  ContentProviderWithoutLength get_multipart_content_provider(
1897
      const std::string &boundary, const UploadFormDataItems &items,
1898
      const FormDataProviderItems &provider_items) const;
1899
1900
  virtual bool
1901
  process_socket(const Socket &socket,
1902
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
1903
                 std::function<bool(Stream &strm)> callback);
1904
  virtual bool is_ssl() const;
1905
1906
  void transfer_socket_ownership_to_handle(StreamHandle &handle);
1907
};
1908
1909
class Client {
1910
public:
1911
  // Universal interface
1912
  explicit Client(const std::string &scheme_host_port);
1913
1914
  explicit Client(const std::string &scheme_host_port,
1915
                  const std::string &client_cert_path,
1916
                  const std::string &client_key_path);
1917
1918
  // HTTP only interface
1919
  explicit Client(const std::string &host, int port);
1920
1921
  explicit Client(const std::string &host, int port,
1922
                  const std::string &client_cert_path,
1923
                  const std::string &client_key_path);
1924
1925
  Client(Client &&) = default;
1926
  Client &operator=(Client &&) = default;
1927
1928
  ~Client();
1929
1930
  bool is_valid() const;
1931
1932
  // clang-format off
1933
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1934
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1935
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1936
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1937
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1938
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1939
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1940
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1941
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1942
1943
  Result Head(const std::string &path);
1944
  Result Head(const std::string &path, const Headers &headers);
1945
1946
  Result Post(const std::string &path);
1947
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1948
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1949
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1950
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1951
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1952
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1953
  Result Post(const std::string &path, const Params &params);
1954
  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1955
  Result Post(const std::string &path, const Headers &headers);
1956
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1957
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1958
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1959
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1960
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1961
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1962
  Result Post(const std::string &path, const Headers &headers, const Params &params);
1963
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1964
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1965
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1966
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1967
1968
  Result Put(const std::string &path);
1969
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1970
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1971
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1972
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1973
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1974
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1975
  Result Put(const std::string &path, const Params &params);
1976
  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1977
  Result Put(const std::string &path, const Headers &headers);
1978
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1979
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1980
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1981
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1982
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1983
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1984
  Result Put(const std::string &path, const Headers &headers, const Params &params);
1985
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1986
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1987
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1988
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1989
1990
  Result Patch(const std::string &path);
1991
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1992
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1993
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1994
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1995
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1996
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1997
  Result Patch(const std::string &path, const Params &params);
1998
  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1999
  Result Patch(const std::string &path, const Headers &headers);
2000
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2001
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2002
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2003
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2004
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2005
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2006
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
2007
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2008
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2009
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2010
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2011
2012
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
2013
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2014
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2015
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2016
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2017
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2018
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2019
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2020
2021
  Result Options(const std::string &path);
2022
  Result Options(const std::string &path, const Headers &headers);
2023
  // clang-format on
2024
2025
  // Streaming API: Open a stream for reading response body incrementally
2026
  // Socket ownership is transferred to StreamHandle for true streaming
2027
  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
2028
  ClientImpl::StreamHandle open_stream(const std::string &method,
2029
                                       const std::string &path,
2030
                                       const Params &params = {},
2031
                                       const Headers &headers = {},
2032
                                       const std::string &body = {},
2033
                                       const std::string &content_type = {});
2034
2035
  bool send(Request &req, Response &res, Error &error);
2036
  Result send(const Request &req);
2037
2038
  void stop();
2039
2040
  std::string host() const;
2041
  int port() const;
2042
2043
  size_t is_socket_open() const;
2044
  socket_t socket() const;
2045
2046
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2047
2048
  void set_default_headers(Headers headers);
2049
2050
  void
2051
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2052
2053
  void set_address_family(int family);
2054
  void set_tcp_nodelay(bool on);
2055
  void set_socket_options(SocketOptions socket_options);
2056
2057
  void set_connection_timeout(time_t sec, time_t usec = 0);
2058
  template <class Rep, class Period>
2059
  void
2060
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2061
2062
  void set_read_timeout(time_t sec, time_t usec = 0);
2063
  template <class Rep, class Period>
2064
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2065
2066
  void set_write_timeout(time_t sec, time_t usec = 0);
2067
  template <class Rep, class Period>
2068
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2069
2070
  void set_max_timeout(time_t msec);
2071
  template <class Rep, class Period>
2072
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2073
2074
  void set_basic_auth(const std::string &username, const std::string &password);
2075
  void set_bearer_token_auth(const std::string &token);
2076
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2077
  void set_digest_auth(const std::string &username,
2078
                       const std::string &password);
2079
#endif
2080
2081
  void set_keep_alive(bool on);
2082
  void set_follow_location(bool on);
2083
2084
  void set_path_encode(bool on);
2085
  void set_url_encode(bool on);
2086
2087
  void set_compress(bool on);
2088
2089
  void set_decompress(bool on);
2090
2091
  void set_interface(const std::string &intf);
2092
2093
  void set_proxy(const std::string &host, int port);
2094
  void set_proxy_basic_auth(const std::string &username,
2095
                            const std::string &password);
2096
  void set_proxy_bearer_token_auth(const std::string &token);
2097
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2098
  void set_proxy_digest_auth(const std::string &username,
2099
                             const std::string &password);
2100
#endif
2101
2102
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2103
  void enable_server_certificate_verification(bool enabled);
2104
  void enable_server_hostname_verification(bool enabled);
2105
  void set_server_certificate_verifier(
2106
      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
2107
#endif
2108
2109
  void set_logger(Logger logger);
2110
  void set_error_logger(ErrorLogger error_logger);
2111
2112
  // SSL
2113
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2114
  void set_ca_cert_path(const std::string &ca_cert_file_path,
2115
                        const std::string &ca_cert_dir_path = std::string());
2116
2117
  void set_ca_cert_store(X509_STORE *ca_cert_store);
2118
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2119
2120
  long get_openssl_verify_result() const;
2121
2122
  SSL_CTX *ssl_context() const;
2123
#endif
2124
2125
private:
2126
  std::unique_ptr<ClientImpl> cli_;
2127
2128
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2129
  bool is_ssl_ = false;
2130
#endif
2131
};
2132
2133
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2134
class SSLServer : public Server {
2135
public:
2136
  SSLServer(const char *cert_path, const char *private_key_path,
2137
            const char *client_ca_cert_file_path = nullptr,
2138
            const char *client_ca_cert_dir_path = nullptr,
2139
            const char *private_key_password = nullptr);
2140
2141
  SSLServer(X509 *cert, EVP_PKEY *private_key,
2142
            X509_STORE *client_ca_cert_store = nullptr);
2143
2144
  SSLServer(
2145
      const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
2146
2147
  ~SSLServer() override;
2148
2149
  bool is_valid() const override;
2150
2151
  SSL_CTX *ssl_context() const;
2152
2153
  void update_certs(X509 *cert, EVP_PKEY *private_key,
2154
                    X509_STORE *client_ca_cert_store = nullptr);
2155
2156
  int ssl_last_error() const { return last_ssl_error_; }
2157
2158
private:
2159
  bool process_and_close_socket(socket_t sock) override;
2160
2161
  STACK_OF(X509_NAME) * extract_ca_names_from_x509_store(X509_STORE *store);
2162
2163
  SSL_CTX *ctx_;
2164
  std::mutex ctx_mutex_;
2165
2166
  int last_ssl_error_ = 0;
2167
};
2168
2169
class SSLClient final : public ClientImpl {
2170
public:
2171
  explicit SSLClient(const std::string &host);
2172
2173
  explicit SSLClient(const std::string &host, int port);
2174
2175
  explicit SSLClient(const std::string &host, int port,
2176
                     const std::string &client_cert_path,
2177
                     const std::string &client_key_path,
2178
                     const std::string &private_key_password = std::string());
2179
2180
  explicit SSLClient(const std::string &host, int port, X509 *client_cert,
2181
                     EVP_PKEY *client_key,
2182
                     const std::string &private_key_password = std::string());
2183
2184
  ~SSLClient() override;
2185
2186
  bool is_valid() const override;
2187
2188
  void set_ca_cert_store(X509_STORE *ca_cert_store);
2189
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2190
2191
  long get_openssl_verify_result() const;
2192
2193
  SSL_CTX *ssl_context() const;
2194
2195
private:
2196
  bool create_and_connect_socket(Socket &socket, Error &error) override;
2197
  bool ensure_socket_connection(Socket &socket, Error &error) override;
2198
  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
2199
  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
2200
2201
  bool
2202
  process_socket(const Socket &socket,
2203
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
2204
                 std::function<bool(Stream &strm)> callback) override;
2205
  bool is_ssl() const override;
2206
2207
  bool connect_with_proxy(
2208
      Socket &sock,
2209
      std::chrono::time_point<std::chrono::steady_clock> start_time,
2210
      Response &res, bool &success, Error &error);
2211
  bool initialize_ssl(Socket &socket, Error &error);
2212
2213
  bool load_certs();
2214
2215
  bool verify_host(X509 *server_cert) const;
2216
  bool verify_host_with_subject_alt_name(X509 *server_cert) const;
2217
  bool verify_host_with_common_name(X509 *server_cert) const;
2218
  bool check_host_name(const char *pattern, size_t pattern_len) const;
2219
2220
  SSL_CTX *ctx_;
2221
  std::mutex ctx_mutex_;
2222
  std::once_flag initialize_cert_;
2223
2224
  std::vector<std::string> host_components_;
2225
2226
  long verify_result_ = 0;
2227
2228
  friend class ClientImpl;
2229
};
2230
#endif
2231
2232
/*
2233
 * Implementation of template methods.
2234
 */
2235
2236
namespace detail {
2237
2238
template <typename T, typename U>
2239
inline void duration_to_sec_and_usec(const T &duration, U callback) {
2240
  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2241
  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2242
                  duration - std::chrono::seconds(sec))
2243
                  .count();
2244
  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2245
}
2246
2247
1.45k
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2248
1.45k
  return N - 1;
2249
1.45k
}
Unexecuted instantiation: unsigned long httplib::detail::str_len<3ul>(char const (&) [3ul])
Unexecuted instantiation: unsigned long httplib::detail::str_len<4ul>(char const (&) [4ul])
unsigned long httplib::detail::str_len<14ul>(char const (&) [14ul])
Line
Count
Source
2247
1.45k
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2248
1.45k
  return N - 1;
2249
1.45k
}
Unexecuted instantiation: unsigned long httplib::detail::str_len<8ul>(char const (&) [8ul])
2250
2251
2.08k
inline bool is_numeric(const std::string &str) {
2252
2.08k
  return !str.empty() &&
2253
2.07k
         std::all_of(str.cbegin(), str.cend(),
2254
15.5k
                     [](unsigned char c) { return std::isdigit(c); });
2255
2.08k
}
2256
2257
inline size_t get_header_value_u64(const Headers &headers,
2258
                                   const std::string &key, size_t def,
2259
2.08k
                                   size_t id, bool &is_invalid_value) {
2260
2.08k
  is_invalid_value = false;
2261
2.08k
  auto rng = headers.equal_range(key);
2262
2.08k
  auto it = rng.first;
2263
2.08k
  std::advance(it, static_cast<ssize_t>(id));
2264
2.08k
  if (it != rng.second) {
2265
2.08k
    if (is_numeric(it->second)) {
2266
2.07k
      return std::strtoull(it->second.data(), nullptr, 10);
2267
2.07k
    } else {
2268
12
      is_invalid_value = true;
2269
12
    }
2270
2.08k
  }
2271
12
  return def;
2272
2.08k
}
2273
2274
inline size_t get_header_value_u64(const Headers &headers,
2275
                                   const std::string &key, size_t def,
2276
475
                                   size_t id) {
2277
475
  auto dummy = false;
2278
475
  return get_header_value_u64(headers, key, def, id, dummy);
2279
475
}
2280
2281
} // namespace detail
2282
2283
inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
2284
475
                                            size_t id) const {
2285
475
  return detail::get_header_value_u64(headers, key, def, id);
2286
475
}
2287
2288
inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
2289
0
                                             size_t id) const {
2290
0
  return detail::get_header_value_u64(headers, key, def, id);
2291
0
}
2292
2293
namespace detail {
2294
2295
inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
2296
0
                                const void *optval, socklen_t optlen) {
2297
0
  return setsockopt(sock, level, optname,
2298
#ifdef _WIN32
2299
                    reinterpret_cast<const char *>(optval),
2300
#else
2301
0
                    optval,
2302
0
#endif
2303
0
                    optlen) == 0;
2304
0
}
2305
2306
0
inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
2307
0
  return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
2308
0
}
2309
2310
inline bool set_socket_opt_time(socket_t sock, int level, int optname,
2311
0
                                time_t sec, time_t usec) {
2312
#ifdef _WIN32
2313
  auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
2314
#else
2315
0
  timeval timeout;
2316
0
  timeout.tv_sec = static_cast<long>(sec);
2317
0
  timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
2318
0
#endif
2319
0
  return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
2320
0
}
2321
2322
} // namespace detail
2323
2324
0
inline void default_socket_options(socket_t sock) {
2325
0
  detail::set_socket_opt(sock, SOL_SOCKET,
2326
0
#ifdef SO_REUSEPORT
2327
0
                         SO_REUSEPORT,
2328
#else
2329
                         SO_REUSEADDR,
2330
#endif
2331
0
                         1);
2332
0
}
2333
2334
0
inline std::string get_bearer_token_auth(const Request &req) {
2335
0
  if (req.has_header("Authorization")) {
2336
0
    constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2337
0
    return req.get_header_value("Authorization")
2338
0
        .substr(bearer_header_prefix_len);
2339
0
  }
2340
0
  return "";
2341
0
}
2342
2343
template <class Rep, class Period>
2344
inline Server &
2345
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2346
  detail::duration_to_sec_and_usec(
2347
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2348
  return *this;
2349
}
2350
2351
template <class Rep, class Period>
2352
inline Server &
2353
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2354
  detail::duration_to_sec_and_usec(
2355
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2356
  return *this;
2357
}
2358
2359
template <class Rep, class Period>
2360
inline Server &
2361
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
2362
  detail::duration_to_sec_and_usec(
2363
      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2364
  return *this;
2365
}
2366
2367
inline size_t Result::get_request_header_value_u64(const std::string &key,
2368
                                                   size_t def,
2369
0
                                                   size_t id) const {
2370
0
  return detail::get_header_value_u64(request_headers_, key, def, id);
2371
0
}
2372
2373
template <class Rep, class Period>
2374
inline void ClientImpl::set_connection_timeout(
2375
    const std::chrono::duration<Rep, Period> &duration) {
2376
  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2377
    set_connection_timeout(sec, usec);
2378
  });
2379
}
2380
2381
template <class Rep, class Period>
2382
inline void ClientImpl::set_read_timeout(
2383
    const std::chrono::duration<Rep, Period> &duration) {
2384
  detail::duration_to_sec_and_usec(
2385
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2386
}
2387
2388
template <class Rep, class Period>
2389
inline void ClientImpl::set_write_timeout(
2390
    const std::chrono::duration<Rep, Period> &duration) {
2391
  detail::duration_to_sec_and_usec(
2392
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2393
}
2394
2395
template <class Rep, class Period>
2396
inline void ClientImpl::set_max_timeout(
2397
    const std::chrono::duration<Rep, Period> &duration) {
2398
  auto msec =
2399
      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2400
  set_max_timeout(msec);
2401
}
2402
2403
template <class Rep, class Period>
2404
inline void Client::set_connection_timeout(
2405
    const std::chrono::duration<Rep, Period> &duration) {
2406
  cli_->set_connection_timeout(duration);
2407
}
2408
2409
template <class Rep, class Period>
2410
inline void
2411
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2412
  cli_->set_read_timeout(duration);
2413
}
2414
2415
template <class Rep, class Period>
2416
inline void
2417
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2418
  cli_->set_write_timeout(duration);
2419
}
2420
2421
0
inline void Client::set_max_timeout(time_t msec) {
2422
0
  cli_->set_max_timeout(msec);
2423
0
}
2424
2425
template <class Rep, class Period>
2426
inline void
2427
Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2428
  cli_->set_max_timeout(duration);
2429
}
2430
2431
/*
2432
 * Forward declarations and types that will be part of the .h file if split into
2433
 * .h + .cc.
2434
 */
2435
2436
std::string hosted_at(const std::string &hostname);
2437
2438
void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2439
2440
// JavaScript-style URL encoding/decoding functions
2441
std::string encode_uri_component(const std::string &value);
2442
std::string encode_uri(const std::string &value);
2443
std::string decode_uri_component(const std::string &value);
2444
std::string decode_uri(const std::string &value);
2445
2446
// RFC 3986 compliant URL component encoding/decoding functions
2447
std::string encode_path_component(const std::string &component);
2448
std::string decode_path_component(const std::string &component);
2449
std::string encode_query_component(const std::string &component,
2450
                                   bool space_as_plus = true);
2451
std::string decode_query_component(const std::string &component,
2452
                                   bool plus_as_space = true);
2453
2454
std::string append_query_params(const std::string &path, const Params &params);
2455
2456
std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2457
2458
std::pair<std::string, std::string>
2459
make_basic_authentication_header(const std::string &username,
2460
                                 const std::string &password,
2461
                                 bool is_proxy = false);
2462
2463
namespace detail {
2464
2465
#if defined(_WIN32)
2466
inline std::wstring u8string_to_wstring(const char *s) {
2467
  if (!s) { return std::wstring(); }
2468
2469
  auto len = static_cast<int>(strlen(s));
2470
  if (!len) { return std::wstring(); }
2471
2472
  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2473
  if (!wlen) { return std::wstring(); }
2474
2475
  std::wstring ws;
2476
  ws.resize(wlen);
2477
  wlen = ::MultiByteToWideChar(
2478
      CP_UTF8, 0, s, len,
2479
      const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2480
  if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
2481
  return ws;
2482
}
2483
#endif
2484
2485
struct FileStat {
2486
  FileStat(const std::string &path);
2487
  bool is_file() const;
2488
  bool is_dir() const;
2489
  time_t mtime() const;
2490
  size_t size() const;
2491
2492
private:
2493
#if defined(_WIN32)
2494
  struct _stat st_;
2495
#else
2496
  struct stat st_;
2497
#endif
2498
  int ret_ = -1;
2499
};
2500
2501
std::string make_host_and_port_string(const std::string &host, int port,
2502
                                      bool is_ssl);
2503
2504
std::string trim_copy(const std::string &s);
2505
2506
void divide(
2507
    const char *data, std::size_t size, char d,
2508
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2509
        fn);
2510
2511
void divide(
2512
    const std::string &str, char d,
2513
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2514
        fn);
2515
2516
void split(const char *b, const char *e, char d,
2517
           std::function<void(const char *, const char *)> fn);
2518
2519
void split(const char *b, const char *e, char d, size_t m,
2520
           std::function<void(const char *, const char *)> fn);
2521
2522
bool process_client_socket(
2523
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2524
    time_t write_timeout_sec, time_t write_timeout_usec,
2525
    time_t max_timeout_msec,
2526
    std::chrono::time_point<std::chrono::steady_clock> start_time,
2527
    std::function<bool(Stream &)> callback);
2528
2529
socket_t create_client_socket(const std::string &host, const std::string &ip,
2530
                              int port, int address_family, bool tcp_nodelay,
2531
                              bool ipv6_v6only, SocketOptions socket_options,
2532
                              time_t connection_timeout_sec,
2533
                              time_t connection_timeout_usec,
2534
                              time_t read_timeout_sec, time_t read_timeout_usec,
2535
                              time_t write_timeout_sec,
2536
                              time_t write_timeout_usec,
2537
                              const std::string &intf, Error &error);
2538
2539
const char *get_header_value(const Headers &headers, const std::string &key,
2540
                             const char *def, size_t id);
2541
2542
std::string params_to_query_str(const Params &params);
2543
2544
void parse_query_text(const char *data, std::size_t size, Params &params);
2545
2546
void parse_query_text(const std::string &s, Params &params);
2547
2548
bool parse_multipart_boundary(const std::string &content_type,
2549
                              std::string &boundary);
2550
2551
bool parse_range_header(const std::string &s, Ranges &ranges);
2552
2553
bool parse_accept_header(const std::string &s,
2554
                         std::vector<std::string> &content_types);
2555
2556
int close_socket(socket_t sock);
2557
2558
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2559
2560
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2561
2562
enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2563
2564
EncodingType encoding_type(const Request &req, const Response &res);
2565
2566
class BufferStream final : public Stream {
2567
public:
2568
6.53k
  BufferStream() = default;
2569
6.53k
  ~BufferStream() override = default;
2570
2571
  bool is_readable() const override;
2572
  bool wait_readable() const override;
2573
  bool wait_writable() const override;
2574
  ssize_t read(char *ptr, size_t size) override;
2575
  ssize_t write(const char *ptr, size_t size) override;
2576
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
2577
  void get_local_ip_and_port(std::string &ip, int &port) const override;
2578
  socket_t socket() const override;
2579
  time_t duration() const override;
2580
2581
  const std::string &get_buffer() const;
2582
2583
private:
2584
  std::string buffer;
2585
  size_t position = 0;
2586
};
2587
2588
class compressor {
2589
public:
2590
18
  virtual ~compressor() = default;
2591
2592
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2593
  virtual bool compress(const char *data, size_t data_length, bool last,
2594
                        Callback callback) = 0;
2595
};
2596
2597
class decompressor {
2598
public:
2599
672
  virtual ~decompressor() = default;
2600
2601
  virtual bool is_valid() const = 0;
2602
2603
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2604
  virtual bool decompress(const char *data, size_t data_length,
2605
                          Callback callback) = 0;
2606
};
2607
2608
class nocompressor final : public compressor {
2609
public:
2610
0
  ~nocompressor() override = default;
2611
2612
  bool compress(const char *data, size_t data_length, bool /*last*/,
2613
                Callback callback) override;
2614
};
2615
2616
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2617
class gzip_compressor final : public compressor {
2618
public:
2619
  gzip_compressor();
2620
  ~gzip_compressor() override;
2621
2622
  bool compress(const char *data, size_t data_length, bool last,
2623
                Callback callback) override;
2624
2625
private:
2626
  bool is_valid_ = false;
2627
  z_stream strm_;
2628
};
2629
2630
class gzip_decompressor final : public decompressor {
2631
public:
2632
  gzip_decompressor();
2633
  ~gzip_decompressor() override;
2634
2635
  bool is_valid() const override;
2636
2637
  bool decompress(const char *data, size_t data_length,
2638
                  Callback callback) override;
2639
2640
private:
2641
  bool is_valid_ = false;
2642
  z_stream strm_;
2643
};
2644
#endif
2645
2646
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2647
class brotli_compressor final : public compressor {
2648
public:
2649
  brotli_compressor();
2650
  ~brotli_compressor();
2651
2652
  bool compress(const char *data, size_t data_length, bool last,
2653
                Callback callback) override;
2654
2655
private:
2656
  BrotliEncoderState *state_ = nullptr;
2657
};
2658
2659
class brotli_decompressor final : public decompressor {
2660
public:
2661
  brotli_decompressor();
2662
  ~brotli_decompressor();
2663
2664
  bool is_valid() const override;
2665
2666
  bool decompress(const char *data, size_t data_length,
2667
                  Callback callback) override;
2668
2669
private:
2670
  BrotliDecoderResult decoder_r;
2671
  BrotliDecoderState *decoder_s = nullptr;
2672
};
2673
#endif
2674
2675
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2676
class zstd_compressor : public compressor {
2677
public:
2678
  zstd_compressor();
2679
  ~zstd_compressor();
2680
2681
  bool compress(const char *data, size_t data_length, bool last,
2682
                Callback callback) override;
2683
2684
private:
2685
  ZSTD_CCtx *ctx_ = nullptr;
2686
};
2687
2688
class zstd_decompressor : public decompressor {
2689
public:
2690
  zstd_decompressor();
2691
  ~zstd_decompressor();
2692
2693
  bool is_valid() const override;
2694
2695
  bool decompress(const char *data, size_t data_length,
2696
                  Callback callback) override;
2697
2698
private:
2699
  ZSTD_DCtx *ctx_ = nullptr;
2700
};
2701
#endif
2702
2703
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2704
// to store data. The call can set memory on stack for performance.
2705
class stream_line_reader {
2706
public:
2707
  stream_line_reader(Stream &strm, char *fixed_buffer,
2708
                     size_t fixed_buffer_size);
2709
  const char *ptr() const;
2710
  size_t size() const;
2711
  bool end_with_crlf() const;
2712
  bool getline();
2713
2714
private:
2715
  void append(char c);
2716
2717
  Stream &strm_;
2718
  char *fixed_buffer_;
2719
  const size_t fixed_buffer_size_;
2720
  size_t fixed_buffer_used_size_ = 0;
2721
  std::string growable_buffer_;
2722
};
2723
2724
bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
2725
                    const Headers &src_headers);
2726
2727
struct ChunkedDecoder {
2728
  Stream &strm;
2729
  size_t chunk_remaining = 0;
2730
  bool finished = false;
2731
  char line_buf[64];
2732
  size_t last_chunk_total = 0;
2733
  size_t last_chunk_offset = 0;
2734
2735
  explicit ChunkedDecoder(Stream &s);
2736
2737
  ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
2738
                       size_t &out_chunk_total);
2739
2740
  bool parse_trailers_into(Headers &dest, const Headers &src_headers);
2741
};
2742
2743
class mmap {
2744
public:
2745
  mmap(const char *path);
2746
  ~mmap();
2747
2748
  bool open(const char *path);
2749
  void close();
2750
2751
  bool is_open() const;
2752
  size_t size() const;
2753
  const char *data() const;
2754
2755
private:
2756
#if defined(_WIN32)
2757
  HANDLE hFile_ = NULL;
2758
  HANDLE hMapping_ = NULL;
2759
#else
2760
  int fd_ = -1;
2761
#endif
2762
  size_t size_ = 0;
2763
  void *addr_ = nullptr;
2764
  bool is_open_empty_file = false;
2765
};
2766
2767
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
2768
namespace fields {
2769
2770
bool is_token_char(char c);
2771
bool is_token(const std::string &s);
2772
bool is_field_name(const std::string &s);
2773
bool is_vchar(char c);
2774
bool is_obs_text(char c);
2775
bool is_field_vchar(char c);
2776
bool is_field_content(const std::string &s);
2777
bool is_field_value(const std::string &s);
2778
2779
} // namespace fields
2780
2781
} // namespace detail
2782
2783
namespace stream {
2784
2785
class Result {
2786
public:
2787
0
  Result() : chunk_size_(8192) {}
2788
2789
  explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192)
2790
0
      : handle_(std::move(handle)), chunk_size_(chunk_size) {}
2791
2792
  Result(Result &&other) noexcept
2793
      : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
2794
        current_size_(other.current_size_), chunk_size_(other.chunk_size_),
2795
0
        finished_(other.finished_) {
2796
0
    other.current_size_ = 0;
2797
0
    other.finished_ = true;
2798
0
  }
2799
2800
0
  Result &operator=(Result &&other) noexcept {
2801
0
    if (this != &other) {
2802
0
      handle_ = std::move(other.handle_);
2803
0
      buffer_ = std::move(other.buffer_);
2804
0
      current_size_ = other.current_size_;
2805
0
      chunk_size_ = other.chunk_size_;
2806
0
      finished_ = other.finished_;
2807
0
      other.current_size_ = 0;
2808
0
      other.finished_ = true;
2809
0
    }
2810
0
    return *this;
2811
0
  }
2812
2813
  Result(const Result &) = delete;
2814
  Result &operator=(const Result &) = delete;
2815
2816
  // Check if the result is valid (connection succeeded and response received)
2817
0
  bool is_valid() const { return handle_.is_valid(); }
2818
0
  explicit operator bool() const { return is_valid(); }
2819
2820
  // Response status code
2821
0
  int status() const {
2822
0
    return handle_.response ? handle_.response->status : -1;
2823
0
  }
2824
2825
  // Response headers
2826
0
  const Headers &headers() const {
2827
0
    static const Headers empty_headers;
2828
0
    return handle_.response ? handle_.response->headers : empty_headers;
2829
0
  }
2830
2831
  std::string get_header_value(const std::string &key,
2832
0
                               const char *def = "") const {
2833
0
    return handle_.response ? handle_.response->get_header_value(key, def)
2834
0
                            : def;
2835
0
  }
2836
2837
0
  bool has_header(const std::string &key) const {
2838
0
    return handle_.response ? handle_.response->has_header(key) : false;
2839
0
  }
2840
2841
  // Error information
2842
0
  Error error() const { return handle_.error; }
2843
0
  Error read_error() const { return handle_.get_read_error(); }
2844
0
  bool has_read_error() const { return handle_.has_read_error(); }
2845
2846
  // Streaming iteration API
2847
  // Call next() to read the next chunk, then access data via data()/size()
2848
  // Returns true if data was read, false when stream is exhausted
2849
0
  bool next() {
2850
0
    if (!handle_.is_valid() || finished_) { return false; }
2851
0
2852
0
    if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }
2853
0
2854
0
    ssize_t n = handle_.read(&buffer_[0], chunk_size_);
2855
0
    if (n > 0) {
2856
0
      current_size_ = static_cast<size_t>(n);
2857
0
      return true;
2858
0
    }
2859
0
2860
0
    current_size_ = 0;
2861
0
    finished_ = true;
2862
0
    return false;
2863
0
  }
2864
2865
  // Pointer to current chunk data (valid after next() returns true)
2866
0
  const char *data() const { return buffer_.data(); }
2867
2868
  // Size of current chunk (valid after next() returns true)
2869
0
  size_t size() const { return current_size_; }
2870
2871
  // Convenience method: read all remaining data into a string
2872
0
  std::string read_all() {
2873
0
    std::string result;
2874
0
    while (next()) {
2875
0
      result.append(data(), size());
2876
0
    }
2877
0
    return result;
2878
0
  }
2879
2880
private:
2881
  ClientImpl::StreamHandle handle_;
2882
  std::string buffer_;
2883
  size_t current_size_ = 0;
2884
  size_t chunk_size_;
2885
  bool finished_ = false;
2886
};
2887
2888
// GET
2889
template <typename ClientType>
2890
inline Result Get(ClientType &cli, const std::string &path,
2891
                  size_t chunk_size = 8192) {
2892
  return Result{cli.open_stream("GET", path), chunk_size};
2893
}
2894
2895
template <typename ClientType>
2896
inline Result Get(ClientType &cli, const std::string &path,
2897
0
                  const Headers &headers, size_t chunk_size = 8192) {
2898
0
  return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
2899
0
}
2900
2901
template <typename ClientType>
2902
inline Result Get(ClientType &cli, const std::string &path,
2903
                  const Params &params, size_t chunk_size = 8192) {
2904
  return Result{cli.open_stream("GET", path, params), chunk_size};
2905
}
2906
2907
template <typename ClientType>
2908
inline Result Get(ClientType &cli, const std::string &path,
2909
                  const Params &params, const Headers &headers,
2910
                  size_t chunk_size = 8192) {
2911
  return Result{cli.open_stream("GET", path, params, headers), chunk_size};
2912
}
2913
2914
// POST
2915
template <typename ClientType>
2916
inline Result Post(ClientType &cli, const std::string &path,
2917
                   const std::string &body, const std::string &content_type,
2918
                   size_t chunk_size = 8192) {
2919
  return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
2920
                chunk_size};
2921
}
2922
2923
template <typename ClientType>
2924
inline Result Post(ClientType &cli, const std::string &path,
2925
                   const Headers &headers, const std::string &body,
2926
                   const std::string &content_type, size_t chunk_size = 8192) {
2927
  return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
2928
                chunk_size};
2929
}
2930
2931
template <typename ClientType>
2932
inline Result Post(ClientType &cli, const std::string &path,
2933
                   const Params &params, const std::string &body,
2934
                   const std::string &content_type, size_t chunk_size = 8192) {
2935
  return Result{cli.open_stream("POST", path, params, {}, body, content_type),
2936
                chunk_size};
2937
}
2938
2939
template <typename ClientType>
2940
inline Result Post(ClientType &cli, const std::string &path,
2941
                   const Params &params, const Headers &headers,
2942
                   const std::string &body, const std::string &content_type,
2943
                   size_t chunk_size = 8192) {
2944
  return Result{
2945
      cli.open_stream("POST", path, params, headers, body, content_type),
2946
      chunk_size};
2947
}
2948
2949
// PUT
2950
template <typename ClientType>
2951
inline Result Put(ClientType &cli, const std::string &path,
2952
                  const std::string &body, const std::string &content_type,
2953
                  size_t chunk_size = 8192) {
2954
  return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
2955
                chunk_size};
2956
}
2957
2958
template <typename ClientType>
2959
inline Result Put(ClientType &cli, const std::string &path,
2960
                  const Headers &headers, const std::string &body,
2961
                  const std::string &content_type, size_t chunk_size = 8192) {
2962
  return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
2963
                chunk_size};
2964
}
2965
2966
template <typename ClientType>
2967
inline Result Put(ClientType &cli, const std::string &path,
2968
                  const Params &params, const std::string &body,
2969
                  const std::string &content_type, size_t chunk_size = 8192) {
2970
  return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
2971
                chunk_size};
2972
}
2973
2974
template <typename ClientType>
2975
inline Result Put(ClientType &cli, const std::string &path,
2976
                  const Params &params, const Headers &headers,
2977
                  const std::string &body, const std::string &content_type,
2978
                  size_t chunk_size = 8192) {
2979
  return Result{
2980
      cli.open_stream("PUT", path, params, headers, body, content_type),
2981
      chunk_size};
2982
}
2983
2984
// PATCH
2985
template <typename ClientType>
2986
inline Result Patch(ClientType &cli, const std::string &path,
2987
                    const std::string &body, const std::string &content_type,
2988
                    size_t chunk_size = 8192) {
2989
  return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
2990
                chunk_size};
2991
}
2992
2993
template <typename ClientType>
2994
inline Result Patch(ClientType &cli, const std::string &path,
2995
                    const Headers &headers, const std::string &body,
2996
                    const std::string &content_type, size_t chunk_size = 8192) {
2997
  return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
2998
                chunk_size};
2999
}
3000
3001
template <typename ClientType>
3002
inline Result Patch(ClientType &cli, const std::string &path,
3003
                    const Params &params, const std::string &body,
3004
                    const std::string &content_type, size_t chunk_size = 8192) {
3005
  return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
3006
                chunk_size};
3007
}
3008
3009
template <typename ClientType>
3010
inline Result Patch(ClientType &cli, const std::string &path,
3011
                    const Params &params, const Headers &headers,
3012
                    const std::string &body, const std::string &content_type,
3013
                    size_t chunk_size = 8192) {
3014
  return Result{
3015
      cli.open_stream("PATCH", path, params, headers, body, content_type),
3016
      chunk_size};
3017
}
3018
3019
// DELETE
3020
template <typename ClientType>
3021
inline Result Delete(ClientType &cli, const std::string &path,
3022
                     size_t chunk_size = 8192) {
3023
  return Result{cli.open_stream("DELETE", path), chunk_size};
3024
}
3025
3026
template <typename ClientType>
3027
inline Result Delete(ClientType &cli, const std::string &path,
3028
                     const Headers &headers, size_t chunk_size = 8192) {
3029
  return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
3030
}
3031
3032
template <typename ClientType>
3033
inline Result Delete(ClientType &cli, const std::string &path,
3034
                     const std::string &body, const std::string &content_type,
3035
                     size_t chunk_size = 8192) {
3036
  return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
3037
                chunk_size};
3038
}
3039
3040
template <typename ClientType>
3041
inline Result Delete(ClientType &cli, const std::string &path,
3042
                     const Headers &headers, const std::string &body,
3043
                     const std::string &content_type,
3044
                     size_t chunk_size = 8192) {
3045
  return Result{
3046
      cli.open_stream("DELETE", path, {}, headers, body, content_type),
3047
      chunk_size};
3048
}
3049
3050
template <typename ClientType>
3051
inline Result Delete(ClientType &cli, const std::string &path,
3052
                     const Params &params, size_t chunk_size = 8192) {
3053
  return Result{cli.open_stream("DELETE", path, params), chunk_size};
3054
}
3055
3056
template <typename ClientType>
3057
inline Result Delete(ClientType &cli, const std::string &path,
3058
                     const Params &params, const Headers &headers,
3059
                     size_t chunk_size = 8192) {
3060
  return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
3061
}
3062
3063
template <typename ClientType>
3064
inline Result Delete(ClientType &cli, const std::string &path,
3065
                     const Params &params, const std::string &body,
3066
                     const std::string &content_type,
3067
                     size_t chunk_size = 8192) {
3068
  return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
3069
                chunk_size};
3070
}
3071
3072
template <typename ClientType>
3073
inline Result Delete(ClientType &cli, const std::string &path,
3074
                     const Params &params, const Headers &headers,
3075
                     const std::string &body, const std::string &content_type,
3076
                     size_t chunk_size = 8192) {
3077
  return Result{
3078
      cli.open_stream("DELETE", path, params, headers, body, content_type),
3079
      chunk_size};
3080
}
3081
3082
// HEAD
3083
template <typename ClientType>
3084
inline Result Head(ClientType &cli, const std::string &path,
3085
                   size_t chunk_size = 8192) {
3086
  return Result{cli.open_stream("HEAD", path), chunk_size};
3087
}
3088
3089
template <typename ClientType>
3090
inline Result Head(ClientType &cli, const std::string &path,
3091
                   const Headers &headers, size_t chunk_size = 8192) {
3092
  return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
3093
}
3094
3095
template <typename ClientType>
3096
inline Result Head(ClientType &cli, const std::string &path,
3097
                   const Params &params, size_t chunk_size = 8192) {
3098
  return Result{cli.open_stream("HEAD", path, params), chunk_size};
3099
}
3100
3101
template <typename ClientType>
3102
inline Result Head(ClientType &cli, const std::string &path,
3103
                   const Params &params, const Headers &headers,
3104
                   size_t chunk_size = 8192) {
3105
  return Result{cli.open_stream("HEAD", path, params, headers), chunk_size};
3106
}
3107
3108
// OPTIONS
3109
template <typename ClientType>
3110
inline Result Options(ClientType &cli, const std::string &path,
3111
                      size_t chunk_size = 8192) {
3112
  return Result{cli.open_stream("OPTIONS", path), chunk_size};
3113
}
3114
3115
template <typename ClientType>
3116
inline Result Options(ClientType &cli, const std::string &path,
3117
                      const Headers &headers, size_t chunk_size = 8192) {
3118
  return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
3119
}
3120
3121
template <typename ClientType>
3122
inline Result Options(ClientType &cli, const std::string &path,
3123
                      const Params &params, size_t chunk_size = 8192) {
3124
  return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
3125
}
3126
3127
template <typename ClientType>
3128
inline Result Options(ClientType &cli, const std::string &path,
3129
                      const Params &params, const Headers &headers,
3130
                      size_t chunk_size = 8192) {
3131
  return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
3132
}
3133
3134
} // namespace stream
3135
3136
namespace sse {
3137
3138
struct SSEMessage {
3139
  std::string event; // Event type (default: "message")
3140
  std::string data;  // Event payload
3141
  std::string id;    // Event ID for Last-Event-ID header
3142
3143
0
  SSEMessage() : event("message") {}
3144
3145
0
  void clear() {
3146
0
    event = "message";
3147
0
    data.clear();
3148
0
    id.clear();
3149
0
  }
3150
};
3151
3152
class SSEClient {
3153
public:
3154
  using MessageHandler = std::function<void(const SSEMessage &)>;
3155
  using ErrorHandler = std::function<void(Error)>;
3156
  using OpenHandler = std::function<void()>;
3157
3158
  SSEClient(Client &client, const std::string &path)
3159
0
      : client_(client), path_(path) {}
3160
3161
  SSEClient(Client &client, const std::string &path, const Headers &headers)
3162
0
      : client_(client), path_(path), headers_(headers) {}
3163
3164
0
  ~SSEClient() { stop(); }
3165
3166
  SSEClient(const SSEClient &) = delete;
3167
  SSEClient &operator=(const SSEClient &) = delete;
3168
3169
  // Event handlers
3170
0
  SSEClient &on_message(MessageHandler handler) {
3171
0
    on_message_ = std::move(handler);
3172
0
    return *this;
3173
0
  }
3174
3175
0
  SSEClient &on_event(const std::string &type, MessageHandler handler) {
3176
0
    event_handlers_[type] = std::move(handler);
3177
0
    return *this;
3178
0
  }
3179
3180
0
  SSEClient &on_open(OpenHandler handler) {
3181
0
    on_open_ = std::move(handler);
3182
0
    return *this;
3183
0
  }
3184
3185
0
  SSEClient &on_error(ErrorHandler handler) {
3186
0
    on_error_ = std::move(handler);
3187
0
    return *this;
3188
0
  }
3189
3190
0
  SSEClient &set_reconnect_interval(int ms) {
3191
0
    reconnect_interval_ms_ = ms;
3192
0
    return *this;
3193
0
  }
3194
3195
0
  SSEClient &set_max_reconnect_attempts(int n) {
3196
0
    max_reconnect_attempts_ = n;
3197
0
    return *this;
3198
0
  }
3199
3200
  // State accessors
3201
0
  bool is_connected() const { return connected_.load(); }
3202
0
  const std::string &last_event_id() const { return last_event_id_; }
3203
3204
  // Blocking start - runs event loop with auto-reconnect
3205
0
  void start() {
3206
0
    running_.store(true);
3207
0
    run_event_loop();
3208
0
  }
3209
3210
  // Non-blocking start - runs in background thread
3211
0
  void start_async() {
3212
0
    running_.store(true);
3213
0
    async_thread_ = std::thread([this]() { run_event_loop(); });
3214
0
  }
3215
3216
  // Stop the client (thread-safe)
3217
0
  void stop() {
3218
0
    running_.store(false);
3219
0
    client_.stop(); // Cancel any pending operations
3220
0
    if (async_thread_.joinable()) { async_thread_.join(); }
3221
0
  }
3222
3223
private:
3224
  // Parse a single SSE field line
3225
  // Returns true if this line ends an event (blank line)
3226
0
  bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms) {
3227
0
    // Blank line signals end of event
3228
0
    if (line.empty() || line == "\r") { return true; }
3229
0
3230
0
    // Lines starting with ':' are comments (ignored)
3231
0
    if (!line.empty() && line[0] == ':') { return false; }
3232
0
3233
0
    // Find the colon separator
3234
0
    auto colon_pos = line.find(':');
3235
0
    if (colon_pos == std::string::npos) {
3236
0
      // Line with no colon is treated as field name with empty value
3237
0
      return false;
3238
0
    }
3239
0
3240
0
    auto field = line.substr(0, colon_pos);
3241
0
    std::string value;
3242
0
3243
0
    // Value starts after colon, skip optional single space
3244
0
    if (colon_pos + 1 < line.size()) {
3245
0
      auto value_start = colon_pos + 1;
3246
0
      if (line[value_start] == ' ') { value_start++; }
3247
0
      value = line.substr(value_start);
3248
0
      // Remove trailing \r if present
3249
0
      if (!value.empty() && value.back() == '\r') { value.pop_back(); }
3250
0
    }
3251
0
3252
0
    // Handle known fields
3253
0
    if (field == "event") {
3254
0
      msg.event = value;
3255
0
    } else if (field == "data") {
3256
0
      // Multiple data lines are concatenated with newlines
3257
0
      if (!msg.data.empty()) { msg.data += "\n"; }
3258
0
      msg.data += value;
3259
0
    } else if (field == "id") {
3260
0
      // Empty id is valid (clears the last event ID)
3261
0
      msg.id = value;
3262
0
    } else if (field == "retry") {
3263
0
      // Parse retry interval in milliseconds
3264
0
      try {
3265
0
        retry_ms = std::stoi(value);
3266
0
      } catch (...) {
3267
0
        // Invalid retry value, ignore
3268
0
      }
3269
0
    }
3270
0
    // Unknown fields are ignored per SSE spec
3271
0
3272
0
    return false;
3273
0
  }
3274
3275
  // Main event loop with auto-reconnect
3276
0
  void run_event_loop() {
3277
0
    auto reconnect_count = 0;
3278
0
3279
0
    while (running_.load()) {
3280
0
      // Build headers, including Last-Event-ID if we have one
3281
0
      auto request_headers = headers_;
3282
0
      if (!last_event_id_.empty()) {
3283
0
        request_headers.emplace("Last-Event-ID", last_event_id_);
3284
0
      }
3285
0
3286
0
      // Open streaming connection
3287
0
      auto result = stream::Get(client_, path_, request_headers);
3288
0
3289
0
      // Connection error handling
3290
0
      if (!result) {
3291
0
        connected_.store(false);
3292
0
        if (on_error_) { on_error_(result.error()); }
3293
0
3294
0
        if (!should_reconnect(reconnect_count)) { break; }
3295
0
        wait_for_reconnect();
3296
0
        reconnect_count++;
3297
0
        continue;
3298
0
      }
3299
0
3300
0
      if (result.status() != 200) {
3301
0
        connected_.store(false);
3302
0
        // For certain errors, don't reconnect
3303
0
        if (result.status() == 204 || // No Content - server wants us to stop
3304
0
            result.status() == 404 || // Not Found
3305
0
            result.status() == 401 || // Unauthorized
3306
0
            result.status() == 403) { // Forbidden
3307
0
          if (on_error_) { on_error_(Error::Connection); }
3308
0
          break;
3309
0
        }
3310
0
3311
0
        if (on_error_) { on_error_(Error::Connection); }
3312
0
3313
0
        if (!should_reconnect(reconnect_count)) { break; }
3314
0
        wait_for_reconnect();
3315
0
        reconnect_count++;
3316
0
        continue;
3317
0
      }
3318
0
3319
0
      // Connection successful
3320
0
      connected_.store(true);
3321
0
      reconnect_count = 0;
3322
0
      if (on_open_) { on_open_(); }
3323
0
3324
0
      // Event receiving loop
3325
0
      std::string buffer;
3326
0
      SSEMessage current_msg;
3327
0
3328
0
      while (running_.load() && result.next()) {
3329
0
        buffer.append(result.data(), result.size());
3330
0
3331
0
        // Process complete lines in the buffer
3332
0
        size_t line_start = 0;
3333
0
        size_t newline_pos;
3334
0
3335
0
        while ((newline_pos = buffer.find('\n', line_start)) !=
3336
0
               std::string::npos) {
3337
0
          auto line = buffer.substr(line_start, newline_pos - line_start);
3338
0
          line_start = newline_pos + 1;
3339
0
3340
0
          // Parse the line and check if event is complete
3341
0
          auto event_complete =
3342
0
              parse_sse_line(line, current_msg, reconnect_interval_ms_);
3343
0
3344
0
          if (event_complete && !current_msg.data.empty()) {
3345
0
            // Update last_event_id for reconnection
3346
0
            if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
3347
0
3348
0
            // Dispatch event to appropriate handler
3349
0
            dispatch_event(current_msg);
3350
0
3351
0
            current_msg.clear();
3352
0
          }
3353
0
        }
3354
0
3355
0
        // Keep unprocessed data in buffer
3356
0
        buffer.erase(0, line_start);
3357
0
      }
3358
0
3359
0
      // Connection ended
3360
0
      connected_.store(false);
3361
0
3362
0
      if (!running_.load()) { break; }
3363
0
3364
0
      // Check for read errors
3365
0
      if (result.has_read_error()) {
3366
0
        if (on_error_) { on_error_(result.read_error()); }
3367
0
      }
3368
0
3369
0
      if (!should_reconnect(reconnect_count)) { break; }
3370
0
      wait_for_reconnect();
3371
0
      reconnect_count++;
3372
0
    }
3373
0
3374
0
    connected_.store(false);
3375
0
  }
3376
3377
  // Dispatch event to appropriate handler
3378
0
  void dispatch_event(const SSEMessage &msg) {
3379
0
    // Check for specific event type handler first
3380
0
    auto it = event_handlers_.find(msg.event);
3381
0
    if (it != event_handlers_.end()) {
3382
0
      it->second(msg);
3383
0
      return;
3384
0
    }
3385
0
3386
0
    // Fall back to generic message handler
3387
0
    if (on_message_) { on_message_(msg); }
3388
0
  }
3389
3390
  // Check if we should attempt to reconnect
3391
0
  bool should_reconnect(int count) const {
3392
0
    if (!running_.load()) { return false; }
3393
0
    if (max_reconnect_attempts_ == 0) { return true; } // unlimited
3394
0
    return count < max_reconnect_attempts_;
3395
0
  }
3396
3397
  // Wait for reconnect interval
3398
0
  void wait_for_reconnect() {
3399
0
    // Use small increments to check running_ flag frequently
3400
0
    auto waited = 0;
3401
0
    while (running_.load() && waited < reconnect_interval_ms_) {
3402
0
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
3403
0
      waited += 100;
3404
0
    }
3405
0
  }
3406
3407
  // Client and path
3408
  Client &client_;
3409
  std::string path_;
3410
  Headers headers_;
3411
3412
  // Callbacks
3413
  MessageHandler on_message_;
3414
  std::map<std::string, MessageHandler> event_handlers_;
3415
  OpenHandler on_open_;
3416
  ErrorHandler on_error_;
3417
3418
  // Configuration
3419
  int reconnect_interval_ms_ = 3000;
3420
  int max_reconnect_attempts_ = 0; // 0 = unlimited
3421
3422
  // State
3423
  std::atomic<bool> running_{false};
3424
  std::atomic<bool> connected_{false};
3425
  std::string last_event_id_;
3426
3427
  // Async support
3428
  std::thread async_thread_;
3429
};
3430
3431
} // namespace sse
3432
3433
// ----------------------------------------------------------------------------
3434
3435
/*
3436
 * Implementation that will be part of the .cc file if split into .h + .cc.
3437
 */
3438
3439
namespace detail {
3440
3441
108k
inline bool is_hex(char c, int &v) {
3442
108k
  if (isdigit(c)) {
3443
9.75k
    v = c - '0';
3444
9.75k
    return true;
3445
98.4k
  } else if ('A' <= c && c <= 'F') {
3446
3.18k
    v = c - 'A' + 10;
3447
3.18k
    return true;
3448
95.3k
  } else if ('a' <= c && c <= 'f') {
3449
7.71k
    v = c - 'a' + 10;
3450
7.71k
    return true;
3451
7.71k
  }
3452
87.5k
  return false;
3453
108k
}
3454
3455
inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
3456
93.4k
                          int &val) {
3457
93.4k
  if (i >= s.size()) { return false; }
3458
3459
93.1k
  val = 0;
3460
113k
  for (; cnt; i++, cnt--) {
3461
108k
    if (!s[i]) { return false; }
3462
108k
    auto v = 0;
3463
108k
    if (is_hex(s[i], v)) {
3464
20.6k
      val = val * 16 + v;
3465
87.5k
    } else {
3466
87.5k
      return false;
3467
87.5k
    }
3468
108k
  }
3469
5.32k
  return true;
3470
93.1k
}
3471
3472
0
inline std::string from_i_to_hex(size_t n) {
3473
0
  static const auto charset = "0123456789abcdef";
3474
0
  std::string ret;
3475
0
  do {
3476
0
    ret = charset[n & 15] + ret;
3477
0
    n >>= 4;
3478
0
  } while (n > 0);
3479
0
  return ret;
3480
0
}
3481
3482
0
inline std::string compute_etag(const FileStat &fs) {
3483
0
  if (!fs.is_file()) { return std::string(); }
3484
3485
  // If mtime cannot be determined (negative value indicates an error
3486
  // or sentinel), do not generate an ETag. Returning a neutral / fixed
3487
  // value like 0 could collide with a real file that legitimately has
3488
  // mtime == 0 (epoch) and lead to misleading validators.
3489
0
  auto mtime_raw = fs.mtime();
3490
0
  if (mtime_raw < 0) { return std::string(); }
3491
3492
0
  auto mtime = static_cast<size_t>(mtime_raw);
3493
0
  auto size = fs.size();
3494
3495
0
  return std::string("W/\"") + from_i_to_hex(mtime) + "-" +
3496
0
         from_i_to_hex(size) + "\"";
3497
0
}
3498
3499
// Format time_t as HTTP-date (RFC 9110 Section 5.6.7): "Sun, 06 Nov 1994
3500
// 08:49:37 GMT" This implementation is defensive: it validates `mtime`, checks
3501
// return values from `gmtime_r`/`gmtime_s`, and ensures `strftime` succeeds.
3502
0
inline std::string file_mtime_to_http_date(time_t mtime) {
3503
0
  if (mtime < 0) { return std::string(); }
3504
3505
0
  struct tm tm_buf;
3506
#ifdef _WIN32
3507
  if (gmtime_s(&tm_buf, &mtime) != 0) { return std::string(); }
3508
#else
3509
0
  if (gmtime_r(&mtime, &tm_buf) == nullptr) { return std::string(); }
3510
0
#endif
3511
0
  char buf[64];
3512
0
  if (strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {
3513
0
    return std::string();
3514
0
  }
3515
3516
0
  return std::string(buf);
3517
0
}
3518
3519
// Parse HTTP-date (RFC 9110 Section 5.6.7) to time_t. Returns -1 on failure.
3520
0
inline time_t parse_http_date(const std::string &date_str) {
3521
0
  struct tm tm_buf;
3522
3523
  // Create a classic locale object once for all parsing attempts
3524
0
  const std::locale classic_locale = std::locale::classic();
3525
3526
  // Try to parse using std::get_time (C++11, cross-platform)
3527
0
  auto try_parse = [&](const char *fmt) -> bool {
3528
0
    std::istringstream ss(date_str);
3529
0
    ss.imbue(classic_locale);
3530
3531
0
    memset(&tm_buf, 0, sizeof(tm_buf));
3532
0
    ss >> std::get_time(&tm_buf, fmt);
3533
3534
0
    return !ss.fail();
3535
0
  };
3536
3537
  // RFC 9110 preferred format (HTTP-date): "Sun, 06 Nov 1994 08:49:37 GMT"
3538
0
  if (!try_parse("%a, %d %b %Y %H:%M:%S")) {
3539
    // RFC 850 format: "Sunday, 06-Nov-94 08:49:37 GMT"
3540
0
    if (!try_parse("%A, %d-%b-%y %H:%M:%S")) {
3541
      // asctime format: "Sun Nov  6 08:49:37 1994"
3542
0
      if (!try_parse("%a %b %d %H:%M:%S %Y")) {
3543
0
        return static_cast<time_t>(-1);
3544
0
      }
3545
0
    }
3546
0
  }
3547
3548
#ifdef _WIN32
3549
  return _mkgmtime(&tm_buf);
3550
#else
3551
0
  return timegm(&tm_buf);
3552
0
#endif
3553
0
}
3554
3555
0
inline bool is_weak_etag(const std::string &s) {
3556
  // Check if the string is a weak ETag (starts with 'W/"')
3557
0
  return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
3558
0
}
3559
3560
0
inline bool is_strong_etag(const std::string &s) {
3561
  // Check if the string is a strong ETag (starts and ends with '"', at least 2
3562
  // chars)
3563
0
  return s.size() >= 2 && s[0] == '"' && s.back() == '"';
3564
0
}
3565
3566
2.57k
inline size_t to_utf8(int code, char *buff) {
3567
2.57k
  if (code < 0x0080) {
3568
349
    buff[0] = static_cast<char>(code & 0x7F);
3569
349
    return 1;
3570
2.22k
  } else if (code < 0x0800) {
3571
565
    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
3572
565
    buff[1] = static_cast<char>(0x80 | (code & 0x3F));
3573
565
    return 2;
3574
1.66k
  } else if (code < 0xD800) {
3575
448
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
3576
448
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
3577
448
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
3578
448
    return 3;
3579
1.21k
  } else if (code < 0xE000) { // D800 - DFFF is invalid...
3580
818
    return 0;
3581
818
  } else if (code < 0x10000) {
3582
397
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
3583
397
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
3584
397
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
3585
397
    return 3;
3586
397
  } else if (code < 0x110000) {
3587
0
    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
3588
0
    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
3589
0
    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
3590
0
    buff[3] = static_cast<char>(0x80 | (code & 0x3F));
3591
0
    return 4;
3592
0
  }
3593
3594
  // NOTREACHED
3595
0
  return 0;
3596
2.57k
}
3597
3598
// NOTE: This code came up with the following stackoverflow post:
3599
// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
3600
0
inline std::string base64_encode(const std::string &in) {
3601
0
  static const auto lookup =
3602
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3603
0
3604
0
  std::string out;
3605
0
  out.reserve(in.size());
3606
0
3607
0
  auto val = 0;
3608
0
  auto valb = -6;
3609
0
3610
0
  for (auto c : in) {
3611
0
    val = (val << 8) + static_cast<uint8_t>(c);
3612
0
    valb += 8;
3613
0
    while (valb >= 0) {
3614
0
      out.push_back(lookup[(val >> valb) & 0x3F]);
3615
0
      valb -= 6;
3616
0
    }
3617
0
  }
3618
0
3619
0
  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
3620
0
3621
0
  while (out.size() % 4) {
3622
0
    out.push_back('=');
3623
0
  }
3624
0
3625
0
  return out;
3626
0
}
3627
3628
0
inline bool is_valid_path(const std::string &path) {
3629
0
  size_t level = 0;
3630
0
  size_t i = 0;
3631
3632
  // Skip slash
3633
0
  while (i < path.size() && path[i] == '/') {
3634
0
    i++;
3635
0
  }
3636
3637
0
  while (i < path.size()) {
3638
    // Read component
3639
0
    auto beg = i;
3640
0
    while (i < path.size() && path[i] != '/') {
3641
0
      if (path[i] == '\0') {
3642
0
        return false;
3643
0
      } else if (path[i] == '\\') {
3644
0
        return false;
3645
0
      }
3646
0
      i++;
3647
0
    }
3648
3649
0
    auto len = i - beg;
3650
0
    assert(len > 0);
3651
3652
0
    if (!path.compare(beg, len, ".")) {
3653
0
      ;
3654
0
    } else if (!path.compare(beg, len, "..")) {
3655
0
      if (level == 0) { return false; }
3656
0
      level--;
3657
0
    } else {
3658
0
      level++;
3659
0
    }
3660
3661
    // Skip slash
3662
0
    while (i < path.size() && path[i] == '/') {
3663
0
      i++;
3664
0
    }
3665
0
  }
3666
3667
0
  return true;
3668
0
}
3669
3670
0
inline FileStat::FileStat(const std::string &path) {
3671
#if defined(_WIN32)
3672
  auto wpath = u8string_to_wstring(path.c_str());
3673
  ret_ = _wstat(wpath.c_str(), &st_);
3674
#else
3675
0
  ret_ = stat(path.c_str(), &st_);
3676
0
#endif
3677
0
}
3678
0
inline bool FileStat::is_file() const {
3679
0
  return ret_ >= 0 && S_ISREG(st_.st_mode);
3680
0
}
3681
0
inline bool FileStat::is_dir() const {
3682
0
  return ret_ >= 0 && S_ISDIR(st_.st_mode);
3683
0
}
3684
3685
0
inline time_t FileStat::mtime() const {
3686
0
  return ret_ >= 0 ? static_cast<time_t>(st_.st_mtime)
3687
0
                   : static_cast<time_t>(-1);
3688
0
}
3689
3690
0
inline size_t FileStat::size() const {
3691
0
  return ret_ >= 0 ? static_cast<size_t>(st_.st_size) : 0;
3692
0
}
3693
3694
0
inline std::string encode_path(const std::string &s) {
3695
0
  std::string result;
3696
0
  result.reserve(s.size());
3697
0
3698
0
  for (size_t i = 0; s[i]; i++) {
3699
0
    switch (s[i]) {
3700
0
    case ' ': result += "%20"; break;
3701
0
    case '+': result += "%2B"; break;
3702
0
    case '\r': result += "%0D"; break;
3703
0
    case '\n': result += "%0A"; break;
3704
0
    case '\'': result += "%27"; break;
3705
0
    case ',': result += "%2C"; break;
3706
0
    // case ':': result += "%3A"; break; // ok? probably...
3707
0
    case ';': result += "%3B"; break;
3708
0
    default:
3709
0
      auto c = static_cast<uint8_t>(s[i]);
3710
0
      if (c >= 0x80) {
3711
0
        result += '%';
3712
0
        char hex[4];
3713
0
        auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
3714
0
        assert(len == 2);
3715
0
        result.append(hex, static_cast<size_t>(len));
3716
0
      } else {
3717
0
        result += s[i];
3718
0
      }
3719
0
      break;
3720
0
    }
3721
0
  }
3722
0
3723
0
  return result;
3724
0
}
3725
3726
0
inline std::string file_extension(const std::string &path) {
3727
0
  std::smatch m;
3728
0
  thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
3729
0
  if (std::regex_search(path, m, re)) { return m[1].str(); }
3730
0
  return std::string();
3731
0
}
3732
3733
628M
inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
3734
3735
template <typename T>
3736
inline bool parse_header(const char *beg, const char *end, T fn);
3737
3738
template <typename T>
3739
218k
inline bool parse_header(const char *beg, const char *end, T fn) {
3740
  // Skip trailing spaces and tabs.
3741
225k
  while (beg < end && is_space_or_tab(end[-1])) {
3742
6.33k
    end--;
3743
6.33k
  }
3744
3745
218k
  auto p = beg;
3746
1.85M
  while (p < end && *p != ':') {
3747
1.63M
    p++;
3748
1.63M
  }
3749
3750
218k
  auto name = std::string(beg, p);
3751
218k
  if (!detail::fields::is_field_name(name)) { return false; }
3752
3753
218k
  if (p == end) { return false; }
3754
3755
218k
  auto key_end = p;
3756
3757
218k
  if (*p++ != ':') { return false; }
3758
3759
220k
  while (p < end && is_space_or_tab(*p)) {
3760
2.83k
    p++;
3761
2.83k
  }
3762
3763
218k
  if (p <= end) {
3764
218k
    auto key_len = key_end - beg;
3765
218k
    if (!key_len) { return false; }
3766
3767
218k
    auto key = std::string(beg, key_end);
3768
218k
    auto val = std::string(p, end);
3769
3770
218k
    if (!detail::fields::is_field_value(val)) { return false; }
3771
3772
218k
    if (case_ignore::equal(key, "Location") ||
3773
217k
        case_ignore::equal(key, "Referer")) {
3774
693
      fn(key, val);
3775
217k
    } else {
3776
217k
      fn(key, decode_path_component(val));
3777
217k
    }
3778
3779
218k
    return true;
3780
218k
  }
3781
3782
0
  return false;
3783
218k
}
bool httplib::detail::parse_header<httplib::detail::read_headers(httplib::Stream&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}>(char const*, char const, httplib::detail::read_headers(httplib::Stream&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1})
Line
Count
Source
3739
13.0k
inline bool parse_header(const char *beg, const char *end, T fn) {
3740
  // Skip trailing spaces and tabs.
3741
13.2k
  while (beg < end && is_space_or_tab(end[-1])) {
3742
224
    end--;
3743
224
  }
3744
3745
13.0k
  auto p = beg;
3746
236k
  while (p < end && *p != ':') {
3747
223k
    p++;
3748
223k
  }
3749
3750
13.0k
  auto name = std::string(beg, p);
3751
13.0k
  if (!detail::fields::is_field_name(name)) { return false; }
3752
3753
12.9k
  if (p == end) { return false; }
3754
3755
12.8k
  auto key_end = p;
3756
3757
12.8k
  if (*p++ != ':') { return false; }
3758
3759
13.2k
  while (p < end && is_space_or_tab(*p)) {
3760
389
    p++;
3761
389
  }
3762
3763
12.8k
  if (p <= end) {
3764
12.8k
    auto key_len = key_end - beg;
3765
12.8k
    if (!key_len) { return false; }
3766
3767
12.8k
    auto key = std::string(beg, key_end);
3768
12.8k
    auto val = std::string(p, end);
3769
3770
12.8k
    if (!detail::fields::is_field_value(val)) { return false; }
3771
3772
12.8k
    if (case_ignore::equal(key, "Location") ||
3773
12.7k
        case_ignore::equal(key, "Referer")) {
3774
88
      fn(key, val);
3775
12.7k
    } else {
3776
12.7k
      fn(key, decode_path_component(val));
3777
12.7k
    }
3778
3779
12.8k
    return true;
3780
12.8k
  }
3781
3782
0
  return false;
3783
12.8k
}
bool httplib::detail::parse_header<httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}>(char const*, char const*, httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1})
Line
Count
Source
3739
101k
inline bool parse_header(const char *beg, const char *end, T fn) {
3740
  // Skip trailing spaces and tabs.
3741
104k
  while (beg < end && is_space_or_tab(end[-1])) {
3742
2.96k
    end--;
3743
2.96k
  }
3744
3745
101k
  auto p = beg;
3746
811k
  while (p < end && *p != ':') {
3747
710k
    p++;
3748
710k
  }
3749
3750
101k
  auto name = std::string(beg, p);
3751
101k
  if (!detail::fields::is_field_name(name)) { return false; }
3752
3753
101k
  if (p == end) { return false; }
3754
3755
101k
  auto key_end = p;
3756
3757
101k
  if (*p++ != ':') { return false; }
3758
3759
102k
  while (p < end && is_space_or_tab(*p)) {
3760
1.02k
    p++;
3761
1.02k
  }
3762
3763
101k
  if (p <= end) {
3764
101k
    auto key_len = key_end - beg;
3765
101k
    if (!key_len) { return false; }
3766
3767
101k
    auto key = std::string(beg, key_end);
3768
101k
    auto val = std::string(p, end);
3769
3770
101k
    if (!detail::fields::is_field_value(val)) { return false; }
3771
3772
101k
    if (case_ignore::equal(key, "Location") ||
3773
101k
        case_ignore::equal(key, "Referer")) {
3774
205
      fn(key, val);
3775
101k
    } else {
3776
101k
      fn(key, decode_path_component(val));
3777
101k
    }
3778
3779
101k
    return true;
3780
101k
  }
3781
3782
0
  return false;
3783
101k
}
bool httplib::detail::parse_header<httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}>(char const*, char const*, httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2})
Line
Count
Source
3739
101k
inline bool parse_header(const char *beg, const char *end, T fn) {
3740
  // Skip trailing spaces and tabs.
3741
104k
  while (beg < end && is_space_or_tab(end[-1])) {
3742
2.77k
    end--;
3743
2.77k
  }
3744
3745
101k
  auto p = beg;
3746
762k
  while (p < end && *p != ':') {
3747
660k
    p++;
3748
660k
  }
3749
3750
101k
  auto name = std::string(beg, p);
3751
101k
  if (!detail::fields::is_field_name(name)) { return false; }
3752
3753
101k
  if (p == end) { return false; }
3754
3755
101k
  auto key_end = p;
3756
3757
101k
  if (*p++ != ':') { return false; }
3758
3759
102k
  while (p < end && is_space_or_tab(*p)) {
3760
1.02k
    p++;
3761
1.02k
  }
3762
3763
101k
  if (p <= end) {
3764
101k
    auto key_len = key_end - beg;
3765
101k
    if (!key_len) { return false; }
3766
3767
101k
    auto key = std::string(beg, key_end);
3768
101k
    auto val = std::string(p, end);
3769
3770
101k
    if (!detail::fields::is_field_value(val)) { return false; }
3771
3772
101k
    if (case_ignore::equal(key, "Location") ||
3773
101k
        case_ignore::equal(key, "Referer")) {
3774
205
      fn(key, val);
3775
101k
    } else {
3776
101k
      fn(key, decode_path_component(val));
3777
101k
    }
3778
3779
101k
    return true;
3780
101k
  }
3781
3782
0
  return false;
3783
101k
}
bool httplib::detail::parse_header<httplib::detail::parse_trailers(httplib::detail::stream_line_reader&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}>(char const*, char const, httplib::detail::parse_trailers(httplib::detail::stream_line_reader&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1})
Line
Count
Source
3739
2.36k
inline bool parse_header(const char *beg, const char *end, T fn) {
3740
  // Skip trailing spaces and tabs.
3741
2.73k
  while (beg < end && is_space_or_tab(end[-1])) {
3742
368
    end--;
3743
368
  }
3744
3745
2.36k
  auto p = beg;
3746
47.2k
  while (p < end && *p != ':') {
3747
44.8k
    p++;
3748
44.8k
  }
3749
3750
2.36k
  auto name = std::string(beg, p);
3751
2.36k
  if (!detail::fields::is_field_name(name)) { return false; }
3752
3753
1.96k
  if (p == end) { return false; }
3754
3755
1.94k
  auto key_end = p;
3756
3757
1.94k
  if (*p++ != ':') { return false; }
3758
3759
2.34k
  while (p < end && is_space_or_tab(*p)) {
3760
395
    p++;
3761
395
  }
3762
3763
1.94k
  if (p <= end) {
3764
1.94k
    auto key_len = key_end - beg;
3765
1.94k
    if (!key_len) { return false; }
3766
3767
1.94k
    auto key = std::string(beg, key_end);
3768
1.94k
    auto val = std::string(p, end);
3769
3770
1.94k
    if (!detail::fields::is_field_value(val)) { return false; }
3771
3772
1.94k
    if (case_ignore::equal(key, "Location") ||
3773
1.74k
        case_ignore::equal(key, "Referer")) {
3774
195
      fn(key, val);
3775
1.74k
    } else {
3776
1.74k
      fn(key, decode_path_component(val));
3777
1.74k
    }
3778
3779
1.94k
    return true;
3780
1.94k
  }
3781
3782
0
  return false;
3783
1.94k
}
3784
3785
inline bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
3786
537
                           const Headers &src_headers) {
3787
  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
3788
  // transfer coding is complete when a chunk with a chunk-size of zero is
3789
  // received, possibly followed by a trailer section, and finally terminated by
3790
  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
3791
  //
3792
  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
3793
  // doesn't care for the existence of the final CRLF. In other words, it seems
3794
  // to be ok whether the final CRLF exists or not in the chunked data.
3795
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
3796
  //
3797
  // According to the reference code in RFC 9112, cpp-httplib now allows
3798
  // chunked transfer coding data without the final CRLF.
3799
3800
  // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
3801
537
  thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
3802
537
      "transfer-encoding",
3803
537
      "content-length",
3804
537
      "host",
3805
537
      "authorization",
3806
537
      "www-authenticate",
3807
537
      "proxy-authenticate",
3808
537
      "proxy-authorization",
3809
537
      "cookie",
3810
537
      "set-cookie",
3811
537
      "cache-control",
3812
537
      "expect",
3813
537
      "max-forwards",
3814
537
      "pragma",
3815
537
      "range",
3816
537
      "te",
3817
537
      "age",
3818
537
      "expires",
3819
537
      "date",
3820
537
      "location",
3821
537
      "retry-after",
3822
537
      "vary",
3823
537
      "warning",
3824
537
      "content-encoding",
3825
537
      "content-type",
3826
537
      "content-range",
3827
537
      "trailer"};
3828
3829
537
  case_ignore::unordered_set<std::string> declared_trailers;
3830
537
  auto trailer_header = get_header_value(src_headers, "Trailer", "", 0);
3831
537
  if (trailer_header && std::strlen(trailer_header)) {
3832
410
    auto len = std::strlen(trailer_header);
3833
410
    split(trailer_header, trailer_header + len, ',',
3834
13.8k
          [&](const char *b, const char *e) {
3835
13.8k
            const char *kbeg = b;
3836
13.8k
            const char *kend = e;
3837
13.8k
            while (kbeg < kend && (*kbeg == ' ' || *kbeg == '\t')) {
3838
0
              ++kbeg;
3839
0
            }
3840
13.8k
            while (kend > kbeg && (kend[-1] == ' ' || kend[-1] == '\t')) {
3841
0
              --kend;
3842
0
            }
3843
13.8k
            std::string key(kbeg, static_cast<size_t>(kend - kbeg));
3844
13.8k
            if (!key.empty() &&
3845
13.8k
                prohibited_trailers.find(key) == prohibited_trailers.end()) {
3846
13.6k
              declared_trailers.insert(key);
3847
13.6k
            }
3848
13.8k
          });
3849
410
  }
3850
3851
537
  size_t trailer_header_count = 0;
3852
2.39k
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
3853
2.37k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
3854
2.36k
    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
3855
3856
2.36k
    constexpr auto line_terminator_len = 2;
3857
2.36k
    auto line_beg = line_reader.ptr();
3858
2.36k
    auto line_end =
3859
2.36k
        line_reader.ptr() + line_reader.size() - line_terminator_len;
3860
3861
2.36k
    if (!parse_header(line_beg, line_end,
3862
2.36k
                      [&](const std::string &key, const std::string &val) {
3863
1.94k
                        if (declared_trailers.find(key) !=
3864
1.94k
                            declared_trailers.end()) {
3865
641
                          dest.emplace(key, val);
3866
641
                          trailer_header_count++;
3867
641
                        }
3868
1.94k
                      })) {
3869
426
      return false;
3870
426
    }
3871
3872
1.94k
    if (!line_reader.getline()) { return false; }
3873
1.94k
  }
3874
3875
21
  return true;
3876
537
}
3877
3878
inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
3879
702k
                                      size_t right) {
3880
313M
  while (b + left < e && is_space_or_tab(b[left])) {
3881
313M
    left++;
3882
313M
  }
3883
313M
  while (right > 0 && is_space_or_tab(b[right - 1])) {
3884
313M
    right--;
3885
313M
  }
3886
702k
  return std::make_pair(left, right);
3887
702k
}
3888
3889
228k
inline std::string trim_copy(const std::string &s) {
3890
228k
  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
3891
228k
  return s.substr(r.first, r.second - r.first);
3892
228k
}
3893
3894
67.1k
inline std::string trim_double_quotes_copy(const std::string &s) {
3895
67.1k
  if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
3896
1.63k
    return s.substr(1, s.size() - 2);
3897
1.63k
  }
3898
65.4k
  return s;
3899
67.1k
}
3900
3901
inline void
3902
divide(const char *data, std::size_t size, char d,
3903
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
3904
12.5k
           fn) {
3905
12.5k
  const auto it = std::find(data, data + size, d);
3906
12.5k
  const auto found = static_cast<std::size_t>(it != data + size);
3907
12.5k
  const auto lhs_data = data;
3908
12.5k
  const auto lhs_size = static_cast<std::size_t>(it - data);
3909
12.5k
  const auto rhs_data = it + found;
3910
12.5k
  const auto rhs_size = size - lhs_size - found;
3911
3912
12.5k
  fn(lhs_data, lhs_size, rhs_data, rhs_size);
3913
12.5k
}
3914
3915
inline void
3916
divide(const std::string &str, char d,
3917
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
3918
6.18k
           fn) {
3919
6.18k
  divide(str.data(), str.size(), d, std::move(fn));
3920
6.18k
}
3921
3922
inline void split(const char *b, const char *e, char d,
3923
56.0k
                  std::function<void(const char *, const char *)> fn) {
3924
56.0k
  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
3925
56.0k
}
3926
3927
inline void split(const char *b, const char *e, char d, size_t m,
3928
56.0k
                  std::function<void(const char *, const char *)> fn) {
3929
56.0k
  size_t i = 0;
3930
56.0k
  size_t beg = 0;
3931
56.0k
  size_t count = 1;
3932
3933
4.18M
  while (e ? (b + i < e) : (b[i] != '\0')) {
3934
4.12M
    if (b[i] == d && count < m) {
3935
423k
      auto r = trim(b, e, beg, i);
3936
423k
      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
3937
423k
      beg = i + 1;
3938
423k
      count++;
3939
423k
    }
3940
4.12M
    i++;
3941
4.12M
  }
3942
3943
56.0k
  if (i) {
3944
50.2k
    auto r = trim(b, e, beg, i);
3945
50.2k
    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
3946
50.2k
  }
3947
56.0k
}
3948
3949
inline bool split_find(const char *b, const char *e, char d, size_t m,
3950
0
                       std::function<bool(const char *, const char *)> fn) {
3951
0
  size_t i = 0;
3952
0
  size_t beg = 0;
3953
0
  size_t count = 1;
3954
3955
0
  while (e ? (b + i < e) : (b[i] != '\0')) {
3956
0
    if (b[i] == d && count < m) {
3957
0
      auto r = trim(b, e, beg, i);
3958
0
      if (r.first < r.second) {
3959
0
        auto found = fn(&b[r.first], &b[r.second]);
3960
0
        if (found) { return true; }
3961
0
      }
3962
0
      beg = i + 1;
3963
0
      count++;
3964
0
    }
3965
0
    i++;
3966
0
  }
3967
3968
0
  if (i) {
3969
0
    auto r = trim(b, e, beg, i);
3970
0
    if (r.first < r.second) {
3971
0
      auto found = fn(&b[r.first], &b[r.second]);
3972
0
      if (found) { return true; }
3973
0
    }
3974
0
  }
3975
3976
0
  return false;
3977
0
}
3978
3979
inline bool split_find(const char *b, const char *e, char d,
3980
0
                       std::function<bool(const char *, const char *)> fn) {
3981
0
  return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
3982
0
                    std::move(fn));
3983
0
}
3984
3985
inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
3986
                                              size_t fixed_buffer_size)
3987
21.9k
    : strm_(strm), fixed_buffer_(fixed_buffer),
3988
21.9k
      fixed_buffer_size_(fixed_buffer_size) {}
3989
3990
71.1k
inline const char *stream_line_reader::ptr() const {
3991
71.1k
  if (growable_buffer_.empty()) {
3992
70.1k
    return fixed_buffer_;
3993
70.1k
  } else {
3994
1.04k
    return growable_buffer_.data();
3995
1.04k
  }
3996
71.1k
}
3997
3998
2.76M
inline size_t stream_line_reader::size() const {
3999
2.76M
  if (growable_buffer_.empty()) {
4000
1.26M
    return fixed_buffer_used_size_;
4001
1.49M
  } else {
4002
1.49M
    return growable_buffer_.size();
4003
1.49M
  }
4004
2.76M
}
4005
4006
17.9k
inline bool stream_line_reader::end_with_crlf() const {
4007
17.9k
  auto end = ptr() + size();
4008
17.9k
  return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
4009
17.9k
}
4010
4011
36.7k
inline bool stream_line_reader::getline() {
4012
36.7k
  fixed_buffer_used_size_ = 0;
4013
36.7k
  growable_buffer_.clear();
4014
4015
36.7k
#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4016
36.7k
  char prev_byte = 0;
4017
36.7k
#endif
4018
4019
2.67M
  for (size_t i = 0;; i++) {
4020
2.67M
    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
4021
      // Treat exceptionally long lines as an error to
4022
      // prevent infinite loops/memory exhaustion
4023
1
      return false;
4024
1
    }
4025
2.67M
    char byte;
4026
2.67M
    auto n = strm_.read(&byte, 1);
4027
4028
2.67M
    if (n < 0) {
4029
0
      return false;
4030
2.67M
    } else if (n == 0) {
4031
2.06k
      if (i == 0) {
4032
1.24k
        return false;
4033
1.24k
      } else {
4034
820
        break;
4035
820
      }
4036
2.06k
    }
4037
4038
2.67M
    append(byte);
4039
4040
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4041
    if (byte == '\n') { break; }
4042
#else
4043
2.67M
    if (prev_byte == '\r' && byte == '\n') { break; }
4044
2.63M
    prev_byte = byte;
4045
2.63M
#endif
4046
2.63M
  }
4047
4048
35.5k
  return true;
4049
36.7k
}
4050
4051
2.67M
inline void stream_line_reader::append(char c) {
4052
2.67M
  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
4053
1.18M
    fixed_buffer_[fixed_buffer_used_size_++] = c;
4054
1.18M
    fixed_buffer_[fixed_buffer_used_size_] = '\0';
4055
1.49M
  } else {
4056
1.49M
    if (growable_buffer_.empty()) {
4057
407
      assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
4058
407
      growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
4059
407
    }
4060
1.49M
    growable_buffer_ += c;
4061
1.49M
  }
4062
2.67M
}
4063
4064
0
inline mmap::mmap(const char *path) { open(path); }
4065
4066
0
inline mmap::~mmap() { close(); }
4067
4068
0
inline bool mmap::open(const char *path) {
4069
0
  close();
4070
4071
#if defined(_WIN32)
4072
  auto wpath = u8string_to_wstring(path);
4073
  if (wpath.empty()) { return false; }
4074
4075
  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
4076
                         OPEN_EXISTING, NULL);
4077
4078
  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
4079
4080
  LARGE_INTEGER size{};
4081
  if (!::GetFileSizeEx(hFile_, &size)) { return false; }
4082
  // If the following line doesn't compile due to QuadPart, update Windows SDK.
4083
  // See:
4084
  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
4085
  if (static_cast<ULONGLONG>(size.QuadPart) >
4086
      (std::numeric_limits<decltype(size_)>::max)()) {
4087
    // `size_t` might be 32-bits, on 32-bits Windows.
4088
    return false;
4089
  }
4090
  size_ = static_cast<size_t>(size.QuadPart);
4091
4092
  hMapping_ =
4093
      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
4094
4095
  // Special treatment for an empty file...
4096
  if (hMapping_ == NULL && size_ == 0) {
4097
    close();
4098
    is_open_empty_file = true;
4099
    return true;
4100
  }
4101
4102
  if (hMapping_ == NULL) {
4103
    close();
4104
    return false;
4105
  }
4106
4107
  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
4108
4109
  if (addr_ == nullptr) {
4110
    close();
4111
    return false;
4112
  }
4113
#else
4114
0
  fd_ = ::open(path, O_RDONLY);
4115
0
  if (fd_ == -1) { return false; }
4116
4117
0
  struct stat sb;
4118
0
  if (fstat(fd_, &sb) == -1) {
4119
0
    close();
4120
0
    return false;
4121
0
  }
4122
0
  size_ = static_cast<size_t>(sb.st_size);
4123
4124
0
  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
4125
4126
  // Special treatment for an empty file...
4127
0
  if (addr_ == MAP_FAILED && size_ == 0) {
4128
0
    close();
4129
0
    is_open_empty_file = true;
4130
0
    return false;
4131
0
  }
4132
0
#endif
4133
4134
0
  return true;
4135
0
}
4136
4137
0
inline bool mmap::is_open() const {
4138
0
  return is_open_empty_file ? true : addr_ != nullptr;
4139
0
}
4140
4141
0
inline size_t mmap::size() const { return size_; }
4142
4143
0
inline const char *mmap::data() const {
4144
0
  return is_open_empty_file ? "" : static_cast<const char *>(addr_);
4145
0
}
4146
4147
0
inline void mmap::close() {
4148
#if defined(_WIN32)
4149
  if (addr_) {
4150
    ::UnmapViewOfFile(addr_);
4151
    addr_ = nullptr;
4152
  }
4153
4154
  if (hMapping_) {
4155
    ::CloseHandle(hMapping_);
4156
    hMapping_ = NULL;
4157
  }
4158
4159
  if (hFile_ != INVALID_HANDLE_VALUE) {
4160
    ::CloseHandle(hFile_);
4161
    hFile_ = INVALID_HANDLE_VALUE;
4162
  }
4163
4164
  is_open_empty_file = false;
4165
#else
4166
0
  if (addr_ != nullptr) {
4167
0
    munmap(addr_, size_);
4168
0
    addr_ = nullptr;
4169
0
  }
4170
4171
0
  if (fd_ != -1) {
4172
0
    ::close(fd_);
4173
0
    fd_ = -1;
4174
0
  }
4175
0
#endif
4176
0
  size_ = 0;
4177
0
}
4178
0
inline int close_socket(socket_t sock) {
4179
#ifdef _WIN32
4180
  return closesocket(sock);
4181
#else
4182
0
  return close(sock);
4183
0
#endif
4184
0
}
4185
4186
0
template <typename T> inline ssize_t handle_EINTR(T fn) {
4187
0
  ssize_t res = 0;
4188
0
  while (true) {
4189
0
    res = fn();
4190
0
    if (res < 0 && errno == EINTR) {
4191
0
      std::this_thread::sleep_for(std::chrono::microseconds{1});
4192
0
      continue;
4193
0
    }
4194
0
    break;
4195
0
  }
4196
0
  return res;
4197
0
}
Unexecuted instantiation: long httplib::detail::handle_EINTR<httplib::detail::select_impl<true>(int, long, long)::{lambda()#1}>(httplib::detail::select_impl<true>(int, long, long)::{lambda()#1})
Unexecuted instantiation: long httplib::detail::handle_EINTR<httplib::detail::read_socket(int, void*, unsigned long, int)::{lambda()#1}>(httplib::detail::read_socket(int, void*, unsigned long, int)::{lambda()#1})
Unexecuted instantiation: long httplib::detail::handle_EINTR<httplib::detail::select_impl<false>(int, long, long)::{lambda()#1}>(httplib::detail::select_impl<false>(int, long, long)::{lambda()#1})
Unexecuted instantiation: long httplib::detail::handle_EINTR<httplib::detail::send_socket(int, void const*, unsigned long, int)::{lambda()#1}>(httplib::detail::send_socket(int, void const*, unsigned long, int)::{lambda()#1})
Unexecuted instantiation: long httplib::detail::handle_EINTR<httplib::detail::wait_until_socket_is_ready(int, long, long)::{lambda()#1}>(httplib::detail::wait_until_socket_is_ready(int, long, long)::{lambda()#1})
4198
4199
0
inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
4200
0
  return handle_EINTR([&]() {
4201
0
    return recv(sock,
4202
#ifdef _WIN32
4203
                static_cast<char *>(ptr), static_cast<int>(size),
4204
#else
4205
0
                ptr, size,
4206
0
#endif
4207
0
                flags);
4208
0
  });
4209
0
}
4210
4211
inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
4212
0
                           int flags) {
4213
0
  return handle_EINTR([&]() {
4214
0
    return send(sock,
4215
#ifdef _WIN32
4216
                static_cast<const char *>(ptr), static_cast<int>(size),
4217
#else
4218
0
                ptr, size,
4219
0
#endif
4220
0
                flags);
4221
0
  });
4222
0
}
4223
4224
0
inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
4225
#ifdef _WIN32
4226
  return ::WSAPoll(fds, nfds, timeout);
4227
#else
4228
0
  return ::poll(fds, nfds, timeout);
4229
0
#endif
4230
0
}
4231
4232
template <bool Read>
4233
0
inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
4234
#ifdef __APPLE__
4235
  if (sock >= FD_SETSIZE) { return -1; }
4236
4237
  fd_set fds, *rfds, *wfds;
4238
  FD_ZERO(&fds);
4239
  FD_SET(sock, &fds);
4240
  rfds = (Read ? &fds : nullptr);
4241
  wfds = (Read ? nullptr : &fds);
4242
4243
  timeval tv;
4244
  tv.tv_sec = static_cast<long>(sec);
4245
  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
4246
4247
  return handle_EINTR([&]() {
4248
    return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
4249
  });
4250
#else
4251
0
  struct pollfd pfd;
4252
0
  pfd.fd = sock;
4253
0
  pfd.events = (Read ? POLLIN : POLLOUT);
4254
4255
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
4256
4257
0
  return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
Unexecuted instantiation: httplib::detail::select_impl<true>(int, long, long)::{lambda()#1}::operator()() const
Unexecuted instantiation: httplib::detail::select_impl<false>(int, long, long)::{lambda()#1}::operator()() const
4258
0
#endif
4259
0
}
Unexecuted instantiation: long httplib::detail::select_impl<true>(int, long, long)
Unexecuted instantiation: long httplib::detail::select_impl<false>(int, long, long)
4260
4261
0
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
4262
0
  return select_impl<true>(sock, sec, usec);
4263
0
}
4264
4265
0
inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
4266
0
  return select_impl<false>(sock, sec, usec);
4267
0
}
4268
4269
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
4270
0
                                        time_t usec) {
4271
#ifdef __APPLE__
4272
  if (sock >= FD_SETSIZE) { return Error::Connection; }
4273
4274
  fd_set fdsr, fdsw;
4275
  FD_ZERO(&fdsr);
4276
  FD_ZERO(&fdsw);
4277
  FD_SET(sock, &fdsr);
4278
  FD_SET(sock, &fdsw);
4279
4280
  timeval tv;
4281
  tv.tv_sec = static_cast<long>(sec);
4282
  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
4283
4284
  auto ret = handle_EINTR([&]() {
4285
    return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
4286
  });
4287
4288
  if (ret == 0) { return Error::ConnectionTimeout; }
4289
4290
  if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
4291
    auto error = 0;
4292
    socklen_t len = sizeof(error);
4293
    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
4294
                          reinterpret_cast<char *>(&error), &len);
4295
    auto successful = res >= 0 && !error;
4296
    return successful ? Error::Success : Error::Connection;
4297
  }
4298
4299
  return Error::Connection;
4300
#else
4301
0
  struct pollfd pfd_read;
4302
0
  pfd_read.fd = sock;
4303
0
  pfd_read.events = POLLIN | POLLOUT;
4304
4305
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
4306
4307
0
  auto poll_res =
4308
0
      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
4309
4310
0
  if (poll_res == 0) { return Error::ConnectionTimeout; }
4311
4312
0
  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
4313
0
    auto error = 0;
4314
0
    socklen_t len = sizeof(error);
4315
0
    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
4316
0
                          reinterpret_cast<char *>(&error), &len);
4317
0
    auto successful = res >= 0 && !error;
4318
0
    return successful ? Error::Success : Error::Connection;
4319
0
  }
4320
4321
0
  return Error::Connection;
4322
0
#endif
4323
0
}
4324
4325
0
inline bool is_socket_alive(socket_t sock) {
4326
0
  const auto val = detail::select_read(sock, 0, 0);
4327
0
  if (val == 0) {
4328
0
    return true;
4329
0
  } else if (val < 0 && errno == EBADF) {
4330
0
    return false;
4331
0
  }
4332
0
  char buf[1];
4333
0
  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
4334
0
}
4335
4336
class SocketStream final : public Stream {
4337
public:
4338
  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
4339
               time_t write_timeout_sec, time_t write_timeout_usec,
4340
               time_t max_timeout_msec = 0,
4341
               std::chrono::time_point<std::chrono::steady_clock> start_time =
4342
                   (std::chrono::steady_clock::time_point::min)());
4343
  ~SocketStream() override;
4344
4345
  bool is_readable() const override;
4346
  bool wait_readable() const override;
4347
  bool wait_writable() const override;
4348
  ssize_t read(char *ptr, size_t size) override;
4349
  ssize_t write(const char *ptr, size_t size) override;
4350
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
4351
  void get_local_ip_and_port(std::string &ip, int &port) const override;
4352
  socket_t socket() const override;
4353
  time_t duration() const override;
4354
4355
private:
4356
  socket_t sock_;
4357
  time_t read_timeout_sec_;
4358
  time_t read_timeout_usec_;
4359
  time_t write_timeout_sec_;
4360
  time_t write_timeout_usec_;
4361
  time_t max_timeout_msec_;
4362
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
4363
4364
  std::vector<char> read_buff_;
4365
  size_t read_buff_off_ = 0;
4366
  size_t read_buff_content_size_ = 0;
4367
4368
  static const size_t read_buff_size_ = 1024l * 4;
4369
};
4370
4371
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4372
class SSLSocketStream final : public Stream {
4373
public:
4374
  SSLSocketStream(
4375
      socket_t sock, SSL *ssl, time_t read_timeout_sec,
4376
      time_t read_timeout_usec, time_t write_timeout_sec,
4377
      time_t write_timeout_usec, time_t max_timeout_msec = 0,
4378
      std::chrono::time_point<std::chrono::steady_clock> start_time =
4379
          (std::chrono::steady_clock::time_point::min)());
4380
  ~SSLSocketStream() override;
4381
4382
  bool is_readable() const override;
4383
  bool wait_readable() const override;
4384
  bool wait_writable() const override;
4385
  ssize_t read(char *ptr, size_t size) override;
4386
  ssize_t write(const char *ptr, size_t size) override;
4387
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
4388
  void get_local_ip_and_port(std::string &ip, int &port) const override;
4389
  socket_t socket() const override;
4390
  time_t duration() const override;
4391
4392
private:
4393
  socket_t sock_;
4394
  SSL *ssl_;
4395
  time_t read_timeout_sec_;
4396
  time_t read_timeout_usec_;
4397
  time_t write_timeout_sec_;
4398
  time_t write_timeout_usec_;
4399
  time_t max_timeout_msec_;
4400
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
4401
};
4402
#endif
4403
4404
inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
4405
0
                       time_t keep_alive_timeout_sec) {
4406
0
  using namespace std::chrono;
4407
4408
0
  const auto interval_usec =
4409
0
      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
4410
4411
  // Avoid expensive `steady_clock::now()` call for the first time
4412
0
  if (select_read(sock, 0, interval_usec) > 0) { return true; }
4413
4414
0
  const auto start = steady_clock::now() - microseconds{interval_usec};
4415
0
  const auto timeout = seconds{keep_alive_timeout_sec};
4416
4417
0
  while (true) {
4418
0
    if (svr_sock == INVALID_SOCKET) {
4419
0
      break; // Server socket is closed
4420
0
    }
4421
4422
0
    auto val = select_read(sock, 0, interval_usec);
4423
0
    if (val < 0) {
4424
0
      break; // Ssocket error
4425
0
    } else if (val == 0) {
4426
0
      if (steady_clock::now() - start > timeout) {
4427
0
        break; // Timeout
4428
0
      }
4429
0
    } else {
4430
0
      return true; // Ready for read
4431
0
    }
4432
0
  }
4433
4434
0
  return false;
4435
0
}
4436
4437
template <typename T>
4438
inline bool
4439
process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
4440
                           size_t keep_alive_max_count,
4441
0
                           time_t keep_alive_timeout_sec, T callback) {
4442
0
  assert(keep_alive_max_count > 0);
4443
0
  auto ret = false;
4444
0
  auto count = keep_alive_max_count;
4445
0
  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
4446
0
    auto close_connection = count == 1;
4447
0
    auto connection_closed = false;
4448
0
    ret = callback(close_connection, connection_closed);
4449
0
    if (!ret || connection_closed) { break; }
4450
0
    count--;
4451
0
  }
4452
0
  return ret;
4453
0
}
4454
4455
template <typename T>
4456
inline bool
4457
process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
4458
                      size_t keep_alive_max_count,
4459
                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,
4460
                      time_t read_timeout_usec, time_t write_timeout_sec,
4461
0
                      time_t write_timeout_usec, T callback) {
4462
0
  return process_server_socket_core(
4463
0
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
4464
0
      [&](bool close_connection, bool &connection_closed) {
4465
0
        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
4466
0
                          write_timeout_sec, write_timeout_usec);
4467
0
        return callback(strm, close_connection, connection_closed);
4468
0
      });
4469
0
}
4470
4471
inline bool process_client_socket(
4472
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
4473
    time_t write_timeout_sec, time_t write_timeout_usec,
4474
    time_t max_timeout_msec,
4475
    std::chrono::time_point<std::chrono::steady_clock> start_time,
4476
0
    std::function<bool(Stream &)> callback) {
4477
0
  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
4478
0
                    write_timeout_sec, write_timeout_usec, max_timeout_msec,
4479
0
                    start_time);
4480
0
  return callback(strm);
4481
0
}
4482
4483
0
inline int shutdown_socket(socket_t sock) {
4484
#ifdef _WIN32
4485
  return shutdown(sock, SD_BOTH);
4486
#else
4487
0
  return shutdown(sock, SHUT_RDWR);
4488
0
#endif
4489
0
}
4490
4491
0
inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
4492
0
  if (s.size() > 1 && s[0] == '\0') {
4493
0
    auto ret = s;
4494
0
    ret[0] = '@';
4495
0
    return ret;
4496
0
  }
4497
0
  return s;
4498
0
}
4499
4500
inline std::string
4501
0
unescape_abstract_namespace_unix_domain(const std::string &s) {
4502
0
  if (s.size() > 1 && s[0] == '@') {
4503
0
    auto ret = s;
4504
0
    ret[0] = '\0';
4505
0
    return ret;
4506
0
  }
4507
0
  return s;
4508
0
}
4509
4510
inline int getaddrinfo_with_timeout(const char *node, const char *service,
4511
                                    const struct addrinfo *hints,
4512
0
                                    struct addrinfo **res, time_t timeout_sec) {
4513
#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
4514
  if (timeout_sec <= 0) {
4515
    // No timeout specified, use standard getaddrinfo
4516
    return getaddrinfo(node, service, hints, res);
4517
  }
4518
4519
#ifdef _WIN32
4520
  // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
4521
  OVERLAPPED overlapped = {0};
4522
  HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
4523
  if (!event) { return EAI_FAIL; }
4524
4525
  overlapped.hEvent = event;
4526
4527
  PADDRINFOEXW result_addrinfo = nullptr;
4528
  HANDLE cancel_handle = nullptr;
4529
4530
  ADDRINFOEXW hints_ex = {0};
4531
  if (hints) {
4532
    hints_ex.ai_flags = hints->ai_flags;
4533
    hints_ex.ai_family = hints->ai_family;
4534
    hints_ex.ai_socktype = hints->ai_socktype;
4535
    hints_ex.ai_protocol = hints->ai_protocol;
4536
  }
4537
4538
  auto wnode = u8string_to_wstring(node);
4539
  auto wservice = u8string_to_wstring(service);
4540
4541
  auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
4542
                              hints ? &hints_ex : nullptr, &result_addrinfo,
4543
                              nullptr, &overlapped, nullptr, &cancel_handle);
4544
4545
  if (ret == WSA_IO_PENDING) {
4546
    auto wait_result =
4547
        ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
4548
    if (wait_result == WAIT_TIMEOUT) {
4549
      if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
4550
      ::CloseHandle(event);
4551
      return EAI_AGAIN;
4552
    }
4553
4554
    DWORD bytes_returned;
4555
    if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
4556
                               &bytes_returned, FALSE)) {
4557
      ::CloseHandle(event);
4558
      return ::WSAGetLastError();
4559
    }
4560
  }
4561
4562
  ::CloseHandle(event);
4563
4564
  if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
4565
    *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
4566
    return 0;
4567
  }
4568
4569
  return ret;
4570
#elif TARGET_OS_MAC
4571
  if (!node) { return EAI_NONAME; }
4572
  // macOS implementation using CFHost API for asynchronous DNS resolution
4573
  CFStringRef hostname_ref = CFStringCreateWithCString(
4574
      kCFAllocatorDefault, node, kCFStringEncodingUTF8);
4575
  if (!hostname_ref) { return EAI_MEMORY; }
4576
4577
  CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
4578
  CFRelease(hostname_ref);
4579
  if (!host_ref) { return EAI_MEMORY; }
4580
4581
  // Set up context for callback
4582
  struct CFHostContext {
4583
    bool completed = false;
4584
    bool success = false;
4585
    CFArrayRef addresses = nullptr;
4586
    std::mutex mutex;
4587
    std::condition_variable cv;
4588
  } context;
4589
4590
  CFHostClientContext client_context;
4591
  memset(&client_context, 0, sizeof(client_context));
4592
  client_context.info = &context;
4593
4594
  // Set callback
4595
  auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
4596
                     const CFStreamError *error, void *info) {
4597
    auto ctx = static_cast<CFHostContext *>(info);
4598
    std::lock_guard<std::mutex> lock(ctx->mutex);
4599
4600
    if (error && error->error != 0) {
4601
      ctx->success = false;
4602
    } else {
4603
      Boolean hasBeenResolved;
4604
      ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
4605
      if (ctx->addresses && hasBeenResolved) {
4606
        CFRetain(ctx->addresses);
4607
        ctx->success = true;
4608
      } else {
4609
        ctx->success = false;
4610
      }
4611
    }
4612
    ctx->completed = true;
4613
    ctx->cv.notify_one();
4614
  };
4615
4616
  if (!CFHostSetClient(host_ref, callback, &client_context)) {
4617
    CFRelease(host_ref);
4618
    return EAI_SYSTEM;
4619
  }
4620
4621
  // Schedule on run loop
4622
  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
4623
  CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
4624
4625
  // Start resolution
4626
  CFStreamError stream_error;
4627
  if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
4628
    CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
4629
    CFRelease(host_ref);
4630
    return EAI_FAIL;
4631
  }
4632
4633
  // Wait for completion with timeout
4634
  auto timeout_time =
4635
      std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
4636
  bool timed_out = false;
4637
4638
  {
4639
    std::unique_lock<std::mutex> lock(context.mutex);
4640
4641
    while (!context.completed) {
4642
      auto now = std::chrono::steady_clock::now();
4643
      if (now >= timeout_time) {
4644
        timed_out = true;
4645
        break;
4646
      }
4647
4648
      // Run the runloop for a short time
4649
      lock.unlock();
4650
      CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
4651
      lock.lock();
4652
    }
4653
  }
4654
4655
  // Clean up
4656
  CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
4657
  CFHostSetClient(host_ref, nullptr, nullptr);
4658
4659
  if (timed_out || !context.completed) {
4660
    CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
4661
    CFRelease(host_ref);
4662
    return EAI_AGAIN;
4663
  }
4664
4665
  if (!context.success || !context.addresses) {
4666
    CFRelease(host_ref);
4667
    return EAI_NODATA;
4668
  }
4669
4670
  // Convert CFArray to addrinfo
4671
  CFIndex count = CFArrayGetCount(context.addresses);
4672
  if (count == 0) {
4673
    CFRelease(context.addresses);
4674
    CFRelease(host_ref);
4675
    return EAI_NODATA;
4676
  }
4677
4678
  struct addrinfo *result_addrinfo = nullptr;
4679
  struct addrinfo **current = &result_addrinfo;
4680
4681
  for (CFIndex i = 0; i < count; i++) {
4682
    CFDataRef addr_data =
4683
        static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
4684
    if (!addr_data) continue;
4685
4686
    const struct sockaddr *sockaddr_ptr =
4687
        reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
4688
    socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
4689
4690
    // Allocate addrinfo structure
4691
    *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
4692
    if (!*current) {
4693
      freeaddrinfo(result_addrinfo);
4694
      CFRelease(context.addresses);
4695
      CFRelease(host_ref);
4696
      return EAI_MEMORY;
4697
    }
4698
4699
    memset(*current, 0, sizeof(struct addrinfo));
4700
4701
    // Set up addrinfo fields
4702
    (*current)->ai_family = sockaddr_ptr->sa_family;
4703
    (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
4704
    (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
4705
    (*current)->ai_addrlen = sockaddr_len;
4706
4707
    // Copy sockaddr
4708
    (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
4709
    if (!(*current)->ai_addr) {
4710
      freeaddrinfo(result_addrinfo);
4711
      CFRelease(context.addresses);
4712
      CFRelease(host_ref);
4713
      return EAI_MEMORY;
4714
    }
4715
    memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
4716
4717
    // Set port if service is specified
4718
    if (service && strlen(service) > 0) {
4719
      int port = atoi(service);
4720
      if (port > 0) {
4721
        if (sockaddr_ptr->sa_family == AF_INET) {
4722
          reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
4723
              ->sin_port = htons(static_cast<uint16_t>(port));
4724
        } else if (sockaddr_ptr->sa_family == AF_INET6) {
4725
          reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
4726
              ->sin6_port = htons(static_cast<uint16_t>(port));
4727
        }
4728
      }
4729
    }
4730
4731
    current = &((*current)->ai_next);
4732
  }
4733
4734
  CFRelease(context.addresses);
4735
  CFRelease(host_ref);
4736
4737
  *res = result_addrinfo;
4738
  return 0;
4739
#elif defined(_GNU_SOURCE) && defined(__GLIBC__) &&                            \
4740
    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
4741
  // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
4742
  struct gaicb request;
4743
  struct gaicb *requests[1] = {&request};
4744
  struct sigevent sevp;
4745
  struct timespec timeout;
4746
4747
  // Initialize the request structure
4748
  memset(&request, 0, sizeof(request));
4749
  request.ar_name = node;
4750
  request.ar_service = service;
4751
  request.ar_request = hints;
4752
4753
  // Set up timeout
4754
  timeout.tv_sec = timeout_sec;
4755
  timeout.tv_nsec = 0;
4756
4757
  // Initialize sigevent structure (not used, but required)
4758
  memset(&sevp, 0, sizeof(sevp));
4759
  sevp.sigev_notify = SIGEV_NONE;
4760
4761
  // Start asynchronous resolution
4762
  int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
4763
  if (start_result != 0) { return start_result; }
4764
4765
  // Wait for completion with timeout
4766
  int wait_result =
4767
      gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
4768
4769
  if (wait_result == 0 || wait_result == EAI_ALLDONE) {
4770
    // Completed successfully, get the result
4771
    int gai_result = gai_error(&request);
4772
    if (gai_result == 0) {
4773
      *res = request.ar_result;
4774
      return 0;
4775
    } else {
4776
      // Clean up on error
4777
      if (request.ar_result) { freeaddrinfo(request.ar_result); }
4778
      return gai_result;
4779
    }
4780
  } else if (wait_result == EAI_AGAIN) {
4781
    // Timeout occurred, cancel the request
4782
    gai_cancel(&request);
4783
    return EAI_AGAIN;
4784
  } else {
4785
    // Other error occurred
4786
    gai_cancel(&request);
4787
    return wait_result;
4788
  }
4789
#else
4790
  // Fallback implementation using thread-based timeout for other Unix systems
4791
4792
  struct GetAddrInfoState {
4793
    ~GetAddrInfoState() {
4794
      if (info) { freeaddrinfo(info); }
4795
    }
4796
4797
    std::mutex mutex;
4798
    std::condition_variable result_cv;
4799
    bool completed = false;
4800
    int result = EAI_SYSTEM;
4801
    std::string node;
4802
    std::string service;
4803
    struct addrinfo hints;
4804
    struct addrinfo *info = nullptr;
4805
  };
4806
4807
  // Allocate on the heap, so the resolver thread can keep using the data.
4808
  auto state = std::make_shared<GetAddrInfoState>();
4809
  if (node) { state->node = node; }
4810
  state->service = service;
4811
  state->hints = *hints;
4812
4813
  std::thread resolve_thread([state]() {
4814
    auto thread_result =
4815
        getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
4816
                    &state->info);
4817
4818
    std::lock_guard<std::mutex> lock(state->mutex);
4819
    state->result = thread_result;
4820
    state->completed = true;
4821
    state->result_cv.notify_one();
4822
  });
4823
4824
  // Wait for completion or timeout
4825
  std::unique_lock<std::mutex> lock(state->mutex);
4826
  auto finished =
4827
      state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
4828
                                [&] { return state->completed; });
4829
4830
  if (finished) {
4831
    // Operation completed within timeout
4832
    resolve_thread.join();
4833
    *res = state->info;
4834
    state->info = nullptr; // Pass ownership to caller
4835
    return state->result;
4836
  } else {
4837
    // Timeout occurred
4838
    resolve_thread.detach(); // Let the thread finish in background
4839
    return EAI_AGAIN;        // Return timeout error
4840
  }
4841
#endif
4842
#else
4843
0
  (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
4844
0
  return getaddrinfo(node, service, hints, res);
4845
0
#endif
4846
0
}
4847
4848
template <typename BindOrConnect>
4849
socket_t create_socket(const std::string &host, const std::string &ip, int port,
4850
                       int address_family, int socket_flags, bool tcp_nodelay,
4851
                       bool ipv6_v6only, SocketOptions socket_options,
4852
0
                       BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
4853
  // Get address info
4854
0
  const char *node = nullptr;
4855
0
  struct addrinfo hints;
4856
0
  struct addrinfo *result;
4857
4858
0
  memset(&hints, 0, sizeof(struct addrinfo));
4859
0
  hints.ai_socktype = SOCK_STREAM;
4860
0
  hints.ai_protocol = IPPROTO_IP;
4861
4862
0
  if (!ip.empty()) {
4863
0
    node = ip.c_str();
4864
    // Ask getaddrinfo to convert IP in c-string to address
4865
0
    hints.ai_family = AF_UNSPEC;
4866
0
    hints.ai_flags = AI_NUMERICHOST;
4867
0
  } else {
4868
0
    if (!host.empty()) { node = host.c_str(); }
4869
0
    hints.ai_family = address_family;
4870
0
    hints.ai_flags = socket_flags;
4871
0
  }
4872
4873
0
#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
4874
0
  if (hints.ai_family == AF_UNIX) {
4875
0
    const auto addrlen = host.length();
4876
0
    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
4877
4878
0
#ifdef SOCK_CLOEXEC
4879
0
    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
4880
0
                       hints.ai_protocol);
4881
#else
4882
    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
4883
#endif
4884
4885
0
    if (sock != INVALID_SOCKET) {
4886
0
      sockaddr_un addr{};
4887
0
      addr.sun_family = AF_UNIX;
4888
4889
0
      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
4890
0
      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
4891
4892
0
      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
4893
0
      hints.ai_addrlen = static_cast<socklen_t>(
4894
0
          sizeof(addr) - sizeof(addr.sun_path) + addrlen);
4895
4896
#ifndef SOCK_CLOEXEC
4897
#ifndef _WIN32
4898
      fcntl(sock, F_SETFD, FD_CLOEXEC);
4899
#endif
4900
#endif
4901
4902
0
      if (socket_options) { socket_options(sock); }
4903
4904
#ifdef _WIN32
4905
      // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
4906
      // remove the option.
4907
      detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
4908
#endif
4909
4910
0
      bool dummy;
4911
0
      if (!bind_or_connect(sock, hints, dummy)) {
4912
0
        close_socket(sock);
4913
0
        sock = INVALID_SOCKET;
4914
0
      }
4915
0
    }
4916
0
    return sock;
4917
0
  }
4918
0
#endif
4919
4920
0
  auto service = std::to_string(port);
4921
4922
0
  if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
4923
0
                               timeout_sec)) {
4924
0
#if defined __linux__ && !defined __ANDROID__
4925
0
    res_init();
4926
0
#endif
4927
0
    return INVALID_SOCKET;
4928
0
  }
4929
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
4930
4931
0
  for (auto rp = result; rp; rp = rp->ai_next) {
4932
    // Create a socket
4933
#ifdef _WIN32
4934
    auto sock =
4935
        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
4936
                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
4937
    /**
4938
     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
4939
     * and above the socket creation fails on older Windows Systems.
4940
     *
4941
     * Let's try to create a socket the old way in this case.
4942
     *
4943
     * Reference:
4944
     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
4945
     *
4946
     * WSA_FLAG_NO_HANDLE_INHERIT:
4947
     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
4948
     * SP1, and later
4949
     *
4950
     */
4951
    if (sock == INVALID_SOCKET) {
4952
      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
4953
    }
4954
#else
4955
4956
0
#ifdef SOCK_CLOEXEC
4957
0
    auto sock =
4958
0
        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
4959
#else
4960
    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
4961
#endif
4962
4963
0
#endif
4964
0
    if (sock == INVALID_SOCKET) { continue; }
4965
4966
#if !defined _WIN32 && !defined SOCK_CLOEXEC
4967
    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
4968
      close_socket(sock);
4969
      continue;
4970
    }
4971
#endif
4972
4973
0
    if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
4974
4975
0
    if (rp->ai_family == AF_INET6) {
4976
0
      set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
4977
0
    }
4978
4979
0
    if (socket_options) { socket_options(sock); }
4980
4981
    // bind or connect
4982
0
    auto quit = false;
4983
0
    if (bind_or_connect(sock, *rp, quit)) { return sock; }
4984
4985
0
    close_socket(sock);
4986
4987
0
    if (quit) { break; }
4988
0
  }
4989
4990
0
  return INVALID_SOCKET;
4991
0
}
Unexecuted instantiation: int httplib::detail::create_socket<httplib::detail::create_client_socket(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, int, bool, bool, std::__1::function<void (int)>, long, long, long, long, long, long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, httplib::Error&)::{lambda(int, addrinfo&, bool&)#1}>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, int, int, bool, bool, std::__1::function<void (int)>, httplib::detail::create_client_socket(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, int, bool, bool, std::__1::function<void (int)>, long, long, long, long, long, long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, httplib::Error&)::{lambda(int, addrinfo&, bool&)#1}, long)
Unexecuted instantiation: int httplib::detail::create_socket<httplib::Server::create_server_socket(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, int, std::__1::function<void (int)>) const::{lambda(int, addrinfo&, bool&)#1}>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, int, int, bool, bool, std::__1::function<void (int)>, httplib::Server::create_server_socket(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, int, std::__1::function<void (int)>) const::{lambda(int, addrinfo&, bool&)#1}, long)
4992
4993
0
inline void set_nonblocking(socket_t sock, bool nonblocking) {
4994
#ifdef _WIN32
4995
  auto flags = nonblocking ? 1UL : 0UL;
4996
  ioctlsocket(sock, FIONBIO, &flags);
4997
#else
4998
0
  auto flags = fcntl(sock, F_GETFL, 0);
4999
0
  fcntl(sock, F_SETFL,
5000
0
        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
5001
0
#endif
5002
0
}
5003
5004
0
inline bool is_connection_error() {
5005
#ifdef _WIN32
5006
  return WSAGetLastError() != WSAEWOULDBLOCK;
5007
#else
5008
0
  return errno != EINPROGRESS;
5009
0
#endif
5010
0
}
5011
5012
0
inline bool bind_ip_address(socket_t sock, const std::string &host) {
5013
0
  struct addrinfo hints;
5014
0
  struct addrinfo *result;
5015
5016
0
  memset(&hints, 0, sizeof(struct addrinfo));
5017
0
  hints.ai_family = AF_UNSPEC;
5018
0
  hints.ai_socktype = SOCK_STREAM;
5019
0
  hints.ai_protocol = 0;
5020
5021
0
  if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
5022
0
    return false;
5023
0
  }
5024
5025
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5026
5027
0
  auto ret = false;
5028
0
  for (auto rp = result; rp; rp = rp->ai_next) {
5029
0
    const auto &ai = *rp;
5030
0
    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
5031
0
      ret = true;
5032
0
      break;
5033
0
    }
5034
0
  }
5035
5036
0
  return ret;
5037
0
}
5038
5039
#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
5040
#define USE_IF2IP
5041
#endif
5042
5043
#ifdef USE_IF2IP
5044
0
inline std::string if2ip(int address_family, const std::string &ifn) {
5045
0
  struct ifaddrs *ifap;
5046
0
  getifaddrs(&ifap);
5047
0
  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
5048
5049
0
  std::string addr_candidate;
5050
0
  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
5051
0
    if (ifa->ifa_addr && ifn == ifa->ifa_name &&
5052
0
        (AF_UNSPEC == address_family ||
5053
0
         ifa->ifa_addr->sa_family == address_family)) {
5054
0
      if (ifa->ifa_addr->sa_family == AF_INET) {
5055
0
        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
5056
0
        char buf[INET_ADDRSTRLEN];
5057
0
        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
5058
0
          return std::string(buf, INET_ADDRSTRLEN);
5059
0
        }
5060
0
      } else if (ifa->ifa_addr->sa_family == AF_INET6) {
5061
0
        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
5062
0
        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
5063
0
          char buf[INET6_ADDRSTRLEN] = {};
5064
0
          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
5065
            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
5066
0
            auto s6_addr_head = sa->sin6_addr.s6_addr[0];
5067
0
            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
5068
0
              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
5069
0
            } else {
5070
0
              return std::string(buf, INET6_ADDRSTRLEN);
5071
0
            }
5072
0
          }
5073
0
        }
5074
0
      }
5075
0
    }
5076
0
  }
5077
0
  return addr_candidate;
5078
0
}
5079
#endif
5080
5081
inline socket_t create_client_socket(
5082
    const std::string &host, const std::string &ip, int port,
5083
    int address_family, bool tcp_nodelay, bool ipv6_v6only,
5084
    SocketOptions socket_options, time_t connection_timeout_sec,
5085
    time_t connection_timeout_usec, time_t read_timeout_sec,
5086
    time_t read_timeout_usec, time_t write_timeout_sec,
5087
0
    time_t write_timeout_usec, const std::string &intf, Error &error) {
5088
0
  auto sock = create_socket(
5089
0
      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
5090
0
      std::move(socket_options),
5091
0
      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
5092
0
        if (!intf.empty()) {
5093
0
#ifdef USE_IF2IP
5094
0
          auto ip_from_if = if2ip(address_family, intf);
5095
0
          if (ip_from_if.empty()) { ip_from_if = intf; }
5096
0
          if (!bind_ip_address(sock2, ip_from_if)) {
5097
0
            error = Error::BindIPAddress;
5098
0
            return false;
5099
0
          }
5100
0
#endif
5101
0
        }
5102
5103
0
        set_nonblocking(sock2, true);
5104
5105
0
        auto ret =
5106
0
            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
5107
5108
0
        if (ret < 0) {
5109
0
          if (is_connection_error()) {
5110
0
            error = Error::Connection;
5111
0
            return false;
5112
0
          }
5113
0
          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
5114
0
                                             connection_timeout_usec);
5115
0
          if (error != Error::Success) {
5116
0
            if (error == Error::ConnectionTimeout) { quit = true; }
5117
0
            return false;
5118
0
          }
5119
0
        }
5120
5121
0
        set_nonblocking(sock2, false);
5122
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
5123
0
                            read_timeout_usec);
5124
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
5125
0
                            write_timeout_usec);
5126
5127
0
        error = Error::Success;
5128
0
        return true;
5129
0
      },
5130
0
      connection_timeout_sec); // Pass DNS timeout
5131
5132
0
  if (sock != INVALID_SOCKET) {
5133
0
    error = Error::Success;
5134
0
  } else {
5135
0
    if (error == Error::Success) { error = Error::Connection; }
5136
0
  }
5137
5138
0
  return sock;
5139
0
}
5140
5141
inline bool get_ip_and_port(const struct sockaddr_storage &addr,
5142
0
                            socklen_t addr_len, std::string &ip, int &port) {
5143
0
  if (addr.ss_family == AF_INET) {
5144
0
    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
5145
0
  } else if (addr.ss_family == AF_INET6) {
5146
0
    port =
5147
0
        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
5148
0
  } else {
5149
0
    return false;
5150
0
  }
5151
5152
0
  std::array<char, NI_MAXHOST> ipstr{};
5153
0
  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
5154
0
                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
5155
0
                  0, NI_NUMERICHOST)) {
5156
0
    return false;
5157
0
  }
5158
5159
0
  ip = ipstr.data();
5160
0
  return true;
5161
0
}
5162
5163
0
inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
5164
0
  struct sockaddr_storage addr;
5165
0
  socklen_t addr_len = sizeof(addr);
5166
0
  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
5167
0
                   &addr_len)) {
5168
0
    get_ip_and_port(addr, addr_len, ip, port);
5169
0
  }
5170
0
}
5171
5172
0
inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
5173
0
  struct sockaddr_storage addr;
5174
0
  socklen_t addr_len = sizeof(addr);
5175
5176
0
  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
5177
0
                   &addr_len)) {
5178
0
#ifndef _WIN32
5179
0
    if (addr.ss_family == AF_UNIX) {
5180
0
#if defined(__linux__)
5181
0
      struct ucred ucred;
5182
0
      socklen_t len = sizeof(ucred);
5183
0
      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
5184
0
        port = ucred.pid;
5185
0
      }
5186
#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
5187
      pid_t pid;
5188
      socklen_t len = sizeof(pid);
5189
      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
5190
        port = pid;
5191
      }
5192
#endif
5193
0
      return;
5194
0
    }
5195
0
#endif
5196
0
    get_ip_and_port(addr, addr_len, ip, port);
5197
0
  }
5198
0
}
5199
5200
inline constexpr unsigned int str2tag_core(const char *s, size_t l,
5201
677k
                                           unsigned int h) {
5202
677k
  return (l == 0)
5203
677k
             ? h
5204
677k
             : str2tag_core(
5205
621k
                   s + 1, l - 1,
5206
                   // Unsets the 6 high bits of h, therefore no overflow happens
5207
621k
                   (((std::numeric_limits<unsigned int>::max)() >> 6) &
5208
621k
                    h * 33) ^
5209
621k
                       static_cast<unsigned char>(*s));
5210
677k
}
5211
5212
56.9k
inline unsigned int str2tag(const std::string &s) {
5213
56.9k
  return str2tag_core(s.data(), s.size(), 0);
5214
56.9k
}
5215
5216
namespace udl {
5217
5218
0
inline constexpr unsigned int operator""_t(const char *s, size_t l) {
5219
0
  return str2tag_core(s, l, 0);
5220
0
}
5221
5222
} // namespace udl
5223
5224
inline std::string
5225
find_content_type(const std::string &path,
5226
                  const std::map<std::string, std::string> &user_data,
5227
0
                  const std::string &default_content_type) {
5228
0
  auto ext = file_extension(path);
5229
5230
0
  auto it = user_data.find(ext);
5231
0
  if (it != user_data.end()) { return it->second; }
5232
5233
0
  using udl::operator""_t;
5234
5235
0
  switch (str2tag(ext)) {
5236
0
  default: return default_content_type;
5237
5238
0
  case "css"_t: return "text/css";
5239
0
  case "csv"_t: return "text/csv";
5240
0
  case "htm"_t:
5241
0
  case "html"_t: return "text/html";
5242
0
  case "js"_t:
5243
0
  case "mjs"_t: return "text/javascript";
5244
0
  case "txt"_t: return "text/plain";
5245
0
  case "vtt"_t: return "text/vtt";
5246
5247
0
  case "apng"_t: return "image/apng";
5248
0
  case "avif"_t: return "image/avif";
5249
0
  case "bmp"_t: return "image/bmp";
5250
0
  case "gif"_t: return "image/gif";
5251
0
  case "png"_t: return "image/png";
5252
0
  case "svg"_t: return "image/svg+xml";
5253
0
  case "webp"_t: return "image/webp";
5254
0
  case "ico"_t: return "image/x-icon";
5255
0
  case "tif"_t: return "image/tiff";
5256
0
  case "tiff"_t: return "image/tiff";
5257
0
  case "jpg"_t:
5258
0
  case "jpeg"_t: return "image/jpeg";
5259
5260
0
  case "mp4"_t: return "video/mp4";
5261
0
  case "mpeg"_t: return "video/mpeg";
5262
0
  case "webm"_t: return "video/webm";
5263
5264
0
  case "mp3"_t: return "audio/mp3";
5265
0
  case "mpga"_t: return "audio/mpeg";
5266
0
  case "weba"_t: return "audio/webm";
5267
0
  case "wav"_t: return "audio/wave";
5268
5269
0
  case "otf"_t: return "font/otf";
5270
0
  case "ttf"_t: return "font/ttf";
5271
0
  case "woff"_t: return "font/woff";
5272
0
  case "woff2"_t: return "font/woff2";
5273
5274
0
  case "7z"_t: return "application/x-7z-compressed";
5275
0
  case "atom"_t: return "application/atom+xml";
5276
0
  case "pdf"_t: return "application/pdf";
5277
0
  case "json"_t: return "application/json";
5278
0
  case "rss"_t: return "application/rss+xml";
5279
0
  case "tar"_t: return "application/x-tar";
5280
0
  case "xht"_t:
5281
0
  case "xhtml"_t: return "application/xhtml+xml";
5282
0
  case "xslt"_t: return "application/xslt+xml";
5283
0
  case "xml"_t: return "application/xml";
5284
0
  case "gz"_t: return "application/gzip";
5285
0
  case "zip"_t: return "application/zip";
5286
0
  case "wasm"_t: return "application/wasm";
5287
0
  }
5288
0
}
5289
5290
1.52k
inline bool can_compress_content_type(const std::string &content_type) {
5291
1.52k
  using udl::operator""_t;
5292
5293
1.52k
  auto tag = str2tag(content_type);
5294
5295
1.52k
  switch (tag) {
5296
0
  case "image/svg+xml"_t:
5297
0
  case "application/javascript"_t:
5298
0
  case "application/json"_t:
5299
0
  case "application/xml"_t:
5300
0
  case "application/protobuf"_t:
5301
0
  case "application/xhtml+xml"_t: return true;
5302
5303
0
  case "text/event-stream"_t: return false;
5304
5305
1.52k
  default: return !content_type.rfind("text/", 0);
5306
1.52k
  }
5307
1.52k
}
5308
5309
1.52k
inline EncodingType encoding_type(const Request &req, const Response &res) {
5310
1.52k
  auto ret =
5311
1.52k
      detail::can_compress_content_type(res.get_header_value("Content-Type"));
5312
1.52k
  if (!ret) { return EncodingType::None; }
5313
5314
1.31k
  const auto &s = req.get_header_value("Accept-Encoding");
5315
1.31k
  (void)(s);
5316
5317
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
5318
  // TODO: 'Accept-Encoding' has br, not br;q=0
5319
  ret = s.find("br") != std::string::npos;
5320
  if (ret) { return EncodingType::Brotli; }
5321
#endif
5322
5323
1.31k
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
5324
  // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
5325
1.31k
  ret = s.find("gzip") != std::string::npos;
5326
1.31k
  if (ret) { return EncodingType::Gzip; }
5327
1.29k
#endif
5328
5329
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
5330
  // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
5331
  ret = s.find("zstd") != std::string::npos;
5332
  if (ret) { return EncodingType::Zstd; }
5333
#endif
5334
5335
1.29k
  return EncodingType::None;
5336
1.31k
}
5337
5338
inline bool nocompressor::compress(const char *data, size_t data_length,
5339
0
                                   bool /*last*/, Callback callback) {
5340
0
  if (!data_length) { return true; }
5341
0
  return callback(data, data_length);
5342
0
}
5343
5344
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
5345
18
inline gzip_compressor::gzip_compressor() {
5346
18
  std::memset(&strm_, 0, sizeof(strm_));
5347
18
  strm_.zalloc = Z_NULL;
5348
18
  strm_.zfree = Z_NULL;
5349
18
  strm_.opaque = Z_NULL;
5350
5351
18
  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
5352
18
                           Z_DEFAULT_STRATEGY) == Z_OK;
5353
18
}
5354
5355
18
inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
5356
5357
inline bool gzip_compressor::compress(const char *data, size_t data_length,
5358
18
                                      bool last, Callback callback) {
5359
18
  assert(is_valid_);
5360
5361
18
  do {
5362
18
    constexpr size_t max_avail_in =
5363
18
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
5364
5365
18
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
5366
18
        (std::min)(data_length, max_avail_in));
5367
18
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
5368
5369
18
    data_length -= strm_.avail_in;
5370
18
    data += strm_.avail_in;
5371
5372
18
    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
5373
18
    auto ret = Z_OK;
5374
5375
18
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5376
18
    do {
5377
18
      strm_.avail_out = static_cast<uInt>(buff.size());
5378
18
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
5379
5380
18
      ret = deflate(&strm_, flush);
5381
18
      if (ret == Z_STREAM_ERROR) { return false; }
5382
5383
18
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
5384
0
        return false;
5385
0
      }
5386
18
    } while (strm_.avail_out == 0);
5387
5388
18
    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
5389
18
           (flush == Z_NO_FLUSH && ret == Z_OK));
5390
18
    assert(strm_.avail_in == 0);
5391
18
  } while (data_length > 0);
5392
5393
18
  return true;
5394
18
}
5395
5396
672
inline gzip_decompressor::gzip_decompressor() {
5397
672
  std::memset(&strm_, 0, sizeof(strm_));
5398
672
  strm_.zalloc = Z_NULL;
5399
672
  strm_.zfree = Z_NULL;
5400
672
  strm_.opaque = Z_NULL;
5401
5402
  // 15 is the value of wbits, which should be at the maximum possible value
5403
  // to ensure that any gzip stream can be decoded. The offset of 32 specifies
5404
  // that the stream type should be automatically detected either gzip or
5405
  // deflate.
5406
672
  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
5407
672
}
5408
5409
672
inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
5410
5411
672
inline bool gzip_decompressor::is_valid() const { return is_valid_; }
5412
5413
inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
5414
2.67k
                                          Callback callback) {
5415
2.67k
  assert(is_valid_);
5416
5417
2.67k
  auto ret = Z_OK;
5418
5419
2.67k
  do {
5420
2.67k
    constexpr size_t max_avail_in =
5421
2.67k
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
5422
5423
2.67k
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
5424
2.67k
        (std::min)(data_length, max_avail_in));
5425
2.67k
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
5426
5427
2.67k
    data_length -= strm_.avail_in;
5428
2.67k
    data += strm_.avail_in;
5429
5430
2.67k
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5431
91.0k
    while (strm_.avail_in > 0 && ret == Z_OK) {
5432
88.4k
      strm_.avail_out = static_cast<uInt>(buff.size());
5433
88.4k
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
5434
5435
88.4k
      ret = inflate(&strm_, Z_NO_FLUSH);
5436
5437
88.4k
      assert(ret != Z_STREAM_ERROR);
5438
88.4k
      switch (ret) {
5439
1
      case Z_NEED_DICT:
5440
31
      case Z_DATA_ERROR:
5441
31
      case Z_MEM_ERROR: inflateEnd(&strm_); return false;
5442
88.4k
      }
5443
5444
88.4k
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
5445
55
        return false;
5446
55
      }
5447
88.4k
    }
5448
5449
2.59k
    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
5450
5451
2.59k
  } while (data_length > 0);
5452
5453
2.59k
  return true;
5454
2.67k
}
5455
#endif
5456
5457
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
5458
inline brotli_compressor::brotli_compressor() {
5459
  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
5460
}
5461
5462
inline brotli_compressor::~brotli_compressor() {
5463
  BrotliEncoderDestroyInstance(state_);
5464
}
5465
5466
inline bool brotli_compressor::compress(const char *data, size_t data_length,
5467
                                        bool last, Callback callback) {
5468
  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5469
5470
  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
5471
  auto available_in = data_length;
5472
  auto next_in = reinterpret_cast<const uint8_t *>(data);
5473
5474
  for (;;) {
5475
    if (last) {
5476
      if (BrotliEncoderIsFinished(state_)) { break; }
5477
    } else {
5478
      if (!available_in) { break; }
5479
    }
5480
5481
    auto available_out = buff.size();
5482
    auto next_out = buff.data();
5483
5484
    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
5485
                                     &available_out, &next_out, nullptr)) {
5486
      return false;
5487
    }
5488
5489
    auto output_bytes = buff.size() - available_out;
5490
    if (output_bytes) {
5491
      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
5492
    }
5493
  }
5494
5495
  return true;
5496
}
5497
5498
inline brotli_decompressor::brotli_decompressor() {
5499
  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
5500
  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
5501
                        : BROTLI_DECODER_RESULT_ERROR;
5502
}
5503
5504
inline brotli_decompressor::~brotli_decompressor() {
5505
  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
5506
}
5507
5508
inline bool brotli_decompressor::is_valid() const { return decoder_s; }
5509
5510
inline bool brotli_decompressor::decompress(const char *data,
5511
                                            size_t data_length,
5512
                                            Callback callback) {
5513
  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
5514
      decoder_r == BROTLI_DECODER_RESULT_ERROR) {
5515
    return 0;
5516
  }
5517
5518
  auto next_in = reinterpret_cast<const uint8_t *>(data);
5519
  size_t avail_in = data_length;
5520
  size_t total_out;
5521
5522
  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
5523
5524
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5525
  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
5526
    char *next_out = buff.data();
5527
    size_t avail_out = buff.size();
5528
5529
    decoder_r = BrotliDecoderDecompressStream(
5530
        decoder_s, &avail_in, &next_in, &avail_out,
5531
        reinterpret_cast<uint8_t **>(&next_out), &total_out);
5532
5533
    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
5534
5535
    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
5536
  }
5537
5538
  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
5539
         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
5540
}
5541
#endif
5542
5543
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
5544
inline zstd_compressor::zstd_compressor() {
5545
  ctx_ = ZSTD_createCCtx();
5546
  ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
5547
}
5548
5549
inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
5550
5551
inline bool zstd_compressor::compress(const char *data, size_t data_length,
5552
                                      bool last, Callback callback) {
5553
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5554
5555
  ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
5556
  ZSTD_inBuffer input = {data, data_length, 0};
5557
5558
  bool finished;
5559
  do {
5560
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
5561
    size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
5562
5563
    if (ZSTD_isError(remaining)) { return false; }
5564
5565
    if (!callback(buff.data(), output.pos)) { return false; }
5566
5567
    finished = last ? (remaining == 0) : (input.pos == input.size);
5568
5569
  } while (!finished);
5570
5571
  return true;
5572
}
5573
5574
inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
5575
5576
inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
5577
5578
inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
5579
5580
inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
5581
                                          Callback callback) {
5582
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5583
  ZSTD_inBuffer input = {data, data_length, 0};
5584
5585
  while (input.pos < input.size) {
5586
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
5587
    size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
5588
5589
    if (ZSTD_isError(remaining)) { return false; }
5590
5591
    if (!callback(buff.data(), output.pos)) { return false; }
5592
  }
5593
5594
  return true;
5595
}
5596
#endif
5597
5598
inline std::unique_ptr<decompressor>
5599
699
create_decompressor(const std::string &encoding) {
5600
699
  std::unique_ptr<decompressor> decompressor;
5601
5602
699
  if (encoding == "gzip" || encoding == "deflate") {
5603
672
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
5604
672
    decompressor = detail::make_unique<gzip_decompressor>();
5605
672
#endif
5606
672
  } else if (encoding.find("br") != std::string::npos) {
5607
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
5608
    decompressor = detail::make_unique<brotli_decompressor>();
5609
#endif
5610
14
  } else if (encoding == "zstd" || encoding.find("zstd") != std::string::npos) {
5611
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
5612
    decompressor = detail::make_unique<zstd_decompressor>();
5613
#endif
5614
12
  }
5615
5616
699
  return decompressor;
5617
699
}
5618
5619
55.4k
inline bool is_prohibited_header_name(const std::string &name) {
5620
55.4k
  using udl::operator""_t;
5621
5622
55.4k
  switch (str2tag(name)) {
5623
0
  case "REMOTE_ADDR"_t:
5624
0
  case "REMOTE_PORT"_t:
5625
0
  case "LOCAL_ADDR"_t:
5626
0
  case "LOCAL_PORT"_t: return true;
5627
55.4k
  default: return false;
5628
55.4k
  }
5629
55.4k
}
5630
5631
16.5k
inline bool has_header(const Headers &headers, const std::string &key) {
5632
16.5k
  if (is_prohibited_header_name(key)) { return false; }
5633
16.5k
  return headers.find(key) != headers.end();
5634
16.5k
}
5635
5636
inline const char *get_header_value(const Headers &headers,
5637
                                    const std::string &key, const char *def,
5638
38.9k
                                    size_t id) {
5639
38.9k
  if (is_prohibited_header_name(key)) {
5640
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5641
0
    std::string msg = "Prohibited header name '" + key + "' is specified.";
5642
0
    throw std::invalid_argument(msg);
5643
#else
5644
    return "";
5645
#endif
5646
0
  }
5647
5648
38.9k
  auto rng = headers.equal_range(key);
5649
38.9k
  auto it = rng.first;
5650
38.9k
  std::advance(it, static_cast<ssize_t>(id));
5651
38.9k
  if (it != rng.second) { return it->second.c_str(); }
5652
28.8k
  return def;
5653
38.9k
}
5654
5655
6.18k
inline bool read_headers(Stream &strm, Headers &headers) {
5656
6.18k
  const auto bufsiz = 2048;
5657
6.18k
  char buf[bufsiz];
5658
6.18k
  stream_line_reader line_reader(strm, buf, bufsiz);
5659
5660
6.18k
  size_t header_count = 0;
5661
5662
19.0k
  for (;;) {
5663
19.0k
    if (!line_reader.getline()) { return false; }
5664
5665
    // Check if the line ends with CRLF.
5666
17.9k
    auto line_terminator_len = 2;
5667
17.9k
    if (line_reader.end_with_crlf()) {
5668
      // Blank line indicates end of headers.
5669
17.9k
      if (line_reader.size() == 2) { break; }
5670
17.9k
    } else {
5671
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5672
      // Blank line indicates end of headers.
5673
      if (line_reader.size() == 1) { break; }
5674
      line_terminator_len = 1;
5675
#else
5676
74
      continue; // Skip invalid line.
5677
74
#endif
5678
74
    }
5679
5680
13.0k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
5681
5682
    // Check header count limit
5683
13.0k
    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
5684
5685
    // Exclude line terminator
5686
13.0k
    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
5687
5688
13.0k
    if (!parse_header(line_reader.ptr(), end,
5689
13.0k
                      [&](const std::string &key, const std::string &val) {
5690
12.8k
                        headers.emplace(key, val);
5691
12.8k
                      })) {
5692
243
      return false;
5693
243
    }
5694
5695
12.8k
    header_count++;
5696
12.8k
  }
5697
5698
4.83k
  return true;
5699
6.18k
}
5700
5701
inline bool read_content_with_length(Stream &strm, size_t len,
5702
                                     DownloadProgress progress,
5703
1.59k
                                     ContentReceiverWithProgress out) {
5704
1.59k
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5705
5706
1.59k
  detail::BodyReader br;
5707
1.59k
  br.stream = &strm;
5708
1.59k
  br.content_length = len;
5709
1.59k
  br.chunked = false;
5710
1.59k
  br.bytes_read = 0;
5711
1.59k
  br.last_error = Error::Success;
5712
5713
1.59k
  size_t r = 0;
5714
3.91k
  while (r < len) {
5715
3.84k
    auto read_len = static_cast<size_t>(len - r);
5716
3.84k
    auto to_read = (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ);
5717
3.84k
    auto n = detail::read_body_content(&strm, br, buf, to_read);
5718
3.84k
    if (n <= 0) { return false; }
5719
5720
2.58k
    if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
5721
2.32k
    r += static_cast<size_t>(n);
5722
5723
2.32k
    if (progress) {
5724
0
      if (!progress(r, len)) { return false; }
5725
0
    }
5726
2.32k
  }
5727
5728
68
  return true;
5729
1.59k
}
5730
5731
0
inline void skip_content_with_length(Stream &strm, size_t len) {
5732
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5733
0
  size_t r = 0;
5734
0
  while (r < len) {
5735
0
    auto read_len = static_cast<size_t>(len - r);
5736
0
    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
5737
0
    if (n <= 0) { return; }
5738
0
    r += static_cast<size_t>(n);
5739
0
  }
5740
0
}
5741
5742
enum class ReadContentResult {
5743
  Success,         // Successfully read the content
5744
  PayloadTooLarge, // The content exceeds the specified payload limit
5745
  Error            // An error occurred while reading the content
5746
};
5747
5748
inline ReadContentResult
5749
read_content_without_length(Stream &strm, size_t payload_max_length,
5750
0
                            ContentReceiverWithProgress out) {
5751
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5752
0
  size_t r = 0;
5753
0
  for (;;) {
5754
0
    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
5755
0
    if (n == 0) { return ReadContentResult::Success; }
5756
0
    if (n < 0) { return ReadContentResult::Error; }
5757
5758
    // Check if adding this data would exceed the payload limit
5759
0
    if (r > payload_max_length ||
5760
0
        payload_max_length - r < static_cast<size_t>(n)) {
5761
0
      return ReadContentResult::PayloadTooLarge;
5762
0
    }
5763
5764
0
    if (!out(buf, static_cast<size_t>(n), r, 0)) {
5765
0
      return ReadContentResult::Error;
5766
0
    }
5767
0
    r += static_cast<size_t>(n);
5768
0
  }
5769
5770
0
  return ReadContentResult::Success;
5771
0
}
5772
5773
template <typename T>
5774
inline ReadContentResult read_content_chunked(Stream &strm, T &x,
5775
                                              size_t payload_max_length,
5776
1.00k
                                              ContentReceiverWithProgress out) {
5777
1.00k
  detail::ChunkedDecoder dec(strm);
5778
5779
1.00k
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5780
1.00k
  size_t total_len = 0;
5781
5782
5.33k
  for (;;) {
5783
5.33k
    size_t chunk_offset = 0;
5784
5.33k
    size_t chunk_total = 0;
5785
5.33k
    auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
5786
5.33k
    if (n < 0) { return ReadContentResult::Error; }
5787
5788
4.89k
    if (n == 0) {
5789
546
      if (!dec.parse_trailers_into(x.trailers, x.headers)) {
5790
525
        return ReadContentResult::Error;
5791
525
      }
5792
21
      return ReadContentResult::Success;
5793
546
    }
5794
5795
4.34k
    if (total_len > payload_max_length ||
5796
4.34k
        payload_max_length - total_len < static_cast<size_t>(n)) {
5797
0
      return ReadContentResult::PayloadTooLarge;
5798
0
    }
5799
5800
4.34k
    if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total)) {
5801
16
      return ReadContentResult::Error;
5802
16
    }
5803
5804
4.33k
    total_len += static_cast<size_t>(n);
5805
4.33k
  }
5806
1.00k
}
httplib::detail::ReadContentResult httplib::detail::read_content_chunked<httplib::Request>(httplib::Stream&, httplib::Request&, unsigned long, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)
Line
Count
Source
5776
1.00k
                                              ContentReceiverWithProgress out) {
5777
1.00k
  detail::ChunkedDecoder dec(strm);
5778
5779
1.00k
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5780
1.00k
  size_t total_len = 0;
5781
5782
5.33k
  for (;;) {
5783
5.33k
    size_t chunk_offset = 0;
5784
5.33k
    size_t chunk_total = 0;
5785
5.33k
    auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
5786
5.33k
    if (n < 0) { return ReadContentResult::Error; }
5787
5788
4.89k
    if (n == 0) {
5789
546
      if (!dec.parse_trailers_into(x.trailers, x.headers)) {
5790
525
        return ReadContentResult::Error;
5791
525
      }
5792
21
      return ReadContentResult::Success;
5793
546
    }
5794
5795
4.34k
    if (total_len > payload_max_length ||
5796
4.34k
        payload_max_length - total_len < static_cast<size_t>(n)) {
5797
0
      return ReadContentResult::PayloadTooLarge;
5798
0
    }
5799
5800
4.34k
    if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total)) {
5801
16
      return ReadContentResult::Error;
5802
16
    }
5803
5804
4.33k
    total_len += static_cast<size_t>(n);
5805
4.33k
  }
5806
1.00k
}
Unexecuted instantiation: httplib::detail::ReadContentResult httplib::detail::read_content_chunked<httplib::Response>(httplib::Stream&, httplib::Response&, unsigned long, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)
5807
5808
5.92k
inline bool is_chunked_transfer_encoding(const Headers &headers) {
5809
5.92k
  return case_ignore::equal(
5810
5.92k
      get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
5811
5.92k
}
5812
5813
template <typename T, typename U>
5814
bool prepare_content_receiver(T &x, int &status,
5815
                              ContentReceiverWithProgress receiver,
5816
2.64k
                              bool decompress, U callback) {
5817
2.64k
  if (decompress) {
5818
2.64k
    std::string encoding = x.get_header_value("Content-Encoding");
5819
2.64k
    std::unique_ptr<decompressor> decompressor;
5820
5821
2.64k
    if (!encoding.empty()) {
5822
699
      decompressor = detail::create_decompressor(encoding);
5823
699
      if (!decompressor) {
5824
        // Unsupported encoding or no support compiled in
5825
27
        status = StatusCode::UnsupportedMediaType_415;
5826
27
        return false;
5827
27
      }
5828
699
    }
5829
5830
2.61k
    if (decompressor) {
5831
672
      if (decompressor->is_valid()) {
5832
672
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
5833
2.67k
                                              size_t off, size_t len) {
5834
2.67k
          return decompressor->decompress(buf, n,
5835
88.4k
                                          [&](const char *buf2, size_t n2) {
5836
88.4k
                                            return receiver(buf2, n2, off, len);
5837
88.4k
                                          });
5838
2.67k
        };
5839
672
        return callback(std::move(out));
5840
672
      } else {
5841
0
        status = StatusCode::InternalServerError_500;
5842
0
        return false;
5843
0
      }
5844
672
    }
5845
2.61k
  }
5846
5847
1.94k
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
5848
4.25k
                                        size_t len) {
5849
4.25k
    return receiver(buf, n, off, len);
5850
4.25k
  };
5851
1.94k
  return callback(std::move(out));
5852
2.64k
}
bool httplib::detail::prepare_content_receiver<httplib::Request, httplib::detail::read_content<httplib::Request>(httplib::Stream&, httplib::Request&, unsigned long, int&, std::__1::function<bool (unsigned long, unsigned long)>, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool)::{lambda(std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)> const&)#1}>(httplib::Request&, int&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool, httplib::detail::read_content<httplib::Request>(httplib::Stream&, httplib::Request&, unsigned long, int&, std::__1::function<bool (unsigned long, unsigned long)>, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool)::{lambda(std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)> const&)#1})
Line
Count
Source
5816
2.64k
                              bool decompress, U callback) {
5817
2.64k
  if (decompress) {
5818
2.64k
    std::string encoding = x.get_header_value("Content-Encoding");
5819
2.64k
    std::unique_ptr<decompressor> decompressor;
5820
5821
2.64k
    if (!encoding.empty()) {
5822
699
      decompressor = detail::create_decompressor(encoding);
5823
699
      if (!decompressor) {
5824
        // Unsupported encoding or no support compiled in
5825
27
        status = StatusCode::UnsupportedMediaType_415;
5826
27
        return false;
5827
27
      }
5828
699
    }
5829
5830
2.61k
    if (decompressor) {
5831
672
      if (decompressor->is_valid()) {
5832
672
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
5833
672
                                              size_t off, size_t len) {
5834
672
          return decompressor->decompress(buf, n,
5835
672
                                          [&](const char *buf2, size_t n2) {
5836
672
                                            return receiver(buf2, n2, off, len);
5837
672
                                          });
5838
672
        };
5839
672
        return callback(std::move(out));
5840
672
      } else {
5841
0
        status = StatusCode::InternalServerError_500;
5842
0
        return false;
5843
0
      }
5844
672
    }
5845
2.61k
  }
5846
5847
1.94k
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
5848
1.94k
                                        size_t len) {
5849
1.94k
    return receiver(buf, n, off, len);
5850
1.94k
  };
5851
1.94k
  return callback(std::move(out));
5852
2.64k
}
Unexecuted instantiation: bool httplib::detail::prepare_content_receiver<httplib::Response, httplib::detail::read_content<httplib::Response>(httplib::Stream&, httplib::Response&, unsigned long, int&, std::__1::function<bool (unsigned long, unsigned long)>, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool)::{lambda(std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)> const&)#1}>(httplib::Response&, int&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool, httplib::detail::read_content<httplib::Response>(httplib::Stream&, httplib::Response&, unsigned long, int&, std::__1::function<bool (unsigned long, unsigned long)>, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool)::{lambda(std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)> const&)#1})
5853
5854
template <typename T>
5855
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
5856
                  DownloadProgress progress,
5857
2.64k
                  ContentReceiverWithProgress receiver, bool decompress) {
5858
2.64k
  return prepare_content_receiver(
5859
2.64k
      x, status, std::move(receiver), decompress,
5860
2.64k
      [&](const ContentReceiverWithProgress &out) {
5861
2.61k
        auto ret = true;
5862
2.61k
        auto exceed_payload_max_length = false;
5863
5864
2.61k
        if (is_chunked_transfer_encoding(x.headers)) {
5865
1.00k
          auto result = read_content_chunked(strm, x, payload_max_length, out);
5866
1.00k
          if (result == ReadContentResult::Success) {
5867
21
            ret = true;
5868
985
          } else if (result == ReadContentResult::PayloadTooLarge) {
5869
0
            exceed_payload_max_length = true;
5870
0
            ret = false;
5871
985
          } else {
5872
985
            ret = false;
5873
985
          }
5874
1.60k
        } else if (!has_header(x.headers, "Content-Length")) {
5875
0
          auto result =
5876
0
              read_content_without_length(strm, payload_max_length, out);
5877
0
          if (result == ReadContentResult::Success) {
5878
0
            ret = true;
5879
0
          } else if (result == ReadContentResult::PayloadTooLarge) {
5880
0
            exceed_payload_max_length = true;
5881
0
            ret = false;
5882
0
          } else {
5883
0
            ret = false;
5884
0
          }
5885
1.60k
        } else {
5886
1.60k
          auto is_invalid_value = false;
5887
1.60k
          auto len = get_header_value_u64(x.headers, "Content-Length",
5888
1.60k
                                          (std::numeric_limits<size_t>::max)(),
5889
1.60k
                                          0, is_invalid_value);
5890
5891
1.60k
          if (is_invalid_value) {
5892
7
            ret = false;
5893
1.60k
          } else if (len > payload_max_length) {
5894
0
            exceed_payload_max_length = true;
5895
0
            skip_content_with_length(strm, len);
5896
0
            ret = false;
5897
1.60k
          } else if (len > 0) {
5898
1.59k
            ret = read_content_with_length(strm, len, std::move(progress), out);
5899
1.59k
          }
5900
1.60k
        }
5901
5902
2.61k
        if (!ret) {
5903
2.51k
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
5904
2.51k
                                             : StatusCode::BadRequest_400;
5905
2.51k
        }
5906
2.61k
        return ret;
5907
2.61k
      });
5908
2.64k
}
bool httplib::detail::read_content<httplib::Request>(httplib::Stream&, httplib::Request&, unsigned long, int&, std::__1::function<bool (unsigned long, unsigned long)>, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool)
Line
Count
Source
5857
2.64k
                  ContentReceiverWithProgress receiver, bool decompress) {
5858
2.64k
  return prepare_content_receiver(
5859
2.64k
      x, status, std::move(receiver), decompress,
5860
2.64k
      [&](const ContentReceiverWithProgress &out) {
5861
2.64k
        auto ret = true;
5862
2.64k
        auto exceed_payload_max_length = false;
5863
5864
2.64k
        if (is_chunked_transfer_encoding(x.headers)) {
5865
2.64k
          auto result = read_content_chunked(strm, x, payload_max_length, out);
5866
2.64k
          if (result == ReadContentResult::Success) {
5867
2.64k
            ret = true;
5868
2.64k
          } else if (result == ReadContentResult::PayloadTooLarge) {
5869
2.64k
            exceed_payload_max_length = true;
5870
2.64k
            ret = false;
5871
2.64k
          } else {
5872
2.64k
            ret = false;
5873
2.64k
          }
5874
2.64k
        } else if (!has_header(x.headers, "Content-Length")) {
5875
2.64k
          auto result =
5876
2.64k
              read_content_without_length(strm, payload_max_length, out);
5877
2.64k
          if (result == ReadContentResult::Success) {
5878
2.64k
            ret = true;
5879
2.64k
          } else if (result == ReadContentResult::PayloadTooLarge) {
5880
2.64k
            exceed_payload_max_length = true;
5881
2.64k
            ret = false;
5882
2.64k
          } else {
5883
2.64k
            ret = false;
5884
2.64k
          }
5885
2.64k
        } else {
5886
2.64k
          auto is_invalid_value = false;
5887
2.64k
          auto len = get_header_value_u64(x.headers, "Content-Length",
5888
2.64k
                                          (std::numeric_limits<size_t>::max)(),
5889
2.64k
                                          0, is_invalid_value);
5890
5891
2.64k
          if (is_invalid_value) {
5892
2.64k
            ret = false;
5893
2.64k
          } else if (len > payload_max_length) {
5894
2.64k
            exceed_payload_max_length = true;
5895
2.64k
            skip_content_with_length(strm, len);
5896
2.64k
            ret = false;
5897
2.64k
          } else if (len > 0) {
5898
2.64k
            ret = read_content_with_length(strm, len, std::move(progress), out);
5899
2.64k
          }
5900
2.64k
        }
5901
5902
2.64k
        if (!ret) {
5903
2.64k
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
5904
2.64k
                                             : StatusCode::BadRequest_400;
5905
2.64k
        }
5906
2.64k
        return ret;
5907
2.64k
      });
5908
2.64k
}
Unexecuted instantiation: bool httplib::detail::read_content<httplib::Response>(httplib::Stream&, httplib::Response&, unsigned long, int&, std::__1::function<bool (unsigned long, unsigned long)>, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>, bool)
5909
5910
inline ssize_t write_request_line(Stream &strm, const std::string &method,
5911
0
                                  const std::string &path) {
5912
0
  std::string s = method;
5913
0
  s += ' ';
5914
0
  s += path;
5915
0
  s += " HTTP/1.1\r\n";
5916
0
  return strm.write(s.data(), s.size());
5917
0
}
5918
5919
6.53k
inline ssize_t write_response_line(Stream &strm, int status) {
5920
6.53k
  std::string s = "HTTP/1.1 ";
5921
6.53k
  s += std::to_string(status);
5922
6.53k
  s += ' ';
5923
6.53k
  s += httplib::status_message(status);
5924
6.53k
  s += "\r\n";
5925
6.53k
  return strm.write(s.data(), s.size());
5926
6.53k
}
5927
5928
6.53k
inline ssize_t write_headers(Stream &strm, const Headers &headers) {
5929
6.53k
  ssize_t write_len = 0;
5930
15.2k
  for (const auto &x : headers) {
5931
15.2k
    std::string s;
5932
15.2k
    s = x.first;
5933
15.2k
    s += ": ";
5934
15.2k
    s += x.second;
5935
15.2k
    s += "\r\n";
5936
5937
15.2k
    auto len = strm.write(s.data(), s.size());
5938
15.2k
    if (len < 0) { return len; }
5939
15.2k
    write_len += len;
5940
15.2k
  }
5941
6.53k
  auto len = strm.write("\r\n");
5942
6.53k
  if (len < 0) { return len; }
5943
6.53k
  write_len += len;
5944
6.53k
  return write_len;
5945
6.53k
}
5946
5947
7.97k
inline bool write_data(Stream &strm, const char *d, size_t l) {
5948
7.97k
  size_t offset = 0;
5949
15.9k
  while (offset < l) {
5950
7.97k
    auto length = strm.write(d + offset, l - offset);
5951
7.97k
    if (length < 0) { return false; }
5952
7.97k
    offset += static_cast<size_t>(length);
5953
7.97k
  }
5954
7.97k
  return true;
5955
7.97k
}
5956
5957
template <typename T>
5958
inline bool write_content_with_progress(Stream &strm,
5959
                                        const ContentProvider &content_provider,
5960
                                        size_t offset, size_t length,
5961
                                        T is_shutting_down,
5962
                                        const UploadProgress &upload_progress,
5963
0
                                        Error &error) {
5964
0
  size_t end_offset = offset + length;
5965
0
  size_t start_offset = offset;
5966
0
  auto ok = true;
5967
0
  DataSink data_sink;
5968
5969
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
5970
0
    if (ok) {
5971
0
      if (write_data(strm, d, l)) {
5972
0
        offset += l;
5973
5974
0
        if (upload_progress && length > 0) {
5975
0
          size_t current_written = offset - start_offset;
5976
0
          if (!upload_progress(current_written, length)) {
5977
0
            ok = false;
5978
0
            return false;
5979
0
          }
5980
0
        }
5981
0
      } else {
5982
0
        ok = false;
5983
0
      }
5984
0
    }
5985
0
    return ok;
5986
0
  };
5987
5988
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
5989
5990
0
  while (offset < end_offset && !is_shutting_down()) {
5991
0
    if (!strm.wait_writable()) {
5992
0
      error = Error::Write;
5993
0
      return false;
5994
0
    } else if (!content_provider(offset, end_offset - offset, data_sink)) {
5995
0
      error = Error::Canceled;
5996
0
      return false;
5997
0
    } else if (!ok) {
5998
0
      error = Error::Write;
5999
0
      return false;
6000
0
    }
6001
0
  }
6002
6003
0
  error = Error::Success;
6004
0
  return true;
6005
0
}
Unexecuted instantiation: bool httplib::detail::write_content_with_progress<httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1}>(httplib::Stream&, std::__1::function<bool (unsigned long, unsigned long, httplib::DataSink&)> const&, unsigned long, unsigned long, httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1}, httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1}<bool (unsigned long, unsigned long)> const&, httplib::Error&)
Unexecuted instantiation: bool httplib::detail::write_content_with_progress<httplib::ClientImpl::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Error&) const::{lambda()#1}>(httplib::Stream&, std::__1::function<bool (unsigned long, unsigned long, httplib::DataSink&)> const&, unsigned long, unsigned long, httplib::ClientImpl::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Error&) const::{lambda()#1}, std::__1<bool (unsigned long, unsigned long)> const&, httplib::Error&)
6006
6007
template <typename T>
6008
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
6009
                          size_t offset, size_t length, T is_shutting_down,
6010
0
                          Error &error) {
6011
0
  return write_content_with_progress<T>(strm, content_provider, offset, length,
6012
0
                                        is_shutting_down, nullptr, error);
6013
0
}
6014
6015
template <typename T>
6016
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
6017
                          size_t offset, size_t length,
6018
0
                          const T &is_shutting_down) {
6019
0
  auto error = Error::Success;
6020
0
  return write_content(strm, content_provider, offset, length, is_shutting_down,
6021
0
                       error);
6022
0
}
6023
6024
template <typename T>
6025
inline bool
6026
write_content_without_length(Stream &strm,
6027
                             const ContentProvider &content_provider,
6028
0
                             const T &is_shutting_down) {
6029
0
  size_t offset = 0;
6030
0
  auto data_available = true;
6031
0
  auto ok = true;
6032
0
  DataSink data_sink;
6033
6034
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
6035
0
    if (ok) {
6036
0
      offset += l;
6037
0
      if (!write_data(strm, d, l)) { ok = false; }
6038
0
    }
6039
0
    return ok;
6040
0
  };
6041
6042
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
6043
6044
0
  data_sink.done = [&](void) { data_available = false; };
6045
6046
0
  while (data_available && !is_shutting_down()) {
6047
0
    if (!strm.wait_writable()) {
6048
0
      return false;
6049
0
    } else if (!content_provider(offset, 0, data_sink)) {
6050
0
      return false;
6051
0
    } else if (!ok) {
6052
0
      return false;
6053
0
    }
6054
0
  }
6055
0
  return true;
6056
0
}
6057
6058
template <typename T, typename U>
6059
inline bool
6060
write_content_chunked(Stream &strm, const ContentProvider &content_provider,
6061
0
                      const T &is_shutting_down, U &compressor, Error &error) {
6062
0
  size_t offset = 0;
6063
0
  auto data_available = true;
6064
0
  auto ok = true;
6065
0
  DataSink data_sink;
6066
6067
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
6068
0
    if (ok) {
6069
0
      data_available = l > 0;
6070
0
      offset += l;
6071
6072
0
      std::string payload;
6073
0
      if (compressor.compress(d, l, false,
6074
0
                              [&](const char *data, size_t data_len) {
6075
0
                                payload.append(data, data_len);
6076
0
                                return true;
6077
0
                              })) {
6078
0
        if (!payload.empty()) {
6079
          // Emit chunked response header and footer for each chunk
6080
0
          auto chunk =
6081
0
              from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
6082
0
          if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
6083
0
        }
6084
0
      } else {
6085
0
        ok = false;
6086
0
      }
6087
0
    }
6088
0
    return ok;
6089
0
  };
6090
6091
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
6092
6093
0
  auto done_with_trailer = [&](const Headers *trailer) {
6094
0
    if (!ok) { return; }
6095
6096
0
    data_available = false;
6097
6098
0
    std::string payload;
6099
0
    if (!compressor.compress(nullptr, 0, true,
6100
0
                             [&](const char *data, size_t data_len) {
6101
0
                               payload.append(data, data_len);
6102
0
                               return true;
6103
0
                             })) {
6104
0
      ok = false;
6105
0
      return;
6106
0
    }
6107
6108
0
    if (!payload.empty()) {
6109
      // Emit chunked response header and footer for each chunk
6110
0
      auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
6111
0
      if (!write_data(strm, chunk.data(), chunk.size())) {
6112
0
        ok = false;
6113
0
        return;
6114
0
      }
6115
0
    }
6116
6117
0
    constexpr const char done_marker[] = "0\r\n";
6118
0
    if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
6119
6120
    // Trailer
6121
0
    if (trailer) {
6122
0
      for (const auto &kv : *trailer) {
6123
0
        std::string field_line = kv.first + ": " + kv.second + "\r\n";
6124
0
        if (!write_data(strm, field_line.data(), field_line.size())) {
6125
0
          ok = false;
6126
0
        }
6127
0
      }
6128
0
    }
6129
6130
0
    constexpr const char crlf[] = "\r\n";
6131
0
    if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
6132
0
  };
6133
6134
0
  data_sink.done = [&](void) { done_with_trailer(nullptr); };
6135
6136
0
  data_sink.done_with_trailer = [&](const Headers &trailer) {
6137
0
    done_with_trailer(&trailer);
6138
0
  };
6139
6140
0
  while (data_available && !is_shutting_down()) {
6141
0
    if (!strm.wait_writable()) {
6142
0
      error = Error::Write;
6143
0
      return false;
6144
0
    } else if (!content_provider(offset, 0, data_sink)) {
6145
0
      error = Error::Canceled;
6146
0
      return false;
6147
0
    } else if (!ok) {
6148
0
      error = Error::Write;
6149
0
      return false;
6150
0
    }
6151
0
  }
6152
6153
0
  error = Error::Success;
6154
0
  return true;
6155
0
}
Unexecuted instantiation: bool httplib::detail::write_content_chunked<httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1}, httplib::detail::compressor>(httplib::Stream&, std::__1::function<bool (unsigned long, unsigned long, httplib::DataSink&)> const&, httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1} const&, httplib::detail::compressor&, httplib::Error&)
Unexecuted instantiation: bool httplib::detail::write_content_chunked<httplib::ClientImpl::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Error&) const::{lambda()#1}, httplib::detail::compressor>(httplib::Stream&, std::__1::function<bool (unsigned long, unsigned long, httplib::DataSink&)> const&, httplib::ClientImpl::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Error&) const::{lambda()#1} const&, httplib::detail::compressor&, httplib::Error&)
6156
6157
template <typename T, typename U>
6158
inline bool write_content_chunked(Stream &strm,
6159
                                  const ContentProvider &content_provider,
6160
0
                                  const T &is_shutting_down, U &compressor) {
6161
0
  auto error = Error::Success;
6162
0
  return write_content_chunked(strm, content_provider, is_shutting_down,
6163
0
                               compressor, error);
6164
0
}
6165
6166
template <typename T>
6167
inline bool redirect(T &cli, Request &req, Response &res,
6168
                     const std::string &path, const std::string &location,
6169
0
                     Error &error) {
6170
0
  Request new_req = req;
6171
0
  new_req.path = path;
6172
0
  new_req.redirect_count_ -= 1;
6173
0
6174
0
  if (res.status == StatusCode::SeeOther_303 &&
6175
0
      (req.method != "GET" && req.method != "HEAD")) {
6176
0
    new_req.method = "GET";
6177
0
    new_req.body.clear();
6178
0
    new_req.headers.clear();
6179
0
  }
6180
0
6181
0
  Response new_res;
6182
0
6183
0
  auto ret = cli.send(new_req, new_res, error);
6184
0
  if (ret) {
6185
0
    req = std::move(new_req);
6186
0
    res = std::move(new_res);
6187
0
6188
0
    if (res.location.empty()) { res.location = location; }
6189
0
  }
6190
0
  return ret;
6191
0
}
6192
6193
0
inline std::string params_to_query_str(const Params &params) {
6194
0
  std::string query;
6195
0
6196
0
  for (auto it = params.begin(); it != params.end(); ++it) {
6197
0
    if (it != params.begin()) { query += '&'; }
6198
0
    query += encode_query_component(it->first);
6199
0
    query += '=';
6200
0
    query += encode_query_component(it->second);
6201
0
  }
6202
0
  return query;
6203
0
}
6204
6205
inline void parse_query_text(const char *data, std::size_t size,
6206
6.20k
                             Params &params) {
6207
6.20k
  std::set<std::string> cache;
6208
14.7k
  split(data, data + size, '&', [&](const char *b, const char *e) {
6209
14.7k
    std::string kv(b, e);
6210
14.7k
    if (cache.find(kv) != cache.end()) { return; }
6211
6.35k
    cache.insert(std::move(kv));
6212
6213
6.35k
    std::string key;
6214
6.35k
    std::string val;
6215
6.35k
    divide(b, static_cast<std::size_t>(e - b), '=',
6216
6.35k
           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
6217
6.35k
               std::size_t rhs_size) {
6218
6.35k
             key.assign(lhs_data, lhs_size);
6219
6.35k
             val.assign(rhs_data, rhs_size);
6220
6.35k
           });
6221
6222
6.35k
    if (!key.empty()) {
6223
5.91k
      params.emplace(decode_query_component(key), decode_query_component(val));
6224
5.91k
    }
6225
6.35k
  });
6226
6.20k
}
6227
6228
17
inline void parse_query_text(const std::string &s, Params &params) {
6229
17
  parse_query_text(s.data(), s.size(), params);
6230
17
}
6231
6232
// Normalize a query string by decoding and re-encoding each key/value pair
6233
// while preserving the original parameter order. This avoids double-encoding
6234
// and ensures consistent encoding without reordering (unlike Params which
6235
// uses std::multimap and sorts keys).
6236
0
inline std::string normalize_query_string(const std::string &query) {
6237
0
  std::string result;
6238
0
  split(query.data(), query.data() + query.size(), '&',
6239
0
        [&](const char *b, const char *e) {
6240
0
          std::string key;
6241
0
          std::string val;
6242
0
          divide(b, static_cast<std::size_t>(e - b), '=',
6243
0
                 [&](const char *lhs_data, std::size_t lhs_size,
6244
0
                     const char *rhs_data, std::size_t rhs_size) {
6245
0
                   key.assign(lhs_data, lhs_size);
6246
0
                   val.assign(rhs_data, rhs_size);
6247
0
                 });
6248
0
6249
0
          if (!key.empty()) {
6250
0
            auto dec_key = decode_query_component(key);
6251
0
            auto dec_val = decode_query_component(val);
6252
0
6253
0
            if (!result.empty()) { result += '&'; }
6254
0
            result += encode_query_component(dec_key);
6255
0
            if (!val.empty() || std::find(b, e, '=') != e) {
6256
0
              result += '=';
6257
0
              result += encode_query_component(dec_val);
6258
0
            }
6259
0
          }
6260
0
        });
6261
0
  return result;
6262
0
}
6263
6264
inline bool parse_multipart_boundary(const std::string &content_type,
6265
1.36k
                                     std::string &boundary) {
6266
1.36k
  auto boundary_keyword = "boundary=";
6267
1.36k
  auto pos = content_type.find(boundary_keyword);
6268
1.36k
  if (pos == std::string::npos) { return false; }
6269
1.36k
  auto end = content_type.find(';', pos);
6270
1.36k
  auto beg = pos + strlen(boundary_keyword);
6271
1.36k
  boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
6272
1.36k
  return !boundary.empty();
6273
1.36k
}
6274
6275
7.10k
inline void parse_disposition_params(const std::string &s, Params &params) {
6276
7.10k
  std::set<std::string> cache;
6277
38.8k
  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
6278
38.8k
    std::string kv(b, e);
6279
38.8k
    if (cache.find(kv) != cache.end()) { return; }
6280
34.0k
    cache.insert(kv);
6281
6282
34.0k
    std::string key;
6283
34.0k
    std::string val;
6284
52.2k
    split(b, e, '=', [&](const char *b2, const char *e2) {
6285
52.2k
      if (key.empty()) {
6286
32.8k
        key.assign(b2, e2);
6287
32.8k
      } else {
6288
19.3k
        val.assign(b2, e2);
6289
19.3k
      }
6290
52.2k
    });
6291
6292
34.0k
    if (!key.empty()) {
6293
32.8k
      params.emplace(trim_double_quotes_copy((key)),
6294
32.8k
                     trim_double_quotes_copy((val)));
6295
32.8k
    }
6296
34.0k
  });
6297
7.10k
}
6298
6299
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6300
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
6301
#else
6302
842
inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
6303
842
#endif
6304
31.9k
  auto is_valid = [](const std::string &str) {
6305
31.9k
    return std::all_of(str.cbegin(), str.cend(),
6306
31.9k
                       [](unsigned char c) { return std::isdigit(c); });
6307
31.9k
  };
6308
6309
842
  if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
6310
817
    const auto pos = static_cast<size_t>(6);
6311
817
    const auto len = static_cast<size_t>(s.size() - 6);
6312
817
    auto all_valid_ranges = true;
6313
20.9k
    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
6314
20.9k
      if (!all_valid_ranges) { return; }
6315
6316
15.9k
      const auto it = std::find(b, e, '-');
6317
15.9k
      if (it == e) {
6318
11
        all_valid_ranges = false;
6319
11
        return;
6320
11
      }
6321
6322
15.9k
      const auto lhs = std::string(b, it);
6323
15.9k
      const auto rhs = std::string(it + 1, e);
6324
15.9k
      if (!is_valid(lhs) || !is_valid(rhs)) {
6325
9
        all_valid_ranges = false;
6326
9
        return;
6327
9
      }
6328
6329
15.9k
      const auto first =
6330
15.9k
          static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
6331
15.9k
      const auto last =
6332
15.9k
          static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
6333
15.9k
      if ((first == -1 && last == -1) ||
6334
15.9k
          (first != -1 && last != -1 && first > last)) {
6335
84
        all_valid_ranges = false;
6336
84
        return;
6337
84
      }
6338
6339
15.8k
      ranges.emplace_back(first, last);
6340
15.8k
    });
6341
817
    return all_valid_ranges && !ranges.empty();
6342
817
  }
6343
25
  return false;
6344
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6345
}
6346
#else
6347
842
} catch (...) { return false; }
6348
#endif
6349
6350
inline bool parse_accept_header(const std::string &s,
6351
985
                                std::vector<std::string> &content_types) {
6352
985
  content_types.clear();
6353
6354
  // Empty string is considered valid (no preference)
6355
985
  if (s.empty()) { return true; }
6356
6357
  // Check for invalid patterns: leading/trailing commas or consecutive commas
6358
970
  if (s.front() == ',' || s.back() == ',' ||
6359
968
      s.find(",,") != std::string::npos) {
6360
5
    return false;
6361
5
  }
6362
6363
965
  struct AcceptEntry {
6364
965
    std::string media_type;
6365
965
    double quality;
6366
965
    int order; // Original order in header
6367
965
  };
6368
6369
965
  std::vector<AcceptEntry> entries;
6370
965
  int order = 0;
6371
965
  bool has_invalid_entry = false;
6372
6373
  // Split by comma and parse each entry
6374
207k
  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
6375
207k
    std::string entry(b, e);
6376
207k
    entry = trim_copy(entry);
6377
6378
207k
    if (entry.empty()) {
6379
0
      has_invalid_entry = true;
6380
0
      return;
6381
0
    }
6382
6383
207k
    AcceptEntry accept_entry;
6384
207k
    accept_entry.quality = 1.0; // Default quality
6385
207k
    accept_entry.order = order++;
6386
6387
    // Find q= parameter
6388
207k
    auto q_pos = entry.find(";q=");
6389
207k
    if (q_pos == std::string::npos) { q_pos = entry.find("; q="); }
6390
6391
207k
    if (q_pos != std::string::npos) {
6392
      // Extract media type (before q parameter)
6393
8.91k
      accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
6394
6395
      // Extract quality value
6396
8.91k
      auto q_start = entry.find('=', q_pos) + 1;
6397
8.91k
      auto q_end = entry.find(';', q_start);
6398
8.91k
      if (q_end == std::string::npos) { q_end = entry.length(); }
6399
6400
8.91k
      std::string quality_str =
6401
8.91k
          trim_copy(entry.substr(q_start, q_end - q_start));
6402
8.91k
      if (quality_str.empty()) {
6403
656
        has_invalid_entry = true;
6404
656
        return;
6405
656
      }
6406
6407
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6408
      {
6409
        std::istringstream iss(quality_str);
6410
        iss >> accept_entry.quality;
6411
6412
        // Check if conversion was successful and entire string was consumed
6413
        if (iss.fail() || !iss.eof()) {
6414
          has_invalid_entry = true;
6415
          return;
6416
        }
6417
      }
6418
#else
6419
8.25k
      try {
6420
8.25k
        accept_entry.quality = std::stod(quality_str);
6421
8.25k
      } catch (...) {
6422
356
        has_invalid_entry = true;
6423
356
        return;
6424
356
      }
6425
0
#endif
6426
      // Check if quality is in valid range [0.0, 1.0]
6427
7.90k
      if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
6428
389
        has_invalid_entry = true;
6429
389
        return;
6430
389
      }
6431
198k
    } else {
6432
      // No quality parameter, use entire entry as media type
6433
198k
      accept_entry.media_type = entry;
6434
198k
    }
6435
6436
    // Remove additional parameters from media type
6437
205k
    auto param_pos = accept_entry.media_type.find(';');
6438
205k
    if (param_pos != std::string::npos) {
6439
1.78k
      accept_entry.media_type =
6440
1.78k
          trim_copy(accept_entry.media_type.substr(0, param_pos));
6441
1.78k
    }
6442
6443
    // Basic validation of media type format
6444
205k
    if (accept_entry.media_type.empty()) {
6445
721
      has_invalid_entry = true;
6446
721
      return;
6447
721
    }
6448
6449
    // Check for basic media type format (should contain '/' or be '*')
6450
205k
    if (accept_entry.media_type != "*" &&
6451
196k
        accept_entry.media_type.find('/') == std::string::npos) {
6452
483
      has_invalid_entry = true;
6453
483
      return;
6454
483
    }
6455
6456
204k
    entries.push_back(std::move(accept_entry));
6457
204k
  });
6458
6459
  // Return false if any invalid entry was found
6460
965
  if (has_invalid_entry) { return false; }
6461
6462
  // Sort by quality (descending), then by original order (ascending)
6463
807
  std::sort(entries.begin(), entries.end(),
6464
3.77M
            [](const AcceptEntry &a, const AcceptEntry &b) {
6465
3.77M
              if (a.quality != b.quality) {
6466
928k
                return a.quality > b.quality; // Higher quality first
6467
928k
              }
6468
2.84M
              return a.order < b.order; // Earlier order first for same quality
6469
3.77M
            });
6470
6471
  // Extract sorted media types
6472
807
  content_types.reserve(entries.size());
6473
203k
  for (auto &entry : entries) {
6474
203k
    content_types.push_back(std::move(entry.media_type));
6475
203k
  }
6476
6477
807
  return true;
6478
965
}
6479
6480
class FormDataParser {
6481
public:
6482
3.72k
  FormDataParser() = default;
6483
6484
1.35k
  void set_boundary(std::string &&boundary) {
6485
1.35k
    boundary_ = std::move(boundary);
6486
1.35k
    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
6487
1.35k
    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
6488
1.35k
  }
6489
6490
27
  bool is_valid() const { return is_valid_; }
6491
6492
  bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
6493
62.2k
             const ContentReceiver &content_callback) {
6494
6495
62.2k
    buf_append(buf, n);
6496
6497
141k
    while (buf_size() > 0) {
6498
140k
      switch (state_) {
6499
2.21k
      case 0: { // Initial boundary
6500
2.21k
        auto pos = buf_find(dash_boundary_crlf_);
6501
2.21k
        if (pos == buf_size()) { return true; }
6502
1.18k
        buf_erase(pos + dash_boundary_crlf_.size());
6503
1.18k
        state_ = 1;
6504
1.18k
        break;
6505
2.21k
      }
6506
20.0k
      case 1: { // New entry
6507
20.0k
        clear_file_info();
6508
20.0k
        state_ = 2;
6509
20.0k
        break;
6510
2.21k
      }
6511
20.5k
      case 2: { // Headers
6512
20.5k
        auto pos = buf_find(crlf_);
6513
20.5k
        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
6514
122k
        while (pos < buf_size()) {
6515
          // Empty line
6516
121k
          if (pos == 0) {
6517
19.3k
            if (!header_callback(file_)) {
6518
1
              is_valid_ = false;
6519
1
              return false;
6520
1
            }
6521
19.3k
            buf_erase(crlf_.size());
6522
19.3k
            state_ = 3;
6523
19.3k
            break;
6524
19.3k
          }
6525
6526
101k
          const auto header = buf_head(pos);
6527
6528
101k
          if (!parse_header(header.data(), header.data() + header.size(),
6529
101k
                            [&](const std::string &, const std::string &) {})) {
6530
75
            is_valid_ = false;
6531
75
            return false;
6532
75
          }
6533
6534
          // Parse and emplace space trimmed headers into a map
6535
101k
          if (!parse_header(
6536
101k
                  header.data(), header.data() + header.size(),
6537
101k
                  [&](const std::string &key, const std::string &val) {
6538
101k
                    file_.headers.emplace(key, val);
6539
101k
                  })) {
6540
0
            is_valid_ = false;
6541
0
            return false;
6542
0
          }
6543
6544
101k
          constexpr const char header_content_type[] = "Content-Type:";
6545
6546
101k
          if (start_with_case_ignore(header, header_content_type)) {
6547
1.45k
            file_.content_type =
6548
1.45k
                trim_copy(header.substr(str_len(header_content_type)));
6549
100k
          } else {
6550
100k
            thread_local const std::regex re_content_disposition(
6551
100k
                R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
6552
100k
                std::regex_constants::icase);
6553
6554
100k
            std::smatch m;
6555
100k
            if (std::regex_match(header, m, re_content_disposition)) {
6556
7.10k
              Params params;
6557
7.10k
              parse_disposition_params(m[1], params);
6558
6559
7.10k
              auto it = params.find("name");
6560
7.10k
              if (it != params.end()) {
6561
6.95k
                file_.name = it->second;
6562
6.95k
              } else {
6563
152
                is_valid_ = false;
6564
152
                return false;
6565
152
              }
6566
6567
6.95k
              it = params.find("filename");
6568
6.95k
              if (it != params.end()) { file_.filename = it->second; }
6569
6570
6.95k
              it = params.find("filename*");
6571
6.95k
              if (it != params.end()) {
6572
                // Only allow UTF-8 encoding...
6573
11
                thread_local const std::regex re_rfc5987_encoding(
6574
11
                    R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
6575
6576
11
                std::smatch m2;
6577
11
                if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
6578
0
                  file_.filename = decode_path_component(m2[1]); // override...
6579
11
                } else {
6580
11
                  is_valid_ = false;
6581
11
                  return false;
6582
11
                }
6583
11
              }
6584
6.95k
            }
6585
100k
          }
6586
101k
          buf_erase(pos + crlf_.size());
6587
101k
          pos = buf_find(crlf_);
6588
101k
        }
6589
20.3k
        if (state_ != 3) { return true; }
6590
19.3k
        break;
6591
20.3k
      }
6592
47.3k
      case 3: { // Body
6593
47.3k
        if (crlf_dash_boundary_.size() > buf_size()) { return true; }
6594
47.0k
        auto pos = buf_find(crlf_dash_boundary_);
6595
47.0k
        if (pos < buf_size()) {
6596
18.9k
          if (!content_callback(buf_data(), pos)) {
6597
0
            is_valid_ = false;
6598
0
            return false;
6599
0
          }
6600
18.9k
          buf_erase(pos + crlf_dash_boundary_.size());
6601
18.9k
          state_ = 4;
6602
28.0k
        } else {
6603
28.0k
          auto len = buf_size() - crlf_dash_boundary_.size();
6604
28.0k
          if (len > 0) {
6605
27.8k
            if (!content_callback(buf_data(), len)) {
6606
0
              is_valid_ = false;
6607
0
              return false;
6608
0
            }
6609
27.8k
            buf_erase(len);
6610
27.8k
          }
6611
28.0k
          return true;
6612
28.0k
        }
6613
18.9k
        break;
6614
47.0k
      }
6615
50.1k
      case 4: { // Boundary
6616
50.1k
        if (crlf_.size() > buf_size()) { return true; }
6617
49.7k
        if (buf_start_with(crlf_)) {
6618
18.9k
          buf_erase(crlf_.size());
6619
18.9k
          state_ = 1;
6620
30.8k
        } else {
6621
30.8k
          if (dash_.size() > buf_size()) { return true; }
6622
30.8k
          if (buf_start_with(dash_)) {
6623
376
            buf_erase(dash_.size());
6624
376
            is_valid_ = true;
6625
376
            buf_erase(buf_size()); // Remove epilogue
6626
30.5k
          } else {
6627
30.5k
            return true;
6628
30.5k
          }
6629
30.8k
        }
6630
19.2k
        break;
6631
49.7k
      }
6632
140k
      }
6633
140k
    }
6634
6635
692
    return true;
6636
62.2k
  }
6637
6638
private:
6639
20.0k
  void clear_file_info() {
6640
20.0k
    file_.name.clear();
6641
20.0k
    file_.filename.clear();
6642
20.0k
    file_.content_type.clear();
6643
20.0k
    file_.headers.clear();
6644
20.0k
  }
6645
6646
101k
  bool start_with_case_ignore(const std::string &a, const char *b) const {
6647
101k
    const auto b_len = strlen(b);
6648
101k
    if (a.size() < b_len) { return false; }
6649
135k
    for (size_t i = 0; i < b_len; i++) {
6650
133k
      if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
6651
21.0k
        return false;
6652
21.0k
      }
6653
133k
    }
6654
1.45k
    return true;
6655
22.5k
  }
6656
6657
  const std::string dash_ = "--";
6658
  const std::string crlf_ = "\r\n";
6659
  std::string boundary_;
6660
  std::string dash_boundary_crlf_;
6661
  std::string crlf_dash_boundary_;
6662
6663
  size_t state_ = 0;
6664
  bool is_valid_ = false;
6665
  FormData file_;
6666
6667
  // Buffer
6668
  bool start_with(const std::string &a, size_t spos, size_t epos,
6669
12.1M
                  const std::string &b) const {
6670
12.1M
    if (epos - spos < b.size()) { return false; }
6671
26.0M
    for (size_t i = 0; i < b.size(); i++) {
6672
25.9M
      if (a[i + spos] != b[i]) { return false; }
6673
25.9M
    }
6674
160k
    return true;
6675
12.1M
  }
6676
6677
561k
  size_t buf_size() const { return buf_epos_ - buf_spos_; }
6678
6679
46.8k
  const char *buf_data() const { return &buf_[buf_spos_]; }
6680
6681
101k
  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
6682
6683
80.6k
  bool buf_start_with(const std::string &s) const {
6684
80.6k
    return start_with(buf_, buf_spos_, buf_epos_, s);
6685
80.6k
  }
6686
6687
171k
  size_t buf_find(const std::string &s) const {
6688
171k
    auto c = s.front();
6689
6690
171k
    size_t off = buf_spos_;
6691
12.1M
    while (off < buf_epos_) {
6692
12.1M
      auto pos = off;
6693
706M
      while (true) {
6694
706M
        if (pos == buf_epos_) { return buf_size(); }
6695
706M
        if (buf_[pos] == c) { break; }
6696
694M
        pos++;
6697
694M
      }
6698
6699
12.0M
      auto remaining_size = buf_epos_ - pos;
6700
12.0M
      if (s.size() > remaining_size) { return buf_size(); }
6701
6702
12.0M
      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
6703
6704
11.9M
      off = pos + 1;
6705
11.9M
    }
6706
6707
386
    return buf_size();
6708
171k
  }
6709
6710
62.2k
  void buf_append(const char *data, size_t n) {
6711
62.2k
    auto remaining_size = buf_size();
6712
62.2k
    if (remaining_size > 0 && buf_spos_ > 0) {
6713
744k
      for (size_t i = 0; i < remaining_size; i++) {
6714
716k
        buf_[i] = buf_[buf_spos_ + i];
6715
716k
      }
6716
28.1k
    }
6717
62.2k
    buf_spos_ = 0;
6718
62.2k
    buf_epos_ = remaining_size;
6719
6720
62.2k
    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
6721
6722
952M
    for (size_t i = 0; i < n; i++) {
6723
952M
      buf_[buf_epos_ + i] = data[i];
6724
952M
    }
6725
62.2k
    buf_epos_ += n;
6726
62.2k
  }
6727
6728
188k
  void buf_erase(size_t size) { buf_spos_ += size; }
6729
6730
  std::string buf_;
6731
  size_t buf_spos_ = 0;
6732
  size_t buf_epos_ = 0;
6733
};
6734
6735
215
inline std::string random_string(size_t length) {
6736
215
  constexpr const char data[] =
6737
215
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
6738
6739
215
  thread_local auto engine([]() {
6740
    // std::random_device might actually be deterministic on some
6741
    // platforms, but due to lack of support in the c++ standard library,
6742
    // doing better requires either some ugly hacks or breaking portability.
6743
1
    std::random_device seed_gen;
6744
    // Request 128 bits of entropy for initialization
6745
1
    std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
6746
1
    return std::mt19937(seed_sequence);
6747
1
  }());
6748
6749
215
  std::string result;
6750
3.65k
  for (size_t i = 0; i < length; i++) {
6751
3.44k
    result += data[engine() % (sizeof(data) - 1)];
6752
3.44k
  }
6753
215
  return result;
6754
215
}
6755
6756
215
inline std::string make_multipart_data_boundary() {
6757
215
  return "--cpp-httplib-multipart-data-" + detail::random_string(16);
6758
215
}
6759
6760
0
inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
6761
0
  auto valid = true;
6762
0
  for (size_t i = 0; i < boundary.size(); i++) {
6763
0
    auto c = boundary[i];
6764
0
    if (!std::isalnum(c) && c != '-' && c != '_') {
6765
0
      valid = false;
6766
0
      break;
6767
0
    }
6768
0
  }
6769
0
  return valid;
6770
0
}
6771
6772
template <typename T>
6773
inline std::string
6774
serialize_multipart_formdata_item_begin(const T &item,
6775
0
                                        const std::string &boundary) {
6776
0
  std::string body = "--" + boundary + "\r\n";
6777
0
  body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
6778
0
  if (!item.filename.empty()) {
6779
0
    body += "; filename=\"" + item.filename + "\"";
6780
0
  }
6781
0
  body += "\r\n";
6782
0
  if (!item.content_type.empty()) {
6783
0
    body += "Content-Type: " + item.content_type + "\r\n";
6784
0
  }
6785
0
  body += "\r\n";
6786
0
6787
0
  return body;
6788
0
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > httplib::detail::serialize_multipart_formdata_item_begin<httplib::UploadFormData>(httplib::UploadFormData const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > httplib::detail::serialize_multipart_formdata_item_begin<httplib::FormDataProvider>(httplib::FormDataProvider const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
6789
6790
0
inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
6791
6792
inline std::string
6793
0
serialize_multipart_formdata_finish(const std::string &boundary) {
6794
0
  return "--" + boundary + "--\r\n";
6795
0
}
6796
6797
inline std::string
6798
0
serialize_multipart_formdata_get_content_type(const std::string &boundary) {
6799
0
  return "multipart/form-data; boundary=" + boundary;
6800
0
}
6801
6802
inline std::string
6803
serialize_multipart_formdata(const UploadFormDataItems &items,
6804
0
                             const std::string &boundary, bool finish = true) {
6805
0
  std::string body;
6806
0
6807
0
  for (const auto &item : items) {
6808
0
    body += serialize_multipart_formdata_item_begin(item, boundary);
6809
0
    body += item.content + serialize_multipart_formdata_item_end();
6810
0
  }
6811
0
6812
0
  if (finish) { body += serialize_multipart_formdata_finish(boundary); }
6813
0
6814
0
  return body;
6815
0
}
6816
6817
391
inline void coalesce_ranges(Ranges &ranges, size_t content_length) {
6818
391
  if (ranges.size() <= 1) return;
6819
6820
  // Sort ranges by start position
6821
251
  std::sort(ranges.begin(), ranges.end(),
6822
1.11k
            [](const Range &a, const Range &b) { return a.first < b.first; });
6823
6824
251
  Ranges coalesced;
6825
251
  coalesced.reserve(ranges.size());
6826
6827
862
  for (auto &r : ranges) {
6828
862
    auto first_pos = r.first;
6829
862
    auto last_pos = r.second;
6830
6831
    // Handle special cases like in range_error
6832
862
    if (first_pos == -1 && last_pos == -1) {
6833
0
      first_pos = 0;
6834
0
      last_pos = static_cast<ssize_t>(content_length);
6835
0
    }
6836
6837
862
    if (first_pos == -1) {
6838
0
      first_pos = static_cast<ssize_t>(content_length) - last_pos;
6839
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
6840
0
    }
6841
6842
862
    if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
6843
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
6844
0
    }
6845
6846
    // Skip invalid ranges
6847
862
    if (!(0 <= first_pos && first_pos <= last_pos &&
6848
862
          last_pos < static_cast<ssize_t>(content_length))) {
6849
0
      continue;
6850
0
    }
6851
6852
    // Coalesce with previous range if overlapping or adjacent (but not
6853
    // identical)
6854
862
    if (!coalesced.empty()) {
6855
611
      auto &prev = coalesced.back();
6856
      // Check if current range overlaps or is adjacent to previous range
6857
      // but don't coalesce identical ranges (allow duplicates)
6858
611
      if (first_pos <= prev.second + 1 &&
6859
379
          !(first_pos == prev.first && last_pos == prev.second)) {
6860
        // Extend the previous range
6861
275
        prev.second = (std::max)(prev.second, last_pos);
6862
275
        continue;
6863
275
      }
6864
611
    }
6865
6866
    // Add new range
6867
587
    coalesced.emplace_back(first_pos, last_pos);
6868
587
  }
6869
6870
251
  ranges = std::move(coalesced);
6871
251
}
6872
6873
1.83k
inline bool range_error(Request &req, Response &res) {
6874
1.83k
  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
6875
700
    ssize_t content_len = static_cast<ssize_t>(
6876
700
        res.content_length_ ? res.content_length_ : res.body.size());
6877
6878
700
    std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
6879
700
    size_t overwrapping_count = 0;
6880
6881
    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
6882
    // 'HTTP Semantics' to avoid potential denial-of-service attacks.
6883
    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
6884
6885
    // Too many ranges
6886
700
    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }
6887
6888
1.62k
    for (auto &r : req.ranges) {
6889
1.62k
      auto &first_pos = r.first;
6890
1.62k
      auto &last_pos = r.second;
6891
6892
1.62k
      if (first_pos == -1 && last_pos == -1) {
6893
0
        first_pos = 0;
6894
0
        last_pos = content_len;
6895
0
      }
6896
6897
1.62k
      if (first_pos == -1) {
6898
583
        first_pos = content_len - last_pos;
6899
583
        last_pos = content_len - 1;
6900
583
      }
6901
6902
      // NOTE: RFC-9110 '14.1.2. Byte Ranges':
6903
      // A client can limit the number of bytes requested without knowing the
6904
      // size of the selected representation. If the last-pos value is absent,
6905
      // or if the value is greater than or equal to the current length of the
6906
      // representation data, the byte range is interpreted as the remainder of
6907
      // the representation (i.e., the server replaces the value of last-pos
6908
      // with a value that is one less than the current length of the selected
6909
      // representation).
6910
      // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
6911
1.62k
      if (last_pos == -1 || last_pos >= content_len) {
6912
509
        last_pos = content_len - 1;
6913
509
      }
6914
6915
      // Range must be within content length
6916
1.62k
      if (!(0 <= first_pos && first_pos <= last_pos &&
6917
1.37k
            last_pos <= content_len - 1)) {
6918
248
        return true;
6919
248
      }
6920
6921
      // Request must not have more than two overlapping ranges
6922
2.00k
      for (const auto &processed_range : processed_ranges) {
6923
2.00k
        if (!(last_pos < processed_range.first ||
6924
1.32k
              first_pos > processed_range.second)) {
6925
445
          overwrapping_count++;
6926
445
          if (overwrapping_count > 2) { return true; }
6927
385
          break; // Only count once per range
6928
445
        }
6929
2.00k
      }
6930
6931
1.31k
      processed_ranges.emplace_back(first_pos, last_pos);
6932
1.31k
    }
6933
6934
    // After validation, coalesce overlapping ranges as per RFC 9110
6935
391
    coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
6936
391
  }
6937
6938
1.52k
  return false;
6939
1.83k
}
6940
6941
inline std::pair<size_t, size_t>
6942
727
get_range_offset_and_length(Range r, size_t content_length) {
6943
727
  assert(r.first != -1 && r.second != -1);
6944
727
  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
6945
727
  assert(r.first <= r.second &&
6946
727
         r.second < static_cast<ssize_t>(content_length));
6947
727
  (void)(content_length);
6948
727
  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
6949
727
}
6950
6951
inline std::string make_content_range_header_field(
6952
727
    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
6953
727
  auto st = offset_and_length.first;
6954
727
  auto ed = st + offset_and_length.second - 1;
6955
6956
727
  std::string field = "bytes ";
6957
727
  field += std::to_string(st);
6958
727
  field += '-';
6959
727
  field += std::to_string(ed);
6960
727
  field += '/';
6961
727
  field += std::to_string(content_length);
6962
727
  return field;
6963
727
}
6964
6965
template <typename SToken, typename CToken, typename Content>
6966
bool process_multipart_ranges_data(const Request &req,
6967
                                   const std::string &boundary,
6968
                                   const std::string &content_type,
6969
                                   size_t content_length, SToken stoken,
6970
215
                                   CToken ctoken, Content content) {
6971
766
  for (size_t i = 0; i < req.ranges.size(); i++) {
6972
551
    ctoken("--");
6973
551
    stoken(boundary);
6974
551
    ctoken("\r\n");
6975
551
    if (!content_type.empty()) {
6976
551
      ctoken("Content-Type: ");
6977
551
      stoken(content_type);
6978
551
      ctoken("\r\n");
6979
551
    }
6980
6981
551
    auto offset_and_length =
6982
551
        get_range_offset_and_length(req.ranges[i], content_length);
6983
6984
551
    ctoken("Content-Range: ");
6985
551
    stoken(make_content_range_header_field(offset_and_length, content_length));
6986
551
    ctoken("\r\n");
6987
551
    ctoken("\r\n");
6988
6989
551
    if (!content(offset_and_length.first, offset_and_length.second)) {
6990
0
      return false;
6991
0
    }
6992
551
    ctoken("\r\n");
6993
551
  }
6994
6995
215
  ctoken("--");
6996
215
  stoken(boundary);
6997
215
  ctoken("--");
6998
6999
215
  return true;
7000
215
}
Unexecuted instantiation: bool httplib::detail::process_multipart_ranges_data<httplib::detail::get_multipart_ranges_data_length(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}, httplib::detail::get_multipart_ranges_data_length(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}, httplib::detail::get_multipart_ranges_data_length(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)::{lambda(unsigned long, unsigned long)#1}>(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, httplib::detail::get_multipart_ranges_data_length(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}, httplib::detail::get_multipart_ranges_data_length(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}, httplib::detail::get_multipart_ranges_data_length(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)::{lambda(unsigned long, unsigned long)#1})
bool httplib::detail::process_multipart_ranges_data<httplib::detail::make_multipart_ranges_data(httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}, httplib::detail::make_multipart_ranges_data(httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}, httplib::detail::make_multipart_ranges_data(httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)::{lambda(unsigned long, unsigned long)#1}>(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, httplib::detail::make_multipart_ranges_data(httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}, httplib::detail::make_multipart_ranges_data(httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}, httplib::detail::make_multipart_ranges_data(httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)::{lambda(unsigned long, unsigned long)#1})
Line
Count
Source
6970
215
                                   CToken ctoken, Content content) {
6971
766
  for (size_t i = 0; i < req.ranges.size(); i++) {
6972
551
    ctoken("--");
6973
551
    stoken(boundary);
6974
551
    ctoken("\r\n");
6975
551
    if (!content_type.empty()) {
6976
551
      ctoken("Content-Type: ");
6977
551
      stoken(content_type);
6978
551
      ctoken("\r\n");
6979
551
    }
6980
6981
551
    auto offset_and_length =
6982
551
        get_range_offset_and_length(req.ranges[i], content_length);
6983
6984
551
    ctoken("Content-Range: ");
6985
551
    stoken(make_content_range_header_field(offset_and_length, content_length));
6986
551
    ctoken("\r\n");
6987
551
    ctoken("\r\n");
6988
6989
551
    if (!content(offset_and_length.first, offset_and_length.second)) {
6990
0
      return false;
6991
0
    }
6992
551
    ctoken("\r\n");
6993
551
  }
6994
6995
215
  ctoken("--");
6996
215
  stoken(boundary);
6997
215
  ctoken("--");
6998
6999
215
  return true;
7000
215
}
Unexecuted instantiation: bool httplib::detail::process_multipart_ranges_data<httplib::detail::write_multipart_ranges_data<httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1}>(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1} const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}, httplib::detail::write_multipart_ranges_data<{lambda()#1}>(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, {lambda()#1} const)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}, httplib::detail::write_multipart_ranges_data<{lambda()#1}>(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, {lambda()#1} const)::{lambda(unsigned long, unsigned long)#1}>(httplib::Request const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, httplib::Server::write_content_with_provider(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::{lambda()#1}, httplib::detail::write_multipart_ranges_data<{lambda()#1}>(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, {lambda()#1} const)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}, httplib::detail::write_multipart_ranges_data<{lambda()#1}>(httplib::Stream&, httplib::Request const&, httplib::Response&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, {lambda()#1} const)::{lambda(unsigned long, unsigned long)#1})
7001
7002
inline void make_multipart_ranges_data(const Request &req, Response &res,
7003
                                       const std::string &boundary,
7004
                                       const std::string &content_type,
7005
                                       size_t content_length,
7006
215
                                       std::string &data) {
7007
215
  process_multipart_ranges_data(
7008
215
      req, boundary, content_type, content_length,
7009
1.86k
      [&](const std::string &token) { data += token; },
7010
4.83k
      [&](const std::string &token) { data += token; },
7011
551
      [&](size_t offset, size_t length) {
7012
551
        assert(offset + length <= content_length);
7013
551
        data += res.body.substr(offset, length);
7014
551
        return true;
7015
551
      });
7016
215
}
7017
7018
inline size_t get_multipart_ranges_data_length(const Request &req,
7019
                                               const std::string &boundary,
7020
                                               const std::string &content_type,
7021
0
                                               size_t content_length) {
7022
0
  size_t data_length = 0;
7023
7024
0
  process_multipart_ranges_data(
7025
0
      req, boundary, content_type, content_length,
7026
0
      [&](const std::string &token) { data_length += token.size(); },
7027
0
      [&](const std::string &token) { data_length += token.size(); },
7028
0
      [&](size_t /*offset*/, size_t length) {
7029
0
        data_length += length;
7030
0
        return true;
7031
0
      });
7032
7033
0
  return data_length;
7034
0
}
7035
7036
template <typename T>
7037
inline bool
7038
write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
7039
                            const std::string &boundary,
7040
                            const std::string &content_type,
7041
0
                            size_t content_length, const T &is_shutting_down) {
7042
0
  return process_multipart_ranges_data(
7043
0
      req, boundary, content_type, content_length,
7044
0
      [&](const std::string &token) { strm.write(token); },
7045
0
      [&](const std::string &token) { strm.write(token); },
7046
0
      [&](size_t offset, size_t length) {
7047
0
        return write_content(strm, res.content_provider_, offset, length,
7048
0
                             is_shutting_down);
7049
0
      });
7050
0
}
7051
7052
4.53k
inline bool expect_content(const Request &req) {
7053
4.53k
  if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
7054
2.82k
      req.method == "DELETE") {
7055
2.82k
    return true;
7056
2.82k
  }
7057
1.70k
  if (req.has_header("Content-Length") &&
7058
475
      req.get_header_value_u64("Content-Length") > 0) {
7059
470
    return true;
7060
470
  }
7061
1.23k
  if (is_chunked_transfer_encoding(req.headers)) { return true; }
7062
801
  return false;
7063
1.23k
}
7064
7065
0
inline bool has_crlf(const std::string &s) {
7066
0
  auto p = s.c_str();
7067
0
  while (*p) {
7068
0
    if (*p == '\r' || *p == '\n') { return true; }
7069
0
    p++;
7070
0
  }
7071
0
  return false;
7072
0
}
7073
7074
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7075
inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
7076
  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
7077
      EVP_MD_CTX_new(), EVP_MD_CTX_free);
7078
7079
  unsigned int hash_length = 0;
7080
  unsigned char hash[EVP_MAX_MD_SIZE];
7081
7082
  EVP_DigestInit_ex(context.get(), algo, nullptr);
7083
  EVP_DigestUpdate(context.get(), s.c_str(), s.size());
7084
  EVP_DigestFinal_ex(context.get(), hash, &hash_length);
7085
7086
  std::stringstream ss;
7087
  for (auto i = 0u; i < hash_length; ++i) {
7088
    ss << std::hex << std::setw(2) << std::setfill('0')
7089
       << static_cast<unsigned int>(hash[i]);
7090
  }
7091
7092
  return ss.str();
7093
}
7094
7095
inline std::string MD5(const std::string &s) {
7096
  return message_digest(s, EVP_md5());
7097
}
7098
7099
inline std::string SHA_256(const std::string &s) {
7100
  return message_digest(s, EVP_sha256());
7101
}
7102
7103
inline std::string SHA_512(const std::string &s) {
7104
  return message_digest(s, EVP_sha512());
7105
}
7106
7107
inline std::pair<std::string, std::string> make_digest_authentication_header(
7108
    const Request &req, const std::map<std::string, std::string> &auth,
7109
    size_t cnonce_count, const std::string &cnonce, const std::string &username,
7110
    const std::string &password, bool is_proxy = false) {
7111
  std::string nc;
7112
  {
7113
    std::stringstream ss;
7114
    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
7115
    nc = ss.str();
7116
  }
7117
7118
  std::string qop;
7119
  if (auth.find("qop") != auth.end()) {
7120
    qop = auth.at("qop");
7121
    if (qop.find("auth-int") != std::string::npos) {
7122
      qop = "auth-int";
7123
    } else if (qop.find("auth") != std::string::npos) {
7124
      qop = "auth";
7125
    } else {
7126
      qop.clear();
7127
    }
7128
  }
7129
7130
  std::string algo = "MD5";
7131
  if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
7132
7133
  std::string response;
7134
  {
7135
    auto H = algo == "SHA-256"   ? detail::SHA_256
7136
             : algo == "SHA-512" ? detail::SHA_512
7137
                                 : detail::MD5;
7138
7139
    auto A1 = username + ":" + auth.at("realm") + ":" + password;
7140
7141
    auto A2 = req.method + ":" + req.path;
7142
    if (qop == "auth-int") { A2 += ":" + H(req.body); }
7143
7144
    if (qop.empty()) {
7145
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
7146
    } else {
7147
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
7148
                   ":" + qop + ":" + H(A2));
7149
    }
7150
  }
7151
7152
  auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
7153
7154
  auto field = "Digest username=\"" + username + "\", realm=\"" +
7155
               auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
7156
               "\", uri=\"" + req.path + "\", algorithm=" + algo +
7157
               (qop.empty() ? ", response=\""
7158
                            : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
7159
                                  cnonce + "\", response=\"") +
7160
               response + "\"" +
7161
               (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
7162
7163
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
7164
  return std::make_pair(key, field);
7165
}
7166
7167
inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
7168
  detail::set_nonblocking(sock, true);
7169
  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
7170
7171
  char buf[1];
7172
  return !SSL_peek(ssl, buf, 1) &&
7173
         SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7174
}
7175
7176
#ifdef _WIN32
7177
// NOTE: This code came up with the following stackoverflow post:
7178
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
7179
inline bool load_system_certs_on_windows(X509_STORE *store) {
7180
  auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
7181
  if (!hStore) { return false; }
7182
7183
  auto result = false;
7184
  PCCERT_CONTEXT pContext = NULL;
7185
  while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
7186
         nullptr) {
7187
    auto encoded_cert =
7188
        static_cast<const unsigned char *>(pContext->pbCertEncoded);
7189
7190
    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
7191
    if (x509) {
7192
      X509_STORE_add_cert(store, x509);
7193
      X509_free(x509);
7194
      result = true;
7195
    }
7196
  }
7197
7198
  CertFreeCertificateContext(pContext);
7199
  CertCloseStore(hStore, 0);
7200
7201
  return result;
7202
}
7203
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
7204
template <typename T>
7205
using CFObjectPtr =
7206
    std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
7207
7208
inline void cf_object_ptr_deleter(CFTypeRef obj) {
7209
  if (obj) { CFRelease(obj); }
7210
}
7211
7212
inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
7213
  CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
7214
  CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
7215
                        kCFBooleanTrue};
7216
7217
  CFObjectPtr<CFDictionaryRef> query(
7218
      CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
7219
                         sizeof(keys) / sizeof(keys[0]),
7220
                         &kCFTypeDictionaryKeyCallBacks,
7221
                         &kCFTypeDictionaryValueCallBacks),
7222
      cf_object_ptr_deleter);
7223
7224
  if (!query) { return false; }
7225
7226
  CFTypeRef security_items = nullptr;
7227
  if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
7228
      CFArrayGetTypeID() != CFGetTypeID(security_items)) {
7229
    return false;
7230
  }
7231
7232
  certs.reset(reinterpret_cast<CFArrayRef>(security_items));
7233
  return true;
7234
}
7235
7236
inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
7237
  CFArrayRef root_security_items = nullptr;
7238
  if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
7239
    return false;
7240
  }
7241
7242
  certs.reset(root_security_items);
7243
  return true;
7244
}
7245
7246
inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
7247
  auto result = false;
7248
  for (auto i = 0; i < CFArrayGetCount(certs); ++i) {
7249
    const auto cert = reinterpret_cast<const __SecCertificate *>(
7250
        CFArrayGetValueAtIndex(certs, i));
7251
7252
    if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
7253
7254
    CFDataRef cert_data = nullptr;
7255
    if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
7256
        errSecSuccess) {
7257
      continue;
7258
    }
7259
7260
    CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
7261
7262
    auto encoded_cert = static_cast<const unsigned char *>(
7263
        CFDataGetBytePtr(cert_data_ptr.get()));
7264
7265
    auto x509 =
7266
        d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
7267
7268
    if (x509) {
7269
      X509_STORE_add_cert(store, x509);
7270
      X509_free(x509);
7271
      result = true;
7272
    }
7273
  }
7274
7275
  return result;
7276
}
7277
7278
inline bool load_system_certs_on_macos(X509_STORE *store) {
7279
  auto result = false;
7280
  CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
7281
  if (retrieve_certs_from_keychain(certs) && certs) {
7282
    result = add_certs_to_x509_store(certs.get(), store);
7283
  }
7284
7285
  if (retrieve_root_certs_from_keychain(certs) && certs) {
7286
    result = add_certs_to_x509_store(certs.get(), store) || result;
7287
  }
7288
7289
  return result;
7290
}
7291
#endif // _WIN32
7292
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
7293
7294
#ifdef _WIN32
7295
class WSInit {
7296
public:
7297
  WSInit() {
7298
    WSADATA wsaData;
7299
    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
7300
  }
7301
7302
  ~WSInit() {
7303
    if (is_valid_) WSACleanup();
7304
  }
7305
7306
  bool is_valid_ = false;
7307
};
7308
7309
static WSInit wsinit_;
7310
#endif
7311
7312
inline bool parse_www_authenticate(const Response &res,
7313
                                   std::map<std::string, std::string> &auth,
7314
0
                                   bool is_proxy) {
7315
0
  auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
7316
0
  if (res.has_header(auth_key)) {
7317
0
    thread_local auto re =
7318
0
        std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
7319
0
    auto s = res.get_header_value(auth_key);
7320
0
    auto pos = s.find(' ');
7321
0
    if (pos != std::string::npos) {
7322
0
      auto type = s.substr(0, pos);
7323
0
      if (type == "Basic") {
7324
0
        return false;
7325
0
      } else if (type == "Digest") {
7326
0
        s = s.substr(pos + 1);
7327
0
        auto beg = std::sregex_iterator(s.begin(), s.end(), re);
7328
0
        for (auto i = beg; i != std::sregex_iterator(); ++i) {
7329
0
          const auto &m = *i;
7330
0
          auto key = s.substr(static_cast<size_t>(m.position(1)),
7331
0
                              static_cast<size_t>(m.length(1)));
7332
0
          auto val = m.length(2) > 0
7333
0
                         ? s.substr(static_cast<size_t>(m.position(2)),
7334
0
                                    static_cast<size_t>(m.length(2)))
7335
0
                         : s.substr(static_cast<size_t>(m.position(3)),
7336
0
                                    static_cast<size_t>(m.length(3)));
7337
0
          auth[std::move(key)] = std::move(val);
7338
0
        }
7339
0
        return true;
7340
0
      }
7341
0
    }
7342
0
  }
7343
0
  return false;
7344
0
}
7345
7346
class ContentProviderAdapter {
7347
public:
7348
  explicit ContentProviderAdapter(
7349
      ContentProviderWithoutLength &&content_provider)
7350
0
      : content_provider_(std::move(content_provider)) {}
7351
7352
0
  bool operator()(size_t offset, size_t, DataSink &sink) {
7353
0
    return content_provider_(offset, sink);
7354
0
  }
7355
7356
private:
7357
  ContentProviderWithoutLength content_provider_;
7358
};
7359
7360
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
7361
namespace fields {
7362
7363
1.67M
inline bool is_token_char(char c) {
7364
1.67M
  return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
7365
133k
         c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
7366
44.6k
         c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
7367
1.67M
}
7368
7369
234k
inline bool is_token(const std::string &s) {
7370
234k
  if (s.empty()) { return false; }
7371
1.67M
  for (auto c : s) {
7372
1.67M
    if (!is_token_char(c)) { return false; }
7373
1.67M
  }
7374
233k
  return true;
7375
233k
}
7376
7377
234k
inline bool is_field_name(const std::string &s) { return is_token(s); }
7378
7379
4.77M
inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
7380
7381
377k
inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
7382
7383
4.77M
inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
7384
7385
233k
inline bool is_field_content(const std::string &s) {
7386
233k
  if (s.empty()) { return true; }
7387
7388
134k
  if (s.size() == 1) {
7389
49.1k
    return is_field_vchar(s[0]);
7390
85.4k
  } else if (s.size() == 2) {
7391
4.28k
    return is_field_vchar(s[0]) && is_field_vchar(s[1]);
7392
81.1k
  } else {
7393
81.1k
    size_t i = 0;
7394
7395
81.1k
    if (!is_field_vchar(s[i])) { return false; }
7396
81.1k
    i++;
7397
7398
4.73M
    while (i < s.size() - 1) {
7399
4.65M
      auto c = s[i++];
7400
4.65M
      if (c == ' ' || c == '\t' || is_field_vchar(c)) {
7401
4.65M
      } else {
7402
17
        return false;
7403
17
      }
7404
4.65M
    }
7405
7406
81.1k
    return is_field_vchar(s[i]);
7407
81.1k
  }
7408
134k
}
7409
7410
233k
inline bool is_field_value(const std::string &s) { return is_field_content(s); }
7411
7412
} // namespace fields
7413
7414
} // namespace detail
7415
7416
6.53k
inline const char *status_message(int status) {
7417
6.53k
  switch (status) {
7418
2
  case StatusCode::Continue_100: return "Continue";
7419
0
  case StatusCode::SwitchingProtocol_101: return "Switching Protocol";
7420
0
  case StatusCode::Processing_102: return "Processing";
7421
0
  case StatusCode::EarlyHints_103: return "Early Hints";
7422
1.13k
  case StatusCode::OK_200: return "OK";
7423
0
  case StatusCode::Created_201: return "Created";
7424
0
  case StatusCode::Accepted_202: return "Accepted";
7425
0
  case StatusCode::NonAuthoritativeInformation_203:
7426
0
    return "Non-Authoritative Information";
7427
0
  case StatusCode::NoContent_204: return "No Content";
7428
0
  case StatusCode::ResetContent_205: return "Reset Content";
7429
391
  case StatusCode::PartialContent_206: return "Partial Content";
7430
0
  case StatusCode::MultiStatus_207: return "Multi-Status";
7431
0
  case StatusCode::AlreadyReported_208: return "Already Reported";
7432
0
  case StatusCode::IMUsed_226: return "IM Used";
7433
0
  case StatusCode::MultipleChoices_300: return "Multiple Choices";
7434
0
  case StatusCode::MovedPermanently_301: return "Moved Permanently";
7435
0
  case StatusCode::Found_302: return "Found";
7436
0
  case StatusCode::SeeOther_303: return "See Other";
7437
0
  case StatusCode::NotModified_304: return "Not Modified";
7438
0
  case StatusCode::UseProxy_305: return "Use Proxy";
7439
0
  case StatusCode::unused_306: return "unused";
7440
0
  case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";
7441
0
  case StatusCode::PermanentRedirect_308: return "Permanent Redirect";
7442
4.46k
  case StatusCode::BadRequest_400: return "Bad Request";
7443
0
  case StatusCode::Unauthorized_401: return "Unauthorized";
7444
0
  case StatusCode::PaymentRequired_402: return "Payment Required";
7445
0
  case StatusCode::Forbidden_403: return "Forbidden";
7446
64
  case StatusCode::NotFound_404: return "Not Found";
7447
0
  case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";
7448
0
  case StatusCode::NotAcceptable_406: return "Not Acceptable";
7449
0
  case StatusCode::ProxyAuthenticationRequired_407:
7450
0
    return "Proxy Authentication Required";
7451
0
  case StatusCode::RequestTimeout_408: return "Request Timeout";
7452
0
  case StatusCode::Conflict_409: return "Conflict";
7453
0
  case StatusCode::Gone_410: return "Gone";
7454
0
  case StatusCode::LengthRequired_411: return "Length Required";
7455
0
  case StatusCode::PreconditionFailed_412: return "Precondition Failed";
7456
1
  case StatusCode::PayloadTooLarge_413: return "Payload Too Large";
7457
5
  case StatusCode::UriTooLong_414: return "URI Too Long";
7458
27
  case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";
7459
442
  case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";
7460
0
  case StatusCode::ExpectationFailed_417: return "Expectation Failed";
7461
0
  case StatusCode::ImATeapot_418: return "I'm a teapot";
7462
0
  case StatusCode::MisdirectedRequest_421: return "Misdirected Request";
7463
0
  case StatusCode::UnprocessableContent_422: return "Unprocessable Content";
7464
0
  case StatusCode::Locked_423: return "Locked";
7465
0
  case StatusCode::FailedDependency_424: return "Failed Dependency";
7466
0
  case StatusCode::TooEarly_425: return "Too Early";
7467
0
  case StatusCode::UpgradeRequired_426: return "Upgrade Required";
7468
0
  case StatusCode::PreconditionRequired_428: return "Precondition Required";
7469
0
  case StatusCode::TooManyRequests_429: return "Too Many Requests";
7470
0
  case StatusCode::RequestHeaderFieldsTooLarge_431:
7471
0
    return "Request Header Fields Too Large";
7472
0
  case StatusCode::UnavailableForLegalReasons_451:
7473
0
    return "Unavailable For Legal Reasons";
7474
0
  case StatusCode::NotImplemented_501: return "Not Implemented";
7475
0
  case StatusCode::BadGateway_502: return "Bad Gateway";
7476
0
  case StatusCode::ServiceUnavailable_503: return "Service Unavailable";
7477
0
  case StatusCode::GatewayTimeout_504: return "Gateway Timeout";
7478
0
  case StatusCode::HttpVersionNotSupported_505:
7479
0
    return "HTTP Version Not Supported";
7480
0
  case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";
7481
0
  case StatusCode::InsufficientStorage_507: return "Insufficient Storage";
7482
0
  case StatusCode::LoopDetected_508: return "Loop Detected";
7483
0
  case StatusCode::NotExtended_510: return "Not Extended";
7484
0
  case StatusCode::NetworkAuthenticationRequired_511:
7485
0
    return "Network Authentication Required";
7486
7487
0
  default:
7488
0
  case StatusCode::InternalServerError_500: return "Internal Server Error";
7489
6.53k
  }
7490
6.53k
}
7491
7492
0
inline std::string to_string(const Error error) {
7493
0
  switch (error) {
7494
0
  case Error::Success: return "Success (no error)";
7495
0
  case Error::Unknown: return "Unknown";
7496
0
  case Error::Connection: return "Could not establish connection";
7497
0
  case Error::BindIPAddress: return "Failed to bind IP address";
7498
0
  case Error::Read: return "Failed to read connection";
7499
0
  case Error::Write: return "Failed to write connection";
7500
0
  case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
7501
0
  case Error::Canceled: return "Connection handling canceled";
7502
0
  case Error::SSLConnection: return "SSL connection failed";
7503
0
  case Error::SSLLoadingCerts: return "SSL certificate loading failed";
7504
0
  case Error::SSLServerVerification: return "SSL server verification failed";
7505
0
  case Error::SSLServerHostnameVerification:
7506
0
    return "SSL server hostname verification failed";
7507
0
  case Error::UnsupportedMultipartBoundaryChars:
7508
0
    return "Unsupported HTTP multipart boundary characters";
7509
0
  case Error::Compression: return "Compression failed";
7510
0
  case Error::ConnectionTimeout: return "Connection timed out";
7511
0
  case Error::ProxyConnection: return "Proxy connection failed";
7512
0
  case Error::ConnectionClosed: return "Connection closed by server";
7513
0
  case Error::Timeout: return "Read timeout";
7514
0
  case Error::ResourceExhaustion: return "Resource exhaustion";
7515
0
  case Error::TooManyFormDataFiles: return "Too many form data files";
7516
0
  case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
7517
0
  case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
7518
0
  case Error::ExceedMaxSocketDescriptorCount:
7519
0
    return "Exceeded maximum socket descriptor count";
7520
0
  case Error::InvalidRequestLine: return "Invalid request line";
7521
0
  case Error::InvalidHTTPMethod: return "Invalid HTTP method";
7522
0
  case Error::InvalidHTTPVersion: return "Invalid HTTP version";
7523
0
  case Error::InvalidHeaders: return "Invalid headers";
7524
0
  case Error::MultipartParsing: return "Multipart parsing failed";
7525
0
  case Error::OpenFile: return "Failed to open file";
7526
0
  case Error::Listen: return "Failed to listen on socket";
7527
0
  case Error::GetSockName: return "Failed to get socket name";
7528
0
  case Error::UnsupportedAddressFamily: return "Unsupported address family";
7529
0
  case Error::HTTPParsing: return "HTTP parsing failed";
7530
0
  case Error::InvalidRangeHeader: return "Invalid Range header";
7531
0
  default: break;
7532
0
  }
7533
0
7534
0
  return "Invalid";
7535
0
}
7536
7537
0
inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
7538
0
  os << to_string(obj);
7539
0
  os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
7540
0
  return os;
7541
0
}
7542
7543
0
inline std::string hosted_at(const std::string &hostname) {
7544
0
  std::vector<std::string> addrs;
7545
0
  hosted_at(hostname, addrs);
7546
0
  if (addrs.empty()) { return std::string(); }
7547
0
  return addrs[0];
7548
0
}
7549
7550
inline void hosted_at(const std::string &hostname,
7551
0
                      std::vector<std::string> &addrs) {
7552
0
  struct addrinfo hints;
7553
0
  struct addrinfo *result;
7554
0
7555
0
  memset(&hints, 0, sizeof(struct addrinfo));
7556
0
  hints.ai_family = AF_UNSPEC;
7557
0
  hints.ai_socktype = SOCK_STREAM;
7558
0
  hints.ai_protocol = 0;
7559
0
7560
0
  if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
7561
0
                                       &result, 0)) {
7562
0
#if defined __linux__ && !defined __ANDROID__
7563
0
    res_init();
7564
0
#endif
7565
0
    return;
7566
0
  }
7567
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
7568
0
7569
0
  for (auto rp = result; rp; rp = rp->ai_next) {
7570
0
    const auto &addr =
7571
0
        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
7572
0
    std::string ip;
7573
0
    auto dummy = -1;
7574
0
    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
7575
0
                                dummy)) {
7576
0
      addrs.emplace_back(std::move(ip));
7577
0
    }
7578
0
  }
7579
0
}
7580
7581
0
inline std::string encode_uri_component(const std::string &value) {
7582
0
  std::ostringstream escaped;
7583
0
  escaped.fill('0');
7584
0
  escaped << std::hex;
7585
0
7586
0
  for (auto c : value) {
7587
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
7588
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
7589
0
        c == ')') {
7590
0
      escaped << c;
7591
0
    } else {
7592
0
      escaped << std::uppercase;
7593
0
      escaped << '%' << std::setw(2)
7594
0
              << static_cast<int>(static_cast<unsigned char>(c));
7595
0
      escaped << std::nouppercase;
7596
0
    }
7597
0
  }
7598
0
7599
0
  return escaped.str();
7600
0
}
7601
7602
0
inline std::string encode_uri(const std::string &value) {
7603
0
  std::ostringstream escaped;
7604
0
  escaped.fill('0');
7605
0
  escaped << std::hex;
7606
0
7607
0
  for (auto c : value) {
7608
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
7609
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
7610
0
        c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
7611
0
        c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {
7612
0
      escaped << c;
7613
0
    } else {
7614
0
      escaped << std::uppercase;
7615
0
      escaped << '%' << std::setw(2)
7616
0
              << static_cast<int>(static_cast<unsigned char>(c));
7617
0
      escaped << std::nouppercase;
7618
0
    }
7619
0
  }
7620
0
7621
0
  return escaped.str();
7622
0
}
7623
7624
0
inline std::string decode_uri_component(const std::string &value) {
7625
0
  std::string result;
7626
0
7627
0
  for (size_t i = 0; i < value.size(); i++) {
7628
0
    if (value[i] == '%' && i + 2 < value.size()) {
7629
0
      auto val = 0;
7630
0
      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
7631
0
        result += static_cast<char>(val);
7632
0
        i += 2;
7633
0
      } else {
7634
0
        result += value[i];
7635
0
      }
7636
0
    } else {
7637
0
      result += value[i];
7638
0
    }
7639
0
  }
7640
0
7641
0
  return result;
7642
0
}
7643
7644
0
inline std::string decode_uri(const std::string &value) {
7645
0
  std::string result;
7646
0
7647
0
  for (size_t i = 0; i < value.size(); i++) {
7648
0
    if (value[i] == '%' && i + 2 < value.size()) {
7649
0
      auto val = 0;
7650
0
      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
7651
0
        result += static_cast<char>(val);
7652
0
        i += 2;
7653
0
      } else {
7654
0
        result += value[i];
7655
0
      }
7656
0
    } else {
7657
0
      result += value[i];
7658
0
    }
7659
0
  }
7660
0
7661
0
  return result;
7662
0
}
7663
7664
0
inline std::string encode_path_component(const std::string &component) {
7665
0
  std::string result;
7666
0
  result.reserve(component.size() * 3);
7667
0
7668
0
  for (size_t i = 0; i < component.size(); i++) {
7669
0
    auto c = static_cast<unsigned char>(component[i]);
7670
0
7671
0
    // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
7672
0
    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
7673
0
      result += static_cast<char>(c);
7674
0
    }
7675
0
    // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
7676
0
    // "," / ";" / "="
7677
0
    else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
7678
0
             c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
7679
0
             c == '=') {
7680
0
      result += static_cast<char>(c);
7681
0
    }
7682
0
    // Colon is allowed in path segments except first segment
7683
0
    else if (c == ':') {
7684
0
      result += static_cast<char>(c);
7685
0
    }
7686
0
    // @ is allowed in path
7687
0
    else if (c == '@') {
7688
0
      result += static_cast<char>(c);
7689
0
    } else {
7690
0
      result += '%';
7691
0
      char hex[3];
7692
0
      snprintf(hex, sizeof(hex), "%02X", c);
7693
0
      result.append(hex, 2);
7694
0
    }
7695
0
  }
7696
0
  return result;
7697
0
}
7698
7699
223k
inline std::string decode_path_component(const std::string &component) {
7700
223k
  std::string result;
7701
223k
  result.reserve(component.size());
7702
7703
5.12M
  for (size_t i = 0; i < component.size(); i++) {
7704
4.90M
    if (component[i] == '%' && i + 1 < component.size()) {
7705
93.4k
      if (component[i + 1] == 'u') {
7706
        // Unicode %uXXXX encoding
7707
5.67k
        auto val = 0;
7708
5.67k
        if (detail::from_hex_to_i(component, i + 2, 4, val)) {
7709
          // 4 digits Unicode codes
7710
2.57k
          char buff[4];
7711
2.57k
          size_t len = detail::to_utf8(val, buff);
7712
2.57k
          if (len > 0) { result.append(buff, len); }
7713
2.57k
          i += 5; // 'u0000'
7714
3.09k
        } else {
7715
3.09k
          result += component[i];
7716
3.09k
        }
7717
87.7k
      } else {
7718
        // Standard %XX encoding
7719
87.7k
        auto val = 0;
7720
87.7k
        if (detail::from_hex_to_i(component, i + 1, 2, val)) {
7721
          // 2 digits hex codes
7722
2.74k
          result += static_cast<char>(val);
7723
2.74k
          i += 2; // 'XX'
7724
84.9k
        } else {
7725
84.9k
          result += component[i];
7726
84.9k
        }
7727
87.7k
      }
7728
4.80M
    } else {
7729
4.80M
      result += component[i];
7730
4.80M
    }
7731
4.90M
  }
7732
223k
  return result;
7733
223k
}
7734
7735
inline std::string encode_query_component(const std::string &component,
7736
0
                                          bool space_as_plus) {
7737
0
  std::string result;
7738
0
  result.reserve(component.size() * 3);
7739
0
7740
0
  for (size_t i = 0; i < component.size(); i++) {
7741
0
    auto c = static_cast<unsigned char>(component[i]);
7742
0
7743
0
    // Unreserved characters per RFC 3986
7744
0
    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
7745
0
      result += static_cast<char>(c);
7746
0
    }
7747
0
    // Space handling
7748
0
    else if (c == ' ') {
7749
0
      if (space_as_plus) {
7750
0
        result += '+';
7751
0
      } else {
7752
0
        result += "%20";
7753
0
      }
7754
0
    }
7755
0
    // Plus sign handling
7756
0
    else if (c == '+') {
7757
0
      if (space_as_plus) {
7758
0
        result += "%2B";
7759
0
      } else {
7760
0
        result += static_cast<char>(c);
7761
0
      }
7762
0
    }
7763
0
    // Query-safe sub-delimiters (excluding & and = which are query delimiters)
7764
0
    else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
7765
0
             c == '*' || c == ',' || c == ';') {
7766
0
      result += static_cast<char>(c);
7767
0
    }
7768
0
    // Colon and @ are allowed in query
7769
0
    else if (c == ':' || c == '@') {
7770
0
      result += static_cast<char>(c);
7771
0
    }
7772
0
    // Forward slash is allowed in query values
7773
0
    else if (c == '/') {
7774
0
      result += static_cast<char>(c);
7775
0
    }
7776
0
    // Question mark is allowed in query values (after first ?)
7777
0
    else if (c == '?') {
7778
0
      result += static_cast<char>(c);
7779
0
    } else {
7780
0
      result += '%';
7781
0
      char hex[3];
7782
0
      snprintf(hex, sizeof(hex), "%02X", c);
7783
0
      result.append(hex, 2);
7784
0
    }
7785
0
  }
7786
0
  return result;
7787
0
}
7788
7789
inline std::string decode_query_component(const std::string &component,
7790
11.8k
                                          bool plus_as_space) {
7791
11.8k
  std::string result;
7792
11.8k
  result.reserve(component.size());
7793
7794
408k
  for (size_t i = 0; i < component.size(); i++) {
7795
397k
    if (component[i] == '%' && i + 2 < component.size()) {
7796
6.01k
      std::string hex = component.substr(i + 1, 2);
7797
6.01k
      char *end;
7798
6.01k
      unsigned long value = std::strtoul(hex.c_str(), &end, 16);
7799
6.01k
      if (end == hex.c_str() + 2) {
7800
637
        result += static_cast<char>(value);
7801
637
        i += 2;
7802
5.38k
      } else {
7803
5.38k
        result += component[i];
7804
5.38k
      }
7805
391k
    } else if (component[i] == '+' && plus_as_space) {
7806
600
      result += ' '; // + becomes space in form-urlencoded
7807
390k
    } else {
7808
390k
      result += component[i];
7809
390k
    }
7810
397k
  }
7811
11.8k
  return result;
7812
11.8k
}
7813
7814
inline std::string append_query_params(const std::string &path,
7815
0
                                       const Params &params) {
7816
0
  std::string path_with_query = path;
7817
0
  thread_local const std::regex re("[^?]+\\?.*");
7818
0
  auto delm = std::regex_match(path, re) ? '&' : '?';
7819
0
  path_with_query += delm + detail::params_to_query_str(params);
7820
0
  return path_with_query;
7821
0
}
7822
7823
// Header utilities
7824
inline std::pair<std::string, std::string>
7825
0
make_range_header(const Ranges &ranges) {
7826
0
  std::string field = "bytes=";
7827
0
  auto i = 0;
7828
0
  for (const auto &r : ranges) {
7829
0
    if (i != 0) { field += ", "; }
7830
0
    if (r.first != -1) { field += std::to_string(r.first); }
7831
0
    field += '-';
7832
0
    if (r.second != -1) { field += std::to_string(r.second); }
7833
0
    i++;
7834
0
  }
7835
0
  return std::make_pair("Range", std::move(field));
7836
0
}
7837
7838
inline std::pair<std::string, std::string>
7839
make_basic_authentication_header(const std::string &username,
7840
0
                                 const std::string &password, bool is_proxy) {
7841
0
  auto field = "Basic " + detail::base64_encode(username + ":" + password);
7842
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
7843
0
  return std::make_pair(key, std::move(field));
7844
0
}
7845
7846
inline std::pair<std::string, std::string>
7847
make_bearer_token_authentication_header(const std::string &token,
7848
0
                                        bool is_proxy = false) {
7849
0
  auto field = "Bearer " + token;
7850
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
7851
0
  return std::make_pair(key, std::move(field));
7852
0
}
7853
7854
// Request implementation
7855
14.9k
inline bool Request::has_header(const std::string &key) const {
7856
14.9k
  return detail::has_header(headers, key);
7857
14.9k
}
7858
7859
inline std::string Request::get_header_value(const std::string &key,
7860
30.9k
                                             const char *def, size_t id) const {
7861
30.9k
  return detail::get_header_value(headers, key, def, id);
7862
30.9k
}
7863
7864
0
inline size_t Request::get_header_value_count(const std::string &key) const {
7865
0
  auto r = headers.equal_range(key);
7866
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7867
0
}
7868
7869
inline void Request::set_header(const std::string &key,
7870
0
                                const std::string &val) {
7871
0
  if (detail::fields::is_field_name(key) &&
7872
0
      detail::fields::is_field_value(val)) {
7873
0
    headers.emplace(key, val);
7874
0
  }
7875
0
}
7876
7877
0
inline bool Request::has_trailer(const std::string &key) const {
7878
0
  return trailers.find(key) != trailers.end();
7879
0
}
7880
7881
inline std::string Request::get_trailer_value(const std::string &key,
7882
0
                                              size_t id) const {
7883
0
  auto rng = trailers.equal_range(key);
7884
0
  auto it = rng.first;
7885
0
  std::advance(it, static_cast<ssize_t>(id));
7886
0
  if (it != rng.second) { return it->second; }
7887
0
  return std::string();
7888
0
}
7889
7890
0
inline size_t Request::get_trailer_value_count(const std::string &key) const {
7891
0
  auto r = trailers.equal_range(key);
7892
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7893
0
}
7894
7895
0
inline bool Request::has_param(const std::string &key) const {
7896
0
  return params.find(key) != params.end();
7897
0
}
7898
7899
inline std::string Request::get_param_value(const std::string &key,
7900
0
                                            size_t id) const {
7901
0
  auto rng = params.equal_range(key);
7902
0
  auto it = rng.first;
7903
0
  std::advance(it, static_cast<ssize_t>(id));
7904
0
  if (it != rng.second) { return it->second; }
7905
0
  return std::string();
7906
0
}
7907
7908
0
inline size_t Request::get_param_value_count(const std::string &key) const {
7909
0
  auto r = params.equal_range(key);
7910
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7911
0
}
7912
7913
3.82k
inline bool Request::is_multipart_form_data() const {
7914
3.82k
  const auto &content_type = get_header_value("Content-Type");
7915
3.82k
  return !content_type.rfind("multipart/form-data", 0);
7916
3.82k
}
7917
7918
// Multipart FormData implementation
7919
inline std::string MultipartFormData::get_field(const std::string &key,
7920
0
                                                size_t id) const {
7921
0
  auto rng = fields.equal_range(key);
7922
0
  auto it = rng.first;
7923
0
  std::advance(it, static_cast<ssize_t>(id));
7924
0
  if (it != rng.second) { return it->second.content; }
7925
0
  return std::string();
7926
0
}
7927
7928
inline std::vector<std::string>
7929
0
MultipartFormData::get_fields(const std::string &key) const {
7930
0
  std::vector<std::string> values;
7931
0
  auto rng = fields.equal_range(key);
7932
0
  for (auto it = rng.first; it != rng.second; it++) {
7933
0
    values.push_back(it->second.content);
7934
0
  }
7935
0
  return values;
7936
0
}
7937
7938
0
inline bool MultipartFormData::has_field(const std::string &key) const {
7939
0
  return fields.find(key) != fields.end();
7940
0
}
7941
7942
0
inline size_t MultipartFormData::get_field_count(const std::string &key) const {
7943
0
  auto r = fields.equal_range(key);
7944
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7945
0
}
7946
7947
inline FormData MultipartFormData::get_file(const std::string &key,
7948
0
                                            size_t id) const {
7949
0
  auto rng = files.equal_range(key);
7950
0
  auto it = rng.first;
7951
0
  std::advance(it, static_cast<ssize_t>(id));
7952
0
  if (it != rng.second) { return it->second; }
7953
0
  return FormData();
7954
0
}
7955
7956
inline std::vector<FormData>
7957
0
MultipartFormData::get_files(const std::string &key) const {
7958
0
  std::vector<FormData> values;
7959
0
  auto rng = files.equal_range(key);
7960
0
  for (auto it = rng.first; it != rng.second; it++) {
7961
0
    values.push_back(it->second);
7962
0
  }
7963
0
  return values;
7964
0
}
7965
7966
0
inline bool MultipartFormData::has_file(const std::string &key) const {
7967
0
  return files.find(key) != files.end();
7968
0
}
7969
7970
0
inline size_t MultipartFormData::get_file_count(const std::string &key) const {
7971
0
  auto r = files.equal_range(key);
7972
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7973
0
}
7974
7975
// Response implementation
7976
6.67k
inline bool Response::has_header(const std::string &key) const {
7977
6.67k
  return headers.find(key) != headers.end();
7978
6.67k
}
7979
7980
inline std::string Response::get_header_value(const std::string &key,
7981
                                              const char *def,
7982
1.52k
                                              size_t id) const {
7983
1.52k
  return detail::get_header_value(headers, key, def, id);
7984
1.52k
}
7985
7986
0
inline size_t Response::get_header_value_count(const std::string &key) const {
7987
0
  auto r = headers.equal_range(key);
7988
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7989
0
}
7990
7991
inline void Response::set_header(const std::string &key,
7992
15.4k
                                 const std::string &val) {
7993
15.4k
  if (detail::fields::is_field_name(key) &&
7994
15.4k
      detail::fields::is_field_value(val)) {
7995
15.4k
    headers.emplace(key, val);
7996
15.4k
  }
7997
15.4k
}
7998
0
inline bool Response::has_trailer(const std::string &key) const {
7999
0
  return trailers.find(key) != trailers.end();
8000
0
}
8001
8002
inline std::string Response::get_trailer_value(const std::string &key,
8003
0
                                               size_t id) const {
8004
0
  auto rng = trailers.equal_range(key);
8005
0
  auto it = rng.first;
8006
0
  std::advance(it, static_cast<ssize_t>(id));
8007
0
  if (it != rng.second) { return it->second; }
8008
0
  return std::string();
8009
0
}
8010
8011
0
inline size_t Response::get_trailer_value_count(const std::string &key) const {
8012
0
  auto r = trailers.equal_range(key);
8013
0
  return static_cast<size_t>(std::distance(r.first, r.second));
8014
0
}
8015
8016
0
inline void Response::set_redirect(const std::string &url, int stat) {
8017
0
  if (detail::fields::is_field_value(url)) {
8018
0
    set_header("Location", url);
8019
0
    if (300 <= stat && stat < 400) {
8020
0
      this->status = stat;
8021
0
    } else {
8022
0
      this->status = StatusCode::Found_302;
8023
0
    }
8024
0
  }
8025
0
}
8026
8027
inline void Response::set_content(const char *s, size_t n,
8028
0
                                  const std::string &content_type) {
8029
0
  body.assign(s, n);
8030
0
8031
0
  auto rng = headers.equal_range("Content-Type");
8032
0
  headers.erase(rng.first, rng.second);
8033
0
  set_header("Content-Type", content_type);
8034
0
}
8035
8036
inline void Response::set_content(const std::string &s,
8037
0
                                  const std::string &content_type) {
8038
0
  set_content(s.data(), s.size(), content_type);
8039
0
}
8040
8041
inline void Response::set_content(std::string &&s,
8042
1.83k
                                  const std::string &content_type) {
8043
1.83k
  body = std::move(s);
8044
8045
1.83k
  auto rng = headers.equal_range("Content-Type");
8046
1.83k
  headers.erase(rng.first, rng.second);
8047
1.83k
  set_header("Content-Type", content_type);
8048
1.83k
}
8049
8050
inline void Response::set_content_provider(
8051
    size_t in_length, const std::string &content_type, ContentProvider provider,
8052
0
    ContentProviderResourceReleaser resource_releaser) {
8053
0
  set_header("Content-Type", content_type);
8054
0
  content_length_ = in_length;
8055
0
  if (in_length > 0) { content_provider_ = std::move(provider); }
8056
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
8057
0
  is_chunked_content_provider_ = false;
8058
0
}
8059
8060
inline void Response::set_content_provider(
8061
    const std::string &content_type, ContentProviderWithoutLength provider,
8062
0
    ContentProviderResourceReleaser resource_releaser) {
8063
0
  set_header("Content-Type", content_type);
8064
0
  content_length_ = 0;
8065
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
8066
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
8067
0
  is_chunked_content_provider_ = false;
8068
0
}
8069
8070
inline void Response::set_chunked_content_provider(
8071
    const std::string &content_type, ContentProviderWithoutLength provider,
8072
0
    ContentProviderResourceReleaser resource_releaser) {
8073
0
  set_header("Content-Type", content_type);
8074
0
  content_length_ = 0;
8075
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
8076
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
8077
0
  is_chunked_content_provider_ = true;
8078
0
}
8079
8080
inline void Response::set_file_content(const std::string &path,
8081
0
                                       const std::string &content_type) {
8082
0
  file_content_path_ = path;
8083
0
  file_content_content_type_ = content_type;
8084
0
}
8085
8086
0
inline void Response::set_file_content(const std::string &path) {
8087
0
  file_content_path_ = path;
8088
0
}
8089
8090
// Result implementation
8091
0
inline bool Result::has_request_header(const std::string &key) const {
8092
0
  return request_headers_.find(key) != request_headers_.end();
8093
0
}
8094
8095
inline std::string Result::get_request_header_value(const std::string &key,
8096
                                                    const char *def,
8097
0
                                                    size_t id) const {
8098
0
  return detail::get_header_value(request_headers_, key, def, id);
8099
0
}
8100
8101
inline size_t
8102
0
Result::get_request_header_value_count(const std::string &key) const {
8103
0
  auto r = request_headers_.equal_range(key);
8104
0
  return static_cast<size_t>(std::distance(r.first, r.second));
8105
0
}
8106
8107
// Stream implementation
8108
6.53k
inline ssize_t Stream::write(const char *ptr) {
8109
6.53k
  return write(ptr, strlen(ptr));
8110
6.53k
}
8111
8112
0
inline ssize_t Stream::write(const std::string &s) {
8113
0
  return write(s.data(), s.size());
8114
0
}
8115
8116
// BodyReader implementation
8117
3.84k
inline ssize_t detail::BodyReader::read(char *buf, size_t len) {
8118
3.84k
  if (!stream) {
8119
0
    last_error = Error::Connection;
8120
0
    return -1;
8121
0
  }
8122
3.84k
  if (eof) { return 0; }
8123
8124
3.84k
  if (!chunked) {
8125
    // Content-Length based reading
8126
3.84k
    if (bytes_read >= content_length) {
8127
0
      eof = true;
8128
0
      return 0;
8129
0
    }
8130
8131
3.84k
    auto remaining = content_length - bytes_read;
8132
3.84k
    auto to_read = (std::min)(len, remaining);
8133
3.84k
    auto n = stream->read(buf, to_read);
8134
8135
3.84k
    if (n < 0) {
8136
0
      last_error = stream->get_error();
8137
0
      if (last_error == Error::Success) { last_error = Error::Read; }
8138
0
      eof = true;
8139
0
      return n;
8140
0
    }
8141
3.84k
    if (n == 0) {
8142
      // Unexpected EOF before content_length
8143
1.26k
      last_error = stream->get_error();
8144
1.26k
      if (last_error == Error::Success) { last_error = Error::Read; }
8145
1.26k
      eof = true;
8146
1.26k
      return 0;
8147
1.26k
    }
8148
8149
2.58k
    bytes_read += static_cast<size_t>(n);
8150
2.58k
    if (bytes_read >= content_length) { eof = true; }
8151
2.58k
    return n;
8152
3.84k
  }
8153
8154
  // Chunked transfer encoding: delegate to shared decoder instance.
8155
0
  if (!chunked_decoder) { chunked_decoder.reset(new ChunkedDecoder(*stream)); }
8156
8157
0
  size_t chunk_offset = 0;
8158
0
  size_t chunk_total = 0;
8159
0
  auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
8160
0
  if (n < 0) {
8161
0
    last_error = stream->get_error();
8162
0
    if (last_error == Error::Success) { last_error = Error::Read; }
8163
0
    eof = true;
8164
0
    return n;
8165
0
  }
8166
8167
0
  if (n == 0) {
8168
    // Final chunk observed. Leave trailer parsing to the caller (StreamHandle).
8169
0
    eof = true;
8170
0
    return 0;
8171
0
  }
8172
8173
0
  bytes_read += static_cast<size_t>(n);
8174
0
  return n;
8175
0
}
8176
8177
namespace detail {
8178
8179
inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
8180
                                time_t timeout_sec, time_t timeout_usec,
8181
                                time_t &actual_timeout_sec,
8182
0
                                time_t &actual_timeout_usec) {
8183
0
  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
8184
8185
0
  auto actual_timeout_msec =
8186
0
      (std::min)(max_timeout_msec - duration_msec, timeout_msec);
8187
8188
0
  if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
8189
8190
0
  actual_timeout_sec = actual_timeout_msec / 1000;
8191
0
  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
8192
0
}
8193
8194
// Socket stream implementation
8195
inline SocketStream::SocketStream(
8196
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
8197
    time_t write_timeout_sec, time_t write_timeout_usec,
8198
    time_t max_timeout_msec,
8199
    std::chrono::time_point<std::chrono::steady_clock> start_time)
8200
0
    : sock_(sock), read_timeout_sec_(read_timeout_sec),
8201
0
      read_timeout_usec_(read_timeout_usec),
8202
0
      write_timeout_sec_(write_timeout_sec),
8203
0
      write_timeout_usec_(write_timeout_usec),
8204
0
      max_timeout_msec_(max_timeout_msec), start_time_(start_time),
8205
0
      read_buff_(read_buff_size_, 0) {}
8206
8207
0
inline SocketStream::~SocketStream() = default;
8208
8209
0
inline bool SocketStream::is_readable() const {
8210
0
  return read_buff_off_ < read_buff_content_size_;
8211
0
}
8212
8213
0
inline bool SocketStream::wait_readable() const {
8214
0
  if (max_timeout_msec_ <= 0) {
8215
0
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
8216
0
  }
8217
8218
0
  time_t read_timeout_sec;
8219
0
  time_t read_timeout_usec;
8220
0
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
8221
0
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
8222
8223
0
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
8224
0
}
8225
8226
0
inline bool SocketStream::wait_writable() const {
8227
0
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
8228
0
         is_socket_alive(sock_);
8229
0
}
8230
8231
0
inline ssize_t SocketStream::read(char *ptr, size_t size) {
8232
#ifdef _WIN32
8233
  size =
8234
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
8235
#else
8236
0
  size = (std::min)(size,
8237
0
                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
8238
0
#endif
8239
8240
0
  if (read_buff_off_ < read_buff_content_size_) {
8241
0
    auto remaining_size = read_buff_content_size_ - read_buff_off_;
8242
0
    if (size <= remaining_size) {
8243
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, size);
8244
0
      read_buff_off_ += size;
8245
0
      return static_cast<ssize_t>(size);
8246
0
    } else {
8247
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
8248
0
      read_buff_off_ += remaining_size;
8249
0
      return static_cast<ssize_t>(remaining_size);
8250
0
    }
8251
0
  }
8252
8253
0
  if (!wait_readable()) {
8254
0
    error_ = Error::Timeout;
8255
0
    return -1;
8256
0
  }
8257
8258
0
  read_buff_off_ = 0;
8259
0
  read_buff_content_size_ = 0;
8260
8261
0
  if (size < read_buff_size_) {
8262
0
    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
8263
0
                         CPPHTTPLIB_RECV_FLAGS);
8264
0
    if (n <= 0) {
8265
0
      if (n == 0) {
8266
0
        error_ = Error::ConnectionClosed;
8267
0
      } else {
8268
0
        error_ = Error::Read;
8269
0
      }
8270
0
      return n;
8271
0
    } else if (n <= static_cast<ssize_t>(size)) {
8272
0
      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
8273
0
      return n;
8274
0
    } else {
8275
0
      memcpy(ptr, read_buff_.data(), size);
8276
0
      read_buff_off_ = size;
8277
0
      read_buff_content_size_ = static_cast<size_t>(n);
8278
0
      return static_cast<ssize_t>(size);
8279
0
    }
8280
0
  } else {
8281
0
    auto n = read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
8282
0
    if (n <= 0) {
8283
0
      if (n == 0) {
8284
0
        error_ = Error::ConnectionClosed;
8285
0
      } else {
8286
0
        error_ = Error::Read;
8287
0
      }
8288
0
    }
8289
0
    return n;
8290
0
  }
8291
0
}
8292
8293
0
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
8294
0
  if (!wait_writable()) { return -1; }
8295
8296
#if defined(_WIN32) && !defined(_WIN64)
8297
  size =
8298
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
8299
#endif
8300
8301
0
  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
8302
0
}
8303
8304
inline void SocketStream::get_remote_ip_and_port(std::string &ip,
8305
0
                                                 int &port) const {
8306
0
  return detail::get_remote_ip_and_port(sock_, ip, port);
8307
0
}
8308
8309
inline void SocketStream::get_local_ip_and_port(std::string &ip,
8310
0
                                                int &port) const {
8311
0
  return detail::get_local_ip_and_port(sock_, ip, port);
8312
0
}
8313
8314
0
inline socket_t SocketStream::socket() const { return sock_; }
8315
8316
0
inline time_t SocketStream::duration() const {
8317
0
  return std::chrono::duration_cast<std::chrono::milliseconds>(
8318
0
             std::chrono::steady_clock::now() - start_time_)
8319
0
      .count();
8320
0
}
8321
8322
// Buffer stream implementation
8323
0
inline bool BufferStream::is_readable() const { return true; }
8324
8325
0
inline bool BufferStream::wait_readable() const { return true; }
8326
8327
0
inline bool BufferStream::wait_writable() const { return true; }
8328
8329
0
inline ssize_t BufferStream::read(char *ptr, size_t size) {
8330
#if defined(_MSC_VER) && _MSC_VER < 1910
8331
  auto len_read = buffer._Copy_s(ptr, size, size, position);
8332
#else
8333
0
  auto len_read = buffer.copy(ptr, size, position);
8334
0
#endif
8335
0
  position += static_cast<size_t>(len_read);
8336
0
  return static_cast<ssize_t>(len_read);
8337
0
}
8338
8339
28.3k
inline ssize_t BufferStream::write(const char *ptr, size_t size) {
8340
28.3k
  buffer.append(ptr, size);
8341
28.3k
  return static_cast<ssize_t>(size);
8342
28.3k
}
8343
8344
inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
8345
0
                                                 int & /*port*/) const {}
8346
8347
inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
8348
0
                                                int & /*port*/) const {}
8349
8350
0
inline socket_t BufferStream::socket() const { return 0; }
8351
8352
0
inline time_t BufferStream::duration() const { return 0; }
8353
8354
6.53k
inline const std::string &BufferStream::get_buffer() const { return buffer; }
8355
8356
inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
8357
0
    : MatcherBase(pattern) {
8358
0
  constexpr const char marker[] = "/:";
8359
8360
  // One past the last ending position of a path param substring
8361
0
  std::size_t last_param_end = 0;
8362
8363
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
8364
  // Needed to ensure that parameter names are unique during matcher
8365
  // construction
8366
  // If exceptions are disabled, only last duplicate path
8367
  // parameter will be set
8368
0
  std::unordered_set<std::string> param_name_set;
8369
0
#endif
8370
8371
0
  while (true) {
8372
0
    const auto marker_pos = pattern.find(
8373
0
        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
8374
0
    if (marker_pos == std::string::npos) { break; }
8375
8376
0
    static_fragments_.push_back(
8377
0
        pattern.substr(last_param_end, marker_pos - last_param_end + 1));
8378
8379
0
    const auto param_name_start = marker_pos + str_len(marker);
8380
8381
0
    auto sep_pos = pattern.find(separator, param_name_start);
8382
0
    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
8383
8384
0
    auto param_name =
8385
0
        pattern.substr(param_name_start, sep_pos - param_name_start);
8386
8387
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
8388
0
    if (param_name_set.find(param_name) != param_name_set.cend()) {
8389
0
      std::string msg = "Encountered path parameter '" + param_name +
8390
0
                        "' multiple times in route pattern '" + pattern + "'.";
8391
0
      throw std::invalid_argument(msg);
8392
0
    }
8393
0
#endif
8394
8395
0
    param_names_.push_back(std::move(param_name));
8396
8397
0
    last_param_end = sep_pos + 1;
8398
0
  }
8399
8400
0
  if (last_param_end < pattern.length()) {
8401
0
    static_fragments_.push_back(pattern.substr(last_param_end));
8402
0
  }
8403
0
}
8404
8405
0
inline bool PathParamsMatcher::match(Request &request) const {
8406
0
  request.matches = std::smatch();
8407
0
  request.path_params.clear();
8408
0
  request.path_params.reserve(param_names_.size());
8409
8410
  // One past the position at which the path matched the pattern last time
8411
0
  std::size_t starting_pos = 0;
8412
0
  for (size_t i = 0; i < static_fragments_.size(); ++i) {
8413
0
    const auto &fragment = static_fragments_[i];
8414
8415
0
    if (starting_pos + fragment.length() > request.path.length()) {
8416
0
      return false;
8417
0
    }
8418
8419
    // Avoid unnecessary allocation by using strncmp instead of substr +
8420
    // comparison
8421
0
    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
8422
0
                     fragment.length()) != 0) {
8423
0
      return false;
8424
0
    }
8425
8426
0
    starting_pos += fragment.length();
8427
8428
    // Should only happen when we have a static fragment after a param
8429
    // Example: '/users/:id/subscriptions'
8430
    // The 'subscriptions' fragment here does not have a corresponding param
8431
0
    if (i >= param_names_.size()) { continue; }
8432
8433
0
    auto sep_pos = request.path.find(separator, starting_pos);
8434
0
    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
8435
8436
0
    const auto &param_name = param_names_[i];
8437
8438
0
    request.path_params.emplace(
8439
0
        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
8440
8441
    // Mark everything up to '/' as matched
8442
0
    starting_pos = sep_pos + 1;
8443
0
  }
8444
  // Returns false if the path is longer than the pattern
8445
0
  return starting_pos >= request.path.length();
8446
0
}
8447
8448
1.89k
inline bool RegexMatcher::match(Request &request) const {
8449
1.89k
  request.path_params.clear();
8450
1.89k
  return std::regex_match(request.path, request.matches, regex_);
8451
1.89k
}
8452
8453
// Enclose IPv6 address in brackets if needed
8454
0
inline std::string prepare_host_string(const std::string &host) {
8455
0
  // Enclose IPv6 address in brackets (but not if already enclosed)
8456
0
  if (host.find(':') == std::string::npos ||
8457
0
      (!host.empty() && host[0] == '[')) {
8458
0
    // IPv4, hostname, or already bracketed IPv6
8459
0
    return host;
8460
0
  } else {
8461
0
    // IPv6 address without brackets
8462
0
    return "[" + host + "]";
8463
0
  }
8464
0
}
8465
8466
inline std::string make_host_and_port_string(const std::string &host, int port,
8467
0
                                             bool is_ssl) {
8468
0
  auto result = prepare_host_string(host);
8469
0
8470
0
  // Append port if not default
8471
0
  if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {
8472
0
    ; // do nothing
8473
0
  } else {
8474
0
    result += ":" + std::to_string(port);
8475
0
  }
8476
0
8477
0
  return result;
8478
0
}
8479
8480
// Create "host:port" string always including port number (for CONNECT method)
8481
inline std::string
8482
0
make_host_and_port_string_always_port(const std::string &host, int port) {
8483
0
  return prepare_host_string(host) + ":" + std::to_string(port);
8484
0
}
8485
8486
template <typename T>
8487
inline bool check_and_write_headers(Stream &strm, Headers &headers,
8488
0
                                    T header_writer, Error &error) {
8489
0
  for (const auto &h : headers) {
8490
0
    if (!detail::fields::is_field_name(h.first) ||
8491
0
        !detail::fields::is_field_value(h.second)) {
8492
0
      error = Error::InvalidHeaders;
8493
0
      return false;
8494
0
    }
8495
0
  }
8496
0
  if (header_writer(strm, headers) <= 0) {
8497
0
    error = Error::Write;
8498
0
    return false;
8499
0
  }
8500
0
  return true;
8501
0
}
8502
8503
} // namespace detail
8504
8505
// HTTP server implementation
8506
inline Server::Server()
8507
2
    : new_task_queue(
8508
2
          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
8509
2
#ifndef _WIN32
8510
2
  signal(SIGPIPE, SIG_IGN);
8511
2
#endif
8512
2
}
8513
8514
0
inline Server::~Server() = default;
8515
8516
inline std::unique_ptr<detail::MatcherBase>
8517
12
Server::make_matcher(const std::string &pattern) {
8518
12
  if (pattern.find("/:") != std::string::npos) {
8519
0
    return detail::make_unique<detail::PathParamsMatcher>(pattern);
8520
12
  } else {
8521
12
    return detail::make_unique<detail::RegexMatcher>(pattern);
8522
12
  }
8523
12
}
8524
8525
2
inline Server &Server::Get(const std::string &pattern, Handler handler) {
8526
2
  get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8527
2
  return *this;
8528
2
}
8529
8530
2
inline Server &Server::Post(const std::string &pattern, Handler handler) {
8531
2
  post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8532
2
  return *this;
8533
2
}
8534
8535
inline Server &Server::Post(const std::string &pattern,
8536
0
                            HandlerWithContentReader handler) {
8537
0
  post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8538
0
                                                 std::move(handler));
8539
0
  return *this;
8540
0
}
8541
8542
2
inline Server &Server::Put(const std::string &pattern, Handler handler) {
8543
2
  put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8544
2
  return *this;
8545
2
}
8546
8547
inline Server &Server::Put(const std::string &pattern,
8548
0
                           HandlerWithContentReader handler) {
8549
0
  put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8550
0
                                                std::move(handler));
8551
0
  return *this;
8552
0
}
8553
8554
2
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
8555
2
  patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8556
2
  return *this;
8557
2
}
8558
8559
inline Server &Server::Patch(const std::string &pattern,
8560
0
                             HandlerWithContentReader handler) {
8561
0
  patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8562
0
                                                  std::move(handler));
8563
0
  return *this;
8564
0
}
8565
8566
2
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
8567
2
  delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8568
2
  return *this;
8569
2
}
8570
8571
inline Server &Server::Delete(const std::string &pattern,
8572
0
                              HandlerWithContentReader handler) {
8573
0
  delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8574
0
                                                   std::move(handler));
8575
0
  return *this;
8576
0
}
8577
8578
2
inline Server &Server::Options(const std::string &pattern, Handler handler) {
8579
2
  options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8580
2
  return *this;
8581
2
}
8582
8583
inline bool Server::set_base_dir(const std::string &dir,
8584
0
                                 const std::string &mount_point) {
8585
0
  return set_mount_point(mount_point, dir);
8586
0
}
8587
8588
inline bool Server::set_mount_point(const std::string &mount_point,
8589
0
                                    const std::string &dir, Headers headers) {
8590
0
  detail::FileStat stat(dir);
8591
0
  if (stat.is_dir()) {
8592
0
    std::string mnt = !mount_point.empty() ? mount_point : "/";
8593
0
    if (!mnt.empty() && mnt[0] == '/') {
8594
0
      base_dirs_.push_back({std::move(mnt), dir, std::move(headers)});
8595
0
      return true;
8596
0
    }
8597
0
  }
8598
0
  return false;
8599
0
}
8600
8601
0
inline bool Server::remove_mount_point(const std::string &mount_point) {
8602
0
  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
8603
0
    if (it->mount_point == mount_point) {
8604
0
      base_dirs_.erase(it);
8605
0
      return true;
8606
0
    }
8607
0
  }
8608
0
  return false;
8609
0
}
8610
8611
inline Server &
8612
Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
8613
0
                                                const std::string &mime) {
8614
0
  file_extension_and_mimetype_map_[ext] = mime;
8615
0
  return *this;
8616
0
}
8617
8618
0
inline Server &Server::set_default_file_mimetype(const std::string &mime) {
8619
0
  default_file_mimetype_ = mime;
8620
0
  return *this;
8621
0
}
8622
8623
0
inline Server &Server::set_file_request_handler(Handler handler) {
8624
0
  file_request_handler_ = std::move(handler);
8625
0
  return *this;
8626
0
}
8627
8628
inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
8629
0
                                              std::true_type) {
8630
0
  error_handler_ = std::move(handler);
8631
0
  return *this;
8632
0
}
8633
8634
inline Server &Server::set_error_handler_core(Handler handler,
8635
0
                                              std::false_type) {
8636
0
  error_handler_ = [handler](const Request &req, Response &res) {
8637
0
    handler(req, res);
8638
0
    return HandlerResponse::Handled;
8639
0
  };
8640
0
  return *this;
8641
0
}
8642
8643
0
inline Server &Server::set_exception_handler(ExceptionHandler handler) {
8644
0
  exception_handler_ = std::move(handler);
8645
0
  return *this;
8646
0
}
8647
8648
0
inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
8649
0
  pre_routing_handler_ = std::move(handler);
8650
0
  return *this;
8651
0
}
8652
8653
0
inline Server &Server::set_post_routing_handler(Handler handler) {
8654
0
  post_routing_handler_ = std::move(handler);
8655
0
  return *this;
8656
0
}
8657
8658
0
inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
8659
0
  pre_request_handler_ = std::move(handler);
8660
0
  return *this;
8661
0
}
8662
8663
0
inline Server &Server::set_logger(Logger logger) {
8664
0
  logger_ = std::move(logger);
8665
0
  return *this;
8666
0
}
8667
8668
0
inline Server &Server::set_error_logger(ErrorLogger error_logger) {
8669
0
  error_logger_ = std::move(error_logger);
8670
0
  return *this;
8671
0
}
8672
8673
0
inline Server &Server::set_pre_compression_logger(Logger logger) {
8674
0
  pre_compression_logger_ = std::move(logger);
8675
0
  return *this;
8676
0
}
8677
8678
inline Server &
8679
0
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
8680
0
  expect_100_continue_handler_ = std::move(handler);
8681
0
  return *this;
8682
0
}
8683
8684
0
inline Server &Server::set_address_family(int family) {
8685
0
  address_family_ = family;
8686
0
  return *this;
8687
0
}
8688
8689
0
inline Server &Server::set_tcp_nodelay(bool on) {
8690
0
  tcp_nodelay_ = on;
8691
0
  return *this;
8692
0
}
8693
8694
0
inline Server &Server::set_ipv6_v6only(bool on) {
8695
0
  ipv6_v6only_ = on;
8696
0
  return *this;
8697
0
}
8698
8699
0
inline Server &Server::set_socket_options(SocketOptions socket_options) {
8700
0
  socket_options_ = std::move(socket_options);
8701
0
  return *this;
8702
0
}
8703
8704
0
inline Server &Server::set_default_headers(Headers headers) {
8705
0
  default_headers_ = std::move(headers);
8706
0
  return *this;
8707
0
}
8708
8709
inline Server &Server::set_header_writer(
8710
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
8711
0
  header_writer_ = writer;
8712
0
  return *this;
8713
0
}
8714
8715
inline Server &
8716
0
Server::set_trusted_proxies(const std::vector<std::string> &proxies) {
8717
0
  trusted_proxies_ = proxies;
8718
0
  return *this;
8719
0
}
8720
8721
0
inline Server &Server::set_keep_alive_max_count(size_t count) {
8722
0
  keep_alive_max_count_ = count;
8723
0
  return *this;
8724
0
}
8725
8726
0
inline Server &Server::set_keep_alive_timeout(time_t sec) {
8727
0
  keep_alive_timeout_sec_ = sec;
8728
0
  return *this;
8729
0
}
8730
8731
0
inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
8732
0
  read_timeout_sec_ = sec;
8733
0
  read_timeout_usec_ = usec;
8734
0
  return *this;
8735
0
}
8736
8737
0
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
8738
0
  write_timeout_sec_ = sec;
8739
0
  write_timeout_usec_ = usec;
8740
0
  return *this;
8741
0
}
8742
8743
0
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
8744
0
  idle_interval_sec_ = sec;
8745
0
  idle_interval_usec_ = usec;
8746
0
  return *this;
8747
0
}
8748
8749
0
inline Server &Server::set_payload_max_length(size_t length) {
8750
0
  payload_max_length_ = length;
8751
0
  return *this;
8752
0
}
8753
8754
inline bool Server::bind_to_port(const std::string &host, int port,
8755
0
                                 int socket_flags) {
8756
0
  auto ret = bind_internal(host, port, socket_flags);
8757
0
  if (ret == -1) { is_decommissioned = true; }
8758
0
  return ret >= 0;
8759
0
}
8760
0
inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
8761
0
  auto ret = bind_internal(host, 0, socket_flags);
8762
0
  if (ret == -1) { is_decommissioned = true; }
8763
0
  return ret;
8764
0
}
8765
8766
0
inline bool Server::listen_after_bind() { return listen_internal(); }
8767
8768
inline bool Server::listen(const std::string &host, int port,
8769
0
                           int socket_flags) {
8770
0
  return bind_to_port(host, port, socket_flags) && listen_internal();
8771
0
}
8772
8773
0
inline bool Server::is_running() const { return is_running_; }
8774
8775
0
inline void Server::wait_until_ready() const {
8776
0
  while (!is_running_ && !is_decommissioned) {
8777
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
8778
0
  }
8779
0
}
8780
8781
0
inline void Server::stop() {
8782
0
  if (is_running_) {
8783
0
    assert(svr_sock_ != INVALID_SOCKET);
8784
0
    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
8785
0
    detail::shutdown_socket(sock);
8786
0
    detail::close_socket(sock);
8787
0
  }
8788
0
  is_decommissioned = false;
8789
0
}
8790
8791
0
inline void Server::decommission() { is_decommissioned = true; }
8792
8793
6.53k
inline bool Server::parse_request_line(const char *s, Request &req) const {
8794
6.53k
  auto len = strlen(s);
8795
6.53k
  if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
8796
6.46k
  len -= 2;
8797
8798
6.46k
  {
8799
6.46k
    size_t count = 0;
8800
8801
35.9k
    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
8802
35.9k
      switch (count) {
8803
6.43k
      case 0: req.method = std::string(b, e); break;
8804
6.31k
      case 1: req.target = std::string(b, e); break;
8805
6.30k
      case 2: req.version = std::string(b, e); break;
8806
16.9k
      default: break;
8807
35.9k
      }
8808
35.9k
      count++;
8809
35.9k
    });
8810
8811
6.46k
    if (count != 3) { return false; }
8812
6.46k
  }
8813
8814
6.27k
  thread_local const std::set<std::string> methods{
8815
6.27k
      "GET",     "HEAD",    "POST",  "PUT",   "DELETE",
8816
6.27k
      "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
8817
8818
6.27k
  if (methods.find(req.method) == methods.end()) {
8819
67
    output_error_log(Error::InvalidHTTPMethod, &req);
8820
67
    return false;
8821
67
  }
8822
8823
6.20k
  if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
8824
22
    output_error_log(Error::InvalidHTTPVersion, &req);
8825
22
    return false;
8826
22
  }
8827
8828
6.18k
  {
8829
    // Skip URL fragment
8830
555k
    for (size_t i = 0; i < req.target.size(); i++) {
8831
548k
      if (req.target[i] == '#') {
8832
37
        req.target.erase(i);
8833
37
        break;
8834
37
      }
8835
548k
    }
8836
8837
6.18k
    detail::divide(req.target, '?',
8838
6.18k
                   [&](const char *lhs_data, std::size_t lhs_size,
8839
6.18k
                       const char *rhs_data, std::size_t rhs_size) {
8840
6.18k
                     req.path =
8841
6.18k
                         decode_path_component(std::string(lhs_data, lhs_size));
8842
6.18k
                     detail::parse_query_text(rhs_data, rhs_size, req.params);
8843
6.18k
                   });
8844
6.18k
  }
8845
8846
6.18k
  return true;
8847
6.20k
}
8848
8849
inline bool Server::write_response(Stream &strm, bool close_connection,
8850
5.00k
                                   Request &req, Response &res) {
8851
  // NOTE: `req.ranges` should be empty, otherwise it will be applied
8852
  // incorrectly to the error content.
8853
5.00k
  req.ranges.clear();
8854
5.00k
  return write_response_core(strm, close_connection, req, res, false);
8855
5.00k
}
8856
8857
inline bool Server::write_response_with_content(Stream &strm,
8858
                                                bool close_connection,
8859
                                                const Request &req,
8860
1.52k
                                                Response &res) {
8861
1.52k
  return write_response_core(strm, close_connection, req, res, true);
8862
1.52k
}
8863
8864
inline bool Server::write_response_core(Stream &strm, bool close_connection,
8865
                                        const Request &req, Response &res,
8866
6.53k
                                        bool need_apply_ranges) {
8867
6.53k
  assert(res.status != -1);
8868
8869
6.53k
  if (400 <= res.status && error_handler_ &&
8870
0
      error_handler_(req, res) == HandlerResponse::Handled) {
8871
0
    need_apply_ranges = true;
8872
0
  }
8873
8874
6.53k
  std::string content_type;
8875
6.53k
  std::string boundary;
8876
6.53k
  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
8877
8878
  // Prepare additional headers
8879
6.53k
  if (close_connection || req.get_header_value("Connection") == "close" ||
8880
6.53k
      400 <= res.status) { // Don't leave connections open after errors
8881
5.00k
    res.set_header("Connection", "close");
8882
5.00k
  } else {
8883
1.52k
    std::string s = "timeout=";
8884
1.52k
    s += std::to_string(keep_alive_timeout_sec_);
8885
1.52k
    s += ", max=";
8886
1.52k
    s += std::to_string(keep_alive_max_count_);
8887
1.52k
    res.set_header("Keep-Alive", s);
8888
1.52k
  }
8889
8890
6.53k
  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
8891
1.52k
      !res.has_header("Content-Type")) {
8892
0
    res.set_header("Content-Type", "text/plain");
8893
0
  }
8894
8895
6.53k
  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
8896
5.00k
      !res.has_header("Content-Length")) {
8897
5.00k
    res.set_header("Content-Length", "0");
8898
5.00k
  }
8899
8900
6.53k
  if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
8901
143
    res.set_header("Accept-Ranges", "bytes");
8902
143
  }
8903
8904
6.53k
  if (post_routing_handler_) { post_routing_handler_(req, res); }
8905
8906
  // Response line and headers
8907
6.53k
  {
8908
6.53k
    detail::BufferStream bstrm;
8909
6.53k
    if (!detail::write_response_line(bstrm, res.status)) { return false; }
8910
6.53k
    if (header_writer_(bstrm, res.headers) <= 0) { return false; }
8911
8912
    // Flush buffer
8913
6.53k
    auto &data = bstrm.get_buffer();
8914
6.53k
    detail::write_data(strm, data.data(), data.size());
8915
6.53k
  }
8916
8917
  // Body
8918
0
  auto ret = true;
8919
6.53k
  if (req.method != "HEAD") {
8920
6.39k
    if (!res.body.empty()) {
8921
1.44k
      if (!detail::write_data(strm, res.body.data(), res.body.size())) {
8922
0
        ret = false;
8923
0
      }
8924
4.94k
    } else if (res.content_provider_) {
8925
0
      if (write_content_with_provider(strm, req, res, boundary, content_type)) {
8926
0
        res.content_provider_success_ = true;
8927
0
      } else {
8928
0
        ret = false;
8929
0
      }
8930
0
    }
8931
6.39k
  }
8932
8933
  // Log
8934
6.53k
  output_log(req, res);
8935
8936
6.53k
  return ret;
8937
6.53k
}
8938
8939
inline bool
8940
Server::write_content_with_provider(Stream &strm, const Request &req,
8941
                                    Response &res, const std::string &boundary,
8942
0
                                    const std::string &content_type) {
8943
0
  auto is_shutting_down = [this]() {
8944
0
    return this->svr_sock_ == INVALID_SOCKET;
8945
0
  };
8946
8947
0
  if (res.content_length_ > 0) {
8948
0
    if (req.ranges.empty()) {
8949
0
      return detail::write_content(strm, res.content_provider_, 0,
8950
0
                                   res.content_length_, is_shutting_down);
8951
0
    } else if (req.ranges.size() == 1) {
8952
0
      auto offset_and_length = detail::get_range_offset_and_length(
8953
0
          req.ranges[0], res.content_length_);
8954
8955
0
      return detail::write_content(strm, res.content_provider_,
8956
0
                                   offset_and_length.first,
8957
0
                                   offset_and_length.second, is_shutting_down);
8958
0
    } else {
8959
0
      return detail::write_multipart_ranges_data(
8960
0
          strm, req, res, boundary, content_type, res.content_length_,
8961
0
          is_shutting_down);
8962
0
    }
8963
0
  } else {
8964
0
    if (res.is_chunked_content_provider_) {
8965
0
      auto type = detail::encoding_type(req, res);
8966
8967
0
      std::unique_ptr<detail::compressor> compressor;
8968
0
      if (type == detail::EncodingType::Gzip) {
8969
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8970
0
        compressor = detail::make_unique<detail::gzip_compressor>();
8971
0
#endif
8972
0
      } else if (type == detail::EncodingType::Brotli) {
8973
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
8974
        compressor = detail::make_unique<detail::brotli_compressor>();
8975
#endif
8976
0
      } else if (type == detail::EncodingType::Zstd) {
8977
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
8978
        compressor = detail::make_unique<detail::zstd_compressor>();
8979
#endif
8980
0
      } else {
8981
0
        compressor = detail::make_unique<detail::nocompressor>();
8982
0
      }
8983
0
      assert(compressor != nullptr);
8984
8985
0
      return detail::write_content_chunked(strm, res.content_provider_,
8986
0
                                           is_shutting_down, *compressor);
8987
0
    } else {
8988
0
      return detail::write_content_without_length(strm, res.content_provider_,
8989
0
                                                  is_shutting_down);
8990
0
    }
8991
0
  }
8992
0
}
8993
8994
3.72k
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
8995
3.72k
  FormFields::iterator cur_field;
8996
3.72k
  FormFiles::iterator cur_file;
8997
3.72k
  auto is_text_field = false;
8998
3.72k
  size_t count = 0;
8999
3.72k
  if (read_content_core(
9000
3.72k
          strm, req, res,
9001
          // Regular
9002
30.4k
          [&](const char *buf, size_t n) {
9003
            // Prevent arithmetic overflow when checking sizes.
9004
            // Avoid computing (req.body.size() + n) directly because
9005
            // adding two unsigned `size_t` values can wrap around and
9006
            // produce a small result instead of indicating overflow.
9007
            // Instead, check using subtraction: ensure `n` does not
9008
            // exceed the remaining capacity `max_size() - size()`.
9009
30.4k
            if (req.body.size() >= req.body.max_size() ||
9010
30.4k
                n > req.body.max_size() - req.body.size()) {
9011
0
              return false;
9012
0
            }
9013
9014
            // Limit decompressed body size to payload_max_length_ to protect
9015
            // against "zip bomb" attacks where a small compressed payload
9016
            // decompresses to a massive size.
9017
30.4k
            if (payload_max_length_ > 0 &&
9018
30.4k
                (req.body.size() >= payload_max_length_ ||
9019
30.4k
                 n > payload_max_length_ - req.body.size())) {
9020
0
              return false;
9021
0
            }
9022
9023
30.4k
            req.body.append(buf, n);
9024
30.4k
            return true;
9025
30.4k
          },
9026
          // Multipart FormData
9027
19.3k
          [&](const FormData &file) {
9028
19.3k
            if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
9029
1
              output_error_log(Error::TooManyFormDataFiles, &req);
9030
1
              return false;
9031
1
            }
9032
9033
19.3k
            if (file.filename.empty()) {
9034
16.0k
              cur_field = req.form.fields.emplace(
9035
16.0k
                  file.name, FormField{file.name, file.content, file.headers});
9036
16.0k
              is_text_field = true;
9037
16.0k
            } else {
9038
3.25k
              cur_file = req.form.files.emplace(file.name, file);
9039
3.25k
              is_text_field = false;
9040
3.25k
            }
9041
19.3k
            return true;
9042
19.3k
          },
9043
46.8k
          [&](const char *buf, size_t n) {
9044
46.8k
            if (is_text_field) {
9045
26.8k
              auto &content = cur_field->second.content;
9046
26.8k
              if (content.size() + n > content.max_size()) { return false; }
9047
26.8k
              content.append(buf, n);
9048
26.8k
            } else {
9049
19.9k
              auto &content = cur_file->second.content;
9050
19.9k
              if (content.size() + n > content.max_size()) { return false; }
9051
19.9k
              content.append(buf, n);
9052
19.9k
            }
9053
46.8k
            return true;
9054
46.8k
          })) {
9055
1.15k
    const auto &content_type = req.get_header_value("Content-Type");
9056
1.15k
    if (!content_type.find("application/x-www-form-urlencoded")) {
9057
18
      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
9058
1
        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
9059
1
        output_error_log(Error::ExceedMaxPayloadSize, &req);
9060
1
        return false;
9061
1
      }
9062
17
      detail::parse_query_text(req.body, req.params);
9063
17
    }
9064
1.15k
    return true;
9065
1.15k
  }
9066
2.57k
  return false;
9067
3.72k
}
9068
9069
inline bool Server::read_content_with_content_receiver(
9070
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
9071
0
    FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
9072
0
  return read_content_core(strm, req, res, std::move(receiver),
9073
0
                           std::move(multipart_header),
9074
0
                           std::move(multipart_receiver));
9075
0
}
9076
9077
inline bool Server::read_content_core(
9078
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
9079
3.72k
    FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {
9080
3.72k
  detail::FormDataParser multipart_form_data_parser;
9081
3.72k
  ContentReceiverWithProgress out;
9082
9083
3.72k
  if (req.is_multipart_form_data()) {
9084
1.36k
    const auto &content_type = req.get_header_value("Content-Type");
9085
1.36k
    std::string boundary;
9086
1.36k
    if (!detail::parse_multipart_boundary(content_type, boundary)) {
9087
9
      res.status = StatusCode::BadRequest_400;
9088
9
      output_error_log(Error::MultipartParsing, &req);
9089
9
      return false;
9090
9
    }
9091
9092
1.35k
    multipart_form_data_parser.set_boundary(std::move(boundary));
9093
62.2k
    out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {
9094
62.2k
      return multipart_form_data_parser.parse(buf, n, multipart_header,
9095
62.2k
                                              multipart_receiver);
9096
62.2k
    };
9097
2.36k
  } else {
9098
2.36k
    out = [receiver](const char *buf, size_t n, size_t /*off*/,
9099
30.4k
                     size_t /*len*/) { return receiver(buf, n); };
9100
2.36k
  }
9101
9102
  // RFC 7230 Section 3.3.3: If this is a request message and none of the above
9103
  // are true (no Transfer-Encoding and no Content-Length), then the message
9104
  // body length is zero (no message body is present).
9105
  //
9106
  // For non-SSL builds, peek into the socket to detect clients that send a
9107
  // body without a Content-Length header (raw HTTP over TCP). If there is
9108
  // pending data that exceeds the configured payload limit, treat this as an
9109
  // oversized request and fail early (causing connection close). For SSL
9110
  // builds we cannot reliably peek the decrypted application bytes, so keep
9111
  // the original behaviour.
9112
3.72k
#if !defined(CPPHTTPLIB_OPENSSL_SUPPORT)
9113
3.72k
  if (!req.has_header("Content-Length") &&
9114
2.07k
      !detail::is_chunked_transfer_encoding(req.headers)) {
9115
    // Only peek if payload_max_length is set to a finite value
9116
1.07k
    if (payload_max_length_ > 0 &&
9117
1.07k
        payload_max_length_ < (std::numeric_limits<size_t>::max)()) {
9118
0
      socket_t s = strm.socket();
9119
0
      if (s != INVALID_SOCKET) {
9120
        // Peek to check if there is any pending data
9121
0
        char peekbuf[1];
9122
0
        ssize_t n = ::recv(s, peekbuf, 1, MSG_PEEK);
9123
0
        if (n > 0) {
9124
          // There is data, so read it with payload limit enforcement
9125
0
          auto result = detail::read_content_without_length(
9126
0
              strm, payload_max_length_, out);
9127
0
          if (result == detail::ReadContentResult::PayloadTooLarge) {
9128
0
            res.status = StatusCode::PayloadTooLarge_413;
9129
0
            return false;
9130
0
          } else if (result != detail::ReadContentResult::Success) {
9131
0
            return false;
9132
0
          }
9133
0
          return true;
9134
0
        }
9135
0
      }
9136
0
    }
9137
1.07k
    return true;
9138
1.07k
  }
9139
#else
9140
  if (!req.has_header("Content-Length") &&
9141
      !detail::is_chunked_transfer_encoding(req.headers)) {
9142
    return true;
9143
  }
9144
#endif
9145
9146
2.64k
  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
9147
2.64k
                            out, true)) {
9148
2.54k
    return false;
9149
2.54k
  }
9150
9151
98
  if (req.is_multipart_form_data()) {
9152
27
    if (!multipart_form_data_parser.is_valid()) {
9153
19
      res.status = StatusCode::BadRequest_400;
9154
19
      output_error_log(Error::MultipartParsing, &req);
9155
19
      return false;
9156
19
    }
9157
27
  }
9158
9159
79
  return true;
9160
98
}
9161
9162
1.39k
inline bool Server::handle_file_request(Request &req, Response &res) {
9163
1.39k
  for (const auto &entry : base_dirs_) {
9164
    // Prefix match
9165
0
    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
9166
0
      std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
9167
0
      if (detail::is_valid_path(sub_path)) {
9168
0
        auto path = entry.base_dir + sub_path;
9169
0
        if (path.back() == '/') { path += "index.html"; }
9170
9171
0
        detail::FileStat stat(path);
9172
9173
0
        if (stat.is_dir()) {
9174
0
          res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
9175
0
          return true;
9176
0
        }
9177
9178
0
        if (stat.is_file()) {
9179
0
          for (const auto &kv : entry.headers) {
9180
0
            res.set_header(kv.first, kv.second);
9181
0
          }
9182
9183
0
          auto etag = detail::compute_etag(stat);
9184
0
          if (!etag.empty()) { res.set_header("ETag", etag); }
9185
9186
0
          auto mtime = stat.mtime();
9187
9188
0
          auto last_modified = detail::file_mtime_to_http_date(mtime);
9189
0
          if (!last_modified.empty()) {
9190
0
            res.set_header("Last-Modified", last_modified);
9191
0
          }
9192
9193
0
          if (check_if_not_modified(req, res, etag, mtime)) { return true; }
9194
9195
0
          check_if_range(req, etag, mtime);
9196
9197
0
          auto mm = std::make_shared<detail::mmap>(path.c_str());
9198
0
          if (!mm->is_open()) {
9199
0
            output_error_log(Error::OpenFile, &req);
9200
0
            return false;
9201
0
          }
9202
9203
0
          res.set_content_provider(
9204
0
              mm->size(),
9205
0
              detail::find_content_type(path, file_extension_and_mimetype_map_,
9206
0
                                        default_file_mimetype_),
9207
0
              [mm](size_t offset, size_t length, DataSink &sink) -> bool {
9208
0
                sink.write(mm->data() + offset, length);
9209
0
                return true;
9210
0
              });
9211
9212
0
          if (req.method != "HEAD" && file_request_handler_) {
9213
0
            file_request_handler_(req, res);
9214
0
          }
9215
9216
0
          return true;
9217
0
        } else {
9218
0
          output_error_log(Error::OpenFile, &req);
9219
0
        }
9220
0
      }
9221
0
    }
9222
0
  }
9223
1.39k
  return false;
9224
1.39k
}
9225
9226
inline bool Server::check_if_not_modified(const Request &req, Response &res,
9227
                                          const std::string &etag,
9228
0
                                          time_t mtime) const {
9229
  // Handle conditional GET:
9230
  // 1. If-None-Match takes precedence (RFC 9110 Section 13.1.2)
9231
  // 2. If-Modified-Since is checked only when If-None-Match is absent
9232
0
  if (req.has_header("If-None-Match")) {
9233
0
    if (!etag.empty()) {
9234
0
      auto val = req.get_header_value("If-None-Match");
9235
9236
      // NOTE: We use exact string matching here. This works correctly
9237
      // because our server always generates weak ETags (W/"..."), and
9238
      // clients typically send back the same ETag they received.
9239
      // RFC 9110 Section 8.8.3.2 allows weak comparison for
9240
      // If-None-Match, where W/"x" and "x" would match, but this
9241
      // simplified implementation requires exact matches.
9242
0
      auto ret = detail::split_find(val.data(), val.data() + val.size(), ',',
9243
0
                                    [&](const char *b, const char *e) {
9244
0
                                      return std::equal(b, e, "*") ||
9245
0
                                             std::equal(b, e, etag.begin());
9246
0
                                    });
9247
9248
0
      if (ret) {
9249
0
        res.status = StatusCode::NotModified_304;
9250
0
        return true;
9251
0
      }
9252
0
    }
9253
0
  } else if (req.has_header("If-Modified-Since")) {
9254
0
    auto val = req.get_header_value("If-Modified-Since");
9255
0
    auto t = detail::parse_http_date(val);
9256
9257
0
    if (t != static_cast<time_t>(-1) && mtime <= t) {
9258
0
      res.status = StatusCode::NotModified_304;
9259
0
      return true;
9260
0
    }
9261
0
  }
9262
0
  return false;
9263
0
}
9264
9265
inline bool Server::check_if_range(Request &req, const std::string &etag,
9266
0
                                   time_t mtime) const {
9267
  // Handle If-Range for partial content requests (RFC 9110
9268
  // Section 13.1.5). If-Range is only evaluated when Range header is
9269
  // present. If the validator matches, serve partial content; otherwise
9270
  // serve full content.
9271
0
  if (!req.ranges.empty() && req.has_header("If-Range")) {
9272
0
    auto val = req.get_header_value("If-Range");
9273
9274
0
    auto is_valid_range = [&]() {
9275
0
      if (detail::is_strong_etag(val)) {
9276
        // RFC 9110 Section 13.1.5: If-Range requires strong ETag
9277
        // comparison.
9278
0
        return (!etag.empty() && val == etag);
9279
0
      } else if (detail::is_weak_etag(val)) {
9280
        // Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
9281
0
        return false;
9282
0
      } else {
9283
        // HTTP-date comparison
9284
0
        auto t = detail::parse_http_date(val);
9285
0
        return (t != static_cast<time_t>(-1) && mtime <= t);
9286
0
      }
9287
0
    };
9288
9289
0
    if (!is_valid_range()) {
9290
      // Validator doesn't match: ignore Range and serve full content
9291
0
      req.ranges.clear();
9292
0
      return false;
9293
0
    }
9294
0
  }
9295
9296
0
  return true;
9297
0
}
9298
9299
inline socket_t
9300
Server::create_server_socket(const std::string &host, int port,
9301
                             int socket_flags,
9302
0
                             SocketOptions socket_options) const {
9303
0
  return detail::create_socket(
9304
0
      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
9305
0
      ipv6_v6only_, std::move(socket_options),
9306
0
      [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
9307
0
        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
9308
0
          output_error_log(Error::BindIPAddress, nullptr);
9309
0
          return false;
9310
0
        }
9311
0
        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
9312
0
          output_error_log(Error::Listen, nullptr);
9313
0
          return false;
9314
0
        }
9315
0
        return true;
9316
0
      });
9317
0
}
9318
9319
inline int Server::bind_internal(const std::string &host, int port,
9320
0
                                 int socket_flags) {
9321
0
  if (is_decommissioned) { return -1; }
9322
0
9323
0
  if (!is_valid()) { return -1; }
9324
0
9325
0
  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
9326
0
  if (svr_sock_ == INVALID_SOCKET) { return -1; }
9327
0
9328
0
  if (port == 0) {
9329
0
    struct sockaddr_storage addr;
9330
0
    socklen_t addr_len = sizeof(addr);
9331
0
    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
9332
0
                    &addr_len) == -1) {
9333
0
      output_error_log(Error::GetSockName, nullptr);
9334
0
      return -1;
9335
0
    }
9336
0
    if (addr.ss_family == AF_INET) {
9337
0
      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
9338
0
    } else if (addr.ss_family == AF_INET6) {
9339
0
      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
9340
0
    } else {
9341
0
      output_error_log(Error::UnsupportedAddressFamily, nullptr);
9342
0
      return -1;
9343
0
    }
9344
0
  } else {
9345
0
    return port;
9346
0
  }
9347
0
}
9348
9349
0
inline bool Server::listen_internal() {
9350
0
  if (is_decommissioned) { return false; }
9351
0
9352
0
  auto ret = true;
9353
0
  is_running_ = true;
9354
0
  auto se = detail::scope_exit([&]() { is_running_ = false; });
9355
0
9356
0
  {
9357
0
    std::unique_ptr<TaskQueue> task_queue(new_task_queue());
9358
0
9359
0
    while (svr_sock_ != INVALID_SOCKET) {
9360
0
#ifndef _WIN32
9361
0
      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
9362
0
#endif
9363
0
        auto val = detail::select_read(svr_sock_, idle_interval_sec_,
9364
0
                                       idle_interval_usec_);
9365
0
        if (val == 0) { // Timeout
9366
0
          task_queue->on_idle();
9367
0
          continue;
9368
0
        }
9369
0
#ifndef _WIN32
9370
0
      }
9371
0
#endif
9372
0
9373
0
#if defined _WIN32
9374
0
      // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
9375
0
      // OVERLAPPED
9376
0
      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
9377
0
#elif defined SOCK_CLOEXEC
9378
0
      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
9379
0
#else
9380
0
      socket_t sock = accept(svr_sock_, nullptr, nullptr);
9381
0
#endif
9382
0
9383
0
      if (sock == INVALID_SOCKET) {
9384
0
        if (errno == EMFILE) {
9385
0
          // The per-process limit of open file descriptors has been reached.
9386
0
          // Try to accept new connections after a short sleep.
9387
0
          std::this_thread::sleep_for(std::chrono::microseconds{1});
9388
0
          continue;
9389
0
        } else if (errno == EINTR || errno == EAGAIN) {
9390
0
          continue;
9391
0
        }
9392
0
        if (svr_sock_ != INVALID_SOCKET) {
9393
0
          detail::close_socket(svr_sock_);
9394
0
          ret = false;
9395
0
          output_error_log(Error::Connection, nullptr);
9396
0
        } else {
9397
0
          ; // The server socket was closed by user.
9398
0
        }
9399
0
        break;
9400
0
      }
9401
0
9402
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
9403
0
                                  read_timeout_sec_, read_timeout_usec_);
9404
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
9405
0
                                  write_timeout_sec_, write_timeout_usec_);
9406
0
9407
0
      if (!task_queue->enqueue(
9408
0
              [this, sock]() { process_and_close_socket(sock); })) {
9409
0
        output_error_log(Error::ResourceExhaustion, nullptr);
9410
0
        detail::shutdown_socket(sock);
9411
0
        detail::close_socket(sock);
9412
0
      }
9413
0
    }
9414
0
9415
0
    task_queue->shutdown();
9416
0
  }
9417
0
9418
0
  is_decommissioned = !ret;
9419
0
  return ret;
9420
0
}
9421
9422
4.53k
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
9423
4.53k
  if (pre_routing_handler_ &&
9424
0
      pre_routing_handler_(req, res) == HandlerResponse::Handled) {
9425
0
    return true;
9426
0
  }
9427
9428
  // File handler
9429
4.53k
  if ((req.method == "GET" || req.method == "HEAD") &&
9430
1.39k
      handle_file_request(req, res)) {
9431
0
    return true;
9432
0
  }
9433
9434
4.53k
  if (detail::expect_content(req)) {
9435
    // Content reader handler
9436
3.72k
    {
9437
3.72k
      ContentReader reader(
9438
3.72k
          [&](ContentReceiver receiver) {
9439
0
            auto result = read_content_with_content_receiver(
9440
0
                strm, req, res, std::move(receiver), nullptr, nullptr);
9441
0
            if (!result) { output_error_log(Error::Read, &req); }
9442
0
            return result;
9443
0
          },
9444
3.72k
          [&](FormDataHeader header, ContentReceiver receiver) {
9445
0
            auto result = read_content_with_content_receiver(
9446
0
                strm, req, res, nullptr, std::move(header),
9447
0
                std::move(receiver));
9448
0
            if (!result) { output_error_log(Error::Read, &req); }
9449
0
            return result;
9450
0
          });
9451
9452
3.72k
      if (req.method == "POST") {
9453
6
        if (dispatch_request_for_content_reader(
9454
6
                req, res, std::move(reader),
9455
6
                post_handlers_for_content_reader_)) {
9456
0
          return true;
9457
0
        }
9458
3.72k
      } else if (req.method == "PUT") {
9459
2.82k
        if (dispatch_request_for_content_reader(
9460
2.82k
                req, res, std::move(reader),
9461
2.82k
                put_handlers_for_content_reader_)) {
9462
0
          return true;
9463
0
        }
9464
2.82k
      } else if (req.method == "PATCH") {
9465
1
        if (dispatch_request_for_content_reader(
9466
1
                req, res, std::move(reader),
9467
1
                patch_handlers_for_content_reader_)) {
9468
0
          return true;
9469
0
        }
9470
902
      } else if (req.method == "DELETE") {
9471
1
        if (dispatch_request_for_content_reader(
9472
1
                req, res, std::move(reader),
9473
1
                delete_handlers_for_content_reader_)) {
9474
0
          return true;
9475
0
        }
9476
1
      }
9477
3.72k
    }
9478
9479
    // Read content into `req.body`
9480
3.72k
    if (!read_content(strm, req, res)) {
9481
2.57k
      output_error_log(Error::Read, &req);
9482
2.57k
      return false;
9483
2.57k
    }
9484
3.72k
  }
9485
9486
  // Regular handler
9487
1.95k
  if (req.method == "GET" || req.method == "HEAD") {
9488
776
    return dispatch_request(req, res, get_handlers_);
9489
1.18k
  } else if (req.method == "POST") {
9490
6
    return dispatch_request(req, res, post_handlers_);
9491
1.17k
  } else if (req.method == "PUT") {
9492
1.11k
    return dispatch_request(req, res, put_handlers_);
9493
1.11k
  } else if (req.method == "DELETE") {
9494
1
    return dispatch_request(req, res, delete_handlers_);
9495
63
  } else if (req.method == "OPTIONS") {
9496
3
    return dispatch_request(req, res, options_handlers_);
9497
60
  } else if (req.method == "PATCH") {
9498
1
    return dispatch_request(req, res, patch_handlers_);
9499
1
  }
9500
9501
59
  res.status = StatusCode::BadRequest_400;
9502
59
  return false;
9503
1.95k
}
9504
9505
inline bool Server::dispatch_request(Request &req, Response &res,
9506
1.89k
                                     const Handlers &handlers) const {
9507
1.89k
  for (const auto &x : handlers) {
9508
1.89k
    const auto &matcher = x.first;
9509
1.89k
    const auto &handler = x.second;
9510
9511
1.89k
    if (matcher->match(req)) {
9512
1.83k
      req.matched_route = matcher->pattern();
9513
1.83k
      if (!pre_request_handler_ ||
9514
1.83k
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
9515
1.83k
        handler(req, res);
9516
1.83k
      }
9517
1.83k
      return true;
9518
1.83k
    }
9519
1.89k
  }
9520
64
  return false;
9521
1.89k
}
9522
9523
inline void Server::apply_ranges(const Request &req, Response &res,
9524
                                 std::string &content_type,
9525
1.52k
                                 std::string &boundary) const {
9526
1.52k
  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
9527
215
    auto it = res.headers.find("Content-Type");
9528
215
    if (it != res.headers.end()) {
9529
215
      content_type = it->second;
9530
215
      res.headers.erase(it);
9531
215
    }
9532
9533
215
    boundary = detail::make_multipart_data_boundary();
9534
9535
215
    res.set_header("Content-Type",
9536
215
                   "multipart/byteranges; boundary=" + boundary);
9537
215
  }
9538
9539
1.52k
  auto type = detail::encoding_type(req, res);
9540
9541
1.52k
  if (res.body.empty()) {
9542
0
    if (res.content_length_ > 0) {
9543
0
      size_t length = 0;
9544
0
      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
9545
0
        length = res.content_length_;
9546
0
      } else if (req.ranges.size() == 1) {
9547
0
        auto offset_and_length = detail::get_range_offset_and_length(
9548
0
            req.ranges[0], res.content_length_);
9549
9550
0
        length = offset_and_length.second;
9551
9552
0
        auto content_range = detail::make_content_range_header_field(
9553
0
            offset_and_length, res.content_length_);
9554
0
        res.set_header("Content-Range", content_range);
9555
0
      } else {
9556
0
        length = detail::get_multipart_ranges_data_length(
9557
0
            req, boundary, content_type, res.content_length_);
9558
0
      }
9559
0
      res.set_header("Content-Length", std::to_string(length));
9560
0
    } else {
9561
0
      if (res.content_provider_) {
9562
0
        if (res.is_chunked_content_provider_) {
9563
0
          res.set_header("Transfer-Encoding", "chunked");
9564
0
          if (type == detail::EncodingType::Gzip) {
9565
0
            res.set_header("Content-Encoding", "gzip");
9566
0
            res.set_header("Vary", "Accept-Encoding");
9567
0
          } else if (type == detail::EncodingType::Brotli) {
9568
0
            res.set_header("Content-Encoding", "br");
9569
0
            res.set_header("Vary", "Accept-Encoding");
9570
0
          } else if (type == detail::EncodingType::Zstd) {
9571
0
            res.set_header("Content-Encoding", "zstd");
9572
0
            res.set_header("Vary", "Accept-Encoding");
9573
0
          }
9574
0
        }
9575
0
      }
9576
0
    }
9577
1.52k
  } else {
9578
1.52k
    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
9579
1.13k
      ;
9580
1.13k
    } else if (req.ranges.size() == 1) {
9581
176
      auto offset_and_length =
9582
176
          detail::get_range_offset_and_length(req.ranges[0], res.body.size());
9583
176
      auto offset = offset_and_length.first;
9584
176
      auto length = offset_and_length.second;
9585
9586
176
      auto content_range = detail::make_content_range_header_field(
9587
176
          offset_and_length, res.body.size());
9588
176
      res.set_header("Content-Range", content_range);
9589
9590
176
      assert(offset + length <= res.body.size());
9591
176
      res.body = res.body.substr(offset, length);
9592
215
    } else {
9593
215
      std::string data;
9594
215
      detail::make_multipart_ranges_data(req, res, boundary, content_type,
9595
215
                                         res.body.size(), data);
9596
215
      res.body.swap(data);
9597
215
    }
9598
9599
1.52k
    if (type != detail::EncodingType::None) {
9600
18
      output_pre_compression_log(req, res);
9601
9602
18
      std::unique_ptr<detail::compressor> compressor;
9603
18
      std::string content_encoding;
9604
9605
18
      if (type == detail::EncodingType::Gzip) {
9606
18
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
9607
18
        compressor = detail::make_unique<detail::gzip_compressor>();
9608
18
        content_encoding = "gzip";
9609
18
#endif
9610
18
      } else if (type == detail::EncodingType::Brotli) {
9611
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
9612
        compressor = detail::make_unique<detail::brotli_compressor>();
9613
        content_encoding = "br";
9614
#endif
9615
0
      } else if (type == detail::EncodingType::Zstd) {
9616
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
9617
        compressor = detail::make_unique<detail::zstd_compressor>();
9618
        content_encoding = "zstd";
9619
#endif
9620
0
      }
9621
9622
18
      if (compressor) {
9623
18
        std::string compressed;
9624
18
        if (compressor->compress(res.body.data(), res.body.size(), true,
9625
18
                                 [&](const char *data, size_t data_len) {
9626
18
                                   compressed.append(data, data_len);
9627
18
                                   return true;
9628
18
                                 })) {
9629
18
          res.body.swap(compressed);
9630
18
          res.set_header("Content-Encoding", content_encoding);
9631
18
          res.set_header("Vary", "Accept-Encoding");
9632
18
        }
9633
18
      }
9634
18
    }
9635
9636
1.52k
    auto length = std::to_string(res.body.size());
9637
1.52k
    res.set_header("Content-Length", length);
9638
1.52k
  }
9639
1.52k
}
9640
9641
inline bool Server::dispatch_request_for_content_reader(
9642
    Request &req, Response &res, ContentReader content_reader,
9643
2.82k
    const HandlersForContentReader &handlers) const {
9644
2.82k
  for (const auto &x : handlers) {
9645
0
    const auto &matcher = x.first;
9646
0
    const auto &handler = x.second;
9647
9648
0
    if (matcher->match(req)) {
9649
0
      req.matched_route = matcher->pattern();
9650
0
      if (!pre_request_handler_ ||
9651
0
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
9652
0
        handler(req, res, content_reader);
9653
0
      }
9654
0
      return true;
9655
0
    }
9656
0
  }
9657
2.82k
  return false;
9658
2.82k
}
9659
9660
inline std::string
9661
get_client_ip(const std::string &x_forwarded_for,
9662
0
              const std::vector<std::string> &trusted_proxies) {
9663
  // X-Forwarded-For is a comma-separated list per RFC 7239
9664
0
  std::vector<std::string> ip_list;
9665
0
  detail::split(x_forwarded_for.data(),
9666
0
                x_forwarded_for.data() + x_forwarded_for.size(), ',',
9667
0
                [&](const char *b, const char *e) {
9668
0
                  auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
9669
0
                  ip_list.emplace_back(std::string(b + r.first, b + r.second));
9670
0
                });
9671
9672
0
  for (size_t i = 0; i < ip_list.size(); ++i) {
9673
0
    auto ip = ip_list[i];
9674
9675
0
    auto is_trusted_proxy =
9676
0
        std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
9677
0
                    [&](const std::string &proxy) { return ip == proxy; });
9678
9679
0
    if (is_trusted_proxy) {
9680
0
      if (i == 0) {
9681
        // If the trusted proxy is the first IP, there's no preceding client IP
9682
0
        return ip;
9683
0
      } else {
9684
        // Return the IP immediately before the trusted proxy
9685
0
        return ip_list[i - 1];
9686
0
      }
9687
0
    }
9688
0
  }
9689
9690
  // If no trusted proxy is found, return the first IP in the list
9691
0
  return ip_list.front();
9692
0
}
9693
9694
inline bool
9695
Server::process_request(Stream &strm, const std::string &remote_addr,
9696
                        int remote_port, const std::string &local_addr,
9697
                        int local_port, bool close_connection,
9698
                        bool &connection_closed,
9699
6.53k
                        const std::function<void(Request &)> &setup_request) {
9700
6.53k
  std::array<char, 2048> buf{};
9701
9702
6.53k
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
9703
9704
  // Connection has been closed on client
9705
6.53k
  if (!line_reader.getline()) { return false; }
9706
9707
6.53k
  Request req;
9708
6.53k
  req.start_time_ = std::chrono::steady_clock::now();
9709
6.53k
  req.remote_addr = remote_addr;
9710
6.53k
  req.remote_port = remote_port;
9711
6.53k
  req.local_addr = local_addr;
9712
6.53k
  req.local_port = local_port;
9713
9714
6.53k
  Response res;
9715
6.53k
  res.version = "HTTP/1.1";
9716
6.53k
  res.headers = default_headers_;
9717
9718
#ifdef __APPLE__
9719
  // Socket file descriptor exceeded FD_SETSIZE...
9720
  if (strm.socket() >= FD_SETSIZE) {
9721
    Headers dummy;
9722
    detail::read_headers(strm, dummy);
9723
    res.status = StatusCode::InternalServerError_500;
9724
    output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
9725
    return write_response(strm, close_connection, req, res);
9726
  }
9727
#endif
9728
9729
  // Request line and headers
9730
6.53k
  if (!parse_request_line(line_reader.ptr(), req)) {
9731
349
    res.status = StatusCode::BadRequest_400;
9732
349
    output_error_log(Error::InvalidRequestLine, &req);
9733
349
    return write_response(strm, close_connection, req, res);
9734
349
  }
9735
9736
  // Request headers
9737
6.18k
  if (!detail::read_headers(strm, req.headers)) {
9738
1.35k
    res.status = StatusCode::BadRequest_400;
9739
1.35k
    output_error_log(Error::InvalidHeaders, &req);
9740
1.35k
    return write_response(strm, close_connection, req, res);
9741
1.35k
  }
9742
9743
  // Check if the request URI doesn't exceed the limit
9744
4.83k
  if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
9745
5
    res.status = StatusCode::UriTooLong_414;
9746
5
    output_error_log(Error::ExceedUriMaxLength, &req);
9747
5
    return write_response(strm, close_connection, req, res);
9748
5
  }
9749
9750
4.82k
  if (req.get_header_value("Connection") == "close") {
9751
1
    connection_closed = true;
9752
1
  }
9753
9754
4.82k
  if (req.version == "HTTP/1.0" &&
9755
2.90k
      req.get_header_value("Connection") != "Keep-Alive") {
9756
2.90k
    connection_closed = true;
9757
2.90k
  }
9758
9759
4.82k
  if (!trusted_proxies_.empty() && req.has_header("X-Forwarded-For")) {
9760
0
    auto x_forwarded_for = req.get_header_value("X-Forwarded-For");
9761
0
    req.remote_addr = get_client_ip(x_forwarded_for, trusted_proxies_);
9762
4.82k
  } else {
9763
4.82k
    req.remote_addr = remote_addr;
9764
4.82k
  }
9765
4.82k
  req.remote_port = remote_port;
9766
9767
4.82k
  req.local_addr = local_addr;
9768
4.82k
  req.local_port = local_port;
9769
9770
4.82k
  if (req.has_header("Accept")) {
9771
985
    const auto &accept_header = req.get_header_value("Accept");
9772
985
    if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
9773
163
      res.status = StatusCode::BadRequest_400;
9774
163
      output_error_log(Error::HTTPParsing, &req);
9775
163
      return write_response(strm, close_connection, req, res);
9776
163
    }
9777
985
  }
9778
9779
4.66k
  if (req.has_header("Range")) {
9780
842
    const auto &range_header_value = req.get_header_value("Range");
9781
842
    if (!detail::parse_range_header(range_header_value, req.ranges)) {
9782
133
      res.status = StatusCode::RangeNotSatisfiable_416;
9783
133
      output_error_log(Error::InvalidRangeHeader, &req);
9784
133
      return write_response(strm, close_connection, req, res);
9785
133
    }
9786
842
  }
9787
9788
4.53k
  if (setup_request) { setup_request(req); }
9789
9790
4.53k
  if (req.get_header_value("Expect") == "100-continue") {
9791
2
    int status = StatusCode::Continue_100;
9792
2
    if (expect_100_continue_handler_) {
9793
0
      status = expect_100_continue_handler_(req, res);
9794
0
    }
9795
2
    switch (status) {
9796
2
    case StatusCode::Continue_100:
9797
2
    case StatusCode::ExpectationFailed_417:
9798
2
      detail::write_response_line(strm, status);
9799
2
      strm.write("\r\n");
9800
2
      break;
9801
0
    default:
9802
0
      connection_closed = true;
9803
0
      return write_response(strm, true, req, res);
9804
2
    }
9805
2
  }
9806
9807
  // Setup `is_connection_closed` method
9808
4.53k
  auto sock = strm.socket();
9809
4.53k
  req.is_connection_closed = [sock]() {
9810
0
    return !detail::is_socket_alive(sock);
9811
0
  };
9812
9813
  // Routing
9814
4.53k
  auto routed = false;
9815
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
9816
  routed = routing(req, res, strm);
9817
#else
9818
4.53k
  try {
9819
4.53k
    routed = routing(req, res, strm);
9820
4.53k
  } catch (std::exception &e) {
9821
0
    if (exception_handler_) {
9822
0
      auto ep = std::current_exception();
9823
0
      exception_handler_(req, res, ep);
9824
0
      routed = true;
9825
0
    } else {
9826
0
      res.status = StatusCode::InternalServerError_500;
9827
0
      std::string val;
9828
0
      auto s = e.what();
9829
0
      for (size_t i = 0; s[i]; i++) {
9830
0
        switch (s[i]) {
9831
0
        case '\r': val += "\\r"; break;
9832
0
        case '\n': val += "\\n"; break;
9833
0
        default: val += s[i]; break;
9834
0
        }
9835
0
      }
9836
0
      res.set_header("EXCEPTION_WHAT", val);
9837
0
    }
9838
0
  } catch (...) {
9839
0
    if (exception_handler_) {
9840
0
      auto ep = std::current_exception();
9841
0
      exception_handler_(req, res, ep);
9842
0
      routed = true;
9843
0
    } else {
9844
0
      res.status = StatusCode::InternalServerError_500;
9845
0
      res.set_header("EXCEPTION_WHAT", "UNKNOWN");
9846
0
    }
9847
0
  }
9848
0
#endif
9849
4.53k
  if (routed) {
9850
1.83k
    if (res.status == -1) {
9851
1.83k
      res.status = req.ranges.empty() ? StatusCode::OK_200
9852
1.83k
                                      : StatusCode::PartialContent_206;
9853
1.83k
    }
9854
9855
    // Serve file content by using a content provider
9856
1.83k
    if (!res.file_content_path_.empty()) {
9857
0
      const auto &path = res.file_content_path_;
9858
0
      auto mm = std::make_shared<detail::mmap>(path.c_str());
9859
0
      if (!mm->is_open()) {
9860
0
        res.body.clear();
9861
0
        res.content_length_ = 0;
9862
0
        res.content_provider_ = nullptr;
9863
0
        res.status = StatusCode::NotFound_404;
9864
0
        output_error_log(Error::OpenFile, &req);
9865
0
        return write_response(strm, close_connection, req, res);
9866
0
      }
9867
9868
0
      auto content_type = res.file_content_content_type_;
9869
0
      if (content_type.empty()) {
9870
0
        content_type = detail::find_content_type(
9871
0
            path, file_extension_and_mimetype_map_, default_file_mimetype_);
9872
0
      }
9873
9874
0
      res.set_content_provider(
9875
0
          mm->size(), content_type,
9876
0
          [mm](size_t offset, size_t length, DataSink &sink) -> bool {
9877
0
            sink.write(mm->data() + offset, length);
9878
0
            return true;
9879
0
          });
9880
0
    }
9881
9882
1.83k
    if (detail::range_error(req, res)) {
9883
309
      res.body.clear();
9884
309
      res.content_length_ = 0;
9885
309
      res.content_provider_ = nullptr;
9886
309
      res.status = StatusCode::RangeNotSatisfiable_416;
9887
309
      return write_response(strm, close_connection, req, res);
9888
309
    }
9889
9890
1.52k
    return write_response_with_content(strm, close_connection, req, res);
9891
2.69k
  } else {
9892
2.69k
    if (res.status == -1) { res.status = StatusCode::NotFound_404; }
9893
9894
2.69k
    return write_response(strm, close_connection, req, res);
9895
2.69k
  }
9896
4.53k
}
9897
9898
0
inline bool Server::is_valid() const { return true; }
9899
9900
0
inline bool Server::process_and_close_socket(socket_t sock) {
9901
0
  std::string remote_addr;
9902
0
  int remote_port = 0;
9903
0
  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
9904
9905
0
  std::string local_addr;
9906
0
  int local_port = 0;
9907
0
  detail::get_local_ip_and_port(sock, local_addr, local_port);
9908
9909
0
  auto ret = detail::process_server_socket(
9910
0
      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
9911
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9912
0
      write_timeout_usec_,
9913
0
      [&](Stream &strm, bool close_connection, bool &connection_closed) {
9914
0
        return process_request(strm, remote_addr, remote_port, local_addr,
9915
0
                               local_port, close_connection, connection_closed,
9916
0
                               nullptr);
9917
0
      });
9918
9919
0
  detail::shutdown_socket(sock);
9920
0
  detail::close_socket(sock);
9921
0
  return ret;
9922
0
}
9923
9924
6.53k
inline void Server::output_log(const Request &req, const Response &res) const {
9925
6.53k
  if (logger_) {
9926
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
9927
0
    logger_(req, res);
9928
0
  }
9929
6.53k
}
9930
9931
inline void Server::output_pre_compression_log(const Request &req,
9932
18
                                               const Response &res) const {
9933
18
  if (pre_compression_logger_) {
9934
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
9935
0
    pre_compression_logger_(req, res);
9936
0
  }
9937
18
}
9938
9939
inline void Server::output_error_log(const Error &err,
9940
4.69k
                                     const Request *req) const {
9941
4.69k
  if (error_logger_) {
9942
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
9943
0
    error_logger_(err, req);
9944
0
  }
9945
4.69k
}
9946
9947
// HTTP client implementation
9948
inline ClientImpl::ClientImpl(const std::string &host)
9949
    : ClientImpl(host, 80, std::string(), std::string()) {}
9950
9951
inline ClientImpl::ClientImpl(const std::string &host, int port)
9952
    : ClientImpl(host, port, std::string(), std::string()) {}
9953
9954
inline ClientImpl::ClientImpl(const std::string &host, int port,
9955
                              const std::string &client_cert_path,
9956
                              const std::string &client_key_path)
9957
    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
9958
      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
9959
9960
0
inline ClientImpl::~ClientImpl() {
9961
  // Wait until all the requests in flight are handled.
9962
0
  size_t retry_count = 10;
9963
0
  while (retry_count-- > 0) {
9964
0
    {
9965
0
      std::lock_guard<std::mutex> guard(socket_mutex_);
9966
0
      if (socket_requests_in_flight_ == 0) { break; }
9967
0
    }
9968
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
9969
0
  }
9970
9971
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
9972
0
  shutdown_socket(socket_);
9973
0
  close_socket(socket_);
9974
0
}
9975
9976
0
inline bool ClientImpl::is_valid() const { return true; }
9977
9978
0
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
9979
0
  client_cert_path_ = rhs.client_cert_path_;
9980
0
  client_key_path_ = rhs.client_key_path_;
9981
0
  connection_timeout_sec_ = rhs.connection_timeout_sec_;
9982
0
  read_timeout_sec_ = rhs.read_timeout_sec_;
9983
0
  read_timeout_usec_ = rhs.read_timeout_usec_;
9984
0
  write_timeout_sec_ = rhs.write_timeout_sec_;
9985
0
  write_timeout_usec_ = rhs.write_timeout_usec_;
9986
0
  max_timeout_msec_ = rhs.max_timeout_msec_;
9987
0
  basic_auth_username_ = rhs.basic_auth_username_;
9988
0
  basic_auth_password_ = rhs.basic_auth_password_;
9989
0
  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
9990
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9991
0
  digest_auth_username_ = rhs.digest_auth_username_;
9992
0
  digest_auth_password_ = rhs.digest_auth_password_;
9993
0
#endif
9994
0
  keep_alive_ = rhs.keep_alive_;
9995
0
  follow_location_ = rhs.follow_location_;
9996
0
  path_encode_ = rhs.path_encode_;
9997
0
  address_family_ = rhs.address_family_;
9998
0
  tcp_nodelay_ = rhs.tcp_nodelay_;
9999
0
  ipv6_v6only_ = rhs.ipv6_v6only_;
10000
0
  socket_options_ = rhs.socket_options_;
10001
0
  compress_ = rhs.compress_;
10002
0
  decompress_ = rhs.decompress_;
10003
0
  interface_ = rhs.interface_;
10004
0
  proxy_host_ = rhs.proxy_host_;
10005
0
  proxy_port_ = rhs.proxy_port_;
10006
0
  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
10007
0
  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
10008
0
  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
10009
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10010
0
  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
10011
0
  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
10012
0
#endif
10013
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10014
0
  ca_cert_file_path_ = rhs.ca_cert_file_path_;
10015
0
  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
10016
0
  ca_cert_store_ = rhs.ca_cert_store_;
10017
0
#endif
10018
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10019
0
  server_certificate_verification_ = rhs.server_certificate_verification_;
10020
0
  server_hostname_verification_ = rhs.server_hostname_verification_;
10021
0
  server_certificate_verifier_ = rhs.server_certificate_verifier_;
10022
0
#endif
10023
0
  logger_ = rhs.logger_;
10024
0
  error_logger_ = rhs.error_logger_;
10025
0
}
10026
10027
0
inline socket_t ClientImpl::create_client_socket(Error &error) const {
10028
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
10029
0
    return detail::create_client_socket(
10030
0
        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
10031
0
        ipv6_v6only_, socket_options_, connection_timeout_sec_,
10032
0
        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
10033
0
        write_timeout_sec_, write_timeout_usec_, interface_, error);
10034
0
  }
10035
10036
  // Check is custom IP specified for host_
10037
0
  std::string ip;
10038
0
  auto it = addr_map_.find(host_);
10039
0
  if (it != addr_map_.end()) { ip = it->second; }
10040
10041
0
  return detail::create_client_socket(
10042
0
      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
10043
0
      socket_options_, connection_timeout_sec_, connection_timeout_usec_,
10044
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
10045
0
      write_timeout_usec_, interface_, error);
10046
0
}
10047
10048
inline bool ClientImpl::create_and_connect_socket(Socket &socket,
10049
0
                                                  Error &error) {
10050
0
  auto sock = create_client_socket(error);
10051
0
  if (sock == INVALID_SOCKET) { return false; }
10052
0
  socket.sock = sock;
10053
0
  return true;
10054
0
}
10055
10056
0
inline bool ClientImpl::ensure_socket_connection(Socket &socket, Error &error) {
10057
0
  return create_and_connect_socket(socket, error);
10058
0
}
10059
10060
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10061
inline bool SSLClient::ensure_socket_connection(Socket &socket, Error &error) {
10062
  if (!ClientImpl::ensure_socket_connection(socket, error)) { return false; }
10063
10064
  if (!proxy_host_.empty() && proxy_port_ != -1) { return true; }
10065
10066
  if (!initialize_ssl(socket, error)) {
10067
    shutdown_socket(socket);
10068
    close_socket(socket);
10069
    return false;
10070
  }
10071
10072
  return true;
10073
}
10074
#endif
10075
10076
inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
10077
0
                                     bool /*shutdown_gracefully*/) {
10078
  // If there are any requests in flight from threads other than us, then it's
10079
  // a thread-unsafe race because individual ssl* objects are not thread-safe.
10080
0
  assert(socket_requests_in_flight_ == 0 ||
10081
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
10082
0
}
10083
10084
0
inline void ClientImpl::shutdown_socket(Socket &socket) const {
10085
0
  if (socket.sock == INVALID_SOCKET) { return; }
10086
0
  detail::shutdown_socket(socket.sock);
10087
0
}
10088
10089
0
inline void ClientImpl::close_socket(Socket &socket) {
10090
  // If there are requests in flight in another thread, usually closing
10091
  // the socket will be fine and they will simply receive an error when
10092
  // using the closed socket, but it is still a bug since rarely the OS
10093
  // may reassign the socket id to be used for a new socket, and then
10094
  // suddenly they will be operating on a live socket that is different
10095
  // than the one they intended!
10096
0
  assert(socket_requests_in_flight_ == 0 ||
10097
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
10098
10099
  // It is also a bug if this happens while SSL is still active
10100
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10101
  assert(socket.ssl == nullptr);
10102
#endif
10103
0
  if (socket.sock == INVALID_SOCKET) { return; }
10104
0
  detail::close_socket(socket.sock);
10105
0
  socket.sock = INVALID_SOCKET;
10106
0
}
10107
10108
inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
10109
                                           Response &res,
10110
0
                                           bool skip_100_continue) const {
10111
0
  std::array<char, 2048> buf{};
10112
0
10113
0
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
10114
0
10115
0
  if (!line_reader.getline()) { return false; }
10116
0
10117
0
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
10118
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
10119
0
#else
10120
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
10121
0
#endif
10122
0
10123
0
  std::cmatch m;
10124
0
  if (!std::regex_match(line_reader.ptr(), m, re)) {
10125
0
    return req.method == "CONNECT";
10126
0
  }
10127
0
  res.version = std::string(m[1]);
10128
0
  res.status = std::stoi(std::string(m[2]));
10129
0
  res.reason = std::string(m[3]);
10130
0
10131
0
  // Ignore '100 Continue' (only when not using Expect: 100-continue explicitly)
10132
0
  while (skip_100_continue && res.status == StatusCode::Continue_100) {
10133
0
    if (!line_reader.getline()) { return false; } // CRLF
10134
0
    if (!line_reader.getline()) { return false; } // next response line
10135
0
10136
0
    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
10137
0
    res.version = std::string(m[1]);
10138
0
    res.status = std::stoi(std::string(m[2]));
10139
0
    res.reason = std::string(m[3]);
10140
0
  }
10141
0
10142
0
  return true;
10143
0
}
10144
10145
0
inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
10146
0
  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
10147
0
  auto ret = send_(req, res, error);
10148
0
  if (error == Error::SSLPeerCouldBeClosed_) {
10149
0
    assert(!ret);
10150
0
    ret = send_(req, res, error);
10151
0
  }
10152
0
  return ret;
10153
0
}
10154
10155
0
inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
10156
0
  {
10157
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10158
0
10159
0
    // Set this to false immediately - if it ever gets set to true by the end
10160
0
    // of the request, we know another thread instructed us to close the
10161
0
    // socket.
10162
0
    socket_should_be_closed_when_request_is_done_ = false;
10163
0
10164
0
    auto is_alive = false;
10165
0
    if (socket_.is_open()) {
10166
0
      is_alive = detail::is_socket_alive(socket_.sock);
10167
0
10168
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10169
0
      if (is_alive && is_ssl()) {
10170
0
        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
10171
0
          is_alive = false;
10172
0
        }
10173
0
      }
10174
0
#endif
10175
0
10176
0
      if (!is_alive) {
10177
0
        // Attempt to avoid sigpipe by shutting down non-gracefully if it
10178
0
        // seems like the other side has already closed the connection Also,
10179
0
        // there cannot be any requests in flight from other threads since we
10180
0
        // locked request_mutex_, so safe to close everything immediately
10181
0
        const bool shutdown_gracefully = false;
10182
0
        shutdown_ssl(socket_, shutdown_gracefully);
10183
0
        shutdown_socket(socket_);
10184
0
        close_socket(socket_);
10185
0
      }
10186
0
    }
10187
0
10188
0
    if (!is_alive) {
10189
0
      if (!ensure_socket_connection(socket_, error)) {
10190
0
        output_error_log(error, &req);
10191
0
        return false;
10192
0
      }
10193
0
10194
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10195
0
      // TODO: refactoring
10196
0
      if (is_ssl()) {
10197
0
        auto &scli = static_cast<SSLClient &>(*this);
10198
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
10199
0
          auto success = false;
10200
0
          if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
10201
0
                                       error)) {
10202
0
            if (!success) { output_error_log(error, &req); }
10203
0
            return success;
10204
0
          }
10205
0
        }
10206
0
10207
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
10208
0
          if (!scli.initialize_ssl(socket_, error)) {
10209
0
            output_error_log(error, &req);
10210
0
            return false;
10211
0
          }
10212
0
        }
10213
0
      }
10214
0
#endif
10215
0
    }
10216
0
10217
0
    // Mark the current socket as being in use so that it cannot be closed by
10218
0
    // anyone else while this request is ongoing, even though we will be
10219
0
    // releasing the mutex.
10220
0
    if (socket_requests_in_flight_ > 1) {
10221
0
      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
10222
0
    }
10223
0
    socket_requests_in_flight_ += 1;
10224
0
    socket_requests_are_from_thread_ = std::this_thread::get_id();
10225
0
  }
10226
0
10227
0
  for (const auto &header : default_headers_) {
10228
0
    if (req.headers.find(header.first) == req.headers.end()) {
10229
0
      req.headers.insert(header);
10230
0
    }
10231
0
  }
10232
0
10233
0
  auto ret = false;
10234
0
  auto close_connection = !keep_alive_;
10235
0
10236
0
  auto se = detail::scope_exit([&]() {
10237
0
    // Briefly lock mutex in order to mark that a request is no longer ongoing
10238
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10239
0
    socket_requests_in_flight_ -= 1;
10240
0
    if (socket_requests_in_flight_ <= 0) {
10241
0
      assert(socket_requests_in_flight_ == 0);
10242
0
      socket_requests_are_from_thread_ = std::thread::id();
10243
0
    }
10244
0
10245
0
    if (socket_should_be_closed_when_request_is_done_ || close_connection ||
10246
0
        !ret) {
10247
0
      shutdown_ssl(socket_, true);
10248
0
      shutdown_socket(socket_);
10249
0
      close_socket(socket_);
10250
0
    }
10251
0
  });
10252
0
10253
0
  ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
10254
0
    return handle_request(strm, req, res, close_connection, error);
10255
0
  });
10256
0
10257
0
  if (!ret) {
10258
0
    if (error == Error::Success) {
10259
0
      error = Error::Unknown;
10260
0
      output_error_log(error, &req);
10261
0
    }
10262
0
  }
10263
0
10264
0
  return ret;
10265
0
}
10266
10267
0
inline Result ClientImpl::send(const Request &req) {
10268
0
  auto req2 = req;
10269
0
  return send_(std::move(req2));
10270
0
}
10271
10272
0
inline Result ClientImpl::send_(Request &&req) {
10273
0
  auto res = detail::make_unique<Response>();
10274
0
  auto error = Error::Success;
10275
0
  auto ret = send(req, *res, error);
10276
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10277
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
10278
0
                last_ssl_error_, last_openssl_error_};
10279
0
#else
10280
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
10281
0
#endif
10282
0
}
10283
10284
inline void ClientImpl::prepare_default_headers(Request &r, bool for_stream,
10285
0
                                                const std::string &ct) {
10286
0
  (void)for_stream;
10287
0
  for (const auto &header : default_headers_) {
10288
0
    if (!r.has_header(header.first)) { r.headers.insert(header); }
10289
0
  }
10290
0
10291
0
  if (!r.has_header("Host")) {
10292
0
    if (address_family_ == AF_UNIX) {
10293
0
      r.headers.emplace("Host", "localhost");
10294
0
    } else {
10295
0
      r.headers.emplace(
10296
0
          "Host", detail::make_host_and_port_string(host_, port_, is_ssl()));
10297
0
    }
10298
0
  }
10299
0
10300
0
  if (!r.has_header("Accept")) { r.headers.emplace("Accept", "*/*"); }
10301
0
10302
0
  if (!r.content_receiver) {
10303
0
    if (!r.has_header("Accept-Encoding")) {
10304
0
      std::string accept_encoding;
10305
0
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
10306
0
      accept_encoding = "br";
10307
0
#endif
10308
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
10309
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
10310
0
      accept_encoding += "gzip, deflate";
10311
0
#endif
10312
0
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
10313
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
10314
0
      accept_encoding += "zstd";
10315
0
#endif
10316
0
      r.set_header("Accept-Encoding", accept_encoding);
10317
0
    }
10318
0
10319
0
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
10320
0
    if (!r.has_header("User-Agent")) {
10321
0
      auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
10322
0
      r.set_header("User-Agent", agent);
10323
0
    }
10324
0
#endif
10325
0
  }
10326
0
10327
0
  if (!r.body.empty()) {
10328
0
    if (!ct.empty() && !r.has_header("Content-Type")) {
10329
0
      r.headers.emplace("Content-Type", ct);
10330
0
    }
10331
0
    if (!r.has_header("Content-Length")) {
10332
0
      r.headers.emplace("Content-Length", std::to_string(r.body.size()));
10333
0
    }
10334
0
  }
10335
0
}
10336
10337
inline ClientImpl::StreamHandle
10338
ClientImpl::open_stream(const std::string &method, const std::string &path,
10339
                        const Params &params, const Headers &headers,
10340
                        const std::string &body,
10341
0
                        const std::string &content_type) {
10342
0
  StreamHandle handle;
10343
0
  handle.response = detail::make_unique<Response>();
10344
0
  handle.error = Error::Success;
10345
0
10346
0
  auto query_path = params.empty() ? path : append_query_params(path, params);
10347
0
  handle.connection_ = detail::make_unique<ClientConnection>();
10348
0
10349
0
  {
10350
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10351
0
10352
0
    auto is_alive = false;
10353
0
    if (socket_.is_open()) {
10354
0
      is_alive = detail::is_socket_alive(socket_.sock);
10355
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10356
0
      if (is_alive && is_ssl()) {
10357
0
        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
10358
0
          is_alive = false;
10359
0
        }
10360
0
      }
10361
0
#endif
10362
0
      if (!is_alive) {
10363
0
        shutdown_ssl(socket_, false);
10364
0
        shutdown_socket(socket_);
10365
0
        close_socket(socket_);
10366
0
      }
10367
0
    }
10368
0
10369
0
    if (!is_alive) {
10370
0
      if (!ensure_socket_connection(socket_, handle.error)) {
10371
0
        handle.response.reset();
10372
0
        return handle;
10373
0
      }
10374
0
10375
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10376
0
      if (is_ssl()) {
10377
0
        auto &scli = static_cast<SSLClient &>(*this);
10378
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
10379
0
          if (!scli.initialize_ssl(socket_, handle.error)) {
10380
0
            handle.response.reset();
10381
0
            return handle;
10382
0
          }
10383
0
        }
10384
0
      }
10385
0
#endif
10386
0
    }
10387
0
10388
0
    transfer_socket_ownership_to_handle(handle);
10389
0
  }
10390
0
10391
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10392
0
  if (is_ssl() && handle.connection_->ssl) {
10393
0
    handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
10394
0
        handle.connection_->sock, handle.connection_->ssl, read_timeout_sec_,
10395
0
        read_timeout_usec_, write_timeout_sec_, write_timeout_usec_);
10396
0
  } else {
10397
0
    handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
10398
0
        handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
10399
0
        write_timeout_sec_, write_timeout_usec_);
10400
0
  }
10401
0
#else
10402
0
  handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
10403
0
      handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
10404
0
      write_timeout_sec_, write_timeout_usec_);
10405
0
#endif
10406
0
  handle.stream_ = handle.socket_stream_.get();
10407
0
10408
0
  Request req;
10409
0
  req.method = method;
10410
0
  req.path = query_path;
10411
0
  req.headers = headers;
10412
0
  req.body = body;
10413
0
10414
0
  prepare_default_headers(req, true, content_type);
10415
0
10416
0
  auto &strm = *handle.stream_;
10417
0
  if (detail::write_request_line(strm, req.method, req.path) < 0) {
10418
0
    handle.error = Error::Write;
10419
0
    handle.response.reset();
10420
0
    return handle;
10421
0
  }
10422
0
10423
0
  if (!detail::check_and_write_headers(strm, req.headers, header_writer_,
10424
0
                                       handle.error)) {
10425
0
    handle.response.reset();
10426
0
    return handle;
10427
0
  }
10428
0
10429
0
  if (!body.empty()) {
10430
0
    if (strm.write(body.data(), body.size()) < 0) {
10431
0
      handle.error = Error::Write;
10432
0
      handle.response.reset();
10433
0
      return handle;
10434
0
    }
10435
0
  }
10436
0
10437
0
  if (!read_response_line(strm, req, *handle.response) ||
10438
0
      !detail::read_headers(strm, handle.response->headers)) {
10439
0
    handle.error = Error::Read;
10440
0
    handle.response.reset();
10441
0
    return handle;
10442
0
  }
10443
0
10444
0
  handle.body_reader_.stream = handle.stream_;
10445
0
10446
0
  auto content_length_str = handle.response->get_header_value("Content-Length");
10447
0
  if (!content_length_str.empty()) {
10448
0
    handle.body_reader_.content_length =
10449
0
        static_cast<size_t>(std::stoull(content_length_str));
10450
0
  }
10451
0
10452
0
  auto transfer_encoding =
10453
0
      handle.response->get_header_value("Transfer-Encoding");
10454
0
  handle.body_reader_.chunked = (transfer_encoding == "chunked");
10455
0
10456
0
  auto content_encoding = handle.response->get_header_value("Content-Encoding");
10457
0
  if (!content_encoding.empty()) {
10458
0
    handle.decompressor_ = detail::create_decompressor(content_encoding);
10459
0
  }
10460
0
10461
0
  return handle;
10462
0
}
10463
10464
0
inline ssize_t ClientImpl::StreamHandle::read(char *buf, size_t len) {
10465
0
  if (!is_valid() || !response) { return -1; }
10466
0
10467
0
  if (decompressor_) { return read_with_decompression(buf, len); }
10468
0
  auto n = detail::read_body_content(stream_, body_reader_, buf, len);
10469
0
10470
0
  if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_) {
10471
0
    trailers_parsed_ = true;
10472
0
    if (body_reader_.chunked_decoder) {
10473
0
      if (!body_reader_.chunked_decoder->parse_trailers_into(
10474
0
              response->trailers, response->headers)) {
10475
0
        return n;
10476
0
      }
10477
0
    } else {
10478
0
      detail::ChunkedDecoder dec(*stream_);
10479
0
      if (!dec.parse_trailers_into(response->trailers, response->headers)) {
10480
0
        return n;
10481
0
      }
10482
0
    }
10483
0
  }
10484
0
10485
0
  return n;
10486
0
}
10487
10488
inline ssize_t ClientImpl::StreamHandle::read_with_decompression(char *buf,
10489
0
                                                                 size_t len) {
10490
0
  if (decompress_offset_ < decompress_buffer_.size()) {
10491
0
    auto available = decompress_buffer_.size() - decompress_offset_;
10492
0
    auto to_copy = (std::min)(len, available);
10493
0
    std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
10494
0
    decompress_offset_ += to_copy;
10495
0
    return static_cast<ssize_t>(to_copy);
10496
0
  }
10497
0
10498
0
  decompress_buffer_.clear();
10499
0
  decompress_offset_ = 0;
10500
0
10501
0
  constexpr size_t kDecompressionBufferSize = 8192;
10502
0
  char compressed_buf[kDecompressionBufferSize];
10503
0
10504
0
  while (true) {
10505
0
    auto n = detail::read_body_content(stream_, body_reader_, compressed_buf,
10506
0
                                       sizeof(compressed_buf));
10507
0
10508
0
    if (n <= 0) { return n; }
10509
0
10510
0
    bool decompress_ok =
10511
0
        decompressor_->decompress(compressed_buf, static_cast<size_t>(n),
10512
0
                                  [this](const char *data, size_t data_len) {
10513
0
                                    decompress_buffer_.append(data, data_len);
10514
0
                                    return true;
10515
0
                                  });
10516
0
10517
0
    if (!decompress_ok) {
10518
0
      body_reader_.last_error = Error::Read;
10519
0
      return -1;
10520
0
    }
10521
0
10522
0
    if (!decompress_buffer_.empty()) { break; }
10523
0
  }
10524
0
10525
0
  auto to_copy = (std::min)(len, decompress_buffer_.size());
10526
0
  std::memcpy(buf, decompress_buffer_.data(), to_copy);
10527
0
  decompress_offset_ = to_copy;
10528
0
  return static_cast<ssize_t>(to_copy);
10529
0
}
10530
10531
0
inline void ClientImpl::StreamHandle::parse_trailers_if_needed() {
10532
0
  if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_) {
10533
0
    return;
10534
0
  }
10535
0
10536
0
  trailers_parsed_ = true;
10537
0
10538
0
  const auto bufsiz = 128;
10539
0
  char line_buf[bufsiz];
10540
0
  detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
10541
0
10542
0
  if (!line_reader.getline()) { return; }
10543
0
10544
0
  if (!detail::parse_trailers(line_reader, response->trailers,
10545
0
                              response->headers)) {
10546
0
    return;
10547
0
  }
10548
0
}
10549
10550
// Inline method implementations for `ChunkedDecoder`.
10551
namespace detail {
10552
10553
1.00k
inline ChunkedDecoder::ChunkedDecoder(Stream &s) : strm(s) {}
10554
10555
inline ssize_t ChunkedDecoder::read_payload(char *buf, size_t len,
10556
                                            size_t &out_chunk_offset,
10557
5.33k
                                            size_t &out_chunk_total) {
10558
5.33k
  if (finished) { return 0; }
10559
10560
5.33k
  if (chunk_remaining == 0) {
10561
4.81k
    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
10562
4.81k
    if (!lr.getline()) { return -1; }
10563
10564
4.77k
    char *endptr = nullptr;
10565
4.77k
    unsigned long chunk_len = std::strtoul(lr.ptr(), &endptr, 16);
10566
4.77k
    if (endptr == lr.ptr()) { return -1; }
10567
4.76k
    if (chunk_len == ULONG_MAX) { return -1; }
10568
10569
4.76k
    if (chunk_len == 0) {
10570
546
      chunk_remaining = 0;
10571
546
      finished = true;
10572
546
      out_chunk_offset = 0;
10573
546
      out_chunk_total = 0;
10574
546
      return 0;
10575
546
    }
10576
10577
4.22k
    chunk_remaining = static_cast<size_t>(chunk_len);
10578
4.22k
    last_chunk_total = chunk_remaining;
10579
4.22k
    last_chunk_offset = 0;
10580
4.22k
  }
10581
10582
4.74k
  auto to_read = (std::min)(chunk_remaining, len);
10583
4.74k
  auto n = strm.read(buf, to_read);
10584
4.74k
  if (n <= 0) { return -1; }
10585
10586
4.37k
  auto offset_before = last_chunk_offset;
10587
4.37k
  last_chunk_offset += static_cast<size_t>(n);
10588
4.37k
  chunk_remaining -= static_cast<size_t>(n);
10589
10590
4.37k
  out_chunk_offset = offset_before;
10591
4.37k
  out_chunk_total = last_chunk_total;
10592
10593
4.37k
  if (chunk_remaining == 0) {
10594
3.83k
    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
10595
3.83k
    if (!lr.getline()) { return -1; }
10596
3.83k
    if (std::strcmp(lr.ptr(), "\r\n") != 0) { return -1; }
10597
3.83k
  }
10598
10599
4.34k
  return n;
10600
4.37k
}
10601
10602
inline bool ChunkedDecoder::parse_trailers_into(Headers &dest,
10603
546
                                                const Headers &src_headers) {
10604
546
  stream_line_reader lr(strm, line_buf, sizeof(line_buf));
10605
546
  if (!lr.getline()) { return false; }
10606
537
  return parse_trailers(lr, dest, src_headers);
10607
546
}
10608
10609
} // namespace detail
10610
10611
inline void
10612
0
ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
10613
0
  handle.connection_->sock = socket_.sock;
10614
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10615
0
  handle.connection_->ssl = socket_.ssl;
10616
0
  socket_.ssl = nullptr;
10617
0
#endif
10618
0
  socket_.sock = INVALID_SOCKET;
10619
0
}
10620
10621
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
10622
                                       Response &res, bool close_connection,
10623
0
                                       Error &error) {
10624
0
  if (req.path.empty()) {
10625
0
    error = Error::Connection;
10626
0
    output_error_log(error, &req);
10627
0
    return false;
10628
0
  }
10629
0
10630
0
  auto req_save = req;
10631
0
10632
0
  bool ret;
10633
0
10634
0
  if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
10635
0
    auto req2 = req;
10636
0
    req2.path = "http://" +
10637
0
                detail::make_host_and_port_string(host_, port_, false) +
10638
0
                req.path;
10639
0
    ret = process_request(strm, req2, res, close_connection, error);
10640
0
    req = std::move(req2);
10641
0
    req.path = req_save.path;
10642
0
  } else {
10643
0
    ret = process_request(strm, req, res, close_connection, error);
10644
0
  }
10645
0
10646
0
  if (!ret) { return false; }
10647
0
10648
0
  if (res.get_header_value("Connection") == "close" ||
10649
0
      (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
10650
0
    // TODO this requires a not-entirely-obvious chain of calls to be correct
10651
0
    // for this to be safe.
10652
0
10653
0
    // This is safe to call because handle_request is only called by send_
10654
0
    // which locks the request mutex during the process. It would be a bug
10655
0
    // to call it from a different thread since it's a thread-safety issue
10656
0
    // to do these things to the socket if another thread is using the socket.
10657
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10658
0
    shutdown_ssl(socket_, true);
10659
0
    shutdown_socket(socket_);
10660
0
    close_socket(socket_);
10661
0
  }
10662
0
10663
0
  if (300 < res.status && res.status < 400 && follow_location_) {
10664
0
    req = std::move(req_save);
10665
0
    ret = redirect(req, res, error);
10666
0
  }
10667
0
10668
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10669
0
  if ((res.status == StatusCode::Unauthorized_401 ||
10670
0
       res.status == StatusCode::ProxyAuthenticationRequired_407) &&
10671
0
      req.authorization_count_ < 5) {
10672
0
    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
10673
0
    const auto &username =
10674
0
        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
10675
0
    const auto &password =
10676
0
        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
10677
0
10678
0
    if (!username.empty() && !password.empty()) {
10679
0
      std::map<std::string, std::string> auth;
10680
0
      if (detail::parse_www_authenticate(res, auth, is_proxy)) {
10681
0
        Request new_req = req;
10682
0
        new_req.authorization_count_ += 1;
10683
0
        new_req.headers.erase(is_proxy ? "Proxy-Authorization"
10684
0
                                       : "Authorization");
10685
0
        new_req.headers.insert(detail::make_digest_authentication_header(
10686
0
            req, auth, new_req.authorization_count_, detail::random_string(10),
10687
0
            username, password, is_proxy));
10688
0
10689
0
        Response new_res;
10690
0
10691
0
        ret = send(new_req, new_res, error);
10692
0
        if (ret) { res = std::move(new_res); }
10693
0
      }
10694
0
    }
10695
0
  }
10696
0
#endif
10697
0
10698
0
  return ret;
10699
0
}
10700
10701
0
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
10702
0
  if (req.redirect_count_ == 0) {
10703
0
    error = Error::ExceedRedirectCount;
10704
0
    output_error_log(error, &req);
10705
0
    return false;
10706
0
  }
10707
0
10708
0
  auto location = res.get_header_value("location");
10709
0
  if (location.empty()) { return false; }
10710
0
10711
0
  thread_local const std::regex re(
10712
0
      R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
10713
0
10714
0
  std::smatch m;
10715
0
  if (!std::regex_match(location, m, re)) { return false; }
10716
0
10717
0
  auto scheme = is_ssl() ? "https" : "http";
10718
0
10719
0
  auto next_scheme = m[1].str();
10720
0
  auto next_host = m[2].str();
10721
0
  if (next_host.empty()) { next_host = m[3].str(); }
10722
0
  auto port_str = m[4].str();
10723
0
  auto next_path = m[5].str();
10724
0
  auto next_query = m[6].str();
10725
0
10726
0
  auto next_port = port_;
10727
0
  if (!port_str.empty()) {
10728
0
    next_port = std::stoi(port_str);
10729
0
  } else if (!next_scheme.empty()) {
10730
0
    next_port = next_scheme == "https" ? 443 : 80;
10731
0
  }
10732
0
10733
0
  if (next_scheme.empty()) { next_scheme = scheme; }
10734
0
  if (next_host.empty()) { next_host = host_; }
10735
0
  if (next_path.empty()) { next_path = "/"; }
10736
0
10737
0
  auto path = decode_query_component(next_path, true) + next_query;
10738
0
10739
0
  // Same host redirect - use current client
10740
0
  if (next_scheme == scheme && next_host == host_ && next_port == port_) {
10741
0
    return detail::redirect(*this, req, res, path, location, error);
10742
0
  }
10743
0
10744
0
  // Cross-host/scheme redirect - create new client with robust setup
10745
0
  return create_redirect_client(next_scheme, next_host, next_port, req, res,
10746
0
                                path, location, error);
10747
0
}
10748
10749
// New method for robust redirect client creation
10750
inline bool ClientImpl::create_redirect_client(
10751
    const std::string &scheme, const std::string &host, int port, Request &req,
10752
    Response &res, const std::string &path, const std::string &location,
10753
0
    Error &error) {
10754
0
  // Determine if we need SSL
10755
0
  auto need_ssl = (scheme == "https");
10756
0
10757
0
  // Clean up request headers that are host/client specific
10758
0
  // Remove headers that should not be carried over to new host
10759
0
  auto headers_to_remove =
10760
0
      std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
10761
0
10762
0
  for (const auto &header_name : headers_to_remove) {
10763
0
    auto it = req.headers.find(header_name);
10764
0
    while (it != req.headers.end()) {
10765
0
      it = req.headers.erase(it);
10766
0
      it = req.headers.find(header_name);
10767
0
    }
10768
0
  }
10769
0
10770
0
  // Create appropriate client type and handle redirect
10771
0
  if (need_ssl) {
10772
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10773
0
    // Create SSL client for HTTPS redirect
10774
0
    SSLClient redirect_client(host, port);
10775
0
10776
0
    // Setup basic client configuration first
10777
0
    setup_redirect_client(redirect_client);
10778
0
10779
0
    // SSL-specific configuration for proxy environments
10780
0
    if (!proxy_host_.empty() && proxy_port_ != -1) {
10781
0
      // Critical: Disable SSL verification for proxy environments
10782
0
      redirect_client.enable_server_certificate_verification(false);
10783
0
      redirect_client.enable_server_hostname_verification(false);
10784
0
    } else {
10785
0
      // For direct SSL connections, copy SSL verification settings
10786
0
      redirect_client.enable_server_certificate_verification(
10787
0
          server_certificate_verification_);
10788
0
      redirect_client.enable_server_hostname_verification(
10789
0
          server_hostname_verification_);
10790
0
    }
10791
0
10792
0
    // Handle CA certificate store and paths if available
10793
0
    if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_)) {
10794
0
      redirect_client.set_ca_cert_store(ca_cert_store_);
10795
0
    }
10796
0
    if (!ca_cert_file_path_.empty()) {
10797
0
      redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
10798
0
    }
10799
0
10800
0
    // Client certificates are set through constructor for SSLClient
10801
0
    // NOTE: SSLClient constructor already takes client_cert_path and
10802
0
    // client_key_path so we need to create it properly if client certs are
10803
0
    // needed
10804
0
10805
0
    // Execute the redirect
10806
0
    return detail::redirect(redirect_client, req, res, path, location, error);
10807
0
#else
10808
0
    // SSL not supported - set appropriate error
10809
0
    error = Error::SSLConnection;
10810
0
    output_error_log(error, &req);
10811
0
    return false;
10812
0
#endif
10813
0
  } else {
10814
0
    // HTTP redirect
10815
0
    ClientImpl redirect_client(host, port);
10816
0
10817
0
    // Setup client with robust configuration
10818
0
    setup_redirect_client(redirect_client);
10819
0
10820
0
    // Execute the redirect
10821
0
    return detail::redirect(redirect_client, req, res, path, location, error);
10822
0
  }
10823
0
}
10824
10825
// New method for robust client setup (based on basic_manual_redirect.cpp
10826
// logic)
10827
template <typename ClientType>
10828
0
inline void ClientImpl::setup_redirect_client(ClientType &client) {
10829
0
  // Copy basic settings first
10830
0
  client.set_connection_timeout(connection_timeout_sec_);
10831
0
  client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
10832
0
  client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
10833
0
  client.set_keep_alive(keep_alive_);
10834
0
  client.set_follow_location(
10835
0
      true); // Enable redirects to handle multi-step redirects
10836
0
  client.set_path_encode(path_encode_);
10837
0
  client.set_compress(compress_);
10838
0
  client.set_decompress(decompress_);
10839
0
10840
0
  // Copy authentication settings BEFORE proxy setup
10841
0
  if (!basic_auth_username_.empty()) {
10842
0
    client.set_basic_auth(basic_auth_username_, basic_auth_password_);
10843
0
  }
10844
0
  if (!bearer_token_auth_token_.empty()) {
10845
0
    client.set_bearer_token_auth(bearer_token_auth_token_);
10846
0
  }
10847
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10848
0
  if (!digest_auth_username_.empty()) {
10849
0
    client.set_digest_auth(digest_auth_username_, digest_auth_password_);
10850
0
  }
10851
0
#endif
10852
0
10853
0
  // Setup proxy configuration (CRITICAL ORDER - proxy must be set
10854
0
  // before proxy auth)
10855
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
10856
0
    // First set proxy host and port
10857
0
    client.set_proxy(proxy_host_, proxy_port_);
10858
0
10859
0
    // Then set proxy authentication (order matters!)
10860
0
    if (!proxy_basic_auth_username_.empty()) {
10861
0
      client.set_proxy_basic_auth(proxy_basic_auth_username_,
10862
0
                                  proxy_basic_auth_password_);
10863
0
    }
10864
0
    if (!proxy_bearer_token_auth_token_.empty()) {
10865
0
      client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
10866
0
    }
10867
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10868
0
    if (!proxy_digest_auth_username_.empty()) {
10869
0
      client.set_proxy_digest_auth(proxy_digest_auth_username_,
10870
0
                                   proxy_digest_auth_password_);
10871
0
    }
10872
0
#endif
10873
0
  }
10874
0
10875
0
  // Copy network and socket settings
10876
0
  client.set_address_family(address_family_);
10877
0
  client.set_tcp_nodelay(tcp_nodelay_);
10878
0
  client.set_ipv6_v6only(ipv6_v6only_);
10879
0
  if (socket_options_) { client.set_socket_options(socket_options_); }
10880
0
  if (!interface_.empty()) { client.set_interface(interface_); }
10881
0
10882
0
  // Copy logging and headers
10883
0
  if (logger_) { client.set_logger(logger_); }
10884
0
  if (error_logger_) { client.set_error_logger(error_logger_); }
10885
0
10886
0
  // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
10887
0
  // Each new client should generate its own headers based on its target host
10888
0
}
10889
10890
inline bool ClientImpl::write_content_with_provider(Stream &strm,
10891
                                                    const Request &req,
10892
0
                                                    Error &error) const {
10893
0
  auto is_shutting_down = []() { return false; };
10894
0
10895
0
  if (req.is_chunked_content_provider_) {
10896
0
    // TODO: Brotli support
10897
0
    std::unique_ptr<detail::compressor> compressor;
10898
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
10899
0
    if (compress_) {
10900
0
      compressor = detail::make_unique<detail::gzip_compressor>();
10901
0
    } else
10902
0
#endif
10903
0
    {
10904
0
      compressor = detail::make_unique<detail::nocompressor>();
10905
0
    }
10906
0
10907
0
    return detail::write_content_chunked(strm, req.content_provider_,
10908
0
                                         is_shutting_down, *compressor, error);
10909
0
  } else {
10910
0
    return detail::write_content_with_progress(
10911
0
        strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
10912
0
        req.upload_progress, error);
10913
0
  }
10914
0
}
10915
10916
inline bool ClientImpl::write_request(Stream &strm, Request &req,
10917
                                      bool close_connection, Error &error,
10918
0
                                      bool skip_body) {
10919
0
  // Prepare additional headers
10920
0
  if (close_connection) {
10921
0
    if (!req.has_header("Connection")) {
10922
0
      req.set_header("Connection", "close");
10923
0
    }
10924
0
  }
10925
0
10926
0
  std::string ct_for_defaults;
10927
0
  if (!req.has_header("Content-Type") && !req.body.empty()) {
10928
0
    ct_for_defaults = "text/plain";
10929
0
  }
10930
0
  prepare_default_headers(req, false, ct_for_defaults);
10931
0
10932
0
  if (req.body.empty()) {
10933
0
    if (req.content_provider_) {
10934
0
      if (!req.is_chunked_content_provider_) {
10935
0
        if (!req.has_header("Content-Length")) {
10936
0
          auto length = std::to_string(req.content_length_);
10937
0
          req.set_header("Content-Length", length);
10938
0
        }
10939
0
      }
10940
0
    } else {
10941
0
      if (req.method == "POST" || req.method == "PUT" ||
10942
0
          req.method == "PATCH") {
10943
0
        req.set_header("Content-Length", "0");
10944
0
      }
10945
0
    }
10946
0
  }
10947
0
10948
0
  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
10949
0
    if (!req.has_header("Authorization")) {
10950
0
      req.headers.insert(make_basic_authentication_header(
10951
0
          basic_auth_username_, basic_auth_password_, false));
10952
0
    }
10953
0
  }
10954
0
10955
0
  if (!proxy_basic_auth_username_.empty() &&
10956
0
      !proxy_basic_auth_password_.empty()) {
10957
0
    if (!req.has_header("Proxy-Authorization")) {
10958
0
      req.headers.insert(make_basic_authentication_header(
10959
0
          proxy_basic_auth_username_, proxy_basic_auth_password_, true));
10960
0
    }
10961
0
  }
10962
0
10963
0
  if (!bearer_token_auth_token_.empty()) {
10964
0
    if (!req.has_header("Authorization")) {
10965
0
      req.headers.insert(make_bearer_token_authentication_header(
10966
0
          bearer_token_auth_token_, false));
10967
0
    }
10968
0
  }
10969
0
10970
0
  if (!proxy_bearer_token_auth_token_.empty()) {
10971
0
    if (!req.has_header("Proxy-Authorization")) {
10972
0
      req.headers.insert(make_bearer_token_authentication_header(
10973
0
          proxy_bearer_token_auth_token_, true));
10974
0
    }
10975
0
  }
10976
0
10977
0
  // Request line and headers
10978
0
  {
10979
0
    detail::BufferStream bstrm;
10980
0
10981
0
    // Extract path and query from req.path
10982
0
    std::string path_part, query_part;
10983
0
    auto query_pos = req.path.find('?');
10984
0
    if (query_pos != std::string::npos) {
10985
0
      path_part = req.path.substr(0, query_pos);
10986
0
      query_part = req.path.substr(query_pos + 1);
10987
0
    } else {
10988
0
      path_part = req.path;
10989
0
      query_part = "";
10990
0
    }
10991
0
10992
0
    // Encode path part. If the original `req.path` already contained a
10993
0
    // query component, preserve its raw query string (including parameter
10994
0
    // order) instead of reparsing and reassembling it which may reorder
10995
0
    // parameters due to container ordering (e.g. `Params` uses
10996
0
    // `std::multimap`). When there is no query in `req.path`, fall back to
10997
0
    // building a query from `req.params` so existing callers that pass
10998
0
    // `Params` continue to work.
10999
0
    auto path_with_query =
11000
0
        path_encode_ ? detail::encode_path(path_part) : path_part;
11001
0
11002
0
    if (!query_part.empty()) {
11003
0
      // Normalize the query string (decode then re-encode) while preserving
11004
0
      // the original parameter order.
11005
0
      auto normalized = detail::normalize_query_string(query_part);
11006
0
      if (!normalized.empty()) { path_with_query += '?' + normalized; }
11007
0
11008
0
      // Still populate req.params for handlers/users who read them.
11009
0
      detail::parse_query_text(query_part, req.params);
11010
0
    } else {
11011
0
      // No query in path; parse any query_part (empty) and append params
11012
0
      // from `req.params` when present (preserves prior behavior for
11013
0
      // callers who provide Params separately).
11014
0
      detail::parse_query_text(query_part, req.params);
11015
0
      if (!req.params.empty()) {
11016
0
        path_with_query = append_query_params(path_with_query, req.params);
11017
0
      }
11018
0
    }
11019
0
11020
0
    // Write request line and headers
11021
0
    detail::write_request_line(bstrm, req.method, path_with_query);
11022
0
    if (!detail::check_and_write_headers(bstrm, req.headers, header_writer_,
11023
0
                                         error)) {
11024
0
      output_error_log(error, &req);
11025
0
      return false;
11026
0
    }
11027
0
11028
0
    // Flush buffer
11029
0
    auto &data = bstrm.get_buffer();
11030
0
    if (!detail::write_data(strm, data.data(), data.size())) {
11031
0
      error = Error::Write;
11032
0
      output_error_log(error, &req);
11033
0
      return false;
11034
0
    }
11035
0
  }
11036
0
11037
0
  // After sending request line and headers, wait briefly for an early server
11038
0
  // response (e.g. 4xx) and avoid sending a potentially large request body
11039
0
  // unnecessarily. This workaround is only enabled on Windows because Unix
11040
0
  // platforms surface write errors (EPIPE) earlier; on Windows kernel send
11041
0
  // buffering can accept large writes even when the peer already responded.
11042
0
  // Check the stream first (which covers SSL via `is_readable()`), then
11043
0
  // fall back to select on the socket. Only perform the wait for very large
11044
0
  // request bodies to avoid interfering with normal small requests and
11045
0
  // reduce side-effects. Poll briefly (up to 50ms as default) for an early
11046
0
  // response. Skip this check when using Expect: 100-continue, as the protocol
11047
0
  // handles early responses properly.
11048
0
#if defined(_WIN32)
11049
0
  if (!skip_body &&
11050
0
      req.body.size() > CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD &&
11051
0
      req.path.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
11052
0
    auto start = std::chrono::high_resolution_clock::now();
11053
0
11054
0
    for (;;) {
11055
0
      // Prefer socket-level readiness to avoid SSL_pending() false-positives
11056
0
      // from SSL internals. If the underlying socket is readable, assume an
11057
0
      // early response may be present.
11058
0
      auto sock = strm.socket();
11059
0
      if (sock != INVALID_SOCKET && detail::select_read(sock, 0, 0) > 0) {
11060
0
        return false;
11061
0
      }
11062
0
11063
0
      // Fallback to stream-level check for non-socket streams or when the
11064
0
      // socket isn't reporting readable. Avoid using `is_readable()` for
11065
0
      // SSL, since `SSL_pending()` may report buffered records that do not
11066
0
      // indicate a complete application-level response yet.
11067
0
      if (!is_ssl() && strm.is_readable()) { return false; }
11068
0
11069
0
      auto now = std::chrono::high_resolution_clock::now();
11070
0
      auto elapsed =
11071
0
          std::chrono::duration_cast<std::chrono::milliseconds>(now - start)
11072
0
              .count();
11073
0
      if (elapsed >= CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND) {
11074
0
        break;
11075
0
      }
11076
0
11077
0
      std::this_thread::sleep_for(std::chrono::milliseconds(1));
11078
0
    }
11079
0
  }
11080
0
#endif
11081
0
11082
0
  // Body
11083
0
  if (skip_body) { return true; }
11084
0
11085
0
  return write_request_body(strm, req, error);
11086
0
}
11087
11088
inline bool ClientImpl::write_request_body(Stream &strm, Request &req,
11089
0
                                           Error &error) {
11090
0
  if (req.body.empty()) {
11091
0
    return write_content_with_provider(strm, req, error);
11092
0
  }
11093
0
11094
0
  if (req.upload_progress) {
11095
0
    auto body_size = req.body.size();
11096
0
    size_t written = 0;
11097
0
    auto data = req.body.data();
11098
0
11099
0
    while (written < body_size) {
11100
0
      size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
11101
0
      if (!detail::write_data(strm, data + written, to_write)) {
11102
0
        error = Error::Write;
11103
0
        output_error_log(error, &req);
11104
0
        return false;
11105
0
      }
11106
0
      written += to_write;
11107
0
11108
0
      if (!req.upload_progress(written, body_size)) {
11109
0
        error = Error::Canceled;
11110
0
        output_error_log(error, &req);
11111
0
        return false;
11112
0
      }
11113
0
    }
11114
0
  } else {
11115
0
    if (!detail::write_data(strm, req.body.data(), req.body.size())) {
11116
0
      error = Error::Write;
11117
0
      output_error_log(error, &req);
11118
0
      return false;
11119
0
    }
11120
0
  }
11121
0
11122
0
  return true;
11123
0
}
11124
11125
inline std::unique_ptr<Response>
11126
ClientImpl::send_with_content_provider_and_receiver(
11127
    Request &req, const char *body, size_t content_length,
11128
    ContentProvider content_provider,
11129
    ContentProviderWithoutLength content_provider_without_length,
11130
    const std::string &content_type, ContentReceiver content_receiver,
11131
0
    Error &error) {
11132
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
11133
0
11134
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
11135
0
  if (compress_) { req.set_header("Content-Encoding", "gzip"); }
11136
0
#endif
11137
0
11138
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
11139
0
  if (compress_ && !content_provider_without_length) {
11140
0
    // TODO: Brotli support
11141
0
    detail::gzip_compressor compressor;
11142
0
11143
0
    if (content_provider) {
11144
0
      auto ok = true;
11145
0
      size_t offset = 0;
11146
0
      DataSink data_sink;
11147
0
11148
0
      data_sink.write = [&](const char *data, size_t data_len) -> bool {
11149
0
        if (ok) {
11150
0
          auto last = offset + data_len == content_length;
11151
0
11152
0
          auto ret = compressor.compress(
11153
0
              data, data_len, last,
11154
0
              [&](const char *compressed_data, size_t compressed_data_len) {
11155
0
                req.body.append(compressed_data, compressed_data_len);
11156
0
                return true;
11157
0
              });
11158
0
11159
0
          if (ret) {
11160
0
            offset += data_len;
11161
0
          } else {
11162
0
            ok = false;
11163
0
          }
11164
0
        }
11165
0
        return ok;
11166
0
      };
11167
0
11168
0
      while (ok && offset < content_length) {
11169
0
        if (!content_provider(offset, content_length - offset, data_sink)) {
11170
0
          error = Error::Canceled;
11171
0
          output_error_log(error, &req);
11172
0
          return nullptr;
11173
0
        }
11174
0
      }
11175
0
    } else {
11176
0
      if (!compressor.compress(body, content_length, true,
11177
0
                               [&](const char *data, size_t data_len) {
11178
0
                                 req.body.append(data, data_len);
11179
0
                                 return true;
11180
0
                               })) {
11181
0
        error = Error::Compression;
11182
0
        output_error_log(error, &req);
11183
0
        return nullptr;
11184
0
      }
11185
0
    }
11186
0
  } else
11187
0
#endif
11188
0
  {
11189
0
    if (content_provider) {
11190
0
      req.content_length_ = content_length;
11191
0
      req.content_provider_ = std::move(content_provider);
11192
0
      req.is_chunked_content_provider_ = false;
11193
0
    } else if (content_provider_without_length) {
11194
0
      req.content_length_ = 0;
11195
0
      req.content_provider_ = detail::ContentProviderAdapter(
11196
0
          std::move(content_provider_without_length));
11197
0
      req.is_chunked_content_provider_ = true;
11198
0
      req.set_header("Transfer-Encoding", "chunked");
11199
0
    } else {
11200
0
      req.body.assign(body, content_length);
11201
0
    }
11202
0
  }
11203
0
11204
0
  if (content_receiver) {
11205
0
    req.content_receiver =
11206
0
        [content_receiver](const char *data, size_t data_length,
11207
0
                           size_t /*offset*/, size_t /*total_length*/) {
11208
0
          return content_receiver(data, data_length);
11209
0
        };
11210
0
  }
11211
0
11212
0
  auto res = detail::make_unique<Response>();
11213
0
  return send(req, *res, error) ? std::move(res) : nullptr;
11214
0
}
11215
11216
inline Result ClientImpl::send_with_content_provider_and_receiver(
11217
    const std::string &method, const std::string &path, const Headers &headers,
11218
    const char *body, size_t content_length, ContentProvider content_provider,
11219
    ContentProviderWithoutLength content_provider_without_length,
11220
    const std::string &content_type, ContentReceiver content_receiver,
11221
0
    UploadProgress progress) {
11222
0
  Request req;
11223
0
  req.method = method;
11224
0
  req.headers = headers;
11225
0
  req.path = path;
11226
0
  req.upload_progress = std::move(progress);
11227
0
  if (max_timeout_msec_ > 0) {
11228
0
    req.start_time_ = std::chrono::steady_clock::now();
11229
0
  }
11230
0
11231
0
  auto error = Error::Success;
11232
0
11233
0
  auto res = send_with_content_provider_and_receiver(
11234
0
      req, body, content_length, std::move(content_provider),
11235
0
      std::move(content_provider_without_length), content_type,
11236
0
      std::move(content_receiver), error);
11237
0
11238
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11239
0
  return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
11240
0
                last_openssl_error_};
11241
0
#else
11242
0
  return Result{std::move(res), error, std::move(req.headers)};
11243
0
#endif
11244
0
}
11245
11246
inline void ClientImpl::output_log(const Request &req,
11247
0
                                   const Response &res) const {
11248
0
  if (logger_) {
11249
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
11250
0
    logger_(req, res);
11251
0
  }
11252
0
}
11253
11254
inline void ClientImpl::output_error_log(const Error &err,
11255
0
                                         const Request *req) const {
11256
0
  if (error_logger_) {
11257
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
11258
0
    error_logger_(err, req);
11259
0
  }
11260
0
}
11261
11262
inline bool ClientImpl::process_request(Stream &strm, Request &req,
11263
                                        Response &res, bool close_connection,
11264
0
                                        Error &error) {
11265
0
  // Auto-add Expect: 100-continue for large bodies
11266
0
  if (CPPHTTPLIB_EXPECT_100_THRESHOLD > 0 && !req.has_header("Expect")) {
11267
0
    auto body_size = req.body.empty() ? req.content_length_ : req.body.size();
11268
0
    if (body_size >= CPPHTTPLIB_EXPECT_100_THRESHOLD) {
11269
0
      req.set_header("Expect", "100-continue");
11270
0
    }
11271
0
  }
11272
0
11273
0
  // Check for Expect: 100-continue
11274
0
  auto expect_100_continue = req.get_header_value("Expect") == "100-continue";
11275
0
11276
0
  // Send request (skip body if using Expect: 100-continue)
11277
0
  auto write_request_success =
11278
0
      write_request(strm, req, close_connection, error, expect_100_continue);
11279
0
11280
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11281
0
  if (is_ssl()) {
11282
0
    auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
11283
0
    if (!is_proxy_enabled) {
11284
0
      if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
11285
0
        error = Error::SSLPeerCouldBeClosed_;
11286
0
        output_error_log(error, &req);
11287
0
        return false;
11288
0
      }
11289
0
    }
11290
0
  }
11291
0
#endif
11292
0
11293
0
  // Handle Expect: 100-continue with timeout
11294
0
  if (expect_100_continue && CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND > 0) {
11295
0
    time_t sec = CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND / 1000;
11296
0
    time_t usec = (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND % 1000) * 1000;
11297
0
    auto ret = detail::select_read(strm.socket(), sec, usec);
11298
0
    if (ret <= 0) {
11299
0
      // Timeout or error: send body anyway (server didn't respond in time)
11300
0
      if (!write_request_body(strm, req, error)) { return false; }
11301
0
      expect_100_continue = false; // Switch to normal response handling
11302
0
    }
11303
0
  }
11304
0
11305
0
  // Receive response and headers
11306
0
  // When using Expect: 100-continue, don't auto-skip `100 Continue` response
11307
0
  if (!read_response_line(strm, req, res, !expect_100_continue) ||
11308
0
      !detail::read_headers(strm, res.headers)) {
11309
0
    if (write_request_success) { error = Error::Read; }
11310
0
    output_error_log(error, &req);
11311
0
    return false;
11312
0
  }
11313
0
11314
0
  if (!write_request_success) { return false; }
11315
0
11316
0
  // Handle Expect: 100-continue response
11317
0
  if (expect_100_continue) {
11318
0
    if (res.status == StatusCode::Continue_100) {
11319
0
      // Server accepted, send the body
11320
0
      if (!write_request_body(strm, req, error)) { return false; }
11321
0
11322
0
      // Read the actual response
11323
0
      res.headers.clear();
11324
0
      res.body.clear();
11325
0
      if (!read_response_line(strm, req, res) ||
11326
0
          !detail::read_headers(strm, res.headers)) {
11327
0
        error = Error::Read;
11328
0
        output_error_log(error, &req);
11329
0
        return false;
11330
0
      }
11331
0
    }
11332
0
    // If not 100 Continue, server returned an error; proceed with that response
11333
0
  }
11334
0
11335
0
  // Body
11336
0
  if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
11337
0
      req.method != "CONNECT") {
11338
0
    auto redirect = 300 < res.status && res.status < 400 &&
11339
0
                    res.status != StatusCode::NotModified_304 &&
11340
0
                    follow_location_;
11341
0
11342
0
    if (req.response_handler && !redirect) {
11343
0
      if (!req.response_handler(res)) {
11344
0
        error = Error::Canceled;
11345
0
        output_error_log(error, &req);
11346
0
        return false;
11347
0
      }
11348
0
    }
11349
0
11350
0
    auto out =
11351
0
        req.content_receiver
11352
0
            ? static_cast<ContentReceiverWithProgress>(
11353
0
                  [&](const char *buf, size_t n, size_t off, size_t len) {
11354
0
                    if (redirect) { return true; }
11355
0
                    auto ret = req.content_receiver(buf, n, off, len);
11356
0
                    if (!ret) {
11357
0
                      error = Error::Canceled;
11358
0
                      output_error_log(error, &req);
11359
0
                    }
11360
0
                    return ret;
11361
0
                  })
11362
0
            : static_cast<ContentReceiverWithProgress>(
11363
0
                  [&](const char *buf, size_t n, size_t /*off*/,
11364
0
                      size_t /*len*/) {
11365
0
                    assert(res.body.size() + n <= res.body.max_size());
11366
0
                    res.body.append(buf, n);
11367
0
                    return true;
11368
0
                  });
11369
0
11370
0
    auto progress = [&](size_t current, size_t total) {
11371
0
      if (!req.download_progress || redirect) { return true; }
11372
0
      auto ret = req.download_progress(current, total);
11373
0
      if (!ret) {
11374
0
        error = Error::Canceled;
11375
0
        output_error_log(error, &req);
11376
0
      }
11377
0
      return ret;
11378
0
    };
11379
0
11380
0
    if (res.has_header("Content-Length")) {
11381
0
      if (!req.content_receiver) {
11382
0
        auto len = res.get_header_value_u64("Content-Length");
11383
0
        if (len > res.body.max_size()) {
11384
0
          error = Error::Read;
11385
0
          output_error_log(error, &req);
11386
0
          return false;
11387
0
        }
11388
0
        res.body.reserve(static_cast<size_t>(len));
11389
0
      }
11390
0
    }
11391
0
11392
0
    if (res.status != StatusCode::NotModified_304) {
11393
0
      int dummy_status;
11394
0
      if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
11395
0
                                dummy_status, std::move(progress),
11396
0
                                std::move(out), decompress_)) {
11397
0
        if (error != Error::Canceled) { error = Error::Read; }
11398
0
        output_error_log(error, &req);
11399
0
        return false;
11400
0
      }
11401
0
    }
11402
0
  }
11403
0
11404
0
  // Log
11405
0
  output_log(req, res);
11406
0
11407
0
  return true;
11408
0
}
11409
11410
inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
11411
    const std::string &boundary, const UploadFormDataItems &items,
11412
0
    const FormDataProviderItems &provider_items) const {
11413
0
  size_t cur_item = 0;
11414
0
  size_t cur_start = 0;
11415
0
  // cur_item and cur_start are copied to within the std::function and
11416
0
  // maintain state between successive calls
11417
0
  return [&, cur_item, cur_start](size_t offset,
11418
0
                                  DataSink &sink) mutable -> bool {
11419
0
    if (!offset && !items.empty()) {
11420
0
      sink.os << detail::serialize_multipart_formdata(items, boundary, false);
11421
0
      return true;
11422
0
    } else if (cur_item < provider_items.size()) {
11423
0
      if (!cur_start) {
11424
0
        const auto &begin = detail::serialize_multipart_formdata_item_begin(
11425
0
            provider_items[cur_item], boundary);
11426
0
        offset += begin.size();
11427
0
        cur_start = offset;
11428
0
        sink.os << begin;
11429
0
      }
11430
0
11431
0
      DataSink cur_sink;
11432
0
      auto has_data = true;
11433
0
      cur_sink.write = sink.write;
11434
0
      cur_sink.done = [&]() { has_data = false; };
11435
0
11436
0
      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
11437
0
        return false;
11438
0
      }
11439
0
11440
0
      if (!has_data) {
11441
0
        sink.os << detail::serialize_multipart_formdata_item_end();
11442
0
        cur_item++;
11443
0
        cur_start = 0;
11444
0
      }
11445
0
      return true;
11446
0
    } else {
11447
0
      sink.os << detail::serialize_multipart_formdata_finish(boundary);
11448
0
      sink.done();
11449
0
      return true;
11450
0
    }
11451
0
  };
11452
0
}
11453
11454
inline bool ClientImpl::process_socket(
11455
    const Socket &socket,
11456
    std::chrono::time_point<std::chrono::steady_clock> start_time,
11457
0
    std::function<bool(Stream &strm)> callback) {
11458
0
  return detail::process_client_socket(
11459
0
      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
11460
0
      write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
11461
0
}
11462
11463
0
inline bool ClientImpl::is_ssl() const { return false; }
11464
11465
inline Result ClientImpl::Get(const std::string &path,
11466
0
                              DownloadProgress progress) {
11467
0
  return Get(path, Headers(), std::move(progress));
11468
0
}
11469
11470
inline Result ClientImpl::Get(const std::string &path, const Params &params,
11471
                              const Headers &headers,
11472
0
                              DownloadProgress progress) {
11473
0
  if (params.empty()) { return Get(path, headers); }
11474
0
11475
0
  std::string path_with_query = append_query_params(path, params);
11476
0
  return Get(path_with_query, headers, std::move(progress));
11477
0
}
11478
11479
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
11480
0
                              DownloadProgress progress) {
11481
0
  Request req;
11482
0
  req.method = "GET";
11483
0
  req.path = path;
11484
0
  req.headers = headers;
11485
0
  req.download_progress = std::move(progress);
11486
0
  if (max_timeout_msec_ > 0) {
11487
0
    req.start_time_ = std::chrono::steady_clock::now();
11488
0
  }
11489
0
11490
0
  return send_(std::move(req));
11491
0
}
11492
11493
inline Result ClientImpl::Get(const std::string &path,
11494
                              ContentReceiver content_receiver,
11495
0
                              DownloadProgress progress) {
11496
0
  return Get(path, Headers(), nullptr, std::move(content_receiver),
11497
0
             std::move(progress));
11498
0
}
11499
11500
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
11501
                              ContentReceiver content_receiver,
11502
0
                              DownloadProgress progress) {
11503
0
  return Get(path, headers, nullptr, std::move(content_receiver),
11504
0
             std::move(progress));
11505
0
}
11506
11507
inline Result ClientImpl::Get(const std::string &path,
11508
                              ResponseHandler response_handler,
11509
                              ContentReceiver content_receiver,
11510
0
                              DownloadProgress progress) {
11511
0
  return Get(path, Headers(), std::move(response_handler),
11512
0
             std::move(content_receiver), std::move(progress));
11513
0
}
11514
11515
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
11516
                              ResponseHandler response_handler,
11517
                              ContentReceiver content_receiver,
11518
0
                              DownloadProgress progress) {
11519
0
  Request req;
11520
0
  req.method = "GET";
11521
0
  req.path = path;
11522
0
  req.headers = headers;
11523
0
  req.response_handler = std::move(response_handler);
11524
0
  req.content_receiver =
11525
0
      [content_receiver](const char *data, size_t data_length,
11526
0
                         size_t /*offset*/, size_t /*total_length*/) {
11527
0
        return content_receiver(data, data_length);
11528
0
      };
11529
0
  req.download_progress = std::move(progress);
11530
0
  if (max_timeout_msec_ > 0) {
11531
0
    req.start_time_ = std::chrono::steady_clock::now();
11532
0
  }
11533
0
11534
0
  return send_(std::move(req));
11535
0
}
11536
11537
inline Result ClientImpl::Get(const std::string &path, const Params &params,
11538
                              const Headers &headers,
11539
                              ContentReceiver content_receiver,
11540
0
                              DownloadProgress progress) {
11541
0
  return Get(path, params, headers, nullptr, std::move(content_receiver),
11542
0
             std::move(progress));
11543
0
}
11544
11545
inline Result ClientImpl::Get(const std::string &path, const Params &params,
11546
                              const Headers &headers,
11547
                              ResponseHandler response_handler,
11548
                              ContentReceiver content_receiver,
11549
0
                              DownloadProgress progress) {
11550
0
  if (params.empty()) {
11551
0
    return Get(path, headers, std::move(response_handler),
11552
0
               std::move(content_receiver), std::move(progress));
11553
0
  }
11554
0
11555
0
  std::string path_with_query = append_query_params(path, params);
11556
0
  return Get(path_with_query, headers, std::move(response_handler),
11557
0
             std::move(content_receiver), std::move(progress));
11558
0
}
11559
11560
0
inline Result ClientImpl::Head(const std::string &path) {
11561
0
  return Head(path, Headers());
11562
0
}
11563
11564
inline Result ClientImpl::Head(const std::string &path,
11565
0
                               const Headers &headers) {
11566
0
  Request req;
11567
0
  req.method = "HEAD";
11568
0
  req.headers = headers;
11569
0
  req.path = path;
11570
0
  if (max_timeout_msec_ > 0) {
11571
0
    req.start_time_ = std::chrono::steady_clock::now();
11572
0
  }
11573
0
11574
0
  return send_(std::move(req));
11575
0
}
11576
11577
0
inline Result ClientImpl::Post(const std::string &path) {
11578
0
  return Post(path, std::string(), std::string());
11579
0
}
11580
11581
inline Result ClientImpl::Post(const std::string &path,
11582
0
                               const Headers &headers) {
11583
0
  return Post(path, headers, nullptr, 0, std::string());
11584
0
}
11585
11586
inline Result ClientImpl::Post(const std::string &path, const char *body,
11587
                               size_t content_length,
11588
                               const std::string &content_type,
11589
0
                               UploadProgress progress) {
11590
0
  return Post(path, Headers(), body, content_length, content_type, progress);
11591
0
}
11592
11593
inline Result ClientImpl::Post(const std::string &path, const std::string &body,
11594
                               const std::string &content_type,
11595
0
                               UploadProgress progress) {
11596
0
  return Post(path, Headers(), body, content_type, progress);
11597
0
}
11598
11599
0
inline Result ClientImpl::Post(const std::string &path, const Params &params) {
11600
0
  return Post(path, Headers(), params);
11601
0
}
11602
11603
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
11604
                               ContentProvider content_provider,
11605
                               const std::string &content_type,
11606
0
                               UploadProgress progress) {
11607
0
  return Post(path, Headers(), content_length, std::move(content_provider),
11608
0
              content_type, progress);
11609
0
}
11610
11611
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
11612
                               ContentProvider content_provider,
11613
                               const std::string &content_type,
11614
                               ContentReceiver content_receiver,
11615
0
                               UploadProgress progress) {
11616
0
  return Post(path, Headers(), content_length, std::move(content_provider),
11617
0
              content_type, std::move(content_receiver), progress);
11618
0
}
11619
11620
inline Result ClientImpl::Post(const std::string &path,
11621
                               ContentProviderWithoutLength content_provider,
11622
                               const std::string &content_type,
11623
0
                               UploadProgress progress) {
11624
0
  return Post(path, Headers(), std::move(content_provider), content_type,
11625
0
              progress);
11626
0
}
11627
11628
inline Result ClientImpl::Post(const std::string &path,
11629
                               ContentProviderWithoutLength content_provider,
11630
                               const std::string &content_type,
11631
                               ContentReceiver content_receiver,
11632
0
                               UploadProgress progress) {
11633
0
  return Post(path, Headers(), std::move(content_provider), content_type,
11634
0
              std::move(content_receiver), progress);
11635
0
}
11636
11637
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11638
0
                               const Params &params) {
11639
0
  auto query = detail::params_to_query_str(params);
11640
0
  return Post(path, headers, query, "application/x-www-form-urlencoded");
11641
0
}
11642
11643
inline Result ClientImpl::Post(const std::string &path,
11644
                               const UploadFormDataItems &items,
11645
0
                               UploadProgress progress) {
11646
0
  return Post(path, Headers(), items, progress);
11647
0
}
11648
11649
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11650
                               const UploadFormDataItems &items,
11651
0
                               UploadProgress progress) {
11652
0
  const auto &boundary = detail::make_multipart_data_boundary();
11653
0
  const auto &content_type =
11654
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11655
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11656
0
  return Post(path, headers, body, content_type, progress);
11657
0
}
11658
11659
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11660
                               const UploadFormDataItems &items,
11661
                               const std::string &boundary,
11662
0
                               UploadProgress progress) {
11663
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
11664
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
11665
0
  }
11666
0
11667
0
  const auto &content_type =
11668
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11669
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11670
0
  return Post(path, headers, body, content_type, progress);
11671
0
}
11672
11673
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11674
                               const char *body, size_t content_length,
11675
                               const std::string &content_type,
11676
0
                               UploadProgress progress) {
11677
0
  return send_with_content_provider_and_receiver(
11678
0
      "POST", path, headers, body, content_length, nullptr, nullptr,
11679
0
      content_type, nullptr, progress);
11680
0
}
11681
11682
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11683
                               const std::string &body,
11684
                               const std::string &content_type,
11685
0
                               UploadProgress progress) {
11686
0
  return send_with_content_provider_and_receiver(
11687
0
      "POST", path, headers, body.data(), body.size(), nullptr, nullptr,
11688
0
      content_type, nullptr, progress);
11689
0
}
11690
11691
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11692
                               size_t content_length,
11693
                               ContentProvider content_provider,
11694
                               const std::string &content_type,
11695
0
                               UploadProgress progress) {
11696
0
  return send_with_content_provider_and_receiver(
11697
0
      "POST", path, headers, nullptr, content_length,
11698
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
11699
0
}
11700
11701
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11702
                               size_t content_length,
11703
                               ContentProvider content_provider,
11704
                               const std::string &content_type,
11705
                               ContentReceiver content_receiver,
11706
0
                               DownloadProgress progress) {
11707
0
  return send_with_content_provider_and_receiver(
11708
0
      "POST", path, headers, nullptr, content_length,
11709
0
      std::move(content_provider), nullptr, content_type,
11710
0
      std::move(content_receiver), std::move(progress));
11711
0
}
11712
11713
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11714
                               ContentProviderWithoutLength content_provider,
11715
                               const std::string &content_type,
11716
0
                               UploadProgress progress) {
11717
0
  return send_with_content_provider_and_receiver(
11718
0
      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11719
0
      content_type, nullptr, progress);
11720
0
}
11721
11722
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11723
                               ContentProviderWithoutLength content_provider,
11724
                               const std::string &content_type,
11725
                               ContentReceiver content_receiver,
11726
0
                               DownloadProgress progress) {
11727
0
  return send_with_content_provider_and_receiver(
11728
0
      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11729
0
      content_type, std::move(content_receiver), std::move(progress));
11730
0
}
11731
11732
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11733
                               const UploadFormDataItems &items,
11734
                               const FormDataProviderItems &provider_items,
11735
0
                               UploadProgress progress) {
11736
0
  const auto &boundary = detail::make_multipart_data_boundary();
11737
0
  const auto &content_type =
11738
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11739
0
  return send_with_content_provider_and_receiver(
11740
0
      "POST", path, headers, nullptr, 0, nullptr,
11741
0
      get_multipart_content_provider(boundary, items, provider_items),
11742
0
      content_type, nullptr, progress);
11743
0
}
11744
11745
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11746
                               const std::string &body,
11747
                               const std::string &content_type,
11748
                               ContentReceiver content_receiver,
11749
0
                               DownloadProgress progress) {
11750
0
  Request req;
11751
0
  req.method = "POST";
11752
0
  req.path = path;
11753
0
  req.headers = headers;
11754
0
  req.body = body;
11755
0
  req.content_receiver =
11756
0
      [content_receiver](const char *data, size_t data_length,
11757
0
                         size_t /*offset*/, size_t /*total_length*/) {
11758
0
        return content_receiver(data, data_length);
11759
0
      };
11760
0
  req.download_progress = std::move(progress);
11761
0
11762
0
  if (max_timeout_msec_ > 0) {
11763
0
    req.start_time_ = std::chrono::steady_clock::now();
11764
0
  }
11765
0
11766
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
11767
0
11768
0
  return send_(std::move(req));
11769
0
}
11770
11771
0
inline Result ClientImpl::Put(const std::string &path) {
11772
0
  return Put(path, std::string(), std::string());
11773
0
}
11774
11775
0
inline Result ClientImpl::Put(const std::string &path, const Headers &headers) {
11776
0
  return Put(path, headers, nullptr, 0, std::string());
11777
0
}
11778
11779
inline Result ClientImpl::Put(const std::string &path, const char *body,
11780
                              size_t content_length,
11781
                              const std::string &content_type,
11782
0
                              UploadProgress progress) {
11783
0
  return Put(path, Headers(), body, content_length, content_type, progress);
11784
0
}
11785
11786
inline Result ClientImpl::Put(const std::string &path, const std::string &body,
11787
                              const std::string &content_type,
11788
0
                              UploadProgress progress) {
11789
0
  return Put(path, Headers(), body, content_type, progress);
11790
0
}
11791
11792
0
inline Result ClientImpl::Put(const std::string &path, const Params &params) {
11793
0
  return Put(path, Headers(), params);
11794
0
}
11795
11796
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
11797
                              ContentProvider content_provider,
11798
                              const std::string &content_type,
11799
0
                              UploadProgress progress) {
11800
0
  return Put(path, Headers(), content_length, std::move(content_provider),
11801
0
             content_type, progress);
11802
0
}
11803
11804
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
11805
                              ContentProvider content_provider,
11806
                              const std::string &content_type,
11807
                              ContentReceiver content_receiver,
11808
0
                              UploadProgress progress) {
11809
0
  return Put(path, Headers(), content_length, std::move(content_provider),
11810
0
             content_type, std::move(content_receiver), progress);
11811
0
}
11812
11813
inline Result ClientImpl::Put(const std::string &path,
11814
                              ContentProviderWithoutLength content_provider,
11815
                              const std::string &content_type,
11816
0
                              UploadProgress progress) {
11817
0
  return Put(path, Headers(), std::move(content_provider), content_type,
11818
0
             progress);
11819
0
}
11820
11821
inline Result ClientImpl::Put(const std::string &path,
11822
                              ContentProviderWithoutLength content_provider,
11823
                              const std::string &content_type,
11824
                              ContentReceiver content_receiver,
11825
0
                              UploadProgress progress) {
11826
0
  return Put(path, Headers(), std::move(content_provider), content_type,
11827
0
             std::move(content_receiver), progress);
11828
0
}
11829
11830
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11831
0
                              const Params &params) {
11832
0
  auto query = detail::params_to_query_str(params);
11833
0
  return Put(path, headers, query, "application/x-www-form-urlencoded");
11834
0
}
11835
11836
inline Result ClientImpl::Put(const std::string &path,
11837
                              const UploadFormDataItems &items,
11838
0
                              UploadProgress progress) {
11839
0
  return Put(path, Headers(), items, progress);
11840
0
}
11841
11842
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11843
                              const UploadFormDataItems &items,
11844
0
                              UploadProgress progress) {
11845
0
  const auto &boundary = detail::make_multipart_data_boundary();
11846
0
  const auto &content_type =
11847
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11848
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11849
0
  return Put(path, headers, body, content_type, progress);
11850
0
}
11851
11852
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11853
                              const UploadFormDataItems &items,
11854
                              const std::string &boundary,
11855
0
                              UploadProgress progress) {
11856
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
11857
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
11858
0
  }
11859
0
11860
0
  const auto &content_type =
11861
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11862
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11863
0
  return Put(path, headers, body, content_type, progress);
11864
0
}
11865
11866
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11867
                              const char *body, size_t content_length,
11868
                              const std::string &content_type,
11869
0
                              UploadProgress progress) {
11870
0
  return send_with_content_provider_and_receiver(
11871
0
      "PUT", path, headers, body, content_length, nullptr, nullptr,
11872
0
      content_type, nullptr, progress);
11873
0
}
11874
11875
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11876
                              const std::string &body,
11877
                              const std::string &content_type,
11878
0
                              UploadProgress progress) {
11879
0
  return send_with_content_provider_and_receiver(
11880
0
      "PUT", path, headers, body.data(), body.size(), nullptr, nullptr,
11881
0
      content_type, nullptr, progress);
11882
0
}
11883
11884
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11885
                              size_t content_length,
11886
                              ContentProvider content_provider,
11887
                              const std::string &content_type,
11888
0
                              UploadProgress progress) {
11889
0
  return send_with_content_provider_and_receiver(
11890
0
      "PUT", path, headers, nullptr, content_length,
11891
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
11892
0
}
11893
11894
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11895
                              size_t content_length,
11896
                              ContentProvider content_provider,
11897
                              const std::string &content_type,
11898
                              ContentReceiver content_receiver,
11899
0
                              UploadProgress progress) {
11900
0
  return send_with_content_provider_and_receiver(
11901
0
      "PUT", path, headers, nullptr, content_length,
11902
0
      std::move(content_provider), nullptr, content_type,
11903
0
      std::move(content_receiver), progress);
11904
0
}
11905
11906
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11907
                              ContentProviderWithoutLength content_provider,
11908
                              const std::string &content_type,
11909
0
                              UploadProgress progress) {
11910
0
  return send_with_content_provider_and_receiver(
11911
0
      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11912
0
      content_type, nullptr, progress);
11913
0
}
11914
11915
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11916
                              ContentProviderWithoutLength content_provider,
11917
                              const std::string &content_type,
11918
                              ContentReceiver content_receiver,
11919
0
                              UploadProgress progress) {
11920
0
  return send_with_content_provider_and_receiver(
11921
0
      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11922
0
      content_type, std::move(content_receiver), progress);
11923
0
}
11924
11925
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11926
                              const UploadFormDataItems &items,
11927
                              const FormDataProviderItems &provider_items,
11928
0
                              UploadProgress progress) {
11929
0
  const auto &boundary = detail::make_multipart_data_boundary();
11930
0
  const auto &content_type =
11931
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11932
0
  return send_with_content_provider_and_receiver(
11933
0
      "PUT", path, headers, nullptr, 0, nullptr,
11934
0
      get_multipart_content_provider(boundary, items, provider_items),
11935
0
      content_type, nullptr, progress);
11936
0
}
11937
11938
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11939
                              const std::string &body,
11940
                              const std::string &content_type,
11941
                              ContentReceiver content_receiver,
11942
0
                              DownloadProgress progress) {
11943
0
  Request req;
11944
0
  req.method = "PUT";
11945
0
  req.path = path;
11946
0
  req.headers = headers;
11947
0
  req.body = body;
11948
0
  req.content_receiver =
11949
0
      [content_receiver](const char *data, size_t data_length,
11950
0
                         size_t /*offset*/, size_t /*total_length*/) {
11951
0
        return content_receiver(data, data_length);
11952
0
      };
11953
0
  req.download_progress = std::move(progress);
11954
0
11955
0
  if (max_timeout_msec_ > 0) {
11956
0
    req.start_time_ = std::chrono::steady_clock::now();
11957
0
  }
11958
0
11959
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
11960
0
11961
0
  return send_(std::move(req));
11962
0
}
11963
11964
0
inline Result ClientImpl::Patch(const std::string &path) {
11965
0
  return Patch(path, std::string(), std::string());
11966
0
}
11967
11968
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11969
0
                                UploadProgress progress) {
11970
0
  return Patch(path, headers, nullptr, 0, std::string(), progress);
11971
0
}
11972
11973
inline Result ClientImpl::Patch(const std::string &path, const char *body,
11974
                                size_t content_length,
11975
                                const std::string &content_type,
11976
0
                                UploadProgress progress) {
11977
0
  return Patch(path, Headers(), body, content_length, content_type, progress);
11978
0
}
11979
11980
inline Result ClientImpl::Patch(const std::string &path,
11981
                                const std::string &body,
11982
                                const std::string &content_type,
11983
0
                                UploadProgress progress) {
11984
0
  return Patch(path, Headers(), body, content_type, progress);
11985
0
}
11986
11987
0
inline Result ClientImpl::Patch(const std::string &path, const Params &params) {
11988
0
  return Patch(path, Headers(), params);
11989
0
}
11990
11991
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
11992
                                ContentProvider content_provider,
11993
                                const std::string &content_type,
11994
0
                                UploadProgress progress) {
11995
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
11996
0
               content_type, progress);
11997
0
}
11998
11999
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
12000
                                ContentProvider content_provider,
12001
                                const std::string &content_type,
12002
                                ContentReceiver content_receiver,
12003
0
                                UploadProgress progress) {
12004
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
12005
0
               content_type, std::move(content_receiver), progress);
12006
0
}
12007
12008
inline Result ClientImpl::Patch(const std::string &path,
12009
                                ContentProviderWithoutLength content_provider,
12010
                                const std::string &content_type,
12011
0
                                UploadProgress progress) {
12012
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
12013
0
               progress);
12014
0
}
12015
12016
inline Result ClientImpl::Patch(const std::string &path,
12017
                                ContentProviderWithoutLength content_provider,
12018
                                const std::string &content_type,
12019
                                ContentReceiver content_receiver,
12020
0
                                UploadProgress progress) {
12021
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
12022
0
               std::move(content_receiver), progress);
12023
0
}
12024
12025
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12026
0
                                const Params &params) {
12027
0
  auto query = detail::params_to_query_str(params);
12028
0
  return Patch(path, headers, query, "application/x-www-form-urlencoded");
12029
0
}
12030
12031
inline Result ClientImpl::Patch(const std::string &path,
12032
                                const UploadFormDataItems &items,
12033
0
                                UploadProgress progress) {
12034
0
  return Patch(path, Headers(), items, progress);
12035
0
}
12036
12037
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12038
                                const UploadFormDataItems &items,
12039
0
                                UploadProgress progress) {
12040
0
  const auto &boundary = detail::make_multipart_data_boundary();
12041
0
  const auto &content_type =
12042
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
12043
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
12044
0
  return Patch(path, headers, body, content_type, progress);
12045
0
}
12046
12047
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12048
                                const UploadFormDataItems &items,
12049
                                const std::string &boundary,
12050
0
                                UploadProgress progress) {
12051
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
12052
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
12053
0
  }
12054
0
12055
0
  const auto &content_type =
12056
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
12057
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
12058
0
  return Patch(path, headers, body, content_type, progress);
12059
0
}
12060
12061
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12062
                                const char *body, size_t content_length,
12063
                                const std::string &content_type,
12064
0
                                UploadProgress progress) {
12065
0
  return send_with_content_provider_and_receiver(
12066
0
      "PATCH", path, headers, body, content_length, nullptr, nullptr,
12067
0
      content_type, nullptr, progress);
12068
0
}
12069
12070
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12071
                                const std::string &body,
12072
                                const std::string &content_type,
12073
0
                                UploadProgress progress) {
12074
0
  return send_with_content_provider_and_receiver(
12075
0
      "PATCH", path, headers, body.data(), body.size(), nullptr, nullptr,
12076
0
      content_type, nullptr, progress);
12077
0
}
12078
12079
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12080
                                size_t content_length,
12081
                                ContentProvider content_provider,
12082
                                const std::string &content_type,
12083
0
                                UploadProgress progress) {
12084
0
  return send_with_content_provider_and_receiver(
12085
0
      "PATCH", path, headers, nullptr, content_length,
12086
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
12087
0
}
12088
12089
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12090
                                size_t content_length,
12091
                                ContentProvider content_provider,
12092
                                const std::string &content_type,
12093
                                ContentReceiver content_receiver,
12094
0
                                UploadProgress progress) {
12095
0
  return send_with_content_provider_and_receiver(
12096
0
      "PATCH", path, headers, nullptr, content_length,
12097
0
      std::move(content_provider), nullptr, content_type,
12098
0
      std::move(content_receiver), progress);
12099
0
}
12100
12101
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12102
                                ContentProviderWithoutLength content_provider,
12103
                                const std::string &content_type,
12104
0
                                UploadProgress progress) {
12105
0
  return send_with_content_provider_and_receiver(
12106
0
      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
12107
0
      content_type, nullptr, progress);
12108
0
}
12109
12110
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12111
                                ContentProviderWithoutLength content_provider,
12112
                                const std::string &content_type,
12113
                                ContentReceiver content_receiver,
12114
0
                                UploadProgress progress) {
12115
0
  return send_with_content_provider_and_receiver(
12116
0
      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
12117
0
      content_type, std::move(content_receiver), progress);
12118
0
}
12119
12120
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12121
                                const UploadFormDataItems &items,
12122
                                const FormDataProviderItems &provider_items,
12123
0
                                UploadProgress progress) {
12124
0
  const auto &boundary = detail::make_multipart_data_boundary();
12125
0
  const auto &content_type =
12126
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
12127
0
  return send_with_content_provider_and_receiver(
12128
0
      "PATCH", path, headers, nullptr, 0, nullptr,
12129
0
      get_multipart_content_provider(boundary, items, provider_items),
12130
0
      content_type, nullptr, progress);
12131
0
}
12132
12133
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
12134
                                const std::string &body,
12135
                                const std::string &content_type,
12136
                                ContentReceiver content_receiver,
12137
0
                                DownloadProgress progress) {
12138
0
  Request req;
12139
0
  req.method = "PATCH";
12140
0
  req.path = path;
12141
0
  req.headers = headers;
12142
0
  req.body = body;
12143
0
  req.content_receiver =
12144
0
      [content_receiver](const char *data, size_t data_length,
12145
0
                         size_t /*offset*/, size_t /*total_length*/) {
12146
0
        return content_receiver(data, data_length);
12147
0
      };
12148
0
  req.download_progress = std::move(progress);
12149
0
12150
0
  if (max_timeout_msec_ > 0) {
12151
0
    req.start_time_ = std::chrono::steady_clock::now();
12152
0
  }
12153
0
12154
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
12155
0
12156
0
  return send_(std::move(req));
12157
0
}
12158
12159
inline Result ClientImpl::Delete(const std::string &path,
12160
0
                                 DownloadProgress progress) {
12161
0
  return Delete(path, Headers(), std::string(), std::string(), progress);
12162
0
}
12163
12164
inline Result ClientImpl::Delete(const std::string &path,
12165
                                 const Headers &headers,
12166
0
                                 DownloadProgress progress) {
12167
0
  return Delete(path, headers, std::string(), std::string(), progress);
12168
0
}
12169
12170
inline Result ClientImpl::Delete(const std::string &path, const char *body,
12171
                                 size_t content_length,
12172
                                 const std::string &content_type,
12173
0
                                 DownloadProgress progress) {
12174
0
  return Delete(path, Headers(), body, content_length, content_type, progress);
12175
0
}
12176
12177
inline Result ClientImpl::Delete(const std::string &path,
12178
                                 const std::string &body,
12179
                                 const std::string &content_type,
12180
0
                                 DownloadProgress progress) {
12181
0
  return Delete(path, Headers(), body.data(), body.size(), content_type,
12182
0
                progress);
12183
0
}
12184
12185
inline Result ClientImpl::Delete(const std::string &path,
12186
                                 const Headers &headers,
12187
                                 const std::string &body,
12188
                                 const std::string &content_type,
12189
0
                                 DownloadProgress progress) {
12190
0
  return Delete(path, headers, body.data(), body.size(), content_type,
12191
0
                progress);
12192
0
}
12193
12194
inline Result ClientImpl::Delete(const std::string &path, const Params &params,
12195
0
                                 DownloadProgress progress) {
12196
0
  return Delete(path, Headers(), params, progress);
12197
0
}
12198
12199
inline Result ClientImpl::Delete(const std::string &path,
12200
                                 const Headers &headers, const Params &params,
12201
0
                                 DownloadProgress progress) {
12202
0
  auto query = detail::params_to_query_str(params);
12203
0
  return Delete(path, headers, query, "application/x-www-form-urlencoded",
12204
0
                progress);
12205
0
}
12206
12207
inline Result ClientImpl::Delete(const std::string &path,
12208
                                 const Headers &headers, const char *body,
12209
                                 size_t content_length,
12210
                                 const std::string &content_type,
12211
0
                                 DownloadProgress progress) {
12212
0
  Request req;
12213
0
  req.method = "DELETE";
12214
0
  req.headers = headers;
12215
0
  req.path = path;
12216
0
  req.download_progress = std::move(progress);
12217
0
  if (max_timeout_msec_ > 0) {
12218
0
    req.start_time_ = std::chrono::steady_clock::now();
12219
0
  }
12220
0
12221
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
12222
0
  req.body.assign(body, content_length);
12223
0
12224
0
  return send_(std::move(req));
12225
0
}
12226
12227
0
inline Result ClientImpl::Options(const std::string &path) {
12228
0
  return Options(path, Headers());
12229
0
}
12230
12231
inline Result ClientImpl::Options(const std::string &path,
12232
0
                                  const Headers &headers) {
12233
0
  Request req;
12234
0
  req.method = "OPTIONS";
12235
0
  req.headers = headers;
12236
0
  req.path = path;
12237
0
  if (max_timeout_msec_ > 0) {
12238
0
    req.start_time_ = std::chrono::steady_clock::now();
12239
0
  }
12240
0
12241
0
  return send_(std::move(req));
12242
0
}
12243
12244
0
inline void ClientImpl::stop() {
12245
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
12246
0
12247
0
  // If there is anything ongoing right now, the ONLY thread-safe thing we can
12248
0
  // do is to shutdown_socket, so that threads using this socket suddenly
12249
0
  // discover they can't read/write any more and error out. Everything else
12250
0
  // (closing the socket, shutting ssl down) is unsafe because these actions
12251
0
  // are not thread-safe.
12252
0
  if (socket_requests_in_flight_ > 0) {
12253
0
    shutdown_socket(socket_);
12254
0
12255
0
    // Aside from that, we set a flag for the socket to be closed when we're
12256
0
    // done.
12257
0
    socket_should_be_closed_when_request_is_done_ = true;
12258
0
    return;
12259
0
  }
12260
0
12261
0
  // Otherwise, still holding the mutex, we can shut everything down ourselves
12262
0
  shutdown_ssl(socket_, true);
12263
0
  shutdown_socket(socket_);
12264
0
  close_socket(socket_);
12265
0
}
12266
12267
0
inline std::string ClientImpl::host() const { return host_; }
12268
12269
0
inline int ClientImpl::port() const { return port_; }
12270
12271
0
inline size_t ClientImpl::is_socket_open() const {
12272
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
12273
0
  return socket_.is_open();
12274
0
}
12275
12276
0
inline socket_t ClientImpl::socket() const { return socket_.sock; }
12277
12278
0
inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
12279
0
  connection_timeout_sec_ = sec;
12280
0
  connection_timeout_usec_ = usec;
12281
0
}
12282
12283
0
inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
12284
0
  read_timeout_sec_ = sec;
12285
0
  read_timeout_usec_ = usec;
12286
0
}
12287
12288
0
inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
12289
0
  write_timeout_sec_ = sec;
12290
0
  write_timeout_usec_ = usec;
12291
0
}
12292
12293
0
inline void ClientImpl::set_max_timeout(time_t msec) {
12294
0
  max_timeout_msec_ = msec;
12295
0
}
12296
12297
inline void ClientImpl::set_basic_auth(const std::string &username,
12298
0
                                       const std::string &password) {
12299
0
  basic_auth_username_ = username;
12300
0
  basic_auth_password_ = password;
12301
0
}
12302
12303
0
inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
12304
0
  bearer_token_auth_token_ = token;
12305
0
}
12306
12307
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12308
inline void ClientImpl::set_digest_auth(const std::string &username,
12309
                                        const std::string &password) {
12310
  digest_auth_username_ = username;
12311
  digest_auth_password_ = password;
12312
}
12313
#endif
12314
12315
0
inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
12316
12317
0
inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
12318
12319
0
inline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
12320
12321
inline void
12322
0
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
12323
0
  addr_map_ = std::move(addr_map);
12324
0
}
12325
12326
0
inline void ClientImpl::set_default_headers(Headers headers) {
12327
0
  default_headers_ = std::move(headers);
12328
0
}
12329
12330
inline void ClientImpl::set_header_writer(
12331
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
12332
0
  header_writer_ = writer;
12333
0
}
12334
12335
0
inline void ClientImpl::set_address_family(int family) {
12336
0
  address_family_ = family;
12337
0
}
12338
12339
0
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
12340
12341
0
inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
12342
12343
0
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
12344
0
  socket_options_ = std::move(socket_options);
12345
0
}
12346
12347
0
inline void ClientImpl::set_compress(bool on) { compress_ = on; }
12348
12349
0
inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
12350
12351
0
inline void ClientImpl::set_interface(const std::string &intf) {
12352
0
  interface_ = intf;
12353
0
}
12354
12355
0
inline void ClientImpl::set_proxy(const std::string &host, int port) {
12356
0
  proxy_host_ = host;
12357
0
  proxy_port_ = port;
12358
0
}
12359
12360
inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
12361
0
                                             const std::string &password) {
12362
0
  proxy_basic_auth_username_ = username;
12363
0
  proxy_basic_auth_password_ = password;
12364
0
}
12365
12366
0
inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
12367
0
  proxy_bearer_token_auth_token_ = token;
12368
0
}
12369
12370
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12371
inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
12372
                                              const std::string &password) {
12373
  proxy_digest_auth_username_ = username;
12374
  proxy_digest_auth_password_ = password;
12375
}
12376
12377
inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
12378
                                         const std::string &ca_cert_dir_path) {
12379
  ca_cert_file_path_ = ca_cert_file_path;
12380
  ca_cert_dir_path_ = ca_cert_dir_path;
12381
}
12382
12383
inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
12384
  if (ca_cert_store && ca_cert_store != ca_cert_store_) {
12385
    ca_cert_store_ = ca_cert_store;
12386
  }
12387
}
12388
12389
inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
12390
                                                    std::size_t size) const {
12391
  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
12392
  auto se = detail::scope_exit([&] { BIO_free_all(mem); });
12393
  if (!mem) { return nullptr; }
12394
12395
  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
12396
  if (!inf) { return nullptr; }
12397
12398
  auto cts = X509_STORE_new();
12399
  if (cts) {
12400
    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
12401
      auto itmp = sk_X509_INFO_value(inf, i);
12402
      if (!itmp) { continue; }
12403
12404
      if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
12405
      if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
12406
    }
12407
  }
12408
12409
  sk_X509_INFO_pop_free(inf, X509_INFO_free);
12410
  return cts;
12411
}
12412
12413
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
12414
  server_certificate_verification_ = enabled;
12415
}
12416
12417
inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
12418
  server_hostname_verification_ = enabled;
12419
}
12420
12421
inline void ClientImpl::set_server_certificate_verifier(
12422
    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
12423
  server_certificate_verifier_ = verifier;
12424
}
12425
#endif
12426
12427
0
inline void ClientImpl::set_logger(Logger logger) {
12428
0
  logger_ = std::move(logger);
12429
0
}
12430
12431
0
inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
12432
0
  error_logger_ = std::move(error_logger);
12433
0
}
12434
12435
/*
12436
 * SSL Implementation
12437
 */
12438
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12439
namespace detail {
12440
12441
inline bool is_ip_address(const std::string &host) {
12442
  struct in_addr addr4;
12443
  struct in6_addr addr6;
12444
  return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
12445
         inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
12446
}
12447
12448
template <typename U, typename V>
12449
inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
12450
                    U SSL_connect_or_accept, V setup) {
12451
  SSL *ssl = nullptr;
12452
  {
12453
    std::lock_guard<std::mutex> guard(ctx_mutex);
12454
    ssl = SSL_new(ctx);
12455
  }
12456
12457
  if (ssl) {
12458
    set_nonblocking(sock, true);
12459
    auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
12460
    BIO_set_nbio(bio, 1);
12461
    SSL_set_bio(ssl, bio, bio);
12462
12463
    if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
12464
      SSL_shutdown(ssl);
12465
      {
12466
        std::lock_guard<std::mutex> guard(ctx_mutex);
12467
        SSL_free(ssl);
12468
      }
12469
      set_nonblocking(sock, false);
12470
      return nullptr;
12471
    }
12472
    BIO_set_nbio(bio, 0);
12473
    set_nonblocking(sock, false);
12474
  }
12475
12476
  return ssl;
12477
}
12478
12479
inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
12480
                       bool shutdown_gracefully) {
12481
  // sometimes we may want to skip this to try to avoid SIGPIPE if we know
12482
  // the remote has closed the network connection
12483
  // Note that it is not always possible to avoid SIGPIPE, this is merely a
12484
  // best-efforts.
12485
  if (shutdown_gracefully) {
12486
    (void)(sock);
12487
    // SSL_shutdown() returns 0 on first call (indicating close_notify alert
12488
    // sent) and 1 on subsequent call (indicating close_notify alert received)
12489
    if (SSL_shutdown(ssl) == 0) {
12490
      // Expected to return 1, but even if it doesn't, we free ssl
12491
      SSL_shutdown(ssl);
12492
    }
12493
  }
12494
12495
  std::lock_guard<std::mutex> guard(ctx_mutex);
12496
  SSL_free(ssl);
12497
}
12498
12499
template <typename U>
12500
bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
12501
                                       U ssl_connect_or_accept,
12502
                                       time_t timeout_sec, time_t timeout_usec,
12503
                                       int *ssl_error) {
12504
  auto res = 0;
12505
  while ((res = ssl_connect_or_accept(ssl)) != 1) {
12506
    auto err = SSL_get_error(ssl, res);
12507
    switch (err) {
12508
    case SSL_ERROR_WANT_READ:
12509
      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
12510
      break;
12511
    case SSL_ERROR_WANT_WRITE:
12512
      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
12513
      break;
12514
    default: break;
12515
    }
12516
    if (ssl_error) { *ssl_error = err; }
12517
    return false;
12518
  }
12519
  return true;
12520
}
12521
12522
template <typename T>
12523
inline bool process_server_socket_ssl(
12524
    const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
12525
    size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
12526
    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
12527
    time_t write_timeout_usec, T callback) {
12528
  return process_server_socket_core(
12529
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
12530
      [&](bool close_connection, bool &connection_closed) {
12531
        SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
12532
                             write_timeout_sec, write_timeout_usec);
12533
        return callback(strm, close_connection, connection_closed);
12534
      });
12535
}
12536
12537
template <typename T>
12538
inline bool process_client_socket_ssl(
12539
    SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
12540
    time_t write_timeout_sec, time_t write_timeout_usec,
12541
    time_t max_timeout_msec,
12542
    std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
12543
  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
12544
                       write_timeout_sec, write_timeout_usec, max_timeout_msec,
12545
                       start_time);
12546
  return callback(strm);
12547
}
12548
12549
// SSL socket stream implementation
12550
inline SSLSocketStream::SSLSocketStream(
12551
    socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
12552
    time_t write_timeout_sec, time_t write_timeout_usec,
12553
    time_t max_timeout_msec,
12554
    std::chrono::time_point<std::chrono::steady_clock> start_time)
12555
    : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
12556
      read_timeout_usec_(read_timeout_usec),
12557
      write_timeout_sec_(write_timeout_sec),
12558
      write_timeout_usec_(write_timeout_usec),
12559
      max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
12560
  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
12561
}
12562
12563
inline SSLSocketStream::~SSLSocketStream() = default;
12564
12565
inline bool SSLSocketStream::is_readable() const {
12566
  return SSL_pending(ssl_) > 0;
12567
}
12568
12569
inline bool SSLSocketStream::wait_readable() const {
12570
  if (max_timeout_msec_ <= 0) {
12571
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
12572
  }
12573
12574
  time_t read_timeout_sec;
12575
  time_t read_timeout_usec;
12576
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
12577
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
12578
12579
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
12580
}
12581
12582
inline bool SSLSocketStream::wait_writable() const {
12583
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
12584
         is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
12585
}
12586
12587
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
12588
  if (SSL_pending(ssl_) > 0) {
12589
    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
12590
    if (ret == 0) { error_ = Error::ConnectionClosed; }
12591
    return ret;
12592
  } else if (wait_readable()) {
12593
    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
12594
    if (ret < 0) {
12595
      auto err = SSL_get_error(ssl_, ret);
12596
      auto n = 1000;
12597
#ifdef _WIN32
12598
      while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
12599
                          (err == SSL_ERROR_SYSCALL &&
12600
                           WSAGetLastError() == WSAETIMEDOUT))) {
12601
#else
12602
      while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
12603
#endif
12604
        if (SSL_pending(ssl_) > 0) {
12605
          return SSL_read(ssl_, ptr, static_cast<int>(size));
12606
        } else if (wait_readable()) {
12607
          std::this_thread::sleep_for(std::chrono::microseconds{10});
12608
          ret = SSL_read(ssl_, ptr, static_cast<int>(size));
12609
          if (ret >= 0) { return ret; }
12610
          err = SSL_get_error(ssl_, ret);
12611
        } else {
12612
          break;
12613
        }
12614
      }
12615
      assert(ret < 0);
12616
    } else if (ret == 0) {
12617
      error_ = Error::ConnectionClosed;
12618
    }
12619
    return ret;
12620
  } else {
12621
    error_ = Error::Timeout;
12622
    return -1;
12623
  }
12624
}
12625
12626
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
12627
  if (wait_writable()) {
12628
    auto handle_size = static_cast<int>(
12629
        std::min<size_t>(size, (std::numeric_limits<int>::max)()));
12630
12631
    auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
12632
    if (ret < 0) {
12633
      auto err = SSL_get_error(ssl_, ret);
12634
      auto n = 1000;
12635
#ifdef _WIN32
12636
      while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
12637
                          (err == SSL_ERROR_SYSCALL &&
12638
                           WSAGetLastError() == WSAETIMEDOUT))) {
12639
#else
12640
      while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
12641
#endif
12642
        if (wait_writable()) {
12643
          std::this_thread::sleep_for(std::chrono::microseconds{10});
12644
          ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
12645
          if (ret >= 0) { return ret; }
12646
          err = SSL_get_error(ssl_, ret);
12647
        } else {
12648
          break;
12649
        }
12650
      }
12651
      assert(ret < 0);
12652
    }
12653
    return ret;
12654
  }
12655
  return -1;
12656
}
12657
12658
inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
12659
                                                    int &port) const {
12660
  detail::get_remote_ip_and_port(sock_, ip, port);
12661
}
12662
12663
inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
12664
                                                   int &port) const {
12665
  detail::get_local_ip_and_port(sock_, ip, port);
12666
}
12667
12668
inline socket_t SSLSocketStream::socket() const { return sock_; }
12669
12670
inline time_t SSLSocketStream::duration() const {
12671
  return std::chrono::duration_cast<std::chrono::milliseconds>(
12672
             std::chrono::steady_clock::now() - start_time_)
12673
      .count();
12674
}
12675
12676
} // namespace detail
12677
12678
// SSL HTTP server implementation
12679
inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
12680
                            const char *client_ca_cert_file_path,
12681
                            const char *client_ca_cert_dir_path,
12682
                            const char *private_key_password) {
12683
  ctx_ = SSL_CTX_new(TLS_server_method());
12684
12685
  if (ctx_) {
12686
    SSL_CTX_set_options(ctx_,
12687
                        SSL_OP_NO_COMPRESSION |
12688
                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
12689
12690
    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
12691
12692
    if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
12693
      SSL_CTX_set_default_passwd_cb_userdata(
12694
          ctx_,
12695
          reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
12696
    }
12697
12698
    if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
12699
        SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
12700
            1 ||
12701
        SSL_CTX_check_private_key(ctx_) != 1) {
12702
      last_ssl_error_ = static_cast<int>(ERR_get_error());
12703
      SSL_CTX_free(ctx_);
12704
      ctx_ = nullptr;
12705
    } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
12706
      SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
12707
                                    client_ca_cert_dir_path);
12708
12709
      // Set client CA list to be sent to clients during TLS handshake
12710
      if (client_ca_cert_file_path) {
12711
        auto ca_list = SSL_load_client_CA_file(client_ca_cert_file_path);
12712
        if (ca_list != nullptr) {
12713
          SSL_CTX_set_client_CA_list(ctx_, ca_list);
12714
        } else {
12715
          // Failed to load client CA list, but we continue since
12716
          // SSL_CTX_load_verify_locations already succeeded and
12717
          // certificate verification will still work
12718
          last_ssl_error_ = static_cast<int>(ERR_get_error());
12719
        }
12720
      }
12721
12722
      SSL_CTX_set_verify(
12723
          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
12724
    }
12725
  }
12726
}
12727
12728
inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
12729
                            X509_STORE *client_ca_cert_store) {
12730
  ctx_ = SSL_CTX_new(TLS_server_method());
12731
12732
  if (ctx_) {
12733
    SSL_CTX_set_options(ctx_,
12734
                        SSL_OP_NO_COMPRESSION |
12735
                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
12736
12737
    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
12738
12739
    if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
12740
        SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
12741
      SSL_CTX_free(ctx_);
12742
      ctx_ = nullptr;
12743
    } else if (client_ca_cert_store) {
12744
      SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
12745
12746
      // Extract CA names from the store and set them as the client CA list
12747
      auto ca_list = extract_ca_names_from_x509_store(client_ca_cert_store);
12748
      if (ca_list) {
12749
        SSL_CTX_set_client_CA_list(ctx_, ca_list);
12750
      } else {
12751
        // Failed to extract CA names, record the error
12752
        last_ssl_error_ = static_cast<int>(ERR_get_error());
12753
      }
12754
12755
      SSL_CTX_set_verify(
12756
          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
12757
    }
12758
  }
12759
}
12760
12761
inline SSLServer::SSLServer(
12762
    const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
12763
  ctx_ = SSL_CTX_new(TLS_method());
12764
  if (ctx_) {
12765
    if (!setup_ssl_ctx_callback(*ctx_)) {
12766
      SSL_CTX_free(ctx_);
12767
      ctx_ = nullptr;
12768
    }
12769
  }
12770
}
12771
12772
inline SSLServer::~SSLServer() {
12773
  if (ctx_) { SSL_CTX_free(ctx_); }
12774
}
12775
12776
inline bool SSLServer::is_valid() const { return ctx_; }
12777
12778
inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
12779
12780
inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
12781
                                    X509_STORE *client_ca_cert_store) {
12782
12783
  std::lock_guard<std::mutex> guard(ctx_mutex_);
12784
12785
  SSL_CTX_use_certificate(ctx_, cert);
12786
  SSL_CTX_use_PrivateKey(ctx_, private_key);
12787
12788
  if (client_ca_cert_store != nullptr) {
12789
    SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
12790
  }
12791
}
12792
12793
inline bool SSLServer::process_and_close_socket(socket_t sock) {
12794
  auto ssl = detail::ssl_new(
12795
      sock, ctx_, ctx_mutex_,
12796
      [&](SSL *ssl2) {
12797
        return detail::ssl_connect_or_accept_nonblocking(
12798
            sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
12799
            &last_ssl_error_);
12800
      },
12801
      [](SSL * /*ssl2*/) { return true; });
12802
12803
  auto ret = false;
12804
  if (ssl) {
12805
    std::string remote_addr;
12806
    int remote_port = 0;
12807
    detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
12808
12809
    std::string local_addr;
12810
    int local_port = 0;
12811
    detail::get_local_ip_and_port(sock, local_addr, local_port);
12812
12813
    ret = detail::process_server_socket_ssl(
12814
        svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
12815
        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12816
        write_timeout_usec_,
12817
        [&](Stream &strm, bool close_connection, bool &connection_closed) {
12818
          return process_request(strm, remote_addr, remote_port, local_addr,
12819
                                 local_port, close_connection,
12820
                                 connection_closed,
12821
                                 [&](Request &req) { req.ssl = ssl; });
12822
        });
12823
12824
    // Shutdown gracefully if the result seemed successful, non-gracefully if
12825
    // the connection appeared to be closed.
12826
    const bool shutdown_gracefully = ret;
12827
    detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
12828
  }
12829
12830
  detail::shutdown_socket(sock);
12831
  detail::close_socket(sock);
12832
  return ret;
12833
}
12834
12835
inline STACK_OF(X509_NAME) * SSLServer::extract_ca_names_from_x509_store(
12836
                                 X509_STORE *store) {
12837
  if (!store) { return nullptr; }
12838
12839
  auto ca_list = sk_X509_NAME_new_null();
12840
  if (!ca_list) { return nullptr; }
12841
12842
  // Get all objects from the store
12843
  auto objs = X509_STORE_get0_objects(store);
12844
  if (!objs) {
12845
    sk_X509_NAME_free(ca_list);
12846
    return nullptr;
12847
  }
12848
12849
  // Iterate through objects and extract certificate subject names
12850
  for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) {
12851
    auto obj = sk_X509_OBJECT_value(objs, i);
12852
    if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
12853
      auto cert = X509_OBJECT_get0_X509(obj);
12854
      if (cert) {
12855
        auto subject = X509_get_subject_name(cert);
12856
        if (subject) {
12857
          auto name_dup = X509_NAME_dup(subject);
12858
          if (name_dup) { sk_X509_NAME_push(ca_list, name_dup); }
12859
        }
12860
      }
12861
    }
12862
  }
12863
12864
  // If no names were extracted, free the list and return nullptr
12865
  if (sk_X509_NAME_num(ca_list) == 0) {
12866
    sk_X509_NAME_free(ca_list);
12867
    return nullptr;
12868
  }
12869
12870
  return ca_list;
12871
}
12872
12873
// SSL HTTP client implementation
12874
inline SSLClient::SSLClient(const std::string &host)
12875
    : SSLClient(host, 443, std::string(), std::string()) {}
12876
12877
inline SSLClient::SSLClient(const std::string &host, int port)
12878
    : SSLClient(host, port, std::string(), std::string()) {}
12879
12880
inline SSLClient::SSLClient(const std::string &host, int port,
12881
                            const std::string &client_cert_path,
12882
                            const std::string &client_key_path,
12883
                            const std::string &private_key_password)
12884
    : ClientImpl(host, port, client_cert_path, client_key_path) {
12885
  ctx_ = SSL_CTX_new(TLS_client_method());
12886
12887
  SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
12888
12889
  detail::split(&host_[0], &host_[host_.size()], '.',
12890
                [&](const char *b, const char *e) {
12891
                  host_components_.emplace_back(b, e);
12892
                });
12893
12894
  if (!client_cert_path.empty() && !client_key_path.empty()) {
12895
    if (!private_key_password.empty()) {
12896
      SSL_CTX_set_default_passwd_cb_userdata(
12897
          ctx_, reinterpret_cast<void *>(
12898
                    const_cast<char *>(private_key_password.c_str())));
12899
    }
12900
12901
    if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
12902
                                     SSL_FILETYPE_PEM) != 1 ||
12903
        SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
12904
                                    SSL_FILETYPE_PEM) != 1) {
12905
      last_openssl_error_ = ERR_get_error();
12906
      SSL_CTX_free(ctx_);
12907
      ctx_ = nullptr;
12908
    }
12909
  }
12910
}
12911
12912
inline SSLClient::SSLClient(const std::string &host, int port,
12913
                            X509 *client_cert, EVP_PKEY *client_key,
12914
                            const std::string &private_key_password)
12915
    : ClientImpl(host, port) {
12916
  ctx_ = SSL_CTX_new(TLS_client_method());
12917
12918
  detail::split(&host_[0], &host_[host_.size()], '.',
12919
                [&](const char *b, const char *e) {
12920
                  host_components_.emplace_back(b, e);
12921
                });
12922
12923
  if (client_cert != nullptr && client_key != nullptr) {
12924
    if (!private_key_password.empty()) {
12925
      SSL_CTX_set_default_passwd_cb_userdata(
12926
          ctx_, reinterpret_cast<void *>(
12927
                    const_cast<char *>(private_key_password.c_str())));
12928
    }
12929
12930
    if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
12931
        SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
12932
      last_openssl_error_ = ERR_get_error();
12933
      SSL_CTX_free(ctx_);
12934
      ctx_ = nullptr;
12935
    }
12936
  }
12937
}
12938
12939
inline SSLClient::~SSLClient() {
12940
  if (ctx_) { SSL_CTX_free(ctx_); }
12941
  // Make sure to shut down SSL since shutdown_ssl will resolve to the
12942
  // base function rather than the derived function once we get to the
12943
  // base class destructor, and won't free the SSL (causing a leak).
12944
  shutdown_ssl_impl(socket_, true);
12945
}
12946
12947
inline bool SSLClient::is_valid() const { return ctx_; }
12948
12949
inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
12950
  if (ca_cert_store) {
12951
    if (ctx_) {
12952
      if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
12953
        // Free memory allocated for old cert and use new store
12954
        // `ca_cert_store`
12955
        SSL_CTX_set_cert_store(ctx_, ca_cert_store);
12956
        ca_cert_store_ = ca_cert_store;
12957
      }
12958
    } else {
12959
      X509_STORE_free(ca_cert_store);
12960
    }
12961
  }
12962
}
12963
12964
inline void SSLClient::load_ca_cert_store(const char *ca_cert,
12965
                                          std::size_t size) {
12966
  set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
12967
}
12968
12969
inline long SSLClient::get_openssl_verify_result() const {
12970
  return verify_result_;
12971
}
12972
12973
inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
12974
12975
inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
12976
  if (!is_valid()) {
12977
    error = Error::SSLConnection;
12978
    return false;
12979
  }
12980
  return ClientImpl::create_and_connect_socket(socket, error);
12981
}
12982
12983
// Assumes that socket_mutex_ is locked and that there are no requests in
12984
// flight
12985
inline bool SSLClient::connect_with_proxy(
12986
    Socket &socket,
12987
    std::chrono::time_point<std::chrono::steady_clock> start_time,
12988
    Response &res, bool &success, Error &error) {
12989
  success = true;
12990
  Response proxy_res;
12991
  if (!detail::process_client_socket(
12992
          socket.sock, read_timeout_sec_, read_timeout_usec_,
12993
          write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
12994
          start_time, [&](Stream &strm) {
12995
            Request req2;
12996
            req2.method = "CONNECT";
12997
            req2.path =
12998
                detail::make_host_and_port_string_always_port(host_, port_);
12999
            if (max_timeout_msec_ > 0) {
13000
              req2.start_time_ = std::chrono::steady_clock::now();
13001
            }
13002
            return process_request(strm, req2, proxy_res, false, error);
13003
          })) {
13004
    // Thread-safe to close everything because we are assuming there are no
13005
    // requests in flight
13006
    shutdown_ssl(socket, true);
13007
    shutdown_socket(socket);
13008
    close_socket(socket);
13009
    success = false;
13010
    return false;
13011
  }
13012
13013
  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
13014
    if (!proxy_digest_auth_username_.empty() &&
13015
        !proxy_digest_auth_password_.empty()) {
13016
      std::map<std::string, std::string> auth;
13017
      if (detail::parse_www_authenticate(proxy_res, auth, true)) {
13018
        // Close the current socket and create a new one for the authenticated
13019
        // request
13020
        shutdown_ssl(socket, true);
13021
        shutdown_socket(socket);
13022
        close_socket(socket);
13023
13024
        // Create a new socket for the authenticated CONNECT request
13025
        if (!ensure_socket_connection(socket, error)) {
13026
          success = false;
13027
          output_error_log(error, nullptr);
13028
          return false;
13029
        }
13030
13031
        proxy_res = Response();
13032
        if (!detail::process_client_socket(
13033
                socket.sock, read_timeout_sec_, read_timeout_usec_,
13034
                write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
13035
                start_time, [&](Stream &strm) {
13036
                  Request req3;
13037
                  req3.method = "CONNECT";
13038
                  req3.path = detail::make_host_and_port_string_always_port(
13039
                      host_, port_);
13040
                  req3.headers.insert(detail::make_digest_authentication_header(
13041
                      req3, auth, 1, detail::random_string(10),
13042
                      proxy_digest_auth_username_, proxy_digest_auth_password_,
13043
                      true));
13044
                  if (max_timeout_msec_ > 0) {
13045
                    req3.start_time_ = std::chrono::steady_clock::now();
13046
                  }
13047
                  return process_request(strm, req3, proxy_res, false, error);
13048
                })) {
13049
          // Thread-safe to close everything because we are assuming there are
13050
          // no requests in flight
13051
          shutdown_ssl(socket, true);
13052
          shutdown_socket(socket);
13053
          close_socket(socket);
13054
          success = false;
13055
          return false;
13056
        }
13057
      }
13058
    }
13059
  }
13060
13061
  // If status code is not 200, proxy request is failed.
13062
  // Set error to ProxyConnection and return proxy response
13063
  // as the response of the request
13064
  if (proxy_res.status != StatusCode::OK_200) {
13065
    error = Error::ProxyConnection;
13066
    output_error_log(error, nullptr);
13067
    res = std::move(proxy_res);
13068
    // Thread-safe to close everything because we are assuming there are
13069
    // no requests in flight
13070
    shutdown_ssl(socket, true);
13071
    shutdown_socket(socket);
13072
    close_socket(socket);
13073
    return false;
13074
  }
13075
13076
  return true;
13077
}
13078
13079
inline bool SSLClient::load_certs() {
13080
  auto ret = true;
13081
13082
  std::call_once(initialize_cert_, [&]() {
13083
    std::lock_guard<std::mutex> guard(ctx_mutex_);
13084
    if (!ca_cert_file_path_.empty()) {
13085
      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
13086
                                         nullptr)) {
13087
        last_openssl_error_ = ERR_get_error();
13088
        ret = false;
13089
      }
13090
    } else if (!ca_cert_dir_path_.empty()) {
13091
      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
13092
                                         ca_cert_dir_path_.c_str())) {
13093
        last_openssl_error_ = ERR_get_error();
13094
        ret = false;
13095
      }
13096
    } else {
13097
      auto loaded = false;
13098
#ifdef _WIN32
13099
      loaded =
13100
          detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
13101
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
13102
      loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
13103
#endif // _WIN32
13104
      if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
13105
    }
13106
  });
13107
13108
  return ret;
13109
}
13110
13111
inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
13112
  auto ssl = detail::ssl_new(
13113
      socket.sock, ctx_, ctx_mutex_,
13114
      [&](SSL *ssl2) {
13115
        if (server_certificate_verification_) {
13116
          if (!load_certs()) {
13117
            error = Error::SSLLoadingCerts;
13118
            output_error_log(error, nullptr);
13119
            return false;
13120
          }
13121
          SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
13122
        }
13123
13124
        if (!detail::ssl_connect_or_accept_nonblocking(
13125
                socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
13126
                connection_timeout_usec_, &last_ssl_error_)) {
13127
          error = Error::SSLConnection;
13128
          output_error_log(error, nullptr);
13129
          return false;
13130
        }
13131
13132
        if (server_certificate_verification_) {
13133
          auto verification_status = SSLVerifierResponse::NoDecisionMade;
13134
13135
          if (server_certificate_verifier_) {
13136
            verification_status = server_certificate_verifier_(ssl2);
13137
          }
13138
13139
          if (verification_status == SSLVerifierResponse::CertificateRejected) {
13140
            last_openssl_error_ = ERR_get_error();
13141
            error = Error::SSLServerVerification;
13142
            output_error_log(error, nullptr);
13143
            return false;
13144
          }
13145
13146
          if (verification_status == SSLVerifierResponse::NoDecisionMade) {
13147
            verify_result_ = SSL_get_verify_result(ssl2);
13148
13149
            if (verify_result_ != X509_V_OK) {
13150
              last_openssl_error_ = static_cast<unsigned long>(verify_result_);
13151
              error = Error::SSLServerVerification;
13152
              output_error_log(error, nullptr);
13153
              return false;
13154
            }
13155
13156
            auto server_cert = SSL_get1_peer_certificate(ssl2);
13157
            auto se = detail::scope_exit([&] { X509_free(server_cert); });
13158
13159
            if (server_cert == nullptr) {
13160
              last_openssl_error_ = ERR_get_error();
13161
              error = Error::SSLServerVerification;
13162
              output_error_log(error, nullptr);
13163
              return false;
13164
            }
13165
13166
            if (server_hostname_verification_) {
13167
              if (!verify_host(server_cert)) {
13168
                last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
13169
                error = Error::SSLServerHostnameVerification;
13170
                output_error_log(error, nullptr);
13171
                return false;
13172
              }
13173
            }
13174
          }
13175
        }
13176
13177
        return true;
13178
      },
13179
      [&](SSL *ssl2) {
13180
        // Set SNI only if host is not IP address
13181
        if (!detail::is_ip_address(host_)) {
13182
#if defined(OPENSSL_IS_BORINGSSL)
13183
          SSL_set_tlsext_host_name(ssl2, host_.c_str());
13184
#else
13185
          // NOTE: Direct call instead of using the OpenSSL macro to suppress
13186
          // -Wold-style-cast warning
13187
          SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,
13188
                   TLSEXT_NAMETYPE_host_name,
13189
                   static_cast<void *>(const_cast<char *>(host_.c_str())));
13190
#endif
13191
        }
13192
        return true;
13193
      });
13194
13195
  if (ssl) {
13196
    socket.ssl = ssl;
13197
    return true;
13198
  }
13199
13200
  if (ctx_ == nullptr) {
13201
    error = Error::SSLConnection;
13202
    last_openssl_error_ = ERR_get_error();
13203
  }
13204
13205
  shutdown_socket(socket);
13206
  close_socket(socket);
13207
  return false;
13208
}
13209
13210
inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
13211
  shutdown_ssl_impl(socket, shutdown_gracefully);
13212
}
13213
13214
inline void SSLClient::shutdown_ssl_impl(Socket &socket,
13215
                                         bool shutdown_gracefully) {
13216
  if (socket.sock == INVALID_SOCKET) {
13217
    assert(socket.ssl == nullptr);
13218
    return;
13219
  }
13220
  if (socket.ssl) {
13221
    detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
13222
                       shutdown_gracefully);
13223
    socket.ssl = nullptr;
13224
  }
13225
  assert(socket.ssl == nullptr);
13226
}
13227
13228
inline bool SSLClient::process_socket(
13229
    const Socket &socket,
13230
    std::chrono::time_point<std::chrono::steady_clock> start_time,
13231
    std::function<bool(Stream &strm)> callback) {
13232
  assert(socket.ssl);
13233
  return detail::process_client_socket_ssl(
13234
      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
13235
      write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
13236
      std::move(callback));
13237
}
13238
13239
inline bool SSLClient::is_ssl() const { return true; }
13240
13241
inline bool SSLClient::verify_host(X509 *server_cert) const {
13242
  /* Quote from RFC2818 section 3.1 "Server Identity"
13243
13244
     If a subjectAltName extension of type dNSName is present, that MUST
13245
     be used as the identity. Otherwise, the (most specific) Common Name
13246
     field in the Subject field of the certificate MUST be used. Although
13247
     the use of the Common Name is existing practice, it is deprecated and
13248
     Certification Authorities are encouraged to use the dNSName instead.
13249
13250
     Matching is performed using the matching rules specified by
13251
     [RFC2459].  If more than one identity of a given type is present in
13252
     the certificate (e.g., more than one dNSName name, a match in any one
13253
     of the set is considered acceptable.) Names may contain the wildcard
13254
     character * which is considered to match any single domain name
13255
     component or component fragment. E.g., *.a.com matches foo.a.com but
13256
     not bar.foo.a.com. f*.com matches foo.com but not bar.com.
13257
13258
     In some cases, the URI is specified as an IP address rather than a
13259
     hostname. In this case, the iPAddress subjectAltName must be present
13260
     in the certificate and must exactly match the IP in the URI.
13261
13262
  */
13263
  return verify_host_with_subject_alt_name(server_cert) ||
13264
         verify_host_with_common_name(server_cert);
13265
}
13266
13267
inline bool
13268
SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
13269
  auto ret = false;
13270
13271
  auto type = GEN_DNS;
13272
13273
  struct in6_addr addr6 = {};
13274
  struct in_addr addr = {};
13275
  size_t addr_len = 0;
13276
13277
#ifndef __MINGW32__
13278
  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
13279
    type = GEN_IPADD;
13280
    addr_len = sizeof(struct in6_addr);
13281
  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
13282
    type = GEN_IPADD;
13283
    addr_len = sizeof(struct in_addr);
13284
  }
13285
#endif
13286
13287
  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
13288
      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
13289
13290
  if (alt_names) {
13291
    auto dsn_matched = false;
13292
    auto ip_matched = false;
13293
13294
    auto count = sk_GENERAL_NAME_num(alt_names);
13295
13296
    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
13297
      auto val = sk_GENERAL_NAME_value(alt_names, i);
13298
      if (!val || val->type != type) { continue; }
13299
13300
      auto name =
13301
          reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
13302
      if (name == nullptr) { continue; }
13303
13304
      auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
13305
13306
      switch (type) {
13307
      case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
13308
13309
      case GEN_IPADD:
13310
        if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
13311
          ip_matched = true;
13312
        }
13313
        break;
13314
      }
13315
    }
13316
13317
    if (dsn_matched || ip_matched) { ret = true; }
13318
  }
13319
13320
  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
13321
      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
13322
  return ret;
13323
}
13324
13325
inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
13326
  const auto subject_name = X509_get_subject_name(server_cert);
13327
13328
  if (subject_name != nullptr) {
13329
    char name[BUFSIZ];
13330
    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
13331
                                              name, sizeof(name));
13332
13333
    if (name_len != -1) {
13334
      return check_host_name(name, static_cast<size_t>(name_len));
13335
    }
13336
  }
13337
13338
  return false;
13339
}
13340
13341
inline bool SSLClient::check_host_name(const char *pattern,
13342
                                       size_t pattern_len) const {
13343
  if (host_.size() == pattern_len && host_ == pattern) { return true; }
13344
13345
  // Wildcard match
13346
  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
13347
  std::vector<std::string> pattern_components;
13348
  detail::split(&pattern[0], &pattern[pattern_len], '.',
13349
                [&](const char *b, const char *e) {
13350
                  pattern_components.emplace_back(b, e);
13351
                });
13352
13353
  if (host_components_.size() != pattern_components.size()) { return false; }
13354
13355
  auto itr = pattern_components.begin();
13356
  for (const auto &h : host_components_) {
13357
    auto &p = *itr;
13358
    if (p != h && p != "*") {
13359
      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
13360
                            !p.compare(0, p.size() - 1, h));
13361
      if (!partial_match) { return false; }
13362
    }
13363
    ++itr;
13364
  }
13365
13366
  return true;
13367
}
13368
#endif
13369
13370
// Universal client implementation
13371
inline Client::Client(const std::string &scheme_host_port)
13372
    : Client(scheme_host_port, std::string(), std::string()) {}
13373
13374
inline Client::Client(const std::string &scheme_host_port,
13375
                      const std::string &client_cert_path,
13376
                      const std::string &client_key_path) {
13377
  const static std::regex re(
13378
      R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
13379
13380
  std::smatch m;
13381
  if (std::regex_match(scheme_host_port, m, re)) {
13382
    auto scheme = m[1].str();
13383
13384
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13385
    if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
13386
#else
13387
    if (!scheme.empty() && scheme != "http") {
13388
#endif
13389
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
13390
      std::string msg = "'" + scheme + "' scheme is not supported.";
13391
      throw std::invalid_argument(msg);
13392
#endif
13393
      return;
13394
    }
13395
13396
    auto is_ssl = scheme == "https";
13397
13398
    auto host = m[2].str();
13399
    if (host.empty()) { host = m[3].str(); }
13400
13401
    auto port_str = m[4].str();
13402
    auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
13403
13404
    if (is_ssl) {
13405
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13406
      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
13407
                                            client_key_path);
13408
      is_ssl_ = is_ssl;
13409
#endif
13410
    } else {
13411
      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
13412
                                             client_key_path);
13413
    }
13414
  } else {
13415
    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
13416
    // if port param below changes.
13417
    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
13418
                                           client_cert_path, client_key_path);
13419
  }
13420
} // namespace detail
13421
13422
inline Client::Client(const std::string &host, int port)
13423
    : cli_(detail::make_unique<ClientImpl>(host, port)) {}
13424
13425
inline Client::Client(const std::string &host, int port,
13426
                      const std::string &client_cert_path,
13427
                      const std::string &client_key_path)
13428
    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
13429
                                           client_key_path)) {}
13430
13431
inline Client::~Client() = default;
13432
13433
0
inline bool Client::is_valid() const {
13434
0
  return cli_ != nullptr && cli_->is_valid();
13435
0
}
13436
13437
0
inline Result Client::Get(const std::string &path, DownloadProgress progress) {
13438
0
  return cli_->Get(path, std::move(progress));
13439
0
}
13440
inline Result Client::Get(const std::string &path, const Headers &headers,
13441
0
                          DownloadProgress progress) {
13442
0
  return cli_->Get(path, headers, std::move(progress));
13443
0
}
13444
inline Result Client::Get(const std::string &path,
13445
                          ContentReceiver content_receiver,
13446
0
                          DownloadProgress progress) {
13447
0
  return cli_->Get(path, std::move(content_receiver), std::move(progress));
13448
0
}
13449
inline Result Client::Get(const std::string &path, const Headers &headers,
13450
                          ContentReceiver content_receiver,
13451
0
                          DownloadProgress progress) {
13452
0
  return cli_->Get(path, headers, std::move(content_receiver),
13453
0
                   std::move(progress));
13454
0
}
13455
inline Result Client::Get(const std::string &path,
13456
                          ResponseHandler response_handler,
13457
                          ContentReceiver content_receiver,
13458
0
                          DownloadProgress progress) {
13459
0
  return cli_->Get(path, std::move(response_handler),
13460
0
                   std::move(content_receiver), std::move(progress));
13461
0
}
13462
inline Result Client::Get(const std::string &path, const Headers &headers,
13463
                          ResponseHandler response_handler,
13464
                          ContentReceiver content_receiver,
13465
0
                          DownloadProgress progress) {
13466
0
  return cli_->Get(path, headers, std::move(response_handler),
13467
0
                   std::move(content_receiver), std::move(progress));
13468
0
}
13469
inline Result Client::Get(const std::string &path, const Params &params,
13470
0
                          const Headers &headers, DownloadProgress progress) {
13471
0
  return cli_->Get(path, params, headers, std::move(progress));
13472
0
}
13473
inline Result Client::Get(const std::string &path, const Params &params,
13474
                          const Headers &headers,
13475
                          ContentReceiver content_receiver,
13476
0
                          DownloadProgress progress) {
13477
0
  return cli_->Get(path, params, headers, std::move(content_receiver),
13478
0
                   std::move(progress));
13479
0
}
13480
inline Result Client::Get(const std::string &path, const Params &params,
13481
                          const Headers &headers,
13482
                          ResponseHandler response_handler,
13483
                          ContentReceiver content_receiver,
13484
0
                          DownloadProgress progress) {
13485
0
  return cli_->Get(path, params, headers, std::move(response_handler),
13486
0
                   std::move(content_receiver), std::move(progress));
13487
0
}
13488
13489
0
inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
13490
0
inline Result Client::Head(const std::string &path, const Headers &headers) {
13491
0
  return cli_->Head(path, headers);
13492
0
}
13493
13494
0
inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
13495
0
inline Result Client::Post(const std::string &path, const Headers &headers) {
13496
0
  return cli_->Post(path, headers);
13497
0
}
13498
inline Result Client::Post(const std::string &path, const char *body,
13499
                           size_t content_length,
13500
                           const std::string &content_type,
13501
0
                           UploadProgress progress) {
13502
0
  return cli_->Post(path, body, content_length, content_type, progress);
13503
0
}
13504
inline Result Client::Post(const std::string &path, const Headers &headers,
13505
                           const char *body, size_t content_length,
13506
                           const std::string &content_type,
13507
0
                           UploadProgress progress) {
13508
0
  return cli_->Post(path, headers, body, content_length, content_type,
13509
0
                    progress);
13510
0
}
13511
inline Result Client::Post(const std::string &path, const std::string &body,
13512
                           const std::string &content_type,
13513
0
                           UploadProgress progress) {
13514
0
  return cli_->Post(path, body, content_type, progress);
13515
0
}
13516
inline Result Client::Post(const std::string &path, const Headers &headers,
13517
                           const std::string &body,
13518
                           const std::string &content_type,
13519
0
                           UploadProgress progress) {
13520
0
  return cli_->Post(path, headers, body, content_type, progress);
13521
0
}
13522
inline Result Client::Post(const std::string &path, size_t content_length,
13523
                           ContentProvider content_provider,
13524
                           const std::string &content_type,
13525
0
                           UploadProgress progress) {
13526
0
  return cli_->Post(path, content_length, std::move(content_provider),
13527
0
                    content_type, progress);
13528
0
}
13529
inline Result Client::Post(const std::string &path, size_t content_length,
13530
                           ContentProvider content_provider,
13531
                           const std::string &content_type,
13532
                           ContentReceiver content_receiver,
13533
0
                           UploadProgress progress) {
13534
0
  return cli_->Post(path, content_length, std::move(content_provider),
13535
0
                    content_type, std::move(content_receiver), progress);
13536
0
}
13537
inline Result Client::Post(const std::string &path,
13538
                           ContentProviderWithoutLength content_provider,
13539
                           const std::string &content_type,
13540
0
                           UploadProgress progress) {
13541
0
  return cli_->Post(path, std::move(content_provider), content_type, progress);
13542
0
}
13543
inline Result Client::Post(const std::string &path,
13544
                           ContentProviderWithoutLength content_provider,
13545
                           const std::string &content_type,
13546
                           ContentReceiver content_receiver,
13547
0
                           UploadProgress progress) {
13548
0
  return cli_->Post(path, std::move(content_provider), content_type,
13549
0
                    std::move(content_receiver), progress);
13550
0
}
13551
inline Result Client::Post(const std::string &path, const Headers &headers,
13552
                           size_t content_length,
13553
                           ContentProvider content_provider,
13554
                           const std::string &content_type,
13555
0
                           UploadProgress progress) {
13556
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
13557
0
                    content_type, progress);
13558
0
}
13559
inline Result Client::Post(const std::string &path, const Headers &headers,
13560
                           size_t content_length,
13561
                           ContentProvider content_provider,
13562
                           const std::string &content_type,
13563
                           ContentReceiver content_receiver,
13564
0
                           DownloadProgress progress) {
13565
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
13566
0
                    content_type, std::move(content_receiver), progress);
13567
0
}
13568
inline Result Client::Post(const std::string &path, const Headers &headers,
13569
                           ContentProviderWithoutLength content_provider,
13570
                           const std::string &content_type,
13571
0
                           UploadProgress progress) {
13572
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
13573
0
                    progress);
13574
0
}
13575
inline Result Client::Post(const std::string &path, const Headers &headers,
13576
                           ContentProviderWithoutLength content_provider,
13577
                           const std::string &content_type,
13578
                           ContentReceiver content_receiver,
13579
0
                           DownloadProgress progress) {
13580
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
13581
0
                    std::move(content_receiver), progress);
13582
0
}
13583
0
inline Result Client::Post(const std::string &path, const Params &params) {
13584
0
  return cli_->Post(path, params);
13585
0
}
13586
inline Result Client::Post(const std::string &path, const Headers &headers,
13587
0
                           const Params &params) {
13588
0
  return cli_->Post(path, headers, params);
13589
0
}
13590
inline Result Client::Post(const std::string &path,
13591
                           const UploadFormDataItems &items,
13592
0
                           UploadProgress progress) {
13593
0
  return cli_->Post(path, items, progress);
13594
0
}
13595
inline Result Client::Post(const std::string &path, const Headers &headers,
13596
                           const UploadFormDataItems &items,
13597
0
                           UploadProgress progress) {
13598
0
  return cli_->Post(path, headers, items, progress);
13599
0
}
13600
inline Result Client::Post(const std::string &path, const Headers &headers,
13601
                           const UploadFormDataItems &items,
13602
                           const std::string &boundary,
13603
0
                           UploadProgress progress) {
13604
0
  return cli_->Post(path, headers, items, boundary, progress);
13605
0
}
13606
inline Result Client::Post(const std::string &path, const Headers &headers,
13607
                           const UploadFormDataItems &items,
13608
                           const FormDataProviderItems &provider_items,
13609
0
                           UploadProgress progress) {
13610
0
  return cli_->Post(path, headers, items, provider_items, progress);
13611
0
}
13612
inline Result Client::Post(const std::string &path, const Headers &headers,
13613
                           const std::string &body,
13614
                           const std::string &content_type,
13615
                           ContentReceiver content_receiver,
13616
0
                           DownloadProgress progress) {
13617
0
  return cli_->Post(path, headers, body, content_type,
13618
0
                    std::move(content_receiver), progress);
13619
0
}
13620
13621
0
inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
13622
0
inline Result Client::Put(const std::string &path, const Headers &headers) {
13623
0
  return cli_->Put(path, headers);
13624
0
}
13625
inline Result Client::Put(const std::string &path, const char *body,
13626
                          size_t content_length,
13627
                          const std::string &content_type,
13628
0
                          UploadProgress progress) {
13629
0
  return cli_->Put(path, body, content_length, content_type, progress);
13630
0
}
13631
inline Result Client::Put(const std::string &path, const Headers &headers,
13632
                          const char *body, size_t content_length,
13633
                          const std::string &content_type,
13634
0
                          UploadProgress progress) {
13635
0
  return cli_->Put(path, headers, body, content_length, content_type, progress);
13636
0
}
13637
inline Result Client::Put(const std::string &path, const std::string &body,
13638
                          const std::string &content_type,
13639
0
                          UploadProgress progress) {
13640
0
  return cli_->Put(path, body, content_type, progress);
13641
0
}
13642
inline Result Client::Put(const std::string &path, const Headers &headers,
13643
                          const std::string &body,
13644
                          const std::string &content_type,
13645
0
                          UploadProgress progress) {
13646
0
  return cli_->Put(path, headers, body, content_type, progress);
13647
0
}
13648
inline Result Client::Put(const std::string &path, size_t content_length,
13649
                          ContentProvider content_provider,
13650
                          const std::string &content_type,
13651
0
                          UploadProgress progress) {
13652
0
  return cli_->Put(path, content_length, std::move(content_provider),
13653
0
                   content_type, progress);
13654
0
}
13655
inline Result Client::Put(const std::string &path, size_t content_length,
13656
                          ContentProvider content_provider,
13657
                          const std::string &content_type,
13658
                          ContentReceiver content_receiver,
13659
0
                          UploadProgress progress) {
13660
0
  return cli_->Put(path, content_length, std::move(content_provider),
13661
0
                   content_type, std::move(content_receiver), progress);
13662
0
}
13663
inline Result Client::Put(const std::string &path,
13664
                          ContentProviderWithoutLength content_provider,
13665
                          const std::string &content_type,
13666
0
                          UploadProgress progress) {
13667
0
  return cli_->Put(path, std::move(content_provider), content_type, progress);
13668
0
}
13669
inline Result Client::Put(const std::string &path,
13670
                          ContentProviderWithoutLength content_provider,
13671
                          const std::string &content_type,
13672
                          ContentReceiver content_receiver,
13673
0
                          UploadProgress progress) {
13674
0
  return cli_->Put(path, std::move(content_provider), content_type,
13675
0
                   std::move(content_receiver), progress);
13676
0
}
13677
inline Result Client::Put(const std::string &path, const Headers &headers,
13678
                          size_t content_length,
13679
                          ContentProvider content_provider,
13680
                          const std::string &content_type,
13681
0
                          UploadProgress progress) {
13682
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
13683
0
                   content_type, progress);
13684
0
}
13685
inline Result Client::Put(const std::string &path, const Headers &headers,
13686
                          size_t content_length,
13687
                          ContentProvider content_provider,
13688
                          const std::string &content_type,
13689
                          ContentReceiver content_receiver,
13690
0
                          UploadProgress progress) {
13691
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
13692
0
                   content_type, std::move(content_receiver), progress);
13693
0
}
13694
inline Result Client::Put(const std::string &path, const Headers &headers,
13695
                          ContentProviderWithoutLength content_provider,
13696
                          const std::string &content_type,
13697
0
                          UploadProgress progress) {
13698
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
13699
0
                   progress);
13700
0
}
13701
inline Result Client::Put(const std::string &path, const Headers &headers,
13702
                          ContentProviderWithoutLength content_provider,
13703
                          const std::string &content_type,
13704
                          ContentReceiver content_receiver,
13705
0
                          UploadProgress progress) {
13706
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
13707
0
                   std::move(content_receiver), progress);
13708
0
}
13709
0
inline Result Client::Put(const std::string &path, const Params &params) {
13710
0
  return cli_->Put(path, params);
13711
0
}
13712
inline Result Client::Put(const std::string &path, const Headers &headers,
13713
0
                          const Params &params) {
13714
0
  return cli_->Put(path, headers, params);
13715
0
}
13716
inline Result Client::Put(const std::string &path,
13717
                          const UploadFormDataItems &items,
13718
0
                          UploadProgress progress) {
13719
0
  return cli_->Put(path, items, progress);
13720
0
}
13721
inline Result Client::Put(const std::string &path, const Headers &headers,
13722
                          const UploadFormDataItems &items,
13723
0
                          UploadProgress progress) {
13724
0
  return cli_->Put(path, headers, items, progress);
13725
0
}
13726
inline Result Client::Put(const std::string &path, const Headers &headers,
13727
                          const UploadFormDataItems &items,
13728
                          const std::string &boundary,
13729
0
                          UploadProgress progress) {
13730
0
  return cli_->Put(path, headers, items, boundary, progress);
13731
0
}
13732
inline Result Client::Put(const std::string &path, const Headers &headers,
13733
                          const UploadFormDataItems &items,
13734
                          const FormDataProviderItems &provider_items,
13735
0
                          UploadProgress progress) {
13736
0
  return cli_->Put(path, headers, items, provider_items, progress);
13737
0
}
13738
inline Result Client::Put(const std::string &path, const Headers &headers,
13739
                          const std::string &body,
13740
                          const std::string &content_type,
13741
                          ContentReceiver content_receiver,
13742
0
                          DownloadProgress progress) {
13743
0
  return cli_->Put(path, headers, body, content_type, content_receiver,
13744
0
                   progress);
13745
0
}
13746
13747
0
inline Result Client::Patch(const std::string &path) {
13748
0
  return cli_->Patch(path);
13749
0
}
13750
0
inline Result Client::Patch(const std::string &path, const Headers &headers) {
13751
0
  return cli_->Patch(path, headers);
13752
0
}
13753
inline Result Client::Patch(const std::string &path, const char *body,
13754
                            size_t content_length,
13755
                            const std::string &content_type,
13756
0
                            UploadProgress progress) {
13757
0
  return cli_->Patch(path, body, content_length, content_type, progress);
13758
0
}
13759
inline Result Client::Patch(const std::string &path, const Headers &headers,
13760
                            const char *body, size_t content_length,
13761
                            const std::string &content_type,
13762
0
                            UploadProgress progress) {
13763
0
  return cli_->Patch(path, headers, body, content_length, content_type,
13764
0
                     progress);
13765
0
}
13766
inline Result Client::Patch(const std::string &path, const std::string &body,
13767
                            const std::string &content_type,
13768
0
                            UploadProgress progress) {
13769
0
  return cli_->Patch(path, body, content_type, progress);
13770
0
}
13771
inline Result Client::Patch(const std::string &path, const Headers &headers,
13772
                            const std::string &body,
13773
                            const std::string &content_type,
13774
0
                            UploadProgress progress) {
13775
0
  return cli_->Patch(path, headers, body, content_type, progress);
13776
0
}
13777
inline Result Client::Patch(const std::string &path, size_t content_length,
13778
                            ContentProvider content_provider,
13779
                            const std::string &content_type,
13780
0
                            UploadProgress progress) {
13781
0
  return cli_->Patch(path, content_length, std::move(content_provider),
13782
0
                     content_type, progress);
13783
0
}
13784
inline Result Client::Patch(const std::string &path, size_t content_length,
13785
                            ContentProvider content_provider,
13786
                            const std::string &content_type,
13787
                            ContentReceiver content_receiver,
13788
0
                            UploadProgress progress) {
13789
0
  return cli_->Patch(path, content_length, std::move(content_provider),
13790
0
                     content_type, std::move(content_receiver), progress);
13791
0
}
13792
inline Result Client::Patch(const std::string &path,
13793
                            ContentProviderWithoutLength content_provider,
13794
                            const std::string &content_type,
13795
0
                            UploadProgress progress) {
13796
0
  return cli_->Patch(path, std::move(content_provider), content_type, progress);
13797
0
}
13798
inline Result Client::Patch(const std::string &path,
13799
                            ContentProviderWithoutLength content_provider,
13800
                            const std::string &content_type,
13801
                            ContentReceiver content_receiver,
13802
0
                            UploadProgress progress) {
13803
0
  return cli_->Patch(path, std::move(content_provider), content_type,
13804
0
                     std::move(content_receiver), progress);
13805
0
}
13806
inline Result Client::Patch(const std::string &path, const Headers &headers,
13807
                            size_t content_length,
13808
                            ContentProvider content_provider,
13809
                            const std::string &content_type,
13810
0
                            UploadProgress progress) {
13811
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
13812
0
                     content_type, progress);
13813
0
}
13814
inline Result Client::Patch(const std::string &path, const Headers &headers,
13815
                            size_t content_length,
13816
                            ContentProvider content_provider,
13817
                            const std::string &content_type,
13818
                            ContentReceiver content_receiver,
13819
0
                            UploadProgress progress) {
13820
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
13821
0
                     content_type, std::move(content_receiver), progress);
13822
0
}
13823
inline Result Client::Patch(const std::string &path, const Headers &headers,
13824
                            ContentProviderWithoutLength content_provider,
13825
                            const std::string &content_type,
13826
0
                            UploadProgress progress) {
13827
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
13828
0
                     progress);
13829
0
}
13830
inline Result Client::Patch(const std::string &path, const Headers &headers,
13831
                            ContentProviderWithoutLength content_provider,
13832
                            const std::string &content_type,
13833
                            ContentReceiver content_receiver,
13834
0
                            UploadProgress progress) {
13835
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
13836
0
                     std::move(content_receiver), progress);
13837
0
}
13838
0
inline Result Client::Patch(const std::string &path, const Params &params) {
13839
0
  return cli_->Patch(path, params);
13840
0
}
13841
inline Result Client::Patch(const std::string &path, const Headers &headers,
13842
0
                            const Params &params) {
13843
0
  return cli_->Patch(path, headers, params);
13844
0
}
13845
inline Result Client::Patch(const std::string &path,
13846
                            const UploadFormDataItems &items,
13847
0
                            UploadProgress progress) {
13848
0
  return cli_->Patch(path, items, progress);
13849
0
}
13850
inline Result Client::Patch(const std::string &path, const Headers &headers,
13851
                            const UploadFormDataItems &items,
13852
0
                            UploadProgress progress) {
13853
0
  return cli_->Patch(path, headers, items, progress);
13854
0
}
13855
inline Result Client::Patch(const std::string &path, const Headers &headers,
13856
                            const UploadFormDataItems &items,
13857
                            const std::string &boundary,
13858
0
                            UploadProgress progress) {
13859
0
  return cli_->Patch(path, headers, items, boundary, progress);
13860
0
}
13861
inline Result Client::Patch(const std::string &path, const Headers &headers,
13862
                            const UploadFormDataItems &items,
13863
                            const FormDataProviderItems &provider_items,
13864
0
                            UploadProgress progress) {
13865
0
  return cli_->Patch(path, headers, items, provider_items, progress);
13866
0
}
13867
inline Result Client::Patch(const std::string &path, const Headers &headers,
13868
                            const std::string &body,
13869
                            const std::string &content_type,
13870
                            ContentReceiver content_receiver,
13871
0
                            DownloadProgress progress) {
13872
0
  return cli_->Patch(path, headers, body, content_type, content_receiver,
13873
0
                     progress);
13874
0
}
13875
13876
inline Result Client::Delete(const std::string &path,
13877
0
                             DownloadProgress progress) {
13878
0
  return cli_->Delete(path, progress);
13879
0
}
13880
inline Result Client::Delete(const std::string &path, const Headers &headers,
13881
0
                             DownloadProgress progress) {
13882
0
  return cli_->Delete(path, headers, progress);
13883
0
}
13884
inline Result Client::Delete(const std::string &path, const char *body,
13885
                             size_t content_length,
13886
                             const std::string &content_type,
13887
0
                             DownloadProgress progress) {
13888
0
  return cli_->Delete(path, body, content_length, content_type, progress);
13889
0
}
13890
inline Result Client::Delete(const std::string &path, const Headers &headers,
13891
                             const char *body, size_t content_length,
13892
                             const std::string &content_type,
13893
0
                             DownloadProgress progress) {
13894
0
  return cli_->Delete(path, headers, body, content_length, content_type,
13895
0
                      progress);
13896
0
}
13897
inline Result Client::Delete(const std::string &path, const std::string &body,
13898
                             const std::string &content_type,
13899
0
                             DownloadProgress progress) {
13900
0
  return cli_->Delete(path, body, content_type, progress);
13901
0
}
13902
inline Result Client::Delete(const std::string &path, const Headers &headers,
13903
                             const std::string &body,
13904
                             const std::string &content_type,
13905
0
                             DownloadProgress progress) {
13906
0
  return cli_->Delete(path, headers, body, content_type, progress);
13907
0
}
13908
inline Result Client::Delete(const std::string &path, const Params &params,
13909
0
                             DownloadProgress progress) {
13910
0
  return cli_->Delete(path, params, progress);
13911
0
}
13912
inline Result Client::Delete(const std::string &path, const Headers &headers,
13913
0
                             const Params &params, DownloadProgress progress) {
13914
0
  return cli_->Delete(path, headers, params, progress);
13915
0
}
13916
13917
0
inline Result Client::Options(const std::string &path) {
13918
0
  return cli_->Options(path);
13919
0
}
13920
0
inline Result Client::Options(const std::string &path, const Headers &headers) {
13921
0
  return cli_->Options(path, headers);
13922
0
}
13923
13924
inline ClientImpl::StreamHandle
13925
Client::open_stream(const std::string &method, const std::string &path,
13926
                    const Params &params, const Headers &headers,
13927
0
                    const std::string &body, const std::string &content_type) {
13928
0
  return cli_->open_stream(method, path, params, headers, body, content_type);
13929
0
}
13930
13931
0
inline bool Client::send(Request &req, Response &res, Error &error) {
13932
0
  return cli_->send(req, res, error);
13933
0
}
13934
13935
0
inline Result Client::send(const Request &req) { return cli_->send(req); }
13936
13937
0
inline void Client::stop() { cli_->stop(); }
13938
13939
0
inline std::string Client::host() const { return cli_->host(); }
13940
13941
0
inline int Client::port() const { return cli_->port(); }
13942
13943
0
inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
13944
13945
0
inline socket_t Client::socket() const { return cli_->socket(); }
13946
13947
inline void
13948
0
Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
13949
0
  cli_->set_hostname_addr_map(std::move(addr_map));
13950
0
}
13951
13952
0
inline void Client::set_default_headers(Headers headers) {
13953
0
  cli_->set_default_headers(std::move(headers));
13954
0
}
13955
13956
inline void Client::set_header_writer(
13957
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
13958
0
  cli_->set_header_writer(writer);
13959
0
}
13960
13961
0
inline void Client::set_address_family(int family) {
13962
0
  cli_->set_address_family(family);
13963
0
}
13964
13965
0
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
13966
13967
0
inline void Client::set_socket_options(SocketOptions socket_options) {
13968
0
  cli_->set_socket_options(std::move(socket_options));
13969
0
}
13970
13971
0
inline void Client::set_connection_timeout(time_t sec, time_t usec) {
13972
0
  cli_->set_connection_timeout(sec, usec);
13973
0
}
13974
13975
0
inline void Client::set_read_timeout(time_t sec, time_t usec) {
13976
0
  cli_->set_read_timeout(sec, usec);
13977
0
}
13978
13979
0
inline void Client::set_write_timeout(time_t sec, time_t usec) {
13980
0
  cli_->set_write_timeout(sec, usec);
13981
0
}
13982
13983
inline void Client::set_basic_auth(const std::string &username,
13984
0
                                   const std::string &password) {
13985
0
  cli_->set_basic_auth(username, password);
13986
0
}
13987
0
inline void Client::set_bearer_token_auth(const std::string &token) {
13988
0
  cli_->set_bearer_token_auth(token);
13989
0
}
13990
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13991
inline void Client::set_digest_auth(const std::string &username,
13992
                                    const std::string &password) {
13993
  cli_->set_digest_auth(username, password);
13994
}
13995
#endif
13996
13997
0
inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
13998
0
inline void Client::set_follow_location(bool on) {
13999
0
  cli_->set_follow_location(on);
14000
0
}
14001
14002
0
inline void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }
14003
14004
[[deprecated("Use set_path_encode instead")]]
14005
0
inline void Client::set_url_encode(bool on) {
14006
0
  cli_->set_path_encode(on);
14007
0
}
14008
14009
0
inline void Client::set_compress(bool on) { cli_->set_compress(on); }
14010
14011
0
inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
14012
14013
0
inline void Client::set_interface(const std::string &intf) {
14014
0
  cli_->set_interface(intf);
14015
0
}
14016
14017
0
inline void Client::set_proxy(const std::string &host, int port) {
14018
0
  cli_->set_proxy(host, port);
14019
0
}
14020
inline void Client::set_proxy_basic_auth(const std::string &username,
14021
0
                                         const std::string &password) {
14022
0
  cli_->set_proxy_basic_auth(username, password);
14023
0
}
14024
0
inline void Client::set_proxy_bearer_token_auth(const std::string &token) {
14025
0
  cli_->set_proxy_bearer_token_auth(token);
14026
0
}
14027
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14028
inline void Client::set_proxy_digest_auth(const std::string &username,
14029
                                          const std::string &password) {
14030
  cli_->set_proxy_digest_auth(username, password);
14031
}
14032
#endif
14033
14034
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14035
inline void Client::enable_server_certificate_verification(bool enabled) {
14036
  cli_->enable_server_certificate_verification(enabled);
14037
}
14038
14039
inline void Client::enable_server_hostname_verification(bool enabled) {
14040
  cli_->enable_server_hostname_verification(enabled);
14041
}
14042
14043
inline void Client::set_server_certificate_verifier(
14044
    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
14045
  cli_->set_server_certificate_verifier(verifier);
14046
}
14047
#endif
14048
14049
0
inline void Client::set_logger(Logger logger) {
14050
0
  cli_->set_logger(std::move(logger));
14051
0
}
14052
14053
0
inline void Client::set_error_logger(ErrorLogger error_logger) {
14054
0
  cli_->set_error_logger(std::move(error_logger));
14055
0
}
14056
14057
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
14058
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
14059
                                     const std::string &ca_cert_dir_path) {
14060
  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
14061
}
14062
14063
inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
14064
  if (is_ssl_) {
14065
    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
14066
  } else {
14067
    cli_->set_ca_cert_store(ca_cert_store);
14068
  }
14069
}
14070
14071
inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
14072
  set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
14073
}
14074
14075
inline long Client::get_openssl_verify_result() const {
14076
  if (is_ssl_) {
14077
    return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
14078
  }
14079
  return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
14080
}
14081
14082
inline SSL_CTX *Client::ssl_context() const {
14083
  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
14084
  return nullptr;
14085
}
14086
#endif
14087
14088
// ----------------------------------------------------------------------------
14089
14090
} // namespace httplib
14091
14092
#endif // CPPHTTPLIB_HTTPLIB_H