Coverage Report

Created: 2026-01-10 07:01

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_IDLE_INTERVAL_SECOND
102
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
103
#endif
104
105
#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
106
#ifdef _WIN32
107
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
108
#else
109
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
110
#endif
111
#endif
112
113
#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
114
4.45k
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
115
#endif
116
117
#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
118
41.3k
#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
119
#endif
120
121
#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
122
14.9k
#define CPPHTTPLIB_HEADER_MAX_COUNT 100
123
#endif
124
125
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
126
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
127
#endif
128
129
#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
130
25.2k
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
131
#endif
132
133
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
134
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
135
#endif
136
137
#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
138
34
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
139
#endif
140
141
#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
142
740
#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
143
#endif
144
145
#ifndef CPPHTTPLIB_TCP_NODELAY
146
#define CPPHTTPLIB_TCP_NODELAY false
147
#endif
148
149
#ifndef CPPHTTPLIB_IPV6_V6ONLY
150
#define CPPHTTPLIB_IPV6_V6ONLY false
151
#endif
152
153
#ifndef CPPHTTPLIB_RECV_BUFSIZ
154
3.46k
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
155
#endif
156
157
#ifndef CPPHTTPLIB_SEND_BUFSIZ
158
#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
159
#endif
160
161
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
162
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
163
#endif
164
165
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
166
#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
167
0
  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \
168
0
                      ? std::thread::hardware_concurrency() - 1                \
169
0
                      : 0))
170
#endif
171
172
#ifndef CPPHTTPLIB_RECV_FLAGS
173
0
#define CPPHTTPLIB_RECV_FLAGS 0
174
#endif
175
176
#ifndef CPPHTTPLIB_SEND_FLAGS
177
0
#define CPPHTTPLIB_SEND_FLAGS 0
178
#endif
179
180
#ifndef CPPHTTPLIB_LISTEN_BACKLOG
181
#define CPPHTTPLIB_LISTEN_BACKLOG 5
182
#endif
183
184
#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
185
3.02M
#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
186
#endif
187
188
/*
189
 * Headers
190
 */
191
192
#ifdef _WIN32
193
#ifndef _CRT_SECURE_NO_WARNINGS
194
#define _CRT_SECURE_NO_WARNINGS
195
#endif //_CRT_SECURE_NO_WARNINGS
196
197
#ifndef _CRT_NONSTDC_NO_DEPRECATE
198
#define _CRT_NONSTDC_NO_DEPRECATE
199
#endif //_CRT_NONSTDC_NO_DEPRECATE
200
201
#if defined(_MSC_VER)
202
#if _MSC_VER < 1900
203
#error Sorry, Visual Studio versions prior to 2015 are not supported
204
#endif
205
206
#pragma comment(lib, "ws2_32.lib")
207
208
#ifndef _SSIZE_T_DEFINED
209
using ssize_t = __int64;
210
#define _SSIZE_T_DEFINED
211
#endif
212
#endif // _MSC_VER
213
214
#ifndef S_ISREG
215
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
216
#endif // S_ISREG
217
218
#ifndef S_ISDIR
219
#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
220
#endif // S_ISDIR
221
222
#ifndef NOMINMAX
223
#define NOMINMAX
224
#endif // NOMINMAX
225
226
#include <io.h>
227
#include <winsock2.h>
228
#include <ws2tcpip.h>
229
230
#if defined(__has_include)
231
#if __has_include(<afunix.h>)
232
// afunix.h uses types declared in winsock2.h, so has to be included after it.
233
#include <afunix.h>
234
#define CPPHTTPLIB_HAVE_AFUNIX_H 1
235
#endif
236
#endif
237
238
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
239
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
240
#endif
241
242
using nfds_t = unsigned long;
243
using socket_t = SOCKET;
244
using socklen_t = int;
245
246
#else // not _WIN32
247
248
#include <arpa/inet.h>
249
#if !defined(_AIX) && !defined(__MVS__)
250
#include <ifaddrs.h>
251
#endif
252
#ifdef __MVS__
253
#include <strings.h>
254
#ifndef NI_MAXHOST
255
#define NI_MAXHOST 1025
256
#endif
257
#endif
258
#include <net/if.h>
259
#include <netdb.h>
260
#include <netinet/in.h>
261
#ifdef __linux__
262
#include <resolv.h>
263
#undef _res // Undefine _res macro to avoid conflicts with user code (#2278)
264
#endif
265
#include <csignal>
266
#include <netinet/tcp.h>
267
#include <poll.h>
268
#include <pthread.h>
269
#include <sys/mman.h>
270
#include <sys/socket.h>
271
#include <sys/un.h>
272
#include <unistd.h>
273
274
using socket_t = int;
275
#ifndef INVALID_SOCKET
276
1.01k
#define INVALID_SOCKET (-1)
277
#endif
278
#endif //_WIN32
279
280
#if defined(__APPLE__)
281
#include <TargetConditionals.h>
282
#endif
283
284
#include <algorithm>
285
#include <array>
286
#include <atomic>
287
#include <cassert>
288
#include <cctype>
289
#include <climits>
290
#include <condition_variable>
291
#include <cstring>
292
#include <errno.h>
293
#include <exception>
294
#include <fcntl.h>
295
#include <functional>
296
#include <iomanip>
297
#include <iostream>
298
#include <list>
299
#include <map>
300
#include <memory>
301
#include <mutex>
302
#include <random>
303
#include <regex>
304
#include <set>
305
#include <sstream>
306
#include <string>
307
#include <sys/stat.h>
308
#include <thread>
309
#include <unordered_map>
310
#include <unordered_set>
311
#include <utility>
312
313
#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) ||                        \
314
    defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
315
#if TARGET_OS_MAC
316
#include <CFNetwork/CFHost.h>
317
#include <CoreFoundation/CoreFoundation.h>
318
#endif
319
#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or
320
       // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
321
322
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
323
#ifdef _WIN32
324
#include <wincrypt.h>
325
326
// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
327
// used
328
#undef X509_NAME
329
#undef X509_CERT_PAIR
330
#undef X509_EXTENSIONS
331
#undef PKCS7_SIGNER_INFO
332
333
#ifdef _MSC_VER
334
#pragma comment(lib, "crypt32.lib")
335
#endif
336
#endif // _WIN32
337
338
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
339
#if TARGET_OS_MAC
340
#include <Security/Security.h>
341
#endif
342
#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
343
344
#include <openssl/err.h>
345
#include <openssl/evp.h>
346
#include <openssl/ssl.h>
347
#include <openssl/x509v3.h>
348
349
#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
350
#include <openssl/applink.c>
351
#endif
352
353
#include <iostream>
354
#include <sstream>
355
356
#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
357
#if OPENSSL_VERSION_NUMBER < 0x1010107f
358
#error Please use OpenSSL or a current version of BoringSSL
359
#endif
360
#define SSL_get1_peer_certificate SSL_get_peer_certificate
361
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
362
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
363
#endif
364
365
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
366
367
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
368
#include <zlib.h>
369
#endif
370
371
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
372
#include <brotli/decode.h>
373
#include <brotli/encode.h>
374
#endif
375
376
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
377
#include <zstd.h>
378
#endif
379
380
/*
381
 * Declaration
382
 */
383
namespace httplib {
384
385
namespace detail {
386
387
/*
388
 * Backport std::make_unique from C++14.
389
 *
390
 * NOTE: This code came up with the following stackoverflow post:
391
 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
392
 *
393
 */
394
395
template <class T, class... Args>
396
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
397
611
make_unique(Args &&...args) {
398
611
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
399
611
}
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
397
12
make_unique(Args &&...args) {
398
12
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
399
12
}
_ZN7httplib6detail11make_uniqueINS0_15gzip_compressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Line
Count
Source
397
18
make_unique(Args &&...args) {
398
18
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
399
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
397
581
make_unique(Args &&...args) {
398
581
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
399
581
}
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_
400
401
template <class T>
402
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
403
make_unique(std::size_t n) {
404
  typedef typename std::remove_extent<T>::type RT;
405
  return std::unique_ptr<T>(new RT[n]);
406
}
407
408
namespace case_ignore {
409
410
144M
inline unsigned char to_lower(int c) {
411
144M
  const static unsigned char table[256] = {
412
144M
      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
413
144M
      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
414
144M
      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
415
144M
      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
416
144M
      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
417
144M
      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
418
144M
      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
419
144M
      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
420
144M
      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
421
144M
      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
422
144M
      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
423
144M
      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
424
144M
      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
425
144M
      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
426
144M
      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
427
144M
      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
428
144M
      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
429
144M
      255,
430
144M
  };
431
144M
  return table[(unsigned char)(char)c];
432
144M
}
433
434
15.3M
inline bool equal(const std::string &a, const std::string &b) {
435
15.3M
  return a.size() == b.size() &&
436
70.4M
         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
437
70.4M
           return to_lower(ca) == to_lower(cb);
438
70.4M
         });
439
15.3M
}
440
441
struct equal_to {
442
15.0M
  bool operator()(const std::string &a, const std::string &b) const {
443
15.0M
    return equal(a, b);
444
15.0M
  }
445
};
446
447
struct hash {
448
375k
  size_t operator()(const std::string &key) const {
449
375k
    return hash_core(key.data(), key.size(), 0);
450
375k
  }
451
452
3.98M
  size_t hash_core(const char *s, size_t l, size_t h) const {
453
3.98M
    return (l == 0) ? h
454
3.98M
                    : hash_core(s + 1, l - 1,
455
                                // Unsets the 6 high bits of h, therefore no
456
                                // overflow happens
457
3.60M
                                (((std::numeric_limits<size_t>::max)() >> 6) &
458
3.60M
                                 h * 33) ^
459
3.60M
                                    static_cast<unsigned char>(to_lower(*s)));
460
3.98M
  }
461
};
462
463
template <typename T>
464
using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
465
                                         detail::case_ignore::equal_to>;
466
467
} // namespace case_ignore
468
469
// This is based on
470
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
471
472
struct scope_exit {
473
  explicit scope_exit(std::function<void(void)> &&f)
474
0
      : exit_function(std::move(f)), execute_on_destruction{true} {}
475
476
  scope_exit(scope_exit &&rhs) noexcept
477
      : exit_function(std::move(rhs.exit_function)),
478
0
        execute_on_destruction{rhs.execute_on_destruction} {
479
0
    rhs.release();
480
0
  }
481
482
0
  ~scope_exit() {
483
0
    if (execute_on_destruction) { this->exit_function(); }
484
0
  }
485
486
0
  void release() { this->execute_on_destruction = false; }
487
488
private:
489
  scope_exit(const scope_exit &) = delete;
490
  void operator=(const scope_exit &) = delete;
491
  scope_exit &operator=(scope_exit &&) = delete;
492
493
  std::function<void(void)> exit_function;
494
  bool execute_on_destruction;
495
};
496
497
} // namespace detail
498
499
enum SSLVerifierResponse {
500
  // no decision has been made, use the built-in certificate verifier
501
  NoDecisionMade,
502
  // connection certificate is verified and accepted
503
  CertificateAccepted,
504
  // connection certificate was processed but is rejected
505
  CertificateRejected
506
};
507
508
enum StatusCode {
509
  // Information responses
510
  Continue_100 = 100,
511
  SwitchingProtocol_101 = 101,
512
  Processing_102 = 102,
513
  EarlyHints_103 = 103,
514
515
  // Successful responses
516
  OK_200 = 200,
517
  Created_201 = 201,
518
  Accepted_202 = 202,
519
  NonAuthoritativeInformation_203 = 203,
520
  NoContent_204 = 204,
521
  ResetContent_205 = 205,
522
  PartialContent_206 = 206,
523
  MultiStatus_207 = 207,
524
  AlreadyReported_208 = 208,
525
  IMUsed_226 = 226,
526
527
  // Redirection messages
528
  MultipleChoices_300 = 300,
529
  MovedPermanently_301 = 301,
530
  Found_302 = 302,
531
  SeeOther_303 = 303,
532
  NotModified_304 = 304,
533
  UseProxy_305 = 305,
534
  unused_306 = 306,
535
  TemporaryRedirect_307 = 307,
536
  PermanentRedirect_308 = 308,
537
538
  // Client error responses
539
  BadRequest_400 = 400,
540
  Unauthorized_401 = 401,
541
  PaymentRequired_402 = 402,
542
  Forbidden_403 = 403,
543
  NotFound_404 = 404,
544
  MethodNotAllowed_405 = 405,
545
  NotAcceptable_406 = 406,
546
  ProxyAuthenticationRequired_407 = 407,
547
  RequestTimeout_408 = 408,
548
  Conflict_409 = 409,
549
  Gone_410 = 410,
550
  LengthRequired_411 = 411,
551
  PreconditionFailed_412 = 412,
552
  PayloadTooLarge_413 = 413,
553
  UriTooLong_414 = 414,
554
  UnsupportedMediaType_415 = 415,
555
  RangeNotSatisfiable_416 = 416,
556
  ExpectationFailed_417 = 417,
557
  ImATeapot_418 = 418,
558
  MisdirectedRequest_421 = 421,
559
  UnprocessableContent_422 = 422,
560
  Locked_423 = 423,
561
  FailedDependency_424 = 424,
562
  TooEarly_425 = 425,
563
  UpgradeRequired_426 = 426,
564
  PreconditionRequired_428 = 428,
565
  TooManyRequests_429 = 429,
566
  RequestHeaderFieldsTooLarge_431 = 431,
567
  UnavailableForLegalReasons_451 = 451,
568
569
  // Server error responses
570
  InternalServerError_500 = 500,
571
  NotImplemented_501 = 501,
572
  BadGateway_502 = 502,
573
  ServiceUnavailable_503 = 503,
574
  GatewayTimeout_504 = 504,
575
  HttpVersionNotSupported_505 = 505,
576
  VariantAlsoNegotiates_506 = 506,
577
  InsufficientStorage_507 = 507,
578
  LoopDetected_508 = 508,
579
  NotExtended_510 = 510,
580
  NetworkAuthenticationRequired_511 = 511,
581
};
582
583
using Headers =
584
    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
585
                            detail::case_ignore::equal_to>;
586
587
using Params = std::multimap<std::string, std::string>;
588
using Match = std::smatch;
589
590
using DownloadProgress = std::function<bool(size_t current, size_t total)>;
591
using UploadProgress = std::function<bool(size_t current, size_t total)>;
592
593
struct Response;
594
using ResponseHandler = std::function<bool(const Response &response)>;
595
596
struct FormData {
597
  std::string name;
598
  std::string content;
599
  std::string filename;
600
  std::string content_type;
601
  Headers headers;
602
};
603
604
struct FormField {
605
  std::string name;
606
  std::string content;
607
  Headers headers;
608
};
609
using FormFields = std::multimap<std::string, FormField>;
610
611
using FormFiles = std::multimap<std::string, FormData>;
612
613
struct MultipartFormData {
614
  FormFields fields; // Text fields from multipart
615
  FormFiles files;   // Files from multipart
616
617
  // Text field access
618
  std::string get_field(const std::string &key, size_t id = 0) const;
619
  std::vector<std::string> get_fields(const std::string &key) const;
620
  bool has_field(const std::string &key) const;
621
  size_t get_field_count(const std::string &key) const;
622
623
  // File access
624
  FormData get_file(const std::string &key, size_t id = 0) const;
625
  std::vector<FormData> get_files(const std::string &key) const;
626
  bool has_file(const std::string &key) const;
627
  size_t get_file_count(const std::string &key) const;
628
};
629
630
struct UploadFormData {
631
  std::string name;
632
  std::string content;
633
  std::string filename;
634
  std::string content_type;
635
};
636
using UploadFormDataItems = std::vector<UploadFormData>;
637
638
class DataSink {
639
public:
640
0
  DataSink() : os(&sb_), sb_(*this) {}
641
642
  DataSink(const DataSink &) = delete;
643
  DataSink &operator=(const DataSink &) = delete;
644
  DataSink(DataSink &&) = delete;
645
  DataSink &operator=(DataSink &&) = delete;
646
647
  std::function<bool(const char *data, size_t data_len)> write;
648
  std::function<bool()> is_writable;
649
  std::function<void()> done;
650
  std::function<void(const Headers &trailer)> done_with_trailer;
651
  std::ostream os;
652
653
private:
654
  class data_sink_streambuf final : public std::streambuf {
655
  public:
656
0
    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
657
658
  protected:
659
0
    std::streamsize xsputn(const char *s, std::streamsize n) override {
660
0
      sink_.write(s, static_cast<size_t>(n));
661
0
      return n;
662
0
    }
663
664
  private:
665
    DataSink &sink_;
666
  };
667
668
  data_sink_streambuf sb_;
669
};
670
671
using ContentProvider =
672
    std::function<bool(size_t offset, size_t length, DataSink &sink)>;
673
674
using ContentProviderWithoutLength =
675
    std::function<bool(size_t offset, DataSink &sink)>;
676
677
using ContentProviderResourceReleaser = std::function<void(bool success)>;
678
679
struct FormDataProvider {
680
  std::string name;
681
  ContentProviderWithoutLength provider;
682
  std::string filename;
683
  std::string content_type;
684
};
685
using FormDataProviderItems = std::vector<FormDataProvider>;
686
687
using ContentReceiverWithProgress = std::function<bool(
688
    const char *data, size_t data_length, size_t offset, size_t total_length)>;
689
690
using ContentReceiver =
691
    std::function<bool(const char *data, size_t data_length)>;
692
693
using FormDataHeader = std::function<bool(const FormData &file)>;
694
695
class ContentReader {
696
public:
697
  using Reader = std::function<bool(ContentReceiver receiver)>;
698
  using FormDataReader =
699
      std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
700
701
  ContentReader(Reader reader, FormDataReader multipart_reader)
702
3.36k
      : reader_(std::move(reader)),
703
3.36k
        formdata_reader_(std::move(multipart_reader)) {}
704
705
0
  bool operator()(FormDataHeader header, ContentReceiver receiver) const {
706
0
    return formdata_reader_(std::move(header), std::move(receiver));
707
0
  }
708
709
0
  bool operator()(ContentReceiver receiver) const {
710
0
    return reader_(std::move(receiver));
711
0
  }
712
713
  Reader reader_;
714
  FormDataReader formdata_reader_;
715
};
716
717
using Range = std::pair<ssize_t, ssize_t>;
718
using Ranges = std::vector<Range>;
719
720
struct Request {
721
  std::string method;
722
  std::string path;
723
  std::string matched_route;
724
  Params params;
725
  Headers headers;
726
  Headers trailers;
727
  std::string body;
728
729
  std::string remote_addr;
730
  int remote_port = -1;
731
  std::string local_addr;
732
  int local_port = -1;
733
734
  // for server
735
  std::string version;
736
  std::string target;
737
  MultipartFormData form;
738
  Ranges ranges;
739
  Match matches;
740
  std::unordered_map<std::string, std::string> path_params;
741
0
  std::function<bool()> is_connection_closed = []() { return true; };
742
743
  // for client
744
  std::vector<std::string> accept_content_types;
745
  ResponseHandler response_handler;
746
  ContentReceiverWithProgress content_receiver;
747
  DownloadProgress download_progress;
748
  UploadProgress upload_progress;
749
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
750
  const SSL *ssl = nullptr;
751
#endif
752
753
  bool has_header(const std::string &key) const;
754
  std::string get_header_value(const std::string &key, const char *def = "",
755
                               size_t id = 0) const;
756
  size_t get_header_value_u64(const std::string &key, size_t def = 0,
757
                              size_t id = 0) const;
758
  size_t get_header_value_count(const std::string &key) const;
759
  void set_header(const std::string &key, const std::string &val);
760
761
  bool has_trailer(const std::string &key) const;
762
  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
763
  size_t get_trailer_value_count(const std::string &key) const;
764
765
  bool has_param(const std::string &key) const;
766
  std::string get_param_value(const std::string &key, size_t id = 0) const;
767
  size_t get_param_value_count(const std::string &key) const;
768
769
  bool is_multipart_form_data() const;
770
771
  // private members...
772
  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
773
  size_t content_length_ = 0;
774
  ContentProvider content_provider_;
775
  bool is_chunked_content_provider_ = false;
776
  size_t authorization_count_ = 0;
777
  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
778
      (std::chrono::steady_clock::time_point::min)();
779
};
780
781
struct Response {
782
  std::string version;
783
  int status = -1;
784
  std::string reason;
785
  Headers headers;
786
  Headers trailers;
787
  std::string body;
788
  std::string location; // Redirect location
789
790
  bool has_header(const std::string &key) const;
791
  std::string get_header_value(const std::string &key, const char *def = "",
792
                               size_t id = 0) const;
793
  size_t get_header_value_u64(const std::string &key, size_t def = 0,
794
                              size_t id = 0) const;
795
  size_t get_header_value_count(const std::string &key) const;
796
  void set_header(const std::string &key, const std::string &val);
797
798
  bool has_trailer(const std::string &key) const;
799
  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
800
  size_t get_trailer_value_count(const std::string &key) const;
801
802
  void set_redirect(const std::string &url, int status = StatusCode::Found_302);
803
  void set_content(const char *s, size_t n, const std::string &content_type);
804
  void set_content(const std::string &s, const std::string &content_type);
805
  void set_content(std::string &&s, const std::string &content_type);
806
807
  void set_content_provider(
808
      size_t length, const std::string &content_type, ContentProvider provider,
809
      ContentProviderResourceReleaser resource_releaser = nullptr);
810
811
  void set_content_provider(
812
      const std::string &content_type, ContentProviderWithoutLength provider,
813
      ContentProviderResourceReleaser resource_releaser = nullptr);
814
815
  void set_chunked_content_provider(
816
      const std::string &content_type, ContentProviderWithoutLength provider,
817
      ContentProviderResourceReleaser resource_releaser = nullptr);
818
819
  void set_file_content(const std::string &path,
820
                        const std::string &content_type);
821
  void set_file_content(const std::string &path);
822
823
6.05k
  Response() = default;
824
  Response(const Response &) = default;
825
  Response &operator=(const Response &) = default;
826
  Response(Response &&) = default;
827
  Response &operator=(Response &&) = default;
828
6.05k
  ~Response() {
829
6.05k
    if (content_provider_resource_releaser_) {
830
0
      content_provider_resource_releaser_(content_provider_success_);
831
0
    }
832
6.05k
  }
833
834
  // private members...
835
  size_t content_length_ = 0;
836
  ContentProvider content_provider_;
837
  ContentProviderResourceReleaser content_provider_resource_releaser_;
838
  bool is_chunked_content_provider_ = false;
839
  bool content_provider_success_ = false;
840
  std::string file_content_path_;
841
  std::string file_content_content_type_;
842
};
843
844
enum class Error {
845
  Success = 0,
846
  Unknown,
847
  Connection,
848
  BindIPAddress,
849
  Read,
850
  Write,
851
  ExceedRedirectCount,
852
  Canceled,
853
  SSLConnection,
854
  SSLLoadingCerts,
855
  SSLServerVerification,
856
  SSLServerHostnameVerification,
857
  UnsupportedMultipartBoundaryChars,
858
  Compression,
859
  ConnectionTimeout,
860
  ProxyConnection,
861
  ConnectionClosed,
862
  Timeout,
863
  ResourceExhaustion,
864
  TooManyFormDataFiles,
865
  ExceedMaxPayloadSize,
866
  ExceedUriMaxLength,
867
  ExceedMaxSocketDescriptorCount,
868
  InvalidRequestLine,
869
  InvalidHTTPMethod,
870
  InvalidHTTPVersion,
871
  InvalidHeaders,
872
  MultipartParsing,
873
  OpenFile,
874
  Listen,
875
  GetSockName,
876
  UnsupportedAddressFamily,
877
  HTTPParsing,
878
  InvalidRangeHeader,
879
880
  // For internal use only
881
  SSLPeerCouldBeClosed_,
882
};
883
884
std::string to_string(Error error);
885
886
std::ostream &operator<<(std::ostream &os, const Error &obj);
887
888
class Stream {
889
public:
890
12.1k
  virtual ~Stream() = default;
891
892
  virtual bool is_readable() const = 0;
893
  virtual bool wait_readable() const = 0;
894
  virtual bool wait_writable() const = 0;
895
896
  virtual ssize_t read(char *ptr, size_t size) = 0;
897
  virtual ssize_t write(const char *ptr, size_t size) = 0;
898
  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
899
  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
900
  virtual socket_t socket() const = 0;
901
902
  virtual time_t duration() const = 0;
903
904
  ssize_t write(const char *ptr);
905
  ssize_t write(const std::string &s);
906
907
1.06k
  Error get_error() const { return error_; }
908
909
protected:
910
  Error error_ = Error::Success;
911
};
912
913
class TaskQueue {
914
public:
915
0
  TaskQueue() = default;
916
0
  virtual ~TaskQueue() = default;
917
918
  virtual bool enqueue(std::function<void()> fn) = 0;
919
  virtual void shutdown() = 0;
920
921
0
  virtual void on_idle() {}
922
};
923
924
class ThreadPool final : public TaskQueue {
925
public:
926
  explicit ThreadPool(size_t n, size_t mqr = 0)
927
0
      : shutdown_(false), max_queued_requests_(mqr) {
928
0
    threads_.reserve(n);
929
0
    while (n) {
930
0
      threads_.emplace_back(worker(*this));
931
0
      n--;
932
0
    }
933
0
  }
934
935
  ThreadPool(const ThreadPool &) = delete;
936
0
  ~ThreadPool() override = default;
937
938
0
  bool enqueue(std::function<void()> fn) override {
939
0
    {
940
0
      std::unique_lock<std::mutex> lock(mutex_);
941
0
      if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
942
0
        return false;
943
0
      }
944
0
      jobs_.push_back(std::move(fn));
945
0
    }
946
947
0
    cond_.notify_one();
948
0
    return true;
949
0
  }
950
951
0
  void shutdown() override {
952
    // Stop all worker threads...
953
0
    {
954
0
      std::unique_lock<std::mutex> lock(mutex_);
955
0
      shutdown_ = true;
956
0
    }
957
958
0
    cond_.notify_all();
959
960
    // Join...
961
0
    for (auto &t : threads_) {
962
0
      t.join();
963
0
    }
964
0
  }
965
966
private:
967
  struct worker {
968
0
    explicit worker(ThreadPool &pool) : pool_(pool) {}
969
970
0
    void operator()() {
971
0
      for (;;) {
972
0
        std::function<void()> fn;
973
0
        {
974
0
          std::unique_lock<std::mutex> lock(pool_.mutex_);
975
976
0
          pool_.cond_.wait(
977
0
              lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
978
979
0
          if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
980
981
0
          fn = pool_.jobs_.front();
982
0
          pool_.jobs_.pop_front();
983
0
        }
984
985
0
        assert(true == static_cast<bool>(fn));
986
0
        fn();
987
0
      }
988
989
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \
990
    !defined(LIBRESSL_VERSION_NUMBER)
991
      OPENSSL_thread_stop();
992
#endif
993
0
    }
994
995
    ThreadPool &pool_;
996
  };
997
  friend struct worker;
998
999
  std::vector<std::thread> threads_;
1000
  std::list<std::function<void()>> jobs_;
1001
1002
  bool shutdown_;
1003
  size_t max_queued_requests_ = 0;
1004
1005
  std::condition_variable cond_;
1006
  std::mutex mutex_;
1007
};
1008
1009
using Logger = std::function<void(const Request &, const Response &)>;
1010
1011
// Forward declaration for Error type
1012
enum class Error;
1013
using ErrorLogger = std::function<void(const Error &, const Request *)>;
1014
1015
using SocketOptions = std::function<void(socket_t sock)>;
1016
1017
void default_socket_options(socket_t sock);
1018
1019
const char *status_message(int status);
1020
1021
std::string to_string(Error error);
1022
1023
std::ostream &operator<<(std::ostream &os, const Error &obj);
1024
1025
std::string get_bearer_token_auth(const Request &req);
1026
1027
namespace detail {
1028
1029
class MatcherBase {
1030
public:
1031
12
  MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
1032
0
  virtual ~MatcherBase() = default;
1033
1034
1.75k
  const std::string &pattern() const { return pattern_; }
1035
1036
  // Match request path and populate its matches and
1037
  virtual bool match(Request &request) const = 0;
1038
1039
private:
1040
  std::string pattern_;
1041
};
1042
1043
/**
1044
 * Captures parameters in request path and stores them in Request::path_params
1045
 *
1046
 * Capture name is a substring of a pattern from : to /.
1047
 * The rest of the pattern is matched against the request path directly
1048
 * Parameters are captured starting from the next character after
1049
 * the end of the last matched static pattern fragment until the next /.
1050
 *
1051
 * Example pattern:
1052
 * "/path/fragments/:capture/more/fragments/:second_capture"
1053
 * Static fragments:
1054
 * "/path/fragments/", "more/fragments/"
1055
 *
1056
 * Given the following request path:
1057
 * "/path/fragments/:1/more/fragments/:2"
1058
 * the resulting capture will be
1059
 * {{"capture", "1"}, {"second_capture", "2"}}
1060
 */
1061
class PathParamsMatcher final : public MatcherBase {
1062
public:
1063
  PathParamsMatcher(const std::string &pattern);
1064
1065
  bool match(Request &request) const override;
1066
1067
private:
1068
  // Treat segment separators as the end of path parameter capture
1069
  // Does not need to handle query parameters as they are parsed before path
1070
  // matching
1071
  static constexpr char separator = '/';
1072
1073
  // Contains static path fragments to match against, excluding the '/' after
1074
  // path params
1075
  // Fragments are separated by path params
1076
  std::vector<std::string> static_fragments_;
1077
  // Stores the names of the path parameters to be used as keys in the
1078
  // Request::path_params map
1079
  std::vector<std::string> param_names_;
1080
};
1081
1082
/**
1083
 * Performs std::regex_match on request path
1084
 * and stores the result in Request::matches
1085
 *
1086
 * Note that regex match is performed directly on the whole request.
1087
 * This means that wildcard patterns may match multiple path segments with /:
1088
 * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".
1089
 */
1090
class RegexMatcher final : public MatcherBase {
1091
public:
1092
  RegexMatcher(const std::string &pattern)
1093
12
      : MatcherBase(pattern), regex_(pattern) {}
1094
1095
  bool match(Request &request) const override;
1096
1097
private:
1098
  std::regex regex_;
1099
};
1100
1101
int close_socket(socket_t sock);
1102
1103
ssize_t write_headers(Stream &strm, const Headers &headers);
1104
1105
} // namespace detail
1106
1107
class Server {
1108
public:
1109
  using Handler = std::function<void(const Request &, Response &)>;
1110
1111
  using ExceptionHandler =
1112
      std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1113
1114
  enum class HandlerResponse {
1115
    Handled,
1116
    Unhandled,
1117
  };
1118
  using HandlerWithResponse =
1119
      std::function<HandlerResponse(const Request &, Response &)>;
1120
1121
  using HandlerWithContentReader = std::function<void(
1122
      const Request &, Response &, const ContentReader &content_reader)>;
1123
1124
  using Expect100ContinueHandler =
1125
      std::function<int(const Request &, Response &)>;
1126
1127
  Server();
1128
1129
  virtual ~Server();
1130
1131
  virtual bool is_valid() const;
1132
1133
  Server &Get(const std::string &pattern, Handler handler);
1134
  Server &Post(const std::string &pattern, Handler handler);
1135
  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1136
  Server &Put(const std::string &pattern, Handler handler);
1137
  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1138
  Server &Patch(const std::string &pattern, Handler handler);
1139
  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1140
  Server &Delete(const std::string &pattern, Handler handler);
1141
  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1142
  Server &Options(const std::string &pattern, Handler handler);
1143
1144
  bool set_base_dir(const std::string &dir,
1145
                    const std::string &mount_point = std::string());
1146
  bool set_mount_point(const std::string &mount_point, const std::string &dir,
1147
                       Headers headers = Headers());
1148
  bool remove_mount_point(const std::string &mount_point);
1149
  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1150
                                                  const std::string &mime);
1151
  Server &set_default_file_mimetype(const std::string &mime);
1152
  Server &set_file_request_handler(Handler handler);
1153
1154
  template <class ErrorHandlerFunc>
1155
  Server &set_error_handler(ErrorHandlerFunc &&handler) {
1156
    return set_error_handler_core(
1157
        std::forward<ErrorHandlerFunc>(handler),
1158
        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1159
  }
1160
1161
  Server &set_exception_handler(ExceptionHandler handler);
1162
1163
  Server &set_pre_routing_handler(HandlerWithResponse handler);
1164
  Server &set_post_routing_handler(Handler handler);
1165
1166
  Server &set_pre_request_handler(HandlerWithResponse handler);
1167
1168
  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1169
  Server &set_logger(Logger logger);
1170
  Server &set_pre_compression_logger(Logger logger);
1171
  Server &set_error_logger(ErrorLogger error_logger);
1172
1173
  Server &set_address_family(int family);
1174
  Server &set_tcp_nodelay(bool on);
1175
  Server &set_ipv6_v6only(bool on);
1176
  Server &set_socket_options(SocketOptions socket_options);
1177
1178
  Server &set_default_headers(Headers headers);
1179
  Server &
1180
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1181
1182
  Server &set_trusted_proxies(const std::vector<std::string> &proxies);
1183
1184
  Server &set_keep_alive_max_count(size_t count);
1185
  Server &set_keep_alive_timeout(time_t sec);
1186
1187
  Server &set_read_timeout(time_t sec, time_t usec = 0);
1188
  template <class Rep, class Period>
1189
  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1190
1191
  Server &set_write_timeout(time_t sec, time_t usec = 0);
1192
  template <class Rep, class Period>
1193
  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1194
1195
  Server &set_idle_interval(time_t sec, time_t usec = 0);
1196
  template <class Rep, class Period>
1197
  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1198
1199
  Server &set_payload_max_length(size_t length);
1200
1201
  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1202
  int bind_to_any_port(const std::string &host, int socket_flags = 0);
1203
  bool listen_after_bind();
1204
1205
  bool listen(const std::string &host, int port, int socket_flags = 0);
1206
1207
  bool is_running() const;
1208
  void wait_until_ready() const;
1209
  void stop();
1210
  void decommission();
1211
1212
  std::function<TaskQueue *(void)> new_task_queue;
1213
1214
protected:
1215
  bool process_request(Stream &strm, const std::string &remote_addr,
1216
                       int remote_port, const std::string &local_addr,
1217
                       int local_port, bool close_connection,
1218
                       bool &connection_closed,
1219
                       const std::function<void(Request &)> &setup_request);
1220
1221
  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1222
1223
  std::vector<std::string> trusted_proxies_;
1224
1225
  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
1226
  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
1227
  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1228
  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1229
  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1230
  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
1231
  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
1232
  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
1233
  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1234
1235
private:
1236
  using Handlers =
1237
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1238
  using HandlersForContentReader =
1239
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1240
                            HandlerWithContentReader>>;
1241
1242
  static std::unique_ptr<detail::MatcherBase>
1243
  make_matcher(const std::string &pattern);
1244
1245
  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1246
  Server &set_error_handler_core(Handler handler, std::false_type);
1247
1248
  socket_t create_server_socket(const std::string &host, int port,
1249
                                int socket_flags,
1250
                                SocketOptions socket_options) const;
1251
  int bind_internal(const std::string &host, int port, int socket_flags);
1252
  bool listen_internal();
1253
1254
  bool routing(Request &req, Response &res, Stream &strm);
1255
  bool handle_file_request(Request &req, Response &res);
1256
  bool check_if_not_modified(const Request &req, Response &res,
1257
                             const std::string &etag, time_t mtime) const;
1258
  bool check_if_range(Request &req, const std::string &etag,
1259
                      time_t mtime) const;
1260
  bool dispatch_request(Request &req, Response &res,
1261
                        const Handlers &handlers) const;
1262
  bool dispatch_request_for_content_reader(
1263
      Request &req, Response &res, ContentReader content_reader,
1264
      const HandlersForContentReader &handlers) const;
1265
1266
  bool parse_request_line(const char *s, Request &req) const;
1267
  void apply_ranges(const Request &req, Response &res,
1268
                    std::string &content_type, std::string &boundary) const;
1269
  bool write_response(Stream &strm, bool close_connection, Request &req,
1270
                      Response &res);
1271
  bool write_response_with_content(Stream &strm, bool close_connection,
1272
                                   const Request &req, Response &res);
1273
  bool write_response_core(Stream &strm, bool close_connection,
1274
                           const Request &req, Response &res,
1275
                           bool need_apply_ranges);
1276
  bool write_content_with_provider(Stream &strm, const Request &req,
1277
                                   Response &res, const std::string &boundary,
1278
                                   const std::string &content_type);
1279
  bool read_content(Stream &strm, Request &req, Response &res);
1280
  bool read_content_with_content_receiver(Stream &strm, Request &req,
1281
                                          Response &res,
1282
                                          ContentReceiver receiver,
1283
                                          FormDataHeader multipart_header,
1284
                                          ContentReceiver multipart_receiver);
1285
  bool read_content_core(Stream &strm, Request &req, Response &res,
1286
                         ContentReceiver receiver,
1287
                         FormDataHeader multipart_header,
1288
                         ContentReceiver multipart_receiver) const;
1289
1290
  virtual bool process_and_close_socket(socket_t sock);
1291
1292
  void output_log(const Request &req, const Response &res) const;
1293
  void output_pre_compression_log(const Request &req,
1294
                                  const Response &res) const;
1295
  void output_error_log(const Error &err, const Request *req) const;
1296
1297
  std::atomic<bool> is_running_{false};
1298
  std::atomic<bool> is_decommissioned{false};
1299
1300
  struct MountPointEntry {
1301
    std::string mount_point;
1302
    std::string base_dir;
1303
    Headers headers;
1304
  };
1305
  std::vector<MountPointEntry> base_dirs_;
1306
  std::map<std::string, std::string> file_extension_and_mimetype_map_;
1307
  std::string default_file_mimetype_ = "application/octet-stream";
1308
  Handler file_request_handler_;
1309
1310
  Handlers get_handlers_;
1311
  Handlers post_handlers_;
1312
  HandlersForContentReader post_handlers_for_content_reader_;
1313
  Handlers put_handlers_;
1314
  HandlersForContentReader put_handlers_for_content_reader_;
1315
  Handlers patch_handlers_;
1316
  HandlersForContentReader patch_handlers_for_content_reader_;
1317
  Handlers delete_handlers_;
1318
  HandlersForContentReader delete_handlers_for_content_reader_;
1319
  Handlers options_handlers_;
1320
1321
  HandlerWithResponse error_handler_;
1322
  ExceptionHandler exception_handler_;
1323
  HandlerWithResponse pre_routing_handler_;
1324
  Handler post_routing_handler_;
1325
  HandlerWithResponse pre_request_handler_;
1326
  Expect100ContinueHandler expect_100_continue_handler_;
1327
1328
  mutable std::mutex logger_mutex_;
1329
  Logger logger_;
1330
  Logger pre_compression_logger_;
1331
  ErrorLogger error_logger_;
1332
1333
  int address_family_ = AF_UNSPEC;
1334
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1335
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1336
  SocketOptions socket_options_ = default_socket_options;
1337
1338
  Headers default_headers_;
1339
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1340
      detail::write_headers;
1341
};
1342
1343
class Result {
1344
public:
1345
  Result() = default;
1346
  Result(std::unique_ptr<Response> &&res, Error err,
1347
         Headers &&request_headers = Headers{})
1348
      : res_(std::move(res)), err_(err),
1349
0
        request_headers_(std::move(request_headers)) {}
1350
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1351
  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1352
         int ssl_error)
1353
      : res_(std::move(res)), err_(err),
1354
        request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
1355
  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1356
         int ssl_error, unsigned long ssl_openssl_error)
1357
      : res_(std::move(res)), err_(err),
1358
        request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1359
        ssl_openssl_error_(ssl_openssl_error) {}
1360
#endif
1361
  // Response
1362
0
  operator bool() const { return res_ != nullptr; }
1363
0
  bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1364
0
  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1365
0
  const Response &value() const { return *res_; }
1366
0
  Response &value() { return *res_; }
1367
0
  const Response &operator*() const { return *res_; }
1368
0
  Response &operator*() { return *res_; }
1369
0
  const Response *operator->() const { return res_.get(); }
1370
0
  Response *operator->() { return res_.get(); }
1371
1372
  // Error
1373
0
  Error error() const { return err_; }
1374
1375
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1376
  // SSL Error
1377
  int ssl_error() const { return ssl_error_; }
1378
  // OpenSSL Error
1379
  unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }
1380
#endif
1381
1382
  // Request Headers
1383
  bool has_request_header(const std::string &key) const;
1384
  std::string get_request_header_value(const std::string &key,
1385
                                       const char *def = "",
1386
                                       size_t id = 0) const;
1387
  size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
1388
                                      size_t id = 0) const;
1389
  size_t get_request_header_value_count(const std::string &key) const;
1390
1391
private:
1392
  std::unique_ptr<Response> res_;
1393
  Error err_ = Error::Unknown;
1394
  Headers request_headers_;
1395
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1396
  int ssl_error_ = 0;
1397
  unsigned long ssl_openssl_error_ = 0;
1398
#endif
1399
};
1400
1401
struct ClientConnection {
1402
  socket_t sock = INVALID_SOCKET;
1403
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1404
  SSL *ssl = nullptr;
1405
#endif
1406
1407
0
  bool is_open() const { return sock != INVALID_SOCKET; }
1408
1409
  ClientConnection() = default;
1410
1411
0
  ~ClientConnection() {
1412
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1413
0
    if (ssl) {
1414
0
      SSL_free(ssl);
1415
0
      ssl = nullptr;
1416
0
    }
1417
0
#endif
1418
0
    if (sock != INVALID_SOCKET) {
1419
0
      detail::close_socket(sock);
1420
0
      sock = INVALID_SOCKET;
1421
0
    }
1422
0
  }
1423
1424
  ClientConnection(const ClientConnection &) = delete;
1425
  ClientConnection &operator=(const ClientConnection &) = delete;
1426
1427
  ClientConnection(ClientConnection &&other) noexcept
1428
      : sock(other.sock)
1429
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1430
        ,
1431
        ssl(other.ssl)
1432
#endif
1433
0
  {
1434
0
    other.sock = INVALID_SOCKET;
1435
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1436
0
    other.ssl = nullptr;
1437
0
#endif
1438
0
  }
1439
1440
0
  ClientConnection &operator=(ClientConnection &&other) noexcept {
1441
0
    if (this != &other) {
1442
0
      sock = other.sock;
1443
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1444
0
      ssl = other.ssl;
1445
0
#endif
1446
0
      other.sock = INVALID_SOCKET;
1447
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1448
0
      other.ssl = nullptr;
1449
0
#endif
1450
0
    }
1451
0
    return *this;
1452
0
  }
1453
};
1454
1455
namespace detail {
1456
1457
struct ChunkedDecoder;
1458
1459
struct BodyReader {
1460
  Stream *stream = nullptr;
1461
  size_t content_length = 0;
1462
  size_t bytes_read = 0;
1463
  bool chunked = false;
1464
  bool eof = false;
1465
  std::unique_ptr<ChunkedDecoder> chunked_decoder;
1466
  Error last_error = Error::Success;
1467
1468
  ssize_t read(char *buf, size_t len);
1469
0
  bool has_error() const { return last_error != Error::Success; }
1470
};
1471
1472
inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
1473
3.46k
                                 size_t len) {
1474
3.46k
  (void)stream;
1475
3.46k
  return br.read(buf, len);
1476
3.46k
}
1477
1478
class decompressor;
1479
1480
} // namespace detail
1481
1482
class ClientImpl {
1483
public:
1484
  explicit ClientImpl(const std::string &host);
1485
1486
  explicit ClientImpl(const std::string &host, int port);
1487
1488
  explicit ClientImpl(const std::string &host, int port,
1489
                      const std::string &client_cert_path,
1490
                      const std::string &client_key_path);
1491
1492
  virtual ~ClientImpl();
1493
1494
  virtual bool is_valid() const;
1495
1496
  struct StreamHandle {
1497
    std::unique_ptr<Response> response;
1498
    Error error = Error::Success;
1499
1500
    StreamHandle() = default;
1501
    StreamHandle(const StreamHandle &) = delete;
1502
    StreamHandle &operator=(const StreamHandle &) = delete;
1503
    StreamHandle(StreamHandle &&) = default;
1504
    StreamHandle &operator=(StreamHandle &&) = default;
1505
    ~StreamHandle() = default;
1506
1507
0
    bool is_valid() const {
1508
0
      return response != nullptr && error == Error::Success;
1509
0
    }
1510
1511
    ssize_t read(char *buf, size_t len);
1512
    void parse_trailers_if_needed();
1513
0
    Error get_read_error() const { return body_reader_.last_error; }
1514
0
    bool has_read_error() const { return body_reader_.has_error(); }
1515
1516
    bool trailers_parsed_ = false;
1517
1518
  private:
1519
    friend class ClientImpl;
1520
1521
    ssize_t read_with_decompression(char *buf, size_t len);
1522
1523
    std::unique_ptr<ClientConnection> connection_;
1524
    std::unique_ptr<Stream> socket_stream_;
1525
    Stream *stream_ = nullptr;
1526
    detail::BodyReader body_reader_;
1527
1528
    std::unique_ptr<detail::decompressor> decompressor_;
1529
    std::string decompress_buffer_;
1530
    size_t decompress_offset_ = 0;
1531
  };
1532
1533
  // clang-format off
1534
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1535
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1536
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1537
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1538
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1539
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1540
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1541
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1542
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1543
1544
  Result Head(const std::string &path);
1545
  Result Head(const std::string &path, const Headers &headers);
1546
1547
  Result Post(const std::string &path);
1548
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1549
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1550
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1551
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1552
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1553
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1554
  Result Post(const std::string &path, const Params &params);
1555
  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1556
  Result Post(const std::string &path, const Headers &headers);
1557
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1558
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1559
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1560
  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);
1561
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1562
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1563
  Result Post(const std::string &path, const Headers &headers, const Params &params);
1564
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1565
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1566
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1567
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1568
1569
  Result Put(const std::string &path);
1570
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1571
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1572
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1573
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1574
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1575
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1576
  Result Put(const std::string &path, const Params &params);
1577
  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1578
  Result Put(const std::string &path, const Headers &headers);
1579
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1580
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1581
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1582
  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);
1583
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1584
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1585
  Result Put(const std::string &path, const Headers &headers, const Params &params);
1586
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1587
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1588
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1589
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1590
1591
  Result Patch(const std::string &path);
1592
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1593
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1594
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1595
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1596
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1597
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1598
  Result Patch(const std::string &path, const Params &params);
1599
  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1600
  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
1601
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1602
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1603
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1604
  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);
1605
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1606
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1607
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
1608
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1609
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1610
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1611
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1612
1613
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1614
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1615
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1616
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1617
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1618
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1619
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1620
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1621
1622
  Result Options(const std::string &path);
1623
  Result Options(const std::string &path, const Headers &headers);
1624
  // clang-format on
1625
1626
  // Streaming API: Open a stream for reading response body incrementally
1627
  // Socket ownership is transferred to StreamHandle for true streaming
1628
  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
1629
  StreamHandle open_stream(const std::string &method, const std::string &path,
1630
                           const Params &params = {},
1631
                           const Headers &headers = {},
1632
                           const std::string &body = {},
1633
                           const std::string &content_type = {});
1634
1635
  bool send(Request &req, Response &res, Error &error);
1636
  Result send(const Request &req);
1637
1638
  void stop();
1639
1640
  std::string host() const;
1641
  int port() const;
1642
1643
  size_t is_socket_open() const;
1644
  socket_t socket() const;
1645
1646
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1647
1648
  void set_default_headers(Headers headers);
1649
1650
  void
1651
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1652
1653
  void set_address_family(int family);
1654
  void set_tcp_nodelay(bool on);
1655
  void set_ipv6_v6only(bool on);
1656
  void set_socket_options(SocketOptions socket_options);
1657
1658
  void set_connection_timeout(time_t sec, time_t usec = 0);
1659
  template <class Rep, class Period>
1660
  void
1661
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1662
1663
  void set_read_timeout(time_t sec, time_t usec = 0);
1664
  template <class Rep, class Period>
1665
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1666
1667
  void set_write_timeout(time_t sec, time_t usec = 0);
1668
  template <class Rep, class Period>
1669
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1670
1671
  void set_max_timeout(time_t msec);
1672
  template <class Rep, class Period>
1673
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1674
1675
  void set_basic_auth(const std::string &username, const std::string &password);
1676
  void set_bearer_token_auth(const std::string &token);
1677
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1678
  void set_digest_auth(const std::string &username,
1679
                       const std::string &password);
1680
#endif
1681
1682
  void set_keep_alive(bool on);
1683
  void set_follow_location(bool on);
1684
1685
  void set_path_encode(bool on);
1686
1687
  void set_compress(bool on);
1688
1689
  void set_decompress(bool on);
1690
1691
  void set_interface(const std::string &intf);
1692
1693
  void set_proxy(const std::string &host, int port);
1694
  void set_proxy_basic_auth(const std::string &username,
1695
                            const std::string &password);
1696
  void set_proxy_bearer_token_auth(const std::string &token);
1697
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1698
  void set_proxy_digest_auth(const std::string &username,
1699
                             const std::string &password);
1700
#endif
1701
1702
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1703
  void set_ca_cert_path(const std::string &ca_cert_file_path,
1704
                        const std::string &ca_cert_dir_path = std::string());
1705
  void set_ca_cert_store(X509_STORE *ca_cert_store);
1706
  X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1707
#endif
1708
1709
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1710
  void enable_server_certificate_verification(bool enabled);
1711
  void enable_server_hostname_verification(bool enabled);
1712
  void set_server_certificate_verifier(
1713
      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1714
#endif
1715
1716
  void set_logger(Logger logger);
1717
  void set_error_logger(ErrorLogger error_logger);
1718
1719
protected:
1720
  struct Socket {
1721
    socket_t sock = INVALID_SOCKET;
1722
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1723
    SSL *ssl = nullptr;
1724
#endif
1725
1726
0
    bool is_open() const { return sock != INVALID_SOCKET; }
1727
  };
1728
1729
  virtual bool create_and_connect_socket(Socket &socket, Error &error);
1730
  virtual bool ensure_socket_connection(Socket &socket, Error &error);
1731
1732
  // All of:
1733
  //   shutdown_ssl
1734
  //   shutdown_socket
1735
  //   close_socket
1736
  // should ONLY be called when socket_mutex_ is locked.
1737
  // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1738
  // with a DIFFERENT thread sending requests using that socket.
1739
  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1740
  void shutdown_socket(Socket &socket) const;
1741
  void close_socket(Socket &socket);
1742
1743
  bool process_request(Stream &strm, Request &req, Response &res,
1744
                       bool close_connection, Error &error);
1745
1746
  bool write_content_with_provider(Stream &strm, const Request &req,
1747
                                   Error &error) const;
1748
1749
  void copy_settings(const ClientImpl &rhs);
1750
1751
  void output_log(const Request &req, const Response &res) const;
1752
  void output_error_log(const Error &err, const Request *req) const;
1753
1754
  // Socket endpoint information
1755
  const std::string host_;
1756
  const int port_;
1757
1758
  // Current open socket
1759
  Socket socket_;
1760
  mutable std::mutex socket_mutex_;
1761
  std::recursive_mutex request_mutex_;
1762
1763
  // These are all protected under socket_mutex
1764
  size_t socket_requests_in_flight_ = 0;
1765
  std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1766
  bool socket_should_be_closed_when_request_is_done_ = false;
1767
1768
  // Hostname-IP map
1769
  std::map<std::string, std::string> addr_map_;
1770
1771
  // Default headers
1772
  Headers default_headers_;
1773
1774
  // Header writer
1775
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1776
      detail::write_headers;
1777
1778
  // Settings
1779
  std::string client_cert_path_;
1780
  std::string client_key_path_;
1781
1782
  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1783
  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1784
  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1785
  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1786
  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1787
  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1788
  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
1789
1790
  std::string basic_auth_username_;
1791
  std::string basic_auth_password_;
1792
  std::string bearer_token_auth_token_;
1793
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1794
  std::string digest_auth_username_;
1795
  std::string digest_auth_password_;
1796
#endif
1797
1798
  bool keep_alive_ = false;
1799
  bool follow_location_ = false;
1800
1801
  bool path_encode_ = true;
1802
1803
  int address_family_ = AF_UNSPEC;
1804
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1805
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1806
  SocketOptions socket_options_ = nullptr;
1807
1808
  bool compress_ = false;
1809
  bool decompress_ = true;
1810
1811
  std::string interface_;
1812
1813
  std::string proxy_host_;
1814
  int proxy_port_ = -1;
1815
1816
  std::string proxy_basic_auth_username_;
1817
  std::string proxy_basic_auth_password_;
1818
  std::string proxy_bearer_token_auth_token_;
1819
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1820
  std::string proxy_digest_auth_username_;
1821
  std::string proxy_digest_auth_password_;
1822
#endif
1823
1824
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1825
  std::string ca_cert_file_path_;
1826
  std::string ca_cert_dir_path_;
1827
1828
  X509_STORE *ca_cert_store_ = nullptr;
1829
#endif
1830
1831
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1832
  bool server_certificate_verification_ = true;
1833
  bool server_hostname_verification_ = true;
1834
  std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
1835
#endif
1836
1837
  mutable std::mutex logger_mutex_;
1838
  Logger logger_;
1839
  ErrorLogger error_logger_;
1840
1841
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1842
  int last_ssl_error_ = 0;
1843
  unsigned long last_openssl_error_ = 0;
1844
#endif
1845
1846
private:
1847
  bool send_(Request &req, Response &res, Error &error);
1848
  Result send_(Request &&req);
1849
1850
  socket_t create_client_socket(Error &error) const;
1851
  bool read_response_line(Stream &strm, const Request &req,
1852
                          Response &res) const;
1853
  bool write_request(Stream &strm, Request &req, bool close_connection,
1854
                     Error &error);
1855
  void prepare_default_headers(Request &r, bool for_stream,
1856
                               const std::string &ct);
1857
  bool redirect(Request &req, Response &res, Error &error);
1858
  bool create_redirect_client(const std::string &scheme,
1859
                              const std::string &host, int port, Request &req,
1860
                              Response &res, const std::string &path,
1861
                              const std::string &location, Error &error);
1862
  template <typename ClientType> void setup_redirect_client(ClientType &client);
1863
  bool handle_request(Stream &strm, Request &req, Response &res,
1864
                      bool close_connection, Error &error);
1865
  std::unique_ptr<Response> send_with_content_provider_and_receiver(
1866
      Request &req, const char *body, size_t content_length,
1867
      ContentProvider content_provider,
1868
      ContentProviderWithoutLength content_provider_without_length,
1869
      const std::string &content_type, ContentReceiver content_receiver,
1870
      Error &error);
1871
  Result send_with_content_provider_and_receiver(
1872
      const std::string &method, const std::string &path,
1873
      const Headers &headers, const char *body, size_t content_length,
1874
      ContentProvider content_provider,
1875
      ContentProviderWithoutLength content_provider_without_length,
1876
      const std::string &content_type, ContentReceiver content_receiver,
1877
      UploadProgress progress);
1878
  ContentProviderWithoutLength get_multipart_content_provider(
1879
      const std::string &boundary, const UploadFormDataItems &items,
1880
      const FormDataProviderItems &provider_items) const;
1881
1882
  virtual bool
1883
  process_socket(const Socket &socket,
1884
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
1885
                 std::function<bool(Stream &strm)> callback);
1886
  virtual bool is_ssl() const;
1887
1888
  void transfer_socket_ownership_to_handle(StreamHandle &handle);
1889
};
1890
1891
class Client {
1892
public:
1893
  // Universal interface
1894
  explicit Client(const std::string &scheme_host_port);
1895
1896
  explicit Client(const std::string &scheme_host_port,
1897
                  const std::string &client_cert_path,
1898
                  const std::string &client_key_path);
1899
1900
  // HTTP only interface
1901
  explicit Client(const std::string &host, int port);
1902
1903
  explicit Client(const std::string &host, int port,
1904
                  const std::string &client_cert_path,
1905
                  const std::string &client_key_path);
1906
1907
  Client(Client &&) = default;
1908
  Client &operator=(Client &&) = default;
1909
1910
  ~Client();
1911
1912
  bool is_valid() const;
1913
1914
  // clang-format off
1915
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1916
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1917
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1918
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1919
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1920
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1921
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1922
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1923
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1924
1925
  Result Head(const std::string &path);
1926
  Result Head(const std::string &path, const Headers &headers);
1927
1928
  Result Post(const std::string &path);
1929
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1930
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1931
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1932
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1933
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1934
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1935
  Result Post(const std::string &path, const Params &params);
1936
  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1937
  Result Post(const std::string &path, const Headers &headers);
1938
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1939
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1940
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1941
  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);
1942
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1943
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1944
  Result Post(const std::string &path, const Headers &headers, const Params &params);
1945
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1946
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1947
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1948
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1949
1950
  Result Put(const std::string &path);
1951
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1952
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1953
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1954
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1955
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1956
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1957
  Result Put(const std::string &path, const Params &params);
1958
  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1959
  Result Put(const std::string &path, const Headers &headers);
1960
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1961
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1962
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1963
  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);
1964
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1965
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1966
  Result Put(const std::string &path, const Headers &headers, const Params &params);
1967
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1968
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1969
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1970
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1971
1972
  Result Patch(const std::string &path);
1973
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1974
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1975
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1976
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1977
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1978
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1979
  Result Patch(const std::string &path, const Params &params);
1980
  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1981
  Result Patch(const std::string &path, const Headers &headers);
1982
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1983
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1984
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1985
  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);
1986
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1987
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
1988
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
1989
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
1990
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
1991
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1992
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1993
1994
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1995
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1996
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1997
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1998
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1999
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2000
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2001
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2002
2003
  Result Options(const std::string &path);
2004
  Result Options(const std::string &path, const Headers &headers);
2005
  // clang-format on
2006
2007
  // Streaming API: Open a stream for reading response body incrementally
2008
  // Socket ownership is transferred to StreamHandle for true streaming
2009
  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
2010
  ClientImpl::StreamHandle open_stream(const std::string &method,
2011
                                       const std::string &path,
2012
                                       const Params &params = {},
2013
                                       const Headers &headers = {},
2014
                                       const std::string &body = {},
2015
                                       const std::string &content_type = {});
2016
2017
  bool send(Request &req, Response &res, Error &error);
2018
  Result send(const Request &req);
2019
2020
  void stop();
2021
2022
  std::string host() const;
2023
  int port() const;
2024
2025
  size_t is_socket_open() const;
2026
  socket_t socket() const;
2027
2028
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2029
2030
  void set_default_headers(Headers headers);
2031
2032
  void
2033
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2034
2035
  void set_address_family(int family);
2036
  void set_tcp_nodelay(bool on);
2037
  void set_socket_options(SocketOptions socket_options);
2038
2039
  void set_connection_timeout(time_t sec, time_t usec = 0);
2040
  template <class Rep, class Period>
2041
  void
2042
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2043
2044
  void set_read_timeout(time_t sec, time_t usec = 0);
2045
  template <class Rep, class Period>
2046
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2047
2048
  void set_write_timeout(time_t sec, time_t usec = 0);
2049
  template <class Rep, class Period>
2050
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2051
2052
  void set_max_timeout(time_t msec);
2053
  template <class Rep, class Period>
2054
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2055
2056
  void set_basic_auth(const std::string &username, const std::string &password);
2057
  void set_bearer_token_auth(const std::string &token);
2058
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2059
  void set_digest_auth(const std::string &username,
2060
                       const std::string &password);
2061
#endif
2062
2063
  void set_keep_alive(bool on);
2064
  void set_follow_location(bool on);
2065
2066
  void set_path_encode(bool on);
2067
  void set_url_encode(bool on);
2068
2069
  void set_compress(bool on);
2070
2071
  void set_decompress(bool on);
2072
2073
  void set_interface(const std::string &intf);
2074
2075
  void set_proxy(const std::string &host, int port);
2076
  void set_proxy_basic_auth(const std::string &username,
2077
                            const std::string &password);
2078
  void set_proxy_bearer_token_auth(const std::string &token);
2079
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2080
  void set_proxy_digest_auth(const std::string &username,
2081
                             const std::string &password);
2082
#endif
2083
2084
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2085
  void enable_server_certificate_verification(bool enabled);
2086
  void enable_server_hostname_verification(bool enabled);
2087
  void set_server_certificate_verifier(
2088
      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
2089
#endif
2090
2091
  void set_logger(Logger logger);
2092
  void set_error_logger(ErrorLogger error_logger);
2093
2094
  // SSL
2095
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2096
  void set_ca_cert_path(const std::string &ca_cert_file_path,
2097
                        const std::string &ca_cert_dir_path = std::string());
2098
2099
  void set_ca_cert_store(X509_STORE *ca_cert_store);
2100
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2101
2102
  long get_openssl_verify_result() const;
2103
2104
  SSL_CTX *ssl_context() const;
2105
#endif
2106
2107
private:
2108
  std::unique_ptr<ClientImpl> cli_;
2109
2110
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2111
  bool is_ssl_ = false;
2112
#endif
2113
};
2114
2115
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2116
class SSLServer : public Server {
2117
public:
2118
  SSLServer(const char *cert_path, const char *private_key_path,
2119
            const char *client_ca_cert_file_path = nullptr,
2120
            const char *client_ca_cert_dir_path = nullptr,
2121
            const char *private_key_password = nullptr);
2122
2123
  SSLServer(X509 *cert, EVP_PKEY *private_key,
2124
            X509_STORE *client_ca_cert_store = nullptr);
2125
2126
  SSLServer(
2127
      const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
2128
2129
  ~SSLServer() override;
2130
2131
  bool is_valid() const override;
2132
2133
  SSL_CTX *ssl_context() const;
2134
2135
  void update_certs(X509 *cert, EVP_PKEY *private_key,
2136
                    X509_STORE *client_ca_cert_store = nullptr);
2137
2138
  int ssl_last_error() const { return last_ssl_error_; }
2139
2140
private:
2141
  bool process_and_close_socket(socket_t sock) override;
2142
2143
  STACK_OF(X509_NAME) * extract_ca_names_from_x509_store(X509_STORE *store);
2144
2145
  SSL_CTX *ctx_;
2146
  std::mutex ctx_mutex_;
2147
2148
  int last_ssl_error_ = 0;
2149
};
2150
2151
class SSLClient final : public ClientImpl {
2152
public:
2153
  explicit SSLClient(const std::string &host);
2154
2155
  explicit SSLClient(const std::string &host, int port);
2156
2157
  explicit SSLClient(const std::string &host, int port,
2158
                     const std::string &client_cert_path,
2159
                     const std::string &client_key_path,
2160
                     const std::string &private_key_password = std::string());
2161
2162
  explicit SSLClient(const std::string &host, int port, X509 *client_cert,
2163
                     EVP_PKEY *client_key,
2164
                     const std::string &private_key_password = std::string());
2165
2166
  ~SSLClient() override;
2167
2168
  bool is_valid() const override;
2169
2170
  void set_ca_cert_store(X509_STORE *ca_cert_store);
2171
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2172
2173
  long get_openssl_verify_result() const;
2174
2175
  SSL_CTX *ssl_context() const;
2176
2177
private:
2178
  bool create_and_connect_socket(Socket &socket, Error &error) override;
2179
  bool ensure_socket_connection(Socket &socket, Error &error) override;
2180
  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
2181
  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
2182
2183
  bool
2184
  process_socket(const Socket &socket,
2185
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
2186
                 std::function<bool(Stream &strm)> callback) override;
2187
  bool is_ssl() const override;
2188
2189
  bool connect_with_proxy(
2190
      Socket &sock,
2191
      std::chrono::time_point<std::chrono::steady_clock> start_time,
2192
      Response &res, bool &success, Error &error);
2193
  bool initialize_ssl(Socket &socket, Error &error);
2194
2195
  bool load_certs();
2196
2197
  bool verify_host(X509 *server_cert) const;
2198
  bool verify_host_with_subject_alt_name(X509 *server_cert) const;
2199
  bool verify_host_with_common_name(X509 *server_cert) const;
2200
  bool check_host_name(const char *pattern, size_t pattern_len) const;
2201
2202
  SSL_CTX *ctx_;
2203
  std::mutex ctx_mutex_;
2204
  std::once_flag initialize_cert_;
2205
2206
  std::vector<std::string> host_components_;
2207
2208
  long verify_result_ = 0;
2209
2210
  friend class ClientImpl;
2211
};
2212
#endif
2213
2214
/*
2215
 * Implementation of template methods.
2216
 */
2217
2218
namespace detail {
2219
2220
template <typename T, typename U>
2221
inline void duration_to_sec_and_usec(const T &duration, U callback) {
2222
  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2223
  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2224
                  duration - std::chrono::seconds(sec))
2225
                  .count();
2226
  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2227
}
2228
2229
1.25k
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2230
1.25k
  return N - 1;
2231
1.25k
}
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
2229
1.25k
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2230
1.25k
  return N - 1;
2231
1.25k
}
Unexecuted instantiation: unsigned long httplib::detail::str_len<8ul>(char const (&) [8ul])
2232
2233
1.79k
inline bool is_numeric(const std::string &str) {
2234
1.79k
  return !str.empty() &&
2235
1.79k
         std::all_of(str.cbegin(), str.cend(),
2236
13.2k
                     [](unsigned char c) { return std::isdigit(c); });
2237
1.79k
}
2238
2239
inline size_t get_header_value_u64(const Headers &headers,
2240
                                   const std::string &key, size_t def,
2241
1.79k
                                   size_t id, bool &is_invalid_value) {
2242
1.79k
  is_invalid_value = false;
2243
1.79k
  auto rng = headers.equal_range(key);
2244
1.79k
  auto it = rng.first;
2245
1.79k
  std::advance(it, static_cast<ssize_t>(id));
2246
1.79k
  if (it != rng.second) {
2247
1.79k
    if (is_numeric(it->second)) {
2248
1.78k
      return std::strtoull(it->second.data(), nullptr, 10);
2249
1.78k
    } else {
2250
7
      is_invalid_value = true;
2251
7
    }
2252
1.79k
  }
2253
7
  return def;
2254
1.79k
}
2255
2256
inline size_t get_header_value_u64(const Headers &headers,
2257
                                   const std::string &key, size_t def,
2258
407
                                   size_t id) {
2259
407
  auto dummy = false;
2260
407
  return get_header_value_u64(headers, key, def, id, dummy);
2261
407
}
2262
2263
} // namespace detail
2264
2265
inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
2266
407
                                            size_t id) const {
2267
407
  return detail::get_header_value_u64(headers, key, def, id);
2268
407
}
2269
2270
inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
2271
0
                                             size_t id) const {
2272
0
  return detail::get_header_value_u64(headers, key, def, id);
2273
0
}
2274
2275
namespace detail {
2276
2277
inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
2278
0
                                const void *optval, socklen_t optlen) {
2279
0
  return setsockopt(sock, level, optname,
2280
#ifdef _WIN32
2281
                    reinterpret_cast<const char *>(optval),
2282
#else
2283
0
                    optval,
2284
0
#endif
2285
0
                    optlen) == 0;
2286
0
}
2287
2288
0
inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
2289
0
  return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
2290
0
}
2291
2292
inline bool set_socket_opt_time(socket_t sock, int level, int optname,
2293
0
                                time_t sec, time_t usec) {
2294
#ifdef _WIN32
2295
  auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
2296
#else
2297
0
  timeval timeout;
2298
0
  timeout.tv_sec = static_cast<long>(sec);
2299
0
  timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
2300
0
#endif
2301
0
  return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
2302
0
}
2303
2304
} // namespace detail
2305
2306
0
inline void default_socket_options(socket_t sock) {
2307
0
  detail::set_socket_opt(sock, SOL_SOCKET,
2308
0
#ifdef SO_REUSEPORT
2309
0
                         SO_REUSEPORT,
2310
#else
2311
                         SO_REUSEADDR,
2312
#endif
2313
0
                         1);
2314
0
}
2315
2316
0
inline std::string get_bearer_token_auth(const Request &req) {
2317
0
  if (req.has_header("Authorization")) {
2318
0
    constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2319
0
    return req.get_header_value("Authorization")
2320
0
        .substr(bearer_header_prefix_len);
2321
0
  }
2322
0
  return "";
2323
0
}
2324
2325
template <class Rep, class Period>
2326
inline Server &
2327
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2328
  detail::duration_to_sec_and_usec(
2329
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2330
  return *this;
2331
}
2332
2333
template <class Rep, class Period>
2334
inline Server &
2335
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2336
  detail::duration_to_sec_and_usec(
2337
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2338
  return *this;
2339
}
2340
2341
template <class Rep, class Period>
2342
inline Server &
2343
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
2344
  detail::duration_to_sec_and_usec(
2345
      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2346
  return *this;
2347
}
2348
2349
inline size_t Result::get_request_header_value_u64(const std::string &key,
2350
                                                   size_t def,
2351
0
                                                   size_t id) const {
2352
0
  return detail::get_header_value_u64(request_headers_, key, def, id);
2353
0
}
2354
2355
template <class Rep, class Period>
2356
inline void ClientImpl::set_connection_timeout(
2357
    const std::chrono::duration<Rep, Period> &duration) {
2358
  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2359
    set_connection_timeout(sec, usec);
2360
  });
2361
}
2362
2363
template <class Rep, class Period>
2364
inline void ClientImpl::set_read_timeout(
2365
    const std::chrono::duration<Rep, Period> &duration) {
2366
  detail::duration_to_sec_and_usec(
2367
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2368
}
2369
2370
template <class Rep, class Period>
2371
inline void ClientImpl::set_write_timeout(
2372
    const std::chrono::duration<Rep, Period> &duration) {
2373
  detail::duration_to_sec_and_usec(
2374
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2375
}
2376
2377
template <class Rep, class Period>
2378
inline void ClientImpl::set_max_timeout(
2379
    const std::chrono::duration<Rep, Period> &duration) {
2380
  auto msec =
2381
      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2382
  set_max_timeout(msec);
2383
}
2384
2385
template <class Rep, class Period>
2386
inline void Client::set_connection_timeout(
2387
    const std::chrono::duration<Rep, Period> &duration) {
2388
  cli_->set_connection_timeout(duration);
2389
}
2390
2391
template <class Rep, class Period>
2392
inline void
2393
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2394
  cli_->set_read_timeout(duration);
2395
}
2396
2397
template <class Rep, class Period>
2398
inline void
2399
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2400
  cli_->set_write_timeout(duration);
2401
}
2402
2403
0
inline void Client::set_max_timeout(time_t msec) {
2404
0
  cli_->set_max_timeout(msec);
2405
0
}
2406
2407
template <class Rep, class Period>
2408
inline void
2409
Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2410
  cli_->set_max_timeout(duration);
2411
}
2412
2413
/*
2414
 * Forward declarations and types that will be part of the .h file if split into
2415
 * .h + .cc.
2416
 */
2417
2418
std::string hosted_at(const std::string &hostname);
2419
2420
void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2421
2422
// JavaScript-style URL encoding/decoding functions
2423
std::string encode_uri_component(const std::string &value);
2424
std::string encode_uri(const std::string &value);
2425
std::string decode_uri_component(const std::string &value);
2426
std::string decode_uri(const std::string &value);
2427
2428
// RFC 3986 compliant URL component encoding/decoding functions
2429
std::string encode_path_component(const std::string &component);
2430
std::string decode_path_component(const std::string &component);
2431
std::string encode_query_component(const std::string &component,
2432
                                   bool space_as_plus = true);
2433
std::string decode_query_component(const std::string &component,
2434
                                   bool plus_as_space = true);
2435
2436
std::string append_query_params(const std::string &path, const Params &params);
2437
2438
std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2439
2440
std::pair<std::string, std::string>
2441
make_basic_authentication_header(const std::string &username,
2442
                                 const std::string &password,
2443
                                 bool is_proxy = false);
2444
2445
namespace detail {
2446
2447
#if defined(_WIN32)
2448
inline std::wstring u8string_to_wstring(const char *s) {
2449
  if (!s) { return std::wstring(); }
2450
2451
  auto len = static_cast<int>(strlen(s));
2452
  if (!len) { return std::wstring(); }
2453
2454
  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2455
  if (!wlen) { return std::wstring(); }
2456
2457
  std::wstring ws;
2458
  ws.resize(wlen);
2459
  wlen = ::MultiByteToWideChar(
2460
      CP_UTF8, 0, s, len,
2461
      const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2462
  if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
2463
  return ws;
2464
}
2465
#endif
2466
2467
struct FileStat {
2468
  FileStat(const std::string &path);
2469
  bool is_file() const;
2470
  bool is_dir() const;
2471
  time_t mtime() const;
2472
  size_t size() const;
2473
2474
private:
2475
#if defined(_WIN32)
2476
  struct _stat st_;
2477
#else
2478
  struct stat st_;
2479
#endif
2480
  int ret_ = -1;
2481
};
2482
2483
std::string make_host_and_port_string(const std::string &host, int port,
2484
                                      bool is_ssl);
2485
2486
std::string trim_copy(const std::string &s);
2487
2488
void divide(
2489
    const char *data, std::size_t size, char d,
2490
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2491
        fn);
2492
2493
void divide(
2494
    const std::string &str, char d,
2495
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2496
        fn);
2497
2498
void split(const char *b, const char *e, char d,
2499
           std::function<void(const char *, const char *)> fn);
2500
2501
void split(const char *b, const char *e, char d, size_t m,
2502
           std::function<void(const char *, const char *)> fn);
2503
2504
bool process_client_socket(
2505
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2506
    time_t write_timeout_sec, time_t write_timeout_usec,
2507
    time_t max_timeout_msec,
2508
    std::chrono::time_point<std::chrono::steady_clock> start_time,
2509
    std::function<bool(Stream &)> callback);
2510
2511
socket_t create_client_socket(const std::string &host, const std::string &ip,
2512
                              int port, int address_family, bool tcp_nodelay,
2513
                              bool ipv6_v6only, SocketOptions socket_options,
2514
                              time_t connection_timeout_sec,
2515
                              time_t connection_timeout_usec,
2516
                              time_t read_timeout_sec, time_t read_timeout_usec,
2517
                              time_t write_timeout_sec,
2518
                              time_t write_timeout_usec,
2519
                              const std::string &intf, Error &error);
2520
2521
const char *get_header_value(const Headers &headers, const std::string &key,
2522
                             const char *def, size_t id);
2523
2524
std::string params_to_query_str(const Params &params);
2525
2526
void parse_query_text(const char *data, std::size_t size, Params &params);
2527
2528
void parse_query_text(const std::string &s, Params &params);
2529
2530
bool parse_multipart_boundary(const std::string &content_type,
2531
                              std::string &boundary);
2532
2533
bool parse_range_header(const std::string &s, Ranges &ranges);
2534
2535
bool parse_accept_header(const std::string &s,
2536
                         std::vector<std::string> &content_types);
2537
2538
int close_socket(socket_t sock);
2539
2540
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2541
2542
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2543
2544
enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2545
2546
EncodingType encoding_type(const Request &req, const Response &res);
2547
2548
class BufferStream final : public Stream {
2549
public:
2550
6.05k
  BufferStream() = default;
2551
6.05k
  ~BufferStream() override = default;
2552
2553
  bool is_readable() const override;
2554
  bool wait_readable() const override;
2555
  bool wait_writable() const override;
2556
  ssize_t read(char *ptr, size_t size) override;
2557
  ssize_t write(const char *ptr, size_t size) override;
2558
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
2559
  void get_local_ip_and_port(std::string &ip, int &port) const override;
2560
  socket_t socket() const override;
2561
  time_t duration() const override;
2562
2563
  const std::string &get_buffer() const;
2564
2565
private:
2566
  std::string buffer;
2567
  size_t position = 0;
2568
};
2569
2570
class compressor {
2571
public:
2572
18
  virtual ~compressor() = default;
2573
2574
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2575
  virtual bool compress(const char *data, size_t data_length, bool last,
2576
                        Callback callback) = 0;
2577
};
2578
2579
class decompressor {
2580
public:
2581
581
  virtual ~decompressor() = default;
2582
2583
  virtual bool is_valid() const = 0;
2584
2585
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2586
  virtual bool decompress(const char *data, size_t data_length,
2587
                          Callback callback) = 0;
2588
};
2589
2590
class nocompressor final : public compressor {
2591
public:
2592
0
  ~nocompressor() override = default;
2593
2594
  bool compress(const char *data, size_t data_length, bool /*last*/,
2595
                Callback callback) override;
2596
};
2597
2598
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2599
class gzip_compressor final : public compressor {
2600
public:
2601
  gzip_compressor();
2602
  ~gzip_compressor() override;
2603
2604
  bool compress(const char *data, size_t data_length, bool last,
2605
                Callback callback) override;
2606
2607
private:
2608
  bool is_valid_ = false;
2609
  z_stream strm_;
2610
};
2611
2612
class gzip_decompressor final : public decompressor {
2613
public:
2614
  gzip_decompressor();
2615
  ~gzip_decompressor() override;
2616
2617
  bool is_valid() const override;
2618
2619
  bool decompress(const char *data, size_t data_length,
2620
                  Callback callback) override;
2621
2622
private:
2623
  bool is_valid_ = false;
2624
  z_stream strm_;
2625
};
2626
#endif
2627
2628
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2629
class brotli_compressor final : public compressor {
2630
public:
2631
  brotli_compressor();
2632
  ~brotli_compressor();
2633
2634
  bool compress(const char *data, size_t data_length, bool last,
2635
                Callback callback) override;
2636
2637
private:
2638
  BrotliEncoderState *state_ = nullptr;
2639
};
2640
2641
class brotli_decompressor final : public decompressor {
2642
public:
2643
  brotli_decompressor();
2644
  ~brotli_decompressor();
2645
2646
  bool is_valid() const override;
2647
2648
  bool decompress(const char *data, size_t data_length,
2649
                  Callback callback) override;
2650
2651
private:
2652
  BrotliDecoderResult decoder_r;
2653
  BrotliDecoderState *decoder_s = nullptr;
2654
};
2655
#endif
2656
2657
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2658
class zstd_compressor : public compressor {
2659
public:
2660
  zstd_compressor();
2661
  ~zstd_compressor();
2662
2663
  bool compress(const char *data, size_t data_length, bool last,
2664
                Callback callback) override;
2665
2666
private:
2667
  ZSTD_CCtx *ctx_ = nullptr;
2668
};
2669
2670
class zstd_decompressor : public decompressor {
2671
public:
2672
  zstd_decompressor();
2673
  ~zstd_decompressor();
2674
2675
  bool is_valid() const override;
2676
2677
  bool decompress(const char *data, size_t data_length,
2678
                  Callback callback) override;
2679
2680
private:
2681
  ZSTD_DCtx *ctx_ = nullptr;
2682
};
2683
#endif
2684
2685
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2686
// to store data. The call can set memory on stack for performance.
2687
class stream_line_reader {
2688
public:
2689
  stream_line_reader(Stream &strm, char *fixed_buffer,
2690
                     size_t fixed_buffer_size);
2691
  const char *ptr() const;
2692
  size_t size() const;
2693
  bool end_with_crlf() const;
2694
  bool getline();
2695
2696
private:
2697
  void append(char c);
2698
2699
  Stream &strm_;
2700
  char *fixed_buffer_;
2701
  const size_t fixed_buffer_size_;
2702
  size_t fixed_buffer_used_size_ = 0;
2703
  std::string growable_buffer_;
2704
};
2705
2706
bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
2707
                    const Headers &src_headers);
2708
2709
struct ChunkedDecoder {
2710
  Stream &strm;
2711
  size_t chunk_remaining = 0;
2712
  bool finished = false;
2713
  char line_buf[64];
2714
  size_t last_chunk_total = 0;
2715
  size_t last_chunk_offset = 0;
2716
2717
  explicit ChunkedDecoder(Stream &s);
2718
2719
  ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
2720
                       size_t &out_chunk_total);
2721
2722
  bool parse_trailers_into(Headers &dest, const Headers &src_headers);
2723
};
2724
2725
class mmap {
2726
public:
2727
  mmap(const char *path);
2728
  ~mmap();
2729
2730
  bool open(const char *path);
2731
  void close();
2732
2733
  bool is_open() const;
2734
  size_t size() const;
2735
  const char *data() const;
2736
2737
private:
2738
#if defined(_WIN32)
2739
  HANDLE hFile_ = NULL;
2740
  HANDLE hMapping_ = NULL;
2741
#else
2742
  int fd_ = -1;
2743
#endif
2744
  size_t size_ = 0;
2745
  void *addr_ = nullptr;
2746
  bool is_open_empty_file = false;
2747
};
2748
2749
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
2750
namespace fields {
2751
2752
bool is_token_char(char c);
2753
bool is_token(const std::string &s);
2754
bool is_field_name(const std::string &s);
2755
bool is_vchar(char c);
2756
bool is_obs_text(char c);
2757
bool is_field_vchar(char c);
2758
bool is_field_content(const std::string &s);
2759
bool is_field_value(const std::string &s);
2760
2761
} // namespace fields
2762
2763
} // namespace detail
2764
2765
namespace stream {
2766
2767
class Result {
2768
public:
2769
0
  Result() : chunk_size_(8192) {}
2770
2771
  explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192)
2772
0
      : handle_(std::move(handle)), chunk_size_(chunk_size) {}
2773
2774
  Result(Result &&other) noexcept
2775
      : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
2776
        current_size_(other.current_size_), chunk_size_(other.chunk_size_),
2777
0
        finished_(other.finished_) {
2778
0
    other.current_size_ = 0;
2779
0
    other.finished_ = true;
2780
0
  }
2781
2782
0
  Result &operator=(Result &&other) noexcept {
2783
0
    if (this != &other) {
2784
0
      handle_ = std::move(other.handle_);
2785
0
      buffer_ = std::move(other.buffer_);
2786
0
      current_size_ = other.current_size_;
2787
0
      chunk_size_ = other.chunk_size_;
2788
0
      finished_ = other.finished_;
2789
0
      other.current_size_ = 0;
2790
0
      other.finished_ = true;
2791
0
    }
2792
0
    return *this;
2793
0
  }
2794
2795
  Result(const Result &) = delete;
2796
  Result &operator=(const Result &) = delete;
2797
2798
  // Check if the result is valid (connection succeeded and response received)
2799
0
  bool is_valid() const { return handle_.is_valid(); }
2800
0
  explicit operator bool() const { return is_valid(); }
2801
2802
  // Response status code
2803
0
  int status() const {
2804
0
    return handle_.response ? handle_.response->status : -1;
2805
0
  }
2806
2807
  // Response headers
2808
0
  const Headers &headers() const {
2809
0
    static const Headers empty_headers;
2810
0
    return handle_.response ? handle_.response->headers : empty_headers;
2811
0
  }
2812
2813
  std::string get_header_value(const std::string &key,
2814
0
                               const char *def = "") const {
2815
0
    return handle_.response ? handle_.response->get_header_value(key, def)
2816
0
                            : def;
2817
0
  }
2818
2819
0
  bool has_header(const std::string &key) const {
2820
0
    return handle_.response ? handle_.response->has_header(key) : false;
2821
0
  }
2822
2823
  // Error information
2824
0
  Error error() const { return handle_.error; }
2825
0
  Error read_error() const { return handle_.get_read_error(); }
2826
0
  bool has_read_error() const { return handle_.has_read_error(); }
2827
2828
  // Streaming iteration API
2829
  // Call next() to read the next chunk, then access data via data()/size()
2830
  // Returns true if data was read, false when stream is exhausted
2831
0
  bool next() {
2832
0
    if (!handle_.is_valid() || finished_) { return false; }
2833
0
2834
0
    if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }
2835
0
2836
0
    ssize_t n = handle_.read(&buffer_[0], chunk_size_);
2837
0
    if (n > 0) {
2838
0
      current_size_ = static_cast<size_t>(n);
2839
0
      return true;
2840
0
    }
2841
0
2842
0
    current_size_ = 0;
2843
0
    finished_ = true;
2844
0
    return false;
2845
0
  }
2846
2847
  // Pointer to current chunk data (valid after next() returns true)
2848
0
  const char *data() const { return buffer_.data(); }
2849
2850
  // Size of current chunk (valid after next() returns true)
2851
0
  size_t size() const { return current_size_; }
2852
2853
  // Convenience method: read all remaining data into a string
2854
0
  std::string read_all() {
2855
0
    std::string result;
2856
0
    while (next()) {
2857
0
      result.append(data(), size());
2858
0
    }
2859
0
    return result;
2860
0
  }
2861
2862
private:
2863
  ClientImpl::StreamHandle handle_;
2864
  std::string buffer_;
2865
  size_t current_size_ = 0;
2866
  size_t chunk_size_;
2867
  bool finished_ = false;
2868
};
2869
2870
// GET
2871
template <typename ClientType>
2872
inline Result Get(ClientType &cli, const std::string &path,
2873
                  size_t chunk_size = 8192) {
2874
  return Result{cli.open_stream("GET", path), chunk_size};
2875
}
2876
2877
template <typename ClientType>
2878
inline Result Get(ClientType &cli, const std::string &path,
2879
0
                  const Headers &headers, size_t chunk_size = 8192) {
2880
0
  return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
2881
0
}
2882
2883
template <typename ClientType>
2884
inline Result Get(ClientType &cli, const std::string &path,
2885
                  const Params &params, size_t chunk_size = 8192) {
2886
  return Result{cli.open_stream("GET", path, params), chunk_size};
2887
}
2888
2889
template <typename ClientType>
2890
inline Result Get(ClientType &cli, const std::string &path,
2891
                  const Params &params, const Headers &headers,
2892
                  size_t chunk_size = 8192) {
2893
  return Result{cli.open_stream("GET", path, params, headers), chunk_size};
2894
}
2895
2896
// POST
2897
template <typename ClientType>
2898
inline Result Post(ClientType &cli, const std::string &path,
2899
                   const std::string &body, const std::string &content_type,
2900
                   size_t chunk_size = 8192) {
2901
  return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
2902
                chunk_size};
2903
}
2904
2905
template <typename ClientType>
2906
inline Result Post(ClientType &cli, const std::string &path,
2907
                   const Headers &headers, const std::string &body,
2908
                   const std::string &content_type, size_t chunk_size = 8192) {
2909
  return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
2910
                chunk_size};
2911
}
2912
2913
template <typename ClientType>
2914
inline Result Post(ClientType &cli, const std::string &path,
2915
                   const Params &params, const std::string &body,
2916
                   const std::string &content_type, size_t chunk_size = 8192) {
2917
  return Result{cli.open_stream("POST", path, params, {}, body, content_type),
2918
                chunk_size};
2919
}
2920
2921
template <typename ClientType>
2922
inline Result Post(ClientType &cli, const std::string &path,
2923
                   const Params &params, const Headers &headers,
2924
                   const std::string &body, const std::string &content_type,
2925
                   size_t chunk_size = 8192) {
2926
  return Result{
2927
      cli.open_stream("POST", path, params, headers, body, content_type),
2928
      chunk_size};
2929
}
2930
2931
// PUT
2932
template <typename ClientType>
2933
inline Result Put(ClientType &cli, const std::string &path,
2934
                  const std::string &body, const std::string &content_type,
2935
                  size_t chunk_size = 8192) {
2936
  return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
2937
                chunk_size};
2938
}
2939
2940
template <typename ClientType>
2941
inline Result Put(ClientType &cli, const std::string &path,
2942
                  const Headers &headers, const std::string &body,
2943
                  const std::string &content_type, size_t chunk_size = 8192) {
2944
  return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
2945
                chunk_size};
2946
}
2947
2948
template <typename ClientType>
2949
inline Result Put(ClientType &cli, const std::string &path,
2950
                  const Params &params, const std::string &body,
2951
                  const std::string &content_type, size_t chunk_size = 8192) {
2952
  return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
2953
                chunk_size};
2954
}
2955
2956
template <typename ClientType>
2957
inline Result Put(ClientType &cli, const std::string &path,
2958
                  const Params &params, const Headers &headers,
2959
                  const std::string &body, const std::string &content_type,
2960
                  size_t chunk_size = 8192) {
2961
  return Result{
2962
      cli.open_stream("PUT", path, params, headers, body, content_type),
2963
      chunk_size};
2964
}
2965
2966
// PATCH
2967
template <typename ClientType>
2968
inline Result Patch(ClientType &cli, const std::string &path,
2969
                    const std::string &body, const std::string &content_type,
2970
                    size_t chunk_size = 8192) {
2971
  return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
2972
                chunk_size};
2973
}
2974
2975
template <typename ClientType>
2976
inline Result Patch(ClientType &cli, const std::string &path,
2977
                    const Headers &headers, const std::string &body,
2978
                    const std::string &content_type, size_t chunk_size = 8192) {
2979
  return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
2980
                chunk_size};
2981
}
2982
2983
template <typename ClientType>
2984
inline Result Patch(ClientType &cli, const std::string &path,
2985
                    const Params &params, const std::string &body,
2986
                    const std::string &content_type, size_t chunk_size = 8192) {
2987
  return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
2988
                chunk_size};
2989
}
2990
2991
template <typename ClientType>
2992
inline Result Patch(ClientType &cli, const std::string &path,
2993
                    const Params &params, const Headers &headers,
2994
                    const std::string &body, const std::string &content_type,
2995
                    size_t chunk_size = 8192) {
2996
  return Result{
2997
      cli.open_stream("PATCH", path, params, headers, body, content_type),
2998
      chunk_size};
2999
}
3000
3001
// DELETE
3002
template <typename ClientType>
3003
inline Result Delete(ClientType &cli, const std::string &path,
3004
                     size_t chunk_size = 8192) {
3005
  return Result{cli.open_stream("DELETE", path), chunk_size};
3006
}
3007
3008
template <typename ClientType>
3009
inline Result Delete(ClientType &cli, const std::string &path,
3010
                     const Headers &headers, size_t chunk_size = 8192) {
3011
  return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
3012
}
3013
3014
template <typename ClientType>
3015
inline Result Delete(ClientType &cli, const std::string &path,
3016
                     const std::string &body, const std::string &content_type,
3017
                     size_t chunk_size = 8192) {
3018
  return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
3019
                chunk_size};
3020
}
3021
3022
template <typename ClientType>
3023
inline Result Delete(ClientType &cli, const std::string &path,
3024
                     const Headers &headers, const std::string &body,
3025
                     const std::string &content_type,
3026
                     size_t chunk_size = 8192) {
3027
  return Result{
3028
      cli.open_stream("DELETE", path, {}, headers, body, content_type),
3029
      chunk_size};
3030
}
3031
3032
template <typename ClientType>
3033
inline Result Delete(ClientType &cli, const std::string &path,
3034
                     const Params &params, size_t chunk_size = 8192) {
3035
  return Result{cli.open_stream("DELETE", path, params), chunk_size};
3036
}
3037
3038
template <typename ClientType>
3039
inline Result Delete(ClientType &cli, const std::string &path,
3040
                     const Params &params, const Headers &headers,
3041
                     size_t chunk_size = 8192) {
3042
  return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
3043
}
3044
3045
template <typename ClientType>
3046
inline Result Delete(ClientType &cli, const std::string &path,
3047
                     const Params &params, const std::string &body,
3048
                     const std::string &content_type,
3049
                     size_t chunk_size = 8192) {
3050
  return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
3051
                chunk_size};
3052
}
3053
3054
template <typename ClientType>
3055
inline Result Delete(ClientType &cli, const std::string &path,
3056
                     const Params &params, const Headers &headers,
3057
                     const std::string &body, const std::string &content_type,
3058
                     size_t chunk_size = 8192) {
3059
  return Result{
3060
      cli.open_stream("DELETE", path, params, headers, body, content_type),
3061
      chunk_size};
3062
}
3063
3064
// HEAD
3065
template <typename ClientType>
3066
inline Result Head(ClientType &cli, const std::string &path,
3067
                   size_t chunk_size = 8192) {
3068
  return Result{cli.open_stream("HEAD", path), chunk_size};
3069
}
3070
3071
template <typename ClientType>
3072
inline Result Head(ClientType &cli, const std::string &path,
3073
                   const Headers &headers, size_t chunk_size = 8192) {
3074
  return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
3075
}
3076
3077
template <typename ClientType>
3078
inline Result Head(ClientType &cli, const std::string &path,
3079
                   const Params &params, size_t chunk_size = 8192) {
3080
  return Result{cli.open_stream("HEAD", path, params), chunk_size};
3081
}
3082
3083
template <typename ClientType>
3084
inline Result Head(ClientType &cli, const std::string &path,
3085
                   const Params &params, const Headers &headers,
3086
                   size_t chunk_size = 8192) {
3087
  return Result{cli.open_stream("HEAD", path, params, headers), chunk_size};
3088
}
3089
3090
// OPTIONS
3091
template <typename ClientType>
3092
inline Result Options(ClientType &cli, const std::string &path,
3093
                      size_t chunk_size = 8192) {
3094
  return Result{cli.open_stream("OPTIONS", path), chunk_size};
3095
}
3096
3097
template <typename ClientType>
3098
inline Result Options(ClientType &cli, const std::string &path,
3099
                      const Headers &headers, size_t chunk_size = 8192) {
3100
  return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
3101
}
3102
3103
template <typename ClientType>
3104
inline Result Options(ClientType &cli, const std::string &path,
3105
                      const Params &params, size_t chunk_size = 8192) {
3106
  return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
3107
}
3108
3109
template <typename ClientType>
3110
inline Result Options(ClientType &cli, const std::string &path,
3111
                      const Params &params, const Headers &headers,
3112
                      size_t chunk_size = 8192) {
3113
  return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
3114
}
3115
3116
} // namespace stream
3117
3118
namespace sse {
3119
3120
struct SSEMessage {
3121
  std::string event; // Event type (default: "message")
3122
  std::string data;  // Event payload
3123
  std::string id;    // Event ID for Last-Event-ID header
3124
3125
0
  SSEMessage() : event("message") {}
3126
3127
0
  void clear() {
3128
0
    event = "message";
3129
0
    data.clear();
3130
0
    id.clear();
3131
0
  }
3132
};
3133
3134
class SSEClient {
3135
public:
3136
  using MessageHandler = std::function<void(const SSEMessage &)>;
3137
  using ErrorHandler = std::function<void(Error)>;
3138
  using OpenHandler = std::function<void()>;
3139
3140
  SSEClient(Client &client, const std::string &path)
3141
0
      : client_(client), path_(path) {}
3142
3143
  SSEClient(Client &client, const std::string &path, const Headers &headers)
3144
0
      : client_(client), path_(path), headers_(headers) {}
3145
3146
0
  ~SSEClient() { stop(); }
3147
3148
  SSEClient(const SSEClient &) = delete;
3149
  SSEClient &operator=(const SSEClient &) = delete;
3150
3151
  // Event handlers
3152
0
  SSEClient &on_message(MessageHandler handler) {
3153
0
    on_message_ = std::move(handler);
3154
0
    return *this;
3155
0
  }
3156
3157
0
  SSEClient &on_event(const std::string &type, MessageHandler handler) {
3158
0
    event_handlers_[type] = std::move(handler);
3159
0
    return *this;
3160
0
  }
3161
3162
0
  SSEClient &on_open(OpenHandler handler) {
3163
0
    on_open_ = std::move(handler);
3164
0
    return *this;
3165
0
  }
3166
3167
0
  SSEClient &on_error(ErrorHandler handler) {
3168
0
    on_error_ = std::move(handler);
3169
0
    return *this;
3170
0
  }
3171
3172
0
  SSEClient &set_reconnect_interval(int ms) {
3173
0
    reconnect_interval_ms_ = ms;
3174
0
    return *this;
3175
0
  }
3176
3177
0
  SSEClient &set_max_reconnect_attempts(int n) {
3178
0
    max_reconnect_attempts_ = n;
3179
0
    return *this;
3180
0
  }
3181
3182
  // State accessors
3183
0
  bool is_connected() const { return connected_.load(); }
3184
0
  const std::string &last_event_id() const { return last_event_id_; }
3185
3186
  // Blocking start - runs event loop with auto-reconnect
3187
0
  void start() {
3188
0
    running_.store(true);
3189
0
    run_event_loop();
3190
0
  }
3191
3192
  // Non-blocking start - runs in background thread
3193
0
  void start_async() {
3194
0
    running_.store(true);
3195
0
    async_thread_ = std::thread([this]() { run_event_loop(); });
3196
0
  }
3197
3198
  // Stop the client (thread-safe)
3199
0
  void stop() {
3200
0
    running_.store(false);
3201
0
    client_.stop(); // Cancel any pending operations
3202
0
    if (async_thread_.joinable()) { async_thread_.join(); }
3203
0
  }
3204
3205
private:
3206
  // Parse a single SSE field line
3207
  // Returns true if this line ends an event (blank line)
3208
0
  bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms) {
3209
0
    // Blank line signals end of event
3210
0
    if (line.empty() || line == "\r") { return true; }
3211
0
3212
0
    // Lines starting with ':' are comments (ignored)
3213
0
    if (!line.empty() && line[0] == ':') { return false; }
3214
0
3215
0
    // Find the colon separator
3216
0
    auto colon_pos = line.find(':');
3217
0
    if (colon_pos == std::string::npos) {
3218
0
      // Line with no colon is treated as field name with empty value
3219
0
      return false;
3220
0
    }
3221
0
3222
0
    auto field = line.substr(0, colon_pos);
3223
0
    std::string value;
3224
0
3225
0
    // Value starts after colon, skip optional single space
3226
0
    if (colon_pos + 1 < line.size()) {
3227
0
      auto value_start = colon_pos + 1;
3228
0
      if (line[value_start] == ' ') { value_start++; }
3229
0
      value = line.substr(value_start);
3230
0
      // Remove trailing \r if present
3231
0
      if (!value.empty() && value.back() == '\r') { value.pop_back(); }
3232
0
    }
3233
0
3234
0
    // Handle known fields
3235
0
    if (field == "event") {
3236
0
      msg.event = value;
3237
0
    } else if (field == "data") {
3238
0
      // Multiple data lines are concatenated with newlines
3239
0
      if (!msg.data.empty()) { msg.data += "\n"; }
3240
0
      msg.data += value;
3241
0
    } else if (field == "id") {
3242
0
      // Empty id is valid (clears the last event ID)
3243
0
      msg.id = value;
3244
0
    } else if (field == "retry") {
3245
0
      // Parse retry interval in milliseconds
3246
0
      try {
3247
0
        retry_ms = std::stoi(value);
3248
0
      } catch (...) {
3249
0
        // Invalid retry value, ignore
3250
0
      }
3251
0
    }
3252
0
    // Unknown fields are ignored per SSE spec
3253
0
3254
0
    return false;
3255
0
  }
3256
3257
  // Main event loop with auto-reconnect
3258
0
  void run_event_loop() {
3259
0
    auto reconnect_count = 0;
3260
0
3261
0
    while (running_.load()) {
3262
0
      // Build headers, including Last-Event-ID if we have one
3263
0
      auto request_headers = headers_;
3264
0
      if (!last_event_id_.empty()) {
3265
0
        request_headers.emplace("Last-Event-ID", last_event_id_);
3266
0
      }
3267
0
3268
0
      // Open streaming connection
3269
0
      auto result = stream::Get(client_, path_, request_headers);
3270
0
3271
0
      // Connection error handling
3272
0
      if (!result) {
3273
0
        connected_.store(false);
3274
0
        if (on_error_) { on_error_(result.error()); }
3275
0
3276
0
        if (!should_reconnect(reconnect_count)) { break; }
3277
0
        wait_for_reconnect();
3278
0
        reconnect_count++;
3279
0
        continue;
3280
0
      }
3281
0
3282
0
      if (result.status() != 200) {
3283
0
        connected_.store(false);
3284
0
        // For certain errors, don't reconnect
3285
0
        if (result.status() == 204 || // No Content - server wants us to stop
3286
0
            result.status() == 404 || // Not Found
3287
0
            result.status() == 401 || // Unauthorized
3288
0
            result.status() == 403) { // Forbidden
3289
0
          if (on_error_) { on_error_(Error::Connection); }
3290
0
          break;
3291
0
        }
3292
0
3293
0
        if (on_error_) { on_error_(Error::Connection); }
3294
0
3295
0
        if (!should_reconnect(reconnect_count)) { break; }
3296
0
        wait_for_reconnect();
3297
0
        reconnect_count++;
3298
0
        continue;
3299
0
      }
3300
0
3301
0
      // Connection successful
3302
0
      connected_.store(true);
3303
0
      reconnect_count = 0;
3304
0
      if (on_open_) { on_open_(); }
3305
0
3306
0
      // Event receiving loop
3307
0
      std::string buffer;
3308
0
      SSEMessage current_msg;
3309
0
3310
0
      while (running_.load() && result.next()) {
3311
0
        buffer.append(result.data(), result.size());
3312
0
3313
0
        // Process complete lines in the buffer
3314
0
        size_t line_start = 0;
3315
0
        size_t newline_pos;
3316
0
3317
0
        while ((newline_pos = buffer.find('\n', line_start)) !=
3318
0
               std::string::npos) {
3319
0
          auto line = buffer.substr(line_start, newline_pos - line_start);
3320
0
          line_start = newline_pos + 1;
3321
0
3322
0
          // Parse the line and check if event is complete
3323
0
          auto event_complete =
3324
0
              parse_sse_line(line, current_msg, reconnect_interval_ms_);
3325
0
3326
0
          if (event_complete && !current_msg.data.empty()) {
3327
0
            // Update last_event_id for reconnection
3328
0
            if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
3329
0
3330
0
            // Dispatch event to appropriate handler
3331
0
            dispatch_event(current_msg);
3332
0
3333
0
            current_msg.clear();
3334
0
          }
3335
0
        }
3336
0
3337
0
        // Keep unprocessed data in buffer
3338
0
        buffer.erase(0, line_start);
3339
0
      }
3340
0
3341
0
      // Connection ended
3342
0
      connected_.store(false);
3343
0
3344
0
      if (!running_.load()) { break; }
3345
0
3346
0
      // Check for read errors
3347
0
      if (result.has_read_error()) {
3348
0
        if (on_error_) { on_error_(result.read_error()); }
3349
0
      }
3350
0
3351
0
      if (!should_reconnect(reconnect_count)) { break; }
3352
0
      wait_for_reconnect();
3353
0
      reconnect_count++;
3354
0
    }
3355
0
3356
0
    connected_.store(false);
3357
0
  }
3358
3359
  // Dispatch event to appropriate handler
3360
0
  void dispatch_event(const SSEMessage &msg) {
3361
0
    // Check for specific event type handler first
3362
0
    auto it = event_handlers_.find(msg.event);
3363
0
    if (it != event_handlers_.end()) {
3364
0
      it->second(msg);
3365
0
      return;
3366
0
    }
3367
0
3368
0
    // Fall back to generic message handler
3369
0
    if (on_message_) { on_message_(msg); }
3370
0
  }
3371
3372
  // Check if we should attempt to reconnect
3373
0
  bool should_reconnect(int count) const {
3374
0
    if (!running_.load()) { return false; }
3375
0
    if (max_reconnect_attempts_ == 0) { return true; } // unlimited
3376
0
    return count < max_reconnect_attempts_;
3377
0
  }
3378
3379
  // Wait for reconnect interval
3380
0
  void wait_for_reconnect() {
3381
0
    // Use small increments to check running_ flag frequently
3382
0
    auto waited = 0;
3383
0
    while (running_.load() && waited < reconnect_interval_ms_) {
3384
0
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
3385
0
      waited += 100;
3386
0
    }
3387
0
  }
3388
3389
  // Client and path
3390
  Client &client_;
3391
  std::string path_;
3392
  Headers headers_;
3393
3394
  // Callbacks
3395
  MessageHandler on_message_;
3396
  std::map<std::string, MessageHandler> event_handlers_;
3397
  OpenHandler on_open_;
3398
  ErrorHandler on_error_;
3399
3400
  // Configuration
3401
  int reconnect_interval_ms_ = 3000;
3402
  int max_reconnect_attempts_ = 0; // 0 = unlimited
3403
3404
  // State
3405
  std::atomic<bool> running_{false};
3406
  std::atomic<bool> connected_{false};
3407
  std::string last_event_id_;
3408
3409
  // Async support
3410
  std::thread async_thread_;
3411
};
3412
3413
} // namespace sse
3414
3415
// ----------------------------------------------------------------------------
3416
3417
/*
3418
 * Implementation that will be part of the .cc file if split into .h + .cc.
3419
 */
3420
3421
namespace detail {
3422
3423
39.5k
inline bool is_hex(char c, int &v) {
3424
39.5k
  if (isdigit(c)) {
3425
7.75k
    v = c - '0';
3426
7.75k
    return true;
3427
31.7k
  } else if ('A' <= c && c <= 'F') {
3428
3.81k
    v = c - 'A' + 10;
3429
3.81k
    return true;
3430
27.9k
  } else if ('a' <= c && c <= 'f') {
3431
7.14k
    v = c - 'a' + 10;
3432
7.14k
    return true;
3433
7.14k
  }
3434
20.7k
  return false;
3435
39.5k
}
3436
3437
inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
3438
26.7k
                          int &val) {
3439
26.7k
  if (i >= s.size()) { return false; }
3440
3441
26.2k
  val = 0;
3442
44.9k
  for (; cnt; i++, cnt--) {
3443
39.8k
    if (!s[i]) { return false; }
3444
39.5k
    auto v = 0;
3445
39.5k
    if (is_hex(s[i], v)) {
3446
18.7k
      val = val * 16 + v;
3447
20.7k
    } else {
3448
20.7k
      return false;
3449
20.7k
    }
3450
39.5k
  }
3451
5.04k
  return true;
3452
26.2k
}
3453
3454
0
inline std::string from_i_to_hex(size_t n) {
3455
0
  static const auto charset = "0123456789abcdef";
3456
0
  std::string ret;
3457
0
  do {
3458
0
    ret = charset[n & 15] + ret;
3459
0
    n >>= 4;
3460
0
  } while (n > 0);
3461
0
  return ret;
3462
0
}
3463
3464
0
inline std::string compute_etag(const FileStat &fs) {
3465
0
  if (!fs.is_file()) { return std::string(); }
3466
3467
  // If mtime cannot be determined (negative value indicates an error
3468
  // or sentinel), do not generate an ETag. Returning a neutral / fixed
3469
  // value like 0 could collide with a real file that legitimately has
3470
  // mtime == 0 (epoch) and lead to misleading validators.
3471
0
  auto mtime_raw = fs.mtime();
3472
0
  if (mtime_raw < 0) { return std::string(); }
3473
3474
0
  auto mtime = static_cast<size_t>(mtime_raw);
3475
0
  auto size = fs.size();
3476
3477
0
  return std::string("W/\"") + from_i_to_hex(mtime) + "-" +
3478
0
         from_i_to_hex(size) + "\"";
3479
0
}
3480
3481
// Format time_t as HTTP-date (RFC 9110 Section 5.6.7): "Sun, 06 Nov 1994
3482
// 08:49:37 GMT" This implementation is defensive: it validates `mtime`, checks
3483
// return values from `gmtime_r`/`gmtime_s`, and ensures `strftime` succeeds.
3484
0
inline std::string file_mtime_to_http_date(time_t mtime) {
3485
0
  if (mtime < 0) { return std::string(); }
3486
3487
0
  struct tm tm_buf;
3488
#ifdef _WIN32
3489
  if (gmtime_s(&tm_buf, &mtime) != 0) { return std::string(); }
3490
#else
3491
0
  if (gmtime_r(&mtime, &tm_buf) == nullptr) { return std::string(); }
3492
0
#endif
3493
0
  char buf[64];
3494
0
  if (strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {
3495
0
    return std::string();
3496
0
  }
3497
3498
0
  return std::string(buf);
3499
0
}
3500
3501
// Parse HTTP-date (RFC 9110 Section 5.6.7) to time_t. Returns -1 on failure.
3502
0
inline time_t parse_http_date(const std::string &date_str) {
3503
0
  struct tm tm_buf;
3504
3505
  // Create a classic locale object once for all parsing attempts
3506
0
  const std::locale classic_locale = std::locale::classic();
3507
3508
  // Try to parse using std::get_time (C++11, cross-platform)
3509
0
  auto try_parse = [&](const char *fmt) -> bool {
3510
0
    std::istringstream ss(date_str);
3511
0
    ss.imbue(classic_locale);
3512
3513
0
    memset(&tm_buf, 0, sizeof(tm_buf));
3514
0
    ss >> std::get_time(&tm_buf, fmt);
3515
3516
0
    return !ss.fail();
3517
0
  };
3518
3519
  // RFC 9110 preferred format (HTTP-date): "Sun, 06 Nov 1994 08:49:37 GMT"
3520
0
  if (!try_parse("%a, %d %b %Y %H:%M:%S")) {
3521
    // RFC 850 format: "Sunday, 06-Nov-94 08:49:37 GMT"
3522
0
    if (!try_parse("%A, %d-%b-%y %H:%M:%S")) {
3523
      // asctime format: "Sun Nov  6 08:49:37 1994"
3524
0
      if (!try_parse("%a %b %d %H:%M:%S %Y")) {
3525
0
        return static_cast<time_t>(-1);
3526
0
      }
3527
0
    }
3528
0
  }
3529
3530
#ifdef _WIN32
3531
  return _mkgmtime(&tm_buf);
3532
#else
3533
0
  return timegm(&tm_buf);
3534
0
#endif
3535
0
}
3536
3537
0
inline bool is_weak_etag(const std::string &s) {
3538
  // Check if the string is a weak ETag (starts with 'W/"')
3539
0
  return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
3540
0
}
3541
3542
0
inline bool is_strong_etag(const std::string &s) {
3543
  // Check if the string is a strong ETag (starts and ends with '"', at least 2
3544
  // chars)
3545
0
  return s.size() >= 2 && s[0] == '"' && s.back() == '"';
3546
0
}
3547
3548
2.25k
inline size_t to_utf8(int code, char *buff) {
3549
2.25k
  if (code < 0x0080) {
3550
218
    buff[0] = static_cast<char>(code & 0x7F);
3551
218
    return 1;
3552
2.03k
  } else if (code < 0x0800) {
3553
569
    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
3554
569
    buff[1] = static_cast<char>(0x80 | (code & 0x3F));
3555
569
    return 2;
3556
1.46k
  } else if (code < 0xD800) {
3557
313
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
3558
313
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
3559
313
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
3560
313
    return 3;
3561
1.15k
  } else if (code < 0xE000) { // D800 - DFFF is invalid...
3562
685
    return 0;
3563
685
  } else if (code < 0x10000) {
3564
469
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
3565
469
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
3566
469
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
3567
469
    return 3;
3568
469
  } else if (code < 0x110000) {
3569
0
    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
3570
0
    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
3571
0
    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
3572
0
    buff[3] = static_cast<char>(0x80 | (code & 0x3F));
3573
0
    return 4;
3574
0
  }
3575
3576
  // NOTREACHED
3577
0
  return 0;
3578
2.25k
}
3579
3580
// NOTE: This code came up with the following stackoverflow post:
3581
// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
3582
0
inline std::string base64_encode(const std::string &in) {
3583
0
  static const auto lookup =
3584
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3585
0
3586
0
  std::string out;
3587
0
  out.reserve(in.size());
3588
0
3589
0
  auto val = 0;
3590
0
  auto valb = -6;
3591
0
3592
0
  for (auto c : in) {
3593
0
    val = (val << 8) + static_cast<uint8_t>(c);
3594
0
    valb += 8;
3595
0
    while (valb >= 0) {
3596
0
      out.push_back(lookup[(val >> valb) & 0x3F]);
3597
0
      valb -= 6;
3598
0
    }
3599
0
  }
3600
0
3601
0
  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
3602
0
3603
0
  while (out.size() % 4) {
3604
0
    out.push_back('=');
3605
0
  }
3606
0
3607
0
  return out;
3608
0
}
3609
3610
0
inline bool is_valid_path(const std::string &path) {
3611
0
  size_t level = 0;
3612
0
  size_t i = 0;
3613
3614
  // Skip slash
3615
0
  while (i < path.size() && path[i] == '/') {
3616
0
    i++;
3617
0
  }
3618
3619
0
  while (i < path.size()) {
3620
    // Read component
3621
0
    auto beg = i;
3622
0
    while (i < path.size() && path[i] != '/') {
3623
0
      if (path[i] == '\0') {
3624
0
        return false;
3625
0
      } else if (path[i] == '\\') {
3626
0
        return false;
3627
0
      }
3628
0
      i++;
3629
0
    }
3630
3631
0
    auto len = i - beg;
3632
0
    assert(len > 0);
3633
3634
0
    if (!path.compare(beg, len, ".")) {
3635
0
      ;
3636
0
    } else if (!path.compare(beg, len, "..")) {
3637
0
      if (level == 0) { return false; }
3638
0
      level--;
3639
0
    } else {
3640
0
      level++;
3641
0
    }
3642
3643
    // Skip slash
3644
0
    while (i < path.size() && path[i] == '/') {
3645
0
      i++;
3646
0
    }
3647
0
  }
3648
3649
0
  return true;
3650
0
}
3651
3652
0
inline FileStat::FileStat(const std::string &path) {
3653
#if defined(_WIN32)
3654
  auto wpath = u8string_to_wstring(path.c_str());
3655
  ret_ = _wstat(wpath.c_str(), &st_);
3656
#else
3657
0
  ret_ = stat(path.c_str(), &st_);
3658
0
#endif
3659
0
}
3660
0
inline bool FileStat::is_file() const {
3661
0
  return ret_ >= 0 && S_ISREG(st_.st_mode);
3662
0
}
3663
0
inline bool FileStat::is_dir() const {
3664
0
  return ret_ >= 0 && S_ISDIR(st_.st_mode);
3665
0
}
3666
3667
0
inline time_t FileStat::mtime() const {
3668
0
  return ret_ >= 0 ? static_cast<time_t>(st_.st_mtime)
3669
0
                   : static_cast<time_t>(-1);
3670
0
}
3671
3672
0
inline size_t FileStat::size() const {
3673
0
  return ret_ >= 0 ? static_cast<size_t>(st_.st_size) : 0;
3674
0
}
3675
3676
0
inline std::string encode_path(const std::string &s) {
3677
0
  std::string result;
3678
0
  result.reserve(s.size());
3679
0
3680
0
  for (size_t i = 0; s[i]; i++) {
3681
0
    switch (s[i]) {
3682
0
    case ' ': result += "%20"; break;
3683
0
    case '+': result += "%2B"; break;
3684
0
    case '\r': result += "%0D"; break;
3685
0
    case '\n': result += "%0A"; break;
3686
0
    case '\'': result += "%27"; break;
3687
0
    case ',': result += "%2C"; break;
3688
0
    // case ':': result += "%3A"; break; // ok? probably...
3689
0
    case ';': result += "%3B"; break;
3690
0
    default:
3691
0
      auto c = static_cast<uint8_t>(s[i]);
3692
0
      if (c >= 0x80) {
3693
0
        result += '%';
3694
0
        char hex[4];
3695
0
        auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
3696
0
        assert(len == 2);
3697
0
        result.append(hex, static_cast<size_t>(len));
3698
0
      } else {
3699
0
        result += s[i];
3700
0
      }
3701
0
      break;
3702
0
    }
3703
0
  }
3704
0
3705
0
  return result;
3706
0
}
3707
3708
0
inline std::string file_extension(const std::string &path) {
3709
0
  std::smatch m;
3710
0
  thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
3711
0
  if (std::regex_search(path, m, re)) { return m[1].str(); }
3712
0
  return std::string();
3713
0
}
3714
3715
629M
inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
3716
3717
template <typename T>
3718
inline bool parse_header(const char *beg, const char *end, T fn);
3719
3720
template <typename T>
3721
156k
inline bool parse_header(const char *beg, const char *end, T fn) {
3722
  // Skip trailing spaces and tabs.
3723
159k
  while (beg < end && is_space_or_tab(end[-1])) {
3724
3.01k
    end--;
3725
3.01k
  }
3726
3727
156k
  auto p = beg;
3728
1.85M
  while (p < end && *p != ':') {
3729
1.70M
    p++;
3730
1.70M
  }
3731
3732
156k
  auto name = std::string(beg, p);
3733
156k
  if (!detail::fields::is_field_name(name)) { return false; }
3734
3735
156k
  if (p == end) { return false; }
3736
3737
156k
  auto key_end = p;
3738
3739
156k
  if (*p++ != ':') { return false; }
3740
3741
158k
  while (p < end && is_space_or_tab(*p)) {
3742
2.57k
    p++;
3743
2.57k
  }
3744
3745
156k
  if (p <= end) {
3746
156k
    auto key_len = key_end - beg;
3747
156k
    if (!key_len) { return false; }
3748
3749
156k
    auto key = std::string(beg, key_end);
3750
156k
    auto val = std::string(p, end);
3751
3752
156k
    if (!detail::fields::is_field_value(val)) { return false; }
3753
3754
156k
    if (case_ignore::equal(key, "Location") ||
3755
155k
        case_ignore::equal(key, "Referer")) {
3756
718
      fn(key, val);
3757
155k
    } else {
3758
155k
      fn(key, decode_path_component(val));
3759
155k
    }
3760
3761
156k
    return true;
3762
156k
  }
3763
3764
0
  return false;
3765
156k
}
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
3721
12.1k
inline bool parse_header(const char *beg, const char *end, T fn) {
3722
  // Skip trailing spaces and tabs.
3723
12.4k
  while (beg < end && is_space_or_tab(end[-1])) {
3724
262
    end--;
3725
262
  }
3726
3727
12.1k
  auto p = beg;
3728
208k
  while (p < end && *p != ':') {
3729
196k
    p++;
3730
196k
  }
3731
3732
12.1k
  auto name = std::string(beg, p);
3733
12.1k
  if (!detail::fields::is_field_name(name)) { return false; }
3734
3735
12.0k
  if (p == end) { return false; }
3736
3737
11.9k
  auto key_end = p;
3738
3739
11.9k
  if (*p++ != ':') { return false; }
3740
3741
12.3k
  while (p < end && is_space_or_tab(*p)) {
3742
398
    p++;
3743
398
  }
3744
3745
11.9k
  if (p <= end) {
3746
11.9k
    auto key_len = key_end - beg;
3747
11.9k
    if (!key_len) { return false; }
3748
3749
11.9k
    auto key = std::string(beg, key_end);
3750
11.9k
    auto val = std::string(p, end);
3751
3752
11.9k
    if (!detail::fields::is_field_value(val)) { return false; }
3753
3754
11.9k
    if (case_ignore::equal(key, "Location") ||
3755
11.8k
        case_ignore::equal(key, "Referer")) {
3756
97
      fn(key, val);
3757
11.8k
    } else {
3758
11.8k
      fn(key, decode_path_component(val));
3759
11.8k
    }
3760
3761
11.9k
    return true;
3762
11.9k
  }
3763
3764
0
  return false;
3765
11.9k
}
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
3721
70.9k
inline bool parse_header(const char *beg, const char *end, T fn) {
3722
  // Skip trailing spaces and tabs.
3723
72.2k
  while (beg < end && is_space_or_tab(end[-1])) {
3724
1.25k
    end--;
3725
1.25k
  }
3726
3727
70.9k
  auto p = beg;
3728
836k
  while (p < end && *p != ':') {
3729
765k
    p++;
3730
765k
  }
3731
3732
70.9k
  auto name = std::string(beg, p);
3733
70.9k
  if (!detail::fields::is_field_name(name)) { return false; }
3734
3735
70.9k
  if (p == end) { return false; }
3736
3737
70.9k
  auto key_end = p;
3738
3739
70.9k
  if (*p++ != ':') { return false; }
3740
3741
71.8k
  while (p < end && is_space_or_tab(*p)) {
3742
928
    p++;
3743
928
  }
3744
3745
70.9k
  if (p <= end) {
3746
70.9k
    auto key_len = key_end - beg;
3747
70.9k
    if (!key_len) { return false; }
3748
3749
70.9k
    auto key = std::string(beg, key_end);
3750
70.9k
    auto val = std::string(p, end);
3751
3752
70.9k
    if (!detail::fields::is_field_value(val)) { return false; }
3753
3754
70.9k
    if (case_ignore::equal(key, "Location") ||
3755
70.6k
        case_ignore::equal(key, "Referer")) {
3756
213
      fn(key, val);
3757
70.6k
    } else {
3758
70.6k
      fn(key, decode_path_component(val));
3759
70.6k
    }
3760
3761
70.9k
    return true;
3762
70.9k
  }
3763
3764
0
  return false;
3765
70.9k
}
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
3721
70.9k
inline bool parse_header(const char *beg, const char *end, T fn) {
3722
  // Skip trailing spaces and tabs.
3723
72.0k
  while (beg < end && is_space_or_tab(end[-1])) {
3724
1.18k
    end--;
3725
1.18k
  }
3726
3727
70.9k
  auto p = beg;
3728
774k
  while (p < end && *p != ':') {
3729
703k
    p++;
3730
703k
  }
3731
3732
70.9k
  auto name = std::string(beg, p);
3733
70.9k
  if (!detail::fields::is_field_name(name)) { return false; }
3734
3735
70.9k
  if (p == end) { return false; }
3736
3737
70.9k
  auto key_end = p;
3738
3739
70.9k
  if (*p++ != ':') { return false; }
3740
3741
71.8k
  while (p < end && is_space_or_tab(*p)) {
3742
928
    p++;
3743
928
  }
3744
3745
70.9k
  if (p <= end) {
3746
70.9k
    auto key_len = key_end - beg;
3747
70.9k
    if (!key_len) { return false; }
3748
3749
70.9k
    auto key = std::string(beg, key_end);
3750
70.9k
    auto val = std::string(p, end);
3751
3752
70.9k
    if (!detail::fields::is_field_value(val)) { return false; }
3753
3754
70.9k
    if (case_ignore::equal(key, "Location") ||
3755
70.6k
        case_ignore::equal(key, "Referer")) {
3756
213
      fn(key, val);
3757
70.6k
    } else {
3758
70.6k
      fn(key, decode_path_component(val));
3759
70.6k
    }
3760
3761
70.9k
    return true;
3762
70.9k
  }
3763
3764
0
  return false;
3765
70.9k
}
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
3721
2.74k
inline bool parse_header(const char *beg, const char *end, T fn) {
3722
  // Skip trailing spaces and tabs.
3723
3.06k
  while (beg < end && is_space_or_tab(end[-1])) {
3724
318
    end--;
3725
318
  }
3726
3727
2.74k
  auto p = beg;
3728
39.3k
  while (p < end && *p != ':') {
3729
36.6k
    p++;
3730
36.6k
  }
3731
3732
2.74k
  auto name = std::string(beg, p);
3733
2.74k
  if (!detail::fields::is_field_name(name)) { return false; }
3734
3735
2.40k
  if (p == end) { return false; }
3736
3737
2.38k
  auto key_end = p;
3738
3739
2.38k
  if (*p++ != ':') { return false; }
3740
3741
2.70k
  while (p < end && is_space_or_tab(*p)) {
3742
317
    p++;
3743
317
  }
3744
3745
2.38k
  if (p <= end) {
3746
2.38k
    auto key_len = key_end - beg;
3747
2.38k
    if (!key_len) { return false; }
3748
3749
2.38k
    auto key = std::string(beg, key_end);
3750
2.38k
    auto val = std::string(p, end);
3751
3752
2.38k
    if (!detail::fields::is_field_value(val)) { return false; }
3753
3754
2.37k
    if (case_ignore::equal(key, "Location") ||
3755
2.17k
        case_ignore::equal(key, "Referer")) {
3756
195
      fn(key, val);
3757
2.17k
    } else {
3758
2.17k
      fn(key, decode_path_component(val));
3759
2.17k
    }
3760
3761
2.37k
    return true;
3762
2.38k
  }
3763
3764
0
  return false;
3765
2.38k
}
3766
3767
inline bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
3768
469
                           const Headers &src_headers) {
3769
  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
3770
  // transfer coding is complete when a chunk with a chunk-size of zero is
3771
  // received, possibly followed by a trailer section, and finally terminated by
3772
  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
3773
  //
3774
  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
3775
  // doesn't care for the existence of the final CRLF. In other words, it seems
3776
  // to be ok whether the final CRLF exists or not in the chunked data.
3777
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
3778
  //
3779
  // According to the reference code in RFC 9112, cpp-httplib now allows
3780
  // chunked transfer coding data without the final CRLF.
3781
3782
  // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
3783
469
  thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
3784
469
      "transfer-encoding",
3785
469
      "content-length",
3786
469
      "host",
3787
469
      "authorization",
3788
469
      "www-authenticate",
3789
469
      "proxy-authenticate",
3790
469
      "proxy-authorization",
3791
469
      "cookie",
3792
469
      "set-cookie",
3793
469
      "cache-control",
3794
469
      "expect",
3795
469
      "max-forwards",
3796
469
      "pragma",
3797
469
      "range",
3798
469
      "te",
3799
469
      "age",
3800
469
      "expires",
3801
469
      "date",
3802
469
      "location",
3803
469
      "retry-after",
3804
469
      "vary",
3805
469
      "warning",
3806
469
      "content-encoding",
3807
469
      "content-type",
3808
469
      "content-range",
3809
469
      "trailer"};
3810
3811
469
  case_ignore::unordered_set<std::string> declared_trailers;
3812
469
  auto trailer_header = get_header_value(src_headers, "Trailer", "", 0);
3813
469
  if (trailer_header && std::strlen(trailer_header)) {
3814
368
    auto len = std::strlen(trailer_header);
3815
368
    split(trailer_header, trailer_header + len, ',',
3816
13.6k
          [&](const char *b, const char *e) {
3817
13.6k
            const char *kbeg = b;
3818
13.6k
            const char *kend = e;
3819
13.6k
            while (kbeg < kend && (*kbeg == ' ' || *kbeg == '\t')) {
3820
0
              ++kbeg;
3821
0
            }
3822
13.6k
            while (kend > kbeg && (kend[-1] == ' ' || kend[-1] == '\t')) {
3823
0
              --kend;
3824
0
            }
3825
13.6k
            std::string key(kbeg, static_cast<size_t>(kend - kbeg));
3826
13.6k
            if (!key.empty() &&
3827
13.6k
                prohibited_trailers.find(key) == prohibited_trailers.end()) {
3828
13.4k
              declared_trailers.insert(key);
3829
13.4k
            }
3830
13.6k
          });
3831
368
  }
3832
3833
469
  size_t trailer_header_count = 0;
3834
2.77k
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
3835
2.74k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
3836
2.74k
    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
3837
3838
2.74k
    constexpr auto line_terminator_len = 2;
3839
2.74k
    auto line_beg = line_reader.ptr();
3840
2.74k
    auto line_end =
3841
2.74k
        line_reader.ptr() + line_reader.size() - line_terminator_len;
3842
3843
2.74k
    if (!parse_header(line_beg, line_end,
3844
2.74k
                      [&](const std::string &key, const std::string &val) {
3845
2.37k
                        if (declared_trailers.find(key) !=
3846
2.37k
                            declared_trailers.end()) {
3847
430
                          dest.emplace(key, val);
3848
430
                          trailer_header_count++;
3849
430
                        }
3850
2.37k
                      })) {
3851
372
      return false;
3852
372
    }
3853
3854
2.37k
    if (!line_reader.getline()) { return false; }
3855
2.37k
  }
3856
3857
31
  return true;
3858
469
}
3859
3860
inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
3861
694k
                                      size_t right) {
3862
314M
  while (b + left < e && is_space_or_tab(b[left])) {
3863
313M
    left++;
3864
313M
  }
3865
314M
  while (right > 0 && is_space_or_tab(b[right - 1])) {
3866
313M
    right--;
3867
313M
  }
3868
694k
  return std::make_pair(left, right);
3869
694k
}
3870
3871
226k
inline std::string trim_copy(const std::string &s) {
3872
226k
  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
3873
226k
  return s.substr(r.first, r.second - r.first);
3874
226k
}
3875
3876
68.4k
inline std::string trim_double_quotes_copy(const std::string &s) {
3877
68.4k
  if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
3878
1.86k
    return s.substr(1, s.size() - 2);
3879
1.86k
  }
3880
66.6k
  return s;
3881
68.4k
}
3882
3883
inline void
3884
divide(const char *data, std::size_t size, char d,
3885
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
3886
11.3k
           fn) {
3887
11.3k
  const auto it = std::find(data, data + size, d);
3888
11.3k
  const auto found = static_cast<std::size_t>(it != data + size);
3889
11.3k
  const auto lhs_data = data;
3890
11.3k
  const auto lhs_size = static_cast<std::size_t>(it - data);
3891
11.3k
  const auto rhs_data = it + found;
3892
11.3k
  const auto rhs_size = size - lhs_size - found;
3893
3894
11.3k
  fn(lhs_data, lhs_size, rhs_data, rhs_size);
3895
11.3k
}
3896
3897
inline void
3898
divide(const std::string &str, char d,
3899
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
3900
5.71k
           fn) {
3901
5.71k
  divide(str.data(), str.size(), d, std::move(fn));
3902
5.71k
}
3903
3904
inline void split(const char *b, const char *e, char d,
3905
56.0k
                  std::function<void(const char *, const char *)> fn) {
3906
56.0k
  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
3907
56.0k
}
3908
3909
inline void split(const char *b, const char *e, char d, size_t m,
3910
56.0k
                  std::function<void(const char *, const char *)> fn) {
3911
56.0k
  size_t i = 0;
3912
56.0k
  size_t beg = 0;
3913
56.0k
  size_t count = 1;
3914
3915
4.52M
  while (e ? (b + i < e) : (b[i] != '\0')) {
3916
4.46M
    if (b[i] == d && count < m) {
3917
416k
      auto r = trim(b, e, beg, i);
3918
416k
      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
3919
416k
      beg = i + 1;
3920
416k
      count++;
3921
416k
    }
3922
4.46M
    i++;
3923
4.46M
  }
3924
3925
56.0k
  if (i) {
3926
50.7k
    auto r = trim(b, e, beg, i);
3927
50.7k
    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
3928
50.7k
  }
3929
56.0k
}
3930
3931
inline bool split_find(const char *b, const char *e, char d, size_t m,
3932
0
                       std::function<bool(const char *, const char *)> fn) {
3933
0
  size_t i = 0;
3934
0
  size_t beg = 0;
3935
0
  size_t count = 1;
3936
3937
0
  while (e ? (b + i < e) : (b[i] != '\0')) {
3938
0
    if (b[i] == d && count < m) {
3939
0
      auto r = trim(b, e, beg, i);
3940
0
      if (r.first < r.second) {
3941
0
        auto found = fn(&b[r.first], &b[r.second]);
3942
0
        if (found) { return true; }
3943
0
      }
3944
0
      beg = i + 1;
3945
0
      count++;
3946
0
    }
3947
0
    i++;
3948
0
  }
3949
3950
0
  if (i) {
3951
0
    auto r = trim(b, e, beg, i);
3952
0
    if (r.first < r.second) {
3953
0
      auto found = fn(&b[r.first], &b[r.second]);
3954
0
      if (found) { return true; }
3955
0
    }
3956
0
  }
3957
3958
0
  return false;
3959
0
}
3960
3961
inline bool split_find(const char *b, const char *e, char d,
3962
0
                       std::function<bool(const char *, const char *)> fn) {
3963
0
  return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
3964
0
                    std::move(fn));
3965
0
}
3966
3967
inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
3968
                                              size_t fixed_buffer_size)
3969
19.3k
    : strm_(strm), fixed_buffer_(fixed_buffer),
3970
19.3k
      fixed_buffer_size_(fixed_buffer_size) {}
3971
3972
66.5k
inline const char *stream_line_reader::ptr() const {
3973
66.5k
  if (growable_buffer_.empty()) {
3974
63.7k
    return fixed_buffer_;
3975
63.7k
  } else {
3976
2.77k
    return growable_buffer_.data();
3977
2.77k
  }
3978
66.5k
}
3979
3980
3.10M
inline size_t stream_line_reader::size() const {
3981
3.10M
  if (growable_buffer_.empty()) {
3982
1.30M
    return fixed_buffer_used_size_;
3983
1.79M
  } else {
3984
1.79M
    return growable_buffer_.size();
3985
1.79M
  }
3986
3.10M
}
3987
3988
16.7k
inline bool stream_line_reader::end_with_crlf() const {
3989
16.7k
  auto end = ptr() + size();
3990
16.7k
  return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
3991
16.7k
}
3992
3993
33.7k
inline bool stream_line_reader::getline() {
3994
33.7k
  fixed_buffer_used_size_ = 0;
3995
33.7k
  growable_buffer_.clear();
3996
3997
33.7k
#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3998
33.7k
  char prev_byte = 0;
3999
33.7k
#endif
4000
4001
3.02M
  for (size_t i = 0;; i++) {
4002
3.02M
    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
4003
      // Treat exceptionally long lines as an error to
4004
      // prevent infinite loops/memory exhaustion
4005
2
      return false;
4006
2
    }
4007
3.02M
    char byte;
4008
3.02M
    auto n = strm_.read(&byte, 1);
4009
4010
3.02M
    if (n < 0) {
4011
0
      return false;
4012
3.02M
    } else if (n == 0) {
4013
1.88k
      if (i == 0) {
4014
1.11k
        return false;
4015
1.11k
      } else {
4016
771
        break;
4017
771
      }
4018
1.88k
    }
4019
4020
3.02M
    append(byte);
4021
4022
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4023
    if (byte == '\n') { break; }
4024
#else
4025
3.02M
    if (prev_byte == '\r' && byte == '\n') { break; }
4026
2.99M
    prev_byte = byte;
4027
2.99M
#endif
4028
2.99M
  }
4029
4030
32.6k
  return true;
4031
33.7k
}
4032
4033
3.02M
inline void stream_line_reader::append(char c) {
4034
3.02M
  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
4035
1.22M
    fixed_buffer_[fixed_buffer_used_size_++] = c;
4036
1.22M
    fixed_buffer_[fixed_buffer_used_size_] = '\0';
4037
1.79M
  } else {
4038
1.79M
    if (growable_buffer_.empty()) {
4039
991
      assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
4040
991
      growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
4041
991
    }
4042
1.79M
    growable_buffer_ += c;
4043
1.79M
  }
4044
3.02M
}
4045
4046
0
inline mmap::mmap(const char *path) { open(path); }
4047
4048
0
inline mmap::~mmap() { close(); }
4049
4050
0
inline bool mmap::open(const char *path) {
4051
0
  close();
4052
4053
#if defined(_WIN32)
4054
  auto wpath = u8string_to_wstring(path);
4055
  if (wpath.empty()) { return false; }
4056
4057
  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
4058
                         OPEN_EXISTING, NULL);
4059
4060
  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
4061
4062
  LARGE_INTEGER size{};
4063
  if (!::GetFileSizeEx(hFile_, &size)) { return false; }
4064
  // If the following line doesn't compile due to QuadPart, update Windows SDK.
4065
  // See:
4066
  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
4067
  if (static_cast<ULONGLONG>(size.QuadPart) >
4068
      (std::numeric_limits<decltype(size_)>::max)()) {
4069
    // `size_t` might be 32-bits, on 32-bits Windows.
4070
    return false;
4071
  }
4072
  size_ = static_cast<size_t>(size.QuadPart);
4073
4074
  hMapping_ =
4075
      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
4076
4077
  // Special treatment for an empty file...
4078
  if (hMapping_ == NULL && size_ == 0) {
4079
    close();
4080
    is_open_empty_file = true;
4081
    return true;
4082
  }
4083
4084
  if (hMapping_ == NULL) {
4085
    close();
4086
    return false;
4087
  }
4088
4089
  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
4090
4091
  if (addr_ == nullptr) {
4092
    close();
4093
    return false;
4094
  }
4095
#else
4096
0
  fd_ = ::open(path, O_RDONLY);
4097
0
  if (fd_ == -1) { return false; }
4098
4099
0
  struct stat sb;
4100
0
  if (fstat(fd_, &sb) == -1) {
4101
0
    close();
4102
0
    return false;
4103
0
  }
4104
0
  size_ = static_cast<size_t>(sb.st_size);
4105
4106
0
  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
4107
4108
  // Special treatment for an empty file...
4109
0
  if (addr_ == MAP_FAILED && size_ == 0) {
4110
0
    close();
4111
0
    is_open_empty_file = true;
4112
0
    return false;
4113
0
  }
4114
0
#endif
4115
4116
0
  return true;
4117
0
}
4118
4119
0
inline bool mmap::is_open() const {
4120
0
  return is_open_empty_file ? true : addr_ != nullptr;
4121
0
}
4122
4123
0
inline size_t mmap::size() const { return size_; }
4124
4125
0
inline const char *mmap::data() const {
4126
0
  return is_open_empty_file ? "" : static_cast<const char *>(addr_);
4127
0
}
4128
4129
0
inline void mmap::close() {
4130
#if defined(_WIN32)
4131
  if (addr_) {
4132
    ::UnmapViewOfFile(addr_);
4133
    addr_ = nullptr;
4134
  }
4135
4136
  if (hMapping_) {
4137
    ::CloseHandle(hMapping_);
4138
    hMapping_ = NULL;
4139
  }
4140
4141
  if (hFile_ != INVALID_HANDLE_VALUE) {
4142
    ::CloseHandle(hFile_);
4143
    hFile_ = INVALID_HANDLE_VALUE;
4144
  }
4145
4146
  is_open_empty_file = false;
4147
#else
4148
0
  if (addr_ != nullptr) {
4149
0
    munmap(addr_, size_);
4150
0
    addr_ = nullptr;
4151
0
  }
4152
4153
0
  if (fd_ != -1) {
4154
0
    ::close(fd_);
4155
0
    fd_ = -1;
4156
0
  }
4157
0
#endif
4158
0
  size_ = 0;
4159
0
}
4160
0
inline int close_socket(socket_t sock) {
4161
#ifdef _WIN32
4162
  return closesocket(sock);
4163
#else
4164
0
  return close(sock);
4165
0
#endif
4166
0
}
4167
4168
0
template <typename T> inline ssize_t handle_EINTR(T fn) {
4169
0
  ssize_t res = 0;
4170
0
  while (true) {
4171
0
    res = fn();
4172
0
    if (res < 0 && errno == EINTR) {
4173
0
      std::this_thread::sleep_for(std::chrono::microseconds{1});
4174
0
      continue;
4175
0
    }
4176
0
    break;
4177
0
  }
4178
0
  return res;
4179
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})
4180
4181
0
inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
4182
0
  return handle_EINTR([&]() {
4183
0
    return recv(sock,
4184
#ifdef _WIN32
4185
                static_cast<char *>(ptr), static_cast<int>(size),
4186
#else
4187
0
                ptr, size,
4188
0
#endif
4189
0
                flags);
4190
0
  });
4191
0
}
4192
4193
inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
4194
0
                           int flags) {
4195
0
  return handle_EINTR([&]() {
4196
0
    return send(sock,
4197
#ifdef _WIN32
4198
                static_cast<const char *>(ptr), static_cast<int>(size),
4199
#else
4200
0
                ptr, size,
4201
0
#endif
4202
0
                flags);
4203
0
  });
4204
0
}
4205
4206
0
inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
4207
#ifdef _WIN32
4208
  return ::WSAPoll(fds, nfds, timeout);
4209
#else
4210
0
  return ::poll(fds, nfds, timeout);
4211
0
#endif
4212
0
}
4213
4214
template <bool Read>
4215
0
inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
4216
#ifdef __APPLE__
4217
  if (sock >= FD_SETSIZE) { return -1; }
4218
4219
  fd_set fds, *rfds, *wfds;
4220
  FD_ZERO(&fds);
4221
  FD_SET(sock, &fds);
4222
  rfds = (Read ? &fds : nullptr);
4223
  wfds = (Read ? nullptr : &fds);
4224
4225
  timeval tv;
4226
  tv.tv_sec = static_cast<long>(sec);
4227
  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
4228
4229
  return handle_EINTR([&]() {
4230
    return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
4231
  });
4232
#else
4233
0
  struct pollfd pfd;
4234
0
  pfd.fd = sock;
4235
0
  pfd.events = (Read ? POLLIN : POLLOUT);
4236
4237
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
4238
4239
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
4240
0
#endif
4241
0
}
Unexecuted instantiation: long httplib::detail::select_impl<true>(int, long, long)
Unexecuted instantiation: long httplib::detail::select_impl<false>(int, long, long)
4242
4243
0
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
4244
0
  return select_impl<true>(sock, sec, usec);
4245
0
}
4246
4247
0
inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
4248
0
  return select_impl<false>(sock, sec, usec);
4249
0
}
4250
4251
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
4252
0
                                        time_t usec) {
4253
#ifdef __APPLE__
4254
  if (sock >= FD_SETSIZE) { return Error::Connection; }
4255
4256
  fd_set fdsr, fdsw;
4257
  FD_ZERO(&fdsr);
4258
  FD_ZERO(&fdsw);
4259
  FD_SET(sock, &fdsr);
4260
  FD_SET(sock, &fdsw);
4261
4262
  timeval tv;
4263
  tv.tv_sec = static_cast<long>(sec);
4264
  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
4265
4266
  auto ret = handle_EINTR([&]() {
4267
    return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
4268
  });
4269
4270
  if (ret == 0) { return Error::ConnectionTimeout; }
4271
4272
  if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
4273
    auto error = 0;
4274
    socklen_t len = sizeof(error);
4275
    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
4276
                          reinterpret_cast<char *>(&error), &len);
4277
    auto successful = res >= 0 && !error;
4278
    return successful ? Error::Success : Error::Connection;
4279
  }
4280
4281
  return Error::Connection;
4282
#else
4283
0
  struct pollfd pfd_read;
4284
0
  pfd_read.fd = sock;
4285
0
  pfd_read.events = POLLIN | POLLOUT;
4286
4287
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
4288
4289
0
  auto poll_res =
4290
0
      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
4291
4292
0
  if (poll_res == 0) { return Error::ConnectionTimeout; }
4293
4294
0
  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
4295
0
    auto error = 0;
4296
0
    socklen_t len = sizeof(error);
4297
0
    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
4298
0
                          reinterpret_cast<char *>(&error), &len);
4299
0
    auto successful = res >= 0 && !error;
4300
0
    return successful ? Error::Success : Error::Connection;
4301
0
  }
4302
4303
0
  return Error::Connection;
4304
0
#endif
4305
0
}
4306
4307
0
inline bool is_socket_alive(socket_t sock) {
4308
0
  const auto val = detail::select_read(sock, 0, 0);
4309
0
  if (val == 0) {
4310
0
    return true;
4311
0
  } else if (val < 0 && errno == EBADF) {
4312
0
    return false;
4313
0
  }
4314
0
  char buf[1];
4315
0
  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
4316
0
}
4317
4318
class SocketStream final : public Stream {
4319
public:
4320
  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
4321
               time_t write_timeout_sec, time_t write_timeout_usec,
4322
               time_t max_timeout_msec = 0,
4323
               std::chrono::time_point<std::chrono::steady_clock> start_time =
4324
                   (std::chrono::steady_clock::time_point::min)());
4325
  ~SocketStream() override;
4326
4327
  bool is_readable() const override;
4328
  bool wait_readable() const override;
4329
  bool wait_writable() const override;
4330
  ssize_t read(char *ptr, size_t size) override;
4331
  ssize_t write(const char *ptr, size_t size) override;
4332
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
4333
  void get_local_ip_and_port(std::string &ip, int &port) const override;
4334
  socket_t socket() const override;
4335
  time_t duration() const override;
4336
4337
private:
4338
  socket_t sock_;
4339
  time_t read_timeout_sec_;
4340
  time_t read_timeout_usec_;
4341
  time_t write_timeout_sec_;
4342
  time_t write_timeout_usec_;
4343
  time_t max_timeout_msec_;
4344
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
4345
4346
  std::vector<char> read_buff_;
4347
  size_t read_buff_off_ = 0;
4348
  size_t read_buff_content_size_ = 0;
4349
4350
  static const size_t read_buff_size_ = 1024l * 4;
4351
};
4352
4353
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4354
class SSLSocketStream final : public Stream {
4355
public:
4356
  SSLSocketStream(
4357
      socket_t sock, SSL *ssl, time_t read_timeout_sec,
4358
      time_t read_timeout_usec, time_t write_timeout_sec,
4359
      time_t write_timeout_usec, time_t max_timeout_msec = 0,
4360
      std::chrono::time_point<std::chrono::steady_clock> start_time =
4361
          (std::chrono::steady_clock::time_point::min)());
4362
  ~SSLSocketStream() override;
4363
4364
  bool is_readable() const override;
4365
  bool wait_readable() const override;
4366
  bool wait_writable() const override;
4367
  ssize_t read(char *ptr, size_t size) override;
4368
  ssize_t write(const char *ptr, size_t size) override;
4369
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
4370
  void get_local_ip_and_port(std::string &ip, int &port) const override;
4371
  socket_t socket() const override;
4372
  time_t duration() const override;
4373
4374
private:
4375
  socket_t sock_;
4376
  SSL *ssl_;
4377
  time_t read_timeout_sec_;
4378
  time_t read_timeout_usec_;
4379
  time_t write_timeout_sec_;
4380
  time_t write_timeout_usec_;
4381
  time_t max_timeout_msec_;
4382
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
4383
};
4384
#endif
4385
4386
inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
4387
0
                       time_t keep_alive_timeout_sec) {
4388
0
  using namespace std::chrono;
4389
4390
0
  const auto interval_usec =
4391
0
      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
4392
4393
  // Avoid expensive `steady_clock::now()` call for the first time
4394
0
  if (select_read(sock, 0, interval_usec) > 0) { return true; }
4395
4396
0
  const auto start = steady_clock::now() - microseconds{interval_usec};
4397
0
  const auto timeout = seconds{keep_alive_timeout_sec};
4398
4399
0
  while (true) {
4400
0
    if (svr_sock == INVALID_SOCKET) {
4401
0
      break; // Server socket is closed
4402
0
    }
4403
4404
0
    auto val = select_read(sock, 0, interval_usec);
4405
0
    if (val < 0) {
4406
0
      break; // Ssocket error
4407
0
    } else if (val == 0) {
4408
0
      if (steady_clock::now() - start > timeout) {
4409
0
        break; // Timeout
4410
0
      }
4411
0
    } else {
4412
0
      return true; // Ready for read
4413
0
    }
4414
0
  }
4415
4416
0
  return false;
4417
0
}
4418
4419
template <typename T>
4420
inline bool
4421
process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
4422
                           size_t keep_alive_max_count,
4423
0
                           time_t keep_alive_timeout_sec, T callback) {
4424
0
  assert(keep_alive_max_count > 0);
4425
0
  auto ret = false;
4426
0
  auto count = keep_alive_max_count;
4427
0
  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
4428
0
    auto close_connection = count == 1;
4429
0
    auto connection_closed = false;
4430
0
    ret = callback(close_connection, connection_closed);
4431
0
    if (!ret || connection_closed) { break; }
4432
0
    count--;
4433
0
  }
4434
0
  return ret;
4435
0
}
4436
4437
template <typename T>
4438
inline bool
4439
process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
4440
                      size_t keep_alive_max_count,
4441
                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,
4442
                      time_t read_timeout_usec, time_t write_timeout_sec,
4443
0
                      time_t write_timeout_usec, T callback) {
4444
0
  return process_server_socket_core(
4445
0
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
4446
0
      [&](bool close_connection, bool &connection_closed) {
4447
0
        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
4448
0
                          write_timeout_sec, write_timeout_usec);
4449
0
        return callback(strm, close_connection, connection_closed);
4450
0
      });
4451
0
}
4452
4453
inline bool process_client_socket(
4454
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
4455
    time_t write_timeout_sec, time_t write_timeout_usec,
4456
    time_t max_timeout_msec,
4457
    std::chrono::time_point<std::chrono::steady_clock> start_time,
4458
0
    std::function<bool(Stream &)> callback) {
4459
0
  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
4460
0
                    write_timeout_sec, write_timeout_usec, max_timeout_msec,
4461
0
                    start_time);
4462
0
  return callback(strm);
4463
0
}
4464
4465
0
inline int shutdown_socket(socket_t sock) {
4466
#ifdef _WIN32
4467
  return shutdown(sock, SD_BOTH);
4468
#else
4469
0
  return shutdown(sock, SHUT_RDWR);
4470
0
#endif
4471
0
}
4472
4473
0
inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
4474
0
  if (s.size() > 1 && s[0] == '\0') {
4475
0
    auto ret = s;
4476
0
    ret[0] = '@';
4477
0
    return ret;
4478
0
  }
4479
0
  return s;
4480
0
}
4481
4482
inline std::string
4483
0
unescape_abstract_namespace_unix_domain(const std::string &s) {
4484
0
  if (s.size() > 1 && s[0] == '@') {
4485
0
    auto ret = s;
4486
0
    ret[0] = '\0';
4487
0
    return ret;
4488
0
  }
4489
0
  return s;
4490
0
}
4491
4492
inline int getaddrinfo_with_timeout(const char *node, const char *service,
4493
                                    const struct addrinfo *hints,
4494
0
                                    struct addrinfo **res, time_t timeout_sec) {
4495
#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
4496
  if (timeout_sec <= 0) {
4497
    // No timeout specified, use standard getaddrinfo
4498
    return getaddrinfo(node, service, hints, res);
4499
  }
4500
4501
#ifdef _WIN32
4502
  // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
4503
  OVERLAPPED overlapped = {0};
4504
  HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
4505
  if (!event) { return EAI_FAIL; }
4506
4507
  overlapped.hEvent = event;
4508
4509
  PADDRINFOEXW result_addrinfo = nullptr;
4510
  HANDLE cancel_handle = nullptr;
4511
4512
  ADDRINFOEXW hints_ex = {0};
4513
  if (hints) {
4514
    hints_ex.ai_flags = hints->ai_flags;
4515
    hints_ex.ai_family = hints->ai_family;
4516
    hints_ex.ai_socktype = hints->ai_socktype;
4517
    hints_ex.ai_protocol = hints->ai_protocol;
4518
  }
4519
4520
  auto wnode = u8string_to_wstring(node);
4521
  auto wservice = u8string_to_wstring(service);
4522
4523
  auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
4524
                              hints ? &hints_ex : nullptr, &result_addrinfo,
4525
                              nullptr, &overlapped, nullptr, &cancel_handle);
4526
4527
  if (ret == WSA_IO_PENDING) {
4528
    auto wait_result =
4529
        ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
4530
    if (wait_result == WAIT_TIMEOUT) {
4531
      if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
4532
      ::CloseHandle(event);
4533
      return EAI_AGAIN;
4534
    }
4535
4536
    DWORD bytes_returned;
4537
    if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
4538
                               &bytes_returned, FALSE)) {
4539
      ::CloseHandle(event);
4540
      return ::WSAGetLastError();
4541
    }
4542
  }
4543
4544
  ::CloseHandle(event);
4545
4546
  if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
4547
    *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
4548
    return 0;
4549
  }
4550
4551
  return ret;
4552
#elif TARGET_OS_MAC
4553
  if (!node) { return EAI_NONAME; }
4554
  // macOS implementation using CFHost API for asynchronous DNS resolution
4555
  CFStringRef hostname_ref = CFStringCreateWithCString(
4556
      kCFAllocatorDefault, node, kCFStringEncodingUTF8);
4557
  if (!hostname_ref) { return EAI_MEMORY; }
4558
4559
  CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
4560
  CFRelease(hostname_ref);
4561
  if (!host_ref) { return EAI_MEMORY; }
4562
4563
  // Set up context for callback
4564
  struct CFHostContext {
4565
    bool completed = false;
4566
    bool success = false;
4567
    CFArrayRef addresses = nullptr;
4568
    std::mutex mutex;
4569
    std::condition_variable cv;
4570
  } context;
4571
4572
  CFHostClientContext client_context;
4573
  memset(&client_context, 0, sizeof(client_context));
4574
  client_context.info = &context;
4575
4576
  // Set callback
4577
  auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
4578
                     const CFStreamError *error, void *info) {
4579
    auto ctx = static_cast<CFHostContext *>(info);
4580
    std::lock_guard<std::mutex> lock(ctx->mutex);
4581
4582
    if (error && error->error != 0) {
4583
      ctx->success = false;
4584
    } else {
4585
      Boolean hasBeenResolved;
4586
      ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
4587
      if (ctx->addresses && hasBeenResolved) {
4588
        CFRetain(ctx->addresses);
4589
        ctx->success = true;
4590
      } else {
4591
        ctx->success = false;
4592
      }
4593
    }
4594
    ctx->completed = true;
4595
    ctx->cv.notify_one();
4596
  };
4597
4598
  if (!CFHostSetClient(host_ref, callback, &client_context)) {
4599
    CFRelease(host_ref);
4600
    return EAI_SYSTEM;
4601
  }
4602
4603
  // Schedule on run loop
4604
  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
4605
  CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
4606
4607
  // Start resolution
4608
  CFStreamError stream_error;
4609
  if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
4610
    CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
4611
    CFRelease(host_ref);
4612
    return EAI_FAIL;
4613
  }
4614
4615
  // Wait for completion with timeout
4616
  auto timeout_time =
4617
      std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
4618
  bool timed_out = false;
4619
4620
  {
4621
    std::unique_lock<std::mutex> lock(context.mutex);
4622
4623
    while (!context.completed) {
4624
      auto now = std::chrono::steady_clock::now();
4625
      if (now >= timeout_time) {
4626
        timed_out = true;
4627
        break;
4628
      }
4629
4630
      // Run the runloop for a short time
4631
      lock.unlock();
4632
      CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
4633
      lock.lock();
4634
    }
4635
  }
4636
4637
  // Clean up
4638
  CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
4639
  CFHostSetClient(host_ref, nullptr, nullptr);
4640
4641
  if (timed_out || !context.completed) {
4642
    CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
4643
    CFRelease(host_ref);
4644
    return EAI_AGAIN;
4645
  }
4646
4647
  if (!context.success || !context.addresses) {
4648
    CFRelease(host_ref);
4649
    return EAI_NODATA;
4650
  }
4651
4652
  // Convert CFArray to addrinfo
4653
  CFIndex count = CFArrayGetCount(context.addresses);
4654
  if (count == 0) {
4655
    CFRelease(context.addresses);
4656
    CFRelease(host_ref);
4657
    return EAI_NODATA;
4658
  }
4659
4660
  struct addrinfo *result_addrinfo = nullptr;
4661
  struct addrinfo **current = &result_addrinfo;
4662
4663
  for (CFIndex i = 0; i < count; i++) {
4664
    CFDataRef addr_data =
4665
        static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
4666
    if (!addr_data) continue;
4667
4668
    const struct sockaddr *sockaddr_ptr =
4669
        reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
4670
    socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
4671
4672
    // Allocate addrinfo structure
4673
    *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
4674
    if (!*current) {
4675
      freeaddrinfo(result_addrinfo);
4676
      CFRelease(context.addresses);
4677
      CFRelease(host_ref);
4678
      return EAI_MEMORY;
4679
    }
4680
4681
    memset(*current, 0, sizeof(struct addrinfo));
4682
4683
    // Set up addrinfo fields
4684
    (*current)->ai_family = sockaddr_ptr->sa_family;
4685
    (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
4686
    (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
4687
    (*current)->ai_addrlen = sockaddr_len;
4688
4689
    // Copy sockaddr
4690
    (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
4691
    if (!(*current)->ai_addr) {
4692
      freeaddrinfo(result_addrinfo);
4693
      CFRelease(context.addresses);
4694
      CFRelease(host_ref);
4695
      return EAI_MEMORY;
4696
    }
4697
    memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
4698
4699
    // Set port if service is specified
4700
    if (service && strlen(service) > 0) {
4701
      int port = atoi(service);
4702
      if (port > 0) {
4703
        if (sockaddr_ptr->sa_family == AF_INET) {
4704
          reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
4705
              ->sin_port = htons(static_cast<uint16_t>(port));
4706
        } else if (sockaddr_ptr->sa_family == AF_INET6) {
4707
          reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
4708
              ->sin6_port = htons(static_cast<uint16_t>(port));
4709
        }
4710
      }
4711
    }
4712
4713
    current = &((*current)->ai_next);
4714
  }
4715
4716
  CFRelease(context.addresses);
4717
  CFRelease(host_ref);
4718
4719
  *res = result_addrinfo;
4720
  return 0;
4721
#elif defined(_GNU_SOURCE) && defined(__GLIBC__) &&                            \
4722
    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
4723
  // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
4724
  struct gaicb request;
4725
  struct gaicb *requests[1] = {&request};
4726
  struct sigevent sevp;
4727
  struct timespec timeout;
4728
4729
  // Initialize the request structure
4730
  memset(&request, 0, sizeof(request));
4731
  request.ar_name = node;
4732
  request.ar_service = service;
4733
  request.ar_request = hints;
4734
4735
  // Set up timeout
4736
  timeout.tv_sec = timeout_sec;
4737
  timeout.tv_nsec = 0;
4738
4739
  // Initialize sigevent structure (not used, but required)
4740
  memset(&sevp, 0, sizeof(sevp));
4741
  sevp.sigev_notify = SIGEV_NONE;
4742
4743
  // Start asynchronous resolution
4744
  int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
4745
  if (start_result != 0) { return start_result; }
4746
4747
  // Wait for completion with timeout
4748
  int wait_result =
4749
      gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
4750
4751
  if (wait_result == 0 || wait_result == EAI_ALLDONE) {
4752
    // Completed successfully, get the result
4753
    int gai_result = gai_error(&request);
4754
    if (gai_result == 0) {
4755
      *res = request.ar_result;
4756
      return 0;
4757
    } else {
4758
      // Clean up on error
4759
      if (request.ar_result) { freeaddrinfo(request.ar_result); }
4760
      return gai_result;
4761
    }
4762
  } else if (wait_result == EAI_AGAIN) {
4763
    // Timeout occurred, cancel the request
4764
    gai_cancel(&request);
4765
    return EAI_AGAIN;
4766
  } else {
4767
    // Other error occurred
4768
    gai_cancel(&request);
4769
    return wait_result;
4770
  }
4771
#else
4772
  // Fallback implementation using thread-based timeout for other Unix systems
4773
4774
  struct GetAddrInfoState {
4775
    ~GetAddrInfoState() {
4776
      if (info) { freeaddrinfo(info); }
4777
    }
4778
4779
    std::mutex mutex;
4780
    std::condition_variable result_cv;
4781
    bool completed = false;
4782
    int result = EAI_SYSTEM;
4783
    std::string node;
4784
    std::string service;
4785
    struct addrinfo hints;
4786
    struct addrinfo *info = nullptr;
4787
  };
4788
4789
  // Allocate on the heap, so the resolver thread can keep using the data.
4790
  auto state = std::make_shared<GetAddrInfoState>();
4791
  state->node = node;
4792
  state->service = service;
4793
  state->hints = *hints;
4794
4795
  std::thread resolve_thread([state]() {
4796
    auto thread_result =
4797
        getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
4798
                    &state->info);
4799
4800
    std::lock_guard<std::mutex> lock(state->mutex);
4801
    state->result = thread_result;
4802
    state->completed = true;
4803
    state->result_cv.notify_one();
4804
  });
4805
4806
  // Wait for completion or timeout
4807
  std::unique_lock<std::mutex> lock(state->mutex);
4808
  auto finished =
4809
      state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
4810
                                [&] { return state->completed; });
4811
4812
  if (finished) {
4813
    // Operation completed within timeout
4814
    resolve_thread.join();
4815
    *res = state->info;
4816
    state->info = nullptr; // Pass ownership to caller
4817
    return state->result;
4818
  } else {
4819
    // Timeout occurred
4820
    resolve_thread.detach(); // Let the thread finish in background
4821
    return EAI_AGAIN;        // Return timeout error
4822
  }
4823
#endif
4824
#else
4825
0
  (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
4826
0
  return getaddrinfo(node, service, hints, res);
4827
0
#endif
4828
0
}
4829
4830
template <typename BindOrConnect>
4831
socket_t create_socket(const std::string &host, const std::string &ip, int port,
4832
                       int address_family, int socket_flags, bool tcp_nodelay,
4833
                       bool ipv6_v6only, SocketOptions socket_options,
4834
0
                       BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
4835
  // Get address info
4836
0
  const char *node = nullptr;
4837
0
  struct addrinfo hints;
4838
0
  struct addrinfo *result;
4839
4840
0
  memset(&hints, 0, sizeof(struct addrinfo));
4841
0
  hints.ai_socktype = SOCK_STREAM;
4842
0
  hints.ai_protocol = IPPROTO_IP;
4843
4844
0
  if (!ip.empty()) {
4845
0
    node = ip.c_str();
4846
    // Ask getaddrinfo to convert IP in c-string to address
4847
0
    hints.ai_family = AF_UNSPEC;
4848
0
    hints.ai_flags = AI_NUMERICHOST;
4849
0
  } else {
4850
0
    if (!host.empty()) { node = host.c_str(); }
4851
0
    hints.ai_family = address_family;
4852
0
    hints.ai_flags = socket_flags;
4853
0
  }
4854
4855
0
#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
4856
0
  if (hints.ai_family == AF_UNIX) {
4857
0
    const auto addrlen = host.length();
4858
0
    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
4859
4860
0
#ifdef SOCK_CLOEXEC
4861
0
    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
4862
0
                       hints.ai_protocol);
4863
#else
4864
    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
4865
#endif
4866
4867
0
    if (sock != INVALID_SOCKET) {
4868
0
      sockaddr_un addr{};
4869
0
      addr.sun_family = AF_UNIX;
4870
4871
0
      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
4872
0
      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
4873
4874
0
      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
4875
0
      hints.ai_addrlen = static_cast<socklen_t>(
4876
0
          sizeof(addr) - sizeof(addr.sun_path) + addrlen);
4877
4878
#ifndef SOCK_CLOEXEC
4879
#ifndef _WIN32
4880
      fcntl(sock, F_SETFD, FD_CLOEXEC);
4881
#endif
4882
#endif
4883
4884
0
      if (socket_options) { socket_options(sock); }
4885
4886
#ifdef _WIN32
4887
      // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
4888
      // remove the option.
4889
      detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
4890
#endif
4891
4892
0
      bool dummy;
4893
0
      if (!bind_or_connect(sock, hints, dummy)) {
4894
0
        close_socket(sock);
4895
0
        sock = INVALID_SOCKET;
4896
0
      }
4897
0
    }
4898
0
    return sock;
4899
0
  }
4900
0
#endif
4901
4902
0
  auto service = std::to_string(port);
4903
4904
0
  if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
4905
0
                               timeout_sec)) {
4906
0
#if defined __linux__ && !defined __ANDROID__
4907
0
    res_init();
4908
0
#endif
4909
0
    return INVALID_SOCKET;
4910
0
  }
4911
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
4912
4913
0
  for (auto rp = result; rp; rp = rp->ai_next) {
4914
    // Create a socket
4915
#ifdef _WIN32
4916
    auto sock =
4917
        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
4918
                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
4919
    /**
4920
     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
4921
     * and above the socket creation fails on older Windows Systems.
4922
     *
4923
     * Let's try to create a socket the old way in this case.
4924
     *
4925
     * Reference:
4926
     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
4927
     *
4928
     * WSA_FLAG_NO_HANDLE_INHERIT:
4929
     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
4930
     * SP1, and later
4931
     *
4932
     */
4933
    if (sock == INVALID_SOCKET) {
4934
      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
4935
    }
4936
#else
4937
4938
0
#ifdef SOCK_CLOEXEC
4939
0
    auto sock =
4940
0
        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
4941
#else
4942
    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
4943
#endif
4944
4945
0
#endif
4946
0
    if (sock == INVALID_SOCKET) { continue; }
4947
4948
#if !defined _WIN32 && !defined SOCK_CLOEXEC
4949
    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
4950
      close_socket(sock);
4951
      continue;
4952
    }
4953
#endif
4954
4955
0
    if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
4956
4957
0
    if (rp->ai_family == AF_INET6) {
4958
0
      set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
4959
0
    }
4960
4961
0
    if (socket_options) { socket_options(sock); }
4962
4963
    // bind or connect
4964
0
    auto quit = false;
4965
0
    if (bind_or_connect(sock, *rp, quit)) { return sock; }
4966
4967
0
    close_socket(sock);
4968
4969
0
    if (quit) { break; }
4970
0
  }
4971
4972
0
  return INVALID_SOCKET;
4973
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)
4974
4975
0
inline void set_nonblocking(socket_t sock, bool nonblocking) {
4976
#ifdef _WIN32
4977
  auto flags = nonblocking ? 1UL : 0UL;
4978
  ioctlsocket(sock, FIONBIO, &flags);
4979
#else
4980
0
  auto flags = fcntl(sock, F_GETFL, 0);
4981
0
  fcntl(sock, F_SETFL,
4982
0
        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
4983
0
#endif
4984
0
}
4985
4986
0
inline bool is_connection_error() {
4987
#ifdef _WIN32
4988
  return WSAGetLastError() != WSAEWOULDBLOCK;
4989
#else
4990
0
  return errno != EINPROGRESS;
4991
0
#endif
4992
0
}
4993
4994
0
inline bool bind_ip_address(socket_t sock, const std::string &host) {
4995
0
  struct addrinfo hints;
4996
0
  struct addrinfo *result;
4997
4998
0
  memset(&hints, 0, sizeof(struct addrinfo));
4999
0
  hints.ai_family = AF_UNSPEC;
5000
0
  hints.ai_socktype = SOCK_STREAM;
5001
0
  hints.ai_protocol = 0;
5002
5003
0
  if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
5004
0
    return false;
5005
0
  }
5006
5007
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5008
5009
0
  auto ret = false;
5010
0
  for (auto rp = result; rp; rp = rp->ai_next) {
5011
0
    const auto &ai = *rp;
5012
0
    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
5013
0
      ret = true;
5014
0
      break;
5015
0
    }
5016
0
  }
5017
5018
0
  return ret;
5019
0
}
5020
5021
#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
5022
#define USE_IF2IP
5023
#endif
5024
5025
#ifdef USE_IF2IP
5026
0
inline std::string if2ip(int address_family, const std::string &ifn) {
5027
0
  struct ifaddrs *ifap;
5028
0
  getifaddrs(&ifap);
5029
0
  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
5030
5031
0
  std::string addr_candidate;
5032
0
  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
5033
0
    if (ifa->ifa_addr && ifn == ifa->ifa_name &&
5034
0
        (AF_UNSPEC == address_family ||
5035
0
         ifa->ifa_addr->sa_family == address_family)) {
5036
0
      if (ifa->ifa_addr->sa_family == AF_INET) {
5037
0
        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
5038
0
        char buf[INET_ADDRSTRLEN];
5039
0
        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
5040
0
          return std::string(buf, INET_ADDRSTRLEN);
5041
0
        }
5042
0
      } else if (ifa->ifa_addr->sa_family == AF_INET6) {
5043
0
        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
5044
0
        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
5045
0
          char buf[INET6_ADDRSTRLEN] = {};
5046
0
          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
5047
            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
5048
0
            auto s6_addr_head = sa->sin6_addr.s6_addr[0];
5049
0
            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
5050
0
              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
5051
0
            } else {
5052
0
              return std::string(buf, INET6_ADDRSTRLEN);
5053
0
            }
5054
0
          }
5055
0
        }
5056
0
      }
5057
0
    }
5058
0
  }
5059
0
  return addr_candidate;
5060
0
}
5061
#endif
5062
5063
inline socket_t create_client_socket(
5064
    const std::string &host, const std::string &ip, int port,
5065
    int address_family, bool tcp_nodelay, bool ipv6_v6only,
5066
    SocketOptions socket_options, time_t connection_timeout_sec,
5067
    time_t connection_timeout_usec, time_t read_timeout_sec,
5068
    time_t read_timeout_usec, time_t write_timeout_sec,
5069
0
    time_t write_timeout_usec, const std::string &intf, Error &error) {
5070
0
  auto sock = create_socket(
5071
0
      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
5072
0
      std::move(socket_options),
5073
0
      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
5074
0
        if (!intf.empty()) {
5075
0
#ifdef USE_IF2IP
5076
0
          auto ip_from_if = if2ip(address_family, intf);
5077
0
          if (ip_from_if.empty()) { ip_from_if = intf; }
5078
0
          if (!bind_ip_address(sock2, ip_from_if)) {
5079
0
            error = Error::BindIPAddress;
5080
0
            return false;
5081
0
          }
5082
0
#endif
5083
0
        }
5084
5085
0
        set_nonblocking(sock2, true);
5086
5087
0
        auto ret =
5088
0
            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
5089
5090
0
        if (ret < 0) {
5091
0
          if (is_connection_error()) {
5092
0
            error = Error::Connection;
5093
0
            return false;
5094
0
          }
5095
0
          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
5096
0
                                             connection_timeout_usec);
5097
0
          if (error != Error::Success) {
5098
0
            if (error == Error::ConnectionTimeout) { quit = true; }
5099
0
            return false;
5100
0
          }
5101
0
        }
5102
5103
0
        set_nonblocking(sock2, false);
5104
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
5105
0
                            read_timeout_usec);
5106
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
5107
0
                            write_timeout_usec);
5108
5109
0
        error = Error::Success;
5110
0
        return true;
5111
0
      },
5112
0
      connection_timeout_sec); // Pass DNS timeout
5113
5114
0
  if (sock != INVALID_SOCKET) {
5115
0
    error = Error::Success;
5116
0
  } else {
5117
0
    if (error == Error::Success) { error = Error::Connection; }
5118
0
  }
5119
5120
0
  return sock;
5121
0
}
5122
5123
inline bool get_ip_and_port(const struct sockaddr_storage &addr,
5124
0
                            socklen_t addr_len, std::string &ip, int &port) {
5125
0
  if (addr.ss_family == AF_INET) {
5126
0
    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
5127
0
  } else if (addr.ss_family == AF_INET6) {
5128
0
    port =
5129
0
        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
5130
0
  } else {
5131
0
    return false;
5132
0
  }
5133
5134
0
  std::array<char, NI_MAXHOST> ipstr{};
5135
0
  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
5136
0
                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
5137
0
                  0, NI_NUMERICHOST)) {
5138
0
    return false;
5139
0
  }
5140
5141
0
  ip = ipstr.data();
5142
0
  return true;
5143
0
}
5144
5145
0
inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
5146
0
  struct sockaddr_storage addr;
5147
0
  socklen_t addr_len = sizeof(addr);
5148
0
  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
5149
0
                   &addr_len)) {
5150
0
    get_ip_and_port(addr, addr_len, ip, port);
5151
0
  }
5152
0
}
5153
5154
0
inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
5155
0
  struct sockaddr_storage addr;
5156
0
  socklen_t addr_len = sizeof(addr);
5157
5158
0
  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
5159
0
                   &addr_len)) {
5160
0
#ifndef _WIN32
5161
0
    if (addr.ss_family == AF_UNIX) {
5162
0
#if defined(__linux__)
5163
0
      struct ucred ucred;
5164
0
      socklen_t len = sizeof(ucred);
5165
0
      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
5166
0
        port = ucred.pid;
5167
0
      }
5168
#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
5169
      pid_t pid;
5170
      socklen_t len = sizeof(pid);
5171
      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
5172
        port = pid;
5173
      }
5174
#endif
5175
0
      return;
5176
0
    }
5177
0
#endif
5178
0
    get_ip_and_port(addr, addr_len, ip, port);
5179
0
  }
5180
0
}
5181
5182
inline constexpr unsigned int str2tag_core(const char *s, size_t l,
5183
626k
                                           unsigned int h) {
5184
626k
  return (l == 0)
5185
626k
             ? h
5186
626k
             : str2tag_core(
5187
574k
                   s + 1, l - 1,
5188
                   // Unsets the 6 high bits of h, therefore no overflow happens
5189
574k
                   (((std::numeric_limits<unsigned int>::max)() >> 6) &
5190
574k
                    h * 33) ^
5191
574k
                       static_cast<unsigned char>(*s));
5192
626k
}
5193
5194
52.5k
inline unsigned int str2tag(const std::string &s) {
5195
52.5k
  return str2tag_core(s.data(), s.size(), 0);
5196
52.5k
}
5197
5198
namespace udl {
5199
5200
0
inline constexpr unsigned int operator""_t(const char *s, size_t l) {
5201
0
  return str2tag_core(s, l, 0);
5202
0
}
5203
5204
} // namespace udl
5205
5206
inline std::string
5207
find_content_type(const std::string &path,
5208
                  const std::map<std::string, std::string> &user_data,
5209
0
                  const std::string &default_content_type) {
5210
0
  auto ext = file_extension(path);
5211
5212
0
  auto it = user_data.find(ext);
5213
0
  if (it != user_data.end()) { return it->second; }
5214
5215
0
  using udl::operator""_t;
5216
5217
0
  switch (str2tag(ext)) {
5218
0
  default: return default_content_type;
5219
5220
0
  case "css"_t: return "text/css";
5221
0
  case "csv"_t: return "text/csv";
5222
0
  case "htm"_t:
5223
0
  case "html"_t: return "text/html";
5224
0
  case "js"_t:
5225
0
  case "mjs"_t: return "text/javascript";
5226
0
  case "txt"_t: return "text/plain";
5227
0
  case "vtt"_t: return "text/vtt";
5228
5229
0
  case "apng"_t: return "image/apng";
5230
0
  case "avif"_t: return "image/avif";
5231
0
  case "bmp"_t: return "image/bmp";
5232
0
  case "gif"_t: return "image/gif";
5233
0
  case "png"_t: return "image/png";
5234
0
  case "svg"_t: return "image/svg+xml";
5235
0
  case "webp"_t: return "image/webp";
5236
0
  case "ico"_t: return "image/x-icon";
5237
0
  case "tif"_t: return "image/tiff";
5238
0
  case "tiff"_t: return "image/tiff";
5239
0
  case "jpg"_t:
5240
0
  case "jpeg"_t: return "image/jpeg";
5241
5242
0
  case "mp4"_t: return "video/mp4";
5243
0
  case "mpeg"_t: return "video/mpeg";
5244
0
  case "webm"_t: return "video/webm";
5245
5246
0
  case "mp3"_t: return "audio/mp3";
5247
0
  case "mpga"_t: return "audio/mpeg";
5248
0
  case "weba"_t: return "audio/webm";
5249
0
  case "wav"_t: return "audio/wave";
5250
5251
0
  case "otf"_t: return "font/otf";
5252
0
  case "ttf"_t: return "font/ttf";
5253
0
  case "woff"_t: return "font/woff";
5254
0
  case "woff2"_t: return "font/woff2";
5255
5256
0
  case "7z"_t: return "application/x-7z-compressed";
5257
0
  case "atom"_t: return "application/atom+xml";
5258
0
  case "pdf"_t: return "application/pdf";
5259
0
  case "json"_t: return "application/json";
5260
0
  case "rss"_t: return "application/rss+xml";
5261
0
  case "tar"_t: return "application/x-tar";
5262
0
  case "xht"_t:
5263
0
  case "xhtml"_t: return "application/xhtml+xml";
5264
0
  case "xslt"_t: return "application/xslt+xml";
5265
0
  case "xml"_t: return "application/xml";
5266
0
  case "gz"_t: return "application/gzip";
5267
0
  case "zip"_t: return "application/zip";
5268
0
  case "wasm"_t: return "application/wasm";
5269
0
  }
5270
0
}
5271
5272
1.44k
inline bool can_compress_content_type(const std::string &content_type) {
5273
1.44k
  using udl::operator""_t;
5274
5275
1.44k
  auto tag = str2tag(content_type);
5276
5277
1.44k
  switch (tag) {
5278
0
  case "image/svg+xml"_t:
5279
0
  case "application/javascript"_t:
5280
0
  case "application/json"_t:
5281
0
  case "application/xml"_t:
5282
0
  case "application/protobuf"_t:
5283
0
  case "application/xhtml+xml"_t: return true;
5284
5285
0
  case "text/event-stream"_t: return false;
5286
5287
1.44k
  default: return !content_type.rfind("text/", 0);
5288
1.44k
  }
5289
1.44k
}
5290
5291
1.44k
inline EncodingType encoding_type(const Request &req, const Response &res) {
5292
1.44k
  auto ret =
5293
1.44k
      detail::can_compress_content_type(res.get_header_value("Content-Type"));
5294
1.44k
  if (!ret) { return EncodingType::None; }
5295
5296
1.20k
  const auto &s = req.get_header_value("Accept-Encoding");
5297
1.20k
  (void)(s);
5298
5299
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
5300
  // TODO: 'Accept-Encoding' has br, not br;q=0
5301
  ret = s.find("br") != std::string::npos;
5302
  if (ret) { return EncodingType::Brotli; }
5303
#endif
5304
5305
1.20k
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
5306
  // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
5307
1.20k
  ret = s.find("gzip") != std::string::npos;
5308
1.20k
  if (ret) { return EncodingType::Gzip; }
5309
1.18k
#endif
5310
5311
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
5312
  // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
5313
  ret = s.find("zstd") != std::string::npos;
5314
  if (ret) { return EncodingType::Zstd; }
5315
#endif
5316
5317
1.18k
  return EncodingType::None;
5318
1.20k
}
5319
5320
inline bool nocompressor::compress(const char *data, size_t data_length,
5321
0
                                   bool /*last*/, Callback callback) {
5322
0
  if (!data_length) { return true; }
5323
0
  return callback(data, data_length);
5324
0
}
5325
5326
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
5327
18
inline gzip_compressor::gzip_compressor() {
5328
18
  std::memset(&strm_, 0, sizeof(strm_));
5329
18
  strm_.zalloc = Z_NULL;
5330
18
  strm_.zfree = Z_NULL;
5331
18
  strm_.opaque = Z_NULL;
5332
5333
18
  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
5334
18
                           Z_DEFAULT_STRATEGY) == Z_OK;
5335
18
}
5336
5337
18
inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
5338
5339
inline bool gzip_compressor::compress(const char *data, size_t data_length,
5340
18
                                      bool last, Callback callback) {
5341
18
  assert(is_valid_);
5342
5343
18
  do {
5344
18
    constexpr size_t max_avail_in =
5345
18
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
5346
5347
18
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
5348
18
        (std::min)(data_length, max_avail_in));
5349
18
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
5350
5351
18
    data_length -= strm_.avail_in;
5352
18
    data += strm_.avail_in;
5353
5354
18
    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
5355
18
    auto ret = Z_OK;
5356
5357
18
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5358
18
    do {
5359
18
      strm_.avail_out = static_cast<uInt>(buff.size());
5360
18
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
5361
5362
18
      ret = deflate(&strm_, flush);
5363
18
      if (ret == Z_STREAM_ERROR) { return false; }
5364
5365
18
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
5366
0
        return false;
5367
0
      }
5368
18
    } while (strm_.avail_out == 0);
5369
5370
18
    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
5371
18
           (flush == Z_NO_FLUSH && ret == Z_OK));
5372
18
    assert(strm_.avail_in == 0);
5373
18
  } while (data_length > 0);
5374
5375
18
  return true;
5376
18
}
5377
5378
581
inline gzip_decompressor::gzip_decompressor() {
5379
581
  std::memset(&strm_, 0, sizeof(strm_));
5380
581
  strm_.zalloc = Z_NULL;
5381
581
  strm_.zfree = Z_NULL;
5382
581
  strm_.opaque = Z_NULL;
5383
5384
  // 15 is the value of wbits, which should be at the maximum possible value
5385
  // to ensure that any gzip stream can be decoded. The offset of 32 specifies
5386
  // that the stream type should be automatically detected either gzip or
5387
  // deflate.
5388
581
  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
5389
581
}
5390
5391
581
inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
5392
5393
581
inline bool gzip_decompressor::is_valid() const { return is_valid_; }
5394
5395
inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
5396
2.28k
                                          Callback callback) {
5397
2.28k
  assert(is_valid_);
5398
5399
2.28k
  auto ret = Z_OK;
5400
5401
2.28k
  do {
5402
2.28k
    constexpr size_t max_avail_in =
5403
2.28k
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
5404
5405
2.28k
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
5406
2.28k
        (std::min)(data_length, max_avail_in));
5407
2.28k
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
5408
5409
2.28k
    data_length -= strm_.avail_in;
5410
2.28k
    data += strm_.avail_in;
5411
5412
2.28k
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5413
87.2k
    while (strm_.avail_in > 0 && ret == Z_OK) {
5414
85.0k
      strm_.avail_out = static_cast<uInt>(buff.size());
5415
85.0k
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
5416
5417
85.0k
      ret = inflate(&strm_, Z_NO_FLUSH);
5418
5419
85.0k
      assert(ret != Z_STREAM_ERROR);
5420
85.0k
      switch (ret) {
5421
1
      case Z_NEED_DICT:
5422
29
      case Z_DATA_ERROR:
5423
29
      case Z_MEM_ERROR: inflateEnd(&strm_); return false;
5424
85.0k
      }
5425
5426
84.9k
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
5427
51
        return false;
5428
51
      }
5429
84.9k
    }
5430
5431
2.20k
    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
5432
5433
2.20k
  } while (data_length > 0);
5434
5435
2.20k
  return true;
5436
2.28k
}
5437
#endif
5438
5439
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
5440
inline brotli_compressor::brotli_compressor() {
5441
  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
5442
}
5443
5444
inline brotli_compressor::~brotli_compressor() {
5445
  BrotliEncoderDestroyInstance(state_);
5446
}
5447
5448
inline bool brotli_compressor::compress(const char *data, size_t data_length,
5449
                                        bool last, Callback callback) {
5450
  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5451
5452
  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
5453
  auto available_in = data_length;
5454
  auto next_in = reinterpret_cast<const uint8_t *>(data);
5455
5456
  for (;;) {
5457
    if (last) {
5458
      if (BrotliEncoderIsFinished(state_)) { break; }
5459
    } else {
5460
      if (!available_in) { break; }
5461
    }
5462
5463
    auto available_out = buff.size();
5464
    auto next_out = buff.data();
5465
5466
    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
5467
                                     &available_out, &next_out, nullptr)) {
5468
      return false;
5469
    }
5470
5471
    auto output_bytes = buff.size() - available_out;
5472
    if (output_bytes) {
5473
      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
5474
    }
5475
  }
5476
5477
  return true;
5478
}
5479
5480
inline brotli_decompressor::brotli_decompressor() {
5481
  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
5482
  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
5483
                        : BROTLI_DECODER_RESULT_ERROR;
5484
}
5485
5486
inline brotli_decompressor::~brotli_decompressor() {
5487
  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
5488
}
5489
5490
inline bool brotli_decompressor::is_valid() const { return decoder_s; }
5491
5492
inline bool brotli_decompressor::decompress(const char *data,
5493
                                            size_t data_length,
5494
                                            Callback callback) {
5495
  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
5496
      decoder_r == BROTLI_DECODER_RESULT_ERROR) {
5497
    return 0;
5498
  }
5499
5500
  auto next_in = reinterpret_cast<const uint8_t *>(data);
5501
  size_t avail_in = data_length;
5502
  size_t total_out;
5503
5504
  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
5505
5506
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5507
  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
5508
    char *next_out = buff.data();
5509
    size_t avail_out = buff.size();
5510
5511
    decoder_r = BrotliDecoderDecompressStream(
5512
        decoder_s, &avail_in, &next_in, &avail_out,
5513
        reinterpret_cast<uint8_t **>(&next_out), &total_out);
5514
5515
    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
5516
5517
    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
5518
  }
5519
5520
  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
5521
         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
5522
}
5523
#endif
5524
5525
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
5526
inline zstd_compressor::zstd_compressor() {
5527
  ctx_ = ZSTD_createCCtx();
5528
  ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
5529
}
5530
5531
inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
5532
5533
inline bool zstd_compressor::compress(const char *data, size_t data_length,
5534
                                      bool last, Callback callback) {
5535
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5536
5537
  ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
5538
  ZSTD_inBuffer input = {data, data_length, 0};
5539
5540
  bool finished;
5541
  do {
5542
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
5543
    size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
5544
5545
    if (ZSTD_isError(remaining)) { return false; }
5546
5547
    if (!callback(buff.data(), output.pos)) { return false; }
5548
5549
    finished = last ? (remaining == 0) : (input.pos == input.size);
5550
5551
  } while (!finished);
5552
5553
  return true;
5554
}
5555
5556
inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
5557
5558
inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
5559
5560
inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
5561
5562
inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
5563
                                          Callback callback) {
5564
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
5565
  ZSTD_inBuffer input = {data, data_length, 0};
5566
5567
  while (input.pos < input.size) {
5568
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
5569
    size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
5570
5571
    if (ZSTD_isError(remaining)) { return false; }
5572
5573
    if (!callback(buff.data(), output.pos)) { return false; }
5574
  }
5575
5576
  return true;
5577
}
5578
#endif
5579
5580
inline std::unique_ptr<decompressor>
5581
608
create_decompressor(const std::string &encoding) {
5582
608
  std::unique_ptr<decompressor> decompressor;
5583
5584
608
  if (encoding == "gzip" || encoding == "deflate") {
5585
581
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
5586
581
    decompressor = detail::make_unique<gzip_decompressor>();
5587
581
#endif
5588
581
  } else if (encoding.find("br") != std::string::npos) {
5589
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
5590
    decompressor = detail::make_unique<brotli_decompressor>();
5591
#endif
5592
15
  } else if (encoding == "zstd" || encoding.find("zstd") != std::string::npos) {
5593
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
5594
    decompressor = detail::make_unique<zstd_decompressor>();
5595
#endif
5596
13
  }
5597
5598
608
  return decompressor;
5599
608
}
5600
5601
51.1k
inline bool is_prohibited_header_name(const std::string &name) {
5602
51.1k
  using udl::operator""_t;
5603
5604
51.1k
  switch (str2tag(name)) {
5605
0
  case "REMOTE_ADDR"_t:
5606
0
  case "REMOTE_PORT"_t:
5607
0
  case "LOCAL_ADDR"_t:
5608
0
  case "LOCAL_PORT"_t: return true;
5609
51.1k
  default: return false;
5610
51.1k
  }
5611
51.1k
}
5612
5613
15.1k
inline bool has_header(const Headers &headers, const std::string &key) {
5614
15.1k
  if (is_prohibited_header_name(key)) { return false; }
5615
15.1k
  return headers.find(key) != headers.end();
5616
15.1k
}
5617
5618
inline const char *get_header_value(const Headers &headers,
5619
                                    const std::string &key, const char *def,
5620
36.0k
                                    size_t id) {
5621
36.0k
  if (is_prohibited_header_name(key)) {
5622
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5623
0
    std::string msg = "Prohibited header name '" + key + "' is specified.";
5624
0
    throw std::invalid_argument(msg);
5625
#else
5626
    return "";
5627
#endif
5628
0
  }
5629
5630
36.0k
  auto rng = headers.equal_range(key);
5631
36.0k
  auto it = rng.first;
5632
36.0k
  std::advance(it, static_cast<ssize_t>(id));
5633
36.0k
  if (it != rng.second) { return it->second.c_str(); }
5634
26.6k
  return def;
5635
36.0k
}
5636
5637
5.71k
inline bool read_headers(Stream &strm, Headers &headers) {
5638
5.71k
  const auto bufsiz = 2048;
5639
5.71k
  char buf[bufsiz];
5640
5.71k
  stream_line_reader line_reader(strm, buf, bufsiz);
5641
5642
5.71k
  size_t header_count = 0;
5643
5644
17.7k
  for (;;) {
5645
17.7k
    if (!line_reader.getline()) { return false; }
5646
5647
    // Check if the line ends with CRLF.
5648
16.7k
    auto line_terminator_len = 2;
5649
16.7k
    if (line_reader.end_with_crlf()) {
5650
      // Blank line indicates end of headers.
5651
16.6k
      if (line_reader.size() == 2) { break; }
5652
16.6k
    } else {
5653
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5654
      // Blank line indicates end of headers.
5655
      if (line_reader.size() == 1) { break; }
5656
      line_terminator_len = 1;
5657
#else
5658
133
      continue; // Skip invalid line.
5659
133
#endif
5660
133
    }
5661
5662
12.1k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
5663
5664
    // Check header count limit
5665
12.1k
    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
5666
5667
    // Exclude line terminator
5668
12.1k
    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
5669
5670
12.1k
    if (!parse_header(line_reader.ptr(), end,
5671
12.1k
                      [&](const std::string &key, const std::string &val) {
5672
11.9k
                        headers.emplace(key, val);
5673
11.9k
                      })) {
5674
242
      return false;
5675
242
    }
5676
5677
11.9k
    header_count++;
5678
11.9k
  }
5679
5680
4.45k
  return true;
5681
5.71k
}
5682
5683
inline bool read_content_with_length(Stream &strm, size_t len,
5684
                                     DownloadProgress progress,
5685
1.37k
                                     ContentReceiverWithProgress out) {
5686
1.37k
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5687
5688
1.37k
  detail::BodyReader br;
5689
1.37k
  br.stream = &strm;
5690
1.37k
  br.content_length = len;
5691
1.37k
  br.chunked = false;
5692
1.37k
  br.bytes_read = 0;
5693
1.37k
  br.last_error = Error::Success;
5694
5695
1.37k
  size_t r = 0;
5696
3.54k
  while (r < len) {
5697
3.46k
    auto read_len = static_cast<size_t>(len - r);
5698
3.46k
    auto to_read = (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ);
5699
3.46k
    auto n = detail::read_body_content(&strm, br, buf, to_read);
5700
3.46k
    if (n <= 0) { return false; }
5701
5702
2.39k
    if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
5703
2.16k
    r += static_cast<size_t>(n);
5704
5705
2.16k
    if (progress) {
5706
0
      if (!progress(r, len)) { return false; }
5707
0
    }
5708
2.16k
  }
5709
5710
74
  return true;
5711
1.37k
}
5712
5713
0
inline void skip_content_with_length(Stream &strm, size_t len) {
5714
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5715
0
  size_t r = 0;
5716
0
  while (r < len) {
5717
0
    auto read_len = static_cast<size_t>(len - r);
5718
0
    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
5719
0
    if (n <= 0) { return; }
5720
0
    r += static_cast<size_t>(n);
5721
0
  }
5722
0
}
5723
5724
enum class ReadContentResult {
5725
  Success,         // Successfully read the content
5726
  PayloadTooLarge, // The content exceeds the specified payload limit
5727
  Error            // An error occurred while reading the content
5728
};
5729
5730
inline ReadContentResult
5731
read_content_without_length(Stream &strm, size_t payload_max_length,
5732
0
                            ContentReceiverWithProgress out) {
5733
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5734
0
  size_t r = 0;
5735
0
  for (;;) {
5736
0
    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
5737
0
    if (n == 0) { return ReadContentResult::Success; }
5738
0
    if (n < 0) { return ReadContentResult::Error; }
5739
5740
    // Check if adding this data would exceed the payload limit
5741
0
    if (r > payload_max_length ||
5742
0
        payload_max_length - r < static_cast<size_t>(n)) {
5743
0
      return ReadContentResult::PayloadTooLarge;
5744
0
    }
5745
5746
0
    if (!out(buf, static_cast<size_t>(n), r, 0)) {
5747
0
      return ReadContentResult::Error;
5748
0
    }
5749
0
    r += static_cast<size_t>(n);
5750
0
  }
5751
5752
0
  return ReadContentResult::Success;
5753
0
}
5754
5755
template <typename T>
5756
inline ReadContentResult read_content_chunked(Stream &strm, T &x,
5757
                                              size_t payload_max_length,
5758
924
                                              ContentReceiverWithProgress out) {
5759
924
  detail::ChunkedDecoder dec(strm);
5760
5761
924
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5762
924
  size_t total_len = 0;
5763
5764
4.55k
  for (;;) {
5765
4.55k
    size_t chunk_offset = 0;
5766
4.55k
    size_t chunk_total = 0;
5767
4.55k
    auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
5768
4.55k
    if (n < 0) { return ReadContentResult::Error; }
5769
5770
4.12k
    if (n == 0) {
5771
477
      if (!dec.parse_trailers_into(x.trailers, x.headers)) {
5772
446
        return ReadContentResult::Error;
5773
446
      }
5774
31
      return ReadContentResult::Success;
5775
477
    }
5776
5777
3.64k
    if (total_len > payload_max_length ||
5778
3.64k
        payload_max_length - total_len < static_cast<size_t>(n)) {
5779
0
      return ReadContentResult::PayloadTooLarge;
5780
0
    }
5781
5782
3.64k
    if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total)) {
5783
18
      return ReadContentResult::Error;
5784
18
    }
5785
5786
3.62k
    total_len += static_cast<size_t>(n);
5787
3.62k
  }
5788
924
}
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
5758
924
                                              ContentReceiverWithProgress out) {
5759
924
  detail::ChunkedDecoder dec(strm);
5760
5761
924
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
5762
924
  size_t total_len = 0;
5763
5764
4.55k
  for (;;) {
5765
4.55k
    size_t chunk_offset = 0;
5766
4.55k
    size_t chunk_total = 0;
5767
4.55k
    auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
5768
4.55k
    if (n < 0) { return ReadContentResult::Error; }
5769
5770
4.12k
    if (n == 0) {
5771
477
      if (!dec.parse_trailers_into(x.trailers, x.headers)) {
5772
446
        return ReadContentResult::Error;
5773
446
      }
5774
31
      return ReadContentResult::Success;
5775
477
    }
5776
5777
3.64k
    if (total_len > payload_max_length ||
5778
3.64k
        payload_max_length - total_len < static_cast<size_t>(n)) {
5779
0
      return ReadContentResult::PayloadTooLarge;
5780
0
    }
5781
5782
3.64k
    if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total)) {
5783
18
      return ReadContentResult::Error;
5784
18
    }
5785
5786
3.62k
    total_len += static_cast<size_t>(n);
5787
3.62k
  }
5788
924
}
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)>)
5789
5790
5.47k
inline bool is_chunked_transfer_encoding(const Headers &headers) {
5791
5.47k
  return case_ignore::equal(
5792
5.47k
      get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
5793
5.47k
}
5794
5795
template <typename T, typename U>
5796
bool prepare_content_receiver(T &x, int &status,
5797
                              ContentReceiverWithProgress receiver,
5798
2.33k
                              bool decompress, U callback) {
5799
2.33k
  if (decompress) {
5800
2.33k
    std::string encoding = x.get_header_value("Content-Encoding");
5801
2.33k
    std::unique_ptr<decompressor> decompressor;
5802
5803
2.33k
    if (!encoding.empty()) {
5804
608
      decompressor = detail::create_decompressor(encoding);
5805
608
      if (!decompressor) {
5806
        // Unsupported encoding or no support compiled in
5807
27
        status = StatusCode::UnsupportedMediaType_415;
5808
27
        return false;
5809
27
      }
5810
608
    }
5811
5812
2.31k
    if (decompressor) {
5813
581
      if (decompressor->is_valid()) {
5814
581
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
5815
2.28k
                                              size_t off, size_t len) {
5816
2.28k
          return decompressor->decompress(buf, n,
5817
84.9k
                                          [&](const char *buf2, size_t n2) {
5818
84.9k
                                            return receiver(buf2, n2, off, len);
5819
84.9k
                                          });
5820
2.28k
        };
5821
581
        return callback(std::move(out));
5822
581
      } else {
5823
0
        status = StatusCode::InternalServerError_500;
5824
0
        return false;
5825
0
      }
5826
581
    }
5827
2.31k
  }
5828
5829
1.73k
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
5830
3.75k
                                        size_t len) {
5831
3.75k
    return receiver(buf, n, off, len);
5832
3.75k
  };
5833
1.73k
  return callback(std::move(out));
5834
2.33k
}
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
5798
2.33k
                              bool decompress, U callback) {
5799
2.33k
  if (decompress) {
5800
2.33k
    std::string encoding = x.get_header_value("Content-Encoding");
5801
2.33k
    std::unique_ptr<decompressor> decompressor;
5802
5803
2.33k
    if (!encoding.empty()) {
5804
608
      decompressor = detail::create_decompressor(encoding);
5805
608
      if (!decompressor) {
5806
        // Unsupported encoding or no support compiled in
5807
27
        status = StatusCode::UnsupportedMediaType_415;
5808
27
        return false;
5809
27
      }
5810
608
    }
5811
5812
2.31k
    if (decompressor) {
5813
581
      if (decompressor->is_valid()) {
5814
581
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
5815
581
                                              size_t off, size_t len) {
5816
581
          return decompressor->decompress(buf, n,
5817
581
                                          [&](const char *buf2, size_t n2) {
5818
581
                                            return receiver(buf2, n2, off, len);
5819
581
                                          });
5820
581
        };
5821
581
        return callback(std::move(out));
5822
581
      } else {
5823
0
        status = StatusCode::InternalServerError_500;
5824
0
        return false;
5825
0
      }
5826
581
    }
5827
2.31k
  }
5828
5829
1.73k
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
5830
1.73k
                                        size_t len) {
5831
1.73k
    return receiver(buf, n, off, len);
5832
1.73k
  };
5833
1.73k
  return callback(std::move(out));
5834
2.33k
}
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})
5835
5836
template <typename T>
5837
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
5838
                  DownloadProgress progress,
5839
2.33k
                  ContentReceiverWithProgress receiver, bool decompress) {
5840
2.33k
  return prepare_content_receiver(
5841
2.33k
      x, status, std::move(receiver), decompress,
5842
2.33k
      [&](const ContentReceiverWithProgress &out) {
5843
2.31k
        auto ret = true;
5844
2.31k
        auto exceed_payload_max_length = false;
5845
5846
2.31k
        if (is_chunked_transfer_encoding(x.headers)) {
5847
924
          auto result = read_content_chunked(strm, x, payload_max_length, out);
5848
924
          if (result == ReadContentResult::Success) {
5849
31
            ret = true;
5850
893
          } else if (result == ReadContentResult::PayloadTooLarge) {
5851
0
            exceed_payload_max_length = true;
5852
0
            ret = false;
5853
893
          } else {
5854
893
            ret = false;
5855
893
          }
5856
1.38k
        } else if (!has_header(x.headers, "Content-Length")) {
5857
0
          auto result =
5858
0
              read_content_without_length(strm, payload_max_length, out);
5859
0
          if (result == ReadContentResult::Success) {
5860
0
            ret = true;
5861
0
          } else if (result == ReadContentResult::PayloadTooLarge) {
5862
0
            exceed_payload_max_length = true;
5863
0
            ret = false;
5864
0
          } else {
5865
0
            ret = false;
5866
0
          }
5867
1.38k
        } else {
5868
1.38k
          auto is_invalid_value = false;
5869
1.38k
          auto len = get_header_value_u64(x.headers, "Content-Length",
5870
1.38k
                                          (std::numeric_limits<size_t>::max)(),
5871
1.38k
                                          0, is_invalid_value);
5872
5873
1.38k
          if (is_invalid_value) {
5874
4
            ret = false;
5875
1.38k
          } else if (len > payload_max_length) {
5876
0
            exceed_payload_max_length = true;
5877
0
            skip_content_with_length(strm, len);
5878
0
            ret = false;
5879
1.38k
          } else if (len > 0) {
5880
1.37k
            ret = read_content_with_length(strm, len, std::move(progress), out);
5881
1.37k
          }
5882
1.38k
        }
5883
5884
2.31k
        if (!ret) {
5885
2.19k
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
5886
2.19k
                                             : StatusCode::BadRequest_400;
5887
2.19k
        }
5888
2.31k
        return ret;
5889
2.31k
      });
5890
2.33k
}
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
5839
2.33k
                  ContentReceiverWithProgress receiver, bool decompress) {
5840
2.33k
  return prepare_content_receiver(
5841
2.33k
      x, status, std::move(receiver), decompress,
5842
2.33k
      [&](const ContentReceiverWithProgress &out) {
5843
2.33k
        auto ret = true;
5844
2.33k
        auto exceed_payload_max_length = false;
5845
5846
2.33k
        if (is_chunked_transfer_encoding(x.headers)) {
5847
2.33k
          auto result = read_content_chunked(strm, x, payload_max_length, out);
5848
2.33k
          if (result == ReadContentResult::Success) {
5849
2.33k
            ret = true;
5850
2.33k
          } else if (result == ReadContentResult::PayloadTooLarge) {
5851
2.33k
            exceed_payload_max_length = true;
5852
2.33k
            ret = false;
5853
2.33k
          } else {
5854
2.33k
            ret = false;
5855
2.33k
          }
5856
2.33k
        } else if (!has_header(x.headers, "Content-Length")) {
5857
2.33k
          auto result =
5858
2.33k
              read_content_without_length(strm, payload_max_length, out);
5859
2.33k
          if (result == ReadContentResult::Success) {
5860
2.33k
            ret = true;
5861
2.33k
          } else if (result == ReadContentResult::PayloadTooLarge) {
5862
2.33k
            exceed_payload_max_length = true;
5863
2.33k
            ret = false;
5864
2.33k
          } else {
5865
2.33k
            ret = false;
5866
2.33k
          }
5867
2.33k
        } else {
5868
2.33k
          auto is_invalid_value = false;
5869
2.33k
          auto len = get_header_value_u64(x.headers, "Content-Length",
5870
2.33k
                                          (std::numeric_limits<size_t>::max)(),
5871
2.33k
                                          0, is_invalid_value);
5872
5873
2.33k
          if (is_invalid_value) {
5874
2.33k
            ret = false;
5875
2.33k
          } else if (len > payload_max_length) {
5876
2.33k
            exceed_payload_max_length = true;
5877
2.33k
            skip_content_with_length(strm, len);
5878
2.33k
            ret = false;
5879
2.33k
          } else if (len > 0) {
5880
2.33k
            ret = read_content_with_length(strm, len, std::move(progress), out);
5881
2.33k
          }
5882
2.33k
        }
5883
5884
2.33k
        if (!ret) {
5885
2.33k
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
5886
2.33k
                                             : StatusCode::BadRequest_400;
5887
2.33k
        }
5888
2.33k
        return ret;
5889
2.33k
      });
5890
2.33k
}
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)
5891
5892
inline ssize_t write_request_line(Stream &strm, const std::string &method,
5893
0
                                  const std::string &path) {
5894
0
  std::string s = method;
5895
0
  s += ' ';
5896
0
  s += path;
5897
0
  s += " HTTP/1.1\r\n";
5898
0
  return strm.write(s.data(), s.size());
5899
0
}
5900
5901
6.05k
inline ssize_t write_response_line(Stream &strm, int status) {
5902
6.05k
  std::string s = "HTTP/1.1 ";
5903
6.05k
  s += std::to_string(status);
5904
6.05k
  s += ' ';
5905
6.05k
  s += httplib::status_message(status);
5906
6.05k
  s += "\r\n";
5907
6.05k
  return strm.write(s.data(), s.size());
5908
6.05k
}
5909
5910
6.05k
inline ssize_t write_headers(Stream &strm, const Headers &headers) {
5911
6.05k
  ssize_t write_len = 0;
5912
14.2k
  for (const auto &x : headers) {
5913
14.2k
    std::string s;
5914
14.2k
    s = x.first;
5915
14.2k
    s += ": ";
5916
14.2k
    s += x.second;
5917
14.2k
    s += "\r\n";
5918
5919
14.2k
    auto len = strm.write(s.data(), s.size());
5920
14.2k
    if (len < 0) { return len; }
5921
14.2k
    write_len += len;
5922
14.2k
  }
5923
6.05k
  auto len = strm.write("\r\n");
5924
6.05k
  if (len < 0) { return len; }
5925
6.05k
  write_len += len;
5926
6.05k
  return write_len;
5927
6.05k
}
5928
5929
7.42k
inline bool write_data(Stream &strm, const char *d, size_t l) {
5930
7.42k
  size_t offset = 0;
5931
14.8k
  while (offset < l) {
5932
7.42k
    auto length = strm.write(d + offset, l - offset);
5933
7.42k
    if (length < 0) { return false; }
5934
7.42k
    offset += static_cast<size_t>(length);
5935
7.42k
  }
5936
7.42k
  return true;
5937
7.42k
}
5938
5939
template <typename T>
5940
inline bool write_content_with_progress(Stream &strm,
5941
                                        const ContentProvider &content_provider,
5942
                                        size_t offset, size_t length,
5943
                                        T is_shutting_down,
5944
                                        const UploadProgress &upload_progress,
5945
0
                                        Error &error) {
5946
0
  size_t end_offset = offset + length;
5947
0
  size_t start_offset = offset;
5948
0
  auto ok = true;
5949
0
  DataSink data_sink;
5950
5951
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
5952
0
    if (ok) {
5953
0
      if (write_data(strm, d, l)) {
5954
0
        offset += l;
5955
5956
0
        if (upload_progress && length > 0) {
5957
0
          size_t current_written = offset - start_offset;
5958
0
          if (!upload_progress(current_written, length)) {
5959
0
            ok = false;
5960
0
            return false;
5961
0
          }
5962
0
        }
5963
0
      } else {
5964
0
        ok = false;
5965
0
      }
5966
0
    }
5967
0
    return ok;
5968
0
  };
5969
5970
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
5971
5972
0
  while (offset < end_offset && !is_shutting_down()) {
5973
0
    if (!strm.wait_writable()) {
5974
0
      error = Error::Write;
5975
0
      return false;
5976
0
    } else if (!content_provider(offset, end_offset - offset, data_sink)) {
5977
0
      error = Error::Canceled;
5978
0
      return false;
5979
0
    } else if (!ok) {
5980
0
      error = Error::Write;
5981
0
      return false;
5982
0
    }
5983
0
  }
5984
5985
0
  error = Error::Success;
5986
0
  return true;
5987
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&)
5988
5989
template <typename T>
5990
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
5991
                          size_t offset, size_t length, T is_shutting_down,
5992
0
                          Error &error) {
5993
0
  return write_content_with_progress<T>(strm, content_provider, offset, length,
5994
0
                                        is_shutting_down, nullptr, error);
5995
0
}
5996
5997
template <typename T>
5998
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
5999
                          size_t offset, size_t length,
6000
0
                          const T &is_shutting_down) {
6001
0
  auto error = Error::Success;
6002
0
  return write_content(strm, content_provider, offset, length, is_shutting_down,
6003
0
                       error);
6004
0
}
6005
6006
template <typename T>
6007
inline bool
6008
write_content_without_length(Stream &strm,
6009
                             const ContentProvider &content_provider,
6010
0
                             const T &is_shutting_down) {
6011
0
  size_t offset = 0;
6012
0
  auto data_available = true;
6013
0
  auto ok = true;
6014
0
  DataSink data_sink;
6015
6016
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
6017
0
    if (ok) {
6018
0
      offset += l;
6019
0
      if (!write_data(strm, d, l)) { ok = false; }
6020
0
    }
6021
0
    return ok;
6022
0
  };
6023
6024
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
6025
6026
0
  data_sink.done = [&](void) { data_available = false; };
6027
6028
0
  while (data_available && !is_shutting_down()) {
6029
0
    if (!strm.wait_writable()) {
6030
0
      return false;
6031
0
    } else if (!content_provider(offset, 0, data_sink)) {
6032
0
      return false;
6033
0
    } else if (!ok) {
6034
0
      return false;
6035
0
    }
6036
0
  }
6037
0
  return true;
6038
0
}
6039
6040
template <typename T, typename U>
6041
inline bool
6042
write_content_chunked(Stream &strm, const ContentProvider &content_provider,
6043
0
                      const T &is_shutting_down, U &compressor, Error &error) {
6044
0
  size_t offset = 0;
6045
0
  auto data_available = true;
6046
0
  auto ok = true;
6047
0
  DataSink data_sink;
6048
6049
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
6050
0
    if (ok) {
6051
0
      data_available = l > 0;
6052
0
      offset += l;
6053
6054
0
      std::string payload;
6055
0
      if (compressor.compress(d, l, false,
6056
0
                              [&](const char *data, size_t data_len) {
6057
0
                                payload.append(data, data_len);
6058
0
                                return true;
6059
0
                              })) {
6060
0
        if (!payload.empty()) {
6061
          // Emit chunked response header and footer for each chunk
6062
0
          auto chunk =
6063
0
              from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
6064
0
          if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
6065
0
        }
6066
0
      } else {
6067
0
        ok = false;
6068
0
      }
6069
0
    }
6070
0
    return ok;
6071
0
  };
6072
6073
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
6074
6075
0
  auto done_with_trailer = [&](const Headers *trailer) {
6076
0
    if (!ok) { return; }
6077
6078
0
    data_available = false;
6079
6080
0
    std::string payload;
6081
0
    if (!compressor.compress(nullptr, 0, true,
6082
0
                             [&](const char *data, size_t data_len) {
6083
0
                               payload.append(data, data_len);
6084
0
                               return true;
6085
0
                             })) {
6086
0
      ok = false;
6087
0
      return;
6088
0
    }
6089
6090
0
    if (!payload.empty()) {
6091
      // Emit chunked response header and footer for each chunk
6092
0
      auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
6093
0
      if (!write_data(strm, chunk.data(), chunk.size())) {
6094
0
        ok = false;
6095
0
        return;
6096
0
      }
6097
0
    }
6098
6099
0
    constexpr const char done_marker[] = "0\r\n";
6100
0
    if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
6101
6102
    // Trailer
6103
0
    if (trailer) {
6104
0
      for (const auto &kv : *trailer) {
6105
0
        std::string field_line = kv.first + ": " + kv.second + "\r\n";
6106
0
        if (!write_data(strm, field_line.data(), field_line.size())) {
6107
0
          ok = false;
6108
0
        }
6109
0
      }
6110
0
    }
6111
6112
0
    constexpr const char crlf[] = "\r\n";
6113
0
    if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
6114
0
  };
6115
6116
0
  data_sink.done = [&](void) { done_with_trailer(nullptr); };
6117
6118
0
  data_sink.done_with_trailer = [&](const Headers &trailer) {
6119
0
    done_with_trailer(&trailer);
6120
0
  };
6121
6122
0
  while (data_available && !is_shutting_down()) {
6123
0
    if (!strm.wait_writable()) {
6124
0
      error = Error::Write;
6125
0
      return false;
6126
0
    } else if (!content_provider(offset, 0, data_sink)) {
6127
0
      error = Error::Canceled;
6128
0
      return false;
6129
0
    } else if (!ok) {
6130
0
      error = Error::Write;
6131
0
      return false;
6132
0
    }
6133
0
  }
6134
6135
0
  error = Error::Success;
6136
0
  return true;
6137
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&)
6138
6139
template <typename T, typename U>
6140
inline bool write_content_chunked(Stream &strm,
6141
                                  const ContentProvider &content_provider,
6142
0
                                  const T &is_shutting_down, U &compressor) {
6143
0
  auto error = Error::Success;
6144
0
  return write_content_chunked(strm, content_provider, is_shutting_down,
6145
0
                               compressor, error);
6146
0
}
6147
6148
template <typename T>
6149
inline bool redirect(T &cli, Request &req, Response &res,
6150
                     const std::string &path, const std::string &location,
6151
0
                     Error &error) {
6152
0
  Request new_req = req;
6153
0
  new_req.path = path;
6154
0
  new_req.redirect_count_ -= 1;
6155
0
6156
0
  if (res.status == StatusCode::SeeOther_303 &&
6157
0
      (req.method != "GET" && req.method != "HEAD")) {
6158
0
    new_req.method = "GET";
6159
0
    new_req.body.clear();
6160
0
    new_req.headers.clear();
6161
0
  }
6162
0
6163
0
  Response new_res;
6164
0
6165
0
  auto ret = cli.send(new_req, new_res, error);
6166
0
  if (ret) {
6167
0
    req = std::move(new_req);
6168
0
    res = std::move(new_res);
6169
0
6170
0
    if (res.location.empty()) { res.location = location; }
6171
0
  }
6172
0
  return ret;
6173
0
}
6174
6175
0
inline std::string params_to_query_str(const Params &params) {
6176
0
  std::string query;
6177
0
6178
0
  for (auto it = params.begin(); it != params.end(); ++it) {
6179
0
    if (it != params.begin()) { query += '&'; }
6180
0
    query += encode_query_component(it->first);
6181
0
    query += '=';
6182
0
    query += encode_query_component(it->second);
6183
0
  }
6184
0
  return query;
6185
0
}
6186
6187
inline void parse_query_text(const char *data, std::size_t size,
6188
5.74k
                             Params &params) {
6189
5.74k
  std::set<std::string> cache;
6190
15.9k
  split(data, data + size, '&', [&](const char *b, const char *e) {
6191
15.9k
    std::string kv(b, e);
6192
15.9k
    if (cache.find(kv) != cache.end()) { return; }
6193
5.66k
    cache.insert(std::move(kv));
6194
6195
5.66k
    std::string key;
6196
5.66k
    std::string val;
6197
5.66k
    divide(b, static_cast<std::size_t>(e - b), '=',
6198
5.66k
           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
6199
5.66k
               std::size_t rhs_size) {
6200
5.66k
             key.assign(lhs_data, lhs_size);
6201
5.66k
             val.assign(rhs_data, rhs_size);
6202
5.66k
           });
6203
6204
5.66k
    if (!key.empty()) {
6205
5.20k
      params.emplace(decode_query_component(key), decode_query_component(val));
6206
5.20k
    }
6207
5.66k
  });
6208
5.74k
}
6209
6210
33
inline void parse_query_text(const std::string &s, Params &params) {
6211
33
  parse_query_text(s.data(), s.size(), params);
6212
33
}
6213
6214
// Normalize a query string by decoding and re-encoding each key/value pair
6215
// while preserving the original parameter order. This avoids double-encoding
6216
// and ensures consistent encoding without reordering (unlike Params which
6217
// uses std::multimap and sorts keys).
6218
0
inline std::string normalize_query_string(const std::string &query) {
6219
0
  std::string result;
6220
0
  split(query.data(), query.data() + query.size(), '&',
6221
0
        [&](const char *b, const char *e) {
6222
0
          std::string key;
6223
0
          std::string val;
6224
0
          divide(b, static_cast<std::size_t>(e - b), '=',
6225
0
                 [&](const char *lhs_data, std::size_t lhs_size,
6226
0
                     const char *rhs_data, std::size_t rhs_size) {
6227
0
                   key.assign(lhs_data, lhs_size);
6228
0
                   val.assign(rhs_data, rhs_size);
6229
0
                 });
6230
0
6231
0
          if (!key.empty()) {
6232
0
            auto dec_key = decode_query_component(key);
6233
0
            auto dec_val = decode_query_component(val);
6234
0
6235
0
            if (!result.empty()) { result += '&'; }
6236
0
            result += encode_query_component(dec_key);
6237
0
            if (!val.empty() || std::find(b, e, '=') != e) {
6238
0
              result += '=';
6239
0
              result += encode_query_component(dec_val);
6240
0
            }
6241
0
          }
6242
0
        });
6243
0
  return result;
6244
0
}
6245
6246
inline bool parse_multipart_boundary(const std::string &content_type,
6247
1.17k
                                     std::string &boundary) {
6248
1.17k
  auto boundary_keyword = "boundary=";
6249
1.17k
  auto pos = content_type.find(boundary_keyword);
6250
1.17k
  if (pos == std::string::npos) { return false; }
6251
1.17k
  auto end = content_type.find(';', pos);
6252
1.17k
  auto beg = pos + strlen(boundary_keyword);
6253
1.17k
  boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
6254
1.17k
  return !boundary.empty();
6255
1.17k
}
6256
6257
8.00k
inline void parse_disposition_params(const std::string &s, Params &params) {
6258
8.00k
  std::set<std::string> cache;
6259
37.5k
  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
6260
37.5k
    std::string kv(b, e);
6261
37.5k
    if (cache.find(kv) != cache.end()) { return; }
6262
34.1k
    cache.insert(kv);
6263
6264
34.1k
    std::string key;
6265
34.1k
    std::string val;
6266
56.4k
    split(b, e, '=', [&](const char *b2, const char *e2) {
6267
56.4k
      if (key.empty()) {
6268
33.6k
        key.assign(b2, e2);
6269
33.6k
      } else {
6270
22.8k
        val.assign(b2, e2);
6271
22.8k
      }
6272
56.4k
    });
6273
6274
34.1k
    if (!key.empty()) {
6275
33.6k
      params.emplace(trim_double_quotes_copy((key)),
6276
33.6k
                     trim_double_quotes_copy((val)));
6277
33.6k
    }
6278
34.1k
  });
6279
8.00k
}
6280
6281
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6282
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
6283
#else
6284
907
inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
6285
907
#endif
6286
27.1k
  auto is_valid = [](const std::string &str) {
6287
27.1k
    return std::all_of(str.cbegin(), str.cend(),
6288
27.1k
                       [](unsigned char c) { return std::isdigit(c); });
6289
27.1k
  };
6290
6291
907
  if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
6292
885
    const auto pos = static_cast<size_t>(6);
6293
885
    const auto len = static_cast<size_t>(s.size() - 6);
6294
885
    auto all_valid_ranges = true;
6295
18.0k
    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
6296
18.0k
      if (!all_valid_ranges) { return; }
6297
6298
13.5k
      const auto it = std::find(b, e, '-');
6299
13.5k
      if (it == e) {
6300
10
        all_valid_ranges = false;
6301
10
        return;
6302
10
      }
6303
6304
13.5k
      const auto lhs = std::string(b, it);
6305
13.5k
      const auto rhs = std::string(it + 1, e);
6306
13.5k
      if (!is_valid(lhs) || !is_valid(rhs)) {
6307
17
        all_valid_ranges = false;
6308
17
        return;
6309
17
      }
6310
6311
13.5k
      const auto first =
6312
13.5k
          static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
6313
13.5k
      const auto last =
6314
13.5k
          static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
6315
13.5k
      if ((first == -1 && last == -1) ||
6316
13.5k
          (first != -1 && last != -1 && first > last)) {
6317
88
        all_valid_ranges = false;
6318
88
        return;
6319
88
      }
6320
6321
13.4k
      ranges.emplace_back(first, last);
6322
13.4k
    });
6323
885
    return all_valid_ranges && !ranges.empty();
6324
885
  }
6325
22
  return false;
6326
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6327
}
6328
#else
6329
907
} catch (...) { return false; }
6330
#endif
6331
6332
inline bool parse_accept_header(const std::string &s,
6333
949
                                std::vector<std::string> &content_types) {
6334
949
  content_types.clear();
6335
6336
  // Empty string is considered valid (no preference)
6337
949
  if (s.empty()) { return true; }
6338
6339
  // Check for invalid patterns: leading/trailing commas or consecutive commas
6340
938
  if (s.front() == ',' || s.back() == ',' ||
6341
936
      s.find(",,") != std::string::npos) {
6342
5
    return false;
6343
5
  }
6344
6345
933
  struct AcceptEntry {
6346
933
    std::string media_type;
6347
933
    double quality;
6348
933
    int order; // Original order in header
6349
933
  };
6350
6351
933
  std::vector<AcceptEntry> entries;
6352
933
  int order = 0;
6353
933
  bool has_invalid_entry = false;
6354
6355
  // Split by comma and parse each entry
6356
204k
  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
6357
204k
    std::string entry(b, e);
6358
204k
    entry = trim_copy(entry);
6359
6360
204k
    if (entry.empty()) {
6361
0
      has_invalid_entry = true;
6362
0
      return;
6363
0
    }
6364
6365
204k
    AcceptEntry accept_entry;
6366
204k
    accept_entry.quality = 1.0; // Default quality
6367
204k
    accept_entry.order = order++;
6368
6369
    // Find q= parameter
6370
204k
    auto q_pos = entry.find(";q=");
6371
204k
    if (q_pos == std::string::npos) { q_pos = entry.find("; q="); }
6372
6373
204k
    if (q_pos != std::string::npos) {
6374
      // Extract media type (before q parameter)
6375
9.35k
      accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
6376
6377
      // Extract quality value
6378
9.35k
      auto q_start = entry.find('=', q_pos) + 1;
6379
9.35k
      auto q_end = entry.find(';', q_start);
6380
9.35k
      if (q_end == std::string::npos) { q_end = entry.length(); }
6381
6382
9.35k
      std::string quality_str =
6383
9.35k
          trim_copy(entry.substr(q_start, q_end - q_start));
6384
9.35k
      if (quality_str.empty()) {
6385
401
        has_invalid_entry = true;
6386
401
        return;
6387
401
      }
6388
6389
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6390
      {
6391
        std::istringstream iss(quality_str);
6392
        iss >> accept_entry.quality;
6393
6394
        // Check if conversion was successful and entire string was consumed
6395
        if (iss.fail() || !iss.eof()) {
6396
          has_invalid_entry = true;
6397
          return;
6398
        }
6399
      }
6400
#else
6401
8.95k
      try {
6402
8.95k
        accept_entry.quality = std::stod(quality_str);
6403
8.95k
      } catch (...) {
6404
367
        has_invalid_entry = true;
6405
367
        return;
6406
367
      }
6407
0
#endif
6408
      // Check if quality is in valid range [0.0, 1.0]
6409
8.58k
      if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
6410
393
        has_invalid_entry = true;
6411
393
        return;
6412
393
      }
6413
195k
    } else {
6414
      // No quality parameter, use entire entry as media type
6415
195k
      accept_entry.media_type = entry;
6416
195k
    }
6417
6418
    // Remove additional parameters from media type
6419
203k
    auto param_pos = accept_entry.media_type.find(';');
6420
203k
    if (param_pos != std::string::npos) {
6421
1.95k
      accept_entry.media_type =
6422
1.95k
          trim_copy(accept_entry.media_type.substr(0, param_pos));
6423
1.95k
    }
6424
6425
    // Basic validation of media type format
6426
203k
    if (accept_entry.media_type.empty()) {
6427
626
      has_invalid_entry = true;
6428
626
      return;
6429
626
    }
6430
6431
    // Check for basic media type format (should contain '/' or be '*')
6432
203k
    if (accept_entry.media_type != "*" &&
6433
197k
        accept_entry.media_type.find('/') == std::string::npos) {
6434
681
      has_invalid_entry = true;
6435
681
      return;
6436
681
    }
6437
6438
202k
    entries.push_back(std::move(accept_entry));
6439
202k
  });
6440
6441
  // Return false if any invalid entry was found
6442
933
  if (has_invalid_entry) { return false; }
6443
6444
  // Sort by quality (descending), then by original order (ascending)
6445
764
  std::sort(entries.begin(), entries.end(),
6446
3.66M
            [](const AcceptEntry &a, const AcceptEntry &b) {
6447
3.66M
              if (a.quality != b.quality) {
6448
981k
                return a.quality > b.quality; // Higher quality first
6449
981k
              }
6450
2.68M
              return a.order < b.order; // Earlier order first for same quality
6451
3.66M
            });
6452
6453
  // Extract sorted media types
6454
764
  content_types.reserve(entries.size());
6455
193k
  for (auto &entry : entries) {
6456
193k
    content_types.push_back(std::move(entry.media_type));
6457
193k
  }
6458
6459
764
  return true;
6460
933
}
6461
6462
class FormDataParser {
6463
public:
6464
3.36k
  FormDataParser() = default;
6465
6466
1.17k
  void set_boundary(std::string &&boundary) {
6467
1.17k
    boundary_ = std::move(boundary);
6468
1.17k
    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
6469
1.17k
    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
6470
1.17k
  }
6471
6472
22
  bool is_valid() const { return is_valid_; }
6473
6474
  bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
6475
65.1k
             const ContentReceiver &content_callback) {
6476
6477
65.1k
    buf_append(buf, n);
6478
6479
167k
    while (buf_size() > 0) {
6480
166k
      switch (state_) {
6481
2.33k
      case 0: { // Initial boundary
6482
2.33k
        auto pos = buf_find(dash_boundary_crlf_);
6483
2.33k
        if (pos == buf_size()) { return true; }
6484
1.03k
        buf_erase(pos + dash_boundary_crlf_.size());
6485
1.03k
        state_ = 1;
6486
1.03k
        break;
6487
2.33k
      }
6488
25.8k
      case 1: { // New entry
6489
25.8k
        clear_file_info();
6490
25.8k
        state_ = 2;
6491
25.8k
        break;
6492
2.33k
      }
6493
26.3k
      case 2: { // Headers
6494
26.3k
        auto pos = buf_find(crlf_);
6495
26.3k
        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
6496
97.1k
        while (pos < buf_size()) {
6497
          // Empty line
6498
96.2k
          if (pos == 0) {
6499
25.2k
            if (!header_callback(file_)) {
6500
1
              is_valid_ = false;
6501
1
              return false;
6502
1
            }
6503
25.2k
            buf_erase(crlf_.size());
6504
25.2k
            state_ = 3;
6505
25.2k
            break;
6506
25.2k
          }
6507
6508
70.9k
          const auto header = buf_head(pos);
6509
6510
70.9k
          if (!parse_header(header.data(), header.data() + header.size(),
6511
70.9k
                            [&](const std::string &, const std::string &) {})) {
6512
83
            is_valid_ = false;
6513
83
            return false;
6514
83
          }
6515
6516
          // Parse and emplace space trimmed headers into a map
6517
70.9k
          if (!parse_header(
6518
70.9k
                  header.data(), header.data() + header.size(),
6519
70.9k
                  [&](const std::string &key, const std::string &val) {
6520
70.9k
                    file_.headers.emplace(key, val);
6521
70.9k
                  })) {
6522
0
            is_valid_ = false;
6523
0
            return false;
6524
0
          }
6525
6526
70.9k
          constexpr const char header_content_type[] = "Content-Type:";
6527
6528
70.9k
          if (start_with_case_ignore(header, header_content_type)) {
6529
1.25k
            file_.content_type =
6530
1.25k
                trim_copy(header.substr(str_len(header_content_type)));
6531
69.6k
          } else {
6532
69.6k
            thread_local const std::regex re_content_disposition(
6533
69.6k
                R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
6534
69.6k
                std::regex_constants::icase);
6535
6536
69.6k
            std::smatch m;
6537
69.6k
            if (std::regex_match(header, m, re_content_disposition)) {
6538
8.00k
              Params params;
6539
8.00k
              parse_disposition_params(m[1], params);
6540
6541
8.00k
              auto it = params.find("name");
6542
8.00k
              if (it != params.end()) {
6543
7.88k
                file_.name = it->second;
6544
7.88k
              } else {
6545
124
                is_valid_ = false;
6546
124
                return false;
6547
124
              }
6548
6549
7.88k
              it = params.find("filename");
6550
7.88k
              if (it != params.end()) { file_.filename = it->second; }
6551
6552
7.88k
              it = params.find("filename*");
6553
7.88k
              if (it != params.end()) {
6554
                // Only allow UTF-8 encoding...
6555
10
                thread_local const std::regex re_rfc5987_encoding(
6556
10
                    R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
6557
6558
10
                std::smatch m2;
6559
10
                if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
6560
0
                  file_.filename = decode_path_component(m2[1]); // override...
6561
10
                } else {
6562
10
                  is_valid_ = false;
6563
10
                  return false;
6564
10
                }
6565
10
              }
6566
7.88k
            }
6567
69.6k
          }
6568
70.7k
          buf_erase(pos + crlf_.size());
6569
70.7k
          pos = buf_find(crlf_);
6570
70.7k
        }
6571
26.1k
        if (state_ != 3) { return true; }
6572
25.2k
        break;
6573
26.1k
      }
6574
67.9k
      case 3: { // Body
6575
67.9k
        if (crlf_dash_boundary_.size() > buf_size()) { return true; }
6576
67.7k
        auto pos = buf_find(crlf_dash_boundary_);
6577
67.7k
        if (pos < buf_size()) {
6578
24.9k
          if (!content_callback(buf_data(), pos)) {
6579
0
            is_valid_ = false;
6580
0
            return false;
6581
0
          }
6582
24.9k
          buf_erase(pos + crlf_dash_boundary_.size());
6583
24.9k
          state_ = 4;
6584
42.8k
        } else {
6585
42.8k
          auto len = buf_size() - crlf_dash_boundary_.size();
6586
42.8k
          if (len > 0) {
6587
42.5k
            if (!content_callback(buf_data(), len)) {
6588
0
              is_valid_ = false;
6589
0
              return false;
6590
0
            }
6591
42.5k
            buf_erase(len);
6592
42.5k
          }
6593
42.8k
          return true;
6594
42.8k
        }
6595
24.9k
        break;
6596
67.7k
      }
6597
44.1k
      case 4: { // Boundary
6598
44.1k
        if (crlf_.size() > buf_size()) { return true; }
6599
43.8k
        if (buf_start_with(crlf_)) {
6600
24.8k
          buf_erase(crlf_.size());
6601
24.8k
          state_ = 1;
6602
24.8k
        } else {
6603
19.0k
          if (dash_.size() > buf_size()) { return true; }
6604
19.0k
          if (buf_start_with(dash_)) {
6605
256
            buf_erase(dash_.size());
6606
256
            is_valid_ = true;
6607
256
            buf_erase(buf_size()); // Remove epilogue
6608
18.8k
          } else {
6609
18.8k
            return true;
6610
18.8k
          }
6611
19.0k
        }
6612
25.0k
        break;
6613
43.8k
      }
6614
166k
      }
6615
166k
    }
6616
6617
536
    return true;
6618
65.1k
  }
6619
6620
private:
6621
25.8k
  void clear_file_info() {
6622
25.8k
    file_.name.clear();
6623
25.8k
    file_.filename.clear();
6624
25.8k
    file_.content_type.clear();
6625
25.8k
    file_.headers.clear();
6626
25.8k
  }
6627
6628
70.9k
  bool start_with_case_ignore(const std::string &a, const char *b) const {
6629
70.9k
    const auto b_len = strlen(b);
6630
70.9k
    if (a.size() < b_len) { return false; }
6631
154k
    for (size_t i = 0; i < b_len; i++) {
6632
152k
      if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
6633
26.3k
        return false;
6634
26.3k
      }
6635
152k
    }
6636
1.25k
    return true;
6637
27.6k
  }
6638
6639
  const std::string dash_ = "--";
6640
  const std::string crlf_ = "\r\n";
6641
  std::string boundary_;
6642
  std::string dash_boundary_crlf_;
6643
  std::string crlf_dash_boundary_;
6644
6645
  size_t state_ = 0;
6646
  bool is_valid_ = false;
6647
  FormData file_;
6648
6649
  // Buffer
6650
  bool start_with(const std::string &a, size_t spos, size_t epos,
6651
28.5M
                  const std::string &b) const {
6652
28.5M
    if (epos - spos < b.size()) { return false; }
6653
58.7M
    for (size_t i = 0; i < b.size(); i++) {
6654
58.5M
      if (a[i + spos] != b[i]) { return false; }
6655
58.5M
    }
6656
147k
    return true;
6657
28.5M
  }
6658
6659
618k
  size_t buf_size() const { return buf_epos_ - buf_spos_; }
6660
6661
67.5k
  const char *buf_data() const { return &buf_[buf_spos_]; }
6662
6663
70.9k
  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
6664
6665
62.9k
  bool buf_start_with(const std::string &s) const {
6666
62.9k
    return start_with(buf_, buf_spos_, buf_epos_, s);
6667
62.9k
  }
6668
6669
167k
  size_t buf_find(const std::string &s) const {
6670
167k
    auto c = s.front();
6671
6672
167k
    size_t off = buf_spos_;
6673
28.5M
    while (off < buf_epos_) {
6674
28.5M
      auto pos = off;
6675
1.19G
      while (true) {
6676
1.19G
        if (pos == buf_epos_) { return buf_size(); }
6677
1.19G
        if (buf_[pos] == c) { break; }
6678
1.16G
        pos++;
6679
1.16G
      }
6680
6681
28.5M
      auto remaining_size = buf_epos_ - pos;
6682
28.5M
      if (s.size() > remaining_size) { return buf_size(); }
6683
6684
28.5M
      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
6685
6686
28.3M
      off = pos + 1;
6687
28.3M
    }
6688
6689
325
    return buf_size();
6690
167k
  }
6691
6692
65.1k
  void buf_append(const char *data, size_t n) {
6693
65.1k
    auto remaining_size = buf_size();
6694
65.1k
    if (remaining_size > 0 && buf_spos_ > 0) {
6695
786k
      for (size_t i = 0; i < remaining_size; i++) {
6696
743k
        buf_[i] = buf_[buf_spos_ + i];
6697
743k
      }
6698
42.8k
    }
6699
65.1k
    buf_spos_ = 0;
6700
65.1k
    buf_epos_ = remaining_size;
6701
6702
65.1k
    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
6703
6704
1.00G
    for (size_t i = 0; i < n; i++) {
6705
1.00G
      buf_[buf_epos_ + i] = data[i];
6706
1.00G
    }
6707
65.1k
    buf_epos_ += n;
6708
65.1k
  }
6709
6710
189k
  void buf_erase(size_t size) { buf_spos_ += size; }
6711
6712
  std::string buf_;
6713
  size_t buf_spos_ = 0;
6714
  size_t buf_epos_ = 0;
6715
};
6716
6717
240
inline std::string random_string(size_t length) {
6718
240
  constexpr const char data[] =
6719
240
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
6720
6721
240
  thread_local auto engine([]() {
6722
    // std::random_device might actually be deterministic on some
6723
    // platforms, but due to lack of support in the c++ standard library,
6724
    // doing better requires either some ugly hacks or breaking portability.
6725
1
    std::random_device seed_gen;
6726
    // Request 128 bits of entropy for initialization
6727
1
    std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
6728
1
    return std::mt19937(seed_sequence);
6729
1
  }());
6730
6731
240
  std::string result;
6732
4.08k
  for (size_t i = 0; i < length; i++) {
6733
3.84k
    result += data[engine() % (sizeof(data) - 1)];
6734
3.84k
  }
6735
240
  return result;
6736
240
}
6737
6738
240
inline std::string make_multipart_data_boundary() {
6739
240
  return "--cpp-httplib-multipart-data-" + detail::random_string(16);
6740
240
}
6741
6742
0
inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
6743
0
  auto valid = true;
6744
0
  for (size_t i = 0; i < boundary.size(); i++) {
6745
0
    auto c = boundary[i];
6746
0
    if (!std::isalnum(c) && c != '-' && c != '_') {
6747
0
      valid = false;
6748
0
      break;
6749
0
    }
6750
0
  }
6751
0
  return valid;
6752
0
}
6753
6754
template <typename T>
6755
inline std::string
6756
serialize_multipart_formdata_item_begin(const T &item,
6757
0
                                        const std::string &boundary) {
6758
0
  std::string body = "--" + boundary + "\r\n";
6759
0
  body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
6760
0
  if (!item.filename.empty()) {
6761
0
    body += "; filename=\"" + item.filename + "\"";
6762
0
  }
6763
0
  body += "\r\n";
6764
0
  if (!item.content_type.empty()) {
6765
0
    body += "Content-Type: " + item.content_type + "\r\n";
6766
0
  }
6767
0
  body += "\r\n";
6768
0
6769
0
  return body;
6770
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&)
6771
6772
0
inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
6773
6774
inline std::string
6775
0
serialize_multipart_formdata_finish(const std::string &boundary) {
6776
0
  return "--" + boundary + "--\r\n";
6777
0
}
6778
6779
inline std::string
6780
0
serialize_multipart_formdata_get_content_type(const std::string &boundary) {
6781
0
  return "multipart/form-data; boundary=" + boundary;
6782
0
}
6783
6784
inline std::string
6785
serialize_multipart_formdata(const UploadFormDataItems &items,
6786
0
                             const std::string &boundary, bool finish = true) {
6787
0
  std::string body;
6788
0
6789
0
  for (const auto &item : items) {
6790
0
    body += serialize_multipart_formdata_item_begin(item, boundary);
6791
0
    body += item.content + serialize_multipart_formdata_item_end();
6792
0
  }
6793
0
6794
0
  if (finish) { body += serialize_multipart_formdata_finish(boundary); }
6795
0
6796
0
  return body;
6797
0
}
6798
6799
427
inline void coalesce_ranges(Ranges &ranges, size_t content_length) {
6800
427
  if (ranges.size() <= 1) return;
6801
6802
  // Sort ranges by start position
6803
276
  std::sort(ranges.begin(), ranges.end(),
6804
1.20k
            [](const Range &a, const Range &b) { return a.first < b.first; });
6805
6806
276
  Ranges coalesced;
6807
276
  coalesced.reserve(ranges.size());
6808
6809
940
  for (auto &r : ranges) {
6810
940
    auto first_pos = r.first;
6811
940
    auto last_pos = r.second;
6812
6813
    // Handle special cases like in range_error
6814
940
    if (first_pos == -1 && last_pos == -1) {
6815
0
      first_pos = 0;
6816
0
      last_pos = static_cast<ssize_t>(content_length);
6817
0
    }
6818
6819
940
    if (first_pos == -1) {
6820
0
      first_pos = static_cast<ssize_t>(content_length) - last_pos;
6821
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
6822
0
    }
6823
6824
940
    if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
6825
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
6826
0
    }
6827
6828
    // Skip invalid ranges
6829
940
    if (!(0 <= first_pos && first_pos <= last_pos &&
6830
940
          last_pos < static_cast<ssize_t>(content_length))) {
6831
0
      continue;
6832
0
    }
6833
6834
    // Coalesce with previous range if overlapping or adjacent (but not
6835
    // identical)
6836
940
    if (!coalesced.empty()) {
6837
664
      auto &prev = coalesced.back();
6838
      // Check if current range overlaps or is adjacent to previous range
6839
      // but don't coalesce identical ranges (allow duplicates)
6840
664
      if (first_pos <= prev.second + 1 &&
6841
416
          !(first_pos == prev.first && last_pos == prev.second)) {
6842
        // Extend the previous range
6843
318
        prev.second = (std::max)(prev.second, last_pos);
6844
318
        continue;
6845
318
      }
6846
664
    }
6847
6848
    // Add new range
6849
622
    coalesced.emplace_back(first_pos, last_pos);
6850
622
  }
6851
6852
276
  ranges = std::move(coalesced);
6853
276
}
6854
6855
1.75k
inline bool range_error(Request &req, Response &res) {
6856
1.75k
  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
6857
740
    ssize_t content_len = static_cast<ssize_t>(
6858
740
        res.content_length_ ? res.content_length_ : res.body.size());
6859
6860
740
    std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
6861
740
    size_t overwrapping_count = 0;
6862
6863
    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
6864
    // 'HTTP Semantics' to avoid potential denial-of-service attacks.
6865
    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
6866
6867
    // Too many ranges
6868
740
    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }
6869
6870
1.62k
    for (auto &r : req.ranges) {
6871
1.62k
      auto &first_pos = r.first;
6872
1.62k
      auto &last_pos = r.second;
6873
6874
1.62k
      if (first_pos == -1 && last_pos == -1) {
6875
0
        first_pos = 0;
6876
0
        last_pos = content_len;
6877
0
      }
6878
6879
1.62k
      if (first_pos == -1) {
6880
545
        first_pos = content_len - last_pos;
6881
545
        last_pos = content_len - 1;
6882
545
      }
6883
6884
      // NOTE: RFC-9110 '14.1.2. Byte Ranges':
6885
      // A client can limit the number of bytes requested without knowing the
6886
      // size of the selected representation. If the last-pos value is absent,
6887
      // or if the value is greater than or equal to the current length of the
6888
      // representation data, the byte range is interpreted as the remainder of
6889
      // the representation (i.e., the server replaces the value of last-pos
6890
      // with a value that is one less than the current length of the selected
6891
      // representation).
6892
      // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
6893
1.62k
      if (last_pos == -1 || last_pos >= content_len) {
6894
519
        last_pos = content_len - 1;
6895
519
      }
6896
6897
      // Range must be within content length
6898
1.62k
      if (!(0 <= first_pos && first_pos <= last_pos &&
6899
1.35k
            last_pos <= content_len - 1)) {
6900
271
        return true;
6901
271
      }
6902
6903
      // Request must not have more than two overlapping ranges
6904
1.98k
      for (const auto &processed_range : processed_ranges) {
6905
1.98k
        if (!(last_pos < processed_range.first ||
6906
1.29k
              first_pos > processed_range.second)) {
6907
385
          overwrapping_count++;
6908
385
          if (overwrapping_count > 2) { return true; }
6909
344
          break; // Only count once per range
6910
385
        }
6911
1.98k
      }
6912
6913
1.31k
      processed_ranges.emplace_back(first_pos, last_pos);
6914
1.31k
    }
6915
6916
    // After validation, coalesce overlapping ranges as per RFC 9110
6917
427
    coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
6918
427
  }
6919
6920
1.44k
  return false;
6921
1.75k
}
6922
6923
inline std::pair<size_t, size_t>
6924
773
get_range_offset_and_length(Range r, size_t content_length) {
6925
773
  assert(r.first != -1 && r.second != -1);
6926
773
  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
6927
773
  assert(r.first <= r.second &&
6928
773
         r.second < static_cast<ssize_t>(content_length));
6929
773
  (void)(content_length);
6930
773
  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
6931
773
}
6932
6933
inline std::string make_content_range_header_field(
6934
773
    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
6935
773
  auto st = offset_and_length.first;
6936
773
  auto ed = st + offset_and_length.second - 1;
6937
6938
773
  std::string field = "bytes ";
6939
773
  field += std::to_string(st);
6940
773
  field += '-';
6941
773
  field += std::to_string(ed);
6942
773
  field += '/';
6943
773
  field += std::to_string(content_length);
6944
773
  return field;
6945
773
}
6946
6947
template <typename SToken, typename CToken, typename Content>
6948
bool process_multipart_ranges_data(const Request &req,
6949
                                   const std::string &boundary,
6950
                                   const std::string &content_type,
6951
                                   size_t content_length, SToken stoken,
6952
240
                                   CToken ctoken, Content content) {
6953
826
  for (size_t i = 0; i < req.ranges.size(); i++) {
6954
586
    ctoken("--");
6955
586
    stoken(boundary);
6956
586
    ctoken("\r\n");
6957
586
    if (!content_type.empty()) {
6958
586
      ctoken("Content-Type: ");
6959
586
      stoken(content_type);
6960
586
      ctoken("\r\n");
6961
586
    }
6962
6963
586
    auto offset_and_length =
6964
586
        get_range_offset_and_length(req.ranges[i], content_length);
6965
6966
586
    ctoken("Content-Range: ");
6967
586
    stoken(make_content_range_header_field(offset_and_length, content_length));
6968
586
    ctoken("\r\n");
6969
586
    ctoken("\r\n");
6970
6971
586
    if (!content(offset_and_length.first, offset_and_length.second)) {
6972
0
      return false;
6973
0
    }
6974
586
    ctoken("\r\n");
6975
586
  }
6976
6977
240
  ctoken("--");
6978
240
  stoken(boundary);
6979
240
  ctoken("--");
6980
6981
240
  return true;
6982
240
}
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
6952
240
                                   CToken ctoken, Content content) {
6953
826
  for (size_t i = 0; i < req.ranges.size(); i++) {
6954
586
    ctoken("--");
6955
586
    stoken(boundary);
6956
586
    ctoken("\r\n");
6957
586
    if (!content_type.empty()) {
6958
586
      ctoken("Content-Type: ");
6959
586
      stoken(content_type);
6960
586
      ctoken("\r\n");
6961
586
    }
6962
6963
586
    auto offset_and_length =
6964
586
        get_range_offset_and_length(req.ranges[i], content_length);
6965
6966
586
    ctoken("Content-Range: ");
6967
586
    stoken(make_content_range_header_field(offset_and_length, content_length));
6968
586
    ctoken("\r\n");
6969
586
    ctoken("\r\n");
6970
6971
586
    if (!content(offset_and_length.first, offset_and_length.second)) {
6972
0
      return false;
6973
0
    }
6974
586
    ctoken("\r\n");
6975
586
  }
6976
6977
240
  ctoken("--");
6978
240
  stoken(boundary);
6979
240
  ctoken("--");
6980
6981
240
  return true;
6982
240
}
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})
6983
6984
inline void make_multipart_ranges_data(const Request &req, Response &res,
6985
                                       const std::string &boundary,
6986
                                       const std::string &content_type,
6987
                                       size_t content_length,
6988
240
                                       std::string &data) {
6989
240
  process_multipart_ranges_data(
6990
240
      req, boundary, content_type, content_length,
6991
1.99k
      [&](const std::string &token) { data += token; },
6992
5.16k
      [&](const std::string &token) { data += token; },
6993
586
      [&](size_t offset, size_t length) {
6994
586
        assert(offset + length <= content_length);
6995
586
        data += res.body.substr(offset, length);
6996
586
        return true;
6997
586
      });
6998
240
}
6999
7000
inline size_t get_multipart_ranges_data_length(const Request &req,
7001
                                               const std::string &boundary,
7002
                                               const std::string &content_type,
7003
0
                                               size_t content_length) {
7004
0
  size_t data_length = 0;
7005
7006
0
  process_multipart_ranges_data(
7007
0
      req, boundary, content_type, content_length,
7008
0
      [&](const std::string &token) { data_length += token.size(); },
7009
0
      [&](const std::string &token) { data_length += token.size(); },
7010
0
      [&](size_t /*offset*/, size_t length) {
7011
0
        data_length += length;
7012
0
        return true;
7013
0
      });
7014
7015
0
  return data_length;
7016
0
}
7017
7018
template <typename T>
7019
inline bool
7020
write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
7021
                            const std::string &boundary,
7022
                            const std::string &content_type,
7023
0
                            size_t content_length, const T &is_shutting_down) {
7024
0
  return process_multipart_ranges_data(
7025
0
      req, boundary, content_type, content_length,
7026
0
      [&](const std::string &token) { strm.write(token); },
7027
0
      [&](const std::string &token) { strm.write(token); },
7028
0
      [&](size_t offset, size_t length) {
7029
0
        return write_content(strm, res.content_provider_, offset, length,
7030
0
                             is_shutting_down);
7031
0
      });
7032
0
}
7033
7034
4.13k
inline bool expect_content(const Request &req) {
7035
4.13k
  if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
7036
2.50k
      req.method == "DELETE") {
7037
2.50k
    return true;
7038
2.50k
  }
7039
1.63k
  if (req.has_header("Content-Length") &&
7040
407
      req.get_header_value_u64("Content-Length") > 0) {
7041
403
    return true;
7042
403
  }
7043
1.23k
  if (is_chunked_transfer_encoding(req.headers)) { return true; }
7044
774
  return false;
7045
1.23k
}
7046
7047
0
inline bool has_crlf(const std::string &s) {
7048
0
  auto p = s.c_str();
7049
0
  while (*p) {
7050
0
    if (*p == '\r' || *p == '\n') { return true; }
7051
0
    p++;
7052
0
  }
7053
0
  return false;
7054
0
}
7055
7056
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7057
inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
7058
  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
7059
      EVP_MD_CTX_new(), EVP_MD_CTX_free);
7060
7061
  unsigned int hash_length = 0;
7062
  unsigned char hash[EVP_MAX_MD_SIZE];
7063
7064
  EVP_DigestInit_ex(context.get(), algo, nullptr);
7065
  EVP_DigestUpdate(context.get(), s.c_str(), s.size());
7066
  EVP_DigestFinal_ex(context.get(), hash, &hash_length);
7067
7068
  std::stringstream ss;
7069
  for (auto i = 0u; i < hash_length; ++i) {
7070
    ss << std::hex << std::setw(2) << std::setfill('0')
7071
       << static_cast<unsigned int>(hash[i]);
7072
  }
7073
7074
  return ss.str();
7075
}
7076
7077
inline std::string MD5(const std::string &s) {
7078
  return message_digest(s, EVP_md5());
7079
}
7080
7081
inline std::string SHA_256(const std::string &s) {
7082
  return message_digest(s, EVP_sha256());
7083
}
7084
7085
inline std::string SHA_512(const std::string &s) {
7086
  return message_digest(s, EVP_sha512());
7087
}
7088
7089
inline std::pair<std::string, std::string> make_digest_authentication_header(
7090
    const Request &req, const std::map<std::string, std::string> &auth,
7091
    size_t cnonce_count, const std::string &cnonce, const std::string &username,
7092
    const std::string &password, bool is_proxy = false) {
7093
  std::string nc;
7094
  {
7095
    std::stringstream ss;
7096
    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
7097
    nc = ss.str();
7098
  }
7099
7100
  std::string qop;
7101
  if (auth.find("qop") != auth.end()) {
7102
    qop = auth.at("qop");
7103
    if (qop.find("auth-int") != std::string::npos) {
7104
      qop = "auth-int";
7105
    } else if (qop.find("auth") != std::string::npos) {
7106
      qop = "auth";
7107
    } else {
7108
      qop.clear();
7109
    }
7110
  }
7111
7112
  std::string algo = "MD5";
7113
  if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
7114
7115
  std::string response;
7116
  {
7117
    auto H = algo == "SHA-256"   ? detail::SHA_256
7118
             : algo == "SHA-512" ? detail::SHA_512
7119
                                 : detail::MD5;
7120
7121
    auto A1 = username + ":" + auth.at("realm") + ":" + password;
7122
7123
    auto A2 = req.method + ":" + req.path;
7124
    if (qop == "auth-int") { A2 += ":" + H(req.body); }
7125
7126
    if (qop.empty()) {
7127
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
7128
    } else {
7129
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
7130
                   ":" + qop + ":" + H(A2));
7131
    }
7132
  }
7133
7134
  auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
7135
7136
  auto field = "Digest username=\"" + username + "\", realm=\"" +
7137
               auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
7138
               "\", uri=\"" + req.path + "\", algorithm=" + algo +
7139
               (qop.empty() ? ", response=\""
7140
                            : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
7141
                                  cnonce + "\", response=\"") +
7142
               response + "\"" +
7143
               (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
7144
7145
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
7146
  return std::make_pair(key, field);
7147
}
7148
7149
inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
7150
  detail::set_nonblocking(sock, true);
7151
  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
7152
7153
  char buf[1];
7154
  return !SSL_peek(ssl, buf, 1) &&
7155
         SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7156
}
7157
7158
#ifdef _WIN32
7159
// NOTE: This code came up with the following stackoverflow post:
7160
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
7161
inline bool load_system_certs_on_windows(X509_STORE *store) {
7162
  auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
7163
  if (!hStore) { return false; }
7164
7165
  auto result = false;
7166
  PCCERT_CONTEXT pContext = NULL;
7167
  while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
7168
         nullptr) {
7169
    auto encoded_cert =
7170
        static_cast<const unsigned char *>(pContext->pbCertEncoded);
7171
7172
    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
7173
    if (x509) {
7174
      X509_STORE_add_cert(store, x509);
7175
      X509_free(x509);
7176
      result = true;
7177
    }
7178
  }
7179
7180
  CertFreeCertificateContext(pContext);
7181
  CertCloseStore(hStore, 0);
7182
7183
  return result;
7184
}
7185
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
7186
template <typename T>
7187
using CFObjectPtr =
7188
    std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
7189
7190
inline void cf_object_ptr_deleter(CFTypeRef obj) {
7191
  if (obj) { CFRelease(obj); }
7192
}
7193
7194
inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
7195
  CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
7196
  CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
7197
                        kCFBooleanTrue};
7198
7199
  CFObjectPtr<CFDictionaryRef> query(
7200
      CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
7201
                         sizeof(keys) / sizeof(keys[0]),
7202
                         &kCFTypeDictionaryKeyCallBacks,
7203
                         &kCFTypeDictionaryValueCallBacks),
7204
      cf_object_ptr_deleter);
7205
7206
  if (!query) { return false; }
7207
7208
  CFTypeRef security_items = nullptr;
7209
  if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
7210
      CFArrayGetTypeID() != CFGetTypeID(security_items)) {
7211
    return false;
7212
  }
7213
7214
  certs.reset(reinterpret_cast<CFArrayRef>(security_items));
7215
  return true;
7216
}
7217
7218
inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
7219
  CFArrayRef root_security_items = nullptr;
7220
  if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
7221
    return false;
7222
  }
7223
7224
  certs.reset(root_security_items);
7225
  return true;
7226
}
7227
7228
inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
7229
  auto result = false;
7230
  for (auto i = 0; i < CFArrayGetCount(certs); ++i) {
7231
    const auto cert = reinterpret_cast<const __SecCertificate *>(
7232
        CFArrayGetValueAtIndex(certs, i));
7233
7234
    if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
7235
7236
    CFDataRef cert_data = nullptr;
7237
    if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
7238
        errSecSuccess) {
7239
      continue;
7240
    }
7241
7242
    CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
7243
7244
    auto encoded_cert = static_cast<const unsigned char *>(
7245
        CFDataGetBytePtr(cert_data_ptr.get()));
7246
7247
    auto x509 =
7248
        d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
7249
7250
    if (x509) {
7251
      X509_STORE_add_cert(store, x509);
7252
      X509_free(x509);
7253
      result = true;
7254
    }
7255
  }
7256
7257
  return result;
7258
}
7259
7260
inline bool load_system_certs_on_macos(X509_STORE *store) {
7261
  auto result = false;
7262
  CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
7263
  if (retrieve_certs_from_keychain(certs) && certs) {
7264
    result = add_certs_to_x509_store(certs.get(), store);
7265
  }
7266
7267
  if (retrieve_root_certs_from_keychain(certs) && certs) {
7268
    result = add_certs_to_x509_store(certs.get(), store) || result;
7269
  }
7270
7271
  return result;
7272
}
7273
#endif // _WIN32
7274
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
7275
7276
#ifdef _WIN32
7277
class WSInit {
7278
public:
7279
  WSInit() {
7280
    WSADATA wsaData;
7281
    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
7282
  }
7283
7284
  ~WSInit() {
7285
    if (is_valid_) WSACleanup();
7286
  }
7287
7288
  bool is_valid_ = false;
7289
};
7290
7291
static WSInit wsinit_;
7292
#endif
7293
7294
inline bool parse_www_authenticate(const Response &res,
7295
                                   std::map<std::string, std::string> &auth,
7296
0
                                   bool is_proxy) {
7297
0
  auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
7298
0
  if (res.has_header(auth_key)) {
7299
0
    thread_local auto re =
7300
0
        std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
7301
0
    auto s = res.get_header_value(auth_key);
7302
0
    auto pos = s.find(' ');
7303
0
    if (pos != std::string::npos) {
7304
0
      auto type = s.substr(0, pos);
7305
0
      if (type == "Basic") {
7306
0
        return false;
7307
0
      } else if (type == "Digest") {
7308
0
        s = s.substr(pos + 1);
7309
0
        auto beg = std::sregex_iterator(s.begin(), s.end(), re);
7310
0
        for (auto i = beg; i != std::sregex_iterator(); ++i) {
7311
0
          const auto &m = *i;
7312
0
          auto key = s.substr(static_cast<size_t>(m.position(1)),
7313
0
                              static_cast<size_t>(m.length(1)));
7314
0
          auto val = m.length(2) > 0
7315
0
                         ? s.substr(static_cast<size_t>(m.position(2)),
7316
0
                                    static_cast<size_t>(m.length(2)))
7317
0
                         : s.substr(static_cast<size_t>(m.position(3)),
7318
0
                                    static_cast<size_t>(m.length(3)));
7319
0
          auth[std::move(key)] = std::move(val);
7320
0
        }
7321
0
        return true;
7322
0
      }
7323
0
    }
7324
0
  }
7325
0
  return false;
7326
0
}
7327
7328
class ContentProviderAdapter {
7329
public:
7330
  explicit ContentProviderAdapter(
7331
      ContentProviderWithoutLength &&content_provider)
7332
0
      : content_provider_(std::move(content_provider)) {}
7333
7334
0
  bool operator()(size_t offset, size_t, DataSink &sink) {
7335
0
    return content_provider_(offset, sink);
7336
0
  }
7337
7338
private:
7339
  ContentProviderWithoutLength content_provider_;
7340
};
7341
7342
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
7343
namespace fields {
7344
7345
1.76M
inline bool is_token_char(char c) {
7346
1.76M
  return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
7347
117k
         c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
7348
33.6k
         c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
7349
1.76M
}
7350
7351
171k
inline bool is_token(const std::string &s) {
7352
171k
  if (s.empty()) { return false; }
7353
1.76M
  for (auto c : s) {
7354
1.76M
    if (!is_token_char(c)) { return false; }
7355
1.76M
  }
7356
170k
  return true;
7357
170k
}
7358
7359
171k
inline bool is_field_name(const std::string &s) { return is_token(s); }
7360
7361
5.55M
inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
7362
7363
426k
inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
7364
7365
5.55M
inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
7366
7367
170k
inline bool is_field_content(const std::string &s) {
7368
170k
  if (s.empty()) { return true; }
7369
7370
131k
  if (s.size() == 1) {
7371
53.5k
    return is_field_vchar(s[0]);
7372
77.6k
  } else if (s.size() == 2) {
7373
3.96k
    return is_field_vchar(s[0]) && is_field_vchar(s[1]);
7374
73.6k
  } else {
7375
73.6k
    size_t i = 0;
7376
7377
73.6k
    if (!is_field_vchar(s[i])) { return false; }
7378
73.6k
    i++;
7379
7380
5.49M
    while (i < s.size() - 1) {
7381
5.42M
      auto c = s[i++];
7382
5.42M
      if (c == ' ' || c == '\t' || is_field_vchar(c)) {
7383
5.42M
      } else {
7384
29
        return false;
7385
29
      }
7386
5.42M
    }
7387
7388
73.6k
    return is_field_vchar(s[i]);
7389
73.6k
  }
7390
131k
}
7391
7392
170k
inline bool is_field_value(const std::string &s) { return is_field_content(s); }
7393
7394
} // namespace fields
7395
7396
} // namespace detail
7397
7398
6.05k
inline const char *status_message(int status) {
7399
6.05k
  switch (status) {
7400
2
  case StatusCode::Continue_100: return "Continue";
7401
0
  case StatusCode::SwitchingProtocol_101: return "Switching Protocol";
7402
0
  case StatusCode::Processing_102: return "Processing";
7403
0
  case StatusCode::EarlyHints_103: return "Early Hints";
7404
1.01k
  case StatusCode::OK_200: return "OK";
7405
0
  case StatusCode::Created_201: return "Created";
7406
0
  case StatusCode::Accepted_202: return "Accepted";
7407
0
  case StatusCode::NonAuthoritativeInformation_203:
7408
0
    return "Non-Authoritative Information";
7409
0
  case StatusCode::NoContent_204: return "No Content";
7410
0
  case StatusCode::ResetContent_205: return "Reset Content";
7411
427
  case StatusCode::PartialContent_206: return "Partial Content";
7412
0
  case StatusCode::MultiStatus_207: return "Multi-Status";
7413
0
  case StatusCode::AlreadyReported_208: return "Already Reported";
7414
0
  case StatusCode::IMUsed_226: return "IM Used";
7415
0
  case StatusCode::MultipleChoices_300: return "Multiple Choices";
7416
0
  case StatusCode::MovedPermanently_301: return "Moved Permanently";
7417
0
  case StatusCode::Found_302: return "Found";
7418
0
  case StatusCode::SeeOther_303: return "See Other";
7419
0
  case StatusCode::NotModified_304: return "Not Modified";
7420
0
  case StatusCode::UseProxy_305: return "Use Proxy";
7421
0
  case StatusCode::unused_306: return "unused";
7422
0
  case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";
7423
0
  case StatusCode::PermanentRedirect_308: return "Permanent Redirect";
7424
4.06k
  case StatusCode::BadRequest_400: return "Bad Request";
7425
0
  case StatusCode::Unauthorized_401: return "Unauthorized";
7426
0
  case StatusCode::PaymentRequired_402: return "Payment Required";
7427
0
  case StatusCode::Forbidden_403: return "Forbidden";
7428
61
  case StatusCode::NotFound_404: return "Not Found";
7429
0
  case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";
7430
0
  case StatusCode::NotAcceptable_406: return "Not Acceptable";
7431
0
  case StatusCode::ProxyAuthenticationRequired_407:
7432
0
    return "Proxy Authentication Required";
7433
0
  case StatusCode::RequestTimeout_408: return "Request Timeout";
7434
0
  case StatusCode::Conflict_409: return "Conflict";
7435
0
  case StatusCode::Gone_410: return "Gone";
7436
0
  case StatusCode::LengthRequired_411: return "Length Required";
7437
0
  case StatusCode::PreconditionFailed_412: return "Precondition Failed";
7438
1
  case StatusCode::PayloadTooLarge_413: return "Payload Too Large";
7439
7
  case StatusCode::UriTooLong_414: return "URI Too Long";
7440
27
  case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";
7441
454
  case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";
7442
0
  case StatusCode::ExpectationFailed_417: return "Expectation Failed";
7443
0
  case StatusCode::ImATeapot_418: return "I'm a teapot";
7444
0
  case StatusCode::MisdirectedRequest_421: return "Misdirected Request";
7445
0
  case StatusCode::UnprocessableContent_422: return "Unprocessable Content";
7446
0
  case StatusCode::Locked_423: return "Locked";
7447
0
  case StatusCode::FailedDependency_424: return "Failed Dependency";
7448
0
  case StatusCode::TooEarly_425: return "Too Early";
7449
0
  case StatusCode::UpgradeRequired_426: return "Upgrade Required";
7450
0
  case StatusCode::PreconditionRequired_428: return "Precondition Required";
7451
0
  case StatusCode::TooManyRequests_429: return "Too Many Requests";
7452
0
  case StatusCode::RequestHeaderFieldsTooLarge_431:
7453
0
    return "Request Header Fields Too Large";
7454
0
  case StatusCode::UnavailableForLegalReasons_451:
7455
0
    return "Unavailable For Legal Reasons";
7456
0
  case StatusCode::NotImplemented_501: return "Not Implemented";
7457
0
  case StatusCode::BadGateway_502: return "Bad Gateway";
7458
0
  case StatusCode::ServiceUnavailable_503: return "Service Unavailable";
7459
0
  case StatusCode::GatewayTimeout_504: return "Gateway Timeout";
7460
0
  case StatusCode::HttpVersionNotSupported_505:
7461
0
    return "HTTP Version Not Supported";
7462
0
  case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";
7463
0
  case StatusCode::InsufficientStorage_507: return "Insufficient Storage";
7464
0
  case StatusCode::LoopDetected_508: return "Loop Detected";
7465
0
  case StatusCode::NotExtended_510: return "Not Extended";
7466
0
  case StatusCode::NetworkAuthenticationRequired_511:
7467
0
    return "Network Authentication Required";
7468
7469
0
  default:
7470
0
  case StatusCode::InternalServerError_500: return "Internal Server Error";
7471
6.05k
  }
7472
6.05k
}
7473
7474
0
inline std::string to_string(const Error error) {
7475
0
  switch (error) {
7476
0
  case Error::Success: return "Success (no error)";
7477
0
  case Error::Unknown: return "Unknown";
7478
0
  case Error::Connection: return "Could not establish connection";
7479
0
  case Error::BindIPAddress: return "Failed to bind IP address";
7480
0
  case Error::Read: return "Failed to read connection";
7481
0
  case Error::Write: return "Failed to write connection";
7482
0
  case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
7483
0
  case Error::Canceled: return "Connection handling canceled";
7484
0
  case Error::SSLConnection: return "SSL connection failed";
7485
0
  case Error::SSLLoadingCerts: return "SSL certificate loading failed";
7486
0
  case Error::SSLServerVerification: return "SSL server verification failed";
7487
0
  case Error::SSLServerHostnameVerification:
7488
0
    return "SSL server hostname verification failed";
7489
0
  case Error::UnsupportedMultipartBoundaryChars:
7490
0
    return "Unsupported HTTP multipart boundary characters";
7491
0
  case Error::Compression: return "Compression failed";
7492
0
  case Error::ConnectionTimeout: return "Connection timed out";
7493
0
  case Error::ProxyConnection: return "Proxy connection failed";
7494
0
  case Error::ConnectionClosed: return "Connection closed by server";
7495
0
  case Error::Timeout: return "Read timeout";
7496
0
  case Error::ResourceExhaustion: return "Resource exhaustion";
7497
0
  case Error::TooManyFormDataFiles: return "Too many form data files";
7498
0
  case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
7499
0
  case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
7500
0
  case Error::ExceedMaxSocketDescriptorCount:
7501
0
    return "Exceeded maximum socket descriptor count";
7502
0
  case Error::InvalidRequestLine: return "Invalid request line";
7503
0
  case Error::InvalidHTTPMethod: return "Invalid HTTP method";
7504
0
  case Error::InvalidHTTPVersion: return "Invalid HTTP version";
7505
0
  case Error::InvalidHeaders: return "Invalid headers";
7506
0
  case Error::MultipartParsing: return "Multipart parsing failed";
7507
0
  case Error::OpenFile: return "Failed to open file";
7508
0
  case Error::Listen: return "Failed to listen on socket";
7509
0
  case Error::GetSockName: return "Failed to get socket name";
7510
0
  case Error::UnsupportedAddressFamily: return "Unsupported address family";
7511
0
  case Error::HTTPParsing: return "HTTP parsing failed";
7512
0
  case Error::InvalidRangeHeader: return "Invalid Range header";
7513
0
  default: break;
7514
0
  }
7515
0
7516
0
  return "Invalid";
7517
0
}
7518
7519
0
inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
7520
0
  os << to_string(obj);
7521
0
  os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
7522
0
  return os;
7523
0
}
7524
7525
0
inline std::string hosted_at(const std::string &hostname) {
7526
0
  std::vector<std::string> addrs;
7527
0
  hosted_at(hostname, addrs);
7528
0
  if (addrs.empty()) { return std::string(); }
7529
0
  return addrs[0];
7530
0
}
7531
7532
inline void hosted_at(const std::string &hostname,
7533
0
                      std::vector<std::string> &addrs) {
7534
0
  struct addrinfo hints;
7535
0
  struct addrinfo *result;
7536
0
7537
0
  memset(&hints, 0, sizeof(struct addrinfo));
7538
0
  hints.ai_family = AF_UNSPEC;
7539
0
  hints.ai_socktype = SOCK_STREAM;
7540
0
  hints.ai_protocol = 0;
7541
0
7542
0
  if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
7543
0
                                       &result, 0)) {
7544
0
#if defined __linux__ && !defined __ANDROID__
7545
0
    res_init();
7546
0
#endif
7547
0
    return;
7548
0
  }
7549
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
7550
0
7551
0
  for (auto rp = result; rp; rp = rp->ai_next) {
7552
0
    const auto &addr =
7553
0
        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
7554
0
    std::string ip;
7555
0
    auto dummy = -1;
7556
0
    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
7557
0
                                dummy)) {
7558
0
      addrs.emplace_back(std::move(ip));
7559
0
    }
7560
0
  }
7561
0
}
7562
7563
0
inline std::string encode_uri_component(const std::string &value) {
7564
0
  std::ostringstream escaped;
7565
0
  escaped.fill('0');
7566
0
  escaped << std::hex;
7567
0
7568
0
  for (auto c : value) {
7569
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
7570
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
7571
0
        c == ')') {
7572
0
      escaped << c;
7573
0
    } else {
7574
0
      escaped << std::uppercase;
7575
0
      escaped << '%' << std::setw(2)
7576
0
              << static_cast<int>(static_cast<unsigned char>(c));
7577
0
      escaped << std::nouppercase;
7578
0
    }
7579
0
  }
7580
0
7581
0
  return escaped.str();
7582
0
}
7583
7584
0
inline std::string encode_uri(const std::string &value) {
7585
0
  std::ostringstream escaped;
7586
0
  escaped.fill('0');
7587
0
  escaped << std::hex;
7588
0
7589
0
  for (auto c : value) {
7590
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
7591
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
7592
0
        c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
7593
0
        c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {
7594
0
      escaped << c;
7595
0
    } else {
7596
0
      escaped << std::uppercase;
7597
0
      escaped << '%' << std::setw(2)
7598
0
              << static_cast<int>(static_cast<unsigned char>(c));
7599
0
      escaped << std::nouppercase;
7600
0
    }
7601
0
  }
7602
0
7603
0
  return escaped.str();
7604
0
}
7605
7606
0
inline std::string decode_uri_component(const std::string &value) {
7607
0
  std::string result;
7608
0
7609
0
  for (size_t i = 0; i < value.size(); i++) {
7610
0
    if (value[i] == '%' && i + 2 < value.size()) {
7611
0
      auto val = 0;
7612
0
      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
7613
0
        result += static_cast<char>(val);
7614
0
        i += 2;
7615
0
      } else {
7616
0
        result += value[i];
7617
0
      }
7618
0
    } else {
7619
0
      result += value[i];
7620
0
    }
7621
0
  }
7622
0
7623
0
  return result;
7624
0
}
7625
7626
0
inline std::string decode_uri(const std::string &value) {
7627
0
  std::string result;
7628
0
7629
0
  for (size_t i = 0; i < value.size(); i++) {
7630
0
    if (value[i] == '%' && i + 2 < value.size()) {
7631
0
      auto val = 0;
7632
0
      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
7633
0
        result += static_cast<char>(val);
7634
0
        i += 2;
7635
0
      } else {
7636
0
        result += value[i];
7637
0
      }
7638
0
    } else {
7639
0
      result += value[i];
7640
0
    }
7641
0
  }
7642
0
7643
0
  return result;
7644
0
}
7645
7646
0
inline std::string encode_path_component(const std::string &component) {
7647
0
  std::string result;
7648
0
  result.reserve(component.size() * 3);
7649
0
7650
0
  for (size_t i = 0; i < component.size(); i++) {
7651
0
    auto c = static_cast<unsigned char>(component[i]);
7652
0
7653
0
    // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
7654
0
    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
7655
0
      result += static_cast<char>(c);
7656
0
    }
7657
0
    // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
7658
0
    // "," / ";" / "="
7659
0
    else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
7660
0
             c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
7661
0
             c == '=') {
7662
0
      result += static_cast<char>(c);
7663
0
    }
7664
0
    // Colon is allowed in path segments except first segment
7665
0
    else if (c == ':') {
7666
0
      result += static_cast<char>(c);
7667
0
    }
7668
0
    // @ is allowed in path
7669
0
    else if (c == '@') {
7670
0
      result += static_cast<char>(c);
7671
0
    } else {
7672
0
      result += '%';
7673
0
      char hex[3];
7674
0
      snprintf(hex, sizeof(hex), "%02X", c);
7675
0
      result.append(hex, 2);
7676
0
    }
7677
0
  }
7678
0
  return result;
7679
0
}
7680
7681
161k
inline std::string decode_path_component(const std::string &component) {
7682
161k
  std::string result;
7683
161k
  result.reserve(component.size());
7684
7685
5.83M
  for (size_t i = 0; i < component.size(); i++) {
7686
5.67M
    if (component[i] == '%' && i + 1 < component.size()) {
7687
26.7k
      if (component[i + 1] == 'u') {
7688
        // Unicode %uXXXX encoding
7689
5.30k
        auto val = 0;
7690
5.30k
        if (detail::from_hex_to_i(component, i + 2, 4, val)) {
7691
          // 4 digits Unicode codes
7692
2.25k
          char buff[4];
7693
2.25k
          size_t len = detail::to_utf8(val, buff);
7694
2.25k
          if (len > 0) { result.append(buff, len); }
7695
2.25k
          i += 5; // 'u0000'
7696
3.05k
        } else {
7697
3.05k
          result += component[i];
7698
3.05k
        }
7699
21.4k
      } else {
7700
        // Standard %XX encoding
7701
21.4k
        auto val = 0;
7702
21.4k
        if (detail::from_hex_to_i(component, i + 1, 2, val)) {
7703
          // 2 digits hex codes
7704
2.78k
          result += static_cast<char>(val);
7705
2.78k
          i += 2; // 'XX'
7706
18.6k
        } else {
7707
18.6k
          result += component[i];
7708
18.6k
        }
7709
21.4k
      }
7710
5.64M
    } else {
7711
5.64M
      result += component[i];
7712
5.64M
    }
7713
5.67M
  }
7714
161k
  return result;
7715
161k
}
7716
7717
inline std::string encode_query_component(const std::string &component,
7718
0
                                          bool space_as_plus) {
7719
0
  std::string result;
7720
0
  result.reserve(component.size() * 3);
7721
0
7722
0
  for (size_t i = 0; i < component.size(); i++) {
7723
0
    auto c = static_cast<unsigned char>(component[i]);
7724
0
7725
0
    // Unreserved characters per RFC 3986
7726
0
    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
7727
0
      result += static_cast<char>(c);
7728
0
    }
7729
0
    // Space handling
7730
0
    else if (c == ' ') {
7731
0
      if (space_as_plus) {
7732
0
        result += '+';
7733
0
      } else {
7734
0
        result += "%20";
7735
0
      }
7736
0
    }
7737
0
    // Plus sign handling
7738
0
    else if (c == '+') {
7739
0
      if (space_as_plus) {
7740
0
        result += "%2B";
7741
0
      } else {
7742
0
        result += static_cast<char>(c);
7743
0
      }
7744
0
    }
7745
0
    // Query-safe sub-delimiters (excluding & and = which are query delimiters)
7746
0
    else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
7747
0
             c == '*' || c == ',' || c == ';') {
7748
0
      result += static_cast<char>(c);
7749
0
    }
7750
0
    // Colon and @ are allowed in query
7751
0
    else if (c == ':' || c == '@') {
7752
0
      result += static_cast<char>(c);
7753
0
    }
7754
0
    // Forward slash is allowed in query values
7755
0
    else if (c == '/') {
7756
0
      result += static_cast<char>(c);
7757
0
    }
7758
0
    // Question mark is allowed in query values (after first ?)
7759
0
    else if (c == '?') {
7760
0
      result += static_cast<char>(c);
7761
0
    } else {
7762
0
      result += '%';
7763
0
      char hex[3];
7764
0
      snprintf(hex, sizeof(hex), "%02X", c);
7765
0
      result.append(hex, 2);
7766
0
    }
7767
0
  }
7768
0
  return result;
7769
0
}
7770
7771
inline std::string decode_query_component(const std::string &component,
7772
10.4k
                                          bool plus_as_space) {
7773
10.4k
  std::string result;
7774
10.4k
  result.reserve(component.size());
7775
7776
532k
  for (size_t i = 0; i < component.size(); i++) {
7777
522k
    if (component[i] == '%' && i + 2 < component.size()) {
7778
9.88k
      std::string hex = component.substr(i + 1, 2);
7779
9.88k
      char *end;
7780
9.88k
      unsigned long value = std::strtoul(hex.c_str(), &end, 16);
7781
9.88k
      if (end == hex.c_str() + 2) {
7782
938
        result += static_cast<char>(value);
7783
938
        i += 2;
7784
8.95k
      } else {
7785
8.95k
        result += component[i];
7786
8.95k
      }
7787
512k
    } else if (component[i] == '+' && plus_as_space) {
7788
1.17k
      result += ' '; // + becomes space in form-urlencoded
7789
511k
    } else {
7790
511k
      result += component[i];
7791
511k
    }
7792
522k
  }
7793
10.4k
  return result;
7794
10.4k
}
7795
7796
inline std::string append_query_params(const std::string &path,
7797
0
                                       const Params &params) {
7798
0
  std::string path_with_query = path;
7799
0
  thread_local const std::regex re("[^?]+\\?.*");
7800
0
  auto delm = std::regex_match(path, re) ? '&' : '?';
7801
0
  path_with_query += delm + detail::params_to_query_str(params);
7802
0
  return path_with_query;
7803
0
}
7804
7805
// Header utilities
7806
inline std::pair<std::string, std::string>
7807
0
make_range_header(const Ranges &ranges) {
7808
0
  std::string field = "bytes=";
7809
0
  auto i = 0;
7810
0
  for (const auto &r : ranges) {
7811
0
    if (i != 0) { field += ", "; }
7812
0
    if (r.first != -1) { field += std::to_string(r.first); }
7813
0
    field += '-';
7814
0
    if (r.second != -1) { field += std::to_string(r.second); }
7815
0
    i++;
7816
0
  }
7817
0
  return std::make_pair("Range", std::move(field));
7818
0
}
7819
7820
inline std::pair<std::string, std::string>
7821
make_basic_authentication_header(const std::string &username,
7822
0
                                 const std::string &password, bool is_proxy) {
7823
0
  auto field = "Basic " + detail::base64_encode(username + ":" + password);
7824
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
7825
0
  return std::make_pair(key, std::move(field));
7826
0
}
7827
7828
inline std::pair<std::string, std::string>
7829
make_bearer_token_authentication_header(const std::string &token,
7830
0
                                        bool is_proxy = false) {
7831
0
  auto field = "Bearer " + token;
7832
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
7833
0
  return std::make_pair(key, std::move(field));
7834
0
}
7835
7836
// Request implementation
7837
13.7k
inline bool Request::has_header(const std::string &key) const {
7838
13.7k
  return detail::has_header(headers, key);
7839
13.7k
}
7840
7841
inline std::string Request::get_header_value(const std::string &key,
7842
28.6k
                                             const char *def, size_t id) const {
7843
28.6k
  return detail::get_header_value(headers, key, def, id);
7844
28.6k
}
7845
7846
0
inline size_t Request::get_header_value_count(const std::string &key) const {
7847
0
  auto r = headers.equal_range(key);
7848
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7849
0
}
7850
7851
inline void Request::set_header(const std::string &key,
7852
0
                                const std::string &val) {
7853
0
  if (detail::fields::is_field_name(key) &&
7854
0
      detail::fields::is_field_value(val)) {
7855
0
    headers.emplace(key, val);
7856
0
  }
7857
0
}
7858
7859
0
inline bool Request::has_trailer(const std::string &key) const {
7860
0
  return trailers.find(key) != trailers.end();
7861
0
}
7862
7863
inline std::string Request::get_trailer_value(const std::string &key,
7864
0
                                              size_t id) const {
7865
0
  auto rng = trailers.equal_range(key);
7866
0
  auto it = rng.first;
7867
0
  std::advance(it, static_cast<ssize_t>(id));
7868
0
  if (it != rng.second) { return it->second; }
7869
0
  return std::string();
7870
0
}
7871
7872
0
inline size_t Request::get_trailer_value_count(const std::string &key) const {
7873
0
  auto r = trailers.equal_range(key);
7874
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7875
0
}
7876
7877
0
inline bool Request::has_param(const std::string &key) const {
7878
0
  return params.find(key) != params.end();
7879
0
}
7880
7881
inline std::string Request::get_param_value(const std::string &key,
7882
0
                                            size_t id) const {
7883
0
  auto rng = params.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_param_value_count(const std::string &key) const {
7891
0
  auto r = params.equal_range(key);
7892
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7893
0
}
7894
7895
3.47k
inline bool Request::is_multipart_form_data() const {
7896
3.47k
  const auto &content_type = get_header_value("Content-Type");
7897
3.47k
  return !content_type.rfind("multipart/form-data", 0);
7898
3.47k
}
7899
7900
// Multipart FormData implementation
7901
inline std::string MultipartFormData::get_field(const std::string &key,
7902
0
                                                size_t id) const {
7903
0
  auto rng = fields.equal_range(key);
7904
0
  auto it = rng.first;
7905
0
  std::advance(it, static_cast<ssize_t>(id));
7906
0
  if (it != rng.second) { return it->second.content; }
7907
0
  return std::string();
7908
0
}
7909
7910
inline std::vector<std::string>
7911
0
MultipartFormData::get_fields(const std::string &key) const {
7912
0
  std::vector<std::string> values;
7913
0
  auto rng = fields.equal_range(key);
7914
0
  for (auto it = rng.first; it != rng.second; it++) {
7915
0
    values.push_back(it->second.content);
7916
0
  }
7917
0
  return values;
7918
0
}
7919
7920
0
inline bool MultipartFormData::has_field(const std::string &key) const {
7921
0
  return fields.find(key) != fields.end();
7922
0
}
7923
7924
0
inline size_t MultipartFormData::get_field_count(const std::string &key) const {
7925
0
  auto r = fields.equal_range(key);
7926
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7927
0
}
7928
7929
inline FormData MultipartFormData::get_file(const std::string &key,
7930
0
                                            size_t id) const {
7931
0
  auto rng = files.equal_range(key);
7932
0
  auto it = rng.first;
7933
0
  std::advance(it, static_cast<ssize_t>(id));
7934
0
  if (it != rng.second) { return it->second; }
7935
0
  return FormData();
7936
0
}
7937
7938
inline std::vector<FormData>
7939
0
MultipartFormData::get_files(const std::string &key) const {
7940
0
  std::vector<FormData> values;
7941
0
  auto rng = files.equal_range(key);
7942
0
  for (auto it = rng.first; it != rng.second; it++) {
7943
0
    values.push_back(it->second);
7944
0
  }
7945
0
  return values;
7946
0
}
7947
7948
0
inline bool MultipartFormData::has_file(const std::string &key) const {
7949
0
  return files.find(key) != files.end();
7950
0
}
7951
7952
0
inline size_t MultipartFormData::get_file_count(const std::string &key) const {
7953
0
  auto r = files.equal_range(key);
7954
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7955
0
}
7956
7957
// Response implementation
7958
6.21k
inline bool Response::has_header(const std::string &key) const {
7959
6.21k
  return headers.find(key) != headers.end();
7960
6.21k
}
7961
7962
inline std::string Response::get_header_value(const std::string &key,
7963
                                              const char *def,
7964
1.44k
                                              size_t id) const {
7965
1.44k
  return detail::get_header_value(headers, key, def, id);
7966
1.44k
}
7967
7968
0
inline size_t Response::get_header_value_count(const std::string &key) const {
7969
0
  auto r = headers.equal_range(key);
7970
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7971
0
}
7972
7973
inline void Response::set_header(const std::string &key,
7974
14.4k
                                 const std::string &val) {
7975
14.4k
  if (detail::fields::is_field_name(key) &&
7976
14.4k
      detail::fields::is_field_value(val)) {
7977
14.4k
    headers.emplace(key, val);
7978
14.4k
  }
7979
14.4k
}
7980
0
inline bool Response::has_trailer(const std::string &key) const {
7981
0
  return trailers.find(key) != trailers.end();
7982
0
}
7983
7984
inline std::string Response::get_trailer_value(const std::string &key,
7985
0
                                               size_t id) const {
7986
0
  auto rng = trailers.equal_range(key);
7987
0
  auto it = rng.first;
7988
0
  std::advance(it, static_cast<ssize_t>(id));
7989
0
  if (it != rng.second) { return it->second; }
7990
0
  return std::string();
7991
0
}
7992
7993
0
inline size_t Response::get_trailer_value_count(const std::string &key) const {
7994
0
  auto r = trailers.equal_range(key);
7995
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7996
0
}
7997
7998
0
inline void Response::set_redirect(const std::string &url, int stat) {
7999
0
  if (detail::fields::is_field_value(url)) {
8000
0
    set_header("Location", url);
8001
0
    if (300 <= stat && stat < 400) {
8002
0
      this->status = stat;
8003
0
    } else {
8004
0
      this->status = StatusCode::Found_302;
8005
0
    }
8006
0
  }
8007
0
}
8008
8009
inline void Response::set_content(const char *s, size_t n,
8010
0
                                  const std::string &content_type) {
8011
0
  body.assign(s, n);
8012
0
8013
0
  auto rng = headers.equal_range("Content-Type");
8014
0
  headers.erase(rng.first, rng.second);
8015
0
  set_header("Content-Type", content_type);
8016
0
}
8017
8018
inline void Response::set_content(const std::string &s,
8019
0
                                  const std::string &content_type) {
8020
0
  set_content(s.data(), s.size(), content_type);
8021
0
}
8022
8023
inline void Response::set_content(std::string &&s,
8024
1.75k
                                  const std::string &content_type) {
8025
1.75k
  body = std::move(s);
8026
8027
1.75k
  auto rng = headers.equal_range("Content-Type");
8028
1.75k
  headers.erase(rng.first, rng.second);
8029
1.75k
  set_header("Content-Type", content_type);
8030
1.75k
}
8031
8032
inline void Response::set_content_provider(
8033
    size_t in_length, const std::string &content_type, ContentProvider provider,
8034
0
    ContentProviderResourceReleaser resource_releaser) {
8035
0
  set_header("Content-Type", content_type);
8036
0
  content_length_ = in_length;
8037
0
  if (in_length > 0) { content_provider_ = std::move(provider); }
8038
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
8039
0
  is_chunked_content_provider_ = false;
8040
0
}
8041
8042
inline void Response::set_content_provider(
8043
    const std::string &content_type, ContentProviderWithoutLength provider,
8044
0
    ContentProviderResourceReleaser resource_releaser) {
8045
0
  set_header("Content-Type", content_type);
8046
0
  content_length_ = 0;
8047
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
8048
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
8049
0
  is_chunked_content_provider_ = false;
8050
0
}
8051
8052
inline void Response::set_chunked_content_provider(
8053
    const std::string &content_type, ContentProviderWithoutLength provider,
8054
0
    ContentProviderResourceReleaser resource_releaser) {
8055
0
  set_header("Content-Type", content_type);
8056
0
  content_length_ = 0;
8057
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
8058
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
8059
0
  is_chunked_content_provider_ = true;
8060
0
}
8061
8062
inline void Response::set_file_content(const std::string &path,
8063
0
                                       const std::string &content_type) {
8064
0
  file_content_path_ = path;
8065
0
  file_content_content_type_ = content_type;
8066
0
}
8067
8068
0
inline void Response::set_file_content(const std::string &path) {
8069
0
  file_content_path_ = path;
8070
0
}
8071
8072
// Result implementation
8073
0
inline bool Result::has_request_header(const std::string &key) const {
8074
0
  return request_headers_.find(key) != request_headers_.end();
8075
0
}
8076
8077
inline std::string Result::get_request_header_value(const std::string &key,
8078
                                                    const char *def,
8079
0
                                                    size_t id) const {
8080
0
  return detail::get_header_value(request_headers_, key, def, id);
8081
0
}
8082
8083
inline size_t
8084
0
Result::get_request_header_value_count(const std::string &key) const {
8085
0
  auto r = request_headers_.equal_range(key);
8086
0
  return static_cast<size_t>(std::distance(r.first, r.second));
8087
0
}
8088
8089
// Stream implementation
8090
6.05k
inline ssize_t Stream::write(const char *ptr) {
8091
6.05k
  return write(ptr, strlen(ptr));
8092
6.05k
}
8093
8094
0
inline ssize_t Stream::write(const std::string &s) {
8095
0
  return write(s.data(), s.size());
8096
0
}
8097
8098
// BodyReader implementation
8099
3.46k
inline ssize_t detail::BodyReader::read(char *buf, size_t len) {
8100
3.46k
  if (!stream) {
8101
0
    last_error = Error::Connection;
8102
0
    return -1;
8103
0
  }
8104
3.46k
  if (eof) { return 0; }
8105
8106
3.46k
  if (!chunked) {
8107
    // Content-Length based reading
8108
3.46k
    if (bytes_read >= content_length) {
8109
0
      eof = true;
8110
0
      return 0;
8111
0
    }
8112
8113
3.46k
    auto remaining = content_length - bytes_read;
8114
3.46k
    auto to_read = (std::min)(len, remaining);
8115
3.46k
    auto n = stream->read(buf, to_read);
8116
8117
3.46k
    if (n < 0) {
8118
0
      last_error = stream->get_error();
8119
0
      if (last_error == Error::Success) { last_error = Error::Read; }
8120
0
      eof = true;
8121
0
      return n;
8122
0
    }
8123
3.46k
    if (n == 0) {
8124
      // Unexpected EOF before content_length
8125
1.06k
      last_error = stream->get_error();
8126
1.06k
      if (last_error == Error::Success) { last_error = Error::Read; }
8127
1.06k
      eof = true;
8128
1.06k
      return 0;
8129
1.06k
    }
8130
8131
2.39k
    bytes_read += static_cast<size_t>(n);
8132
2.39k
    if (bytes_read >= content_length) { eof = true; }
8133
2.39k
    return n;
8134
3.46k
  }
8135
8136
  // Chunked transfer encoding: delegate to shared decoder instance.
8137
0
  if (!chunked_decoder) { chunked_decoder.reset(new ChunkedDecoder(*stream)); }
8138
8139
0
  size_t chunk_offset = 0;
8140
0
  size_t chunk_total = 0;
8141
0
  auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
8142
0
  if (n < 0) {
8143
0
    last_error = stream->get_error();
8144
0
    if (last_error == Error::Success) { last_error = Error::Read; }
8145
0
    eof = true;
8146
0
    return n;
8147
0
  }
8148
8149
0
  if (n == 0) {
8150
    // Final chunk observed. Leave trailer parsing to the caller (StreamHandle).
8151
0
    eof = true;
8152
0
    return 0;
8153
0
  }
8154
8155
0
  bytes_read += static_cast<size_t>(n);
8156
0
  return n;
8157
0
}
8158
8159
namespace detail {
8160
8161
inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
8162
                                time_t timeout_sec, time_t timeout_usec,
8163
                                time_t &actual_timeout_sec,
8164
0
                                time_t &actual_timeout_usec) {
8165
0
  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
8166
8167
0
  auto actual_timeout_msec =
8168
0
      (std::min)(max_timeout_msec - duration_msec, timeout_msec);
8169
8170
0
  if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
8171
8172
0
  actual_timeout_sec = actual_timeout_msec / 1000;
8173
0
  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
8174
0
}
8175
8176
// Socket stream implementation
8177
inline SocketStream::SocketStream(
8178
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
8179
    time_t write_timeout_sec, time_t write_timeout_usec,
8180
    time_t max_timeout_msec,
8181
    std::chrono::time_point<std::chrono::steady_clock> start_time)
8182
0
    : sock_(sock), read_timeout_sec_(read_timeout_sec),
8183
0
      read_timeout_usec_(read_timeout_usec),
8184
0
      write_timeout_sec_(write_timeout_sec),
8185
0
      write_timeout_usec_(write_timeout_usec),
8186
0
      max_timeout_msec_(max_timeout_msec), start_time_(start_time),
8187
0
      read_buff_(read_buff_size_, 0) {}
8188
8189
0
inline SocketStream::~SocketStream() = default;
8190
8191
0
inline bool SocketStream::is_readable() const {
8192
0
  return read_buff_off_ < read_buff_content_size_;
8193
0
}
8194
8195
0
inline bool SocketStream::wait_readable() const {
8196
0
  if (max_timeout_msec_ <= 0) {
8197
0
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
8198
0
  }
8199
8200
0
  time_t read_timeout_sec;
8201
0
  time_t read_timeout_usec;
8202
0
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
8203
0
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
8204
8205
0
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
8206
0
}
8207
8208
0
inline bool SocketStream::wait_writable() const {
8209
0
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
8210
0
         is_socket_alive(sock_);
8211
0
}
8212
8213
0
inline ssize_t SocketStream::read(char *ptr, size_t size) {
8214
#ifdef _WIN32
8215
  size =
8216
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
8217
#else
8218
0
  size = (std::min)(size,
8219
0
                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
8220
0
#endif
8221
8222
0
  if (read_buff_off_ < read_buff_content_size_) {
8223
0
    auto remaining_size = read_buff_content_size_ - read_buff_off_;
8224
0
    if (size <= remaining_size) {
8225
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, size);
8226
0
      read_buff_off_ += size;
8227
0
      return static_cast<ssize_t>(size);
8228
0
    } else {
8229
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
8230
0
      read_buff_off_ += remaining_size;
8231
0
      return static_cast<ssize_t>(remaining_size);
8232
0
    }
8233
0
  }
8234
8235
0
  if (!wait_readable()) {
8236
0
    error_ = Error::Timeout;
8237
0
    return -1;
8238
0
  }
8239
8240
0
  read_buff_off_ = 0;
8241
0
  read_buff_content_size_ = 0;
8242
8243
0
  if (size < read_buff_size_) {
8244
0
    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
8245
0
                         CPPHTTPLIB_RECV_FLAGS);
8246
0
    if (n <= 0) {
8247
0
      if (n == 0) {
8248
0
        error_ = Error::ConnectionClosed;
8249
0
      } else {
8250
0
        error_ = Error::Read;
8251
0
      }
8252
0
      return n;
8253
0
    } else if (n <= static_cast<ssize_t>(size)) {
8254
0
      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
8255
0
      return n;
8256
0
    } else {
8257
0
      memcpy(ptr, read_buff_.data(), size);
8258
0
      read_buff_off_ = size;
8259
0
      read_buff_content_size_ = static_cast<size_t>(n);
8260
0
      return static_cast<ssize_t>(size);
8261
0
    }
8262
0
  } else {
8263
0
    auto n = read_socket(sock_, ptr, size, 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
    }
8271
0
    return n;
8272
0
  }
8273
0
}
8274
8275
0
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
8276
0
  if (!wait_writable()) { return -1; }
8277
8278
#if defined(_WIN32) && !defined(_WIN64)
8279
  size =
8280
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
8281
#endif
8282
8283
0
  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
8284
0
}
8285
8286
inline void SocketStream::get_remote_ip_and_port(std::string &ip,
8287
0
                                                 int &port) const {
8288
0
  return detail::get_remote_ip_and_port(sock_, ip, port);
8289
0
}
8290
8291
inline void SocketStream::get_local_ip_and_port(std::string &ip,
8292
0
                                                int &port) const {
8293
0
  return detail::get_local_ip_and_port(sock_, ip, port);
8294
0
}
8295
8296
0
inline socket_t SocketStream::socket() const { return sock_; }
8297
8298
0
inline time_t SocketStream::duration() const {
8299
0
  return std::chrono::duration_cast<std::chrono::milliseconds>(
8300
0
             std::chrono::steady_clock::now() - start_time_)
8301
0
      .count();
8302
0
}
8303
8304
// Buffer stream implementation
8305
0
inline bool BufferStream::is_readable() const { return true; }
8306
8307
0
inline bool BufferStream::wait_readable() const { return true; }
8308
8309
0
inline bool BufferStream::wait_writable() const { return true; }
8310
8311
0
inline ssize_t BufferStream::read(char *ptr, size_t size) {
8312
#if defined(_MSC_VER) && _MSC_VER < 1910
8313
  auto len_read = buffer._Copy_s(ptr, size, size, position);
8314
#else
8315
0
  auto len_read = buffer.copy(ptr, size, position);
8316
0
#endif
8317
0
  position += static_cast<size_t>(len_read);
8318
0
  return static_cast<ssize_t>(len_read);
8319
0
}
8320
8321
26.3k
inline ssize_t BufferStream::write(const char *ptr, size_t size) {
8322
26.3k
  buffer.append(ptr, size);
8323
26.3k
  return static_cast<ssize_t>(size);
8324
26.3k
}
8325
8326
inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
8327
0
                                                 int & /*port*/) const {}
8328
8329
inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
8330
0
                                                int & /*port*/) const {}
8331
8332
0
inline socket_t BufferStream::socket() const { return 0; }
8333
8334
0
inline time_t BufferStream::duration() const { return 0; }
8335
8336
6.05k
inline const std::string &BufferStream::get_buffer() const { return buffer; }
8337
8338
inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
8339
0
    : MatcherBase(pattern) {
8340
0
  constexpr const char marker[] = "/:";
8341
8342
  // One past the last ending position of a path param substring
8343
0
  std::size_t last_param_end = 0;
8344
8345
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
8346
  // Needed to ensure that parameter names are unique during matcher
8347
  // construction
8348
  // If exceptions are disabled, only last duplicate path
8349
  // parameter will be set
8350
0
  std::unordered_set<std::string> param_name_set;
8351
0
#endif
8352
8353
0
  while (true) {
8354
0
    const auto marker_pos = pattern.find(
8355
0
        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
8356
0
    if (marker_pos == std::string::npos) { break; }
8357
8358
0
    static_fragments_.push_back(
8359
0
        pattern.substr(last_param_end, marker_pos - last_param_end + 1));
8360
8361
0
    const auto param_name_start = marker_pos + str_len(marker);
8362
8363
0
    auto sep_pos = pattern.find(separator, param_name_start);
8364
0
    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
8365
8366
0
    auto param_name =
8367
0
        pattern.substr(param_name_start, sep_pos - param_name_start);
8368
8369
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
8370
0
    if (param_name_set.find(param_name) != param_name_set.cend()) {
8371
0
      std::string msg = "Encountered path parameter '" + param_name +
8372
0
                        "' multiple times in route pattern '" + pattern + "'.";
8373
0
      throw std::invalid_argument(msg);
8374
0
    }
8375
0
#endif
8376
8377
0
    param_names_.push_back(std::move(param_name));
8378
8379
0
    last_param_end = sep_pos + 1;
8380
0
  }
8381
8382
0
  if (last_param_end < pattern.length()) {
8383
0
    static_fragments_.push_back(pattern.substr(last_param_end));
8384
0
  }
8385
0
}
8386
8387
0
inline bool PathParamsMatcher::match(Request &request) const {
8388
0
  request.matches = std::smatch();
8389
0
  request.path_params.clear();
8390
0
  request.path_params.reserve(param_names_.size());
8391
8392
  // One past the position at which the path matched the pattern last time
8393
0
  std::size_t starting_pos = 0;
8394
0
  for (size_t i = 0; i < static_fragments_.size(); ++i) {
8395
0
    const auto &fragment = static_fragments_[i];
8396
8397
0
    if (starting_pos + fragment.length() > request.path.length()) {
8398
0
      return false;
8399
0
    }
8400
8401
    // Avoid unnecessary allocation by using strncmp instead of substr +
8402
    // comparison
8403
0
    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
8404
0
                     fragment.length()) != 0) {
8405
0
      return false;
8406
0
    }
8407
8408
0
    starting_pos += fragment.length();
8409
8410
    // Should only happen when we have a static fragment after a param
8411
    // Example: '/users/:id/subscriptions'
8412
    // The 'subscriptions' fragment here does not have a corresponding param
8413
0
    if (i >= param_names_.size()) { continue; }
8414
8415
0
    auto sep_pos = request.path.find(separator, starting_pos);
8416
0
    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
8417
8418
0
    const auto &param_name = param_names_[i];
8419
8420
0
    request.path_params.emplace(
8421
0
        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
8422
8423
    // Mark everything up to '/' as matched
8424
0
    starting_pos = sep_pos + 1;
8425
0
  }
8426
  // Returns false if the path is longer than the pattern
8427
0
  return starting_pos >= request.path.length();
8428
0
}
8429
8430
1.81k
inline bool RegexMatcher::match(Request &request) const {
8431
1.81k
  request.path_params.clear();
8432
1.81k
  return std::regex_match(request.path, request.matches, regex_);
8433
1.81k
}
8434
8435
// Enclose IPv6 address in brackets if needed
8436
0
inline std::string prepare_host_string(const std::string &host) {
8437
0
  // Enclose IPv6 address in brackets (but not if already enclosed)
8438
0
  if (host.find(':') == std::string::npos ||
8439
0
      (!host.empty() && host[0] == '[')) {
8440
0
    // IPv4, hostname, or already bracketed IPv6
8441
0
    return host;
8442
0
  } else {
8443
0
    // IPv6 address without brackets
8444
0
    return "[" + host + "]";
8445
0
  }
8446
0
}
8447
8448
inline std::string make_host_and_port_string(const std::string &host, int port,
8449
0
                                             bool is_ssl) {
8450
0
  auto result = prepare_host_string(host);
8451
0
8452
0
  // Append port if not default
8453
0
  if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {
8454
0
    ; // do nothing
8455
0
  } else {
8456
0
    result += ":" + std::to_string(port);
8457
0
  }
8458
0
8459
0
  return result;
8460
0
}
8461
8462
// Create "host:port" string always including port number (for CONNECT method)
8463
inline std::string
8464
0
make_host_and_port_string_always_port(const std::string &host, int port) {
8465
0
  return prepare_host_string(host) + ":" + std::to_string(port);
8466
0
}
8467
8468
template <typename T>
8469
inline bool check_and_write_headers(Stream &strm, Headers &headers,
8470
0
                                    T header_writer, Error &error) {
8471
0
  for (const auto &h : headers) {
8472
0
    if (!detail::fields::is_field_name(h.first) ||
8473
0
        !detail::fields::is_field_value(h.second)) {
8474
0
      error = Error::InvalidHeaders;
8475
0
      return false;
8476
0
    }
8477
0
  }
8478
0
  if (header_writer(strm, headers) <= 0) {
8479
0
    error = Error::Write;
8480
0
    return false;
8481
0
  }
8482
0
  return true;
8483
0
}
8484
8485
} // namespace detail
8486
8487
// HTTP server implementation
8488
inline Server::Server()
8489
2
    : new_task_queue(
8490
2
          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
8491
2
#ifndef _WIN32
8492
2
  signal(SIGPIPE, SIG_IGN);
8493
2
#endif
8494
2
}
8495
8496
0
inline Server::~Server() = default;
8497
8498
inline std::unique_ptr<detail::MatcherBase>
8499
12
Server::make_matcher(const std::string &pattern) {
8500
12
  if (pattern.find("/:") != std::string::npos) {
8501
0
    return detail::make_unique<detail::PathParamsMatcher>(pattern);
8502
12
  } else {
8503
12
    return detail::make_unique<detail::RegexMatcher>(pattern);
8504
12
  }
8505
12
}
8506
8507
2
inline Server &Server::Get(const std::string &pattern, Handler handler) {
8508
2
  get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8509
2
  return *this;
8510
2
}
8511
8512
2
inline Server &Server::Post(const std::string &pattern, Handler handler) {
8513
2
  post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8514
2
  return *this;
8515
2
}
8516
8517
inline Server &Server::Post(const std::string &pattern,
8518
0
                            HandlerWithContentReader handler) {
8519
0
  post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8520
0
                                                 std::move(handler));
8521
0
  return *this;
8522
0
}
8523
8524
2
inline Server &Server::Put(const std::string &pattern, Handler handler) {
8525
2
  put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8526
2
  return *this;
8527
2
}
8528
8529
inline Server &Server::Put(const std::string &pattern,
8530
0
                           HandlerWithContentReader handler) {
8531
0
  put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8532
0
                                                std::move(handler));
8533
0
  return *this;
8534
0
}
8535
8536
2
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
8537
2
  patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8538
2
  return *this;
8539
2
}
8540
8541
inline Server &Server::Patch(const std::string &pattern,
8542
0
                             HandlerWithContentReader handler) {
8543
0
  patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8544
0
                                                  std::move(handler));
8545
0
  return *this;
8546
0
}
8547
8548
2
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
8549
2
  delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8550
2
  return *this;
8551
2
}
8552
8553
inline Server &Server::Delete(const std::string &pattern,
8554
0
                              HandlerWithContentReader handler) {
8555
0
  delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
8556
0
                                                   std::move(handler));
8557
0
  return *this;
8558
0
}
8559
8560
2
inline Server &Server::Options(const std::string &pattern, Handler handler) {
8561
2
  options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
8562
2
  return *this;
8563
2
}
8564
8565
inline bool Server::set_base_dir(const std::string &dir,
8566
0
                                 const std::string &mount_point) {
8567
0
  return set_mount_point(mount_point, dir);
8568
0
}
8569
8570
inline bool Server::set_mount_point(const std::string &mount_point,
8571
0
                                    const std::string &dir, Headers headers) {
8572
0
  detail::FileStat stat(dir);
8573
0
  if (stat.is_dir()) {
8574
0
    std::string mnt = !mount_point.empty() ? mount_point : "/";
8575
0
    if (!mnt.empty() && mnt[0] == '/') {
8576
0
      base_dirs_.push_back({std::move(mnt), dir, std::move(headers)});
8577
0
      return true;
8578
0
    }
8579
0
  }
8580
0
  return false;
8581
0
}
8582
8583
0
inline bool Server::remove_mount_point(const std::string &mount_point) {
8584
0
  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
8585
0
    if (it->mount_point == mount_point) {
8586
0
      base_dirs_.erase(it);
8587
0
      return true;
8588
0
    }
8589
0
  }
8590
0
  return false;
8591
0
}
8592
8593
inline Server &
8594
Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
8595
0
                                                const std::string &mime) {
8596
0
  file_extension_and_mimetype_map_[ext] = mime;
8597
0
  return *this;
8598
0
}
8599
8600
0
inline Server &Server::set_default_file_mimetype(const std::string &mime) {
8601
0
  default_file_mimetype_ = mime;
8602
0
  return *this;
8603
0
}
8604
8605
0
inline Server &Server::set_file_request_handler(Handler handler) {
8606
0
  file_request_handler_ = std::move(handler);
8607
0
  return *this;
8608
0
}
8609
8610
inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
8611
0
                                              std::true_type) {
8612
0
  error_handler_ = std::move(handler);
8613
0
  return *this;
8614
0
}
8615
8616
inline Server &Server::set_error_handler_core(Handler handler,
8617
0
                                              std::false_type) {
8618
0
  error_handler_ = [handler](const Request &req, Response &res) {
8619
0
    handler(req, res);
8620
0
    return HandlerResponse::Handled;
8621
0
  };
8622
0
  return *this;
8623
0
}
8624
8625
0
inline Server &Server::set_exception_handler(ExceptionHandler handler) {
8626
0
  exception_handler_ = std::move(handler);
8627
0
  return *this;
8628
0
}
8629
8630
0
inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
8631
0
  pre_routing_handler_ = std::move(handler);
8632
0
  return *this;
8633
0
}
8634
8635
0
inline Server &Server::set_post_routing_handler(Handler handler) {
8636
0
  post_routing_handler_ = std::move(handler);
8637
0
  return *this;
8638
0
}
8639
8640
0
inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
8641
0
  pre_request_handler_ = std::move(handler);
8642
0
  return *this;
8643
0
}
8644
8645
0
inline Server &Server::set_logger(Logger logger) {
8646
0
  logger_ = std::move(logger);
8647
0
  return *this;
8648
0
}
8649
8650
0
inline Server &Server::set_error_logger(ErrorLogger error_logger) {
8651
0
  error_logger_ = std::move(error_logger);
8652
0
  return *this;
8653
0
}
8654
8655
0
inline Server &Server::set_pre_compression_logger(Logger logger) {
8656
0
  pre_compression_logger_ = std::move(logger);
8657
0
  return *this;
8658
0
}
8659
8660
inline Server &
8661
0
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
8662
0
  expect_100_continue_handler_ = std::move(handler);
8663
0
  return *this;
8664
0
}
8665
8666
0
inline Server &Server::set_address_family(int family) {
8667
0
  address_family_ = family;
8668
0
  return *this;
8669
0
}
8670
8671
0
inline Server &Server::set_tcp_nodelay(bool on) {
8672
0
  tcp_nodelay_ = on;
8673
0
  return *this;
8674
0
}
8675
8676
0
inline Server &Server::set_ipv6_v6only(bool on) {
8677
0
  ipv6_v6only_ = on;
8678
0
  return *this;
8679
0
}
8680
8681
0
inline Server &Server::set_socket_options(SocketOptions socket_options) {
8682
0
  socket_options_ = std::move(socket_options);
8683
0
  return *this;
8684
0
}
8685
8686
0
inline Server &Server::set_default_headers(Headers headers) {
8687
0
  default_headers_ = std::move(headers);
8688
0
  return *this;
8689
0
}
8690
8691
inline Server &Server::set_header_writer(
8692
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
8693
0
  header_writer_ = writer;
8694
0
  return *this;
8695
0
}
8696
8697
inline Server &
8698
0
Server::set_trusted_proxies(const std::vector<std::string> &proxies) {
8699
0
  trusted_proxies_ = proxies;
8700
0
  return *this;
8701
0
}
8702
8703
0
inline Server &Server::set_keep_alive_max_count(size_t count) {
8704
0
  keep_alive_max_count_ = count;
8705
0
  return *this;
8706
0
}
8707
8708
0
inline Server &Server::set_keep_alive_timeout(time_t sec) {
8709
0
  keep_alive_timeout_sec_ = sec;
8710
0
  return *this;
8711
0
}
8712
8713
0
inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
8714
0
  read_timeout_sec_ = sec;
8715
0
  read_timeout_usec_ = usec;
8716
0
  return *this;
8717
0
}
8718
8719
0
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
8720
0
  write_timeout_sec_ = sec;
8721
0
  write_timeout_usec_ = usec;
8722
0
  return *this;
8723
0
}
8724
8725
0
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
8726
0
  idle_interval_sec_ = sec;
8727
0
  idle_interval_usec_ = usec;
8728
0
  return *this;
8729
0
}
8730
8731
0
inline Server &Server::set_payload_max_length(size_t length) {
8732
0
  payload_max_length_ = length;
8733
0
  return *this;
8734
0
}
8735
8736
inline bool Server::bind_to_port(const std::string &host, int port,
8737
0
                                 int socket_flags) {
8738
0
  auto ret = bind_internal(host, port, socket_flags);
8739
0
  if (ret == -1) { is_decommissioned = true; }
8740
0
  return ret >= 0;
8741
0
}
8742
0
inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
8743
0
  auto ret = bind_internal(host, 0, socket_flags);
8744
0
  if (ret == -1) { is_decommissioned = true; }
8745
0
  return ret;
8746
0
}
8747
8748
0
inline bool Server::listen_after_bind() { return listen_internal(); }
8749
8750
inline bool Server::listen(const std::string &host, int port,
8751
0
                           int socket_flags) {
8752
0
  return bind_to_port(host, port, socket_flags) && listen_internal();
8753
0
}
8754
8755
0
inline bool Server::is_running() const { return is_running_; }
8756
8757
0
inline void Server::wait_until_ready() const {
8758
0
  while (!is_running_ && !is_decommissioned) {
8759
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
8760
0
  }
8761
0
}
8762
8763
0
inline void Server::stop() {
8764
0
  if (is_running_) {
8765
0
    assert(svr_sock_ != INVALID_SOCKET);
8766
0
    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
8767
0
    detail::shutdown_socket(sock);
8768
0
    detail::close_socket(sock);
8769
0
  }
8770
0
  is_decommissioned = false;
8771
0
}
8772
8773
0
inline void Server::decommission() { is_decommissioned = true; }
8774
8775
6.05k
inline bool Server::parse_request_line(const char *s, Request &req) const {
8776
6.05k
  auto len = strlen(s);
8777
6.05k
  if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
8778
5.98k
  len -= 2;
8779
8780
5.98k
  {
8781
5.98k
    size_t count = 0;
8782
8783
32.2k
    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
8784
32.2k
      switch (count) {
8785
5.95k
      case 0: req.method = std::string(b, e); break;
8786
5.83k
      case 1: req.target = std::string(b, e); break;
8787
5.82k
      case 2: req.version = std::string(b, e); break;
8788
14.6k
      default: break;
8789
32.2k
      }
8790
32.2k
      count++;
8791
32.2k
    });
8792
8793
5.98k
    if (count != 3) { return false; }
8794
5.98k
  }
8795
8796
5.79k
  thread_local const std::set<std::string> methods{
8797
5.79k
      "GET",     "HEAD",    "POST",  "PUT",   "DELETE",
8798
5.79k
      "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
8799
8800
5.79k
  if (methods.find(req.method) == methods.end()) {
8801
45
    output_error_log(Error::InvalidHTTPMethod, &req);
8802
45
    return false;
8803
45
  }
8804
8805
5.75k
  if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
8806
42
    output_error_log(Error::InvalidHTTPVersion, &req);
8807
42
    return false;
8808
42
  }
8809
8810
5.71k
  {
8811
    // Skip URL fragment
8812
645k
    for (size_t i = 0; i < req.target.size(); i++) {
8813
640k
      if (req.target[i] == '#') {
8814
16
        req.target.erase(i);
8815
16
        break;
8816
16
      }
8817
640k
    }
8818
8819
5.71k
    detail::divide(req.target, '?',
8820
5.71k
                   [&](const char *lhs_data, std::size_t lhs_size,
8821
5.71k
                       const char *rhs_data, std::size_t rhs_size) {
8822
5.71k
                     req.path =
8823
5.71k
                         decode_path_component(std::string(lhs_data, lhs_size));
8824
5.71k
                     detail::parse_query_text(rhs_data, rhs_size, req.params);
8825
5.71k
                   });
8826
5.71k
  }
8827
8828
5.71k
  return true;
8829
5.75k
}
8830
8831
inline bool Server::write_response(Stream &strm, bool close_connection,
8832
4.61k
                                   Request &req, Response &res) {
8833
  // NOTE: `req.ranges` should be empty, otherwise it will be applied
8834
  // incorrectly to the error content.
8835
4.61k
  req.ranges.clear();
8836
4.61k
  return write_response_core(strm, close_connection, req, res, false);
8837
4.61k
}
8838
8839
inline bool Server::write_response_with_content(Stream &strm,
8840
                                                bool close_connection,
8841
                                                const Request &req,
8842
1.44k
                                                Response &res) {
8843
1.44k
  return write_response_core(strm, close_connection, req, res, true);
8844
1.44k
}
8845
8846
inline bool Server::write_response_core(Stream &strm, bool close_connection,
8847
                                        const Request &req, Response &res,
8848
6.05k
                                        bool need_apply_ranges) {
8849
6.05k
  assert(res.status != -1);
8850
8851
6.05k
  if (400 <= res.status && error_handler_ &&
8852
0
      error_handler_(req, res) == HandlerResponse::Handled) {
8853
0
    need_apply_ranges = true;
8854
0
  }
8855
8856
6.05k
  std::string content_type;
8857
6.05k
  std::string boundary;
8858
6.05k
  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
8859
8860
  // Prepare additional headers
8861
6.05k
  if (close_connection || req.get_header_value("Connection") == "close" ||
8862
6.05k
      400 <= res.status) { // Don't leave connections open after errors
8863
4.61k
    res.set_header("Connection", "close");
8864
4.61k
  } else {
8865
1.44k
    std::string s = "timeout=";
8866
1.44k
    s += std::to_string(keep_alive_timeout_sec_);
8867
1.44k
    s += ", max=";
8868
1.44k
    s += std::to_string(keep_alive_max_count_);
8869
1.44k
    res.set_header("Keep-Alive", s);
8870
1.44k
  }
8871
8872
6.05k
  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
8873
1.44k
      !res.has_header("Content-Type")) {
8874
0
    res.set_header("Content-Type", "text/plain");
8875
0
  }
8876
8877
6.05k
  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
8878
4.61k
      !res.has_header("Content-Length")) {
8879
4.61k
    res.set_header("Content-Length", "0");
8880
4.61k
  }
8881
8882
6.05k
  if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
8883
159
    res.set_header("Accept-Ranges", "bytes");
8884
159
  }
8885
8886
6.05k
  if (post_routing_handler_) { post_routing_handler_(req, res); }
8887
8888
  // Response line and headers
8889
6.05k
  {
8890
6.05k
    detail::BufferStream bstrm;
8891
6.05k
    if (!detail::write_response_line(bstrm, res.status)) { return false; }
8892
6.05k
    if (header_writer_(bstrm, res.headers) <= 0) { return false; }
8893
8894
    // Flush buffer
8895
6.05k
    auto &data = bstrm.get_buffer();
8896
6.05k
    detail::write_data(strm, data.data(), data.size());
8897
6.05k
  }
8898
8899
  // Body
8900
0
  auto ret = true;
8901
6.05k
  if (req.method != "HEAD") {
8902
5.89k
    if (!res.body.empty()) {
8903
1.37k
      if (!detail::write_data(strm, res.body.data(), res.body.size())) {
8904
0
        ret = false;
8905
0
      }
8906
4.52k
    } else if (res.content_provider_) {
8907
0
      if (write_content_with_provider(strm, req, res, boundary, content_type)) {
8908
0
        res.content_provider_success_ = true;
8909
0
      } else {
8910
0
        ret = false;
8911
0
      }
8912
0
    }
8913
5.89k
  }
8914
8915
  // Log
8916
6.05k
  output_log(req, res);
8917
8918
6.05k
  return ret;
8919
6.05k
}
8920
8921
inline bool
8922
Server::write_content_with_provider(Stream &strm, const Request &req,
8923
                                    Response &res, const std::string &boundary,
8924
0
                                    const std::string &content_type) {
8925
0
  auto is_shutting_down = [this]() {
8926
0
    return this->svr_sock_ == INVALID_SOCKET;
8927
0
  };
8928
8929
0
  if (res.content_length_ > 0) {
8930
0
    if (req.ranges.empty()) {
8931
0
      return detail::write_content(strm, res.content_provider_, 0,
8932
0
                                   res.content_length_, is_shutting_down);
8933
0
    } else if (req.ranges.size() == 1) {
8934
0
      auto offset_and_length = detail::get_range_offset_and_length(
8935
0
          req.ranges[0], res.content_length_);
8936
8937
0
      return detail::write_content(strm, res.content_provider_,
8938
0
                                   offset_and_length.first,
8939
0
                                   offset_and_length.second, is_shutting_down);
8940
0
    } else {
8941
0
      return detail::write_multipart_ranges_data(
8942
0
          strm, req, res, boundary, content_type, res.content_length_,
8943
0
          is_shutting_down);
8944
0
    }
8945
0
  } else {
8946
0
    if (res.is_chunked_content_provider_) {
8947
0
      auto type = detail::encoding_type(req, res);
8948
8949
0
      std::unique_ptr<detail::compressor> compressor;
8950
0
      if (type == detail::EncodingType::Gzip) {
8951
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8952
0
        compressor = detail::make_unique<detail::gzip_compressor>();
8953
0
#endif
8954
0
      } else if (type == detail::EncodingType::Brotli) {
8955
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
8956
        compressor = detail::make_unique<detail::brotli_compressor>();
8957
#endif
8958
0
      } else if (type == detail::EncodingType::Zstd) {
8959
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
8960
        compressor = detail::make_unique<detail::zstd_compressor>();
8961
#endif
8962
0
      } else {
8963
0
        compressor = detail::make_unique<detail::nocompressor>();
8964
0
      }
8965
0
      assert(compressor != nullptr);
8966
8967
0
      return detail::write_content_chunked(strm, res.content_provider_,
8968
0
                                           is_shutting_down, *compressor);
8969
0
    } else {
8970
0
      return detail::write_content_without_length(strm, res.content_provider_,
8971
0
                                                  is_shutting_down);
8972
0
    }
8973
0
  }
8974
0
}
8975
8976
3.36k
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
8977
3.36k
  FormFields::iterator cur_field;
8978
3.36k
  FormFiles::iterator cur_file;
8979
3.36k
  auto is_text_field = false;
8980
3.36k
  size_t count = 0;
8981
3.36k
  if (read_content_core(
8982
3.36k
          strm, req, res,
8983
          // Regular
8984
23.6k
          [&](const char *buf, size_t n) {
8985
            // Limit decompressed body size to payload_max_length_ to protect
8986
            // against "zip bomb" attacks where a small compressed payload
8987
            // decompresses to a massive size.
8988
23.6k
            if (req.body.size() + n > payload_max_length_ ||
8989
23.6k
                req.body.size() + n > req.body.max_size()) {
8990
0
              return false;
8991
0
            }
8992
23.6k
            req.body.append(buf, n);
8993
23.6k
            return true;
8994
23.6k
          },
8995
          // Multipart FormData
8996
25.2k
          [&](const FormData &file) {
8997
25.2k
            if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
8998
1
              output_error_log(Error::TooManyFormDataFiles, &req);
8999
1
              return false;
9000
1
            }
9001
9002
25.2k
            if (file.filename.empty()) {
9003
21.3k
              cur_field = req.form.fields.emplace(
9004
21.3k
                  file.name, FormField{file.name, file.content, file.headers});
9005
21.3k
              is_text_field = true;
9006
21.3k
            } else {
9007
3.90k
              cur_file = req.form.files.emplace(file.name, file);
9008
3.90k
              is_text_field = false;
9009
3.90k
            }
9010
25.2k
            return true;
9011
25.2k
          },
9012
67.5k
          [&](const char *buf, size_t n) {
9013
67.5k
            if (is_text_field) {
9014
42.7k
              auto &content = cur_field->second.content;
9015
42.7k
              if (content.size() + n > content.max_size()) { return false; }
9016
42.7k
              content.append(buf, n);
9017
42.7k
            } else {
9018
24.7k
              auto &content = cur_file->second.content;
9019
24.7k
              if (content.size() + n > content.max_size()) { return false; }
9020
24.7k
              content.append(buf, n);
9021
24.7k
            }
9022
67.5k
            return true;
9023
67.5k
          })) {
9024
1.11k
    const auto &content_type = req.get_header_value("Content-Type");
9025
1.11k
    if (!content_type.find("application/x-www-form-urlencoded")) {
9026
34
      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
9027
1
        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
9028
1
        output_error_log(Error::ExceedMaxPayloadSize, &req);
9029
1
        return false;
9030
1
      }
9031
33
      detail::parse_query_text(req.body, req.params);
9032
33
    }
9033
1.11k
    return true;
9034
1.11k
  }
9035
2.24k
  return false;
9036
3.36k
}
9037
9038
inline bool Server::read_content_with_content_receiver(
9039
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
9040
0
    FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
9041
0
  return read_content_core(strm, req, res, std::move(receiver),
9042
0
                           std::move(multipart_header),
9043
0
                           std::move(multipart_receiver));
9044
0
}
9045
9046
inline bool Server::read_content_core(
9047
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
9048
3.36k
    FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {
9049
3.36k
  detail::FormDataParser multipart_form_data_parser;
9050
3.36k
  ContentReceiverWithProgress out;
9051
9052
3.36k
  if (req.is_multipart_form_data()) {
9053
1.17k
    const auto &content_type = req.get_header_value("Content-Type");
9054
1.17k
    std::string boundary;
9055
1.17k
    if (!detail::parse_multipart_boundary(content_type, boundary)) {
9056
5
      res.status = StatusCode::BadRequest_400;
9057
5
      output_error_log(Error::MultipartParsing, &req);
9058
5
      return false;
9059
5
    }
9060
9061
1.17k
    multipart_form_data_parser.set_boundary(std::move(boundary));
9062
65.1k
    out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {
9063
65.1k
      return multipart_form_data_parser.parse(buf, n, multipart_header,
9064
65.1k
                                              multipart_receiver);
9065
65.1k
    };
9066
2.18k
  } else {
9067
2.18k
    out = [receiver](const char *buf, size_t n, size_t /*off*/,
9068
23.6k
                     size_t /*len*/) { return receiver(buf, n); };
9069
2.18k
  }
9070
9071
  // RFC 7230 Section 3.3.3: If this is a request message and none of the above
9072
  // are true (no Transfer-Encoding and no Content-Length), then the message
9073
  // body length is zero (no message body is present).
9074
  //
9075
  // For non-SSL builds, peek into the socket to detect clients that send a
9076
  // body without a Content-Length header (raw HTTP over TCP). If there is
9077
  // pending data that exceeds the configured payload limit, treat this as an
9078
  // oversized request and fail early (causing connection close). For SSL
9079
  // builds we cannot reliably peek the decrypted application bytes, so keep
9080
  // the original behaviour.
9081
3.35k
#if !defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(_WIN32)
9082
3.35k
  if (!req.has_header("Content-Length") &&
9083
1.93k
      !detail::is_chunked_transfer_encoding(req.headers)) {
9084
1.01k
    socket_t s = strm.socket();
9085
1.01k
    if (s != INVALID_SOCKET) {
9086
      // Peek up to payload_max_length_ + 1 bytes. If more than
9087
      // payload_max_length_ bytes are pending, reject the request.
9088
1.01k
      size_t to_peek =
9089
1.01k
          (payload_max_length_ > 0)
9090
1.01k
              ? (std::min)(payload_max_length_ + 1, static_cast<size_t>(4096))
9091
1.01k
              : 1;
9092
1.01k
      std::vector<char> peekbuf(to_peek);
9093
1.01k
      ssize_t n = ::recv(s, peekbuf.data(), to_peek, MSG_PEEK);
9094
1.01k
      if (n > 0 && static_cast<size_t>(n) > payload_max_length_) {
9095
        // Indicate failure so connection will be closed.
9096
0
        return false;
9097
0
      }
9098
1.01k
    }
9099
1.01k
    return true;
9100
1.01k
  }
9101
#else
9102
  if (!req.has_header("Content-Length") &&
9103
      !detail::is_chunked_transfer_encoding(req.headers)) {
9104
    return true;
9105
  }
9106
#endif
9107
9108
2.33k
  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
9109
2.33k
                            out, true)) {
9110
2.22k
    return false;
9111
2.22k
  }
9112
9113
115
  if (req.is_multipart_form_data()) {
9114
22
    if (!multipart_form_data_parser.is_valid()) {
9115
16
      res.status = StatusCode::BadRequest_400;
9116
16
      output_error_log(Error::MultipartParsing, &req);
9117
16
      return false;
9118
16
    }
9119
22
  }
9120
9121
99
  return true;
9122
115
}
9123
9124
1.34k
inline bool Server::handle_file_request(Request &req, Response &res) {
9125
1.34k
  for (const auto &entry : base_dirs_) {
9126
    // Prefix match
9127
0
    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
9128
0
      std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
9129
0
      if (detail::is_valid_path(sub_path)) {
9130
0
        auto path = entry.base_dir + sub_path;
9131
0
        if (path.back() == '/') { path += "index.html"; }
9132
9133
0
        detail::FileStat stat(path);
9134
9135
0
        if (stat.is_dir()) {
9136
0
          res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
9137
0
          return true;
9138
0
        }
9139
9140
0
        if (stat.is_file()) {
9141
0
          for (const auto &kv : entry.headers) {
9142
0
            res.set_header(kv.first, kv.second);
9143
0
          }
9144
9145
0
          auto etag = detail::compute_etag(stat);
9146
0
          if (!etag.empty()) { res.set_header("ETag", etag); }
9147
9148
0
          auto mtime = stat.mtime();
9149
9150
0
          auto last_modified = detail::file_mtime_to_http_date(mtime);
9151
0
          if (!last_modified.empty()) {
9152
0
            res.set_header("Last-Modified", last_modified);
9153
0
          }
9154
9155
0
          if (check_if_not_modified(req, res, etag, mtime)) { return true; }
9156
9157
0
          check_if_range(req, etag, mtime);
9158
9159
0
          auto mm = std::make_shared<detail::mmap>(path.c_str());
9160
0
          if (!mm->is_open()) {
9161
0
            output_error_log(Error::OpenFile, &req);
9162
0
            return false;
9163
0
          }
9164
9165
0
          res.set_content_provider(
9166
0
              mm->size(),
9167
0
              detail::find_content_type(path, file_extension_and_mimetype_map_,
9168
0
                                        default_file_mimetype_),
9169
0
              [mm](size_t offset, size_t length, DataSink &sink) -> bool {
9170
0
                sink.write(mm->data() + offset, length);
9171
0
                return true;
9172
0
              });
9173
9174
0
          if (req.method != "HEAD" && file_request_handler_) {
9175
0
            file_request_handler_(req, res);
9176
0
          }
9177
9178
0
          return true;
9179
0
        } else {
9180
0
          output_error_log(Error::OpenFile, &req);
9181
0
        }
9182
0
      }
9183
0
    }
9184
0
  }
9185
1.34k
  return false;
9186
1.34k
}
9187
9188
inline bool Server::check_if_not_modified(const Request &req, Response &res,
9189
                                          const std::string &etag,
9190
0
                                          time_t mtime) const {
9191
  // Handle conditional GET:
9192
  // 1. If-None-Match takes precedence (RFC 9110 Section 13.1.2)
9193
  // 2. If-Modified-Since is checked only when If-None-Match is absent
9194
0
  if (req.has_header("If-None-Match")) {
9195
0
    if (!etag.empty()) {
9196
0
      auto val = req.get_header_value("If-None-Match");
9197
9198
      // NOTE: We use exact string matching here. This works correctly
9199
      // because our server always generates weak ETags (W/"..."), and
9200
      // clients typically send back the same ETag they received.
9201
      // RFC 9110 Section 8.8.3.2 allows weak comparison for
9202
      // If-None-Match, where W/"x" and "x" would match, but this
9203
      // simplified implementation requires exact matches.
9204
0
      auto ret = detail::split_find(val.data(), val.data() + val.size(), ',',
9205
0
                                    [&](const char *b, const char *e) {
9206
0
                                      return std::equal(b, e, "*") ||
9207
0
                                             std::equal(b, e, etag.begin());
9208
0
                                    });
9209
9210
0
      if (ret) {
9211
0
        res.status = StatusCode::NotModified_304;
9212
0
        return true;
9213
0
      }
9214
0
    }
9215
0
  } else if (req.has_header("If-Modified-Since")) {
9216
0
    auto val = req.get_header_value("If-Modified-Since");
9217
0
    auto t = detail::parse_http_date(val);
9218
9219
0
    if (t != static_cast<time_t>(-1) && mtime <= t) {
9220
0
      res.status = StatusCode::NotModified_304;
9221
0
      return true;
9222
0
    }
9223
0
  }
9224
0
  return false;
9225
0
}
9226
9227
inline bool Server::check_if_range(Request &req, const std::string &etag,
9228
0
                                   time_t mtime) const {
9229
  // Handle If-Range for partial content requests (RFC 9110
9230
  // Section 13.1.5). If-Range is only evaluated when Range header is
9231
  // present. If the validator matches, serve partial content; otherwise
9232
  // serve full content.
9233
0
  if (!req.ranges.empty() && req.has_header("If-Range")) {
9234
0
    auto val = req.get_header_value("If-Range");
9235
9236
0
    auto is_valid_range = [&]() {
9237
0
      if (detail::is_strong_etag(val)) {
9238
        // RFC 9110 Section 13.1.5: If-Range requires strong ETag
9239
        // comparison.
9240
0
        return (!etag.empty() && val == etag);
9241
0
      } else if (detail::is_weak_etag(val)) {
9242
        // Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
9243
0
        return false;
9244
0
      } else {
9245
        // HTTP-date comparison
9246
0
        auto t = detail::parse_http_date(val);
9247
0
        return (t != static_cast<time_t>(-1) && mtime <= t);
9248
0
      }
9249
0
    };
9250
9251
0
    if (!is_valid_range()) {
9252
      // Validator doesn't match: ignore Range and serve full content
9253
0
      req.ranges.clear();
9254
0
      return false;
9255
0
    }
9256
0
  }
9257
9258
0
  return true;
9259
0
}
9260
9261
inline socket_t
9262
Server::create_server_socket(const std::string &host, int port,
9263
                             int socket_flags,
9264
0
                             SocketOptions socket_options) const {
9265
0
  return detail::create_socket(
9266
0
      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
9267
0
      ipv6_v6only_, std::move(socket_options),
9268
0
      [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
9269
0
        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
9270
0
          output_error_log(Error::BindIPAddress, nullptr);
9271
0
          return false;
9272
0
        }
9273
0
        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
9274
0
          output_error_log(Error::Listen, nullptr);
9275
0
          return false;
9276
0
        }
9277
0
        return true;
9278
0
      });
9279
0
}
9280
9281
inline int Server::bind_internal(const std::string &host, int port,
9282
0
                                 int socket_flags) {
9283
0
  if (is_decommissioned) { return -1; }
9284
0
9285
0
  if (!is_valid()) { return -1; }
9286
0
9287
0
  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
9288
0
  if (svr_sock_ == INVALID_SOCKET) { return -1; }
9289
0
9290
0
  if (port == 0) {
9291
0
    struct sockaddr_storage addr;
9292
0
    socklen_t addr_len = sizeof(addr);
9293
0
    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
9294
0
                    &addr_len) == -1) {
9295
0
      output_error_log(Error::GetSockName, nullptr);
9296
0
      return -1;
9297
0
    }
9298
0
    if (addr.ss_family == AF_INET) {
9299
0
      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
9300
0
    } else if (addr.ss_family == AF_INET6) {
9301
0
      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
9302
0
    } else {
9303
0
      output_error_log(Error::UnsupportedAddressFamily, nullptr);
9304
0
      return -1;
9305
0
    }
9306
0
  } else {
9307
0
    return port;
9308
0
  }
9309
0
}
9310
9311
0
inline bool Server::listen_internal() {
9312
0
  if (is_decommissioned) { return false; }
9313
0
9314
0
  auto ret = true;
9315
0
  is_running_ = true;
9316
0
  auto se = detail::scope_exit([&]() { is_running_ = false; });
9317
0
9318
0
  {
9319
0
    std::unique_ptr<TaskQueue> task_queue(new_task_queue());
9320
0
9321
0
    while (svr_sock_ != INVALID_SOCKET) {
9322
0
#ifndef _WIN32
9323
0
      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
9324
0
#endif
9325
0
        auto val = detail::select_read(svr_sock_, idle_interval_sec_,
9326
0
                                       idle_interval_usec_);
9327
0
        if (val == 0) { // Timeout
9328
0
          task_queue->on_idle();
9329
0
          continue;
9330
0
        }
9331
0
#ifndef _WIN32
9332
0
      }
9333
0
#endif
9334
0
9335
0
#if defined _WIN32
9336
0
      // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
9337
0
      // OVERLAPPED
9338
0
      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
9339
0
#elif defined SOCK_CLOEXEC
9340
0
      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
9341
0
#else
9342
0
      socket_t sock = accept(svr_sock_, nullptr, nullptr);
9343
0
#endif
9344
0
9345
0
      if (sock == INVALID_SOCKET) {
9346
0
        if (errno == EMFILE) {
9347
0
          // The per-process limit of open file descriptors has been reached.
9348
0
          // Try to accept new connections after a short sleep.
9349
0
          std::this_thread::sleep_for(std::chrono::microseconds{1});
9350
0
          continue;
9351
0
        } else if (errno == EINTR || errno == EAGAIN) {
9352
0
          continue;
9353
0
        }
9354
0
        if (svr_sock_ != INVALID_SOCKET) {
9355
0
          detail::close_socket(svr_sock_);
9356
0
          ret = false;
9357
0
          output_error_log(Error::Connection, nullptr);
9358
0
        } else {
9359
0
          ; // The server socket was closed by user.
9360
0
        }
9361
0
        break;
9362
0
      }
9363
0
9364
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
9365
0
                                  read_timeout_sec_, read_timeout_usec_);
9366
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
9367
0
                                  write_timeout_sec_, write_timeout_usec_);
9368
0
9369
0
      if (!task_queue->enqueue(
9370
0
              [this, sock]() { process_and_close_socket(sock); })) {
9371
0
        output_error_log(Error::ResourceExhaustion, nullptr);
9372
0
        detail::shutdown_socket(sock);
9373
0
        detail::close_socket(sock);
9374
0
      }
9375
0
    }
9376
0
9377
0
    task_queue->shutdown();
9378
0
  }
9379
0
9380
0
  is_decommissioned = !ret;
9381
0
  return ret;
9382
0
}
9383
9384
4.13k
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
9385
4.13k
  if (pre_routing_handler_ &&
9386
0
      pre_routing_handler_(req, res) == HandlerResponse::Handled) {
9387
0
    return true;
9388
0
  }
9389
9390
  // File handler
9391
4.13k
  if ((req.method == "GET" || req.method == "HEAD") &&
9392
1.34k
      handle_file_request(req, res)) {
9393
0
    return true;
9394
0
  }
9395
9396
4.13k
  if (detail::expect_content(req)) {
9397
    // Content reader handler
9398
3.36k
    {
9399
3.36k
      ContentReader reader(
9400
3.36k
          [&](ContentReceiver receiver) {
9401
0
            auto result = read_content_with_content_receiver(
9402
0
                strm, req, res, std::move(receiver), nullptr, nullptr);
9403
0
            if (!result) { output_error_log(Error::Read, &req); }
9404
0
            return result;
9405
0
          },
9406
3.36k
          [&](FormDataHeader header, ContentReceiver receiver) {
9407
0
            auto result = read_content_with_content_receiver(
9408
0
                strm, req, res, nullptr, std::move(header),
9409
0
                std::move(receiver));
9410
0
            if (!result) { output_error_log(Error::Read, &req); }
9411
0
            return result;
9412
0
          });
9413
9414
3.36k
      if (req.method == "POST") {
9415
5
        if (dispatch_request_for_content_reader(
9416
5
                req, res, std::move(reader),
9417
5
                post_handlers_for_content_reader_)) {
9418
0
          return true;
9419
0
        }
9420
3.35k
      } else if (req.method == "PUT") {
9421
2.49k
        if (dispatch_request_for_content_reader(
9422
2.49k
                req, res, std::move(reader),
9423
2.49k
                put_handlers_for_content_reader_)) {
9424
0
          return true;
9425
0
        }
9426
2.49k
      } else if (req.method == "PATCH") {
9427
1
        if (dispatch_request_for_content_reader(
9428
1
                req, res, std::move(reader),
9429
1
                patch_handlers_for_content_reader_)) {
9430
0
          return true;
9431
0
        }
9432
861
      } else if (req.method == "DELETE") {
9433
1
        if (dispatch_request_for_content_reader(
9434
1
                req, res, std::move(reader),
9435
1
                delete_handlers_for_content_reader_)) {
9436
0
          return true;
9437
0
        }
9438
1
      }
9439
3.36k
    }
9440
9441
    // Read content into `req.body`
9442
3.36k
    if (!read_content(strm, req, res)) {
9443
2.24k
      output_error_log(Error::Read, &req);
9444
2.24k
      return false;
9445
2.24k
    }
9446
3.36k
  }
9447
9448
  // Regular handler
9449
1.88k
  if (req.method == "GET" || req.method == "HEAD") {
9450
738
    return dispatch_request(req, res, get_handlers_);
9451
1.15k
  } else if (req.method == "POST") {
9452
5
    return dispatch_request(req, res, post_handlers_);
9453
1.14k
  } else if (req.method == "PUT") {
9454
1.07k
    return dispatch_request(req, res, put_handlers_);
9455
1.07k
  } else if (req.method == "DELETE") {
9456
1
    return dispatch_request(req, res, delete_handlers_);
9457
72
  } else if (req.method == "OPTIONS") {
9458
1
    return dispatch_request(req, res, options_handlers_);
9459
71
  } else if (req.method == "PATCH") {
9460
1
    return dispatch_request(req, res, patch_handlers_);
9461
1
  }
9462
9463
70
  res.status = StatusCode::BadRequest_400;
9464
70
  return false;
9465
1.88k
}
9466
9467
inline bool Server::dispatch_request(Request &req, Response &res,
9468
1.81k
                                     const Handlers &handlers) const {
9469
1.81k
  for (const auto &x : handlers) {
9470
1.81k
    const auto &matcher = x.first;
9471
1.81k
    const auto &handler = x.second;
9472
9473
1.81k
    if (matcher->match(req)) {
9474
1.75k
      req.matched_route = matcher->pattern();
9475
1.75k
      if (!pre_request_handler_ ||
9476
1.75k
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
9477
1.75k
        handler(req, res);
9478
1.75k
      }
9479
1.75k
      return true;
9480
1.75k
    }
9481
1.81k
  }
9482
61
  return false;
9483
1.81k
}
9484
9485
inline void Server::apply_ranges(const Request &req, Response &res,
9486
                                 std::string &content_type,
9487
1.44k
                                 std::string &boundary) const {
9488
1.44k
  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
9489
240
    auto it = res.headers.find("Content-Type");
9490
240
    if (it != res.headers.end()) {
9491
240
      content_type = it->second;
9492
240
      res.headers.erase(it);
9493
240
    }
9494
9495
240
    boundary = detail::make_multipart_data_boundary();
9496
9497
240
    res.set_header("Content-Type",
9498
240
                   "multipart/byteranges; boundary=" + boundary);
9499
240
  }
9500
9501
1.44k
  auto type = detail::encoding_type(req, res);
9502
9503
1.44k
  if (res.body.empty()) {
9504
0
    if (res.content_length_ > 0) {
9505
0
      size_t length = 0;
9506
0
      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
9507
0
        length = res.content_length_;
9508
0
      } else if (req.ranges.size() == 1) {
9509
0
        auto offset_and_length = detail::get_range_offset_and_length(
9510
0
            req.ranges[0], res.content_length_);
9511
9512
0
        length = offset_and_length.second;
9513
9514
0
        auto content_range = detail::make_content_range_header_field(
9515
0
            offset_and_length, res.content_length_);
9516
0
        res.set_header("Content-Range", content_range);
9517
0
      } else {
9518
0
        length = detail::get_multipart_ranges_data_length(
9519
0
            req, boundary, content_type, res.content_length_);
9520
0
      }
9521
0
      res.set_header("Content-Length", std::to_string(length));
9522
0
    } else {
9523
0
      if (res.content_provider_) {
9524
0
        if (res.is_chunked_content_provider_) {
9525
0
          res.set_header("Transfer-Encoding", "chunked");
9526
0
          if (type == detail::EncodingType::Gzip) {
9527
0
            res.set_header("Content-Encoding", "gzip");
9528
0
            res.set_header("Vary", "Accept-Encoding");
9529
0
          } else if (type == detail::EncodingType::Brotli) {
9530
0
            res.set_header("Content-Encoding", "br");
9531
0
            res.set_header("Vary", "Accept-Encoding");
9532
0
          } else if (type == detail::EncodingType::Zstd) {
9533
0
            res.set_header("Content-Encoding", "zstd");
9534
0
            res.set_header("Vary", "Accept-Encoding");
9535
0
          }
9536
0
        }
9537
0
      }
9538
0
    }
9539
1.44k
  } else {
9540
1.44k
    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
9541
1.01k
      ;
9542
1.01k
    } else if (req.ranges.size() == 1) {
9543
187
      auto offset_and_length =
9544
187
          detail::get_range_offset_and_length(req.ranges[0], res.body.size());
9545
187
      auto offset = offset_and_length.first;
9546
187
      auto length = offset_and_length.second;
9547
9548
187
      auto content_range = detail::make_content_range_header_field(
9549
187
          offset_and_length, res.body.size());
9550
187
      res.set_header("Content-Range", content_range);
9551
9552
187
      assert(offset + length <= res.body.size());
9553
187
      res.body = res.body.substr(offset, length);
9554
240
    } else {
9555
240
      std::string data;
9556
240
      detail::make_multipart_ranges_data(req, res, boundary, content_type,
9557
240
                                         res.body.size(), data);
9558
240
      res.body.swap(data);
9559
240
    }
9560
9561
1.44k
    if (type != detail::EncodingType::None) {
9562
18
      output_pre_compression_log(req, res);
9563
9564
18
      std::unique_ptr<detail::compressor> compressor;
9565
18
      std::string content_encoding;
9566
9567
18
      if (type == detail::EncodingType::Gzip) {
9568
18
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
9569
18
        compressor = detail::make_unique<detail::gzip_compressor>();
9570
18
        content_encoding = "gzip";
9571
18
#endif
9572
18
      } else if (type == detail::EncodingType::Brotli) {
9573
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
9574
        compressor = detail::make_unique<detail::brotli_compressor>();
9575
        content_encoding = "br";
9576
#endif
9577
0
      } else if (type == detail::EncodingType::Zstd) {
9578
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
9579
        compressor = detail::make_unique<detail::zstd_compressor>();
9580
        content_encoding = "zstd";
9581
#endif
9582
0
      }
9583
9584
18
      if (compressor) {
9585
18
        std::string compressed;
9586
18
        if (compressor->compress(res.body.data(), res.body.size(), true,
9587
18
                                 [&](const char *data, size_t data_len) {
9588
18
                                   compressed.append(data, data_len);
9589
18
                                   return true;
9590
18
                                 })) {
9591
18
          res.body.swap(compressed);
9592
18
          res.set_header("Content-Encoding", content_encoding);
9593
18
          res.set_header("Vary", "Accept-Encoding");
9594
18
        }
9595
18
      }
9596
18
    }
9597
9598
1.44k
    auto length = std::to_string(res.body.size());
9599
1.44k
    res.set_header("Content-Length", length);
9600
1.44k
  }
9601
1.44k
}
9602
9603
inline bool Server::dispatch_request_for_content_reader(
9604
    Request &req, Response &res, ContentReader content_reader,
9605
2.50k
    const HandlersForContentReader &handlers) const {
9606
2.50k
  for (const auto &x : handlers) {
9607
0
    const auto &matcher = x.first;
9608
0
    const auto &handler = x.second;
9609
9610
0
    if (matcher->match(req)) {
9611
0
      req.matched_route = matcher->pattern();
9612
0
      if (!pre_request_handler_ ||
9613
0
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
9614
0
        handler(req, res, content_reader);
9615
0
      }
9616
0
      return true;
9617
0
    }
9618
0
  }
9619
2.50k
  return false;
9620
2.50k
}
9621
9622
inline std::string
9623
get_client_ip(const std::string &x_forwarded_for,
9624
0
              const std::vector<std::string> &trusted_proxies) {
9625
  // X-Forwarded-For is a comma-separated list per RFC 7239
9626
0
  std::vector<std::string> ip_list;
9627
0
  detail::split(x_forwarded_for.data(),
9628
0
                x_forwarded_for.data() + x_forwarded_for.size(), ',',
9629
0
                [&](const char *b, const char *e) {
9630
0
                  auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
9631
0
                  ip_list.emplace_back(std::string(b + r.first, b + r.second));
9632
0
                });
9633
9634
0
  for (size_t i = 0; i < ip_list.size(); ++i) {
9635
0
    auto ip = ip_list[i];
9636
9637
0
    auto is_trusted_proxy =
9638
0
        std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
9639
0
                    [&](const std::string &proxy) { return ip == proxy; });
9640
9641
0
    if (is_trusted_proxy) {
9642
0
      if (i == 0) {
9643
        // If the trusted proxy is the first IP, there's no preceding client IP
9644
0
        return ip;
9645
0
      } else {
9646
        // Return the IP immediately before the trusted proxy
9647
0
        return ip_list[i - 1];
9648
0
      }
9649
0
    }
9650
0
  }
9651
9652
  // If no trusted proxy is found, return the first IP in the list
9653
0
  return ip_list.front();
9654
0
}
9655
9656
inline bool
9657
Server::process_request(Stream &strm, const std::string &remote_addr,
9658
                        int remote_port, const std::string &local_addr,
9659
                        int local_port, bool close_connection,
9660
                        bool &connection_closed,
9661
6.05k
                        const std::function<void(Request &)> &setup_request) {
9662
6.05k
  std::array<char, 2048> buf{};
9663
9664
6.05k
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
9665
9666
  // Connection has been closed on client
9667
6.05k
  if (!line_reader.getline()) { return false; }
9668
9669
6.05k
  Request req;
9670
6.05k
  req.start_time_ = std::chrono::steady_clock::now();
9671
6.05k
  req.remote_addr = remote_addr;
9672
6.05k
  req.remote_port = remote_port;
9673
6.05k
  req.local_addr = local_addr;
9674
6.05k
  req.local_port = local_port;
9675
9676
6.05k
  Response res;
9677
6.05k
  res.version = "HTTP/1.1";
9678
6.05k
  res.headers = default_headers_;
9679
9680
#ifdef __APPLE__
9681
  // Socket file descriptor exceeded FD_SETSIZE...
9682
  if (strm.socket() >= FD_SETSIZE) {
9683
    Headers dummy;
9684
    detail::read_headers(strm, dummy);
9685
    res.status = StatusCode::InternalServerError_500;
9686
    output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);
9687
    return write_response(strm, close_connection, req, res);
9688
  }
9689
#endif
9690
9691
  // Request line and headers
9692
6.05k
  if (!parse_request_line(line_reader.ptr(), req)) {
9693
346
    res.status = StatusCode::BadRequest_400;
9694
346
    output_error_log(Error::InvalidRequestLine, &req);
9695
346
    return write_response(strm, close_connection, req, res);
9696
346
  }
9697
9698
  // Request headers
9699
5.71k
  if (!detail::read_headers(strm, req.headers)) {
9700
1.25k
    res.status = StatusCode::BadRequest_400;
9701
1.25k
    output_error_log(Error::InvalidHeaders, &req);
9702
1.25k
    return write_response(strm, close_connection, req, res);
9703
1.25k
  }
9704
9705
  // Check if the request URI doesn't exceed the limit
9706
4.45k
  if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
9707
7
    res.status = StatusCode::UriTooLong_414;
9708
7
    output_error_log(Error::ExceedUriMaxLength, &req);
9709
7
    return write_response(strm, close_connection, req, res);
9710
7
  }
9711
9712
4.45k
  if (req.get_header_value("Connection") == "close") {
9713
2
    connection_closed = true;
9714
2
  }
9715
9716
4.45k
  if (req.version == "HTTP/1.0" &&
9717
2.82k
      req.get_header_value("Connection") != "Keep-Alive") {
9718
2.82k
    connection_closed = true;
9719
2.82k
  }
9720
9721
4.45k
  if (!trusted_proxies_.empty() && req.has_header("X-Forwarded-For")) {
9722
0
    auto x_forwarded_for = req.get_header_value("X-Forwarded-For");
9723
0
    req.remote_addr = get_client_ip(x_forwarded_for, trusted_proxies_);
9724
4.45k
  } else {
9725
4.45k
    req.remote_addr = remote_addr;
9726
4.45k
  }
9727
4.45k
  req.remote_port = remote_port;
9728
9729
4.45k
  req.local_addr = local_addr;
9730
4.45k
  req.local_port = local_port;
9731
9732
4.45k
  if (req.has_header("Accept")) {
9733
949
    const auto &accept_header = req.get_header_value("Accept");
9734
949
    if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
9735
174
      res.status = StatusCode::BadRequest_400;
9736
174
      output_error_log(Error::HTTPParsing, &req);
9737
174
      return write_response(strm, close_connection, req, res);
9738
174
    }
9739
949
  }
9740
9741
4.27k
  if (req.has_header("Range")) {
9742
907
    const auto &range_header_value = req.get_header_value("Range");
9743
907
    if (!detail::parse_range_header(range_header_value, req.ranges)) {
9744
141
      res.status = StatusCode::RangeNotSatisfiable_416;
9745
141
      output_error_log(Error::InvalidRangeHeader, &req);
9746
141
      return write_response(strm, close_connection, req, res);
9747
141
    }
9748
907
  }
9749
9750
4.13k
  if (setup_request) { setup_request(req); }
9751
9752
4.13k
  if (req.get_header_value("Expect") == "100-continue") {
9753
2
    int status = StatusCode::Continue_100;
9754
2
    if (expect_100_continue_handler_) {
9755
0
      status = expect_100_continue_handler_(req, res);
9756
0
    }
9757
2
    switch (status) {
9758
2
    case StatusCode::Continue_100:
9759
2
    case StatusCode::ExpectationFailed_417:
9760
2
      detail::write_response_line(strm, status);
9761
2
      strm.write("\r\n");
9762
2
      break;
9763
0
    default:
9764
0
      connection_closed = true;
9765
0
      return write_response(strm, true, req, res);
9766
2
    }
9767
2
  }
9768
9769
  // Setup `is_connection_closed` method
9770
4.13k
  auto sock = strm.socket();
9771
4.13k
  req.is_connection_closed = [sock]() {
9772
0
    return !detail::is_socket_alive(sock);
9773
0
  };
9774
9775
  // Routing
9776
4.13k
  auto routed = false;
9777
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
9778
  routed = routing(req, res, strm);
9779
#else
9780
4.13k
  try {
9781
4.13k
    routed = routing(req, res, strm);
9782
4.13k
  } catch (std::exception &e) {
9783
0
    if (exception_handler_) {
9784
0
      auto ep = std::current_exception();
9785
0
      exception_handler_(req, res, ep);
9786
0
      routed = true;
9787
0
    } else {
9788
0
      res.status = StatusCode::InternalServerError_500;
9789
0
      std::string val;
9790
0
      auto s = e.what();
9791
0
      for (size_t i = 0; s[i]; i++) {
9792
0
        switch (s[i]) {
9793
0
        case '\r': val += "\\r"; break;
9794
0
        case '\n': val += "\\n"; break;
9795
0
        default: val += s[i]; break;
9796
0
        }
9797
0
      }
9798
0
      res.set_header("EXCEPTION_WHAT", val);
9799
0
    }
9800
0
  } catch (...) {
9801
0
    if (exception_handler_) {
9802
0
      auto ep = std::current_exception();
9803
0
      exception_handler_(req, res, ep);
9804
0
      routed = true;
9805
0
    } else {
9806
0
      res.status = StatusCode::InternalServerError_500;
9807
0
      res.set_header("EXCEPTION_WHAT", "UNKNOWN");
9808
0
    }
9809
0
  }
9810
0
#endif
9811
4.13k
  if (routed) {
9812
1.75k
    if (res.status == -1) {
9813
1.75k
      res.status = req.ranges.empty() ? StatusCode::OK_200
9814
1.75k
                                      : StatusCode::PartialContent_206;
9815
1.75k
    }
9816
9817
    // Serve file content by using a content provider
9818
1.75k
    if (!res.file_content_path_.empty()) {
9819
0
      const auto &path = res.file_content_path_;
9820
0
      auto mm = std::make_shared<detail::mmap>(path.c_str());
9821
0
      if (!mm->is_open()) {
9822
0
        res.body.clear();
9823
0
        res.content_length_ = 0;
9824
0
        res.content_provider_ = nullptr;
9825
0
        res.status = StatusCode::NotFound_404;
9826
0
        output_error_log(Error::OpenFile, &req);
9827
0
        return write_response(strm, close_connection, req, res);
9828
0
      }
9829
9830
0
      auto content_type = res.file_content_content_type_;
9831
0
      if (content_type.empty()) {
9832
0
        content_type = detail::find_content_type(
9833
0
            path, file_extension_and_mimetype_map_, default_file_mimetype_);
9834
0
      }
9835
9836
0
      res.set_content_provider(
9837
0
          mm->size(), content_type,
9838
0
          [mm](size_t offset, size_t length, DataSink &sink) -> bool {
9839
0
            sink.write(mm->data() + offset, length);
9840
0
            return true;
9841
0
          });
9842
0
    }
9843
9844
1.75k
    if (detail::range_error(req, res)) {
9845
313
      res.body.clear();
9846
313
      res.content_length_ = 0;
9847
313
      res.content_provider_ = nullptr;
9848
313
      res.status = StatusCode::RangeNotSatisfiable_416;
9849
313
      return write_response(strm, close_connection, req, res);
9850
313
    }
9851
9852
1.44k
    return write_response_with_content(strm, close_connection, req, res);
9853
2.37k
  } else {
9854
2.37k
    if (res.status == -1) { res.status = StatusCode::NotFound_404; }
9855
9856
2.37k
    return write_response(strm, close_connection, req, res);
9857
2.37k
  }
9858
4.13k
}
9859
9860
0
inline bool Server::is_valid() const { return true; }
9861
9862
0
inline bool Server::process_and_close_socket(socket_t sock) {
9863
0
  std::string remote_addr;
9864
0
  int remote_port = 0;
9865
0
  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
9866
9867
0
  std::string local_addr;
9868
0
  int local_port = 0;
9869
0
  detail::get_local_ip_and_port(sock, local_addr, local_port);
9870
9871
0
  auto ret = detail::process_server_socket(
9872
0
      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
9873
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9874
0
      write_timeout_usec_,
9875
0
      [&](Stream &strm, bool close_connection, bool &connection_closed) {
9876
0
        return process_request(strm, remote_addr, remote_port, local_addr,
9877
0
                               local_port, close_connection, connection_closed,
9878
0
                               nullptr);
9879
0
      });
9880
9881
0
  detail::shutdown_socket(sock);
9882
0
  detail::close_socket(sock);
9883
0
  return ret;
9884
0
}
9885
9886
6.05k
inline void Server::output_log(const Request &req, const Response &res) const {
9887
6.05k
  if (logger_) {
9888
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
9889
0
    logger_(req, res);
9890
0
  }
9891
6.05k
}
9892
9893
inline void Server::output_pre_compression_log(const Request &req,
9894
18
                                               const Response &res) const {
9895
18
  if (pre_compression_logger_) {
9896
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
9897
0
    pre_compression_logger_(req, res);
9898
0
  }
9899
18
}
9900
9901
inline void Server::output_error_log(const Error &err,
9902
4.27k
                                     const Request *req) const {
9903
4.27k
  if (error_logger_) {
9904
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
9905
0
    error_logger_(err, req);
9906
0
  }
9907
4.27k
}
9908
9909
// HTTP client implementation
9910
inline ClientImpl::ClientImpl(const std::string &host)
9911
    : ClientImpl(host, 80, std::string(), std::string()) {}
9912
9913
inline ClientImpl::ClientImpl(const std::string &host, int port)
9914
    : ClientImpl(host, port, std::string(), std::string()) {}
9915
9916
inline ClientImpl::ClientImpl(const std::string &host, int port,
9917
                              const std::string &client_cert_path,
9918
                              const std::string &client_key_path)
9919
    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
9920
      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
9921
9922
0
inline ClientImpl::~ClientImpl() {
9923
  // Wait until all the requests in flight are handled.
9924
0
  size_t retry_count = 10;
9925
0
  while (retry_count-- > 0) {
9926
0
    {
9927
0
      std::lock_guard<std::mutex> guard(socket_mutex_);
9928
0
      if (socket_requests_in_flight_ == 0) { break; }
9929
0
    }
9930
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
9931
0
  }
9932
9933
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
9934
0
  shutdown_socket(socket_);
9935
0
  close_socket(socket_);
9936
0
}
9937
9938
0
inline bool ClientImpl::is_valid() const { return true; }
9939
9940
0
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
9941
0
  client_cert_path_ = rhs.client_cert_path_;
9942
0
  client_key_path_ = rhs.client_key_path_;
9943
0
  connection_timeout_sec_ = rhs.connection_timeout_sec_;
9944
0
  read_timeout_sec_ = rhs.read_timeout_sec_;
9945
0
  read_timeout_usec_ = rhs.read_timeout_usec_;
9946
0
  write_timeout_sec_ = rhs.write_timeout_sec_;
9947
0
  write_timeout_usec_ = rhs.write_timeout_usec_;
9948
0
  max_timeout_msec_ = rhs.max_timeout_msec_;
9949
0
  basic_auth_username_ = rhs.basic_auth_username_;
9950
0
  basic_auth_password_ = rhs.basic_auth_password_;
9951
0
  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
9952
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9953
0
  digest_auth_username_ = rhs.digest_auth_username_;
9954
0
  digest_auth_password_ = rhs.digest_auth_password_;
9955
0
#endif
9956
0
  keep_alive_ = rhs.keep_alive_;
9957
0
  follow_location_ = rhs.follow_location_;
9958
0
  path_encode_ = rhs.path_encode_;
9959
0
  address_family_ = rhs.address_family_;
9960
0
  tcp_nodelay_ = rhs.tcp_nodelay_;
9961
0
  ipv6_v6only_ = rhs.ipv6_v6only_;
9962
0
  socket_options_ = rhs.socket_options_;
9963
0
  compress_ = rhs.compress_;
9964
0
  decompress_ = rhs.decompress_;
9965
0
  interface_ = rhs.interface_;
9966
0
  proxy_host_ = rhs.proxy_host_;
9967
0
  proxy_port_ = rhs.proxy_port_;
9968
0
  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
9969
0
  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
9970
0
  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
9971
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9972
0
  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
9973
0
  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
9974
0
#endif
9975
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9976
0
  ca_cert_file_path_ = rhs.ca_cert_file_path_;
9977
0
  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
9978
0
  ca_cert_store_ = rhs.ca_cert_store_;
9979
0
#endif
9980
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9981
0
  server_certificate_verification_ = rhs.server_certificate_verification_;
9982
0
  server_hostname_verification_ = rhs.server_hostname_verification_;
9983
0
  server_certificate_verifier_ = rhs.server_certificate_verifier_;
9984
0
#endif
9985
0
  logger_ = rhs.logger_;
9986
0
  error_logger_ = rhs.error_logger_;
9987
0
}
9988
9989
0
inline socket_t ClientImpl::create_client_socket(Error &error) const {
9990
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
9991
0
    return detail::create_client_socket(
9992
0
        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
9993
0
        ipv6_v6only_, socket_options_, connection_timeout_sec_,
9994
0
        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
9995
0
        write_timeout_sec_, write_timeout_usec_, interface_, error);
9996
0
  }
9997
9998
  // Check is custom IP specified for host_
9999
0
  std::string ip;
10000
0
  auto it = addr_map_.find(host_);
10001
0
  if (it != addr_map_.end()) { ip = it->second; }
10002
10003
0
  return detail::create_client_socket(
10004
0
      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
10005
0
      socket_options_, connection_timeout_sec_, connection_timeout_usec_,
10006
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
10007
0
      write_timeout_usec_, interface_, error);
10008
0
}
10009
10010
inline bool ClientImpl::create_and_connect_socket(Socket &socket,
10011
0
                                                  Error &error) {
10012
0
  auto sock = create_client_socket(error);
10013
0
  if (sock == INVALID_SOCKET) { return false; }
10014
0
  socket.sock = sock;
10015
0
  return true;
10016
0
}
10017
10018
0
inline bool ClientImpl::ensure_socket_connection(Socket &socket, Error &error) {
10019
0
  return create_and_connect_socket(socket, error);
10020
0
}
10021
10022
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10023
inline bool SSLClient::ensure_socket_connection(Socket &socket, Error &error) {
10024
  if (!ClientImpl::ensure_socket_connection(socket, error)) { return false; }
10025
10026
  if (!proxy_host_.empty() && proxy_port_ != -1) { return true; }
10027
10028
  if (!initialize_ssl(socket, error)) {
10029
    shutdown_socket(socket);
10030
    close_socket(socket);
10031
    return false;
10032
  }
10033
10034
  return true;
10035
}
10036
#endif
10037
10038
inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
10039
0
                                     bool /*shutdown_gracefully*/) {
10040
  // If there are any requests in flight from threads other than us, then it's
10041
  // a thread-unsafe race because individual ssl* objects are not thread-safe.
10042
0
  assert(socket_requests_in_flight_ == 0 ||
10043
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
10044
0
}
10045
10046
0
inline void ClientImpl::shutdown_socket(Socket &socket) const {
10047
0
  if (socket.sock == INVALID_SOCKET) { return; }
10048
0
  detail::shutdown_socket(socket.sock);
10049
0
}
10050
10051
0
inline void ClientImpl::close_socket(Socket &socket) {
10052
  // If there are requests in flight in another thread, usually closing
10053
  // the socket will be fine and they will simply receive an error when
10054
  // using the closed socket, but it is still a bug since rarely the OS
10055
  // may reassign the socket id to be used for a new socket, and then
10056
  // suddenly they will be operating on a live socket that is different
10057
  // than the one they intended!
10058
0
  assert(socket_requests_in_flight_ == 0 ||
10059
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
10060
10061
  // It is also a bug if this happens while SSL is still active
10062
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10063
  assert(socket.ssl == nullptr);
10064
#endif
10065
0
  if (socket.sock == INVALID_SOCKET) { return; }
10066
0
  detail::close_socket(socket.sock);
10067
0
  socket.sock = INVALID_SOCKET;
10068
0
}
10069
10070
inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
10071
0
                                           Response &res) const {
10072
0
  std::array<char, 2048> buf{};
10073
0
10074
0
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
10075
0
10076
0
  if (!line_reader.getline()) { return false; }
10077
0
10078
0
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
10079
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
10080
0
#else
10081
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
10082
0
#endif
10083
0
10084
0
  std::cmatch m;
10085
0
  if (!std::regex_match(line_reader.ptr(), m, re)) {
10086
0
    return req.method == "CONNECT";
10087
0
  }
10088
0
  res.version = std::string(m[1]);
10089
0
  res.status = std::stoi(std::string(m[2]));
10090
0
  res.reason = std::string(m[3]);
10091
0
10092
0
  // Ignore '100 Continue'
10093
0
  while (res.status == StatusCode::Continue_100) {
10094
0
    if (!line_reader.getline()) { return false; } // CRLF
10095
0
    if (!line_reader.getline()) { return false; } // next response line
10096
0
10097
0
    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
10098
0
    res.version = std::string(m[1]);
10099
0
    res.status = std::stoi(std::string(m[2]));
10100
0
    res.reason = std::string(m[3]);
10101
0
  }
10102
0
10103
0
  return true;
10104
0
}
10105
10106
0
inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
10107
0
  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
10108
0
  auto ret = send_(req, res, error);
10109
0
  if (error == Error::SSLPeerCouldBeClosed_) {
10110
0
    assert(!ret);
10111
0
    ret = send_(req, res, error);
10112
0
  }
10113
0
  return ret;
10114
0
}
10115
10116
0
inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
10117
0
  {
10118
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10119
0
10120
0
    // Set this to false immediately - if it ever gets set to true by the end
10121
0
    // of the request, we know another thread instructed us to close the
10122
0
    // socket.
10123
0
    socket_should_be_closed_when_request_is_done_ = false;
10124
0
10125
0
    auto is_alive = false;
10126
0
    if (socket_.is_open()) {
10127
0
      is_alive = detail::is_socket_alive(socket_.sock);
10128
0
10129
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10130
0
      if (is_alive && is_ssl()) {
10131
0
        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
10132
0
          is_alive = false;
10133
0
        }
10134
0
      }
10135
0
#endif
10136
0
10137
0
      if (!is_alive) {
10138
0
        // Attempt to avoid sigpipe by shutting down non-gracefully if it
10139
0
        // seems like the other side has already closed the connection Also,
10140
0
        // there cannot be any requests in flight from other threads since we
10141
0
        // locked request_mutex_, so safe to close everything immediately
10142
0
        const bool shutdown_gracefully = false;
10143
0
        shutdown_ssl(socket_, shutdown_gracefully);
10144
0
        shutdown_socket(socket_);
10145
0
        close_socket(socket_);
10146
0
      }
10147
0
    }
10148
0
10149
0
    if (!is_alive) {
10150
0
      if (!ensure_socket_connection(socket_, error)) {
10151
0
        output_error_log(error, &req);
10152
0
        return false;
10153
0
      }
10154
0
10155
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10156
0
      // TODO: refactoring
10157
0
      if (is_ssl()) {
10158
0
        auto &scli = static_cast<SSLClient &>(*this);
10159
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
10160
0
          auto success = false;
10161
0
          if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
10162
0
                                       error)) {
10163
0
            if (!success) { output_error_log(error, &req); }
10164
0
            return success;
10165
0
          }
10166
0
        }
10167
0
10168
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
10169
0
          if (!scli.initialize_ssl(socket_, error)) {
10170
0
            output_error_log(error, &req);
10171
0
            return false;
10172
0
          }
10173
0
        }
10174
0
      }
10175
0
#endif
10176
0
    }
10177
0
10178
0
    // Mark the current socket as being in use so that it cannot be closed by
10179
0
    // anyone else while this request is ongoing, even though we will be
10180
0
    // releasing the mutex.
10181
0
    if (socket_requests_in_flight_ > 1) {
10182
0
      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
10183
0
    }
10184
0
    socket_requests_in_flight_ += 1;
10185
0
    socket_requests_are_from_thread_ = std::this_thread::get_id();
10186
0
  }
10187
0
10188
0
  for (const auto &header : default_headers_) {
10189
0
    if (req.headers.find(header.first) == req.headers.end()) {
10190
0
      req.headers.insert(header);
10191
0
    }
10192
0
  }
10193
0
10194
0
  auto ret = false;
10195
0
  auto close_connection = !keep_alive_;
10196
0
10197
0
  auto se = detail::scope_exit([&]() {
10198
0
    // Briefly lock mutex in order to mark that a request is no longer ongoing
10199
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10200
0
    socket_requests_in_flight_ -= 1;
10201
0
    if (socket_requests_in_flight_ <= 0) {
10202
0
      assert(socket_requests_in_flight_ == 0);
10203
0
      socket_requests_are_from_thread_ = std::thread::id();
10204
0
    }
10205
0
10206
0
    if (socket_should_be_closed_when_request_is_done_ || close_connection ||
10207
0
        !ret) {
10208
0
      shutdown_ssl(socket_, true);
10209
0
      shutdown_socket(socket_);
10210
0
      close_socket(socket_);
10211
0
    }
10212
0
  });
10213
0
10214
0
  ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
10215
0
    return handle_request(strm, req, res, close_connection, error);
10216
0
  });
10217
0
10218
0
  if (!ret) {
10219
0
    if (error == Error::Success) {
10220
0
      error = Error::Unknown;
10221
0
      output_error_log(error, &req);
10222
0
    }
10223
0
  }
10224
0
10225
0
  return ret;
10226
0
}
10227
10228
0
inline Result ClientImpl::send(const Request &req) {
10229
0
  auto req2 = req;
10230
0
  return send_(std::move(req2));
10231
0
}
10232
10233
0
inline Result ClientImpl::send_(Request &&req) {
10234
0
  auto res = detail::make_unique<Response>();
10235
0
  auto error = Error::Success;
10236
0
  auto ret = send(req, *res, error);
10237
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10238
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
10239
0
                last_ssl_error_, last_openssl_error_};
10240
0
#else
10241
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
10242
0
#endif
10243
0
}
10244
10245
inline void ClientImpl::prepare_default_headers(Request &r, bool for_stream,
10246
0
                                                const std::string &ct) {
10247
0
  (void)for_stream;
10248
0
  for (const auto &header : default_headers_) {
10249
0
    if (!r.has_header(header.first)) { r.headers.insert(header); }
10250
0
  }
10251
0
10252
0
  if (!r.has_header("Host")) {
10253
0
    if (address_family_ == AF_UNIX) {
10254
0
      r.headers.emplace("Host", "localhost");
10255
0
    } else {
10256
0
      r.headers.emplace(
10257
0
          "Host", detail::make_host_and_port_string(host_, port_, is_ssl()));
10258
0
    }
10259
0
  }
10260
0
10261
0
  if (!r.has_header("Accept")) { r.headers.emplace("Accept", "*/*"); }
10262
0
10263
0
  if (!r.content_receiver) {
10264
0
    if (!r.has_header("Accept-Encoding")) {
10265
0
      std::string accept_encoding;
10266
0
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
10267
0
      accept_encoding = "br";
10268
0
#endif
10269
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
10270
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
10271
0
      accept_encoding += "gzip, deflate";
10272
0
#endif
10273
0
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
10274
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
10275
0
      accept_encoding += "zstd";
10276
0
#endif
10277
0
      r.set_header("Accept-Encoding", accept_encoding);
10278
0
    }
10279
0
10280
0
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
10281
0
    if (!r.has_header("User-Agent")) {
10282
0
      auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
10283
0
      r.set_header("User-Agent", agent);
10284
0
    }
10285
0
#endif
10286
0
  }
10287
0
10288
0
  if (!r.body.empty()) {
10289
0
    if (!ct.empty() && !r.has_header("Content-Type")) {
10290
0
      r.headers.emplace("Content-Type", ct);
10291
0
    }
10292
0
    if (!r.has_header("Content-Length")) {
10293
0
      r.headers.emplace("Content-Length", std::to_string(r.body.size()));
10294
0
    }
10295
0
  }
10296
0
}
10297
10298
inline ClientImpl::StreamHandle
10299
ClientImpl::open_stream(const std::string &method, const std::string &path,
10300
                        const Params &params, const Headers &headers,
10301
                        const std::string &body,
10302
0
                        const std::string &content_type) {
10303
0
  StreamHandle handle;
10304
0
  handle.response = detail::make_unique<Response>();
10305
0
  handle.error = Error::Success;
10306
0
10307
0
  auto query_path = params.empty() ? path : append_query_params(path, params);
10308
0
  handle.connection_ = detail::make_unique<ClientConnection>();
10309
0
10310
0
  {
10311
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10312
0
10313
0
    auto is_alive = false;
10314
0
    if (socket_.is_open()) {
10315
0
      is_alive = detail::is_socket_alive(socket_.sock);
10316
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10317
0
      if (is_alive && is_ssl()) {
10318
0
        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
10319
0
          is_alive = false;
10320
0
        }
10321
0
      }
10322
0
#endif
10323
0
      if (!is_alive) {
10324
0
        shutdown_ssl(socket_, false);
10325
0
        shutdown_socket(socket_);
10326
0
        close_socket(socket_);
10327
0
      }
10328
0
    }
10329
0
10330
0
    if (!is_alive) {
10331
0
      if (!ensure_socket_connection(socket_, handle.error)) {
10332
0
        handle.response.reset();
10333
0
        return handle;
10334
0
      }
10335
0
10336
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10337
0
      if (is_ssl()) {
10338
0
        auto &scli = static_cast<SSLClient &>(*this);
10339
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
10340
0
          if (!scli.initialize_ssl(socket_, handle.error)) {
10341
0
            handle.response.reset();
10342
0
            return handle;
10343
0
          }
10344
0
        }
10345
0
      }
10346
0
#endif
10347
0
    }
10348
0
10349
0
    transfer_socket_ownership_to_handle(handle);
10350
0
  }
10351
0
10352
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10353
0
  if (is_ssl() && handle.connection_->ssl) {
10354
0
    handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
10355
0
        handle.connection_->sock, handle.connection_->ssl, read_timeout_sec_,
10356
0
        read_timeout_usec_, write_timeout_sec_, write_timeout_usec_);
10357
0
  } else {
10358
0
    handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
10359
0
        handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
10360
0
        write_timeout_sec_, write_timeout_usec_);
10361
0
  }
10362
0
#else
10363
0
  handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
10364
0
      handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
10365
0
      write_timeout_sec_, write_timeout_usec_);
10366
0
#endif
10367
0
  handle.stream_ = handle.socket_stream_.get();
10368
0
10369
0
  Request req;
10370
0
  req.method = method;
10371
0
  req.path = query_path;
10372
0
  req.headers = headers;
10373
0
  req.body = body;
10374
0
10375
0
  prepare_default_headers(req, true, content_type);
10376
0
10377
0
  auto &strm = *handle.stream_;
10378
0
  if (detail::write_request_line(strm, req.method, req.path) < 0) {
10379
0
    handle.error = Error::Write;
10380
0
    handle.response.reset();
10381
0
    return handle;
10382
0
  }
10383
0
10384
0
  if (!detail::check_and_write_headers(strm, req.headers, header_writer_,
10385
0
                                       handle.error)) {
10386
0
    handle.response.reset();
10387
0
    return handle;
10388
0
  }
10389
0
10390
0
  if (!body.empty()) {
10391
0
    if (strm.write(body.data(), body.size()) < 0) {
10392
0
      handle.error = Error::Write;
10393
0
      handle.response.reset();
10394
0
      return handle;
10395
0
    }
10396
0
  }
10397
0
10398
0
  if (!read_response_line(strm, req, *handle.response) ||
10399
0
      !detail::read_headers(strm, handle.response->headers)) {
10400
0
    handle.error = Error::Read;
10401
0
    handle.response.reset();
10402
0
    return handle;
10403
0
  }
10404
0
10405
0
  handle.body_reader_.stream = handle.stream_;
10406
0
10407
0
  auto content_length_str = handle.response->get_header_value("Content-Length");
10408
0
  if (!content_length_str.empty()) {
10409
0
    handle.body_reader_.content_length =
10410
0
        static_cast<size_t>(std::stoull(content_length_str));
10411
0
  }
10412
0
10413
0
  auto transfer_encoding =
10414
0
      handle.response->get_header_value("Transfer-Encoding");
10415
0
  handle.body_reader_.chunked = (transfer_encoding == "chunked");
10416
0
10417
0
  auto content_encoding = handle.response->get_header_value("Content-Encoding");
10418
0
  if (!content_encoding.empty()) {
10419
0
    handle.decompressor_ = detail::create_decompressor(content_encoding);
10420
0
  }
10421
0
10422
0
  return handle;
10423
0
}
10424
10425
0
inline ssize_t ClientImpl::StreamHandle::read(char *buf, size_t len) {
10426
0
  if (!is_valid() || !response) { return -1; }
10427
0
10428
0
  if (decompressor_) { return read_with_decompression(buf, len); }
10429
0
  auto n = detail::read_body_content(stream_, body_reader_, buf, len);
10430
0
10431
0
  if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_) {
10432
0
    trailers_parsed_ = true;
10433
0
    if (body_reader_.chunked_decoder) {
10434
0
      if (!body_reader_.chunked_decoder->parse_trailers_into(
10435
0
              response->trailers, response->headers)) {
10436
0
        return n;
10437
0
      }
10438
0
    } else {
10439
0
      detail::ChunkedDecoder dec(*stream_);
10440
0
      if (!dec.parse_trailers_into(response->trailers, response->headers)) {
10441
0
        return n;
10442
0
      }
10443
0
    }
10444
0
  }
10445
0
10446
0
  return n;
10447
0
}
10448
10449
inline ssize_t ClientImpl::StreamHandle::read_with_decompression(char *buf,
10450
0
                                                                 size_t len) {
10451
0
  if (decompress_offset_ < decompress_buffer_.size()) {
10452
0
    auto available = decompress_buffer_.size() - decompress_offset_;
10453
0
    auto to_copy = (std::min)(len, available);
10454
0
    std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
10455
0
    decompress_offset_ += to_copy;
10456
0
    return static_cast<ssize_t>(to_copy);
10457
0
  }
10458
0
10459
0
  decompress_buffer_.clear();
10460
0
  decompress_offset_ = 0;
10461
0
10462
0
  constexpr size_t kDecompressionBufferSize = 8192;
10463
0
  char compressed_buf[kDecompressionBufferSize];
10464
0
10465
0
  while (true) {
10466
0
    auto n = detail::read_body_content(stream_, body_reader_, compressed_buf,
10467
0
                                       sizeof(compressed_buf));
10468
0
10469
0
    if (n <= 0) { return n; }
10470
0
10471
0
    bool decompress_ok =
10472
0
        decompressor_->decompress(compressed_buf, static_cast<size_t>(n),
10473
0
                                  [this](const char *data, size_t data_len) {
10474
0
                                    decompress_buffer_.append(data, data_len);
10475
0
                                    return true;
10476
0
                                  });
10477
0
10478
0
    if (!decompress_ok) {
10479
0
      body_reader_.last_error = Error::Read;
10480
0
      return -1;
10481
0
    }
10482
0
10483
0
    if (!decompress_buffer_.empty()) { break; }
10484
0
  }
10485
0
10486
0
  auto to_copy = (std::min)(len, decompress_buffer_.size());
10487
0
  std::memcpy(buf, decompress_buffer_.data(), to_copy);
10488
0
  decompress_offset_ = to_copy;
10489
0
  return static_cast<ssize_t>(to_copy);
10490
0
}
10491
10492
0
inline void ClientImpl::StreamHandle::parse_trailers_if_needed() {
10493
0
  if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_) {
10494
0
    return;
10495
0
  }
10496
0
10497
0
  trailers_parsed_ = true;
10498
0
10499
0
  const auto bufsiz = 128;
10500
0
  char line_buf[bufsiz];
10501
0
  detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
10502
0
10503
0
  if (!line_reader.getline()) { return; }
10504
0
10505
0
  if (!detail::parse_trailers(line_reader, response->trailers,
10506
0
                              response->headers)) {
10507
0
    return;
10508
0
  }
10509
0
}
10510
10511
// Inline method implementations for `ChunkedDecoder`.
10512
namespace detail {
10513
10514
924
inline ChunkedDecoder::ChunkedDecoder(Stream &s) : strm(s) {}
10515
10516
inline ssize_t ChunkedDecoder::read_payload(char *buf, size_t len,
10517
                                            size_t &out_chunk_offset,
10518
4.55k
                                            size_t &out_chunk_total) {
10519
4.55k
  if (finished) { return 0; }
10520
10521
4.55k
  if (chunk_remaining == 0) {
10522
3.98k
    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
10523
3.98k
    if (!lr.getline()) { return -1; }
10524
10525
3.96k
    char *endptr = nullptr;
10526
3.96k
    unsigned long chunk_len = std::strtoul(lr.ptr(), &endptr, 16);
10527
3.96k
    if (endptr == lr.ptr()) { return -1; }
10528
3.93k
    if (chunk_len == ULONG_MAX) { return -1; }
10529
10530
3.93k
    if (chunk_len == 0) {
10531
477
      chunk_remaining = 0;
10532
477
      finished = true;
10533
477
      out_chunk_offset = 0;
10534
477
      out_chunk_total = 0;
10535
477
      return 0;
10536
477
    }
10537
10538
3.46k
    chunk_remaining = static_cast<size_t>(chunk_len);
10539
3.46k
    last_chunk_total = chunk_remaining;
10540
3.46k
    last_chunk_offset = 0;
10541
3.46k
  }
10542
10543
4.02k
  auto to_read = (std::min)(chunk_remaining, len);
10544
4.02k
  auto n = strm.read(buf, to_read);
10545
4.02k
  if (n <= 0) { return -1; }
10546
10547
3.68k
  auto offset_before = last_chunk_offset;
10548
3.68k
  last_chunk_offset += static_cast<size_t>(n);
10549
3.68k
  chunk_remaining -= static_cast<size_t>(n);
10550
10551
3.68k
  out_chunk_offset = offset_before;
10552
3.68k
  out_chunk_total = last_chunk_total;
10553
10554
3.68k
  if (chunk_remaining == 0) {
10555
3.10k
    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
10556
3.10k
    if (!lr.getline()) { return -1; }
10557
3.09k
    if (std::strcmp(lr.ptr(), "\r\n") != 0) { return -1; }
10558
3.09k
  }
10559
10560
3.64k
  return n;
10561
3.68k
}
10562
10563
inline bool ChunkedDecoder::parse_trailers_into(Headers &dest,
10564
477
                                                const Headers &src_headers) {
10565
477
  stream_line_reader lr(strm, line_buf, sizeof(line_buf));
10566
477
  if (!lr.getline()) { return false; }
10567
469
  return parse_trailers(lr, dest, src_headers);
10568
477
}
10569
10570
} // namespace detail
10571
10572
inline void
10573
0
ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
10574
0
  handle.connection_->sock = socket_.sock;
10575
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10576
0
  handle.connection_->ssl = socket_.ssl;
10577
0
  socket_.ssl = nullptr;
10578
0
#endif
10579
0
  socket_.sock = INVALID_SOCKET;
10580
0
}
10581
10582
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
10583
                                       Response &res, bool close_connection,
10584
0
                                       Error &error) {
10585
0
  if (req.path.empty()) {
10586
0
    error = Error::Connection;
10587
0
    output_error_log(error, &req);
10588
0
    return false;
10589
0
  }
10590
0
10591
0
  auto req_save = req;
10592
0
10593
0
  bool ret;
10594
0
10595
0
  if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
10596
0
    auto req2 = req;
10597
0
    req2.path = "http://" +
10598
0
                detail::make_host_and_port_string(host_, port_, false) +
10599
0
                req.path;
10600
0
    ret = process_request(strm, req2, res, close_connection, error);
10601
0
    req = std::move(req2);
10602
0
    req.path = req_save.path;
10603
0
  } else {
10604
0
    ret = process_request(strm, req, res, close_connection, error);
10605
0
  }
10606
0
10607
0
  if (!ret) { return false; }
10608
0
10609
0
  if (res.get_header_value("Connection") == "close" ||
10610
0
      (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
10611
0
    // TODO this requires a not-entirely-obvious chain of calls to be correct
10612
0
    // for this to be safe.
10613
0
10614
0
    // This is safe to call because handle_request is only called by send_
10615
0
    // which locks the request mutex during the process. It would be a bug
10616
0
    // to call it from a different thread since it's a thread-safety issue
10617
0
    // to do these things to the socket if another thread is using the socket.
10618
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
10619
0
    shutdown_ssl(socket_, true);
10620
0
    shutdown_socket(socket_);
10621
0
    close_socket(socket_);
10622
0
  }
10623
0
10624
0
  if (300 < res.status && res.status < 400 && follow_location_) {
10625
0
    req = std::move(req_save);
10626
0
    ret = redirect(req, res, error);
10627
0
  }
10628
0
10629
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10630
0
  if ((res.status == StatusCode::Unauthorized_401 ||
10631
0
       res.status == StatusCode::ProxyAuthenticationRequired_407) &&
10632
0
      req.authorization_count_ < 5) {
10633
0
    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
10634
0
    const auto &username =
10635
0
        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
10636
0
    const auto &password =
10637
0
        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
10638
0
10639
0
    if (!username.empty() && !password.empty()) {
10640
0
      std::map<std::string, std::string> auth;
10641
0
      if (detail::parse_www_authenticate(res, auth, is_proxy)) {
10642
0
        Request new_req = req;
10643
0
        new_req.authorization_count_ += 1;
10644
0
        new_req.headers.erase(is_proxy ? "Proxy-Authorization"
10645
0
                                       : "Authorization");
10646
0
        new_req.headers.insert(detail::make_digest_authentication_header(
10647
0
            req, auth, new_req.authorization_count_, detail::random_string(10),
10648
0
            username, password, is_proxy));
10649
0
10650
0
        Response new_res;
10651
0
10652
0
        ret = send(new_req, new_res, error);
10653
0
        if (ret) { res = std::move(new_res); }
10654
0
      }
10655
0
    }
10656
0
  }
10657
0
#endif
10658
0
10659
0
  return ret;
10660
0
}
10661
10662
0
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
10663
0
  if (req.redirect_count_ == 0) {
10664
0
    error = Error::ExceedRedirectCount;
10665
0
    output_error_log(error, &req);
10666
0
    return false;
10667
0
  }
10668
0
10669
0
  auto location = res.get_header_value("location");
10670
0
  if (location.empty()) { return false; }
10671
0
10672
0
  thread_local const std::regex re(
10673
0
      R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
10674
0
10675
0
  std::smatch m;
10676
0
  if (!std::regex_match(location, m, re)) { return false; }
10677
0
10678
0
  auto scheme = is_ssl() ? "https" : "http";
10679
0
10680
0
  auto next_scheme = m[1].str();
10681
0
  auto next_host = m[2].str();
10682
0
  if (next_host.empty()) { next_host = m[3].str(); }
10683
0
  auto port_str = m[4].str();
10684
0
  auto next_path = m[5].str();
10685
0
  auto next_query = m[6].str();
10686
0
10687
0
  auto next_port = port_;
10688
0
  if (!port_str.empty()) {
10689
0
    next_port = std::stoi(port_str);
10690
0
  } else if (!next_scheme.empty()) {
10691
0
    next_port = next_scheme == "https" ? 443 : 80;
10692
0
  }
10693
0
10694
0
  if (next_scheme.empty()) { next_scheme = scheme; }
10695
0
  if (next_host.empty()) { next_host = host_; }
10696
0
  if (next_path.empty()) { next_path = "/"; }
10697
0
10698
0
  auto path = decode_query_component(next_path, true) + next_query;
10699
0
10700
0
  // Same host redirect - use current client
10701
0
  if (next_scheme == scheme && next_host == host_ && next_port == port_) {
10702
0
    return detail::redirect(*this, req, res, path, location, error);
10703
0
  }
10704
0
10705
0
  // Cross-host/scheme redirect - create new client with robust setup
10706
0
  return create_redirect_client(next_scheme, next_host, next_port, req, res,
10707
0
                                path, location, error);
10708
0
}
10709
10710
// New method for robust redirect client creation
10711
inline bool ClientImpl::create_redirect_client(
10712
    const std::string &scheme, const std::string &host, int port, Request &req,
10713
    Response &res, const std::string &path, const std::string &location,
10714
0
    Error &error) {
10715
0
  // Determine if we need SSL
10716
0
  auto need_ssl = (scheme == "https");
10717
0
10718
0
  // Clean up request headers that are host/client specific
10719
0
  // Remove headers that should not be carried over to new host
10720
0
  auto headers_to_remove =
10721
0
      std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
10722
0
10723
0
  for (const auto &header_name : headers_to_remove) {
10724
0
    auto it = req.headers.find(header_name);
10725
0
    while (it != req.headers.end()) {
10726
0
      it = req.headers.erase(it);
10727
0
      it = req.headers.find(header_name);
10728
0
    }
10729
0
  }
10730
0
10731
0
  // Create appropriate client type and handle redirect
10732
0
  if (need_ssl) {
10733
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10734
0
    // Create SSL client for HTTPS redirect
10735
0
    SSLClient redirect_client(host, port);
10736
0
10737
0
    // Setup basic client configuration first
10738
0
    setup_redirect_client(redirect_client);
10739
0
10740
0
    // SSL-specific configuration for proxy environments
10741
0
    if (!proxy_host_.empty() && proxy_port_ != -1) {
10742
0
      // Critical: Disable SSL verification for proxy environments
10743
0
      redirect_client.enable_server_certificate_verification(false);
10744
0
      redirect_client.enable_server_hostname_verification(false);
10745
0
    } else {
10746
0
      // For direct SSL connections, copy SSL verification settings
10747
0
      redirect_client.enable_server_certificate_verification(
10748
0
          server_certificate_verification_);
10749
0
      redirect_client.enable_server_hostname_verification(
10750
0
          server_hostname_verification_);
10751
0
    }
10752
0
10753
0
    // Handle CA certificate store and paths if available
10754
0
    if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_)) {
10755
0
      redirect_client.set_ca_cert_store(ca_cert_store_);
10756
0
    }
10757
0
    if (!ca_cert_file_path_.empty()) {
10758
0
      redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
10759
0
    }
10760
0
10761
0
    // Client certificates are set through constructor for SSLClient
10762
0
    // NOTE: SSLClient constructor already takes client_cert_path and
10763
0
    // client_key_path so we need to create it properly if client certs are
10764
0
    // needed
10765
0
10766
0
    // Execute the redirect
10767
0
    return detail::redirect(redirect_client, req, res, path, location, error);
10768
0
#else
10769
0
    // SSL not supported - set appropriate error
10770
0
    error = Error::SSLConnection;
10771
0
    output_error_log(error, &req);
10772
0
    return false;
10773
0
#endif
10774
0
  } else {
10775
0
    // HTTP redirect
10776
0
    ClientImpl redirect_client(host, port);
10777
0
10778
0
    // Setup client with robust configuration
10779
0
    setup_redirect_client(redirect_client);
10780
0
10781
0
    // Execute the redirect
10782
0
    return detail::redirect(redirect_client, req, res, path, location, error);
10783
0
  }
10784
0
}
10785
10786
// New method for robust client setup (based on basic_manual_redirect.cpp
10787
// logic)
10788
template <typename ClientType>
10789
0
inline void ClientImpl::setup_redirect_client(ClientType &client) {
10790
0
  // Copy basic settings first
10791
0
  client.set_connection_timeout(connection_timeout_sec_);
10792
0
  client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
10793
0
  client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
10794
0
  client.set_keep_alive(keep_alive_);
10795
0
  client.set_follow_location(
10796
0
      true); // Enable redirects to handle multi-step redirects
10797
0
  client.set_path_encode(path_encode_);
10798
0
  client.set_compress(compress_);
10799
0
  client.set_decompress(decompress_);
10800
0
10801
0
  // Copy authentication settings BEFORE proxy setup
10802
0
  if (!basic_auth_username_.empty()) {
10803
0
    client.set_basic_auth(basic_auth_username_, basic_auth_password_);
10804
0
  }
10805
0
  if (!bearer_token_auth_token_.empty()) {
10806
0
    client.set_bearer_token_auth(bearer_token_auth_token_);
10807
0
  }
10808
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10809
0
  if (!digest_auth_username_.empty()) {
10810
0
    client.set_digest_auth(digest_auth_username_, digest_auth_password_);
10811
0
  }
10812
0
#endif
10813
0
10814
0
  // Setup proxy configuration (CRITICAL ORDER - proxy must be set
10815
0
  // before proxy auth)
10816
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
10817
0
    // First set proxy host and port
10818
0
    client.set_proxy(proxy_host_, proxy_port_);
10819
0
10820
0
    // Then set proxy authentication (order matters!)
10821
0
    if (!proxy_basic_auth_username_.empty()) {
10822
0
      client.set_proxy_basic_auth(proxy_basic_auth_username_,
10823
0
                                  proxy_basic_auth_password_);
10824
0
    }
10825
0
    if (!proxy_bearer_token_auth_token_.empty()) {
10826
0
      client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
10827
0
    }
10828
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10829
0
    if (!proxy_digest_auth_username_.empty()) {
10830
0
      client.set_proxy_digest_auth(proxy_digest_auth_username_,
10831
0
                                   proxy_digest_auth_password_);
10832
0
    }
10833
0
#endif
10834
0
  }
10835
0
10836
0
  // Copy network and socket settings
10837
0
  client.set_address_family(address_family_);
10838
0
  client.set_tcp_nodelay(tcp_nodelay_);
10839
0
  client.set_ipv6_v6only(ipv6_v6only_);
10840
0
  if (socket_options_) { client.set_socket_options(socket_options_); }
10841
0
  if (!interface_.empty()) { client.set_interface(interface_); }
10842
0
10843
0
  // Copy logging and headers
10844
0
  if (logger_) { client.set_logger(logger_); }
10845
0
  if (error_logger_) { client.set_error_logger(error_logger_); }
10846
0
10847
0
  // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
10848
0
  // Each new client should generate its own headers based on its target host
10849
0
}
10850
10851
inline bool ClientImpl::write_content_with_provider(Stream &strm,
10852
                                                    const Request &req,
10853
0
                                                    Error &error) const {
10854
0
  auto is_shutting_down = []() { return false; };
10855
0
10856
0
  if (req.is_chunked_content_provider_) {
10857
0
    // TODO: Brotli support
10858
0
    std::unique_ptr<detail::compressor> compressor;
10859
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
10860
0
    if (compress_) {
10861
0
      compressor = detail::make_unique<detail::gzip_compressor>();
10862
0
    } else
10863
0
#endif
10864
0
    {
10865
0
      compressor = detail::make_unique<detail::nocompressor>();
10866
0
    }
10867
0
10868
0
    return detail::write_content_chunked(strm, req.content_provider_,
10869
0
                                         is_shutting_down, *compressor, error);
10870
0
  } else {
10871
0
    return detail::write_content_with_progress(
10872
0
        strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
10873
0
        req.upload_progress, error);
10874
0
  }
10875
0
}
10876
10877
inline bool ClientImpl::write_request(Stream &strm, Request &req,
10878
0
                                      bool close_connection, Error &error) {
10879
0
  // Prepare additional headers
10880
0
  if (close_connection) {
10881
0
    if (!req.has_header("Connection")) {
10882
0
      req.set_header("Connection", "close");
10883
0
    }
10884
0
  }
10885
0
10886
0
  std::string ct_for_defaults;
10887
0
  if (!req.has_header("Content-Type") && !req.body.empty()) {
10888
0
    ct_for_defaults = "text/plain";
10889
0
  }
10890
0
  prepare_default_headers(req, false, ct_for_defaults);
10891
0
10892
0
  if (req.body.empty()) {
10893
0
    if (req.content_provider_) {
10894
0
      if (!req.is_chunked_content_provider_) {
10895
0
        if (!req.has_header("Content-Length")) {
10896
0
          auto length = std::to_string(req.content_length_);
10897
0
          req.set_header("Content-Length", length);
10898
0
        }
10899
0
      }
10900
0
    } else {
10901
0
      if (req.method == "POST" || req.method == "PUT" ||
10902
0
          req.method == "PATCH") {
10903
0
        req.set_header("Content-Length", "0");
10904
0
      }
10905
0
    }
10906
0
  }
10907
0
10908
0
  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
10909
0
    if (!req.has_header("Authorization")) {
10910
0
      req.headers.insert(make_basic_authentication_header(
10911
0
          basic_auth_username_, basic_auth_password_, false));
10912
0
    }
10913
0
  }
10914
0
10915
0
  if (!proxy_basic_auth_username_.empty() &&
10916
0
      !proxy_basic_auth_password_.empty()) {
10917
0
    if (!req.has_header("Proxy-Authorization")) {
10918
0
      req.headers.insert(make_basic_authentication_header(
10919
0
          proxy_basic_auth_username_, proxy_basic_auth_password_, true));
10920
0
    }
10921
0
  }
10922
0
10923
0
  if (!bearer_token_auth_token_.empty()) {
10924
0
    if (!req.has_header("Authorization")) {
10925
0
      req.headers.insert(make_bearer_token_authentication_header(
10926
0
          bearer_token_auth_token_, false));
10927
0
    }
10928
0
  }
10929
0
10930
0
  if (!proxy_bearer_token_auth_token_.empty()) {
10931
0
    if (!req.has_header("Proxy-Authorization")) {
10932
0
      req.headers.insert(make_bearer_token_authentication_header(
10933
0
          proxy_bearer_token_auth_token_, true));
10934
0
    }
10935
0
  }
10936
0
10937
0
  // Request line and headers
10938
0
  {
10939
0
    detail::BufferStream bstrm;
10940
0
10941
0
    // Extract path and query from req.path
10942
0
    std::string path_part, query_part;
10943
0
    auto query_pos = req.path.find('?');
10944
0
    if (query_pos != std::string::npos) {
10945
0
      path_part = req.path.substr(0, query_pos);
10946
0
      query_part = req.path.substr(query_pos + 1);
10947
0
    } else {
10948
0
      path_part = req.path;
10949
0
      query_part = "";
10950
0
    }
10951
0
10952
0
    // Encode path part. If the original `req.path` already contained a
10953
0
    // query component, preserve its raw query string (including parameter
10954
0
    // order) instead of reparsing and reassembling it which may reorder
10955
0
    // parameters due to container ordering (e.g. `Params` uses
10956
0
    // `std::multimap`). When there is no query in `req.path`, fall back to
10957
0
    // building a query from `req.params` so existing callers that pass
10958
0
    // `Params` continue to work.
10959
0
    auto path_with_query =
10960
0
        path_encode_ ? detail::encode_path(path_part) : path_part;
10961
0
10962
0
    if (!query_part.empty()) {
10963
0
      // Normalize the query string (decode then re-encode) while preserving
10964
0
      // the original parameter order.
10965
0
      auto normalized = detail::normalize_query_string(query_part);
10966
0
      if (!normalized.empty()) { path_with_query += '?' + normalized; }
10967
0
10968
0
      // Still populate req.params for handlers/users who read them.
10969
0
      detail::parse_query_text(query_part, req.params);
10970
0
    } else {
10971
0
      // No query in path; parse any query_part (empty) and append params
10972
0
      // from `req.params` when present (preserves prior behavior for
10973
0
      // callers who provide Params separately).
10974
0
      detail::parse_query_text(query_part, req.params);
10975
0
      if (!req.params.empty()) {
10976
0
        path_with_query = append_query_params(path_with_query, req.params);
10977
0
      }
10978
0
    }
10979
0
10980
0
    // Write request line and headers
10981
0
    detail::write_request_line(bstrm, req.method, path_with_query);
10982
0
    if (!detail::check_and_write_headers(bstrm, req.headers, header_writer_,
10983
0
                                         error)) {
10984
0
      output_error_log(error, &req);
10985
0
      return false;
10986
0
    }
10987
0
10988
0
    // Flush buffer
10989
0
    auto &data = bstrm.get_buffer();
10990
0
    if (!detail::write_data(strm, data.data(), data.size())) {
10991
0
      error = Error::Write;
10992
0
      output_error_log(error, &req);
10993
0
      return false;
10994
0
    }
10995
0
  }
10996
0
10997
0
  // Body
10998
0
  if (req.body.empty()) {
10999
0
    return write_content_with_provider(strm, req, error);
11000
0
  }
11001
0
11002
0
  if (req.upload_progress) {
11003
0
    auto body_size = req.body.size();
11004
0
    size_t written = 0;
11005
0
    auto data = req.body.data();
11006
0
11007
0
    while (written < body_size) {
11008
0
      size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
11009
0
      if (!detail::write_data(strm, data + written, to_write)) {
11010
0
        error = Error::Write;
11011
0
        output_error_log(error, &req);
11012
0
        return false;
11013
0
      }
11014
0
      written += to_write;
11015
0
11016
0
      if (!req.upload_progress(written, body_size)) {
11017
0
        error = Error::Canceled;
11018
0
        output_error_log(error, &req);
11019
0
        return false;
11020
0
      }
11021
0
    }
11022
0
  } else {
11023
0
    if (!detail::write_data(strm, req.body.data(), req.body.size())) {
11024
0
      error = Error::Write;
11025
0
      output_error_log(error, &req);
11026
0
      return false;
11027
0
    }
11028
0
  }
11029
0
11030
0
  return true;
11031
0
}
11032
11033
inline std::unique_ptr<Response>
11034
ClientImpl::send_with_content_provider_and_receiver(
11035
    Request &req, const char *body, size_t content_length,
11036
    ContentProvider content_provider,
11037
    ContentProviderWithoutLength content_provider_without_length,
11038
    const std::string &content_type, ContentReceiver content_receiver,
11039
0
    Error &error) {
11040
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
11041
0
11042
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
11043
0
  if (compress_) { req.set_header("Content-Encoding", "gzip"); }
11044
0
#endif
11045
0
11046
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
11047
0
  if (compress_ && !content_provider_without_length) {
11048
0
    // TODO: Brotli support
11049
0
    detail::gzip_compressor compressor;
11050
0
11051
0
    if (content_provider) {
11052
0
      auto ok = true;
11053
0
      size_t offset = 0;
11054
0
      DataSink data_sink;
11055
0
11056
0
      data_sink.write = [&](const char *data, size_t data_len) -> bool {
11057
0
        if (ok) {
11058
0
          auto last = offset + data_len == content_length;
11059
0
11060
0
          auto ret = compressor.compress(
11061
0
              data, data_len, last,
11062
0
              [&](const char *compressed_data, size_t compressed_data_len) {
11063
0
                req.body.append(compressed_data, compressed_data_len);
11064
0
                return true;
11065
0
              });
11066
0
11067
0
          if (ret) {
11068
0
            offset += data_len;
11069
0
          } else {
11070
0
            ok = false;
11071
0
          }
11072
0
        }
11073
0
        return ok;
11074
0
      };
11075
0
11076
0
      while (ok && offset < content_length) {
11077
0
        if (!content_provider(offset, content_length - offset, data_sink)) {
11078
0
          error = Error::Canceled;
11079
0
          output_error_log(error, &req);
11080
0
          return nullptr;
11081
0
        }
11082
0
      }
11083
0
    } else {
11084
0
      if (!compressor.compress(body, content_length, true,
11085
0
                               [&](const char *data, size_t data_len) {
11086
0
                                 req.body.append(data, data_len);
11087
0
                                 return true;
11088
0
                               })) {
11089
0
        error = Error::Compression;
11090
0
        output_error_log(error, &req);
11091
0
        return nullptr;
11092
0
      }
11093
0
    }
11094
0
  } else
11095
0
#endif
11096
0
  {
11097
0
    if (content_provider) {
11098
0
      req.content_length_ = content_length;
11099
0
      req.content_provider_ = std::move(content_provider);
11100
0
      req.is_chunked_content_provider_ = false;
11101
0
    } else if (content_provider_without_length) {
11102
0
      req.content_length_ = 0;
11103
0
      req.content_provider_ = detail::ContentProviderAdapter(
11104
0
          std::move(content_provider_without_length));
11105
0
      req.is_chunked_content_provider_ = true;
11106
0
      req.set_header("Transfer-Encoding", "chunked");
11107
0
    } else {
11108
0
      req.body.assign(body, content_length);
11109
0
    }
11110
0
  }
11111
0
11112
0
  if (content_receiver) {
11113
0
    req.content_receiver =
11114
0
        [content_receiver](const char *data, size_t data_length,
11115
0
                           size_t /*offset*/, size_t /*total_length*/) {
11116
0
          return content_receiver(data, data_length);
11117
0
        };
11118
0
  }
11119
0
11120
0
  auto res = detail::make_unique<Response>();
11121
0
  return send(req, *res, error) ? std::move(res) : nullptr;
11122
0
}
11123
11124
inline Result ClientImpl::send_with_content_provider_and_receiver(
11125
    const std::string &method, const std::string &path, const Headers &headers,
11126
    const char *body, size_t content_length, ContentProvider content_provider,
11127
    ContentProviderWithoutLength content_provider_without_length,
11128
    const std::string &content_type, ContentReceiver content_receiver,
11129
0
    UploadProgress progress) {
11130
0
  Request req;
11131
0
  req.method = method;
11132
0
  req.headers = headers;
11133
0
  req.path = path;
11134
0
  req.upload_progress = std::move(progress);
11135
0
  if (max_timeout_msec_ > 0) {
11136
0
    req.start_time_ = std::chrono::steady_clock::now();
11137
0
  }
11138
0
11139
0
  auto error = Error::Success;
11140
0
11141
0
  auto res = send_with_content_provider_and_receiver(
11142
0
      req, body, content_length, std::move(content_provider),
11143
0
      std::move(content_provider_without_length), content_type,
11144
0
      std::move(content_receiver), error);
11145
0
11146
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11147
0
  return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
11148
0
                last_openssl_error_};
11149
0
#else
11150
0
  return Result{std::move(res), error, std::move(req.headers)};
11151
0
#endif
11152
0
}
11153
11154
inline void ClientImpl::output_log(const Request &req,
11155
0
                                   const Response &res) const {
11156
0
  if (logger_) {
11157
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
11158
0
    logger_(req, res);
11159
0
  }
11160
0
}
11161
11162
inline void ClientImpl::output_error_log(const Error &err,
11163
0
                                         const Request *req) const {
11164
0
  if (error_logger_) {
11165
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
11166
0
    error_logger_(err, req);
11167
0
  }
11168
0
}
11169
11170
inline bool ClientImpl::process_request(Stream &strm, Request &req,
11171
                                        Response &res, bool close_connection,
11172
0
                                        Error &error) {
11173
0
  // Send request
11174
0
  if (!write_request(strm, req, close_connection, error)) { return false; }
11175
0
11176
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11177
0
  if (is_ssl()) {
11178
0
    auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
11179
0
    if (!is_proxy_enabled) {
11180
0
      if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
11181
0
        error = Error::SSLPeerCouldBeClosed_;
11182
0
        output_error_log(error, &req);
11183
0
        return false;
11184
0
      }
11185
0
    }
11186
0
  }
11187
0
#endif
11188
0
11189
0
  // Receive response and headers
11190
0
  if (!read_response_line(strm, req, res) ||
11191
0
      !detail::read_headers(strm, res.headers)) {
11192
0
    error = Error::Read;
11193
0
    output_error_log(error, &req);
11194
0
    return false;
11195
0
  }
11196
0
11197
0
  // Body
11198
0
  if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
11199
0
      req.method != "CONNECT") {
11200
0
    auto redirect = 300 < res.status && res.status < 400 &&
11201
0
                    res.status != StatusCode::NotModified_304 &&
11202
0
                    follow_location_;
11203
0
11204
0
    if (req.response_handler && !redirect) {
11205
0
      if (!req.response_handler(res)) {
11206
0
        error = Error::Canceled;
11207
0
        output_error_log(error, &req);
11208
0
        return false;
11209
0
      }
11210
0
    }
11211
0
11212
0
    auto out =
11213
0
        req.content_receiver
11214
0
            ? static_cast<ContentReceiverWithProgress>(
11215
0
                  [&](const char *buf, size_t n, size_t off, size_t len) {
11216
0
                    if (redirect) { return true; }
11217
0
                    auto ret = req.content_receiver(buf, n, off, len);
11218
0
                    if (!ret) {
11219
0
                      error = Error::Canceled;
11220
0
                      output_error_log(error, &req);
11221
0
                    }
11222
0
                    return ret;
11223
0
                  })
11224
0
            : static_cast<ContentReceiverWithProgress>(
11225
0
                  [&](const char *buf, size_t n, size_t /*off*/,
11226
0
                      size_t /*len*/) {
11227
0
                    assert(res.body.size() + n <= res.body.max_size());
11228
0
                    res.body.append(buf, n);
11229
0
                    return true;
11230
0
                  });
11231
0
11232
0
    auto progress = [&](size_t current, size_t total) {
11233
0
      if (!req.download_progress || redirect) { return true; }
11234
0
      auto ret = req.download_progress(current, total);
11235
0
      if (!ret) {
11236
0
        error = Error::Canceled;
11237
0
        output_error_log(error, &req);
11238
0
      }
11239
0
      return ret;
11240
0
    };
11241
0
11242
0
    if (res.has_header("Content-Length")) {
11243
0
      if (!req.content_receiver) {
11244
0
        auto len = res.get_header_value_u64("Content-Length");
11245
0
        if (len > res.body.max_size()) {
11246
0
          error = Error::Read;
11247
0
          output_error_log(error, &req);
11248
0
          return false;
11249
0
        }
11250
0
        res.body.reserve(static_cast<size_t>(len));
11251
0
      }
11252
0
    }
11253
0
11254
0
    if (res.status != StatusCode::NotModified_304) {
11255
0
      int dummy_status;
11256
0
      if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
11257
0
                                dummy_status, std::move(progress),
11258
0
                                std::move(out), decompress_)) {
11259
0
        if (error != Error::Canceled) { error = Error::Read; }
11260
0
        output_error_log(error, &req);
11261
0
        return false;
11262
0
      }
11263
0
    }
11264
0
  }
11265
0
11266
0
  // Log
11267
0
  output_log(req, res);
11268
0
11269
0
  return true;
11270
0
}
11271
11272
inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
11273
    const std::string &boundary, const UploadFormDataItems &items,
11274
0
    const FormDataProviderItems &provider_items) const {
11275
0
  size_t cur_item = 0;
11276
0
  size_t cur_start = 0;
11277
0
  // cur_item and cur_start are copied to within the std::function and
11278
0
  // maintain state between successive calls
11279
0
  return [&, cur_item, cur_start](size_t offset,
11280
0
                                  DataSink &sink) mutable -> bool {
11281
0
    if (!offset && !items.empty()) {
11282
0
      sink.os << detail::serialize_multipart_formdata(items, boundary, false);
11283
0
      return true;
11284
0
    } else if (cur_item < provider_items.size()) {
11285
0
      if (!cur_start) {
11286
0
        const auto &begin = detail::serialize_multipart_formdata_item_begin(
11287
0
            provider_items[cur_item], boundary);
11288
0
        offset += begin.size();
11289
0
        cur_start = offset;
11290
0
        sink.os << begin;
11291
0
      }
11292
0
11293
0
      DataSink cur_sink;
11294
0
      auto has_data = true;
11295
0
      cur_sink.write = sink.write;
11296
0
      cur_sink.done = [&]() { has_data = false; };
11297
0
11298
0
      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
11299
0
        return false;
11300
0
      }
11301
0
11302
0
      if (!has_data) {
11303
0
        sink.os << detail::serialize_multipart_formdata_item_end();
11304
0
        cur_item++;
11305
0
        cur_start = 0;
11306
0
      }
11307
0
      return true;
11308
0
    } else {
11309
0
      sink.os << detail::serialize_multipart_formdata_finish(boundary);
11310
0
      sink.done();
11311
0
      return true;
11312
0
    }
11313
0
  };
11314
0
}
11315
11316
inline bool ClientImpl::process_socket(
11317
    const Socket &socket,
11318
    std::chrono::time_point<std::chrono::steady_clock> start_time,
11319
0
    std::function<bool(Stream &strm)> callback) {
11320
0
  return detail::process_client_socket(
11321
0
      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
11322
0
      write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
11323
0
}
11324
11325
0
inline bool ClientImpl::is_ssl() const { return false; }
11326
11327
inline Result ClientImpl::Get(const std::string &path,
11328
0
                              DownloadProgress progress) {
11329
0
  return Get(path, Headers(), std::move(progress));
11330
0
}
11331
11332
inline Result ClientImpl::Get(const std::string &path, const Params &params,
11333
                              const Headers &headers,
11334
0
                              DownloadProgress progress) {
11335
0
  if (params.empty()) { return Get(path, headers); }
11336
0
11337
0
  std::string path_with_query = append_query_params(path, params);
11338
0
  return Get(path_with_query, headers, std::move(progress));
11339
0
}
11340
11341
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
11342
0
                              DownloadProgress progress) {
11343
0
  Request req;
11344
0
  req.method = "GET";
11345
0
  req.path = path;
11346
0
  req.headers = headers;
11347
0
  req.download_progress = std::move(progress);
11348
0
  if (max_timeout_msec_ > 0) {
11349
0
    req.start_time_ = std::chrono::steady_clock::now();
11350
0
  }
11351
0
11352
0
  return send_(std::move(req));
11353
0
}
11354
11355
inline Result ClientImpl::Get(const std::string &path,
11356
                              ContentReceiver content_receiver,
11357
0
                              DownloadProgress progress) {
11358
0
  return Get(path, Headers(), nullptr, std::move(content_receiver),
11359
0
             std::move(progress));
11360
0
}
11361
11362
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
11363
                              ContentReceiver content_receiver,
11364
0
                              DownloadProgress progress) {
11365
0
  return Get(path, headers, nullptr, std::move(content_receiver),
11366
0
             std::move(progress));
11367
0
}
11368
11369
inline Result ClientImpl::Get(const std::string &path,
11370
                              ResponseHandler response_handler,
11371
                              ContentReceiver content_receiver,
11372
0
                              DownloadProgress progress) {
11373
0
  return Get(path, Headers(), std::move(response_handler),
11374
0
             std::move(content_receiver), std::move(progress));
11375
0
}
11376
11377
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
11378
                              ResponseHandler response_handler,
11379
                              ContentReceiver content_receiver,
11380
0
                              DownloadProgress progress) {
11381
0
  Request req;
11382
0
  req.method = "GET";
11383
0
  req.path = path;
11384
0
  req.headers = headers;
11385
0
  req.response_handler = std::move(response_handler);
11386
0
  req.content_receiver =
11387
0
      [content_receiver](const char *data, size_t data_length,
11388
0
                         size_t /*offset*/, size_t /*total_length*/) {
11389
0
        return content_receiver(data, data_length);
11390
0
      };
11391
0
  req.download_progress = std::move(progress);
11392
0
  if (max_timeout_msec_ > 0) {
11393
0
    req.start_time_ = std::chrono::steady_clock::now();
11394
0
  }
11395
0
11396
0
  return send_(std::move(req));
11397
0
}
11398
11399
inline Result ClientImpl::Get(const std::string &path, const Params &params,
11400
                              const Headers &headers,
11401
                              ContentReceiver content_receiver,
11402
0
                              DownloadProgress progress) {
11403
0
  return Get(path, params, headers, nullptr, std::move(content_receiver),
11404
0
             std::move(progress));
11405
0
}
11406
11407
inline Result ClientImpl::Get(const std::string &path, const Params &params,
11408
                              const Headers &headers,
11409
                              ResponseHandler response_handler,
11410
                              ContentReceiver content_receiver,
11411
0
                              DownloadProgress progress) {
11412
0
  if (params.empty()) {
11413
0
    return Get(path, headers, std::move(response_handler),
11414
0
               std::move(content_receiver), std::move(progress));
11415
0
  }
11416
0
11417
0
  std::string path_with_query = append_query_params(path, params);
11418
0
  return Get(path_with_query, headers, std::move(response_handler),
11419
0
             std::move(content_receiver), std::move(progress));
11420
0
}
11421
11422
0
inline Result ClientImpl::Head(const std::string &path) {
11423
0
  return Head(path, Headers());
11424
0
}
11425
11426
inline Result ClientImpl::Head(const std::string &path,
11427
0
                               const Headers &headers) {
11428
0
  Request req;
11429
0
  req.method = "HEAD";
11430
0
  req.headers = headers;
11431
0
  req.path = path;
11432
0
  if (max_timeout_msec_ > 0) {
11433
0
    req.start_time_ = std::chrono::steady_clock::now();
11434
0
  }
11435
0
11436
0
  return send_(std::move(req));
11437
0
}
11438
11439
0
inline Result ClientImpl::Post(const std::string &path) {
11440
0
  return Post(path, std::string(), std::string());
11441
0
}
11442
11443
inline Result ClientImpl::Post(const std::string &path,
11444
0
                               const Headers &headers) {
11445
0
  return Post(path, headers, nullptr, 0, std::string());
11446
0
}
11447
11448
inline Result ClientImpl::Post(const std::string &path, const char *body,
11449
                               size_t content_length,
11450
                               const std::string &content_type,
11451
0
                               UploadProgress progress) {
11452
0
  return Post(path, Headers(), body, content_length, content_type, progress);
11453
0
}
11454
11455
inline Result ClientImpl::Post(const std::string &path, const std::string &body,
11456
                               const std::string &content_type,
11457
0
                               UploadProgress progress) {
11458
0
  return Post(path, Headers(), body, content_type, progress);
11459
0
}
11460
11461
0
inline Result ClientImpl::Post(const std::string &path, const Params &params) {
11462
0
  return Post(path, Headers(), params);
11463
0
}
11464
11465
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
11466
                               ContentProvider content_provider,
11467
                               const std::string &content_type,
11468
0
                               UploadProgress progress) {
11469
0
  return Post(path, Headers(), content_length, std::move(content_provider),
11470
0
              content_type, progress);
11471
0
}
11472
11473
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
11474
                               ContentProvider content_provider,
11475
                               const std::string &content_type,
11476
                               ContentReceiver content_receiver,
11477
0
                               UploadProgress progress) {
11478
0
  return Post(path, Headers(), content_length, std::move(content_provider),
11479
0
              content_type, std::move(content_receiver), progress);
11480
0
}
11481
11482
inline Result ClientImpl::Post(const std::string &path,
11483
                               ContentProviderWithoutLength content_provider,
11484
                               const std::string &content_type,
11485
0
                               UploadProgress progress) {
11486
0
  return Post(path, Headers(), std::move(content_provider), content_type,
11487
0
              progress);
11488
0
}
11489
11490
inline Result ClientImpl::Post(const std::string &path,
11491
                               ContentProviderWithoutLength content_provider,
11492
                               const std::string &content_type,
11493
                               ContentReceiver content_receiver,
11494
0
                               UploadProgress progress) {
11495
0
  return Post(path, Headers(), std::move(content_provider), content_type,
11496
0
              std::move(content_receiver), progress);
11497
0
}
11498
11499
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11500
0
                               const Params &params) {
11501
0
  auto query = detail::params_to_query_str(params);
11502
0
  return Post(path, headers, query, "application/x-www-form-urlencoded");
11503
0
}
11504
11505
inline Result ClientImpl::Post(const std::string &path,
11506
                               const UploadFormDataItems &items,
11507
0
                               UploadProgress progress) {
11508
0
  return Post(path, Headers(), items, progress);
11509
0
}
11510
11511
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11512
                               const UploadFormDataItems &items,
11513
0
                               UploadProgress progress) {
11514
0
  const auto &boundary = detail::make_multipart_data_boundary();
11515
0
  const auto &content_type =
11516
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11517
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11518
0
  return Post(path, headers, body, content_type, progress);
11519
0
}
11520
11521
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11522
                               const UploadFormDataItems &items,
11523
                               const std::string &boundary,
11524
0
                               UploadProgress progress) {
11525
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
11526
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
11527
0
  }
11528
0
11529
0
  const auto &content_type =
11530
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11531
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11532
0
  return Post(path, headers, body, content_type, progress);
11533
0
}
11534
11535
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11536
                               const char *body, size_t content_length,
11537
                               const std::string &content_type,
11538
0
                               UploadProgress progress) {
11539
0
  return send_with_content_provider_and_receiver(
11540
0
      "POST", path, headers, body, content_length, nullptr, nullptr,
11541
0
      content_type, nullptr, progress);
11542
0
}
11543
11544
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11545
                               const std::string &body,
11546
                               const std::string &content_type,
11547
0
                               UploadProgress progress) {
11548
0
  return send_with_content_provider_and_receiver(
11549
0
      "POST", path, headers, body.data(), body.size(), nullptr, nullptr,
11550
0
      content_type, nullptr, progress);
11551
0
}
11552
11553
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11554
                               size_t content_length,
11555
                               ContentProvider content_provider,
11556
                               const std::string &content_type,
11557
0
                               UploadProgress progress) {
11558
0
  return send_with_content_provider_and_receiver(
11559
0
      "POST", path, headers, nullptr, content_length,
11560
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
11561
0
}
11562
11563
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11564
                               size_t content_length,
11565
                               ContentProvider content_provider,
11566
                               const std::string &content_type,
11567
                               ContentReceiver content_receiver,
11568
0
                               DownloadProgress progress) {
11569
0
  return send_with_content_provider_and_receiver(
11570
0
      "POST", path, headers, nullptr, content_length,
11571
0
      std::move(content_provider), nullptr, content_type,
11572
0
      std::move(content_receiver), std::move(progress));
11573
0
}
11574
11575
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11576
                               ContentProviderWithoutLength content_provider,
11577
                               const std::string &content_type,
11578
0
                               UploadProgress progress) {
11579
0
  return send_with_content_provider_and_receiver(
11580
0
      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11581
0
      content_type, nullptr, progress);
11582
0
}
11583
11584
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11585
                               ContentProviderWithoutLength content_provider,
11586
                               const std::string &content_type,
11587
                               ContentReceiver content_receiver,
11588
0
                               DownloadProgress progress) {
11589
0
  return send_with_content_provider_and_receiver(
11590
0
      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11591
0
      content_type, std::move(content_receiver), std::move(progress));
11592
0
}
11593
11594
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11595
                               const UploadFormDataItems &items,
11596
                               const FormDataProviderItems &provider_items,
11597
0
                               UploadProgress progress) {
11598
0
  const auto &boundary = detail::make_multipart_data_boundary();
11599
0
  const auto &content_type =
11600
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11601
0
  return send_with_content_provider_and_receiver(
11602
0
      "POST", path, headers, nullptr, 0, nullptr,
11603
0
      get_multipart_content_provider(boundary, items, provider_items),
11604
0
      content_type, nullptr, progress);
11605
0
}
11606
11607
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
11608
                               const std::string &body,
11609
                               const std::string &content_type,
11610
                               ContentReceiver content_receiver,
11611
0
                               DownloadProgress progress) {
11612
0
  Request req;
11613
0
  req.method = "POST";
11614
0
  req.path = path;
11615
0
  req.headers = headers;
11616
0
  req.body = body;
11617
0
  req.content_receiver =
11618
0
      [content_receiver](const char *data, size_t data_length,
11619
0
                         size_t /*offset*/, size_t /*total_length*/) {
11620
0
        return content_receiver(data, data_length);
11621
0
      };
11622
0
  req.download_progress = std::move(progress);
11623
0
11624
0
  if (max_timeout_msec_ > 0) {
11625
0
    req.start_time_ = std::chrono::steady_clock::now();
11626
0
  }
11627
0
11628
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
11629
0
11630
0
  return send_(std::move(req));
11631
0
}
11632
11633
0
inline Result ClientImpl::Put(const std::string &path) {
11634
0
  return Put(path, std::string(), std::string());
11635
0
}
11636
11637
0
inline Result ClientImpl::Put(const std::string &path, const Headers &headers) {
11638
0
  return Put(path, headers, nullptr, 0, std::string());
11639
0
}
11640
11641
inline Result ClientImpl::Put(const std::string &path, const char *body,
11642
                              size_t content_length,
11643
                              const std::string &content_type,
11644
0
                              UploadProgress progress) {
11645
0
  return Put(path, Headers(), body, content_length, content_type, progress);
11646
0
}
11647
11648
inline Result ClientImpl::Put(const std::string &path, const std::string &body,
11649
                              const std::string &content_type,
11650
0
                              UploadProgress progress) {
11651
0
  return Put(path, Headers(), body, content_type, progress);
11652
0
}
11653
11654
0
inline Result ClientImpl::Put(const std::string &path, const Params &params) {
11655
0
  return Put(path, Headers(), params);
11656
0
}
11657
11658
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
11659
                              ContentProvider content_provider,
11660
                              const std::string &content_type,
11661
0
                              UploadProgress progress) {
11662
0
  return Put(path, Headers(), content_length, std::move(content_provider),
11663
0
             content_type, progress);
11664
0
}
11665
11666
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
11667
                              ContentProvider content_provider,
11668
                              const std::string &content_type,
11669
                              ContentReceiver content_receiver,
11670
0
                              UploadProgress progress) {
11671
0
  return Put(path, Headers(), content_length, std::move(content_provider),
11672
0
             content_type, std::move(content_receiver), progress);
11673
0
}
11674
11675
inline Result ClientImpl::Put(const std::string &path,
11676
                              ContentProviderWithoutLength content_provider,
11677
                              const std::string &content_type,
11678
0
                              UploadProgress progress) {
11679
0
  return Put(path, Headers(), std::move(content_provider), content_type,
11680
0
             progress);
11681
0
}
11682
11683
inline Result ClientImpl::Put(const std::string &path,
11684
                              ContentProviderWithoutLength content_provider,
11685
                              const std::string &content_type,
11686
                              ContentReceiver content_receiver,
11687
0
                              UploadProgress progress) {
11688
0
  return Put(path, Headers(), std::move(content_provider), content_type,
11689
0
             std::move(content_receiver), progress);
11690
0
}
11691
11692
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11693
0
                              const Params &params) {
11694
0
  auto query = detail::params_to_query_str(params);
11695
0
  return Put(path, headers, query, "application/x-www-form-urlencoded");
11696
0
}
11697
11698
inline Result ClientImpl::Put(const std::string &path,
11699
                              const UploadFormDataItems &items,
11700
0
                              UploadProgress progress) {
11701
0
  return Put(path, Headers(), items, progress);
11702
0
}
11703
11704
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11705
                              const UploadFormDataItems &items,
11706
0
                              UploadProgress progress) {
11707
0
  const auto &boundary = detail::make_multipart_data_boundary();
11708
0
  const auto &content_type =
11709
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11710
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11711
0
  return Put(path, headers, body, content_type, progress);
11712
0
}
11713
11714
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11715
                              const UploadFormDataItems &items,
11716
                              const std::string &boundary,
11717
0
                              UploadProgress progress) {
11718
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
11719
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
11720
0
  }
11721
0
11722
0
  const auto &content_type =
11723
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11724
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11725
0
  return Put(path, headers, body, content_type, progress);
11726
0
}
11727
11728
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11729
                              const char *body, size_t content_length,
11730
                              const std::string &content_type,
11731
0
                              UploadProgress progress) {
11732
0
  return send_with_content_provider_and_receiver(
11733
0
      "PUT", path, headers, body, content_length, nullptr, nullptr,
11734
0
      content_type, nullptr, progress);
11735
0
}
11736
11737
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11738
                              const std::string &body,
11739
                              const std::string &content_type,
11740
0
                              UploadProgress progress) {
11741
0
  return send_with_content_provider_and_receiver(
11742
0
      "PUT", path, headers, body.data(), body.size(), nullptr, nullptr,
11743
0
      content_type, nullptr, progress);
11744
0
}
11745
11746
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11747
                              size_t content_length,
11748
                              ContentProvider content_provider,
11749
                              const std::string &content_type,
11750
0
                              UploadProgress progress) {
11751
0
  return send_with_content_provider_and_receiver(
11752
0
      "PUT", path, headers, nullptr, content_length,
11753
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
11754
0
}
11755
11756
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11757
                              size_t content_length,
11758
                              ContentProvider content_provider,
11759
                              const std::string &content_type,
11760
                              ContentReceiver content_receiver,
11761
0
                              UploadProgress progress) {
11762
0
  return send_with_content_provider_and_receiver(
11763
0
      "PUT", path, headers, nullptr, content_length,
11764
0
      std::move(content_provider), nullptr, content_type,
11765
0
      std::move(content_receiver), progress);
11766
0
}
11767
11768
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11769
                              ContentProviderWithoutLength content_provider,
11770
                              const std::string &content_type,
11771
0
                              UploadProgress progress) {
11772
0
  return send_with_content_provider_and_receiver(
11773
0
      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11774
0
      content_type, nullptr, progress);
11775
0
}
11776
11777
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11778
                              ContentProviderWithoutLength content_provider,
11779
                              const std::string &content_type,
11780
                              ContentReceiver content_receiver,
11781
0
                              UploadProgress progress) {
11782
0
  return send_with_content_provider_and_receiver(
11783
0
      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11784
0
      content_type, std::move(content_receiver), progress);
11785
0
}
11786
11787
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11788
                              const UploadFormDataItems &items,
11789
                              const FormDataProviderItems &provider_items,
11790
0
                              UploadProgress progress) {
11791
0
  const auto &boundary = detail::make_multipart_data_boundary();
11792
0
  const auto &content_type =
11793
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11794
0
  return send_with_content_provider_and_receiver(
11795
0
      "PUT", path, headers, nullptr, 0, nullptr,
11796
0
      get_multipart_content_provider(boundary, items, provider_items),
11797
0
      content_type, nullptr, progress);
11798
0
}
11799
11800
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
11801
                              const std::string &body,
11802
                              const std::string &content_type,
11803
                              ContentReceiver content_receiver,
11804
0
                              DownloadProgress progress) {
11805
0
  Request req;
11806
0
  req.method = "PUT";
11807
0
  req.path = path;
11808
0
  req.headers = headers;
11809
0
  req.body = body;
11810
0
  req.content_receiver =
11811
0
      [content_receiver](const char *data, size_t data_length,
11812
0
                         size_t /*offset*/, size_t /*total_length*/) {
11813
0
        return content_receiver(data, data_length);
11814
0
      };
11815
0
  req.download_progress = std::move(progress);
11816
0
11817
0
  if (max_timeout_msec_ > 0) {
11818
0
    req.start_time_ = std::chrono::steady_clock::now();
11819
0
  }
11820
0
11821
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
11822
0
11823
0
  return send_(std::move(req));
11824
0
}
11825
11826
0
inline Result ClientImpl::Patch(const std::string &path) {
11827
0
  return Patch(path, std::string(), std::string());
11828
0
}
11829
11830
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11831
0
                                UploadProgress progress) {
11832
0
  return Patch(path, headers, nullptr, 0, std::string(), progress);
11833
0
}
11834
11835
inline Result ClientImpl::Patch(const std::string &path, const char *body,
11836
                                size_t content_length,
11837
                                const std::string &content_type,
11838
0
                                UploadProgress progress) {
11839
0
  return Patch(path, Headers(), body, content_length, content_type, progress);
11840
0
}
11841
11842
inline Result ClientImpl::Patch(const std::string &path,
11843
                                const std::string &body,
11844
                                const std::string &content_type,
11845
0
                                UploadProgress progress) {
11846
0
  return Patch(path, Headers(), body, content_type, progress);
11847
0
}
11848
11849
0
inline Result ClientImpl::Patch(const std::string &path, const Params &params) {
11850
0
  return Patch(path, Headers(), params);
11851
0
}
11852
11853
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
11854
                                ContentProvider content_provider,
11855
                                const std::string &content_type,
11856
0
                                UploadProgress progress) {
11857
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
11858
0
               content_type, progress);
11859
0
}
11860
11861
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
11862
                                ContentProvider content_provider,
11863
                                const std::string &content_type,
11864
                                ContentReceiver content_receiver,
11865
0
                                UploadProgress progress) {
11866
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
11867
0
               content_type, std::move(content_receiver), progress);
11868
0
}
11869
11870
inline Result ClientImpl::Patch(const std::string &path,
11871
                                ContentProviderWithoutLength content_provider,
11872
                                const std::string &content_type,
11873
0
                                UploadProgress progress) {
11874
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
11875
0
               progress);
11876
0
}
11877
11878
inline Result ClientImpl::Patch(const std::string &path,
11879
                                ContentProviderWithoutLength content_provider,
11880
                                const std::string &content_type,
11881
                                ContentReceiver content_receiver,
11882
0
                                UploadProgress progress) {
11883
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
11884
0
               std::move(content_receiver), progress);
11885
0
}
11886
11887
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11888
0
                                const Params &params) {
11889
0
  auto query = detail::params_to_query_str(params);
11890
0
  return Patch(path, headers, query, "application/x-www-form-urlencoded");
11891
0
}
11892
11893
inline Result ClientImpl::Patch(const std::string &path,
11894
                                const UploadFormDataItems &items,
11895
0
                                UploadProgress progress) {
11896
0
  return Patch(path, Headers(), items, progress);
11897
0
}
11898
11899
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11900
                                const UploadFormDataItems &items,
11901
0
                                UploadProgress progress) {
11902
0
  const auto &boundary = detail::make_multipart_data_boundary();
11903
0
  const auto &content_type =
11904
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11905
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11906
0
  return Patch(path, headers, body, content_type, progress);
11907
0
}
11908
11909
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11910
                                const UploadFormDataItems &items,
11911
                                const std::string &boundary,
11912
0
                                UploadProgress progress) {
11913
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
11914
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
11915
0
  }
11916
0
11917
0
  const auto &content_type =
11918
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11919
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
11920
0
  return Patch(path, headers, body, content_type, progress);
11921
0
}
11922
11923
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11924
                                const char *body, size_t content_length,
11925
                                const std::string &content_type,
11926
0
                                UploadProgress progress) {
11927
0
  return send_with_content_provider_and_receiver(
11928
0
      "PATCH", path, headers, body, content_length, nullptr, nullptr,
11929
0
      content_type, nullptr, progress);
11930
0
}
11931
11932
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11933
                                const std::string &body,
11934
                                const std::string &content_type,
11935
0
                                UploadProgress progress) {
11936
0
  return send_with_content_provider_and_receiver(
11937
0
      "PATCH", path, headers, body.data(), body.size(), nullptr, nullptr,
11938
0
      content_type, nullptr, progress);
11939
0
}
11940
11941
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11942
                                size_t content_length,
11943
                                ContentProvider content_provider,
11944
                                const std::string &content_type,
11945
0
                                UploadProgress progress) {
11946
0
  return send_with_content_provider_and_receiver(
11947
0
      "PATCH", path, headers, nullptr, content_length,
11948
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
11949
0
}
11950
11951
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11952
                                size_t content_length,
11953
                                ContentProvider content_provider,
11954
                                const std::string &content_type,
11955
                                ContentReceiver content_receiver,
11956
0
                                UploadProgress progress) {
11957
0
  return send_with_content_provider_and_receiver(
11958
0
      "PATCH", path, headers, nullptr, content_length,
11959
0
      std::move(content_provider), nullptr, content_type,
11960
0
      std::move(content_receiver), progress);
11961
0
}
11962
11963
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11964
                                ContentProviderWithoutLength content_provider,
11965
                                const std::string &content_type,
11966
0
                                UploadProgress progress) {
11967
0
  return send_with_content_provider_and_receiver(
11968
0
      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11969
0
      content_type, nullptr, progress);
11970
0
}
11971
11972
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11973
                                ContentProviderWithoutLength content_provider,
11974
                                const std::string &content_type,
11975
                                ContentReceiver content_receiver,
11976
0
                                UploadProgress progress) {
11977
0
  return send_with_content_provider_and_receiver(
11978
0
      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
11979
0
      content_type, std::move(content_receiver), progress);
11980
0
}
11981
11982
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11983
                                const UploadFormDataItems &items,
11984
                                const FormDataProviderItems &provider_items,
11985
0
                                UploadProgress progress) {
11986
0
  const auto &boundary = detail::make_multipart_data_boundary();
11987
0
  const auto &content_type =
11988
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
11989
0
  return send_with_content_provider_and_receiver(
11990
0
      "PATCH", path, headers, nullptr, 0, nullptr,
11991
0
      get_multipart_content_provider(boundary, items, provider_items),
11992
0
      content_type, nullptr, progress);
11993
0
}
11994
11995
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
11996
                                const std::string &body,
11997
                                const std::string &content_type,
11998
                                ContentReceiver content_receiver,
11999
0
                                DownloadProgress progress) {
12000
0
  Request req;
12001
0
  req.method = "PATCH";
12002
0
  req.path = path;
12003
0
  req.headers = headers;
12004
0
  req.body = body;
12005
0
  req.content_receiver =
12006
0
      [content_receiver](const char *data, size_t data_length,
12007
0
                         size_t /*offset*/, size_t /*total_length*/) {
12008
0
        return content_receiver(data, data_length);
12009
0
      };
12010
0
  req.download_progress = std::move(progress);
12011
0
12012
0
  if (max_timeout_msec_ > 0) {
12013
0
    req.start_time_ = std::chrono::steady_clock::now();
12014
0
  }
12015
0
12016
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
12017
0
12018
0
  return send_(std::move(req));
12019
0
}
12020
12021
inline Result ClientImpl::Delete(const std::string &path,
12022
0
                                 DownloadProgress progress) {
12023
0
  return Delete(path, Headers(), std::string(), std::string(), progress);
12024
0
}
12025
12026
inline Result ClientImpl::Delete(const std::string &path,
12027
                                 const Headers &headers,
12028
0
                                 DownloadProgress progress) {
12029
0
  return Delete(path, headers, std::string(), std::string(), progress);
12030
0
}
12031
12032
inline Result ClientImpl::Delete(const std::string &path, const char *body,
12033
                                 size_t content_length,
12034
                                 const std::string &content_type,
12035
0
                                 DownloadProgress progress) {
12036
0
  return Delete(path, Headers(), body, content_length, content_type, progress);
12037
0
}
12038
12039
inline Result ClientImpl::Delete(const std::string &path,
12040
                                 const std::string &body,
12041
                                 const std::string &content_type,
12042
0
                                 DownloadProgress progress) {
12043
0
  return Delete(path, Headers(), body.data(), body.size(), content_type,
12044
0
                progress);
12045
0
}
12046
12047
inline Result ClientImpl::Delete(const std::string &path,
12048
                                 const Headers &headers,
12049
                                 const std::string &body,
12050
                                 const std::string &content_type,
12051
0
                                 DownloadProgress progress) {
12052
0
  return Delete(path, headers, body.data(), body.size(), content_type,
12053
0
                progress);
12054
0
}
12055
12056
inline Result ClientImpl::Delete(const std::string &path, const Params &params,
12057
0
                                 DownloadProgress progress) {
12058
0
  return Delete(path, Headers(), params, progress);
12059
0
}
12060
12061
inline Result ClientImpl::Delete(const std::string &path,
12062
                                 const Headers &headers, const Params &params,
12063
0
                                 DownloadProgress progress) {
12064
0
  auto query = detail::params_to_query_str(params);
12065
0
  return Delete(path, headers, query, "application/x-www-form-urlencoded",
12066
0
                progress);
12067
0
}
12068
12069
inline Result ClientImpl::Delete(const std::string &path,
12070
                                 const Headers &headers, const char *body,
12071
                                 size_t content_length,
12072
                                 const std::string &content_type,
12073
0
                                 DownloadProgress progress) {
12074
0
  Request req;
12075
0
  req.method = "DELETE";
12076
0
  req.headers = headers;
12077
0
  req.path = path;
12078
0
  req.download_progress = std::move(progress);
12079
0
  if (max_timeout_msec_ > 0) {
12080
0
    req.start_time_ = std::chrono::steady_clock::now();
12081
0
  }
12082
0
12083
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
12084
0
  req.body.assign(body, content_length);
12085
0
12086
0
  return send_(std::move(req));
12087
0
}
12088
12089
0
inline Result ClientImpl::Options(const std::string &path) {
12090
0
  return Options(path, Headers());
12091
0
}
12092
12093
inline Result ClientImpl::Options(const std::string &path,
12094
0
                                  const Headers &headers) {
12095
0
  Request req;
12096
0
  req.method = "OPTIONS";
12097
0
  req.headers = headers;
12098
0
  req.path = path;
12099
0
  if (max_timeout_msec_ > 0) {
12100
0
    req.start_time_ = std::chrono::steady_clock::now();
12101
0
  }
12102
0
12103
0
  return send_(std::move(req));
12104
0
}
12105
12106
0
inline void ClientImpl::stop() {
12107
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
12108
0
12109
0
  // If there is anything ongoing right now, the ONLY thread-safe thing we can
12110
0
  // do is to shutdown_socket, so that threads using this socket suddenly
12111
0
  // discover they can't read/write any more and error out. Everything else
12112
0
  // (closing the socket, shutting ssl down) is unsafe because these actions
12113
0
  // are not thread-safe.
12114
0
  if (socket_requests_in_flight_ > 0) {
12115
0
    shutdown_socket(socket_);
12116
0
12117
0
    // Aside from that, we set a flag for the socket to be closed when we're
12118
0
    // done.
12119
0
    socket_should_be_closed_when_request_is_done_ = true;
12120
0
    return;
12121
0
  }
12122
0
12123
0
  // Otherwise, still holding the mutex, we can shut everything down ourselves
12124
0
  shutdown_ssl(socket_, true);
12125
0
  shutdown_socket(socket_);
12126
0
  close_socket(socket_);
12127
0
}
12128
12129
0
inline std::string ClientImpl::host() const { return host_; }
12130
12131
0
inline int ClientImpl::port() const { return port_; }
12132
12133
0
inline size_t ClientImpl::is_socket_open() const {
12134
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
12135
0
  return socket_.is_open();
12136
0
}
12137
12138
0
inline socket_t ClientImpl::socket() const { return socket_.sock; }
12139
12140
0
inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
12141
0
  connection_timeout_sec_ = sec;
12142
0
  connection_timeout_usec_ = usec;
12143
0
}
12144
12145
0
inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
12146
0
  read_timeout_sec_ = sec;
12147
0
  read_timeout_usec_ = usec;
12148
0
}
12149
12150
0
inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
12151
0
  write_timeout_sec_ = sec;
12152
0
  write_timeout_usec_ = usec;
12153
0
}
12154
12155
0
inline void ClientImpl::set_max_timeout(time_t msec) {
12156
0
  max_timeout_msec_ = msec;
12157
0
}
12158
12159
inline void ClientImpl::set_basic_auth(const std::string &username,
12160
0
                                       const std::string &password) {
12161
0
  basic_auth_username_ = username;
12162
0
  basic_auth_password_ = password;
12163
0
}
12164
12165
0
inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
12166
0
  bearer_token_auth_token_ = token;
12167
0
}
12168
12169
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12170
inline void ClientImpl::set_digest_auth(const std::string &username,
12171
                                        const std::string &password) {
12172
  digest_auth_username_ = username;
12173
  digest_auth_password_ = password;
12174
}
12175
#endif
12176
12177
0
inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
12178
12179
0
inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
12180
12181
0
inline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
12182
12183
inline void
12184
0
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
12185
0
  addr_map_ = std::move(addr_map);
12186
0
}
12187
12188
0
inline void ClientImpl::set_default_headers(Headers headers) {
12189
0
  default_headers_ = std::move(headers);
12190
0
}
12191
12192
inline void ClientImpl::set_header_writer(
12193
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
12194
0
  header_writer_ = writer;
12195
0
}
12196
12197
0
inline void ClientImpl::set_address_family(int family) {
12198
0
  address_family_ = family;
12199
0
}
12200
12201
0
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
12202
12203
0
inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
12204
12205
0
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
12206
0
  socket_options_ = std::move(socket_options);
12207
0
}
12208
12209
0
inline void ClientImpl::set_compress(bool on) { compress_ = on; }
12210
12211
0
inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
12212
12213
0
inline void ClientImpl::set_interface(const std::string &intf) {
12214
0
  interface_ = intf;
12215
0
}
12216
12217
0
inline void ClientImpl::set_proxy(const std::string &host, int port) {
12218
0
  proxy_host_ = host;
12219
0
  proxy_port_ = port;
12220
0
}
12221
12222
inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
12223
0
                                             const std::string &password) {
12224
0
  proxy_basic_auth_username_ = username;
12225
0
  proxy_basic_auth_password_ = password;
12226
0
}
12227
12228
0
inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
12229
0
  proxy_bearer_token_auth_token_ = token;
12230
0
}
12231
12232
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12233
inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
12234
                                              const std::string &password) {
12235
  proxy_digest_auth_username_ = username;
12236
  proxy_digest_auth_password_ = password;
12237
}
12238
12239
inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
12240
                                         const std::string &ca_cert_dir_path) {
12241
  ca_cert_file_path_ = ca_cert_file_path;
12242
  ca_cert_dir_path_ = ca_cert_dir_path;
12243
}
12244
12245
inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
12246
  if (ca_cert_store && ca_cert_store != ca_cert_store_) {
12247
    ca_cert_store_ = ca_cert_store;
12248
  }
12249
}
12250
12251
inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
12252
                                                    std::size_t size) const {
12253
  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
12254
  auto se = detail::scope_exit([&] { BIO_free_all(mem); });
12255
  if (!mem) { return nullptr; }
12256
12257
  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
12258
  if (!inf) { return nullptr; }
12259
12260
  auto cts = X509_STORE_new();
12261
  if (cts) {
12262
    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
12263
      auto itmp = sk_X509_INFO_value(inf, i);
12264
      if (!itmp) { continue; }
12265
12266
      if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
12267
      if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
12268
    }
12269
  }
12270
12271
  sk_X509_INFO_pop_free(inf, X509_INFO_free);
12272
  return cts;
12273
}
12274
12275
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
12276
  server_certificate_verification_ = enabled;
12277
}
12278
12279
inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
12280
  server_hostname_verification_ = enabled;
12281
}
12282
12283
inline void ClientImpl::set_server_certificate_verifier(
12284
    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
12285
  server_certificate_verifier_ = verifier;
12286
}
12287
#endif
12288
12289
0
inline void ClientImpl::set_logger(Logger logger) {
12290
0
  logger_ = std::move(logger);
12291
0
}
12292
12293
0
inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
12294
0
  error_logger_ = std::move(error_logger);
12295
0
}
12296
12297
/*
12298
 * SSL Implementation
12299
 */
12300
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
12301
namespace detail {
12302
12303
inline bool is_ip_address(const std::string &host) {
12304
  struct in_addr addr4;
12305
  struct in6_addr addr6;
12306
  return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
12307
         inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
12308
}
12309
12310
template <typename U, typename V>
12311
inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
12312
                    U SSL_connect_or_accept, V setup) {
12313
  SSL *ssl = nullptr;
12314
  {
12315
    std::lock_guard<std::mutex> guard(ctx_mutex);
12316
    ssl = SSL_new(ctx);
12317
  }
12318
12319
  if (ssl) {
12320
    set_nonblocking(sock, true);
12321
    auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
12322
    BIO_set_nbio(bio, 1);
12323
    SSL_set_bio(ssl, bio, bio);
12324
12325
    if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
12326
      SSL_shutdown(ssl);
12327
      {
12328
        std::lock_guard<std::mutex> guard(ctx_mutex);
12329
        SSL_free(ssl);
12330
      }
12331
      set_nonblocking(sock, false);
12332
      return nullptr;
12333
    }
12334
    BIO_set_nbio(bio, 0);
12335
    set_nonblocking(sock, false);
12336
  }
12337
12338
  return ssl;
12339
}
12340
12341
inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
12342
                       bool shutdown_gracefully) {
12343
  // sometimes we may want to skip this to try to avoid SIGPIPE if we know
12344
  // the remote has closed the network connection
12345
  // Note that it is not always possible to avoid SIGPIPE, this is merely a
12346
  // best-efforts.
12347
  if (shutdown_gracefully) {
12348
    (void)(sock);
12349
    // SSL_shutdown() returns 0 on first call (indicating close_notify alert
12350
    // sent) and 1 on subsequent call (indicating close_notify alert received)
12351
    if (SSL_shutdown(ssl) == 0) {
12352
      // Expected to return 1, but even if it doesn't, we free ssl
12353
      SSL_shutdown(ssl);
12354
    }
12355
  }
12356
12357
  std::lock_guard<std::mutex> guard(ctx_mutex);
12358
  SSL_free(ssl);
12359
}
12360
12361
template <typename U>
12362
bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
12363
                                       U ssl_connect_or_accept,
12364
                                       time_t timeout_sec, time_t timeout_usec,
12365
                                       int *ssl_error) {
12366
  auto res = 0;
12367
  while ((res = ssl_connect_or_accept(ssl)) != 1) {
12368
    auto err = SSL_get_error(ssl, res);
12369
    switch (err) {
12370
    case SSL_ERROR_WANT_READ:
12371
      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
12372
      break;
12373
    case SSL_ERROR_WANT_WRITE:
12374
      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
12375
      break;
12376
    default: break;
12377
    }
12378
    if (ssl_error) { *ssl_error = err; }
12379
    return false;
12380
  }
12381
  return true;
12382
}
12383
12384
template <typename T>
12385
inline bool process_server_socket_ssl(
12386
    const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
12387
    size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
12388
    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
12389
    time_t write_timeout_usec, T callback) {
12390
  return process_server_socket_core(
12391
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
12392
      [&](bool close_connection, bool &connection_closed) {
12393
        SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
12394
                             write_timeout_sec, write_timeout_usec);
12395
        return callback(strm, close_connection, connection_closed);
12396
      });
12397
}
12398
12399
template <typename T>
12400
inline bool process_client_socket_ssl(
12401
    SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
12402
    time_t write_timeout_sec, time_t write_timeout_usec,
12403
    time_t max_timeout_msec,
12404
    std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
12405
  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
12406
                       write_timeout_sec, write_timeout_usec, max_timeout_msec,
12407
                       start_time);
12408
  return callback(strm);
12409
}
12410
12411
// SSL socket stream implementation
12412
inline SSLSocketStream::SSLSocketStream(
12413
    socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
12414
    time_t write_timeout_sec, time_t write_timeout_usec,
12415
    time_t max_timeout_msec,
12416
    std::chrono::time_point<std::chrono::steady_clock> start_time)
12417
    : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
12418
      read_timeout_usec_(read_timeout_usec),
12419
      write_timeout_sec_(write_timeout_sec),
12420
      write_timeout_usec_(write_timeout_usec),
12421
      max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
12422
  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
12423
}
12424
12425
inline SSLSocketStream::~SSLSocketStream() = default;
12426
12427
inline bool SSLSocketStream::is_readable() const {
12428
  return SSL_pending(ssl_) > 0;
12429
}
12430
12431
inline bool SSLSocketStream::wait_readable() const {
12432
  if (max_timeout_msec_ <= 0) {
12433
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
12434
  }
12435
12436
  time_t read_timeout_sec;
12437
  time_t read_timeout_usec;
12438
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
12439
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
12440
12441
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
12442
}
12443
12444
inline bool SSLSocketStream::wait_writable() const {
12445
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
12446
         is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
12447
}
12448
12449
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
12450
  if (SSL_pending(ssl_) > 0) {
12451
    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
12452
    if (ret == 0) { error_ = Error::ConnectionClosed; }
12453
    return ret;
12454
  } else if (wait_readable()) {
12455
    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
12456
    if (ret < 0) {
12457
      auto err = SSL_get_error(ssl_, ret);
12458
      auto n = 1000;
12459
#ifdef _WIN32
12460
      while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
12461
                          (err == SSL_ERROR_SYSCALL &&
12462
                           WSAGetLastError() == WSAETIMEDOUT))) {
12463
#else
12464
      while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
12465
#endif
12466
        if (SSL_pending(ssl_) > 0) {
12467
          return SSL_read(ssl_, ptr, static_cast<int>(size));
12468
        } else if (wait_readable()) {
12469
          std::this_thread::sleep_for(std::chrono::microseconds{10});
12470
          ret = SSL_read(ssl_, ptr, static_cast<int>(size));
12471
          if (ret >= 0) { return ret; }
12472
          err = SSL_get_error(ssl_, ret);
12473
        } else {
12474
          break;
12475
        }
12476
      }
12477
      assert(ret < 0);
12478
    } else if (ret == 0) {
12479
      error_ = Error::ConnectionClosed;
12480
    }
12481
    return ret;
12482
  } else {
12483
    error_ = Error::Timeout;
12484
    return -1;
12485
  }
12486
}
12487
12488
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
12489
  if (wait_writable()) {
12490
    auto handle_size = static_cast<int>(
12491
        std::min<size_t>(size, (std::numeric_limits<int>::max)()));
12492
12493
    auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
12494
    if (ret < 0) {
12495
      auto err = SSL_get_error(ssl_, ret);
12496
      auto n = 1000;
12497
#ifdef _WIN32
12498
      while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
12499
                          (err == SSL_ERROR_SYSCALL &&
12500
                           WSAGetLastError() == WSAETIMEDOUT))) {
12501
#else
12502
      while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
12503
#endif
12504
        if (wait_writable()) {
12505
          std::this_thread::sleep_for(std::chrono::microseconds{10});
12506
          ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
12507
          if (ret >= 0) { return ret; }
12508
          err = SSL_get_error(ssl_, ret);
12509
        } else {
12510
          break;
12511
        }
12512
      }
12513
      assert(ret < 0);
12514
    }
12515
    return ret;
12516
  }
12517
  return -1;
12518
}
12519
12520
inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
12521
                                                    int &port) const {
12522
  detail::get_remote_ip_and_port(sock_, ip, port);
12523
}
12524
12525
inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
12526
                                                   int &port) const {
12527
  detail::get_local_ip_and_port(sock_, ip, port);
12528
}
12529
12530
inline socket_t SSLSocketStream::socket() const { return sock_; }
12531
12532
inline time_t SSLSocketStream::duration() const {
12533
  return std::chrono::duration_cast<std::chrono::milliseconds>(
12534
             std::chrono::steady_clock::now() - start_time_)
12535
      .count();
12536
}
12537
12538
} // namespace detail
12539
12540
// SSL HTTP server implementation
12541
inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
12542
                            const char *client_ca_cert_file_path,
12543
                            const char *client_ca_cert_dir_path,
12544
                            const char *private_key_password) {
12545
  ctx_ = SSL_CTX_new(TLS_server_method());
12546
12547
  if (ctx_) {
12548
    SSL_CTX_set_options(ctx_,
12549
                        SSL_OP_NO_COMPRESSION |
12550
                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
12551
12552
    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
12553
12554
    if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
12555
      SSL_CTX_set_default_passwd_cb_userdata(
12556
          ctx_,
12557
          reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
12558
    }
12559
12560
    if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
12561
        SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
12562
            1 ||
12563
        SSL_CTX_check_private_key(ctx_) != 1) {
12564
      last_ssl_error_ = static_cast<int>(ERR_get_error());
12565
      SSL_CTX_free(ctx_);
12566
      ctx_ = nullptr;
12567
    } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
12568
      SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
12569
                                    client_ca_cert_dir_path);
12570
12571
      // Set client CA list to be sent to clients during TLS handshake
12572
      if (client_ca_cert_file_path) {
12573
        auto ca_list = SSL_load_client_CA_file(client_ca_cert_file_path);
12574
        if (ca_list != nullptr) {
12575
          SSL_CTX_set_client_CA_list(ctx_, ca_list);
12576
        } else {
12577
          // Failed to load client CA list, but we continue since
12578
          // SSL_CTX_load_verify_locations already succeeded and
12579
          // certificate verification will still work
12580
          last_ssl_error_ = static_cast<int>(ERR_get_error());
12581
        }
12582
      }
12583
12584
      SSL_CTX_set_verify(
12585
          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
12586
    }
12587
  }
12588
}
12589
12590
inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
12591
                            X509_STORE *client_ca_cert_store) {
12592
  ctx_ = SSL_CTX_new(TLS_server_method());
12593
12594
  if (ctx_) {
12595
    SSL_CTX_set_options(ctx_,
12596
                        SSL_OP_NO_COMPRESSION |
12597
                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
12598
12599
    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
12600
12601
    if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
12602
        SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
12603
      SSL_CTX_free(ctx_);
12604
      ctx_ = nullptr;
12605
    } else if (client_ca_cert_store) {
12606
      SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
12607
12608
      // Extract CA names from the store and set them as the client CA list
12609
      auto ca_list = extract_ca_names_from_x509_store(client_ca_cert_store);
12610
      if (ca_list) {
12611
        SSL_CTX_set_client_CA_list(ctx_, ca_list);
12612
      } else {
12613
        // Failed to extract CA names, record the error
12614
        last_ssl_error_ = static_cast<int>(ERR_get_error());
12615
      }
12616
12617
      SSL_CTX_set_verify(
12618
          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
12619
    }
12620
  }
12621
}
12622
12623
inline SSLServer::SSLServer(
12624
    const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
12625
  ctx_ = SSL_CTX_new(TLS_method());
12626
  if (ctx_) {
12627
    if (!setup_ssl_ctx_callback(*ctx_)) {
12628
      SSL_CTX_free(ctx_);
12629
      ctx_ = nullptr;
12630
    }
12631
  }
12632
}
12633
12634
inline SSLServer::~SSLServer() {
12635
  if (ctx_) { SSL_CTX_free(ctx_); }
12636
}
12637
12638
inline bool SSLServer::is_valid() const { return ctx_; }
12639
12640
inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
12641
12642
inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
12643
                                    X509_STORE *client_ca_cert_store) {
12644
12645
  std::lock_guard<std::mutex> guard(ctx_mutex_);
12646
12647
  SSL_CTX_use_certificate(ctx_, cert);
12648
  SSL_CTX_use_PrivateKey(ctx_, private_key);
12649
12650
  if (client_ca_cert_store != nullptr) {
12651
    SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
12652
  }
12653
}
12654
12655
inline bool SSLServer::process_and_close_socket(socket_t sock) {
12656
  auto ssl = detail::ssl_new(
12657
      sock, ctx_, ctx_mutex_,
12658
      [&](SSL *ssl2) {
12659
        return detail::ssl_connect_or_accept_nonblocking(
12660
            sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,
12661
            &last_ssl_error_);
12662
      },
12663
      [](SSL * /*ssl2*/) { return true; });
12664
12665
  auto ret = false;
12666
  if (ssl) {
12667
    std::string remote_addr;
12668
    int remote_port = 0;
12669
    detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
12670
12671
    std::string local_addr;
12672
    int local_port = 0;
12673
    detail::get_local_ip_and_port(sock, local_addr, local_port);
12674
12675
    ret = detail::process_server_socket_ssl(
12676
        svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
12677
        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12678
        write_timeout_usec_,
12679
        [&](Stream &strm, bool close_connection, bool &connection_closed) {
12680
          return process_request(strm, remote_addr, remote_port, local_addr,
12681
                                 local_port, close_connection,
12682
                                 connection_closed,
12683
                                 [&](Request &req) { req.ssl = ssl; });
12684
        });
12685
12686
    // Shutdown gracefully if the result seemed successful, non-gracefully if
12687
    // the connection appeared to be closed.
12688
    const bool shutdown_gracefully = ret;
12689
    detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
12690
  }
12691
12692
  detail::shutdown_socket(sock);
12693
  detail::close_socket(sock);
12694
  return ret;
12695
}
12696
12697
inline STACK_OF(X509_NAME) * SSLServer::extract_ca_names_from_x509_store(
12698
                                 X509_STORE *store) {
12699
  if (!store) { return nullptr; }
12700
12701
  auto ca_list = sk_X509_NAME_new_null();
12702
  if (!ca_list) { return nullptr; }
12703
12704
  // Get all objects from the store
12705
  auto objs = X509_STORE_get0_objects(store);
12706
  if (!objs) {
12707
    sk_X509_NAME_free(ca_list);
12708
    return nullptr;
12709
  }
12710
12711
  // Iterate through objects and extract certificate subject names
12712
  for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) {
12713
    auto obj = sk_X509_OBJECT_value(objs, i);
12714
    if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
12715
      auto cert = X509_OBJECT_get0_X509(obj);
12716
      if (cert) {
12717
        auto subject = X509_get_subject_name(cert);
12718
        if (subject) {
12719
          auto name_dup = X509_NAME_dup(subject);
12720
          if (name_dup) { sk_X509_NAME_push(ca_list, name_dup); }
12721
        }
12722
      }
12723
    }
12724
  }
12725
12726
  // If no names were extracted, free the list and return nullptr
12727
  if (sk_X509_NAME_num(ca_list) == 0) {
12728
    sk_X509_NAME_free(ca_list);
12729
    return nullptr;
12730
  }
12731
12732
  return ca_list;
12733
}
12734
12735
// SSL HTTP client implementation
12736
inline SSLClient::SSLClient(const std::string &host)
12737
    : SSLClient(host, 443, std::string(), std::string()) {}
12738
12739
inline SSLClient::SSLClient(const std::string &host, int port)
12740
    : SSLClient(host, port, std::string(), std::string()) {}
12741
12742
inline SSLClient::SSLClient(const std::string &host, int port,
12743
                            const std::string &client_cert_path,
12744
                            const std::string &client_key_path,
12745
                            const std::string &private_key_password)
12746
    : ClientImpl(host, port, client_cert_path, client_key_path) {
12747
  ctx_ = SSL_CTX_new(TLS_client_method());
12748
12749
  SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
12750
12751
  detail::split(&host_[0], &host_[host_.size()], '.',
12752
                [&](const char *b, const char *e) {
12753
                  host_components_.emplace_back(b, e);
12754
                });
12755
12756
  if (!client_cert_path.empty() && !client_key_path.empty()) {
12757
    if (!private_key_password.empty()) {
12758
      SSL_CTX_set_default_passwd_cb_userdata(
12759
          ctx_, reinterpret_cast<void *>(
12760
                    const_cast<char *>(private_key_password.c_str())));
12761
    }
12762
12763
    if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
12764
                                     SSL_FILETYPE_PEM) != 1 ||
12765
        SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
12766
                                    SSL_FILETYPE_PEM) != 1) {
12767
      last_openssl_error_ = ERR_get_error();
12768
      SSL_CTX_free(ctx_);
12769
      ctx_ = nullptr;
12770
    }
12771
  }
12772
}
12773
12774
inline SSLClient::SSLClient(const std::string &host, int port,
12775
                            X509 *client_cert, EVP_PKEY *client_key,
12776
                            const std::string &private_key_password)
12777
    : ClientImpl(host, port) {
12778
  ctx_ = SSL_CTX_new(TLS_client_method());
12779
12780
  detail::split(&host_[0], &host_[host_.size()], '.',
12781
                [&](const char *b, const char *e) {
12782
                  host_components_.emplace_back(b, e);
12783
                });
12784
12785
  if (client_cert != nullptr && client_key != nullptr) {
12786
    if (!private_key_password.empty()) {
12787
      SSL_CTX_set_default_passwd_cb_userdata(
12788
          ctx_, reinterpret_cast<void *>(
12789
                    const_cast<char *>(private_key_password.c_str())));
12790
    }
12791
12792
    if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
12793
        SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
12794
      last_openssl_error_ = ERR_get_error();
12795
      SSL_CTX_free(ctx_);
12796
      ctx_ = nullptr;
12797
    }
12798
  }
12799
}
12800
12801
inline SSLClient::~SSLClient() {
12802
  if (ctx_) { SSL_CTX_free(ctx_); }
12803
  // Make sure to shut down SSL since shutdown_ssl will resolve to the
12804
  // base function rather than the derived function once we get to the
12805
  // base class destructor, and won't free the SSL (causing a leak).
12806
  shutdown_ssl_impl(socket_, true);
12807
}
12808
12809
inline bool SSLClient::is_valid() const { return ctx_; }
12810
12811
inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
12812
  if (ca_cert_store) {
12813
    if (ctx_) {
12814
      if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
12815
        // Free memory allocated for old cert and use new store
12816
        // `ca_cert_store`
12817
        SSL_CTX_set_cert_store(ctx_, ca_cert_store);
12818
        ca_cert_store_ = ca_cert_store;
12819
      }
12820
    } else {
12821
      X509_STORE_free(ca_cert_store);
12822
    }
12823
  }
12824
}
12825
12826
inline void SSLClient::load_ca_cert_store(const char *ca_cert,
12827
                                          std::size_t size) {
12828
  set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
12829
}
12830
12831
inline long SSLClient::get_openssl_verify_result() const {
12832
  return verify_result_;
12833
}
12834
12835
inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
12836
12837
inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
12838
  if (!is_valid()) {
12839
    error = Error::SSLConnection;
12840
    return false;
12841
  }
12842
  return ClientImpl::create_and_connect_socket(socket, error);
12843
}
12844
12845
// Assumes that socket_mutex_ is locked and that there are no requests in
12846
// flight
12847
inline bool SSLClient::connect_with_proxy(
12848
    Socket &socket,
12849
    std::chrono::time_point<std::chrono::steady_clock> start_time,
12850
    Response &res, bool &success, Error &error) {
12851
  success = true;
12852
  Response proxy_res;
12853
  if (!detail::process_client_socket(
12854
          socket.sock, read_timeout_sec_, read_timeout_usec_,
12855
          write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
12856
          start_time, [&](Stream &strm) {
12857
            Request req2;
12858
            req2.method = "CONNECT";
12859
            req2.path =
12860
                detail::make_host_and_port_string_always_port(host_, port_);
12861
            if (max_timeout_msec_ > 0) {
12862
              req2.start_time_ = std::chrono::steady_clock::now();
12863
            }
12864
            return process_request(strm, req2, proxy_res, false, error);
12865
          })) {
12866
    // Thread-safe to close everything because we are assuming there are no
12867
    // requests in flight
12868
    shutdown_ssl(socket, true);
12869
    shutdown_socket(socket);
12870
    close_socket(socket);
12871
    success = false;
12872
    return false;
12873
  }
12874
12875
  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
12876
    if (!proxy_digest_auth_username_.empty() &&
12877
        !proxy_digest_auth_password_.empty()) {
12878
      std::map<std::string, std::string> auth;
12879
      if (detail::parse_www_authenticate(proxy_res, auth, true)) {
12880
        // Close the current socket and create a new one for the authenticated
12881
        // request
12882
        shutdown_ssl(socket, true);
12883
        shutdown_socket(socket);
12884
        close_socket(socket);
12885
12886
        // Create a new socket for the authenticated CONNECT request
12887
        if (!ensure_socket_connection(socket, error)) {
12888
          success = false;
12889
          output_error_log(error, nullptr);
12890
          return false;
12891
        }
12892
12893
        proxy_res = Response();
12894
        if (!detail::process_client_socket(
12895
                socket.sock, read_timeout_sec_, read_timeout_usec_,
12896
                write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
12897
                start_time, [&](Stream &strm) {
12898
                  Request req3;
12899
                  req3.method = "CONNECT";
12900
                  req3.path = detail::make_host_and_port_string_always_port(
12901
                      host_, port_);
12902
                  req3.headers.insert(detail::make_digest_authentication_header(
12903
                      req3, auth, 1, detail::random_string(10),
12904
                      proxy_digest_auth_username_, proxy_digest_auth_password_,
12905
                      true));
12906
                  if (max_timeout_msec_ > 0) {
12907
                    req3.start_time_ = std::chrono::steady_clock::now();
12908
                  }
12909
                  return process_request(strm, req3, proxy_res, false, error);
12910
                })) {
12911
          // Thread-safe to close everything because we are assuming there are
12912
          // no requests in flight
12913
          shutdown_ssl(socket, true);
12914
          shutdown_socket(socket);
12915
          close_socket(socket);
12916
          success = false;
12917
          return false;
12918
        }
12919
      }
12920
    }
12921
  }
12922
12923
  // If status code is not 200, proxy request is failed.
12924
  // Set error to ProxyConnection and return proxy response
12925
  // as the response of the request
12926
  if (proxy_res.status != StatusCode::OK_200) {
12927
    error = Error::ProxyConnection;
12928
    output_error_log(error, nullptr);
12929
    res = std::move(proxy_res);
12930
    // Thread-safe to close everything because we are assuming there are
12931
    // no requests in flight
12932
    shutdown_ssl(socket, true);
12933
    shutdown_socket(socket);
12934
    close_socket(socket);
12935
    return false;
12936
  }
12937
12938
  return true;
12939
}
12940
12941
inline bool SSLClient::load_certs() {
12942
  auto ret = true;
12943
12944
  std::call_once(initialize_cert_, [&]() {
12945
    std::lock_guard<std::mutex> guard(ctx_mutex_);
12946
    if (!ca_cert_file_path_.empty()) {
12947
      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
12948
                                         nullptr)) {
12949
        last_openssl_error_ = ERR_get_error();
12950
        ret = false;
12951
      }
12952
    } else if (!ca_cert_dir_path_.empty()) {
12953
      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
12954
                                         ca_cert_dir_path_.c_str())) {
12955
        last_openssl_error_ = ERR_get_error();
12956
        ret = false;
12957
      }
12958
    } else {
12959
      auto loaded = false;
12960
#ifdef _WIN32
12961
      loaded =
12962
          detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
12963
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC
12964
      loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
12965
#endif // _WIN32
12966
      if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
12967
    }
12968
  });
12969
12970
  return ret;
12971
}
12972
12973
inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
12974
  auto ssl = detail::ssl_new(
12975
      socket.sock, ctx_, ctx_mutex_,
12976
      [&](SSL *ssl2) {
12977
        if (server_certificate_verification_) {
12978
          if (!load_certs()) {
12979
            error = Error::SSLLoadingCerts;
12980
            output_error_log(error, nullptr);
12981
            return false;
12982
          }
12983
          SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
12984
        }
12985
12986
        if (!detail::ssl_connect_or_accept_nonblocking(
12987
                socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
12988
                connection_timeout_usec_, &last_ssl_error_)) {
12989
          error = Error::SSLConnection;
12990
          output_error_log(error, nullptr);
12991
          return false;
12992
        }
12993
12994
        if (server_certificate_verification_) {
12995
          auto verification_status = SSLVerifierResponse::NoDecisionMade;
12996
12997
          if (server_certificate_verifier_) {
12998
            verification_status = server_certificate_verifier_(ssl2);
12999
          }
13000
13001
          if (verification_status == SSLVerifierResponse::CertificateRejected) {
13002
            last_openssl_error_ = ERR_get_error();
13003
            error = Error::SSLServerVerification;
13004
            output_error_log(error, nullptr);
13005
            return false;
13006
          }
13007
13008
          if (verification_status == SSLVerifierResponse::NoDecisionMade) {
13009
            verify_result_ = SSL_get_verify_result(ssl2);
13010
13011
            if (verify_result_ != X509_V_OK) {
13012
              last_openssl_error_ = static_cast<unsigned long>(verify_result_);
13013
              error = Error::SSLServerVerification;
13014
              output_error_log(error, nullptr);
13015
              return false;
13016
            }
13017
13018
            auto server_cert = SSL_get1_peer_certificate(ssl2);
13019
            auto se = detail::scope_exit([&] { X509_free(server_cert); });
13020
13021
            if (server_cert == nullptr) {
13022
              last_openssl_error_ = ERR_get_error();
13023
              error = Error::SSLServerVerification;
13024
              output_error_log(error, nullptr);
13025
              return false;
13026
            }
13027
13028
            if (server_hostname_verification_) {
13029
              if (!verify_host(server_cert)) {
13030
                last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;
13031
                error = Error::SSLServerHostnameVerification;
13032
                output_error_log(error, nullptr);
13033
                return false;
13034
              }
13035
            }
13036
          }
13037
        }
13038
13039
        return true;
13040
      },
13041
      [&](SSL *ssl2) {
13042
        // Set SNI only if host is not IP address
13043
        if (!detail::is_ip_address(host_)) {
13044
#if defined(OPENSSL_IS_BORINGSSL)
13045
          SSL_set_tlsext_host_name(ssl2, host_.c_str());
13046
#else
13047
          // NOTE: Direct call instead of using the OpenSSL macro to suppress
13048
          // -Wold-style-cast warning
13049
          SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,
13050
                   TLSEXT_NAMETYPE_host_name,
13051
                   static_cast<void *>(const_cast<char *>(host_.c_str())));
13052
#endif
13053
        }
13054
        return true;
13055
      });
13056
13057
  if (ssl) {
13058
    socket.ssl = ssl;
13059
    return true;
13060
  }
13061
13062
  if (ctx_ == nullptr) {
13063
    error = Error::SSLConnection;
13064
    last_openssl_error_ = ERR_get_error();
13065
  }
13066
13067
  shutdown_socket(socket);
13068
  close_socket(socket);
13069
  return false;
13070
}
13071
13072
inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
13073
  shutdown_ssl_impl(socket, shutdown_gracefully);
13074
}
13075
13076
inline void SSLClient::shutdown_ssl_impl(Socket &socket,
13077
                                         bool shutdown_gracefully) {
13078
  if (socket.sock == INVALID_SOCKET) {
13079
    assert(socket.ssl == nullptr);
13080
    return;
13081
  }
13082
  if (socket.ssl) {
13083
    detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
13084
                       shutdown_gracefully);
13085
    socket.ssl = nullptr;
13086
  }
13087
  assert(socket.ssl == nullptr);
13088
}
13089
13090
inline bool SSLClient::process_socket(
13091
    const Socket &socket,
13092
    std::chrono::time_point<std::chrono::steady_clock> start_time,
13093
    std::function<bool(Stream &strm)> callback) {
13094
  assert(socket.ssl);
13095
  return detail::process_client_socket_ssl(
13096
      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
13097
      write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
13098
      std::move(callback));
13099
}
13100
13101
inline bool SSLClient::is_ssl() const { return true; }
13102
13103
inline bool SSLClient::verify_host(X509 *server_cert) const {
13104
  /* Quote from RFC2818 section 3.1 "Server Identity"
13105
13106
     If a subjectAltName extension of type dNSName is present, that MUST
13107
     be used as the identity. Otherwise, the (most specific) Common Name
13108
     field in the Subject field of the certificate MUST be used. Although
13109
     the use of the Common Name is existing practice, it is deprecated and
13110
     Certification Authorities are encouraged to use the dNSName instead.
13111
13112
     Matching is performed using the matching rules specified by
13113
     [RFC2459].  If more than one identity of a given type is present in
13114
     the certificate (e.g., more than one dNSName name, a match in any one
13115
     of the set is considered acceptable.) Names may contain the wildcard
13116
     character * which is considered to match any single domain name
13117
     component or component fragment. E.g., *.a.com matches foo.a.com but
13118
     not bar.foo.a.com. f*.com matches foo.com but not bar.com.
13119
13120
     In some cases, the URI is specified as an IP address rather than a
13121
     hostname. In this case, the iPAddress subjectAltName must be present
13122
     in the certificate and must exactly match the IP in the URI.
13123
13124
  */
13125
  return verify_host_with_subject_alt_name(server_cert) ||
13126
         verify_host_with_common_name(server_cert);
13127
}
13128
13129
inline bool
13130
SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
13131
  auto ret = false;
13132
13133
  auto type = GEN_DNS;
13134
13135
  struct in6_addr addr6 = {};
13136
  struct in_addr addr = {};
13137
  size_t addr_len = 0;
13138
13139
#ifndef __MINGW32__
13140
  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
13141
    type = GEN_IPADD;
13142
    addr_len = sizeof(struct in6_addr);
13143
  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
13144
    type = GEN_IPADD;
13145
    addr_len = sizeof(struct in_addr);
13146
  }
13147
#endif
13148
13149
  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
13150
      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
13151
13152
  if (alt_names) {
13153
    auto dsn_matched = false;
13154
    auto ip_matched = false;
13155
13156
    auto count = sk_GENERAL_NAME_num(alt_names);
13157
13158
    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
13159
      auto val = sk_GENERAL_NAME_value(alt_names, i);
13160
      if (!val || val->type != type) { continue; }
13161
13162
      auto name =
13163
          reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
13164
      if (name == nullptr) { continue; }
13165
13166
      auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
13167
13168
      switch (type) {
13169
      case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
13170
13171
      case GEN_IPADD:
13172
        if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
13173
          ip_matched = true;
13174
        }
13175
        break;
13176
      }
13177
    }
13178
13179
    if (dsn_matched || ip_matched) { ret = true; }
13180
  }
13181
13182
  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
13183
      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
13184
  return ret;
13185
}
13186
13187
inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
13188
  const auto subject_name = X509_get_subject_name(server_cert);
13189
13190
  if (subject_name != nullptr) {
13191
    char name[BUFSIZ];
13192
    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
13193
                                              name, sizeof(name));
13194
13195
    if (name_len != -1) {
13196
      return check_host_name(name, static_cast<size_t>(name_len));
13197
    }
13198
  }
13199
13200
  return false;
13201
}
13202
13203
inline bool SSLClient::check_host_name(const char *pattern,
13204
                                       size_t pattern_len) const {
13205
  if (host_.size() == pattern_len && host_ == pattern) { return true; }
13206
13207
  // Wildcard match
13208
  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
13209
  std::vector<std::string> pattern_components;
13210
  detail::split(&pattern[0], &pattern[pattern_len], '.',
13211
                [&](const char *b, const char *e) {
13212
                  pattern_components.emplace_back(b, e);
13213
                });
13214
13215
  if (host_components_.size() != pattern_components.size()) { return false; }
13216
13217
  auto itr = pattern_components.begin();
13218
  for (const auto &h : host_components_) {
13219
    auto &p = *itr;
13220
    if (p != h && p != "*") {
13221
      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
13222
                            !p.compare(0, p.size() - 1, h));
13223
      if (!partial_match) { return false; }
13224
    }
13225
    ++itr;
13226
  }
13227
13228
  return true;
13229
}
13230
#endif
13231
13232
// Universal client implementation
13233
inline Client::Client(const std::string &scheme_host_port)
13234
    : Client(scheme_host_port, std::string(), std::string()) {}
13235
13236
inline Client::Client(const std::string &scheme_host_port,
13237
                      const std::string &client_cert_path,
13238
                      const std::string &client_key_path) {
13239
  const static std::regex re(
13240
      R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
13241
13242
  std::smatch m;
13243
  if (std::regex_match(scheme_host_port, m, re)) {
13244
    auto scheme = m[1].str();
13245
13246
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13247
    if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
13248
#else
13249
    if (!scheme.empty() && scheme != "http") {
13250
#endif
13251
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
13252
      std::string msg = "'" + scheme + "' scheme is not supported.";
13253
      throw std::invalid_argument(msg);
13254
#endif
13255
      return;
13256
    }
13257
13258
    auto is_ssl = scheme == "https";
13259
13260
    auto host = m[2].str();
13261
    if (host.empty()) { host = m[3].str(); }
13262
13263
    auto port_str = m[4].str();
13264
    auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
13265
13266
    if (is_ssl) {
13267
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13268
      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
13269
                                            client_key_path);
13270
      is_ssl_ = is_ssl;
13271
#endif
13272
    } else {
13273
      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
13274
                                             client_key_path);
13275
    }
13276
  } else {
13277
    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
13278
    // if port param below changes.
13279
    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
13280
                                           client_cert_path, client_key_path);
13281
  }
13282
} // namespace detail
13283
13284
inline Client::Client(const std::string &host, int port)
13285
    : cli_(detail::make_unique<ClientImpl>(host, port)) {}
13286
13287
inline Client::Client(const std::string &host, int port,
13288
                      const std::string &client_cert_path,
13289
                      const std::string &client_key_path)
13290
    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
13291
                                           client_key_path)) {}
13292
13293
inline Client::~Client() = default;
13294
13295
0
inline bool Client::is_valid() const {
13296
0
  return cli_ != nullptr && cli_->is_valid();
13297
0
}
13298
13299
0
inline Result Client::Get(const std::string &path, DownloadProgress progress) {
13300
0
  return cli_->Get(path, std::move(progress));
13301
0
}
13302
inline Result Client::Get(const std::string &path, const Headers &headers,
13303
0
                          DownloadProgress progress) {
13304
0
  return cli_->Get(path, headers, std::move(progress));
13305
0
}
13306
inline Result Client::Get(const std::string &path,
13307
                          ContentReceiver content_receiver,
13308
0
                          DownloadProgress progress) {
13309
0
  return cli_->Get(path, std::move(content_receiver), std::move(progress));
13310
0
}
13311
inline Result Client::Get(const std::string &path, const Headers &headers,
13312
                          ContentReceiver content_receiver,
13313
0
                          DownloadProgress progress) {
13314
0
  return cli_->Get(path, headers, std::move(content_receiver),
13315
0
                   std::move(progress));
13316
0
}
13317
inline Result Client::Get(const std::string &path,
13318
                          ResponseHandler response_handler,
13319
                          ContentReceiver content_receiver,
13320
0
                          DownloadProgress progress) {
13321
0
  return cli_->Get(path, std::move(response_handler),
13322
0
                   std::move(content_receiver), std::move(progress));
13323
0
}
13324
inline Result Client::Get(const std::string &path, const Headers &headers,
13325
                          ResponseHandler response_handler,
13326
                          ContentReceiver content_receiver,
13327
0
                          DownloadProgress progress) {
13328
0
  return cli_->Get(path, headers, std::move(response_handler),
13329
0
                   std::move(content_receiver), std::move(progress));
13330
0
}
13331
inline Result Client::Get(const std::string &path, const Params &params,
13332
0
                          const Headers &headers, DownloadProgress progress) {
13333
0
  return cli_->Get(path, params, headers, std::move(progress));
13334
0
}
13335
inline Result Client::Get(const std::string &path, const Params &params,
13336
                          const Headers &headers,
13337
                          ContentReceiver content_receiver,
13338
0
                          DownloadProgress progress) {
13339
0
  return cli_->Get(path, params, headers, std::move(content_receiver),
13340
0
                   std::move(progress));
13341
0
}
13342
inline Result Client::Get(const std::string &path, const Params &params,
13343
                          const Headers &headers,
13344
                          ResponseHandler response_handler,
13345
                          ContentReceiver content_receiver,
13346
0
                          DownloadProgress progress) {
13347
0
  return cli_->Get(path, params, headers, std::move(response_handler),
13348
0
                   std::move(content_receiver), std::move(progress));
13349
0
}
13350
13351
0
inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
13352
0
inline Result Client::Head(const std::string &path, const Headers &headers) {
13353
0
  return cli_->Head(path, headers);
13354
0
}
13355
13356
0
inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
13357
0
inline Result Client::Post(const std::string &path, const Headers &headers) {
13358
0
  return cli_->Post(path, headers);
13359
0
}
13360
inline Result Client::Post(const std::string &path, const char *body,
13361
                           size_t content_length,
13362
                           const std::string &content_type,
13363
0
                           UploadProgress progress) {
13364
0
  return cli_->Post(path, body, content_length, content_type, progress);
13365
0
}
13366
inline Result Client::Post(const std::string &path, const Headers &headers,
13367
                           const char *body, size_t content_length,
13368
                           const std::string &content_type,
13369
0
                           UploadProgress progress) {
13370
0
  return cli_->Post(path, headers, body, content_length, content_type,
13371
0
                    progress);
13372
0
}
13373
inline Result Client::Post(const std::string &path, const std::string &body,
13374
                           const std::string &content_type,
13375
0
                           UploadProgress progress) {
13376
0
  return cli_->Post(path, body, content_type, progress);
13377
0
}
13378
inline Result Client::Post(const std::string &path, const Headers &headers,
13379
                           const std::string &body,
13380
                           const std::string &content_type,
13381
0
                           UploadProgress progress) {
13382
0
  return cli_->Post(path, headers, body, content_type, progress);
13383
0
}
13384
inline Result Client::Post(const std::string &path, size_t content_length,
13385
                           ContentProvider content_provider,
13386
                           const std::string &content_type,
13387
0
                           UploadProgress progress) {
13388
0
  return cli_->Post(path, content_length, std::move(content_provider),
13389
0
                    content_type, progress);
13390
0
}
13391
inline Result Client::Post(const std::string &path, size_t content_length,
13392
                           ContentProvider content_provider,
13393
                           const std::string &content_type,
13394
                           ContentReceiver content_receiver,
13395
0
                           UploadProgress progress) {
13396
0
  return cli_->Post(path, content_length, std::move(content_provider),
13397
0
                    content_type, std::move(content_receiver), progress);
13398
0
}
13399
inline Result Client::Post(const std::string &path,
13400
                           ContentProviderWithoutLength content_provider,
13401
                           const std::string &content_type,
13402
0
                           UploadProgress progress) {
13403
0
  return cli_->Post(path, std::move(content_provider), content_type, progress);
13404
0
}
13405
inline Result Client::Post(const std::string &path,
13406
                           ContentProviderWithoutLength content_provider,
13407
                           const std::string &content_type,
13408
                           ContentReceiver content_receiver,
13409
0
                           UploadProgress progress) {
13410
0
  return cli_->Post(path, std::move(content_provider), content_type,
13411
0
                    std::move(content_receiver), progress);
13412
0
}
13413
inline Result Client::Post(const std::string &path, const Headers &headers,
13414
                           size_t content_length,
13415
                           ContentProvider content_provider,
13416
                           const std::string &content_type,
13417
0
                           UploadProgress progress) {
13418
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
13419
0
                    content_type, progress);
13420
0
}
13421
inline Result Client::Post(const std::string &path, const Headers &headers,
13422
                           size_t content_length,
13423
                           ContentProvider content_provider,
13424
                           const std::string &content_type,
13425
                           ContentReceiver content_receiver,
13426
0
                           DownloadProgress progress) {
13427
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
13428
0
                    content_type, std::move(content_receiver), progress);
13429
0
}
13430
inline Result Client::Post(const std::string &path, const Headers &headers,
13431
                           ContentProviderWithoutLength content_provider,
13432
                           const std::string &content_type,
13433
0
                           UploadProgress progress) {
13434
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
13435
0
                    progress);
13436
0
}
13437
inline Result Client::Post(const std::string &path, const Headers &headers,
13438
                           ContentProviderWithoutLength content_provider,
13439
                           const std::string &content_type,
13440
                           ContentReceiver content_receiver,
13441
0
                           DownloadProgress progress) {
13442
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
13443
0
                    std::move(content_receiver), progress);
13444
0
}
13445
0
inline Result Client::Post(const std::string &path, const Params &params) {
13446
0
  return cli_->Post(path, params);
13447
0
}
13448
inline Result Client::Post(const std::string &path, const Headers &headers,
13449
0
                           const Params &params) {
13450
0
  return cli_->Post(path, headers, params);
13451
0
}
13452
inline Result Client::Post(const std::string &path,
13453
                           const UploadFormDataItems &items,
13454
0
                           UploadProgress progress) {
13455
0
  return cli_->Post(path, items, progress);
13456
0
}
13457
inline Result Client::Post(const std::string &path, const Headers &headers,
13458
                           const UploadFormDataItems &items,
13459
0
                           UploadProgress progress) {
13460
0
  return cli_->Post(path, headers, items, progress);
13461
0
}
13462
inline Result Client::Post(const std::string &path, const Headers &headers,
13463
                           const UploadFormDataItems &items,
13464
                           const std::string &boundary,
13465
0
                           UploadProgress progress) {
13466
0
  return cli_->Post(path, headers, items, boundary, progress);
13467
0
}
13468
inline Result Client::Post(const std::string &path, const Headers &headers,
13469
                           const UploadFormDataItems &items,
13470
                           const FormDataProviderItems &provider_items,
13471
0
                           UploadProgress progress) {
13472
0
  return cli_->Post(path, headers, items, provider_items, progress);
13473
0
}
13474
inline Result Client::Post(const std::string &path, const Headers &headers,
13475
                           const std::string &body,
13476
                           const std::string &content_type,
13477
                           ContentReceiver content_receiver,
13478
0
                           DownloadProgress progress) {
13479
0
  return cli_->Post(path, headers, body, content_type,
13480
0
                    std::move(content_receiver), progress);
13481
0
}
13482
13483
0
inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
13484
0
inline Result Client::Put(const std::string &path, const Headers &headers) {
13485
0
  return cli_->Put(path, headers);
13486
0
}
13487
inline Result Client::Put(const std::string &path, const char *body,
13488
                          size_t content_length,
13489
                          const std::string &content_type,
13490
0
                          UploadProgress progress) {
13491
0
  return cli_->Put(path, body, content_length, content_type, progress);
13492
0
}
13493
inline Result Client::Put(const std::string &path, const Headers &headers,
13494
                          const char *body, size_t content_length,
13495
                          const std::string &content_type,
13496
0
                          UploadProgress progress) {
13497
0
  return cli_->Put(path, headers, body, content_length, content_type, progress);
13498
0
}
13499
inline Result Client::Put(const std::string &path, const std::string &body,
13500
                          const std::string &content_type,
13501
0
                          UploadProgress progress) {
13502
0
  return cli_->Put(path, body, content_type, progress);
13503
0
}
13504
inline Result Client::Put(const std::string &path, const Headers &headers,
13505
                          const std::string &body,
13506
                          const std::string &content_type,
13507
0
                          UploadProgress progress) {
13508
0
  return cli_->Put(path, headers, body, content_type, progress);
13509
0
}
13510
inline Result Client::Put(const std::string &path, size_t content_length,
13511
                          ContentProvider content_provider,
13512
                          const std::string &content_type,
13513
0
                          UploadProgress progress) {
13514
0
  return cli_->Put(path, content_length, std::move(content_provider),
13515
0
                   content_type, progress);
13516
0
}
13517
inline Result Client::Put(const std::string &path, size_t content_length,
13518
                          ContentProvider content_provider,
13519
                          const std::string &content_type,
13520
                          ContentReceiver content_receiver,
13521
0
                          UploadProgress progress) {
13522
0
  return cli_->Put(path, content_length, std::move(content_provider),
13523
0
                   content_type, std::move(content_receiver), progress);
13524
0
}
13525
inline Result Client::Put(const std::string &path,
13526
                          ContentProviderWithoutLength content_provider,
13527
                          const std::string &content_type,
13528
0
                          UploadProgress progress) {
13529
0
  return cli_->Put(path, std::move(content_provider), content_type, progress);
13530
0
}
13531
inline Result Client::Put(const std::string &path,
13532
                          ContentProviderWithoutLength content_provider,
13533
                          const std::string &content_type,
13534
                          ContentReceiver content_receiver,
13535
0
                          UploadProgress progress) {
13536
0
  return cli_->Put(path, std::move(content_provider), content_type,
13537
0
                   std::move(content_receiver), progress);
13538
0
}
13539
inline Result Client::Put(const std::string &path, const Headers &headers,
13540
                          size_t content_length,
13541
                          ContentProvider content_provider,
13542
                          const std::string &content_type,
13543
0
                          UploadProgress progress) {
13544
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
13545
0
                   content_type, progress);
13546
0
}
13547
inline Result Client::Put(const std::string &path, const Headers &headers,
13548
                          size_t content_length,
13549
                          ContentProvider content_provider,
13550
                          const std::string &content_type,
13551
                          ContentReceiver content_receiver,
13552
0
                          UploadProgress progress) {
13553
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
13554
0
                   content_type, std::move(content_receiver), progress);
13555
0
}
13556
inline Result Client::Put(const std::string &path, const Headers &headers,
13557
                          ContentProviderWithoutLength content_provider,
13558
                          const std::string &content_type,
13559
0
                          UploadProgress progress) {
13560
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
13561
0
                   progress);
13562
0
}
13563
inline Result Client::Put(const std::string &path, const Headers &headers,
13564
                          ContentProviderWithoutLength content_provider,
13565
                          const std::string &content_type,
13566
                          ContentReceiver content_receiver,
13567
0
                          UploadProgress progress) {
13568
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
13569
0
                   std::move(content_receiver), progress);
13570
0
}
13571
0
inline Result Client::Put(const std::string &path, const Params &params) {
13572
0
  return cli_->Put(path, params);
13573
0
}
13574
inline Result Client::Put(const std::string &path, const Headers &headers,
13575
0
                          const Params &params) {
13576
0
  return cli_->Put(path, headers, params);
13577
0
}
13578
inline Result Client::Put(const std::string &path,
13579
                          const UploadFormDataItems &items,
13580
0
                          UploadProgress progress) {
13581
0
  return cli_->Put(path, items, progress);
13582
0
}
13583
inline Result Client::Put(const std::string &path, const Headers &headers,
13584
                          const UploadFormDataItems &items,
13585
0
                          UploadProgress progress) {
13586
0
  return cli_->Put(path, headers, items, progress);
13587
0
}
13588
inline Result Client::Put(const std::string &path, const Headers &headers,
13589
                          const UploadFormDataItems &items,
13590
                          const std::string &boundary,
13591
0
                          UploadProgress progress) {
13592
0
  return cli_->Put(path, headers, items, boundary, progress);
13593
0
}
13594
inline Result Client::Put(const std::string &path, const Headers &headers,
13595
                          const UploadFormDataItems &items,
13596
                          const FormDataProviderItems &provider_items,
13597
0
                          UploadProgress progress) {
13598
0
  return cli_->Put(path, headers, items, provider_items, progress);
13599
0
}
13600
inline Result Client::Put(const std::string &path, const Headers &headers,
13601
                          const std::string &body,
13602
                          const std::string &content_type,
13603
                          ContentReceiver content_receiver,
13604
0
                          DownloadProgress progress) {
13605
0
  return cli_->Put(path, headers, body, content_type, content_receiver,
13606
0
                   progress);
13607
0
}
13608
13609
0
inline Result Client::Patch(const std::string &path) {
13610
0
  return cli_->Patch(path);
13611
0
}
13612
0
inline Result Client::Patch(const std::string &path, const Headers &headers) {
13613
0
  return cli_->Patch(path, headers);
13614
0
}
13615
inline Result Client::Patch(const std::string &path, const char *body,
13616
                            size_t content_length,
13617
                            const std::string &content_type,
13618
0
                            UploadProgress progress) {
13619
0
  return cli_->Patch(path, body, content_length, content_type, progress);
13620
0
}
13621
inline Result Client::Patch(const std::string &path, const Headers &headers,
13622
                            const char *body, size_t content_length,
13623
                            const std::string &content_type,
13624
0
                            UploadProgress progress) {
13625
0
  return cli_->Patch(path, headers, body, content_length, content_type,
13626
0
                     progress);
13627
0
}
13628
inline Result Client::Patch(const std::string &path, const std::string &body,
13629
                            const std::string &content_type,
13630
0
                            UploadProgress progress) {
13631
0
  return cli_->Patch(path, body, content_type, progress);
13632
0
}
13633
inline Result Client::Patch(const std::string &path, const Headers &headers,
13634
                            const std::string &body,
13635
                            const std::string &content_type,
13636
0
                            UploadProgress progress) {
13637
0
  return cli_->Patch(path, headers, body, content_type, progress);
13638
0
}
13639
inline Result Client::Patch(const std::string &path, size_t content_length,
13640
                            ContentProvider content_provider,
13641
                            const std::string &content_type,
13642
0
                            UploadProgress progress) {
13643
0
  return cli_->Patch(path, content_length, std::move(content_provider),
13644
0
                     content_type, progress);
13645
0
}
13646
inline Result Client::Patch(const std::string &path, size_t content_length,
13647
                            ContentProvider content_provider,
13648
                            const std::string &content_type,
13649
                            ContentReceiver content_receiver,
13650
0
                            UploadProgress progress) {
13651
0
  return cli_->Patch(path, content_length, std::move(content_provider),
13652
0
                     content_type, std::move(content_receiver), progress);
13653
0
}
13654
inline Result Client::Patch(const std::string &path,
13655
                            ContentProviderWithoutLength content_provider,
13656
                            const std::string &content_type,
13657
0
                            UploadProgress progress) {
13658
0
  return cli_->Patch(path, std::move(content_provider), content_type, progress);
13659
0
}
13660
inline Result Client::Patch(const std::string &path,
13661
                            ContentProviderWithoutLength content_provider,
13662
                            const std::string &content_type,
13663
                            ContentReceiver content_receiver,
13664
0
                            UploadProgress progress) {
13665
0
  return cli_->Patch(path, std::move(content_provider), content_type,
13666
0
                     std::move(content_receiver), progress);
13667
0
}
13668
inline Result Client::Patch(const std::string &path, const Headers &headers,
13669
                            size_t content_length,
13670
                            ContentProvider content_provider,
13671
                            const std::string &content_type,
13672
0
                            UploadProgress progress) {
13673
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
13674
0
                     content_type, progress);
13675
0
}
13676
inline Result Client::Patch(const std::string &path, const Headers &headers,
13677
                            size_t content_length,
13678
                            ContentProvider content_provider,
13679
                            const std::string &content_type,
13680
                            ContentReceiver content_receiver,
13681
0
                            UploadProgress progress) {
13682
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
13683
0
                     content_type, std::move(content_receiver), progress);
13684
0
}
13685
inline Result Client::Patch(const std::string &path, const Headers &headers,
13686
                            ContentProviderWithoutLength content_provider,
13687
                            const std::string &content_type,
13688
0
                            UploadProgress progress) {
13689
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
13690
0
                     progress);
13691
0
}
13692
inline Result Client::Patch(const std::string &path, const Headers &headers,
13693
                            ContentProviderWithoutLength content_provider,
13694
                            const std::string &content_type,
13695
                            ContentReceiver content_receiver,
13696
0
                            UploadProgress progress) {
13697
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
13698
0
                     std::move(content_receiver), progress);
13699
0
}
13700
0
inline Result Client::Patch(const std::string &path, const Params &params) {
13701
0
  return cli_->Patch(path, params);
13702
0
}
13703
inline Result Client::Patch(const std::string &path, const Headers &headers,
13704
0
                            const Params &params) {
13705
0
  return cli_->Patch(path, headers, params);
13706
0
}
13707
inline Result Client::Patch(const std::string &path,
13708
                            const UploadFormDataItems &items,
13709
0
                            UploadProgress progress) {
13710
0
  return cli_->Patch(path, items, progress);
13711
0
}
13712
inline Result Client::Patch(const std::string &path, const Headers &headers,
13713
                            const UploadFormDataItems &items,
13714
0
                            UploadProgress progress) {
13715
0
  return cli_->Patch(path, headers, items, progress);
13716
0
}
13717
inline Result Client::Patch(const std::string &path, const Headers &headers,
13718
                            const UploadFormDataItems &items,
13719
                            const std::string &boundary,
13720
0
                            UploadProgress progress) {
13721
0
  return cli_->Patch(path, headers, items, boundary, progress);
13722
0
}
13723
inline Result Client::Patch(const std::string &path, const Headers &headers,
13724
                            const UploadFormDataItems &items,
13725
                            const FormDataProviderItems &provider_items,
13726
0
                            UploadProgress progress) {
13727
0
  return cli_->Patch(path, headers, items, provider_items, progress);
13728
0
}
13729
inline Result Client::Patch(const std::string &path, const Headers &headers,
13730
                            const std::string &body,
13731
                            const std::string &content_type,
13732
                            ContentReceiver content_receiver,
13733
0
                            DownloadProgress progress) {
13734
0
  return cli_->Patch(path, headers, body, content_type, content_receiver,
13735
0
                     progress);
13736
0
}
13737
13738
inline Result Client::Delete(const std::string &path,
13739
0
                             DownloadProgress progress) {
13740
0
  return cli_->Delete(path, progress);
13741
0
}
13742
inline Result Client::Delete(const std::string &path, const Headers &headers,
13743
0
                             DownloadProgress progress) {
13744
0
  return cli_->Delete(path, headers, progress);
13745
0
}
13746
inline Result Client::Delete(const std::string &path, const char *body,
13747
                             size_t content_length,
13748
                             const std::string &content_type,
13749
0
                             DownloadProgress progress) {
13750
0
  return cli_->Delete(path, body, content_length, content_type, progress);
13751
0
}
13752
inline Result Client::Delete(const std::string &path, const Headers &headers,
13753
                             const char *body, size_t content_length,
13754
                             const std::string &content_type,
13755
0
                             DownloadProgress progress) {
13756
0
  return cli_->Delete(path, headers, body, content_length, content_type,
13757
0
                      progress);
13758
0
}
13759
inline Result Client::Delete(const std::string &path, const std::string &body,
13760
                             const std::string &content_type,
13761
0
                             DownloadProgress progress) {
13762
0
  return cli_->Delete(path, body, content_type, progress);
13763
0
}
13764
inline Result Client::Delete(const std::string &path, const Headers &headers,
13765
                             const std::string &body,
13766
                             const std::string &content_type,
13767
0
                             DownloadProgress progress) {
13768
0
  return cli_->Delete(path, headers, body, content_type, progress);
13769
0
}
13770
inline Result Client::Delete(const std::string &path, const Params &params,
13771
0
                             DownloadProgress progress) {
13772
0
  return cli_->Delete(path, params, progress);
13773
0
}
13774
inline Result Client::Delete(const std::string &path, const Headers &headers,
13775
0
                             const Params &params, DownloadProgress progress) {
13776
0
  return cli_->Delete(path, headers, params, progress);
13777
0
}
13778
13779
0
inline Result Client::Options(const std::string &path) {
13780
0
  return cli_->Options(path);
13781
0
}
13782
0
inline Result Client::Options(const std::string &path, const Headers &headers) {
13783
0
  return cli_->Options(path, headers);
13784
0
}
13785
13786
inline ClientImpl::StreamHandle
13787
Client::open_stream(const std::string &method, const std::string &path,
13788
                    const Params &params, const Headers &headers,
13789
0
                    const std::string &body, const std::string &content_type) {
13790
0
  return cli_->open_stream(method, path, params, headers, body, content_type);
13791
0
}
13792
13793
0
inline bool Client::send(Request &req, Response &res, Error &error) {
13794
0
  return cli_->send(req, res, error);
13795
0
}
13796
13797
0
inline Result Client::send(const Request &req) { return cli_->send(req); }
13798
13799
0
inline void Client::stop() { cli_->stop(); }
13800
13801
0
inline std::string Client::host() const { return cli_->host(); }
13802
13803
0
inline int Client::port() const { return cli_->port(); }
13804
13805
0
inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
13806
13807
0
inline socket_t Client::socket() const { return cli_->socket(); }
13808
13809
inline void
13810
0
Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
13811
0
  cli_->set_hostname_addr_map(std::move(addr_map));
13812
0
}
13813
13814
0
inline void Client::set_default_headers(Headers headers) {
13815
0
  cli_->set_default_headers(std::move(headers));
13816
0
}
13817
13818
inline void Client::set_header_writer(
13819
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
13820
0
  cli_->set_header_writer(writer);
13821
0
}
13822
13823
0
inline void Client::set_address_family(int family) {
13824
0
  cli_->set_address_family(family);
13825
0
}
13826
13827
0
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
13828
13829
0
inline void Client::set_socket_options(SocketOptions socket_options) {
13830
0
  cli_->set_socket_options(std::move(socket_options));
13831
0
}
13832
13833
0
inline void Client::set_connection_timeout(time_t sec, time_t usec) {
13834
0
  cli_->set_connection_timeout(sec, usec);
13835
0
}
13836
13837
0
inline void Client::set_read_timeout(time_t sec, time_t usec) {
13838
0
  cli_->set_read_timeout(sec, usec);
13839
0
}
13840
13841
0
inline void Client::set_write_timeout(time_t sec, time_t usec) {
13842
0
  cli_->set_write_timeout(sec, usec);
13843
0
}
13844
13845
inline void Client::set_basic_auth(const std::string &username,
13846
0
                                   const std::string &password) {
13847
0
  cli_->set_basic_auth(username, password);
13848
0
}
13849
0
inline void Client::set_bearer_token_auth(const std::string &token) {
13850
0
  cli_->set_bearer_token_auth(token);
13851
0
}
13852
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13853
inline void Client::set_digest_auth(const std::string &username,
13854
                                    const std::string &password) {
13855
  cli_->set_digest_auth(username, password);
13856
}
13857
#endif
13858
13859
0
inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
13860
0
inline void Client::set_follow_location(bool on) {
13861
0
  cli_->set_follow_location(on);
13862
0
}
13863
13864
0
inline void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }
13865
13866
[[deprecated("Use set_path_encode instead")]]
13867
0
inline void Client::set_url_encode(bool on) {
13868
0
  cli_->set_path_encode(on);
13869
0
}
13870
13871
0
inline void Client::set_compress(bool on) { cli_->set_compress(on); }
13872
13873
0
inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
13874
13875
0
inline void Client::set_interface(const std::string &intf) {
13876
0
  cli_->set_interface(intf);
13877
0
}
13878
13879
0
inline void Client::set_proxy(const std::string &host, int port) {
13880
0
  cli_->set_proxy(host, port);
13881
0
}
13882
inline void Client::set_proxy_basic_auth(const std::string &username,
13883
0
                                         const std::string &password) {
13884
0
  cli_->set_proxy_basic_auth(username, password);
13885
0
}
13886
0
inline void Client::set_proxy_bearer_token_auth(const std::string &token) {
13887
0
  cli_->set_proxy_bearer_token_auth(token);
13888
0
}
13889
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13890
inline void Client::set_proxy_digest_auth(const std::string &username,
13891
                                          const std::string &password) {
13892
  cli_->set_proxy_digest_auth(username, password);
13893
}
13894
#endif
13895
13896
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13897
inline void Client::enable_server_certificate_verification(bool enabled) {
13898
  cli_->enable_server_certificate_verification(enabled);
13899
}
13900
13901
inline void Client::enable_server_hostname_verification(bool enabled) {
13902
  cli_->enable_server_hostname_verification(enabled);
13903
}
13904
13905
inline void Client::set_server_certificate_verifier(
13906
    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
13907
  cli_->set_server_certificate_verifier(verifier);
13908
}
13909
#endif
13910
13911
0
inline void Client::set_logger(Logger logger) {
13912
0
  cli_->set_logger(std::move(logger));
13913
0
}
13914
13915
0
inline void Client::set_error_logger(ErrorLogger error_logger) {
13916
0
  cli_->set_error_logger(std::move(error_logger));
13917
0
}
13918
13919
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
13920
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
13921
                                     const std::string &ca_cert_dir_path) {
13922
  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
13923
}
13924
13925
inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
13926
  if (is_ssl_) {
13927
    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
13928
  } else {
13929
    cli_->set_ca_cert_store(ca_cert_store);
13930
  }
13931
}
13932
13933
inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
13934
  set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
13935
}
13936
13937
inline long Client::get_openssl_verify_result() const {
13938
  if (is_ssl_) {
13939
    return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
13940
  }
13941
  return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
13942
}
13943
13944
inline SSL_CTX *Client::ssl_context() const {
13945
  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
13946
  return nullptr;
13947
}
13948
#endif
13949
13950
// ----------------------------------------------------------------------------
13951
13952
} // namespace httplib
13953
13954
#endif // CPPHTTPLIB_HTTPLIB_H