Coverage Report

Created: 2025-07-04 06:22

/src/cpp-httplib/httplib.h
Line
Count
Source (jump to first uncovered line)
1
//
2
//  httplib.h
3
//
4
//  Copyright (c) 2025 Yuji Hirose. All rights reserved.
5
//  MIT License
6
//
7
8
#ifndef CPPHTTPLIB_HTTPLIB_H
9
#define CPPHTTPLIB_HTTPLIB_H
10
11
#define CPPHTTPLIB_VERSION "0.22.0"
12
13
/*
14
 * Configuration
15
 */
16
17
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
18
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
19
#endif
20
21
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
22
0
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
23
#endif
24
25
#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
26
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
27
#endif
28
29
#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
30
#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
31
#endif
32
33
#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
34
#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
35
#endif
36
37
#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
38
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
39
#endif
40
41
#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
42
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
43
#endif
44
45
#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
46
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
47
#endif
48
49
#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
50
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
51
#endif
52
53
#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
54
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
55
#endif
56
57
#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
58
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
59
#endif
60
61
#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
62
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
63
#endif
64
65
#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
66
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
67
#endif
68
69
#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
70
#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
71
#endif
72
73
#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
74
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
75
#endif
76
77
#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
78
#ifdef _WIN32
79
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
80
#else
81
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
82
#endif
83
#endif
84
85
#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
86
2.83k
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
87
#endif
88
89
#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
90
26.6k
#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
91
#endif
92
93
#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
94
11.2k
#define CPPHTTPLIB_HEADER_MAX_COUNT 100
95
#endif
96
97
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
98
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
99
#endif
100
101
#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
102
14.6k
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
103
#endif
104
105
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
106
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
107
#endif
108
109
#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
110
145
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
111
#endif
112
113
#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
114
467
#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
115
#endif
116
117
#ifndef CPPHTTPLIB_TCP_NODELAY
118
#define CPPHTTPLIB_TCP_NODELAY false
119
#endif
120
121
#ifndef CPPHTTPLIB_IPV6_V6ONLY
122
#define CPPHTTPLIB_IPV6_V6ONLY false
123
#endif
124
125
#ifndef CPPHTTPLIB_RECV_BUFSIZ
126
7.29k
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
127
#endif
128
129
#ifndef CPPHTTPLIB_SEND_BUFSIZ
130
#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
131
#endif
132
133
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
134
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
135
#endif
136
137
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
138
#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
139
0
  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \
140
0
                      ? std::thread::hardware_concurrency() - 1                \
141
0
                      : 0))
142
#endif
143
144
#ifndef CPPHTTPLIB_RECV_FLAGS
145
0
#define CPPHTTPLIB_RECV_FLAGS 0
146
#endif
147
148
#ifndef CPPHTTPLIB_SEND_FLAGS
149
0
#define CPPHTTPLIB_SEND_FLAGS 0
150
#endif
151
152
#ifndef CPPHTTPLIB_LISTEN_BACKLOG
153
#define CPPHTTPLIB_LISTEN_BACKLOG 5
154
#endif
155
156
#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
157
2.52M
#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
158
#endif
159
160
/*
161
 * Headers
162
 */
163
164
#ifdef _WIN32
165
#ifndef _CRT_SECURE_NO_WARNINGS
166
#define _CRT_SECURE_NO_WARNINGS
167
#endif //_CRT_SECURE_NO_WARNINGS
168
169
#ifndef _CRT_NONSTDC_NO_DEPRECATE
170
#define _CRT_NONSTDC_NO_DEPRECATE
171
#endif //_CRT_NONSTDC_NO_DEPRECATE
172
173
#if defined(_MSC_VER)
174
#if _MSC_VER < 1900
175
#error Sorry, Visual Studio versions prior to 2015 are not supported
176
#endif
177
178
#pragma comment(lib, "ws2_32.lib")
179
180
#ifdef _WIN64
181
using ssize_t = __int64;
182
#else
183
using ssize_t = long;
184
#endif
185
#endif // _MSC_VER
186
187
#ifndef S_ISREG
188
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
189
#endif // S_ISREG
190
191
#ifndef S_ISDIR
192
#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
193
#endif // S_ISDIR
194
195
#ifndef NOMINMAX
196
#define NOMINMAX
197
#endif // NOMINMAX
198
199
#include <io.h>
200
#include <winsock2.h>
201
#include <ws2tcpip.h>
202
203
#if defined(__has_include)
204
#if __has_include(<afunix.h>)
205
// afunix.h uses types declared in winsock2.h, so has to be included after it.
206
#include <afunix.h>
207
#define CPPHTTPLIB_HAVE_AFUNIX_H 1
208
#endif
209
#endif
210
211
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
212
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
213
#endif
214
215
using nfds_t = unsigned long;
216
using socket_t = SOCKET;
217
using socklen_t = int;
218
219
#else // not _WIN32
220
221
#include <arpa/inet.h>
222
#if !defined(_AIX) && !defined(__MVS__)
223
#include <ifaddrs.h>
224
#endif
225
#ifdef __MVS__
226
#include <strings.h>
227
#ifndef NI_MAXHOST
228
#define NI_MAXHOST 1025
229
#endif
230
#endif
231
#include <net/if.h>
232
#include <netdb.h>
233
#include <netinet/in.h>
234
#ifdef __linux__
235
#include <resolv.h>
236
#endif
237
#include <csignal>
238
#include <netinet/tcp.h>
239
#include <poll.h>
240
#include <pthread.h>
241
#include <sys/mman.h>
242
#include <sys/socket.h>
243
#include <sys/un.h>
244
#include <unistd.h>
245
246
using socket_t = int;
247
#ifndef INVALID_SOCKET
248
0
#define INVALID_SOCKET (-1)
249
#endif
250
#endif //_WIN32
251
252
#include <algorithm>
253
#include <array>
254
#include <atomic>
255
#include <cassert>
256
#include <cctype>
257
#include <climits>
258
#include <condition_variable>
259
#include <cstring>
260
#include <errno.h>
261
#include <exception>
262
#include <fcntl.h>
263
#include <functional>
264
#include <iomanip>
265
#include <iostream>
266
#include <list>
267
#include <map>
268
#include <memory>
269
#include <mutex>
270
#include <random>
271
#include <regex>
272
#include <set>
273
#include <sstream>
274
#include <string>
275
#include <sys/stat.h>
276
#include <thread>
277
#include <unordered_map>
278
#include <unordered_set>
279
#include <utility>
280
281
#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO)
282
#if defined(__APPLE__)
283
#include <TargetConditionals.h>
284
#if TARGET_OS_OSX
285
#include <CFNetwork/CFHost.h>
286
#include <CoreFoundation/CoreFoundation.h>
287
#endif
288
#endif
289
#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
290
291
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
292
#ifdef _WIN32
293
#include <wincrypt.h>
294
295
// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
296
// used
297
#undef X509_NAME
298
#undef X509_CERT_PAIR
299
#undef X509_EXTENSIONS
300
#undef PKCS7_SIGNER_INFO
301
302
#ifdef _MSC_VER
303
#pragma comment(lib, "crypt32.lib")
304
#endif
305
#endif // _WIN32
306
307
#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO)
308
#if defined(__APPLE__)
309
#include <TargetConditionals.h>
310
#if TARGET_OS_OSX
311
#include <CFNetwork/CFHost.h>
312
#include <CoreFoundation/CoreFoundation.h>
313
#include <Security/Security.h>
314
#endif
315
#endif
316
#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
317
318
#include <openssl/err.h>
319
#include <openssl/evp.h>
320
#include <openssl/ssl.h>
321
#include <openssl/x509v3.h>
322
323
#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
324
#include <openssl/applink.c>
325
#endif
326
327
#include <iostream>
328
#include <sstream>
329
330
#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
331
#if OPENSSL_VERSION_NUMBER < 0x1010107f
332
#error Please use OpenSSL or a current version of BoringSSL
333
#endif
334
#define SSL_get1_peer_certificate SSL_get_peer_certificate
335
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
336
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
337
#endif
338
339
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
340
341
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
342
#include <zlib.h>
343
#endif
344
345
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
346
#include <brotli/decode.h>
347
#include <brotli/encode.h>
348
#endif
349
350
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
351
#include <zstd.h>
352
#endif
353
354
/*
355
 * Declaration
356
 */
357
namespace httplib {
358
359
namespace detail {
360
361
/*
362
 * Backport std::make_unique from C++14.
363
 *
364
 * NOTE: This code came up with the following stackoverflow post:
365
 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
366
 *
367
 */
368
369
template <class T, class... Args>
370
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
371
441
make_unique(Args &&...args) {
372
441
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
373
441
}
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
371
12
make_unique(Args &&...args) {
372
12
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
373
12
}
_ZN7httplib6detail11make_uniqueINS0_15gzip_compressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Line
Count
Source
371
10
make_unique(Args &&...args) {
372
10
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
373
10
}
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
371
419
make_unique(Args &&...args) {
372
419
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
373
419
}
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_8ResponseEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_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_
374
375
template <class T>
376
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
377
make_unique(std::size_t n) {
378
  typedef typename std::remove_extent<T>::type RT;
379
  return std::unique_ptr<T>(new RT[n]);
380
}
381
382
namespace case_ignore {
383
384
123M
inline unsigned char to_lower(int c) {
385
123M
  const static unsigned char table[256] = {
386
123M
      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
387
123M
      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
388
123M
      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
389
123M
      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
390
123M
      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
391
123M
      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
392
123M
      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
393
123M
      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
394
123M
      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
395
123M
      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
396
123M
      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
397
123M
      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
398
123M
      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
399
123M
      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
400
123M
      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
401
123M
      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
402
123M
      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
403
123M
      255,
404
123M
  };
405
123M
  return table[(unsigned char)(char)c];
406
123M
}
407
408
5.52M
inline bool equal(const std::string &a, const std::string &b) {
409
5.52M
  return a.size() == b.size() &&
410
60.4M
         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
411
60.4M
           return to_lower(ca) == to_lower(cb);
412
60.4M
         });
413
5.52M
}
414
415
struct equal_to {
416
5.36M
  bool operator()(const std::string &a, const std::string &b) const {
417
5.36M
    return equal(a, b);
418
5.36M
  }
419
};
420
421
struct hash {
422
205k
  size_t operator()(const std::string &key) const {
423
205k
    return hash_core(key.data(), key.size(), 0);
424
205k
  }
425
426
2.67M
  size_t hash_core(const char *s, size_t l, size_t h) const {
427
2.67M
    return (l == 0) ? h
428
2.67M
                    : hash_core(s + 1, l - 1,
429
                                // Unsets the 6 high bits of h, therefore no
430
                                // overflow happens
431
2.47M
                                (((std::numeric_limits<size_t>::max)() >> 6) &
432
2.47M
                                 h * 33) ^
433
2.47M
                                    static_cast<unsigned char>(to_lower(*s)));
434
2.67M
  }
435
};
436
437
} // namespace case_ignore
438
439
// This is based on
440
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
441
442
struct scope_exit {
443
  explicit scope_exit(std::function<void(void)> &&f)
444
0
      : exit_function(std::move(f)), execute_on_destruction{true} {}
445
446
  scope_exit(scope_exit &&rhs) noexcept
447
      : exit_function(std::move(rhs.exit_function)),
448
0
        execute_on_destruction{rhs.execute_on_destruction} {
449
0
    rhs.release();
450
0
  }
451
452
0
  ~scope_exit() {
453
0
    if (execute_on_destruction) { this->exit_function(); }
454
0
  }
455
456
0
  void release() { this->execute_on_destruction = false; }
457
458
private:
459
  scope_exit(const scope_exit &) = delete;
460
  void operator=(const scope_exit &) = delete;
461
  scope_exit &operator=(scope_exit &&) = delete;
462
463
  std::function<void(void)> exit_function;
464
  bool execute_on_destruction;
465
};
466
467
} // namespace detail
468
469
enum SSLVerifierResponse {
470
  // no decision has been made, use the built-in certificate verifier
471
  NoDecisionMade,
472
  // connection certificate is verified and accepted
473
  CertificateAccepted,
474
  // connection certificate was processed but is rejected
475
  CertificateRejected
476
};
477
478
enum StatusCode {
479
  // Information responses
480
  Continue_100 = 100,
481
  SwitchingProtocol_101 = 101,
482
  Processing_102 = 102,
483
  EarlyHints_103 = 103,
484
485
  // Successful responses
486
  OK_200 = 200,
487
  Created_201 = 201,
488
  Accepted_202 = 202,
489
  NonAuthoritativeInformation_203 = 203,
490
  NoContent_204 = 204,
491
  ResetContent_205 = 205,
492
  PartialContent_206 = 206,
493
  MultiStatus_207 = 207,
494
  AlreadyReported_208 = 208,
495
  IMUsed_226 = 226,
496
497
  // Redirection messages
498
  MultipleChoices_300 = 300,
499
  MovedPermanently_301 = 301,
500
  Found_302 = 302,
501
  SeeOther_303 = 303,
502
  NotModified_304 = 304,
503
  UseProxy_305 = 305,
504
  unused_306 = 306,
505
  TemporaryRedirect_307 = 307,
506
  PermanentRedirect_308 = 308,
507
508
  // Client error responses
509
  BadRequest_400 = 400,
510
  Unauthorized_401 = 401,
511
  PaymentRequired_402 = 402,
512
  Forbidden_403 = 403,
513
  NotFound_404 = 404,
514
  MethodNotAllowed_405 = 405,
515
  NotAcceptable_406 = 406,
516
  ProxyAuthenticationRequired_407 = 407,
517
  RequestTimeout_408 = 408,
518
  Conflict_409 = 409,
519
  Gone_410 = 410,
520
  LengthRequired_411 = 411,
521
  PreconditionFailed_412 = 412,
522
  PayloadTooLarge_413 = 413,
523
  UriTooLong_414 = 414,
524
  UnsupportedMediaType_415 = 415,
525
  RangeNotSatisfiable_416 = 416,
526
  ExpectationFailed_417 = 417,
527
  ImATeapot_418 = 418,
528
  MisdirectedRequest_421 = 421,
529
  UnprocessableContent_422 = 422,
530
  Locked_423 = 423,
531
  FailedDependency_424 = 424,
532
  TooEarly_425 = 425,
533
  UpgradeRequired_426 = 426,
534
  PreconditionRequired_428 = 428,
535
  TooManyRequests_429 = 429,
536
  RequestHeaderFieldsTooLarge_431 = 431,
537
  UnavailableForLegalReasons_451 = 451,
538
539
  // Server error responses
540
  InternalServerError_500 = 500,
541
  NotImplemented_501 = 501,
542
  BadGateway_502 = 502,
543
  ServiceUnavailable_503 = 503,
544
  GatewayTimeout_504 = 504,
545
  HttpVersionNotSupported_505 = 505,
546
  VariantAlsoNegotiates_506 = 506,
547
  InsufficientStorage_507 = 507,
548
  LoopDetected_508 = 508,
549
  NotExtended_510 = 510,
550
  NetworkAuthenticationRequired_511 = 511,
551
};
552
553
using Headers =
554
    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
555
                            detail::case_ignore::equal_to>;
556
557
using Params = std::multimap<std::string, std::string>;
558
using Match = std::smatch;
559
560
using DownloadProgress = std::function<bool(uint64_t current, uint64_t total)>;
561
using UploadProgress = std::function<bool(uint64_t current, uint64_t total)>;
562
563
struct Response;
564
using ResponseHandler = std::function<bool(const Response &response)>;
565
566
struct MultipartFormData {
567
  std::string name;
568
  std::string content;
569
  std::string filename;
570
  std::string content_type;
571
  Headers headers;
572
};
573
using MultipartFormDataItems = std::vector<MultipartFormData>;
574
using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
575
576
struct MultipartFormDataForClientInput {
577
  std::string name;
578
  std::string content;
579
  std::string filename;
580
  std::string content_type;
581
};
582
using MultipartFormDataItemsForClientInput =
583
    std::vector<MultipartFormDataForClientInput>;
584
585
class DataSink {
586
public:
587
0
  DataSink() : os(&sb_), sb_(*this) {}
588
589
  DataSink(const DataSink &) = delete;
590
  DataSink &operator=(const DataSink &) = delete;
591
  DataSink(DataSink &&) = delete;
592
  DataSink &operator=(DataSink &&) = delete;
593
594
  std::function<bool(const char *data, size_t data_len)> write;
595
  std::function<bool()> is_writable;
596
  std::function<void()> done;
597
  std::function<void(const Headers &trailer)> done_with_trailer;
598
  std::ostream os;
599
600
private:
601
  class data_sink_streambuf final : public std::streambuf {
602
  public:
603
0
    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
604
605
  protected:
606
0
    std::streamsize xsputn(const char *s, std::streamsize n) override {
607
0
      sink_.write(s, static_cast<size_t>(n));
608
0
      return n;
609
0
    }
610
611
  private:
612
    DataSink &sink_;
613
  };
614
615
  data_sink_streambuf sb_;
616
};
617
618
using ContentProvider =
619
    std::function<bool(size_t offset, size_t length, DataSink &sink)>;
620
621
using ContentProviderWithoutLength =
622
    std::function<bool(size_t offset, DataSink &sink)>;
623
624
using ContentProviderResourceReleaser = std::function<void(bool success)>;
625
626
struct MultipartFormDataProvider {
627
  std::string name;
628
  ContentProviderWithoutLength provider;
629
  std::string filename;
630
  std::string content_type;
631
};
632
using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
633
634
using ContentReceiverWithProgress =
635
    std::function<bool(const char *data, size_t data_length, uint64_t offset,
636
                       uint64_t total_length)>;
637
638
using ContentReceiver =
639
    std::function<bool(const char *data, size_t data_length)>;
640
641
using MultipartContentHeader =
642
    std::function<bool(const MultipartFormData &file)>;
643
644
class ContentReader {
645
public:
646
  using Reader = std::function<bool(ContentReceiver receiver)>;
647
  using MultipartReader = std::function<bool(MultipartContentHeader header,
648
                                             ContentReceiver receiver)>;
649
650
  ContentReader(Reader reader, MultipartReader multipart_reader)
651
2.30k
      : reader_(std::move(reader)),
652
2.30k
        multipart_reader_(std::move(multipart_reader)) {}
653
654
  bool operator()(MultipartContentHeader header,
655
0
                  ContentReceiver receiver) const {
656
0
    return multipart_reader_(std::move(header), std::move(receiver));
657
0
  }
658
659
0
  bool operator()(ContentReceiver receiver) const {
660
0
    return reader_(std::move(receiver));
661
0
  }
662
663
  Reader reader_;
664
  MultipartReader multipart_reader_;
665
};
666
667
using Range = std::pair<ssize_t, ssize_t>;
668
using Ranges = std::vector<Range>;
669
670
struct Request {
671
  std::string method;
672
  std::string path;
673
  std::string matched_route;
674
  Params params;
675
  Headers headers;
676
  std::string body;
677
678
  std::string remote_addr;
679
  int remote_port = -1;
680
  std::string local_addr;
681
  int local_port = -1;
682
683
  // for server
684
  std::string version;
685
  std::string target;
686
  MultipartFormDataMap files;
687
  Ranges ranges;
688
  Match matches;
689
  std::unordered_map<std::string, std::string> path_params;
690
0
  std::function<bool()> is_connection_closed = []() { return true; };
691
692
  // for client
693
  std::vector<std::string> accept_content_types;
694
  ResponseHandler response_handler;
695
  ContentReceiverWithProgress content_receiver;
696
  DownloadProgress download_progress;
697
  UploadProgress upload_progress;
698
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
699
  const SSL *ssl = nullptr;
700
#endif
701
702
  bool has_header(const std::string &key) const;
703
  std::string get_header_value(const std::string &key, const char *def = "",
704
                               size_t id = 0) const;
705
  uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,
706
                                size_t id = 0) const;
707
  size_t get_header_value_count(const std::string &key) const;
708
  void set_header(const std::string &key, const std::string &val);
709
710
  bool has_param(const std::string &key) const;
711
  std::string get_param_value(const std::string &key, size_t id = 0) const;
712
  size_t get_param_value_count(const std::string &key) const;
713
714
  bool is_multipart_form_data() const;
715
716
  bool has_file(const std::string &key) const;
717
  MultipartFormData get_file_value(const std::string &key) const;
718
  std::vector<MultipartFormData> get_file_values(const std::string &key) const;
719
720
  // private members...
721
  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
722
  size_t content_length_ = 0;
723
  ContentProvider content_provider_;
724
  bool is_chunked_content_provider_ = false;
725
  size_t authorization_count_ = 0;
726
  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
727
      (std::chrono::steady_clock::time_point::min)();
728
};
729
730
struct Response {
731
  std::string version;
732
  int status = -1;
733
  std::string reason;
734
  Headers headers;
735
  std::string body;
736
  std::string location; // Redirect location
737
738
  bool has_header(const std::string &key) const;
739
  std::string get_header_value(const std::string &key, const char *def = "",
740
                               size_t id = 0) const;
741
  uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,
742
                                size_t id = 0) const;
743
  size_t get_header_value_count(const std::string &key) const;
744
  void set_header(const std::string &key, const std::string &val);
745
746
  void set_redirect(const std::string &url, int status = StatusCode::Found_302);
747
  void set_content(const char *s, size_t n, const std::string &content_type);
748
  void set_content(const std::string &s, const std::string &content_type);
749
  void set_content(std::string &&s, const std::string &content_type);
750
751
  void set_content_provider(
752
      size_t length, const std::string &content_type, ContentProvider provider,
753
      ContentProviderResourceReleaser resource_releaser = nullptr);
754
755
  void set_content_provider(
756
      const std::string &content_type, ContentProviderWithoutLength provider,
757
      ContentProviderResourceReleaser resource_releaser = nullptr);
758
759
  void set_chunked_content_provider(
760
      const std::string &content_type, ContentProviderWithoutLength provider,
761
      ContentProviderResourceReleaser resource_releaser = nullptr);
762
763
  void set_file_content(const std::string &path,
764
                        const std::string &content_type);
765
  void set_file_content(const std::string &path);
766
767
3.90k
  Response() = default;
768
  Response(const Response &) = default;
769
  Response &operator=(const Response &) = default;
770
  Response(Response &&) = default;
771
  Response &operator=(Response &&) = default;
772
3.90k
  ~Response() {
773
3.90k
    if (content_provider_resource_releaser_) {
774
0
      content_provider_resource_releaser_(content_provider_success_);
775
0
    }
776
3.90k
  }
777
778
  // private members...
779
  size_t content_length_ = 0;
780
  ContentProvider content_provider_;
781
  ContentProviderResourceReleaser content_provider_resource_releaser_;
782
  bool is_chunked_content_provider_ = false;
783
  bool content_provider_success_ = false;
784
  std::string file_content_path_;
785
  std::string file_content_content_type_;
786
};
787
788
class Stream {
789
public:
790
7.81k
  virtual ~Stream() = default;
791
792
  virtual bool is_readable() const = 0;
793
  virtual bool wait_readable() const = 0;
794
  virtual bool wait_writable() const = 0;
795
796
  virtual ssize_t read(char *ptr, size_t size) = 0;
797
  virtual ssize_t write(const char *ptr, size_t size) = 0;
798
  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
799
  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
800
  virtual socket_t socket() const = 0;
801
802
  virtual time_t duration() const = 0;
803
804
  ssize_t write(const char *ptr);
805
  ssize_t write(const std::string &s);
806
};
807
808
class TaskQueue {
809
public:
810
0
  TaskQueue() = default;
811
0
  virtual ~TaskQueue() = default;
812
813
  virtual bool enqueue(std::function<void()> fn) = 0;
814
  virtual void shutdown() = 0;
815
816
0
  virtual void on_idle() {}
817
};
818
819
class ThreadPool final : public TaskQueue {
820
public:
821
  explicit ThreadPool(size_t n, size_t mqr = 0)
822
0
      : shutdown_(false), max_queued_requests_(mqr) {
823
0
    while (n) {
824
0
      threads_.emplace_back(worker(*this));
825
0
      n--;
826
0
    }
827
0
  }
828
829
  ThreadPool(const ThreadPool &) = delete;
830
0
  ~ThreadPool() override = default;
831
832
0
  bool enqueue(std::function<void()> fn) override {
833
0
    {
834
0
      std::unique_lock<std::mutex> lock(mutex_);
835
0
      if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
836
0
        return false;
837
0
      }
838
0
      jobs_.push_back(std::move(fn));
839
0
    }
840
841
0
    cond_.notify_one();
842
0
    return true;
843
0
  }
844
845
0
  void shutdown() override {
846
    // Stop all worker threads...
847
0
    {
848
0
      std::unique_lock<std::mutex> lock(mutex_);
849
0
      shutdown_ = true;
850
0
    }
851
852
0
    cond_.notify_all();
853
854
    // Join...
855
0
    for (auto &t : threads_) {
856
0
      t.join();
857
0
    }
858
0
  }
859
860
private:
861
  struct worker {
862
0
    explicit worker(ThreadPool &pool) : pool_(pool) {}
863
864
0
    void operator()() {
865
0
      for (;;) {
866
0
        std::function<void()> fn;
867
0
        {
868
0
          std::unique_lock<std::mutex> lock(pool_.mutex_);
869
870
0
          pool_.cond_.wait(
871
0
              lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
872
873
0
          if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
874
875
0
          fn = pool_.jobs_.front();
876
0
          pool_.jobs_.pop_front();
877
0
        }
878
879
0
        assert(true == static_cast<bool>(fn));
880
0
        fn();
881
0
      }
882
883
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \
884
    !defined(LIBRESSL_VERSION_NUMBER)
885
      OPENSSL_thread_stop();
886
#endif
887
0
    }
888
889
    ThreadPool &pool_;
890
  };
891
  friend struct worker;
892
893
  std::vector<std::thread> threads_;
894
  std::list<std::function<void()>> jobs_;
895
896
  bool shutdown_;
897
  size_t max_queued_requests_ = 0;
898
899
  std::condition_variable cond_;
900
  std::mutex mutex_;
901
};
902
903
using Logger = std::function<void(const Request &, const Response &)>;
904
905
using SocketOptions = std::function<void(socket_t sock)>;
906
907
namespace detail {
908
909
bool set_socket_opt_impl(socket_t sock, int level, int optname,
910
                         const void *optval, socklen_t optlen);
911
bool set_socket_opt(socket_t sock, int level, int optname, int opt);
912
bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,
913
                         time_t usec);
914
915
} // namespace detail
916
917
void default_socket_options(socket_t sock);
918
919
const char *status_message(int status);
920
921
std::string get_bearer_token_auth(const Request &req);
922
923
namespace detail {
924
925
class MatcherBase {
926
public:
927
12
  MatcherBase(std::string pattern) : pattern_(pattern) {}
928
0
  virtual ~MatcherBase() = default;
929
930
1.31k
  const std::string &pattern() const { return pattern_; }
931
932
  // Match request path and populate its matches and
933
  virtual bool match(Request &request) const = 0;
934
935
private:
936
  std::string pattern_;
937
};
938
939
/**
940
 * Captures parameters in request path and stores them in Request::path_params
941
 *
942
 * Capture name is a substring of a pattern from : to /.
943
 * The rest of the pattern is matched against the request path directly
944
 * Parameters are captured starting from the next character after
945
 * the end of the last matched static pattern fragment until the next /.
946
 *
947
 * Example pattern:
948
 * "/path/fragments/:capture/more/fragments/:second_capture"
949
 * Static fragments:
950
 * "/path/fragments/", "more/fragments/"
951
 *
952
 * Given the following request path:
953
 * "/path/fragments/:1/more/fragments/:2"
954
 * the resulting capture will be
955
 * {{"capture", "1"}, {"second_capture", "2"}}
956
 */
957
class PathParamsMatcher final : public MatcherBase {
958
public:
959
  PathParamsMatcher(const std::string &pattern);
960
961
  bool match(Request &request) const override;
962
963
private:
964
  // Treat segment separators as the end of path parameter capture
965
  // Does not need to handle query parameters as they are parsed before path
966
  // matching
967
  static constexpr char separator = '/';
968
969
  // Contains static path fragments to match against, excluding the '/' after
970
  // path params
971
  // Fragments are separated by path params
972
  std::vector<std::string> static_fragments_;
973
  // Stores the names of the path parameters to be used as keys in the
974
  // Request::path_params map
975
  std::vector<std::string> param_names_;
976
};
977
978
/**
979
 * Performs std::regex_match on request path
980
 * and stores the result in Request::matches
981
 *
982
 * Note that regex match is performed directly on the whole request.
983
 * This means that wildcard patterns may match multiple path segments with /:
984
 * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".
985
 */
986
class RegexMatcher final : public MatcherBase {
987
public:
988
  RegexMatcher(const std::string &pattern)
989
12
      : MatcherBase(pattern), regex_(pattern) {}
990
991
  bool match(Request &request) const override;
992
993
private:
994
  std::regex regex_;
995
};
996
997
ssize_t write_headers(Stream &strm, const Headers &headers);
998
999
} // namespace detail
1000
1001
class Server {
1002
public:
1003
  using Handler = std::function<void(const Request &, Response &)>;
1004
1005
  using ExceptionHandler =
1006
      std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1007
1008
  enum class HandlerResponse {
1009
    Handled,
1010
    Unhandled,
1011
  };
1012
  using HandlerWithResponse =
1013
      std::function<HandlerResponse(const Request &, Response &)>;
1014
1015
  using HandlerWithContentReader = std::function<void(
1016
      const Request &, Response &, const ContentReader &content_reader)>;
1017
1018
  using Expect100ContinueHandler =
1019
      std::function<int(const Request &, Response &)>;
1020
1021
  Server();
1022
1023
  virtual ~Server();
1024
1025
  virtual bool is_valid() const;
1026
1027
  Server &Get(const std::string &pattern, Handler handler);
1028
  Server &Post(const std::string &pattern, Handler handler);
1029
  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1030
  Server &Put(const std::string &pattern, Handler handler);
1031
  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1032
  Server &Patch(const std::string &pattern, Handler handler);
1033
  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1034
  Server &Delete(const std::string &pattern, Handler handler);
1035
  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1036
  Server &Options(const std::string &pattern, Handler handler);
1037
1038
  bool set_base_dir(const std::string &dir,
1039
                    const std::string &mount_point = std::string());
1040
  bool set_mount_point(const std::string &mount_point, const std::string &dir,
1041
                       Headers headers = Headers());
1042
  bool remove_mount_point(const std::string &mount_point);
1043
  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1044
                                                  const std::string &mime);
1045
  Server &set_default_file_mimetype(const std::string &mime);
1046
  Server &set_file_request_handler(Handler handler);
1047
1048
  template <class ErrorHandlerFunc>
1049
  Server &set_error_handler(ErrorHandlerFunc &&handler) {
1050
    return set_error_handler_core(
1051
        std::forward<ErrorHandlerFunc>(handler),
1052
        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1053
  }
1054
1055
  Server &set_exception_handler(ExceptionHandler handler);
1056
1057
  Server &set_pre_routing_handler(HandlerWithResponse handler);
1058
  Server &set_post_routing_handler(Handler handler);
1059
1060
  Server &set_pre_request_handler(HandlerWithResponse handler);
1061
1062
  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1063
  Server &set_logger(Logger logger);
1064
1065
  Server &set_address_family(int family);
1066
  Server &set_tcp_nodelay(bool on);
1067
  Server &set_ipv6_v6only(bool on);
1068
  Server &set_socket_options(SocketOptions socket_options);
1069
1070
  Server &set_default_headers(Headers headers);
1071
  Server &
1072
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1073
1074
  Server &set_keep_alive_max_count(size_t count);
1075
  Server &set_keep_alive_timeout(time_t sec);
1076
1077
  Server &set_read_timeout(time_t sec, time_t usec = 0);
1078
  template <class Rep, class Period>
1079
  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1080
1081
  Server &set_write_timeout(time_t sec, time_t usec = 0);
1082
  template <class Rep, class Period>
1083
  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1084
1085
  Server &set_idle_interval(time_t sec, time_t usec = 0);
1086
  template <class Rep, class Period>
1087
  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1088
1089
  Server &set_payload_max_length(size_t length);
1090
1091
  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1092
  int bind_to_any_port(const std::string &host, int socket_flags = 0);
1093
  bool listen_after_bind();
1094
1095
  bool listen(const std::string &host, int port, int socket_flags = 0);
1096
1097
  bool is_running() const;
1098
  void wait_until_ready() const;
1099
  void stop();
1100
  void decommission();
1101
1102
  std::function<TaskQueue *(void)> new_task_queue;
1103
1104
protected:
1105
  bool process_request(Stream &strm, const std::string &remote_addr,
1106
                       int remote_port, const std::string &local_addr,
1107
                       int local_port, bool close_connection,
1108
                       bool &connection_closed,
1109
                       const std::function<void(Request &)> &setup_request);
1110
1111
  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1112
  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
1113
  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
1114
  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1115
  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1116
  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1117
  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
1118
  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
1119
  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
1120
  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1121
1122
private:
1123
  using Handlers =
1124
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1125
  using HandlersForContentReader =
1126
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1127
                            HandlerWithContentReader>>;
1128
1129
  static std::unique_ptr<detail::MatcherBase>
1130
  make_matcher(const std::string &pattern);
1131
1132
  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1133
  Server &set_error_handler_core(Handler handler, std::false_type);
1134
1135
  socket_t create_server_socket(const std::string &host, int port,
1136
                                int socket_flags,
1137
                                SocketOptions socket_options) const;
1138
  int bind_internal(const std::string &host, int port, int socket_flags);
1139
  bool listen_internal();
1140
1141
  bool routing(Request &req, Response &res, Stream &strm);
1142
  bool handle_file_request(const Request &req, Response &res);
1143
  bool dispatch_request(Request &req, Response &res,
1144
                        const Handlers &handlers) const;
1145
  bool dispatch_request_for_content_reader(
1146
      Request &req, Response &res, ContentReader content_reader,
1147
      const HandlersForContentReader &handlers) const;
1148
1149
  bool parse_request_line(const char *s, Request &req) const;
1150
  void apply_ranges(const Request &req, Response &res,
1151
                    std::string &content_type, std::string &boundary) const;
1152
  bool write_response(Stream &strm, bool close_connection, Request &req,
1153
                      Response &res);
1154
  bool write_response_with_content(Stream &strm, bool close_connection,
1155
                                   const Request &req, Response &res);
1156
  bool write_response_core(Stream &strm, bool close_connection,
1157
                           const Request &req, Response &res,
1158
                           bool need_apply_ranges);
1159
  bool write_content_with_provider(Stream &strm, const Request &req,
1160
                                   Response &res, const std::string &boundary,
1161
                                   const std::string &content_type);
1162
  bool read_content(Stream &strm, Request &req, Response &res);
1163
  bool
1164
  read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
1165
                                     ContentReceiver receiver,
1166
                                     MultipartContentHeader multipart_header,
1167
                                     ContentReceiver multipart_receiver);
1168
  bool read_content_core(Stream &strm, Request &req, Response &res,
1169
                         ContentReceiver receiver,
1170
                         MultipartContentHeader multipart_header,
1171
                         ContentReceiver multipart_receiver) const;
1172
1173
  virtual bool process_and_close_socket(socket_t sock);
1174
1175
  std::atomic<bool> is_running_{false};
1176
  std::atomic<bool> is_decommissioned{false};
1177
1178
  struct MountPointEntry {
1179
    std::string mount_point;
1180
    std::string base_dir;
1181
    Headers headers;
1182
  };
1183
  std::vector<MountPointEntry> base_dirs_;
1184
  std::map<std::string, std::string> file_extension_and_mimetype_map_;
1185
  std::string default_file_mimetype_ = "application/octet-stream";
1186
  Handler file_request_handler_;
1187
1188
  Handlers get_handlers_;
1189
  Handlers post_handlers_;
1190
  HandlersForContentReader post_handlers_for_content_reader_;
1191
  Handlers put_handlers_;
1192
  HandlersForContentReader put_handlers_for_content_reader_;
1193
  Handlers patch_handlers_;
1194
  HandlersForContentReader patch_handlers_for_content_reader_;
1195
  Handlers delete_handlers_;
1196
  HandlersForContentReader delete_handlers_for_content_reader_;
1197
  Handlers options_handlers_;
1198
1199
  HandlerWithResponse error_handler_;
1200
  ExceptionHandler exception_handler_;
1201
  HandlerWithResponse pre_routing_handler_;
1202
  Handler post_routing_handler_;
1203
  HandlerWithResponse pre_request_handler_;
1204
  Expect100ContinueHandler expect_100_continue_handler_;
1205
1206
  Logger logger_;
1207
1208
  int address_family_ = AF_UNSPEC;
1209
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1210
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1211
  SocketOptions socket_options_ = default_socket_options;
1212
1213
  Headers default_headers_;
1214
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1215
      detail::write_headers;
1216
};
1217
1218
enum class Error {
1219
  Success = 0,
1220
  Unknown,
1221
  Connection,
1222
  BindIPAddress,
1223
  Read,
1224
  Write,
1225
  ExceedRedirectCount,
1226
  Canceled,
1227
  SSLConnection,
1228
  SSLLoadingCerts,
1229
  SSLServerVerification,
1230
  SSLServerHostnameVerification,
1231
  UnsupportedMultipartBoundaryChars,
1232
  Compression,
1233
  ConnectionTimeout,
1234
  ProxyConnection,
1235
1236
  // For internal use only
1237
  SSLPeerCouldBeClosed_,
1238
};
1239
1240
std::string to_string(Error error);
1241
1242
std::ostream &operator<<(std::ostream &os, const Error &obj);
1243
1244
class Result {
1245
public:
1246
  Result() = default;
1247
  Result(std::unique_ptr<Response> &&res, Error err,
1248
         Headers &&request_headers = Headers{})
1249
      : res_(std::move(res)), err_(err),
1250
0
        request_headers_(std::move(request_headers)) {}
1251
  // Response
1252
0
  operator bool() const { return res_ != nullptr; }
1253
0
  bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1254
0
  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1255
0
  const Response &value() const { return *res_; }
1256
0
  Response &value() { return *res_; }
1257
0
  const Response &operator*() const { return *res_; }
1258
0
  Response &operator*() { return *res_; }
1259
0
  const Response *operator->() const { return res_.get(); }
1260
0
  Response *operator->() { return res_.get(); }
1261
1262
  // Error
1263
0
  Error error() const { return err_; }
1264
1265
  // Request Headers
1266
  bool has_request_header(const std::string &key) const;
1267
  std::string get_request_header_value(const std::string &key,
1268
                                       const char *def = "",
1269
                                       size_t id = 0) const;
1270
  uint64_t get_request_header_value_u64(const std::string &key,
1271
                                        uint64_t def = 0, size_t id = 0) const;
1272
  size_t get_request_header_value_count(const std::string &key) const;
1273
1274
private:
1275
  std::unique_ptr<Response> res_;
1276
  Error err_ = Error::Unknown;
1277
  Headers request_headers_;
1278
};
1279
1280
class ClientImpl {
1281
public:
1282
  explicit ClientImpl(const std::string &host);
1283
1284
  explicit ClientImpl(const std::string &host, int port);
1285
1286
  explicit ClientImpl(const std::string &host, int port,
1287
                      const std::string &client_cert_path,
1288
                      const std::string &client_key_path);
1289
1290
  virtual ~ClientImpl();
1291
1292
  virtual bool is_valid() const;
1293
1294
  // clang-format off
1295
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1296
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1297
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1298
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1299
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1300
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1301
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1302
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1303
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1304
1305
  Result Head(const std::string &path);
1306
  Result Head(const std::string &path, const Headers &headers);
1307
1308
  Result Post(const std::string &path);
1309
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1310
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1311
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1312
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1313
  Result Post(const std::string &path, const Params &params);
1314
  Result Post(const std::string &path, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1315
  Result Post(const std::string &path, const Headers &headers);
1316
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1317
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1318
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1319
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1320
  Result Post(const std::string &path, const Headers &headers, const Params &params);
1321
  Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1322
  Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr);
1323
  Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1324
1325
  Result Put(const std::string &path);
1326
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1327
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1328
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1329
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1330
  Result Put(const std::string &path, const Params &params);
1331
  Result Put(const std::string &path, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1332
  Result Put(const std::string &path, const Headers &headers);
1333
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1334
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1335
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1336
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1337
  Result Put(const std::string &path, const Headers &headers, const Params &params);
1338
  Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1339
  Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr);
1340
  Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1341
1342
  Result Patch(const std::string &path);
1343
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1344
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1345
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1346
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1347
  Result Patch(const std::string &path, const Params &params);
1348
  Result Patch(const std::string &path, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1349
  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
1350
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1351
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1352
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1353
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1354
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
1355
  Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1356
  Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr);
1357
  Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1358
1359
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1360
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1361
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1362
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1363
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1364
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1365
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1366
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1367
1368
  Result Options(const std::string &path);
1369
  Result Options(const std::string &path, const Headers &headers);
1370
  // clang-format on
1371
1372
  bool send(Request &req, Response &res, Error &error);
1373
  Result send(const Request &req);
1374
1375
  void stop();
1376
1377
  std::string host() const;
1378
  int port() const;
1379
1380
  size_t is_socket_open() const;
1381
  socket_t socket() const;
1382
1383
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1384
1385
  void set_default_headers(Headers headers);
1386
1387
  void
1388
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1389
1390
  void set_address_family(int family);
1391
  void set_tcp_nodelay(bool on);
1392
  void set_ipv6_v6only(bool on);
1393
  void set_socket_options(SocketOptions socket_options);
1394
1395
  void set_connection_timeout(time_t sec, time_t usec = 0);
1396
  template <class Rep, class Period>
1397
  void
1398
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1399
1400
  void set_read_timeout(time_t sec, time_t usec = 0);
1401
  template <class Rep, class Period>
1402
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1403
1404
  void set_write_timeout(time_t sec, time_t usec = 0);
1405
  template <class Rep, class Period>
1406
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1407
1408
  void set_max_timeout(time_t msec);
1409
  template <class Rep, class Period>
1410
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1411
1412
  void set_basic_auth(const std::string &username, const std::string &password);
1413
  void set_bearer_token_auth(const std::string &token);
1414
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1415
  void set_digest_auth(const std::string &username,
1416
                       const std::string &password);
1417
#endif
1418
1419
  void set_keep_alive(bool on);
1420
  void set_follow_location(bool on);
1421
1422
  void set_url_encode(bool on);
1423
1424
  void set_compress(bool on);
1425
1426
  void set_decompress(bool on);
1427
1428
  void set_interface(const std::string &intf);
1429
1430
  void set_proxy(const std::string &host, int port);
1431
  void set_proxy_basic_auth(const std::string &username,
1432
                            const std::string &password);
1433
  void set_proxy_bearer_token_auth(const std::string &token);
1434
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1435
  void set_proxy_digest_auth(const std::string &username,
1436
                             const std::string &password);
1437
#endif
1438
1439
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1440
  void set_ca_cert_path(const std::string &ca_cert_file_path,
1441
                        const std::string &ca_cert_dir_path = std::string());
1442
  void set_ca_cert_store(X509_STORE *ca_cert_store);
1443
  X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1444
#endif
1445
1446
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1447
  void enable_server_certificate_verification(bool enabled);
1448
  void enable_server_hostname_verification(bool enabled);
1449
  void set_server_certificate_verifier(
1450
      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1451
#endif
1452
1453
  void set_logger(Logger logger);
1454
1455
protected:
1456
  struct Socket {
1457
    socket_t sock = INVALID_SOCKET;
1458
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1459
    SSL *ssl = nullptr;
1460
#endif
1461
1462
0
    bool is_open() const { return sock != INVALID_SOCKET; }
1463
  };
1464
1465
  virtual bool create_and_connect_socket(Socket &socket, Error &error);
1466
1467
  // All of:
1468
  //   shutdown_ssl
1469
  //   shutdown_socket
1470
  //   close_socket
1471
  // should ONLY be called when socket_mutex_ is locked.
1472
  // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1473
  // with a DIFFERENT thread sending requests using that socket.
1474
  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1475
  void shutdown_socket(Socket &socket) const;
1476
  void close_socket(Socket &socket);
1477
1478
  bool process_request(Stream &strm, Request &req, Response &res,
1479
                       bool close_connection, Error &error);
1480
1481
  bool write_content_with_provider(Stream &strm, const Request &req,
1482
                                   Error &error) const;
1483
1484
  void copy_settings(const ClientImpl &rhs);
1485
1486
  // Socket endpoint information
1487
  const std::string host_;
1488
  const int port_;
1489
  const std::string host_and_port_;
1490
1491
  // Current open socket
1492
  Socket socket_;
1493
  mutable std::mutex socket_mutex_;
1494
  std::recursive_mutex request_mutex_;
1495
1496
  // These are all protected under socket_mutex
1497
  size_t socket_requests_in_flight_ = 0;
1498
  std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1499
  bool socket_should_be_closed_when_request_is_done_ = false;
1500
1501
  // Hostname-IP map
1502
  std::map<std::string, std::string> addr_map_;
1503
1504
  // Default headers
1505
  Headers default_headers_;
1506
1507
  // Header writer
1508
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1509
      detail::write_headers;
1510
1511
  // Settings
1512
  std::string client_cert_path_;
1513
  std::string client_key_path_;
1514
1515
  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1516
  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1517
  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1518
  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1519
  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1520
  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1521
  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
1522
1523
  std::string basic_auth_username_;
1524
  std::string basic_auth_password_;
1525
  std::string bearer_token_auth_token_;
1526
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1527
  std::string digest_auth_username_;
1528
  std::string digest_auth_password_;
1529
#endif
1530
1531
  bool keep_alive_ = false;
1532
  bool follow_location_ = false;
1533
1534
  bool url_encode_ = true;
1535
1536
  int address_family_ = AF_UNSPEC;
1537
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1538
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1539
  SocketOptions socket_options_ = nullptr;
1540
1541
  bool compress_ = false;
1542
  bool decompress_ = true;
1543
1544
  std::string interface_;
1545
1546
  std::string proxy_host_;
1547
  int proxy_port_ = -1;
1548
1549
  std::string proxy_basic_auth_username_;
1550
  std::string proxy_basic_auth_password_;
1551
  std::string proxy_bearer_token_auth_token_;
1552
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1553
  std::string proxy_digest_auth_username_;
1554
  std::string proxy_digest_auth_password_;
1555
#endif
1556
1557
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1558
  std::string ca_cert_file_path_;
1559
  std::string ca_cert_dir_path_;
1560
1561
  X509_STORE *ca_cert_store_ = nullptr;
1562
#endif
1563
1564
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1565
  bool server_certificate_verification_ = true;
1566
  bool server_hostname_verification_ = true;
1567
  std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
1568
#endif
1569
1570
  Logger logger_;
1571
1572
private:
1573
  bool send_(Request &req, Response &res, Error &error);
1574
  Result send_(Request &&req);
1575
1576
  socket_t create_client_socket(Error &error) const;
1577
  bool read_response_line(Stream &strm, const Request &req,
1578
                          Response &res) const;
1579
  bool write_request(Stream &strm, Request &req, bool close_connection,
1580
                     Error &error);
1581
  bool redirect(Request &req, Response &res, Error &error);
1582
  bool create_redirect_client(const std::string &scheme,
1583
                              const std::string &host, int port, Request &req,
1584
                              Response &res, const std::string &path,
1585
                              const std::string &location, Error &error);
1586
  template <typename ClientType> void setup_redirect_client(ClientType &client);
1587
  bool handle_request(Stream &strm, Request &req, Response &res,
1588
                      bool close_connection, Error &error);
1589
  std::unique_ptr<Response> send_with_content_provider(
1590
      Request &req, const char *body, size_t content_length,
1591
      ContentProvider content_provider,
1592
      ContentProviderWithoutLength content_provider_without_length,
1593
      const std::string &content_type, Error &error);
1594
  Result send_with_content_provider(
1595
      const std::string &method, const std::string &path,
1596
      const Headers &headers, const char *body, size_t content_length,
1597
      ContentProvider content_provider,
1598
      ContentProviderWithoutLength content_provider_without_length,
1599
      const std::string &content_type, UploadProgress progress);
1600
  ContentProviderWithoutLength get_multipart_content_provider(
1601
      const std::string &boundary,
1602
      const MultipartFormDataItemsForClientInput &items,
1603
      const MultipartFormDataProviderItems &provider_items) const;
1604
1605
  std::string adjust_host_string(const std::string &host) const;
1606
1607
  virtual bool
1608
  process_socket(const Socket &socket,
1609
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
1610
                 std::function<bool(Stream &strm)> callback);
1611
  virtual bool is_ssl() const;
1612
};
1613
1614
class Client {
1615
public:
1616
  // Universal interface
1617
  explicit Client(const std::string &scheme_host_port);
1618
1619
  explicit Client(const std::string &scheme_host_port,
1620
                  const std::string &client_cert_path,
1621
                  const std::string &client_key_path);
1622
1623
  // HTTP only interface
1624
  explicit Client(const std::string &host, int port);
1625
1626
  explicit Client(const std::string &host, int port,
1627
                  const std::string &client_cert_path,
1628
                  const std::string &client_key_path);
1629
1630
  Client(Client &&) = default;
1631
  Client &operator=(Client &&) = default;
1632
1633
  ~Client();
1634
1635
  bool is_valid() const;
1636
1637
  // clang-format off
1638
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
1639
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1640
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1641
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1642
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1643
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1644
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
1645
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1646
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
1647
1648
  Result Head(const std::string &path);
1649
  Result Head(const std::string &path, const Headers &headers);
1650
1651
  Result Post(const std::string &path);
1652
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1653
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1654
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1655
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1656
  Result Post(const std::string &path, const Params &params);
1657
  Result Post(const std::string &path, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1658
  Result Post(const std::string &path, const Headers &headers);
1659
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1660
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1661
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1662
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1663
  Result Post(const std::string &path, const Headers &headers, const Params &params);
1664
  Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1665
  Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr);
1666
  Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1667
1668
  Result Put(const std::string &path);
1669
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1670
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1671
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1672
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1673
  Result Put(const std::string &path, const Params &params);
1674
  Result Put(const std::string &path, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1675
  Result Put(const std::string &path, const Headers &headers);
1676
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1677
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1678
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1679
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1680
  Result Put(const std::string &path, const Headers &headers, const Params &params);
1681
  Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1682
  Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr);
1683
  Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1684
1685
  Result Patch(const std::string &path);
1686
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1687
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1688
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1689
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1690
  Result Patch(const std::string &path, const Params &params);
1691
  Result Patch(const std::string &path, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1692
  Result Patch(const std::string &path, const Headers &headers);
1693
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
1694
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
1695
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1696
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
1697
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
1698
  Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, UploadProgress progress = nullptr);
1699
  Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const std::string &boundary, UploadProgress progress = nullptr);
1700
  Result Patch(const std::string &path, const Headers &headers, const MultipartFormDataItemsForClientInput &items, const MultipartFormDataProviderItems &provider_items, UploadProgress progress = nullptr);
1701
1702
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
1703
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1704
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1705
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
1706
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
1707
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
1708
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
1709
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
1710
1711
  Result Options(const std::string &path);
1712
  Result Options(const std::string &path, const Headers &headers);
1713
  // clang-format on
1714
1715
  bool send(Request &req, Response &res, Error &error);
1716
  Result send(const Request &req);
1717
1718
  void stop();
1719
1720
  std::string host() const;
1721
  int port() const;
1722
1723
  size_t is_socket_open() const;
1724
  socket_t socket() const;
1725
1726
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1727
1728
  void set_default_headers(Headers headers);
1729
1730
  void
1731
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1732
1733
  void set_address_family(int family);
1734
  void set_tcp_nodelay(bool on);
1735
  void set_socket_options(SocketOptions socket_options);
1736
1737
  void set_connection_timeout(time_t sec, time_t usec = 0);
1738
  template <class Rep, class Period>
1739
  void
1740
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1741
1742
  void set_read_timeout(time_t sec, time_t usec = 0);
1743
  template <class Rep, class Period>
1744
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1745
1746
  void set_write_timeout(time_t sec, time_t usec = 0);
1747
  template <class Rep, class Period>
1748
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1749
1750
  void set_max_timeout(time_t msec);
1751
  template <class Rep, class Period>
1752
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1753
1754
  void set_basic_auth(const std::string &username, const std::string &password);
1755
  void set_bearer_token_auth(const std::string &token);
1756
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1757
  void set_digest_auth(const std::string &username,
1758
                       const std::string &password);
1759
#endif
1760
1761
  void set_keep_alive(bool on);
1762
  void set_follow_location(bool on);
1763
1764
  void set_url_encode(bool on);
1765
1766
  void set_compress(bool on);
1767
1768
  void set_decompress(bool on);
1769
1770
  void set_interface(const std::string &intf);
1771
1772
  void set_proxy(const std::string &host, int port);
1773
  void set_proxy_basic_auth(const std::string &username,
1774
                            const std::string &password);
1775
  void set_proxy_bearer_token_auth(const std::string &token);
1776
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1777
  void set_proxy_digest_auth(const std::string &username,
1778
                             const std::string &password);
1779
#endif
1780
1781
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1782
  void enable_server_certificate_verification(bool enabled);
1783
  void enable_server_hostname_verification(bool enabled);
1784
  void set_server_certificate_verifier(
1785
      std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1786
#endif
1787
1788
  void set_logger(Logger logger);
1789
1790
  // SSL
1791
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1792
  void set_ca_cert_path(const std::string &ca_cert_file_path,
1793
                        const std::string &ca_cert_dir_path = std::string());
1794
1795
  void set_ca_cert_store(X509_STORE *ca_cert_store);
1796
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
1797
1798
  long get_openssl_verify_result() const;
1799
1800
  SSL_CTX *ssl_context() const;
1801
#endif
1802
1803
private:
1804
  std::unique_ptr<ClientImpl> cli_;
1805
1806
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1807
  bool is_ssl_ = false;
1808
#endif
1809
};
1810
1811
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1812
class SSLServer : public Server {
1813
public:
1814
  SSLServer(const char *cert_path, const char *private_key_path,
1815
            const char *client_ca_cert_file_path = nullptr,
1816
            const char *client_ca_cert_dir_path = nullptr,
1817
            const char *private_key_password = nullptr);
1818
1819
  SSLServer(X509 *cert, EVP_PKEY *private_key,
1820
            X509_STORE *client_ca_cert_store = nullptr);
1821
1822
  SSLServer(
1823
      const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1824
1825
  ~SSLServer() override;
1826
1827
  bool is_valid() const override;
1828
1829
  SSL_CTX *ssl_context() const;
1830
1831
  void update_certs(X509 *cert, EVP_PKEY *private_key,
1832
                    X509_STORE *client_ca_cert_store = nullptr);
1833
1834
private:
1835
  bool process_and_close_socket(socket_t sock) override;
1836
1837
  SSL_CTX *ctx_;
1838
  std::mutex ctx_mutex_;
1839
};
1840
1841
class SSLClient final : public ClientImpl {
1842
public:
1843
  explicit SSLClient(const std::string &host);
1844
1845
  explicit SSLClient(const std::string &host, int port);
1846
1847
  explicit SSLClient(const std::string &host, int port,
1848
                     const std::string &client_cert_path,
1849
                     const std::string &client_key_path,
1850
                     const std::string &private_key_password = std::string());
1851
1852
  explicit SSLClient(const std::string &host, int port, X509 *client_cert,
1853
                     EVP_PKEY *client_key,
1854
                     const std::string &private_key_password = std::string());
1855
1856
  ~SSLClient() override;
1857
1858
  bool is_valid() const override;
1859
1860
  void set_ca_cert_store(X509_STORE *ca_cert_store);
1861
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
1862
1863
  long get_openssl_verify_result() const;
1864
1865
  SSL_CTX *ssl_context() const;
1866
1867
private:
1868
  bool create_and_connect_socket(Socket &socket, Error &error) override;
1869
  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1870
  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
1871
1872
  bool
1873
  process_socket(const Socket &socket,
1874
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
1875
                 std::function<bool(Stream &strm)> callback) override;
1876
  bool is_ssl() const override;
1877
1878
  bool connect_with_proxy(
1879
      Socket &sock,
1880
      std::chrono::time_point<std::chrono::steady_clock> start_time,
1881
      Response &res, bool &success, Error &error);
1882
  bool initialize_ssl(Socket &socket, Error &error);
1883
1884
  bool load_certs();
1885
1886
  bool verify_host(X509 *server_cert) const;
1887
  bool verify_host_with_subject_alt_name(X509 *server_cert) const;
1888
  bool verify_host_with_common_name(X509 *server_cert) const;
1889
  bool check_host_name(const char *pattern, size_t pattern_len) const;
1890
1891
  SSL_CTX *ctx_;
1892
  std::mutex ctx_mutex_;
1893
  std::once_flag initialize_cert_;
1894
1895
  std::vector<std::string> host_components_;
1896
1897
  long verify_result_ = 0;
1898
1899
  friend class ClientImpl;
1900
};
1901
#endif
1902
1903
/*
1904
 * Implementation of template methods.
1905
 */
1906
1907
namespace detail {
1908
1909
template <typename T, typename U>
1910
inline void duration_to_sec_and_usec(const T &duration, U callback) {
1911
  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
1912
  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
1913
                  duration - std::chrono::seconds(sec))
1914
                  .count();
1915
  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
1916
}
1917
1918
1.46k
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
1919
1.46k
  return N - 1;
1920
1.46k
}
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
1918
1.46k
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
1919
1.46k
  return N - 1;
1920
1.46k
}
Unexecuted instantiation: unsigned long httplib::detail::str_len<8ul>(char const (&) [8ul])
1921
1922
328
inline bool is_numeric(const std::string &str) {
1923
328
  return !str.empty() &&
1924
328
         std::all_of(str.cbegin(), str.cend(),
1925
6.55k
                     [](unsigned char c) { return std::isdigit(c); });
1926
328
}
1927
1928
inline uint64_t get_header_value_u64(const Headers &headers,
1929
                                     const std::string &key, uint64_t def,
1930
328
                                     size_t id, bool &is_invalid_value) {
1931
328
  is_invalid_value = false;
1932
328
  auto rng = headers.equal_range(key);
1933
328
  auto it = rng.first;
1934
328
  std::advance(it, static_cast<ssize_t>(id));
1935
328
  if (it != rng.second) {
1936
328
    if (is_numeric(it->second)) {
1937
320
      return std::strtoull(it->second.data(), nullptr, 10);
1938
320
    } else {
1939
8
      is_invalid_value = true;
1940
8
    }
1941
328
  }
1942
8
  return def;
1943
328
}
1944
1945
inline uint64_t get_header_value_u64(const Headers &headers,
1946
                                     const std::string &key, uint64_t def,
1947
132
                                     size_t id) {
1948
132
  bool dummy = false;
1949
132
  return get_header_value_u64(headers, key, def, id, dummy);
1950
132
}
1951
1952
} // namespace detail
1953
1954
inline uint64_t Request::get_header_value_u64(const std::string &key,
1955
132
                                              uint64_t def, size_t id) const {
1956
132
  return detail::get_header_value_u64(headers, key, def, id);
1957
132
}
1958
1959
inline uint64_t Response::get_header_value_u64(const std::string &key,
1960
0
                                               uint64_t def, size_t id) const {
1961
0
  return detail::get_header_value_u64(headers, key, def, id);
1962
0
}
1963
1964
namespace detail {
1965
1966
inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
1967
0
                                const void *optval, socklen_t optlen) {
1968
0
  return setsockopt(sock, level, optname,
1969
#ifdef _WIN32
1970
                    reinterpret_cast<const char *>(optval),
1971
#else
1972
0
                    optval,
1973
0
#endif
1974
0
                    optlen) == 0;
1975
0
}
1976
1977
0
inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
1978
0
  return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
1979
0
}
1980
1981
inline bool set_socket_opt_time(socket_t sock, int level, int optname,
1982
0
                                time_t sec, time_t usec) {
1983
#ifdef _WIN32
1984
  auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
1985
#else
1986
0
  timeval timeout;
1987
0
  timeout.tv_sec = static_cast<long>(sec);
1988
0
  timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
1989
0
#endif
1990
0
  return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
1991
0
}
1992
1993
} // namespace detail
1994
1995
0
inline void default_socket_options(socket_t sock) {
1996
0
  detail::set_socket_opt(sock, SOL_SOCKET,
1997
0
#ifdef SO_REUSEPORT
1998
0
                         SO_REUSEPORT,
1999
#else
2000
                         SO_REUSEADDR,
2001
#endif
2002
0
                         1);
2003
0
}
2004
2005
3.90k
inline const char *status_message(int status) {
2006
3.90k
  switch (status) {
2007
2
  case StatusCode::Continue_100: return "Continue";
2008
0
  case StatusCode::SwitchingProtocol_101: return "Switching Protocol";
2009
0
  case StatusCode::Processing_102: return "Processing";
2010
0
  case StatusCode::EarlyHints_103: return "Early Hints";
2011
843
  case StatusCode::OK_200: return "OK";
2012
0
  case StatusCode::Created_201: return "Created";
2013
0
  case StatusCode::Accepted_202: return "Accepted";
2014
0
  case StatusCode::NonAuthoritativeInformation_203:
2015
0
    return "Non-Authoritative Information";
2016
0
  case StatusCode::NoContent_204: return "No Content";
2017
0
  case StatusCode::ResetContent_205: return "Reset Content";
2018
257
  case StatusCode::PartialContent_206: return "Partial Content";
2019
0
  case StatusCode::MultiStatus_207: return "Multi-Status";
2020
0
  case StatusCode::AlreadyReported_208: return "Already Reported";
2021
0
  case StatusCode::IMUsed_226: return "IM Used";
2022
0
  case StatusCode::MultipleChoices_300: return "Multiple Choices";
2023
0
  case StatusCode::MovedPermanently_301: return "Moved Permanently";
2024
0
  case StatusCode::Found_302: return "Found";
2025
0
  case StatusCode::SeeOther_303: return "See Other";
2026
0
  case StatusCode::NotModified_304: return "Not Modified";
2027
0
  case StatusCode::UseProxy_305: return "Use Proxy";
2028
0
  case StatusCode::unused_306: return "unused";
2029
0
  case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";
2030
0
  case StatusCode::PermanentRedirect_308: return "Permanent Redirect";
2031
2.37k
  case StatusCode::BadRequest_400: return "Bad Request";
2032
0
  case StatusCode::Unauthorized_401: return "Unauthorized";
2033
0
  case StatusCode::PaymentRequired_402: return "Payment Required";
2034
0
  case StatusCode::Forbidden_403: return "Forbidden";
2035
65
  case StatusCode::NotFound_404: return "Not Found";
2036
0
  case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";
2037
0
  case StatusCode::NotAcceptable_406: return "Not Acceptable";
2038
0
  case StatusCode::ProxyAuthenticationRequired_407:
2039
0
    return "Proxy Authentication Required";
2040
0
  case StatusCode::RequestTimeout_408: return "Request Timeout";
2041
0
  case StatusCode::Conflict_409: return "Conflict";
2042
0
  case StatusCode::Gone_410: return "Gone";
2043
0
  case StatusCode::LengthRequired_411: return "Length Required";
2044
0
  case StatusCode::PreconditionFailed_412: return "Precondition Failed";
2045
28
  case StatusCode::PayloadTooLarge_413: return "Payload Too Large";
2046
10
  case StatusCode::UriTooLong_414: return "URI Too Long";
2047
14
  case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";
2048
309
  case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";
2049
0
  case StatusCode::ExpectationFailed_417: return "Expectation Failed";
2050
0
  case StatusCode::ImATeapot_418: return "I'm a teapot";
2051
0
  case StatusCode::MisdirectedRequest_421: return "Misdirected Request";
2052
0
  case StatusCode::UnprocessableContent_422: return "Unprocessable Content";
2053
0
  case StatusCode::Locked_423: return "Locked";
2054
0
  case StatusCode::FailedDependency_424: return "Failed Dependency";
2055
0
  case StatusCode::TooEarly_425: return "Too Early";
2056
0
  case StatusCode::UpgradeRequired_426: return "Upgrade Required";
2057
0
  case StatusCode::PreconditionRequired_428: return "Precondition Required";
2058
0
  case StatusCode::TooManyRequests_429: return "Too Many Requests";
2059
0
  case StatusCode::RequestHeaderFieldsTooLarge_431:
2060
0
    return "Request Header Fields Too Large";
2061
0
  case StatusCode::UnavailableForLegalReasons_451:
2062
0
    return "Unavailable For Legal Reasons";
2063
0
  case StatusCode::NotImplemented_501: return "Not Implemented";
2064
0
  case StatusCode::BadGateway_502: return "Bad Gateway";
2065
0
  case StatusCode::ServiceUnavailable_503: return "Service Unavailable";
2066
0
  case StatusCode::GatewayTimeout_504: return "Gateway Timeout";
2067
0
  case StatusCode::HttpVersionNotSupported_505:
2068
0
    return "HTTP Version Not Supported";
2069
0
  case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";
2070
0
  case StatusCode::InsufficientStorage_507: return "Insufficient Storage";
2071
0
  case StatusCode::LoopDetected_508: return "Loop Detected";
2072
0
  case StatusCode::NotExtended_510: return "Not Extended";
2073
0
  case StatusCode::NetworkAuthenticationRequired_511:
2074
0
    return "Network Authentication Required";
2075
2076
0
  default:
2077
0
  case StatusCode::InternalServerError_500: return "Internal Server Error";
2078
3.90k
  }
2079
3.90k
}
2080
2081
0
inline std::string get_bearer_token_auth(const Request &req) {
2082
0
  if (req.has_header("Authorization")) {
2083
0
    constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2084
0
    return req.get_header_value("Authorization")
2085
0
        .substr(bearer_header_prefix_len);
2086
0
  }
2087
0
  return "";
2088
0
}
2089
2090
template <class Rep, class Period>
2091
inline Server &
2092
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2093
  detail::duration_to_sec_and_usec(
2094
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2095
  return *this;
2096
}
2097
2098
template <class Rep, class Period>
2099
inline Server &
2100
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2101
  detail::duration_to_sec_and_usec(
2102
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2103
  return *this;
2104
}
2105
2106
template <class Rep, class Period>
2107
inline Server &
2108
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
2109
  detail::duration_to_sec_and_usec(
2110
      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2111
  return *this;
2112
}
2113
2114
0
inline std::string to_string(const Error error) {
2115
0
  switch (error) {
2116
0
  case Error::Success: return "Success (no error)";
2117
0
  case Error::Connection: return "Could not establish connection";
2118
0
  case Error::BindIPAddress: return "Failed to bind IP address";
2119
0
  case Error::Read: return "Failed to read connection";
2120
0
  case Error::Write: return "Failed to write connection";
2121
0
  case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
2122
0
  case Error::Canceled: return "Connection handling canceled";
2123
0
  case Error::SSLConnection: return "SSL connection failed";
2124
0
  case Error::SSLLoadingCerts: return "SSL certificate loading failed";
2125
0
  case Error::SSLServerVerification: return "SSL server verification failed";
2126
0
  case Error::SSLServerHostnameVerification:
2127
0
    return "SSL server hostname verification failed";
2128
0
  case Error::UnsupportedMultipartBoundaryChars:
2129
0
    return "Unsupported HTTP multipart boundary characters";
2130
0
  case Error::Compression: return "Compression failed";
2131
0
  case Error::ConnectionTimeout: return "Connection timed out";
2132
0
  case Error::ProxyConnection: return "Proxy connection failed";
2133
0
  case Error::Unknown: return "Unknown";
2134
0
  default: break;
2135
0
  }
2136
0
2137
0
  return "Invalid";
2138
0
}
2139
2140
0
inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
2141
0
  os << to_string(obj);
2142
0
  os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
2143
0
  return os;
2144
0
}
2145
2146
inline uint64_t Result::get_request_header_value_u64(const std::string &key,
2147
                                                     uint64_t def,
2148
0
                                                     size_t id) const {
2149
0
  return detail::get_header_value_u64(request_headers_, key, def, id);
2150
0
}
2151
2152
template <class Rep, class Period>
2153
inline void ClientImpl::set_connection_timeout(
2154
    const std::chrono::duration<Rep, Period> &duration) {
2155
  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2156
    set_connection_timeout(sec, usec);
2157
  });
2158
}
2159
2160
template <class Rep, class Period>
2161
inline void ClientImpl::set_read_timeout(
2162
    const std::chrono::duration<Rep, Period> &duration) {
2163
  detail::duration_to_sec_and_usec(
2164
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2165
}
2166
2167
template <class Rep, class Period>
2168
inline void ClientImpl::set_write_timeout(
2169
    const std::chrono::duration<Rep, Period> &duration) {
2170
  detail::duration_to_sec_and_usec(
2171
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2172
}
2173
2174
template <class Rep, class Period>
2175
inline void ClientImpl::set_max_timeout(
2176
    const std::chrono::duration<Rep, Period> &duration) {
2177
  auto msec =
2178
      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2179
  set_max_timeout(msec);
2180
}
2181
2182
template <class Rep, class Period>
2183
inline void Client::set_connection_timeout(
2184
    const std::chrono::duration<Rep, Period> &duration) {
2185
  cli_->set_connection_timeout(duration);
2186
}
2187
2188
template <class Rep, class Period>
2189
inline void
2190
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2191
  cli_->set_read_timeout(duration);
2192
}
2193
2194
template <class Rep, class Period>
2195
inline void
2196
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2197
  cli_->set_write_timeout(duration);
2198
}
2199
2200
template <class Rep, class Period>
2201
inline void
2202
Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2203
  cli_->set_max_timeout(duration);
2204
}
2205
2206
/*
2207
 * Forward declarations and types that will be part of the .h file if split into
2208
 * .h + .cc.
2209
 */
2210
2211
std::string hosted_at(const std::string &hostname);
2212
2213
void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2214
2215
std::string append_query_params(const std::string &path, const Params &params);
2216
2217
std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2218
2219
std::pair<std::string, std::string>
2220
make_basic_authentication_header(const std::string &username,
2221
                                 const std::string &password,
2222
                                 bool is_proxy = false);
2223
2224
namespace detail {
2225
2226
#if defined(_WIN32)
2227
inline std::wstring u8string_to_wstring(const char *s) {
2228
  std::wstring ws;
2229
  auto len = static_cast<int>(strlen(s));
2230
  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2231
  if (wlen > 0) {
2232
    ws.resize(wlen);
2233
    wlen = ::MultiByteToWideChar(
2234
        CP_UTF8, 0, s, len,
2235
        const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2236
    if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
2237
  }
2238
  return ws;
2239
}
2240
#endif
2241
2242
struct FileStat {
2243
  FileStat(const std::string &path);
2244
  bool is_file() const;
2245
  bool is_dir() const;
2246
2247
private:
2248
#if defined(_WIN32)
2249
  struct _stat st_;
2250
#else
2251
  struct stat st_;
2252
#endif
2253
  int ret_ = -1;
2254
};
2255
2256
std::string encode_query_param(const std::string &value);
2257
2258
std::string decode_url(const std::string &s, bool convert_plus_to_space);
2259
2260
std::string trim_copy(const std::string &s);
2261
2262
void divide(
2263
    const char *data, std::size_t size, char d,
2264
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2265
        fn);
2266
2267
void divide(
2268
    const std::string &str, char d,
2269
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
2270
        fn);
2271
2272
void split(const char *b, const char *e, char d,
2273
           std::function<void(const char *, const char *)> fn);
2274
2275
void split(const char *b, const char *e, char d, size_t m,
2276
           std::function<void(const char *, const char *)> fn);
2277
2278
bool process_client_socket(
2279
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2280
    time_t write_timeout_sec, time_t write_timeout_usec,
2281
    time_t max_timeout_msec,
2282
    std::chrono::time_point<std::chrono::steady_clock> start_time,
2283
    std::function<bool(Stream &)> callback);
2284
2285
socket_t create_client_socket(const std::string &host, const std::string &ip,
2286
                              int port, int address_family, bool tcp_nodelay,
2287
                              bool ipv6_v6only, SocketOptions socket_options,
2288
                              time_t connection_timeout_sec,
2289
                              time_t connection_timeout_usec,
2290
                              time_t read_timeout_sec, time_t read_timeout_usec,
2291
                              time_t write_timeout_sec,
2292
                              time_t write_timeout_usec,
2293
                              const std::string &intf, Error &error);
2294
2295
const char *get_header_value(const Headers &headers, const std::string &key,
2296
                             const char *def, size_t id);
2297
2298
std::string params_to_query_str(const Params &params);
2299
2300
void parse_query_text(const char *data, std::size_t size, Params &params);
2301
2302
void parse_query_text(const std::string &s, Params &params);
2303
2304
bool parse_multipart_boundary(const std::string &content_type,
2305
                              std::string &boundary);
2306
2307
bool parse_range_header(const std::string &s, Ranges &ranges);
2308
2309
bool parse_accept_header(const std::string &s,
2310
                         std::vector<std::string> &content_types);
2311
2312
int close_socket(socket_t sock);
2313
2314
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2315
2316
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2317
2318
enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2319
2320
EncodingType encoding_type(const Request &req, const Response &res);
2321
2322
class BufferStream final : public Stream {
2323
public:
2324
3.90k
  BufferStream() = default;
2325
3.90k
  ~BufferStream() override = default;
2326
2327
  bool is_readable() const override;
2328
  bool wait_readable() const override;
2329
  bool wait_writable() const override;
2330
  ssize_t read(char *ptr, size_t size) override;
2331
  ssize_t write(const char *ptr, size_t size) override;
2332
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
2333
  void get_local_ip_and_port(std::string &ip, int &port) const override;
2334
  socket_t socket() const override;
2335
  time_t duration() const override;
2336
2337
  const std::string &get_buffer() const;
2338
2339
private:
2340
  std::string buffer;
2341
  size_t position = 0;
2342
};
2343
2344
class compressor {
2345
public:
2346
10
  virtual ~compressor() = default;
2347
2348
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2349
  virtual bool compress(const char *data, size_t data_length, bool last,
2350
                        Callback callback) = 0;
2351
};
2352
2353
class decompressor {
2354
public:
2355
419
  virtual ~decompressor() = default;
2356
2357
  virtual bool is_valid() const = 0;
2358
2359
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
2360
  virtual bool decompress(const char *data, size_t data_length,
2361
                          Callback callback) = 0;
2362
};
2363
2364
class nocompressor final : public compressor {
2365
public:
2366
0
  ~nocompressor() override = default;
2367
2368
  bool compress(const char *data, size_t data_length, bool /*last*/,
2369
                Callback callback) override;
2370
};
2371
2372
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2373
class gzip_compressor final : public compressor {
2374
public:
2375
  gzip_compressor();
2376
  ~gzip_compressor() override;
2377
2378
  bool compress(const char *data, size_t data_length, bool last,
2379
                Callback callback) override;
2380
2381
private:
2382
  bool is_valid_ = false;
2383
  z_stream strm_;
2384
};
2385
2386
class gzip_decompressor final : public decompressor {
2387
public:
2388
  gzip_decompressor();
2389
  ~gzip_decompressor() override;
2390
2391
  bool is_valid() const override;
2392
2393
  bool decompress(const char *data, size_t data_length,
2394
                  Callback callback) override;
2395
2396
private:
2397
  bool is_valid_ = false;
2398
  z_stream strm_;
2399
};
2400
#endif
2401
2402
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2403
class brotli_compressor final : public compressor {
2404
public:
2405
  brotli_compressor();
2406
  ~brotli_compressor();
2407
2408
  bool compress(const char *data, size_t data_length, bool last,
2409
                Callback callback) override;
2410
2411
private:
2412
  BrotliEncoderState *state_ = nullptr;
2413
};
2414
2415
class brotli_decompressor final : public decompressor {
2416
public:
2417
  brotli_decompressor();
2418
  ~brotli_decompressor();
2419
2420
  bool is_valid() const override;
2421
2422
  bool decompress(const char *data, size_t data_length,
2423
                  Callback callback) override;
2424
2425
private:
2426
  BrotliDecoderResult decoder_r;
2427
  BrotliDecoderState *decoder_s = nullptr;
2428
};
2429
#endif
2430
2431
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
2432
class zstd_compressor : public compressor {
2433
public:
2434
  zstd_compressor();
2435
  ~zstd_compressor();
2436
2437
  bool compress(const char *data, size_t data_length, bool last,
2438
                Callback callback) override;
2439
2440
private:
2441
  ZSTD_CCtx *ctx_ = nullptr;
2442
};
2443
2444
class zstd_decompressor : public decompressor {
2445
public:
2446
  zstd_decompressor();
2447
  ~zstd_decompressor();
2448
2449
  bool is_valid() const override;
2450
2451
  bool decompress(const char *data, size_t data_length,
2452
                  Callback callback) override;
2453
2454
private:
2455
  ZSTD_DCtx *ctx_ = nullptr;
2456
};
2457
#endif
2458
2459
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2460
// to store data. The call can set memory on stack for performance.
2461
class stream_line_reader {
2462
public:
2463
  stream_line_reader(Stream &strm, char *fixed_buffer,
2464
                     size_t fixed_buffer_size);
2465
  const char *ptr() const;
2466
  size_t size() const;
2467
  bool end_with_crlf() const;
2468
  bool getline();
2469
2470
private:
2471
  void append(char c);
2472
2473
  Stream &strm_;
2474
  char *fixed_buffer_;
2475
  const size_t fixed_buffer_size_;
2476
  size_t fixed_buffer_used_size_ = 0;
2477
  std::string growable_buffer_;
2478
};
2479
2480
class mmap {
2481
public:
2482
  mmap(const char *path);
2483
  ~mmap();
2484
2485
  bool open(const char *path);
2486
  void close();
2487
2488
  bool is_open() const;
2489
  size_t size() const;
2490
  const char *data() const;
2491
2492
private:
2493
#if defined(_WIN32)
2494
  HANDLE hFile_ = NULL;
2495
  HANDLE hMapping_ = NULL;
2496
#else
2497
  int fd_ = -1;
2498
#endif
2499
  size_t size_ = 0;
2500
  void *addr_ = nullptr;
2501
  bool is_open_empty_file = false;
2502
};
2503
2504
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
2505
namespace fields {
2506
2507
1.32M
inline bool is_token_char(char c) {
2508
1.32M
  return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
2509
1.32M
         c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
2510
1.32M
         c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
2511
1.32M
}
2512
2513
100k
inline bool is_token(const std::string &s) {
2514
100k
  if (s.empty()) { return false; }
2515
1.32M
  for (auto c : s) {
2516
1.32M
    if (!is_token_char(c)) { return false; }
2517
1.32M
  }
2518
99.2k
  return true;
2519
99.7k
}
2520
2521
100k
inline bool is_field_name(const std::string &s) { return is_token(s); }
2522
2523
4.05M
inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
2524
2525
612k
inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
2526
2527
4.05M
inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
2528
2529
99.0k
inline bool is_field_content(const std::string &s) {
2530
99.0k
  if (s.empty()) { return true; }
2531
2532
78.6k
  if (s.size() == 1) {
2533
20.4k
    return is_field_vchar(s[0]);
2534
58.1k
  } else if (s.size() == 2) {
2535
3.96k
    return is_field_vchar(s[0]) && is_field_vchar(s[1]);
2536
54.2k
  } else {
2537
54.2k
    size_t i = 0;
2538
2539
54.2k
    if (!is_field_vchar(s[i])) { return false; }
2540
54.1k
    i++;
2541
2542
4.01M
    while (i < s.size() - 1) {
2543
3.96M
      auto c = s[i++];
2544
3.96M
      if (c == ' ' || c == '\t' || is_field_vchar(c)) {
2545
3.96M
      } else {
2546
99
        return false;
2547
99
      }
2548
3.96M
    }
2549
2550
54.0k
    return is_field_vchar(s[i]);
2551
54.1k
  }
2552
78.6k
}
2553
2554
99.0k
inline bool is_field_value(const std::string &s) { return is_field_content(s); }
2555
2556
} // namespace fields
2557
2558
} // namespace detail
2559
2560
// ----------------------------------------------------------------------------
2561
2562
/*
2563
 * Implementation that will be part of the .cc file if split into .h + .cc.
2564
 */
2565
2566
namespace detail {
2567
2568
81.9k
inline bool is_hex(char c, int &v) {
2569
81.9k
  if (0x20 <= c && isdigit(c)) {
2570
11.6k
    v = c - '0';
2571
11.6k
    return true;
2572
70.3k
  } else if ('A' <= c && c <= 'F') {
2573
5.49k
    v = c - 'A' + 10;
2574
5.49k
    return true;
2575
64.8k
  } else if ('a' <= c && c <= 'f') {
2576
19.4k
    v = c - 'a' + 10;
2577
19.4k
    return true;
2578
19.4k
  }
2579
45.3k
  return false;
2580
81.9k
}
2581
2582
inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
2583
55.0k
                          int &val) {
2584
55.0k
  if (i >= s.size()) { return false; }
2585
2586
54.1k
  val = 0;
2587
90.7k
  for (; cnt; i++, cnt--) {
2588
82.6k
    if (!s[i]) { return false; }
2589
81.9k
    auto v = 0;
2590
81.9k
    if (is_hex(s[i], v)) {
2591
36.5k
      val = val * 16 + v;
2592
45.3k
    } else {
2593
45.3k
      return false;
2594
45.3k
    }
2595
81.9k
  }
2596
8.16k
  return true;
2597
54.1k
}
2598
2599
0
inline std::string from_i_to_hex(size_t n) {
2600
0
  static const auto charset = "0123456789abcdef";
2601
0
  std::string ret;
2602
0
  do {
2603
0
    ret = charset[n & 15] + ret;
2604
0
    n >>= 4;
2605
0
  } while (n > 0);
2606
0
  return ret;
2607
0
}
2608
2609
5.73k
inline size_t to_utf8(int code, char *buff) {
2610
5.73k
  if (code < 0x0080) {
2611
709
    buff[0] = static_cast<char>(code & 0x7F);
2612
709
    return 1;
2613
5.02k
  } else if (code < 0x0800) {
2614
878
    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2615
878
    buff[1] = static_cast<char>(0x80 | (code & 0x3F));
2616
878
    return 2;
2617
4.14k
  } else if (code < 0xD800) {
2618
548
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2619
548
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2620
548
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
2621
548
    return 3;
2622
3.59k
  } else if (code < 0xE000) { // D800 - DFFF is invalid...
2623
2.75k
    return 0;
2624
2.75k
  } else if (code < 0x10000) {
2625
843
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2626
843
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2627
843
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
2628
843
    return 3;
2629
843
  } else if (code < 0x110000) {
2630
0
    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2631
0
    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2632
0
    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2633
0
    buff[3] = static_cast<char>(0x80 | (code & 0x3F));
2634
0
    return 4;
2635
0
  }
2636
2637
  // NOTREACHED
2638
0
  return 0;
2639
5.73k
}
2640
2641
// NOTE: This code came up with the following stackoverflow post:
2642
// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
2643
0
inline std::string base64_encode(const std::string &in) {
2644
0
  static const auto lookup =
2645
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2646
0
2647
0
  std::string out;
2648
0
  out.reserve(in.size());
2649
0
2650
0
  auto val = 0;
2651
0
  auto valb = -6;
2652
0
2653
0
  for (auto c : in) {
2654
0
    val = (val << 8) + static_cast<uint8_t>(c);
2655
0
    valb += 8;
2656
0
    while (valb >= 0) {
2657
0
      out.push_back(lookup[(val >> valb) & 0x3F]);
2658
0
      valb -= 6;
2659
0
    }
2660
0
  }
2661
0
2662
0
  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
2663
0
2664
0
  while (out.size() % 4) {
2665
0
    out.push_back('=');
2666
0
  }
2667
0
2668
0
  return out;
2669
0
}
2670
2671
0
inline bool is_valid_path(const std::string &path) {
2672
0
  size_t level = 0;
2673
0
  size_t i = 0;
2674
2675
  // Skip slash
2676
0
  while (i < path.size() && path[i] == '/') {
2677
0
    i++;
2678
0
  }
2679
2680
0
  while (i < path.size()) {
2681
    // Read component
2682
0
    auto beg = i;
2683
0
    while (i < path.size() && path[i] != '/') {
2684
0
      if (path[i] == '\0') {
2685
0
        return false;
2686
0
      } else if (path[i] == '\\') {
2687
0
        return false;
2688
0
      }
2689
0
      i++;
2690
0
    }
2691
2692
0
    auto len = i - beg;
2693
0
    assert(len > 0);
2694
2695
0
    if (!path.compare(beg, len, ".")) {
2696
0
      ;
2697
0
    } else if (!path.compare(beg, len, "..")) {
2698
0
      if (level == 0) { return false; }
2699
0
      level--;
2700
0
    } else {
2701
0
      level++;
2702
0
    }
2703
2704
    // Skip slash
2705
0
    while (i < path.size() && path[i] == '/') {
2706
0
      i++;
2707
0
    }
2708
0
  }
2709
2710
0
  return true;
2711
0
}
2712
2713
0
inline FileStat::FileStat(const std::string &path) {
2714
#if defined(_WIN32)
2715
  auto wpath = u8string_to_wstring(path.c_str());
2716
  ret_ = _wstat(wpath.c_str(), &st_);
2717
#else
2718
0
  ret_ = stat(path.c_str(), &st_);
2719
0
#endif
2720
0
}
2721
0
inline bool FileStat::is_file() const {
2722
0
  return ret_ >= 0 && S_ISREG(st_.st_mode);
2723
0
}
2724
0
inline bool FileStat::is_dir() const {
2725
0
  return ret_ >= 0 && S_ISDIR(st_.st_mode);
2726
0
}
2727
2728
0
inline std::string encode_query_param(const std::string &value) {
2729
0
  std::ostringstream escaped;
2730
0
  escaped.fill('0');
2731
0
  escaped << std::hex;
2732
0
2733
0
  for (auto c : value) {
2734
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
2735
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
2736
0
        c == ')') {
2737
0
      escaped << c;
2738
0
    } else {
2739
0
      escaped << std::uppercase;
2740
0
      escaped << '%' << std::setw(2)
2741
0
              << static_cast<int>(static_cast<unsigned char>(c));
2742
0
      escaped << std::nouppercase;
2743
0
    }
2744
0
  }
2745
0
2746
0
  return escaped.str();
2747
0
}
2748
2749
0
inline std::string encode_url(const std::string &s) {
2750
0
  std::string result;
2751
0
  result.reserve(s.size());
2752
0
2753
0
  for (size_t i = 0; s[i]; i++) {
2754
0
    switch (s[i]) {
2755
0
    case ' ': result += "%20"; break;
2756
0
    case '+': result += "%2B"; break;
2757
0
    case '\r': result += "%0D"; break;
2758
0
    case '\n': result += "%0A"; break;
2759
0
    case '\'': result += "%27"; break;
2760
0
    case ',': result += "%2C"; break;
2761
0
    // case ':': result += "%3A"; break; // ok? probably...
2762
0
    case ';': result += "%3B"; break;
2763
0
    default:
2764
0
      auto c = static_cast<uint8_t>(s[i]);
2765
0
      if (c >= 0x80) {
2766
0
        result += '%';
2767
0
        char hex[4];
2768
0
        auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
2769
0
        assert(len == 2);
2770
0
        result.append(hex, static_cast<size_t>(len));
2771
0
      } else {
2772
0
        result += s[i];
2773
0
      }
2774
0
      break;
2775
0
    }
2776
0
  }
2777
0
2778
0
  return result;
2779
0
}
2780
2781
inline std::string decode_url(const std::string &s,
2782
89.9k
                              bool convert_plus_to_space) {
2783
89.9k
  std::string result;
2784
2785
4.73M
  for (size_t i = 0; i < s.size(); i++) {
2786
4.64M
    if (s[i] == '%' && i + 1 < s.size()) {
2787
55.0k
      if (s[i + 1] == 'u') {
2788
10.1k
        auto val = 0;
2789
10.1k
        if (from_hex_to_i(s, i + 2, 4, val)) {
2790
          // 4 digits Unicode codes
2791
5.73k
          char buff[4];
2792
5.73k
          size_t len = to_utf8(val, buff);
2793
5.73k
          if (len > 0) { result.append(buff, len); }
2794
5.73k
          i += 5; // 'u0000'
2795
5.73k
        } else {
2796
4.44k
          result += s[i];
2797
4.44k
        }
2798
44.8k
      } else {
2799
44.8k
        auto val = 0;
2800
44.8k
        if (from_hex_to_i(s, i + 1, 2, val)) {
2801
          // 2 digits hex codes
2802
2.43k
          result += static_cast<char>(val);
2803
2.43k
          i += 2; // '00'
2804
42.4k
        } else {
2805
42.4k
          result += s[i];
2806
42.4k
        }
2807
44.8k
      }
2808
4.59M
    } else if (convert_plus_to_space && s[i] == '+') {
2809
451
      result += ' ';
2810
4.59M
    } else {
2811
4.59M
      result += s[i];
2812
4.59M
    }
2813
4.64M
  }
2814
2815
89.9k
  return result;
2816
89.9k
}
2817
2818
0
inline std::string file_extension(const std::string &path) {
2819
0
  std::smatch m;
2820
0
  thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
2821
0
  if (std::regex_search(path, m, re)) { return m[1].str(); }
2822
0
  return std::string();
2823
0
}
2824
2825
432M
inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
2826
2827
inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
2828
581k
                                      size_t right) {
2829
216M
  while (b + left < e && is_space_or_tab(b[left])) {
2830
215M
    left++;
2831
215M
  }
2832
216M
  while (right > 0 && is_space_or_tab(b[right - 1])) {
2833
215M
    right--;
2834
215M
  }
2835
581k
  return std::make_pair(left, right);
2836
581k
}
2837
2838
46.0k
inline std::string trim_copy(const std::string &s) {
2839
46.0k
  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2840
46.0k
  return s.substr(r.first, r.second - r.first);
2841
46.0k
}
2842
2843
35.4k
inline std::string trim_double_quotes_copy(const std::string &s) {
2844
35.4k
  if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
2845
994
    return s.substr(1, s.size() - 2);
2846
994
  }
2847
34.4k
  return s;
2848
35.4k
}
2849
2850
inline void
2851
divide(const char *data, std::size_t size, char d,
2852
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
2853
8.53k
           fn) {
2854
8.53k
  const auto it = std::find(data, data + size, d);
2855
8.53k
  const auto found = static_cast<std::size_t>(it != data + size);
2856
8.53k
  const auto lhs_data = data;
2857
8.53k
  const auto lhs_size = static_cast<std::size_t>(it - data);
2858
8.53k
  const auto rhs_data = it + found;
2859
8.53k
  const auto rhs_size = size - lhs_size - found;
2860
2861
8.53k
  fn(lhs_data, lhs_size, rhs_data, rhs_size);
2862
8.53k
}
2863
2864
inline void
2865
divide(const std::string &str, char d,
2866
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
2867
3.61k
           fn) {
2868
3.61k
  divide(str.data(), str.size(), d, std::move(fn));
2869
3.61k
}
2870
2871
inline void split(const char *b, const char *e, char d,
2872
30.1k
                  std::function<void(const char *, const char *)> fn) {
2873
30.1k
  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
2874
30.1k
}
2875
2876
inline void split(const char *b, const char *e, char d, size_t m,
2877
30.1k
                  std::function<void(const char *, const char *)> fn) {
2878
30.1k
  size_t i = 0;
2879
30.1k
  size_t beg = 0;
2880
30.1k
  size_t count = 1;
2881
2882
3.81M
  while (e ? (b + i < e) : (b[i] != '\0')) {
2883
3.78M
    if (b[i] == d && count < m) {
2884
508k
      auto r = trim(b, e, beg, i);
2885
508k
      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2886
508k
      beg = i + 1;
2887
508k
      count++;
2888
508k
    }
2889
3.78M
    i++;
2890
3.78M
  }
2891
2892
30.1k
  if (i) {
2893
26.7k
    auto r = trim(b, e, beg, i);
2894
26.7k
    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
2895
26.7k
  }
2896
30.1k
}
2897
2898
inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
2899
                                              size_t fixed_buffer_size)
2900
7.79k
    : strm_(strm), fixed_buffer_(fixed_buffer),
2901
7.79k
      fixed_buffer_size_(fixed_buffer_size) {}
2902
2903
49.1k
inline const char *stream_line_reader::ptr() const {
2904
49.1k
  if (growable_buffer_.empty()) {
2905
47.3k
    return fixed_buffer_;
2906
47.3k
  } else {
2907
1.81k
    return growable_buffer_.data();
2908
1.81k
  }
2909
49.1k
}
2910
2911
2.58M
inline size_t stream_line_reader::size() const {
2912
2.58M
  if (growable_buffer_.empty()) {
2913
1.18M
    return fixed_buffer_used_size_;
2914
1.40M
  } else {
2915
1.40M
    return growable_buffer_.size();
2916
1.40M
  }
2917
2.58M
}
2918
2919
11.9k
inline bool stream_line_reader::end_with_crlf() const {
2920
11.9k
  auto end = ptr() + size();
2921
11.9k
  return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
2922
11.9k
}
2923
2924
24.4k
inline bool stream_line_reader::getline() {
2925
24.4k
  fixed_buffer_used_size_ = 0;
2926
24.4k
  growable_buffer_.clear();
2927
2928
24.4k
#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2929
24.4k
  char prev_byte = 0;
2930
24.4k
#endif
2931
2932
2.52M
  for (size_t i = 0;; i++) {
2933
2.52M
    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
2934
      // Treat exceptionally long lines as an error to
2935
      // prevent infinite loops/memory exhaustion
2936
2
      return false;
2937
2
    }
2938
2.52M
    char byte;
2939
2.52M
    auto n = strm_.read(&byte, 1);
2940
2941
2.52M
    if (n < 0) {
2942
0
      return false;
2943
2.52M
    } else if (n == 0) {
2944
1.14k
      if (i == 0) {
2945
671
        return false;
2946
671
      } else {
2947
470
        break;
2948
470
      }
2949
1.14k
    }
2950
2951
2.52M
    append(byte);
2952
2953
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2954
    if (byte == '\n') { break; }
2955
#else
2956
2.52M
    if (prev_byte == '\r' && byte == '\n') { break; }
2957
2.50M
    prev_byte = byte;
2958
2.50M
#endif
2959
2.50M
  }
2960
2961
23.7k
  return true;
2962
24.4k
}
2963
2964
2.52M
inline void stream_line_reader::append(char c) {
2965
2.52M
  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2966
1.12M
    fixed_buffer_[fixed_buffer_used_size_++] = c;
2967
1.12M
    fixed_buffer_[fixed_buffer_used_size_] = '\0';
2968
1.40M
  } else {
2969
1.40M
    if (growable_buffer_.empty()) {
2970
671
      assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
2971
671
      growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2972
671
    }
2973
1.40M
    growable_buffer_ += c;
2974
1.40M
  }
2975
2.52M
}
2976
2977
0
inline mmap::mmap(const char *path) { open(path); }
2978
2979
0
inline mmap::~mmap() { close(); }
2980
2981
0
inline bool mmap::open(const char *path) {
2982
0
  close();
2983
2984
#if defined(_WIN32)
2985
  auto wpath = u8string_to_wstring(path);
2986
  if (wpath.empty()) { return false; }
2987
2988
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2989
  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
2990
                         OPEN_EXISTING, NULL);
2991
#else
2992
  hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
2993
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2994
#endif
2995
2996
  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
2997
2998
  LARGE_INTEGER size{};
2999
  if (!::GetFileSizeEx(hFile_, &size)) { return false; }
3000
  // If the following line doesn't compile due to QuadPart, update Windows SDK.
3001
  // See:
3002
  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
3003
  if (static_cast<ULONGLONG>(size.QuadPart) >
3004
      (std::numeric_limits<decltype(size_)>::max)()) {
3005
    // `size_t` might be 32-bits, on 32-bits Windows.
3006
    return false;
3007
  }
3008
  size_ = static_cast<size_t>(size.QuadPart);
3009
3010
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
3011
  hMapping_ =
3012
      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
3013
#else
3014
  hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
3015
#endif
3016
3017
  // Special treatment for an empty file...
3018
  if (hMapping_ == NULL && size_ == 0) {
3019
    close();
3020
    is_open_empty_file = true;
3021
    return true;
3022
  }
3023
3024
  if (hMapping_ == NULL) {
3025
    close();
3026
    return false;
3027
  }
3028
3029
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
3030
  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
3031
#else
3032
  addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
3033
#endif
3034
3035
  if (addr_ == nullptr) {
3036
    close();
3037
    return false;
3038
  }
3039
#else
3040
0
  fd_ = ::open(path, O_RDONLY);
3041
0
  if (fd_ == -1) { return false; }
3042
3043
0
  struct stat sb;
3044
0
  if (fstat(fd_, &sb) == -1) {
3045
0
    close();
3046
0
    return false;
3047
0
  }
3048
0
  size_ = static_cast<size_t>(sb.st_size);
3049
3050
0
  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
3051
3052
  // Special treatment for an empty file...
3053
0
  if (addr_ == MAP_FAILED && size_ == 0) {
3054
0
    close();
3055
0
    is_open_empty_file = true;
3056
0
    return false;
3057
0
  }
3058
0
#endif
3059
3060
0
  return true;
3061
0
}
3062
3063
0
inline bool mmap::is_open() const {
3064
0
  return is_open_empty_file ? true : addr_ != nullptr;
3065
0
}
3066
3067
0
inline size_t mmap::size() const { return size_; }
3068
3069
0
inline const char *mmap::data() const {
3070
0
  return is_open_empty_file ? "" : static_cast<const char *>(addr_);
3071
0
}
3072
3073
0
inline void mmap::close() {
3074
#if defined(_WIN32)
3075
  if (addr_) {
3076
    ::UnmapViewOfFile(addr_);
3077
    addr_ = nullptr;
3078
  }
3079
3080
  if (hMapping_) {
3081
    ::CloseHandle(hMapping_);
3082
    hMapping_ = NULL;
3083
  }
3084
3085
  if (hFile_ != INVALID_HANDLE_VALUE) {
3086
    ::CloseHandle(hFile_);
3087
    hFile_ = INVALID_HANDLE_VALUE;
3088
  }
3089
3090
  is_open_empty_file = false;
3091
#else
3092
0
  if (addr_ != nullptr) {
3093
0
    munmap(addr_, size_);
3094
0
    addr_ = nullptr;
3095
0
  }
3096
3097
0
  if (fd_ != -1) {
3098
0
    ::close(fd_);
3099
0
    fd_ = -1;
3100
0
  }
3101
0
#endif
3102
0
  size_ = 0;
3103
0
}
3104
0
inline int close_socket(socket_t sock) {
3105
#ifdef _WIN32
3106
  return closesocket(sock);
3107
#else
3108
0
  return close(sock);
3109
0
#endif
3110
0
}
3111
3112
0
template <typename T> inline ssize_t handle_EINTR(T fn) {
3113
0
  ssize_t res = 0;
3114
0
  while (true) {
3115
0
    res = fn();
3116
0
    if (res < 0 && errno == EINTR) {
3117
0
      std::this_thread::sleep_for(std::chrono::microseconds{1});
3118
0
      continue;
3119
0
    }
3120
0
    break;
3121
0
  }
3122
0
  return res;
3123
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})
3124
3125
0
inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
3126
0
  return handle_EINTR([&]() {
3127
0
    return recv(sock,
3128
#ifdef _WIN32
3129
                static_cast<char *>(ptr), static_cast<int>(size),
3130
#else
3131
0
                ptr, size,
3132
0
#endif
3133
0
                flags);
3134
0
  });
3135
0
}
3136
3137
inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
3138
0
                           int flags) {
3139
0
  return handle_EINTR([&]() {
3140
0
    return send(sock,
3141
#ifdef _WIN32
3142
                static_cast<const char *>(ptr), static_cast<int>(size),
3143
#else
3144
0
                ptr, size,
3145
0
#endif
3146
0
                flags);
3147
0
  });
3148
0
}
3149
3150
0
inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
3151
#ifdef _WIN32
3152
  return ::WSAPoll(fds, nfds, timeout);
3153
#else
3154
0
  return ::poll(fds, nfds, timeout);
3155
0
#endif
3156
0
}
3157
3158
template <bool Read>
3159
0
inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
3160
0
  struct pollfd pfd;
3161
0
  pfd.fd = sock;
3162
0
  pfd.events = (Read ? POLLIN : POLLOUT);
3163
3164
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3165
3166
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
3167
0
}
Unexecuted instantiation: long httplib::detail::select_impl<true>(int, long, long)
Unexecuted instantiation: long httplib::detail::select_impl<false>(int, long, long)
3168
3169
0
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
3170
0
  return select_impl<true>(sock, sec, usec);
3171
0
}
3172
3173
0
inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
3174
0
  return select_impl<false>(sock, sec, usec);
3175
0
}
3176
3177
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
3178
0
                                        time_t usec) {
3179
0
  struct pollfd pfd_read;
3180
0
  pfd_read.fd = sock;
3181
0
  pfd_read.events = POLLIN | POLLOUT;
3182
3183
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3184
3185
0
  auto poll_res =
3186
0
      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
3187
3188
0
  if (poll_res == 0) { return Error::ConnectionTimeout; }
3189
3190
0
  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
3191
0
    auto error = 0;
3192
0
    socklen_t len = sizeof(error);
3193
0
    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3194
0
                          reinterpret_cast<char *>(&error), &len);
3195
0
    auto successful = res >= 0 && !error;
3196
0
    return successful ? Error::Success : Error::Connection;
3197
0
  }
3198
3199
0
  return Error::Connection;
3200
0
}
3201
3202
0
inline bool is_socket_alive(socket_t sock) {
3203
0
  const auto val = detail::select_read(sock, 0, 0);
3204
0
  if (val == 0) {
3205
0
    return true;
3206
0
  } else if (val < 0 && errno == EBADF) {
3207
0
    return false;
3208
0
  }
3209
0
  char buf[1];
3210
0
  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
3211
0
}
3212
3213
class SocketStream final : public Stream {
3214
public:
3215
  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3216
               time_t write_timeout_sec, time_t write_timeout_usec,
3217
               time_t max_timeout_msec = 0,
3218
               std::chrono::time_point<std::chrono::steady_clock> start_time =
3219
                   (std::chrono::steady_clock::time_point::min)());
3220
  ~SocketStream() override;
3221
3222
  bool is_readable() const override;
3223
  bool wait_readable() const override;
3224
  bool wait_writable() const override;
3225
  ssize_t read(char *ptr, size_t size) override;
3226
  ssize_t write(const char *ptr, size_t size) override;
3227
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
3228
  void get_local_ip_and_port(std::string &ip, int &port) const override;
3229
  socket_t socket() const override;
3230
  time_t duration() const override;
3231
3232
private:
3233
  socket_t sock_;
3234
  time_t read_timeout_sec_;
3235
  time_t read_timeout_usec_;
3236
  time_t write_timeout_sec_;
3237
  time_t write_timeout_usec_;
3238
  time_t max_timeout_msec_;
3239
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
3240
3241
  std::vector<char> read_buff_;
3242
  size_t read_buff_off_ = 0;
3243
  size_t read_buff_content_size_ = 0;
3244
3245
  static const size_t read_buff_size_ = 1024l * 4;
3246
};
3247
3248
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3249
class SSLSocketStream final : public Stream {
3250
public:
3251
  SSLSocketStream(
3252
      socket_t sock, SSL *ssl, time_t read_timeout_sec,
3253
      time_t read_timeout_usec, time_t write_timeout_sec,
3254
      time_t write_timeout_usec, time_t max_timeout_msec = 0,
3255
      std::chrono::time_point<std::chrono::steady_clock> start_time =
3256
          (std::chrono::steady_clock::time_point::min)());
3257
  ~SSLSocketStream() override;
3258
3259
  bool is_readable() const override;
3260
  bool wait_readable() const override;
3261
  bool wait_writable() const override;
3262
  ssize_t read(char *ptr, size_t size) override;
3263
  ssize_t write(const char *ptr, size_t size) override;
3264
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
3265
  void get_local_ip_and_port(std::string &ip, int &port) const override;
3266
  socket_t socket() const override;
3267
  time_t duration() const override;
3268
3269
private:
3270
  socket_t sock_;
3271
  SSL *ssl_;
3272
  time_t read_timeout_sec_;
3273
  time_t read_timeout_usec_;
3274
  time_t write_timeout_sec_;
3275
  time_t write_timeout_usec_;
3276
  time_t max_timeout_msec_;
3277
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
3278
};
3279
#endif
3280
3281
inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
3282
0
                       time_t keep_alive_timeout_sec) {
3283
0
  using namespace std::chrono;
3284
3285
0
  const auto interval_usec =
3286
0
      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
3287
3288
  // Avoid expensive `steady_clock::now()` call for the first time
3289
0
  if (select_read(sock, 0, interval_usec) > 0) { return true; }
3290
3291
0
  const auto start = steady_clock::now() - microseconds{interval_usec};
3292
0
  const auto timeout = seconds{keep_alive_timeout_sec};
3293
3294
0
  while (true) {
3295
0
    if (svr_sock == INVALID_SOCKET) {
3296
0
      break; // Server socket is closed
3297
0
    }
3298
3299
0
    auto val = select_read(sock, 0, interval_usec);
3300
0
    if (val < 0) {
3301
0
      break; // Ssocket error
3302
0
    } else if (val == 0) {
3303
0
      if (steady_clock::now() - start > timeout) {
3304
0
        break; // Timeout
3305
0
      }
3306
0
    } else {
3307
0
      return true; // Ready for read
3308
0
    }
3309
0
  }
3310
3311
0
  return false;
3312
0
}
3313
3314
template <typename T>
3315
inline bool
3316
process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
3317
                           size_t keep_alive_max_count,
3318
0
                           time_t keep_alive_timeout_sec, T callback) {
3319
0
  assert(keep_alive_max_count > 0);
3320
0
  auto ret = false;
3321
0
  auto count = keep_alive_max_count;
3322
0
  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3323
0
    auto close_connection = count == 1;
3324
0
    auto connection_closed = false;
3325
0
    ret = callback(close_connection, connection_closed);
3326
0
    if (!ret || connection_closed) { break; }
3327
0
    count--;
3328
0
  }
3329
0
  return ret;
3330
0
}
3331
3332
template <typename T>
3333
inline bool
3334
process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
3335
                      size_t keep_alive_max_count,
3336
                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,
3337
                      time_t read_timeout_usec, time_t write_timeout_sec,
3338
0
                      time_t write_timeout_usec, T callback) {
3339
0
  return process_server_socket_core(
3340
0
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3341
0
      [&](bool close_connection, bool &connection_closed) {
3342
0
        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3343
0
                          write_timeout_sec, write_timeout_usec);
3344
0
        return callback(strm, close_connection, connection_closed);
3345
0
      });
3346
0
}
3347
3348
inline bool process_client_socket(
3349
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3350
    time_t write_timeout_sec, time_t write_timeout_usec,
3351
    time_t max_timeout_msec,
3352
    std::chrono::time_point<std::chrono::steady_clock> start_time,
3353
0
    std::function<bool(Stream &)> callback) {
3354
0
  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3355
0
                    write_timeout_sec, write_timeout_usec, max_timeout_msec,
3356
0
                    start_time);
3357
0
  return callback(strm);
3358
0
}
3359
3360
0
inline int shutdown_socket(socket_t sock) {
3361
#ifdef _WIN32
3362
  return shutdown(sock, SD_BOTH);
3363
#else
3364
0
  return shutdown(sock, SHUT_RDWR);
3365
0
#endif
3366
0
}
3367
3368
0
inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
3369
0
  if (s.size() > 1 && s[0] == '\0') {
3370
0
    auto ret = s;
3371
0
    ret[0] = '@';
3372
0
    return ret;
3373
0
  }
3374
0
  return s;
3375
0
}
3376
3377
inline std::string
3378
0
unescape_abstract_namespace_unix_domain(const std::string &s) {
3379
0
  if (s.size() > 1 && s[0] == '@') {
3380
0
    auto ret = s;
3381
0
    ret[0] = '\0';
3382
0
    return ret;
3383
0
  }
3384
0
  return s;
3385
0
}
3386
3387
inline int getaddrinfo_with_timeout(const char *node, const char *service,
3388
                                    const struct addrinfo *hints,
3389
0
                                    struct addrinfo **res, time_t timeout_sec) {
3390
#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
3391
  if (timeout_sec <= 0) {
3392
    // No timeout specified, use standard getaddrinfo
3393
    return getaddrinfo(node, service, hints, res);
3394
  }
3395
3396
#ifdef _WIN32
3397
  // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
3398
  OVERLAPPED overlapped = {0};
3399
  HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
3400
  if (!event) { return EAI_FAIL; }
3401
3402
  overlapped.hEvent = event;
3403
3404
  PADDRINFOEXW result_addrinfo = nullptr;
3405
  HANDLE cancel_handle = nullptr;
3406
3407
  ADDRINFOEXW hints_ex = {0};
3408
  if (hints) {
3409
    hints_ex.ai_flags = hints->ai_flags;
3410
    hints_ex.ai_family = hints->ai_family;
3411
    hints_ex.ai_socktype = hints->ai_socktype;
3412
    hints_ex.ai_protocol = hints->ai_protocol;
3413
  }
3414
3415
  auto wnode = u8string_to_wstring(node);
3416
  auto wservice = u8string_to_wstring(service);
3417
3418
  auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
3419
                              hints ? &hints_ex : nullptr, &result_addrinfo,
3420
                              nullptr, &overlapped, nullptr, &cancel_handle);
3421
3422
  if (ret == WSA_IO_PENDING) {
3423
    auto wait_result =
3424
        ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
3425
    if (wait_result == WAIT_TIMEOUT) {
3426
      if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
3427
      ::CloseHandle(event);
3428
      return EAI_AGAIN;
3429
    }
3430
3431
    DWORD bytes_returned;
3432
    if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
3433
                               &bytes_returned, FALSE)) {
3434
      ::CloseHandle(event);
3435
      return ::WSAGetLastError();
3436
    }
3437
  }
3438
3439
  ::CloseHandle(event);
3440
3441
  if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
3442
    *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
3443
    return 0;
3444
  }
3445
3446
  return ret;
3447
#elif defined(__APPLE__)
3448
  // macOS implementation using CFHost API for asynchronous DNS resolution
3449
  CFStringRef hostname_ref = CFStringCreateWithCString(
3450
      kCFAllocatorDefault, node, kCFStringEncodingUTF8);
3451
  if (!hostname_ref) { return EAI_MEMORY; }
3452
3453
  CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
3454
  CFRelease(hostname_ref);
3455
  if (!host_ref) { return EAI_MEMORY; }
3456
3457
  // Set up context for callback
3458
  struct CFHostContext {
3459
    bool completed = false;
3460
    bool success = false;
3461
    CFArrayRef addresses = nullptr;
3462
    std::mutex mutex;
3463
    std::condition_variable cv;
3464
  } context;
3465
3466
  CFHostClientContext client_context;
3467
  memset(&client_context, 0, sizeof(client_context));
3468
  client_context.info = &context;
3469
3470
  // Set callback
3471
  auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
3472
                     const CFStreamError *error, void *info) {
3473
    auto ctx = static_cast<CFHostContext *>(info);
3474
    std::lock_guard<std::mutex> lock(ctx->mutex);
3475
3476
    if (error && error->error != 0) {
3477
      ctx->success = false;
3478
    } else {
3479
      Boolean hasBeenResolved;
3480
      ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
3481
      if (ctx->addresses && hasBeenResolved) {
3482
        CFRetain(ctx->addresses);
3483
        ctx->success = true;
3484
      } else {
3485
        ctx->success = false;
3486
      }
3487
    }
3488
    ctx->completed = true;
3489
    ctx->cv.notify_one();
3490
  };
3491
3492
  if (!CFHostSetClient(host_ref, callback, &client_context)) {
3493
    CFRelease(host_ref);
3494
    return EAI_SYSTEM;
3495
  }
3496
3497
  // Schedule on run loop
3498
  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
3499
  CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
3500
3501
  // Start resolution
3502
  CFStreamError stream_error;
3503
  if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
3504
    CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
3505
    CFRelease(host_ref);
3506
    return EAI_FAIL;
3507
  }
3508
3509
  // Wait for completion with timeout
3510
  auto timeout_time =
3511
      std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
3512
  bool timed_out = false;
3513
3514
  {
3515
    std::unique_lock<std::mutex> lock(context.mutex);
3516
3517
    while (!context.completed) {
3518
      auto now = std::chrono::steady_clock::now();
3519
      if (now >= timeout_time) {
3520
        timed_out = true;
3521
        break;
3522
      }
3523
3524
      // Run the runloop for a short time
3525
      lock.unlock();
3526
      CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
3527
      lock.lock();
3528
    }
3529
  }
3530
3531
  // Clean up
3532
  CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
3533
  CFHostSetClient(host_ref, nullptr, nullptr);
3534
3535
  if (timed_out || !context.completed) {
3536
    CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
3537
    CFRelease(host_ref);
3538
    return EAI_AGAIN;
3539
  }
3540
3541
  if (!context.success || !context.addresses) {
3542
    CFRelease(host_ref);
3543
    return EAI_NODATA;
3544
  }
3545
3546
  // Convert CFArray to addrinfo
3547
  CFIndex count = CFArrayGetCount(context.addresses);
3548
  if (count == 0) {
3549
    CFRelease(context.addresses);
3550
    CFRelease(host_ref);
3551
    return EAI_NODATA;
3552
  }
3553
3554
  struct addrinfo *result_addrinfo = nullptr;
3555
  struct addrinfo **current = &result_addrinfo;
3556
3557
  for (CFIndex i = 0; i < count; i++) {
3558
    CFDataRef addr_data =
3559
        static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
3560
    if (!addr_data) continue;
3561
3562
    const struct sockaddr *sockaddr_ptr =
3563
        reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
3564
    socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
3565
3566
    // Allocate addrinfo structure
3567
    *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
3568
    if (!*current) {
3569
      freeaddrinfo(result_addrinfo);
3570
      CFRelease(context.addresses);
3571
      CFRelease(host_ref);
3572
      return EAI_MEMORY;
3573
    }
3574
3575
    memset(*current, 0, sizeof(struct addrinfo));
3576
3577
    // Set up addrinfo fields
3578
    (*current)->ai_family = sockaddr_ptr->sa_family;
3579
    (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
3580
    (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
3581
    (*current)->ai_addrlen = sockaddr_len;
3582
3583
    // Copy sockaddr
3584
    (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
3585
    if (!(*current)->ai_addr) {
3586
      freeaddrinfo(result_addrinfo);
3587
      CFRelease(context.addresses);
3588
      CFRelease(host_ref);
3589
      return EAI_MEMORY;
3590
    }
3591
    memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
3592
3593
    // Set port if service is specified
3594
    if (service && strlen(service) > 0) {
3595
      int port = atoi(service);
3596
      if (port > 0) {
3597
        if (sockaddr_ptr->sa_family == AF_INET) {
3598
          reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
3599
              ->sin_port = htons(static_cast<uint16_t>(port));
3600
        } else if (sockaddr_ptr->sa_family == AF_INET6) {
3601
          reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
3602
              ->sin6_port = htons(static_cast<uint16_t>(port));
3603
        }
3604
      }
3605
    }
3606
3607
    current = &((*current)->ai_next);
3608
  }
3609
3610
  CFRelease(context.addresses);
3611
  CFRelease(host_ref);
3612
3613
  *res = result_addrinfo;
3614
  return 0;
3615
#elif defined(_GNU_SOURCE) && defined(__GLIBC__) &&                            \
3616
    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
3617
  // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
3618
  struct gaicb request;
3619
  struct gaicb *requests[1] = {&request};
3620
  struct sigevent sevp;
3621
  struct timespec timeout;
3622
3623
  // Initialize the request structure
3624
  memset(&request, 0, sizeof(request));
3625
  request.ar_name = node;
3626
  request.ar_service = service;
3627
  request.ar_request = hints;
3628
3629
  // Set up timeout
3630
  timeout.tv_sec = timeout_sec;
3631
  timeout.tv_nsec = 0;
3632
3633
  // Initialize sigevent structure (not used, but required)
3634
  memset(&sevp, 0, sizeof(sevp));
3635
  sevp.sigev_notify = SIGEV_NONE;
3636
3637
  // Start asynchronous resolution
3638
  int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
3639
  if (start_result != 0) { return start_result; }
3640
3641
  // Wait for completion with timeout
3642
  int wait_result =
3643
      gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
3644
3645
  if (wait_result == 0) {
3646
    // Completed successfully, get the result
3647
    int gai_result = gai_error(&request);
3648
    if (gai_result == 0) {
3649
      *res = request.ar_result;
3650
      return 0;
3651
    } else {
3652
      // Clean up on error
3653
      if (request.ar_result) { freeaddrinfo(request.ar_result); }
3654
      return gai_result;
3655
    }
3656
  } else if (wait_result == EAI_AGAIN) {
3657
    // Timeout occurred, cancel the request
3658
    gai_cancel(&request);
3659
    return EAI_AGAIN;
3660
  } else {
3661
    // Other error occurred
3662
    gai_cancel(&request);
3663
    return wait_result;
3664
  }
3665
#else
3666
  // Fallback implementation using thread-based timeout for other Unix systems
3667
  std::mutex result_mutex;
3668
  std::condition_variable result_cv;
3669
  auto completed = false;
3670
  auto result = EAI_SYSTEM;
3671
  struct addrinfo *result_addrinfo = nullptr;
3672
3673
  std::thread resolve_thread([&]() {
3674
    auto thread_result = getaddrinfo(node, service, hints, &result_addrinfo);
3675
3676
    std::lock_guard<std::mutex> lock(result_mutex);
3677
    result = thread_result;
3678
    completed = true;
3679
    result_cv.notify_one();
3680
  });
3681
3682
  // Wait for completion or timeout
3683
  std::unique_lock<std::mutex> lock(result_mutex);
3684
  auto finished = result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
3685
                                     [&] { return completed; });
3686
3687
  if (finished) {
3688
    // Operation completed within timeout
3689
    resolve_thread.join();
3690
    *res = result_addrinfo;
3691
    return result;
3692
  } else {
3693
    // Timeout occurred
3694
    resolve_thread.detach(); // Let the thread finish in background
3695
    return EAI_AGAIN;        // Return timeout error
3696
  }
3697
#endif
3698
#else
3699
0
  (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
3700
0
  return getaddrinfo(node, service, hints, res);
3701
0
#endif
3702
0
}
3703
3704
template <typename BindOrConnect>
3705
socket_t create_socket(const std::string &host, const std::string &ip, int port,
3706
                       int address_family, int socket_flags, bool tcp_nodelay,
3707
                       bool ipv6_v6only, SocketOptions socket_options,
3708
0
                       BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
3709
  // Get address info
3710
0
  const char *node = nullptr;
3711
0
  struct addrinfo hints;
3712
0
  struct addrinfo *result;
3713
3714
0
  memset(&hints, 0, sizeof(struct addrinfo));
3715
0
  hints.ai_socktype = SOCK_STREAM;
3716
0
  hints.ai_protocol = IPPROTO_IP;
3717
3718
0
  if (!ip.empty()) {
3719
0
    node = ip.c_str();
3720
    // Ask getaddrinfo to convert IP in c-string to address
3721
0
    hints.ai_family = AF_UNSPEC;
3722
0
    hints.ai_flags = AI_NUMERICHOST;
3723
0
  } else {
3724
0
    if (!host.empty()) { node = host.c_str(); }
3725
0
    hints.ai_family = address_family;
3726
0
    hints.ai_flags = socket_flags;
3727
0
  }
3728
3729
0
#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
3730
0
  if (hints.ai_family == AF_UNIX) {
3731
0
    const auto addrlen = host.length();
3732
0
    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
3733
3734
0
#ifdef SOCK_CLOEXEC
3735
0
    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
3736
0
                       hints.ai_protocol);
3737
#else
3738
    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3739
#endif
3740
3741
0
    if (sock != INVALID_SOCKET) {
3742
0
      sockaddr_un addr{};
3743
0
      addr.sun_family = AF_UNIX;
3744
3745
0
      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
3746
0
      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3747
3748
0
      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
3749
0
      hints.ai_addrlen = static_cast<socklen_t>(
3750
0
          sizeof(addr) - sizeof(addr.sun_path) + addrlen);
3751
3752
#ifndef SOCK_CLOEXEC
3753
#ifndef _WIN32
3754
      fcntl(sock, F_SETFD, FD_CLOEXEC);
3755
#endif
3756
#endif
3757
3758
0
      if (socket_options) { socket_options(sock); }
3759
3760
#ifdef _WIN32
3761
      // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
3762
      // remove the option.
3763
      detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
3764
#endif
3765
3766
0
      bool dummy;
3767
0
      if (!bind_or_connect(sock, hints, dummy)) {
3768
0
        close_socket(sock);
3769
0
        sock = INVALID_SOCKET;
3770
0
      }
3771
0
    }
3772
0
    return sock;
3773
0
  }
3774
0
#endif
3775
3776
0
  auto service = std::to_string(port);
3777
3778
0
  if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
3779
0
                               timeout_sec)) {
3780
0
#if defined __linux__ && !defined __ANDROID__
3781
0
    res_init();
3782
0
#endif
3783
0
    return INVALID_SOCKET;
3784
0
  }
3785
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3786
3787
0
  for (auto rp = result; rp; rp = rp->ai_next) {
3788
    // Create a socket
3789
#ifdef _WIN32
3790
    auto sock =
3791
        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
3792
                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3793
    /**
3794
     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
3795
     * and above the socket creation fails on older Windows Systems.
3796
     *
3797
     * Let's try to create a socket the old way in this case.
3798
     *
3799
     * Reference:
3800
     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
3801
     *
3802
     * WSA_FLAG_NO_HANDLE_INHERIT:
3803
     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
3804
     * SP1, and later
3805
     *
3806
     */
3807
    if (sock == INVALID_SOCKET) {
3808
      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3809
    }
3810
#else
3811
3812
0
#ifdef SOCK_CLOEXEC
3813
0
    auto sock =
3814
0
        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3815
#else
3816
    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3817
#endif
3818
3819
0
#endif
3820
0
    if (sock == INVALID_SOCKET) { continue; }
3821
3822
#if !defined _WIN32 && !defined SOCK_CLOEXEC
3823
    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3824
      close_socket(sock);
3825
      continue;
3826
    }
3827
#endif
3828
3829
0
    if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
3830
3831
0
    if (rp->ai_family == AF_INET6) {
3832
0
      set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
3833
0
    }
3834
3835
0
    if (socket_options) { socket_options(sock); }
3836
3837
    // bind or connect
3838
0
    auto quit = false;
3839
0
    if (bind_or_connect(sock, *rp, quit)) { return sock; }
3840
3841
0
    close_socket(sock);
3842
3843
0
    if (quit) { break; }
3844
0
  }
3845
3846
0
  return INVALID_SOCKET;
3847
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)
3848
3849
0
inline void set_nonblocking(socket_t sock, bool nonblocking) {
3850
#ifdef _WIN32
3851
  auto flags = nonblocking ? 1UL : 0UL;
3852
  ioctlsocket(sock, FIONBIO, &flags);
3853
#else
3854
0
  auto flags = fcntl(sock, F_GETFL, 0);
3855
0
  fcntl(sock, F_SETFL,
3856
0
        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3857
0
#endif
3858
0
}
3859
3860
0
inline bool is_connection_error() {
3861
#ifdef _WIN32
3862
  return WSAGetLastError() != WSAEWOULDBLOCK;
3863
#else
3864
0
  return errno != EINPROGRESS;
3865
0
#endif
3866
0
}
3867
3868
0
inline bool bind_ip_address(socket_t sock, const std::string &host) {
3869
0
  struct addrinfo hints;
3870
0
  struct addrinfo *result;
3871
3872
0
  memset(&hints, 0, sizeof(struct addrinfo));
3873
0
  hints.ai_family = AF_UNSPEC;
3874
0
  hints.ai_socktype = SOCK_STREAM;
3875
0
  hints.ai_protocol = 0;
3876
3877
0
  if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
3878
0
    return false;
3879
0
  }
3880
3881
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3882
3883
0
  auto ret = false;
3884
0
  for (auto rp = result; rp; rp = rp->ai_next) {
3885
0
    const auto &ai = *rp;
3886
0
    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
3887
0
      ret = true;
3888
0
      break;
3889
0
    }
3890
0
  }
3891
3892
0
  return ret;
3893
0
}
3894
3895
#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3896
#define USE_IF2IP
3897
#endif
3898
3899
#ifdef USE_IF2IP
3900
0
inline std::string if2ip(int address_family, const std::string &ifn) {
3901
0
  struct ifaddrs *ifap;
3902
0
  getifaddrs(&ifap);
3903
0
  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
3904
3905
0
  std::string addr_candidate;
3906
0
  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3907
0
    if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3908
0
        (AF_UNSPEC == address_family ||
3909
0
         ifa->ifa_addr->sa_family == address_family)) {
3910
0
      if (ifa->ifa_addr->sa_family == AF_INET) {
3911
0
        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
3912
0
        char buf[INET_ADDRSTRLEN];
3913
0
        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3914
0
          return std::string(buf, INET_ADDRSTRLEN);
3915
0
        }
3916
0
      } else if (ifa->ifa_addr->sa_family == AF_INET6) {
3917
0
        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
3918
0
        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
3919
0
          char buf[INET6_ADDRSTRLEN] = {};
3920
0
          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
3921
            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
3922
0
            auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3923
0
            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3924
0
              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3925
0
            } else {
3926
0
              return std::string(buf, INET6_ADDRSTRLEN);
3927
0
            }
3928
0
          }
3929
0
        }
3930
0
      }
3931
0
    }
3932
0
  }
3933
0
  return addr_candidate;
3934
0
}
3935
#endif
3936
3937
inline socket_t create_client_socket(
3938
    const std::string &host, const std::string &ip, int port,
3939
    int address_family, bool tcp_nodelay, bool ipv6_v6only,
3940
    SocketOptions socket_options, time_t connection_timeout_sec,
3941
    time_t connection_timeout_usec, time_t read_timeout_sec,
3942
    time_t read_timeout_usec, time_t write_timeout_sec,
3943
0
    time_t write_timeout_usec, const std::string &intf, Error &error) {
3944
0
  auto sock = create_socket(
3945
0
      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
3946
0
      std::move(socket_options),
3947
0
      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
3948
0
        if (!intf.empty()) {
3949
0
#ifdef USE_IF2IP
3950
0
          auto ip_from_if = if2ip(address_family, intf);
3951
0
          if (ip_from_if.empty()) { ip_from_if = intf; }
3952
0
          if (!bind_ip_address(sock2, ip_from_if)) {
3953
0
            error = Error::BindIPAddress;
3954
0
            return false;
3955
0
          }
3956
0
#endif
3957
0
        }
3958
3959
0
        set_nonblocking(sock2, true);
3960
3961
0
        auto ret =
3962
0
            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
3963
3964
0
        if (ret < 0) {
3965
0
          if (is_connection_error()) {
3966
0
            error = Error::Connection;
3967
0
            return false;
3968
0
          }
3969
0
          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
3970
0
                                             connection_timeout_usec);
3971
0
          if (error != Error::Success) {
3972
0
            if (error == Error::ConnectionTimeout) { quit = true; }
3973
0
            return false;
3974
0
          }
3975
0
        }
3976
3977
0
        set_nonblocking(sock2, false);
3978
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
3979
0
                            read_timeout_usec);
3980
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
3981
0
                            write_timeout_usec);
3982
3983
0
        error = Error::Success;
3984
0
        return true;
3985
0
      },
3986
0
      connection_timeout_sec); // Pass DNS timeout
3987
3988
0
  if (sock != INVALID_SOCKET) {
3989
0
    error = Error::Success;
3990
0
  } else {
3991
0
    if (error == Error::Success) { error = Error::Connection; }
3992
0
  }
3993
3994
0
  return sock;
3995
0
}
3996
3997
inline bool get_ip_and_port(const struct sockaddr_storage &addr,
3998
0
                            socklen_t addr_len, std::string &ip, int &port) {
3999
0
  if (addr.ss_family == AF_INET) {
4000
0
    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
4001
0
  } else if (addr.ss_family == AF_INET6) {
4002
0
    port =
4003
0
        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
4004
0
  } else {
4005
0
    return false;
4006
0
  }
4007
4008
0
  std::array<char, NI_MAXHOST> ipstr{};
4009
0
  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
4010
0
                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
4011
0
                  0, NI_NUMERICHOST)) {
4012
0
    return false;
4013
0
  }
4014
4015
0
  ip = ipstr.data();
4016
0
  return true;
4017
0
}
4018
4019
0
inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
4020
0
  struct sockaddr_storage addr;
4021
0
  socklen_t addr_len = sizeof(addr);
4022
0
  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
4023
0
                   &addr_len)) {
4024
0
    get_ip_and_port(addr, addr_len, ip, port);
4025
0
  }
4026
0
}
4027
4028
0
inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
4029
0
  struct sockaddr_storage addr;
4030
0
  socklen_t addr_len = sizeof(addr);
4031
4032
0
  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
4033
0
                   &addr_len)) {
4034
0
#ifndef _WIN32
4035
0
    if (addr.ss_family == AF_UNIX) {
4036
0
#if defined(__linux__)
4037
0
      struct ucred ucred;
4038
0
      socklen_t len = sizeof(ucred);
4039
0
      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
4040
0
        port = ucred.pid;
4041
0
      }
4042
#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
4043
      pid_t pid;
4044
      socklen_t len = sizeof(pid);
4045
      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
4046
        port = pid;
4047
      }
4048
#endif
4049
0
      return;
4050
0
    }
4051
0
#endif
4052
0
    get_ip_and_port(addr, addr_len, ip, port);
4053
0
  }
4054
0
}
4055
4056
inline constexpr unsigned int str2tag_core(const char *s, size_t l,
4057
21.6k
                                           unsigned int h) {
4058
21.6k
  return (l == 0)
4059
21.6k
             ? h
4060
21.6k
             : str2tag_core(
4061
20.5k
                   s + 1, l - 1,
4062
                   // Unsets the 6 high bits of h, therefore no overflow happens
4063
20.5k
                   (((std::numeric_limits<unsigned int>::max)() >> 6) &
4064
20.5k
                    h * 33) ^
4065
20.5k
                       static_cast<unsigned char>(*s));
4066
21.6k
}
4067
4068
1.10k
inline unsigned int str2tag(const std::string &s) {
4069
1.10k
  return str2tag_core(s.data(), s.size(), 0);
4070
1.10k
}
4071
4072
namespace udl {
4073
4074
0
inline constexpr unsigned int operator""_t(const char *s, size_t l) {
4075
0
  return str2tag_core(s, l, 0);
4076
0
}
4077
4078
} // namespace udl
4079
4080
inline std::string
4081
find_content_type(const std::string &path,
4082
                  const std::map<std::string, std::string> &user_data,
4083
0
                  const std::string &default_content_type) {
4084
0
  auto ext = file_extension(path);
4085
4086
0
  auto it = user_data.find(ext);
4087
0
  if (it != user_data.end()) { return it->second; }
4088
4089
0
  using udl::operator""_t;
4090
4091
0
  switch (str2tag(ext)) {
4092
0
  default: return default_content_type;
4093
4094
0
  case "css"_t: return "text/css";
4095
0
  case "csv"_t: return "text/csv";
4096
0
  case "htm"_t:
4097
0
  case "html"_t: return "text/html";
4098
0
  case "js"_t:
4099
0
  case "mjs"_t: return "text/javascript";
4100
0
  case "txt"_t: return "text/plain";
4101
0
  case "vtt"_t: return "text/vtt";
4102
4103
0
  case "apng"_t: return "image/apng";
4104
0
  case "avif"_t: return "image/avif";
4105
0
  case "bmp"_t: return "image/bmp";
4106
0
  case "gif"_t: return "image/gif";
4107
0
  case "png"_t: return "image/png";
4108
0
  case "svg"_t: return "image/svg+xml";
4109
0
  case "webp"_t: return "image/webp";
4110
0
  case "ico"_t: return "image/x-icon";
4111
0
  case "tif"_t: return "image/tiff";
4112
0
  case "tiff"_t: return "image/tiff";
4113
0
  case "jpg"_t:
4114
0
  case "jpeg"_t: return "image/jpeg";
4115
4116
0
  case "mp4"_t: return "video/mp4";
4117
0
  case "mpeg"_t: return "video/mpeg";
4118
0
  case "webm"_t: return "video/webm";
4119
4120
0
  case "mp3"_t: return "audio/mp3";
4121
0
  case "mpga"_t: return "audio/mpeg";
4122
0
  case "weba"_t: return "audio/webm";
4123
0
  case "wav"_t: return "audio/wave";
4124
4125
0
  case "otf"_t: return "font/otf";
4126
0
  case "ttf"_t: return "font/ttf";
4127
0
  case "woff"_t: return "font/woff";
4128
0
  case "woff2"_t: return "font/woff2";
4129
4130
0
  case "7z"_t: return "application/x-7z-compressed";
4131
0
  case "atom"_t: return "application/atom+xml";
4132
0
  case "pdf"_t: return "application/pdf";
4133
0
  case "json"_t: return "application/json";
4134
0
  case "rss"_t: return "application/rss+xml";
4135
0
  case "tar"_t: return "application/x-tar";
4136
0
  case "xht"_t:
4137
0
  case "xhtml"_t: return "application/xhtml+xml";
4138
0
  case "xslt"_t: return "application/xslt+xml";
4139
0
  case "xml"_t: return "application/xml";
4140
0
  case "gz"_t: return "application/gzip";
4141
0
  case "zip"_t: return "application/zip";
4142
0
  case "wasm"_t: return "application/wasm";
4143
0
  }
4144
0
}
4145
4146
1.10k
inline bool can_compress_content_type(const std::string &content_type) {
4147
1.10k
  using udl::operator""_t;
4148
4149
1.10k
  auto tag = str2tag(content_type);
4150
4151
1.10k
  switch (tag) {
4152
0
  case "image/svg+xml"_t:
4153
0
  case "application/javascript"_t:
4154
0
  case "application/json"_t:
4155
0
  case "application/xml"_t:
4156
0
  case "application/protobuf"_t:
4157
0
  case "application/xhtml+xml"_t: return true;
4158
4159
0
  case "text/event-stream"_t: return false;
4160
4161
1.10k
  default: return !content_type.rfind("text/", 0);
4162
1.10k
  }
4163
1.10k
}
4164
4165
1.10k
inline EncodingType encoding_type(const Request &req, const Response &res) {
4166
1.10k
  auto ret =
4167
1.10k
      detail::can_compress_content_type(res.get_header_value("Content-Type"));
4168
1.10k
  if (!ret) { return EncodingType::None; }
4169
4170
955
  const auto &s = req.get_header_value("Accept-Encoding");
4171
955
  (void)(s);
4172
4173
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4174
  // TODO: 'Accept-Encoding' has br, not br;q=0
4175
  ret = s.find("br") != std::string::npos;
4176
  if (ret) { return EncodingType::Brotli; }
4177
#endif
4178
4179
955
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4180
  // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
4181
955
  ret = s.find("gzip") != std::string::npos;
4182
955
  if (ret) { return EncodingType::Gzip; }
4183
945
#endif
4184
4185
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4186
  // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
4187
  ret = s.find("zstd") != std::string::npos;
4188
  if (ret) { return EncodingType::Zstd; }
4189
#endif
4190
4191
945
  return EncodingType::None;
4192
955
}
4193
4194
inline bool nocompressor::compress(const char *data, size_t data_length,
4195
0
                                   bool /*last*/, Callback callback) {
4196
0
  if (!data_length) { return true; }
4197
0
  return callback(data, data_length);
4198
0
}
4199
4200
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4201
10
inline gzip_compressor::gzip_compressor() {
4202
10
  std::memset(&strm_, 0, sizeof(strm_));
4203
10
  strm_.zalloc = Z_NULL;
4204
10
  strm_.zfree = Z_NULL;
4205
10
  strm_.opaque = Z_NULL;
4206
4207
10
  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
4208
10
                           Z_DEFAULT_STRATEGY) == Z_OK;
4209
10
}
4210
4211
10
inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
4212
4213
inline bool gzip_compressor::compress(const char *data, size_t data_length,
4214
10
                                      bool last, Callback callback) {
4215
10
  assert(is_valid_);
4216
4217
10
  do {
4218
10
    constexpr size_t max_avail_in =
4219
10
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
4220
4221
10
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
4222
10
        (std::min)(data_length, max_avail_in));
4223
10
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
4224
4225
10
    data_length -= strm_.avail_in;
4226
10
    data += strm_.avail_in;
4227
4228
10
    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
4229
10
    auto ret = Z_OK;
4230
4231
10
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4232
10
    do {
4233
10
      strm_.avail_out = static_cast<uInt>(buff.size());
4234
10
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
4235
4236
10
      ret = deflate(&strm_, flush);
4237
10
      if (ret == Z_STREAM_ERROR) { return false; }
4238
4239
10
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
4240
0
        return false;
4241
0
      }
4242
10
    } while (strm_.avail_out == 0);
4243
4244
10
    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
4245
10
           (flush == Z_NO_FLUSH && ret == Z_OK));
4246
10
    assert(strm_.avail_in == 0);
4247
10
  } while (data_length > 0);
4248
4249
10
  return true;
4250
10
}
4251
4252
419
inline gzip_decompressor::gzip_decompressor() {
4253
419
  std::memset(&strm_, 0, sizeof(strm_));
4254
419
  strm_.zalloc = Z_NULL;
4255
419
  strm_.zfree = Z_NULL;
4256
419
  strm_.opaque = Z_NULL;
4257
4258
  // 15 is the value of wbits, which should be at the maximum possible value
4259
  // to ensure that any gzip stream can be decoded. The offset of 32 specifies
4260
  // that the stream type should be automatically detected either gzip or
4261
  // deflate.
4262
419
  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
4263
419
}
4264
4265
419
inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
4266
4267
419
inline bool gzip_decompressor::is_valid() const { return is_valid_; }
4268
4269
inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
4270
1.72k
                                          Callback callback) {
4271
1.72k
  assert(is_valid_);
4272
4273
1.72k
  auto ret = Z_OK;
4274
4275
1.72k
  do {
4276
1.72k
    constexpr size_t max_avail_in =
4277
1.72k
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
4278
4279
1.72k
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
4280
1.72k
        (std::min)(data_length, max_avail_in));
4281
1.72k
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
4282
4283
1.72k
    data_length -= strm_.avail_in;
4284
1.72k
    data += strm_.avail_in;
4285
4286
1.72k
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4287
67.6k
    while (strm_.avail_in > 0 && ret == Z_OK) {
4288
65.9k
      strm_.avail_out = static_cast<uInt>(buff.size());
4289
65.9k
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
4290
4291
65.9k
      ret = inflate(&strm_, Z_NO_FLUSH);
4292
4293
65.9k
      assert(ret != Z_STREAM_ERROR);
4294
65.9k
      switch (ret) {
4295
2
      case Z_NEED_DICT:
4296
25
      case Z_DATA_ERROR:
4297
25
      case Z_MEM_ERROR: inflateEnd(&strm_); return false;
4298
65.9k
      }
4299
4300
65.9k
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
4301
37
        return false;
4302
37
      }
4303
65.9k
    }
4304
4305
1.66k
    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
4306
4307
1.66k
  } while (data_length > 0);
4308
4309
1.66k
  return true;
4310
1.72k
}
4311
#endif
4312
4313
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4314
inline brotli_compressor::brotli_compressor() {
4315
  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
4316
}
4317
4318
inline brotli_compressor::~brotli_compressor() {
4319
  BrotliEncoderDestroyInstance(state_);
4320
}
4321
4322
inline bool brotli_compressor::compress(const char *data, size_t data_length,
4323
                                        bool last, Callback callback) {
4324
  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4325
4326
  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
4327
  auto available_in = data_length;
4328
  auto next_in = reinterpret_cast<const uint8_t *>(data);
4329
4330
  for (;;) {
4331
    if (last) {
4332
      if (BrotliEncoderIsFinished(state_)) { break; }
4333
    } else {
4334
      if (!available_in) { break; }
4335
    }
4336
4337
    auto available_out = buff.size();
4338
    auto next_out = buff.data();
4339
4340
    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
4341
                                     &available_out, &next_out, nullptr)) {
4342
      return false;
4343
    }
4344
4345
    auto output_bytes = buff.size() - available_out;
4346
    if (output_bytes) {
4347
      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
4348
    }
4349
  }
4350
4351
  return true;
4352
}
4353
4354
inline brotli_decompressor::brotli_decompressor() {
4355
  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
4356
  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
4357
                        : BROTLI_DECODER_RESULT_ERROR;
4358
}
4359
4360
inline brotli_decompressor::~brotli_decompressor() {
4361
  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
4362
}
4363
4364
inline bool brotli_decompressor::is_valid() const { return decoder_s; }
4365
4366
inline bool brotli_decompressor::decompress(const char *data,
4367
                                            size_t data_length,
4368
                                            Callback callback) {
4369
  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4370
      decoder_r == BROTLI_DECODER_RESULT_ERROR) {
4371
    return 0;
4372
  }
4373
4374
  auto next_in = reinterpret_cast<const uint8_t *>(data);
4375
  size_t avail_in = data_length;
4376
  size_t total_out;
4377
4378
  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
4379
4380
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4381
  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
4382
    char *next_out = buff.data();
4383
    size_t avail_out = buff.size();
4384
4385
    decoder_r = BrotliDecoderDecompressStream(
4386
        decoder_s, &avail_in, &next_in, &avail_out,
4387
        reinterpret_cast<uint8_t **>(&next_out), &total_out);
4388
4389
    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
4390
4391
    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
4392
  }
4393
4394
  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
4395
         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
4396
}
4397
#endif
4398
4399
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4400
inline zstd_compressor::zstd_compressor() {
4401
  ctx_ = ZSTD_createCCtx();
4402
  ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
4403
}
4404
4405
inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
4406
4407
inline bool zstd_compressor::compress(const char *data, size_t data_length,
4408
                                      bool last, Callback callback) {
4409
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4410
4411
  ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4412
  ZSTD_inBuffer input = {data, data_length, 0};
4413
4414
  bool finished;
4415
  do {
4416
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
4417
    size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
4418
4419
    if (ZSTD_isError(remaining)) { return false; }
4420
4421
    if (!callback(buff.data(), output.pos)) { return false; }
4422
4423
    finished = last ? (remaining == 0) : (input.pos == input.size);
4424
4425
  } while (!finished);
4426
4427
  return true;
4428
}
4429
4430
inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
4431
4432
inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
4433
4434
inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
4435
4436
inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
4437
                                          Callback callback) {
4438
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4439
  ZSTD_inBuffer input = {data, data_length, 0};
4440
4441
  while (input.pos < input.size) {
4442
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
4443
    size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
4444
4445
    if (ZSTD_isError(remaining)) { return false; }
4446
4447
    if (!callback(buff.data(), output.pos)) { return false; }
4448
  }
4449
4450
  return true;
4451
}
4452
#endif
4453
4454
7.95k
inline bool has_header(const Headers &headers, const std::string &key) {
4455
7.95k
  return headers.find(key) != headers.end();
4456
7.95k
}
4457
4458
inline const char *get_header_value(const Headers &headers,
4459
                                    const std::string &key, const char *def,
4460
25.3k
                                    size_t id) {
4461
25.3k
  auto rng = headers.equal_range(key);
4462
25.3k
  auto it = rng.first;
4463
25.3k
  std::advance(it, static_cast<ssize_t>(id));
4464
25.3k
  if (it != rng.second) { return it->second.c_str(); }
4465
19.2k
  return def;
4466
25.3k
}
4467
4468
template <typename T>
4469
79.4k
inline bool parse_header(const char *beg, const char *end, T fn) {
4470
  // Skip trailing spaces and tabs.
4471
85.7k
  while (beg < end && is_space_or_tab(end[-1])) {
4472
6.30k
    end--;
4473
6.30k
  }
4474
4475
79.4k
  auto p = beg;
4476
1.23M
  while (p < end && *p != ':') {
4477
1.15M
    p++;
4478
1.15M
  }
4479
4480
79.4k
  auto name = std::string(beg, p);
4481
79.4k
  if (!detail::fields::is_field_name(name)) { return false; }
4482
4483
78.5k
  if (p == end) { return false; }
4484
4485
78.2k
  auto key_end = p;
4486
4487
78.2k
  if (*p++ != ':') { return false; }
4488
4489
84.6k
  while (p < end && is_space_or_tab(*p)) {
4490
6.39k
    p++;
4491
6.39k
  }
4492
4493
78.2k
  if (p <= end) {
4494
78.2k
    auto key_len = key_end - beg;
4495
78.2k
    if (!key_len) { return false; }
4496
4497
78.2k
    auto key = std::string(beg, key_end);
4498
78.2k
    auto val = std::string(p, end);
4499
4500
78.2k
    if (!detail::fields::is_field_value(val)) { return false; }
4501
4502
77.8k
    if (case_ignore::equal(key, "Location") ||
4503
77.8k
        case_ignore::equal(key, "Referer")) {
4504
825
      fn(key, val);
4505
77.0k
    } else {
4506
77.0k
      fn(key, decode_url(val, false));
4507
77.0k
    }
4508
4509
77.8k
    return true;
4510
78.2k
  }
4511
4512
0
  return false;
4513
78.2k
}
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
4469
8.88k
inline bool parse_header(const char *beg, const char *end, T fn) {
4470
  // Skip trailing spaces and tabs.
4471
9.29k
  while (beg < end && is_space_or_tab(end[-1])) {
4472
414
    end--;
4473
414
  }
4474
4475
8.88k
  auto p = beg;
4476
189k
  while (p < end && *p != ':') {
4477
181k
    p++;
4478
181k
  }
4479
4480
8.88k
  auto name = std::string(beg, p);
4481
8.88k
  if (!detail::fields::is_field_name(name)) { return false; }
4482
4483
8.76k
  if (p == end) { return false; }
4484
4485
8.68k
  auto key_end = p;
4486
4487
8.68k
  if (*p++ != ':') { return false; }
4488
4489
9.39k
  while (p < end && is_space_or_tab(*p)) {
4490
705
    p++;
4491
705
  }
4492
4493
8.68k
  if (p <= end) {
4494
8.68k
    auto key_len = key_end - beg;
4495
8.68k
    if (!key_len) { return false; }
4496
4497
8.68k
    auto key = std::string(beg, key_end);
4498
8.68k
    auto val = std::string(p, end);
4499
4500
8.68k
    if (!detail::fields::is_field_value(val)) { return false; }
4501
4502
8.65k
    if (case_ignore::equal(key, "Location") ||
4503
8.65k
        case_ignore::equal(key, "Referer")) {
4504
72
      fn(key, val);
4505
8.58k
    } else {
4506
8.58k
      fn(key, decode_url(val, false));
4507
8.58k
    }
4508
4509
8.65k
    return true;
4510
8.68k
  }
4511
4512
0
  return false;
4513
8.68k
}
bool httplib::detail::parse_header<httplib::detail::MultipartFormDataParser::parse(char const*, unsigned long, std::__1::function<bool (char const*, unsigned long)> const&, std::__1::function<bool (httplib::MultipartFormData const&)> 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::MultipartFormDataParser::parse(char const*, unsigned long, std::__1::function<bool (char const*, unsigned long)> const&, std::__1::function<bool (httplib::MultipartFormData const&)> 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
4469
34.1k
inline bool parse_header(const char *beg, const char *end, T fn) {
4470
  // Skip trailing spaces and tabs.
4471
36.8k
  while (beg < end && is_space_or_tab(end[-1])) {
4472
2.67k
    end--;
4473
2.67k
  }
4474
4475
34.1k
  auto p = beg;
4476
520k
  while (p < end && *p != ':') {
4477
486k
    p++;
4478
486k
  }
4479
4480
34.1k
  auto name = std::string(beg, p);
4481
34.1k
  if (!detail::fields::is_field_name(name)) { return false; }
4482
4483
34.0k
  if (p == end) { return false; }
4484
4485
34.0k
  auto key_end = p;
4486
4487
34.0k
  if (*p++ != ':') { return false; }
4488
4489
36.7k
  while (p < end && is_space_or_tab(*p)) {
4490
2.71k
    p++;
4491
2.71k
  }
4492
4493
34.0k
  if (p <= end) {
4494
34.0k
    auto key_len = key_end - beg;
4495
34.0k
    if (!key_len) { return false; }
4496
4497
34.0k
    auto key = std::string(beg, key_end);
4498
34.0k
    auto val = std::string(p, end);
4499
4500
34.0k
    if (!detail::fields::is_field_value(val)) { return false; }
4501
4502
34.0k
    if (case_ignore::equal(key, "Location") ||
4503
34.0k
        case_ignore::equal(key, "Referer")) {
4504
331
      fn(key, val);
4505
33.7k
    } else {
4506
33.7k
      fn(key, decode_url(val, false));
4507
33.7k
    }
4508
4509
34.0k
    return true;
4510
34.0k
  }
4511
4512
0
  return false;
4513
34.0k
}
bool httplib::detail::parse_header<httplib::detail::MultipartFormDataParser::parse(char const*, unsigned long, std::__1::function<bool (char const*, unsigned long)> const&, std::__1::function<bool (httplib::MultipartFormData const&)> 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::MultipartFormDataParser::parse(char const*, unsigned long, std::__1::function<bool (char const*, unsigned long)> const&, std::__1::function<bool (httplib::MultipartFormData const&)> 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
4469
34.0k
inline bool parse_header(const char *beg, const char *end, T fn) {
4470
  // Skip trailing spaces and tabs.
4471
36.5k
  while (beg < end && is_space_or_tab(end[-1])) {
4472
2.47k
    end--;
4473
2.47k
  }
4474
4475
34.0k
  auto p = beg;
4476
489k
  while (p < end && *p != ':') {
4477
455k
    p++;
4478
455k
  }
4479
4480
34.0k
  auto name = std::string(beg, p);
4481
34.0k
  if (!detail::fields::is_field_name(name)) { return false; }
4482
4483
34.0k
  if (p == end) { return false; }
4484
4485
34.0k
  auto key_end = p;
4486
4487
34.0k
  if (*p++ != ':') { return false; }
4488
4489
36.7k
  while (p < end && is_space_or_tab(*p)) {
4490
2.70k
    p++;
4491
2.70k
  }
4492
4493
34.0k
  if (p <= end) {
4494
34.0k
    auto key_len = key_end - beg;
4495
34.0k
    if (!key_len) { return false; }
4496
4497
34.0k
    auto key = std::string(beg, key_end);
4498
34.0k
    auto val = std::string(p, end);
4499
4500
34.0k
    if (!detail::fields::is_field_value(val)) { return false; }
4501
4502
34.0k
    if (case_ignore::equal(key, "Location") ||
4503
34.0k
        case_ignore::equal(key, "Referer")) {
4504
331
      fn(key, val);
4505
33.7k
    } else {
4506
33.7k
      fn(key, decode_url(val, false));
4507
33.7k
    }
4508
4509
34.0k
    return true;
4510
34.0k
  }
4511
4512
0
  return false;
4513
34.0k
}
bool httplib::detail::parse_header<httplib::detail::read_content_chunked<httplib::Request>(httplib::Stream&, httplib::Request&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)::{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_content_chunked<httplib::Request>(httplib::Stream&, httplib::Request&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)::{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
4469
2.39k
inline bool parse_header(const char *beg, const char *end, T fn) {
4470
  // Skip trailing spaces and tabs.
4471
3.13k
  while (beg < end && is_space_or_tab(end[-1])) {
4472
738
    end--;
4473
738
  }
4474
4475
2.39k
  auto p = beg;
4476
33.0k
  while (p < end && *p != ':') {
4477
30.6k
    p++;
4478
30.6k
  }
4479
4480
2.39k
  auto name = std::string(beg, p);
4481
2.39k
  if (!detail::fields::is_field_name(name)) { return false; }
4482
4483
1.65k
  if (p == end) { return false; }
4484
4485
1.48k
  auto key_end = p;
4486
4487
1.48k
  if (*p++ != ':') { return false; }
4488
4489
1.75k
  while (p < end && is_space_or_tab(*p)) {
4490
272
    p++;
4491
272
  }
4492
4493
1.48k
  if (p <= end) {
4494
1.48k
    auto key_len = key_end - beg;
4495
1.48k
    if (!key_len) { return false; }
4496
4497
1.48k
    auto key = std::string(beg, key_end);
4498
1.48k
    auto val = std::string(p, end);
4499
4500
1.48k
    if (!detail::fields::is_field_value(val)) { return false; }
4501
4502
1.16k
    if (case_ignore::equal(key, "Location") ||
4503
1.16k
        case_ignore::equal(key, "Referer")) {
4504
91
      fn(key, val);
4505
1.07k
    } else {
4506
1.07k
      fn(key, decode_url(val, false));
4507
1.07k
    }
4508
4509
1.16k
    return true;
4510
1.48k
  }
4511
4512
0
  return false;
4513
1.48k
}
Unexecuted instantiation: bool httplib::detail::parse_header<httplib::detail::read_content_chunked<httplib::Response>(httplib::Stream&, httplib::Response&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)::{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_content_chunked<httplib::Response>(httplib::Stream&, httplib::Response&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)::{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})
4514
4515
3.62k
inline bool read_headers(Stream &strm, Headers &headers) {
4516
3.62k
  const auto bufsiz = 2048;
4517
3.62k
  char buf[bufsiz];
4518
3.62k
  stream_line_reader line_reader(strm, buf, bufsiz);
4519
4520
3.62k
  size_t header_count = 0;
4521
4522
12.5k
  for (;;) {
4523
12.5k
    if (!line_reader.getline()) { return false; }
4524
4525
    // Check if the line ends with CRLF.
4526
11.9k
    auto line_terminator_len = 2;
4527
11.9k
    if (line_reader.end_with_crlf()) {
4528
      // Blank line indicates end of headers.
4529
11.7k
      if (line_reader.size() == 2) { break; }
4530
11.7k
    } else {
4531
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4532
      // Blank line indicates end of headers.
4533
      if (line_reader.size() == 1) { break; }
4534
      line_terminator_len = 1;
4535
#else
4536
254
      continue; // Skip invalid line.
4537
254
#endif
4538
254
    }
4539
4540
8.88k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
4541
4542
    // Check header count limit
4543
8.88k
    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
4544
4545
    // Exclude line terminator
4546
8.88k
    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4547
4548
8.88k
    if (!parse_header(line_reader.ptr(), end,
4549
8.88k
                      [&](const std::string &key, const std::string &val) {
4550
8.65k
                        headers.emplace(key, val);
4551
8.65k
                      })) {
4552
223
      return false;
4553
223
    }
4554
4555
8.65k
    header_count++;
4556
8.65k
  }
4557
4558
2.83k
  return true;
4559
3.62k
}
4560
4561
inline bool read_content_with_length(Stream &strm, uint64_t len,
4562
                                     DownloadProgress progress,
4563
2.92k
                                     ContentReceiverWithProgress out) {
4564
2.92k
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
4565
4566
2.92k
  uint64_t r = 0;
4567
5.73k
  while (r < len) {
4568
3.08k
    auto read_len = static_cast<size_t>(len - r);
4569
3.08k
    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4570
3.08k
    if (n <= 0) { return false; }
4571
4572
2.80k
    if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
4573
2.80k
    r += static_cast<uint64_t>(n);
4574
4575
2.80k
    if (progress) {
4576
0
      if (!progress(r, len)) { return false; }
4577
0
    }
4578
2.80k
  }
4579
4580
2.65k
  return true;
4581
2.92k
}
4582
4583
0
inline void skip_content_with_length(Stream &strm, uint64_t len) {
4584
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
4585
0
  uint64_t r = 0;
4586
0
  while (r < len) {
4587
0
    auto read_len = static_cast<size_t>(len - r);
4588
0
    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4589
0
    if (n <= 0) { return; }
4590
0
    r += static_cast<uint64_t>(n);
4591
0
  }
4592
0
}
4593
4594
inline bool read_content_without_length(Stream &strm,
4595
1.81k
                                        ContentReceiverWithProgress out) {
4596
1.81k
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
4597
1.81k
  uint64_t r = 0;
4598
4.21k
  for (;;) {
4599
4.21k
    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4600
4.21k
    if (n == 0) { return true; }
4601
2.58k
    if (n < 0) { return false; }
4602
4603
2.58k
    if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
4604
2.39k
    r += static_cast<uint64_t>(n);
4605
2.39k
  }
4606
4607
0
  return true;
4608
1.81k
}
4609
4610
template <typename T>
4611
inline bool read_content_chunked(Stream &strm, T &x,
4612
262
                                 ContentReceiverWithProgress out) {
4613
262
  const auto bufsiz = 16;
4614
262
  char buf[bufsiz];
4615
4616
262
  stream_line_reader line_reader(strm, buf, bufsiz);
4617
4618
262
  if (!line_reader.getline()) { return false; }
4619
4620
260
  unsigned long chunk_len;
4621
2.84k
  while (true) {
4622
2.84k
    char *end_ptr;
4623
4624
2.84k
    chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
4625
4626
2.84k
    if (end_ptr == line_reader.ptr()) { return false; }
4627
2.83k
    if (chunk_len == ULONG_MAX) { return false; }
4628
4629
2.83k
    if (chunk_len == 0) { break; }
4630
4631
2.74k
    if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
4632
100
      return false;
4633
100
    }
4634
4635
2.64k
    if (!line_reader.getline()) { return false; }
4636
4637
2.61k
    if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
4638
4639
2.58k
    if (!line_reader.getline()) { return false; }
4640
2.58k
  }
4641
4642
96
  assert(chunk_len == 0);
4643
4644
  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
4645
  // transfer coding is complete when a chunk with a chunk-size of zero is
4646
  // received, possibly followed by a trailer section, and finally terminated by
4647
  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
4648
  //
4649
  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
4650
  // does't care for the existence of the final CRLF. In other words, it seems
4651
  // to be ok whether the final CRLF exists or not in the chunked data.
4652
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4653
  //
4654
  // According to the reference code in RFC 9112, cpp-httplib now allows
4655
  // chunked transfer coding data without the final CRLF.
4656
96
  if (!line_reader.getline()) { return true; }
4657
4658
92
  size_t trailer_header_count = 0;
4659
2.41k
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
4660
2.40k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
4661
4662
    // Check trailer header count limit
4663
2.40k
    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
4664
4665
    // Exclude line terminator
4666
2.39k
    constexpr auto line_terminator_len = 2;
4667
2.39k
    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4668
4669
2.39k
    parse_header(line_reader.ptr(), end,
4670
2.39k
                 [&](const std::string &key, const std::string &val) {
4671
1.16k
                   x.headers.emplace(key, val);
4672
1.16k
                 });
4673
4674
2.39k
    trailer_header_count++;
4675
4676
2.39k
    if (!line_reader.getline()) { return false; }
4677
2.39k
  }
4678
4679
12
  return true;
4680
92
}
bool httplib::detail::read_content_chunked<httplib::Request>(httplib::Stream&, httplib::Request&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)
Line
Count
Source
4612
262
                                 ContentReceiverWithProgress out) {
4613
262
  const auto bufsiz = 16;
4614
262
  char buf[bufsiz];
4615
4616
262
  stream_line_reader line_reader(strm, buf, bufsiz);
4617
4618
262
  if (!line_reader.getline()) { return false; }
4619
4620
260
  unsigned long chunk_len;
4621
2.84k
  while (true) {
4622
2.84k
    char *end_ptr;
4623
4624
2.84k
    chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
4625
4626
2.84k
    if (end_ptr == line_reader.ptr()) { return false; }
4627
2.83k
    if (chunk_len == ULONG_MAX) { return false; }
4628
4629
2.83k
    if (chunk_len == 0) { break; }
4630
4631
2.74k
    if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
4632
100
      return false;
4633
100
    }
4634
4635
2.64k
    if (!line_reader.getline()) { return false; }
4636
4637
2.61k
    if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
4638
4639
2.58k
    if (!line_reader.getline()) { return false; }
4640
2.58k
  }
4641
4642
96
  assert(chunk_len == 0);
4643
4644
  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
4645
  // transfer coding is complete when a chunk with a chunk-size of zero is
4646
  // received, possibly followed by a trailer section, and finally terminated by
4647
  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
4648
  //
4649
  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
4650
  // does't care for the existence of the final CRLF. In other words, it seems
4651
  // to be ok whether the final CRLF exists or not in the chunked data.
4652
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4653
  //
4654
  // According to the reference code in RFC 9112, cpp-httplib now allows
4655
  // chunked transfer coding data without the final CRLF.
4656
96
  if (!line_reader.getline()) { return true; }
4657
4658
92
  size_t trailer_header_count = 0;
4659
2.41k
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
4660
2.40k
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
4661
4662
    // Check trailer header count limit
4663
2.40k
    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
4664
4665
    // Exclude line terminator
4666
2.39k
    constexpr auto line_terminator_len = 2;
4667
2.39k
    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4668
4669
2.39k
    parse_header(line_reader.ptr(), end,
4670
2.39k
                 [&](const std::string &key, const std::string &val) {
4671
2.39k
                   x.headers.emplace(key, val);
4672
2.39k
                 });
4673
4674
2.39k
    trailer_header_count++;
4675
4676
2.39k
    if (!line_reader.getline()) { return false; }
4677
2.39k
  }
4678
4679
12
  return true;
4680
92
}
Unexecuted instantiation: bool httplib::detail::read_content_chunked<httplib::Response>(httplib::Stream&, httplib::Response&, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)
4681
4682
2.58k
inline bool is_chunked_transfer_encoding(const Headers &headers) {
4683
2.58k
  return case_ignore::equal(
4684
2.58k
      get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
4685
2.58k
}
4686
4687
template <typename T, typename U>
4688
bool prepare_content_receiver(T &x, int &status,
4689
                              ContentReceiverWithProgress receiver,
4690
2.28k
                              bool decompress, U callback) {
4691
2.28k
  if (decompress) {
4692
2.28k
    std::string encoding = x.get_header_value("Content-Encoding");
4693
2.28k
    std::unique_ptr<decompressor> decompressor;
4694
4695
2.28k
    if (encoding == "gzip" || encoding == "deflate") {
4696
419
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4697
419
      decompressor = detail::make_unique<gzip_decompressor>();
4698
#else
4699
      status = StatusCode::UnsupportedMediaType_415;
4700
      return false;
4701
#endif
4702
1.86k
    } else if (encoding.find("br") != std::string::npos) {
4703
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4704
      decompressor = detail::make_unique<brotli_decompressor>();
4705
#else
4706
13
      status = StatusCode::UnsupportedMediaType_415;
4707
13
      return false;
4708
13
#endif
4709
1.85k
    } else if (encoding == "zstd") {
4710
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4711
      decompressor = detail::make_unique<zstd_decompressor>();
4712
#else
4713
1
      status = StatusCode::UnsupportedMediaType_415;
4714
1
      return false;
4715
1
#endif
4716
1
    }
4717
4718
2.27k
    if (decompressor) {
4719
419
      if (decompressor->is_valid()) {
4720
419
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
4721
1.72k
                                              uint64_t off, uint64_t len) {
4722
1.72k
          return decompressor->decompress(buf, n,
4723
65.9k
                                          [&](const char *buf2, size_t n2) {
4724
65.9k
                                            return receiver(buf2, n2, off, len);
4725
65.9k
                                          });
4726
1.72k
        };
4727
419
        return callback(std::move(out));
4728
419
      } else {
4729
0
        status = StatusCode::InternalServerError_500;
4730
0
        return false;
4731
0
      }
4732
419
    }
4733
2.27k
  }
4734
4735
1.85k
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
4736
3.66k
                                        uint64_t len) {
4737
3.66k
    return receiver(buf, n, off, len);
4738
3.66k
  };
4739
1.85k
  return callback(std::move(out));
4740
2.28k
}
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
4690
2.28k
                              bool decompress, U callback) {
4691
2.28k
  if (decompress) {
4692
2.28k
    std::string encoding = x.get_header_value("Content-Encoding");
4693
2.28k
    std::unique_ptr<decompressor> decompressor;
4694
4695
2.28k
    if (encoding == "gzip" || encoding == "deflate") {
4696
419
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4697
419
      decompressor = detail::make_unique<gzip_decompressor>();
4698
#else
4699
      status = StatusCode::UnsupportedMediaType_415;
4700
      return false;
4701
#endif
4702
1.86k
    } else if (encoding.find("br") != std::string::npos) {
4703
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4704
      decompressor = detail::make_unique<brotli_decompressor>();
4705
#else
4706
13
      status = StatusCode::UnsupportedMediaType_415;
4707
13
      return false;
4708
13
#endif
4709
1.85k
    } else if (encoding == "zstd") {
4710
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
4711
      decompressor = detail::make_unique<zstd_decompressor>();
4712
#else
4713
1
      status = StatusCode::UnsupportedMediaType_415;
4714
1
      return false;
4715
1
#endif
4716
1
    }
4717
4718
2.27k
    if (decompressor) {
4719
419
      if (decompressor->is_valid()) {
4720
419
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
4721
419
                                              uint64_t off, uint64_t len) {
4722
419
          return decompressor->decompress(buf, n,
4723
419
                                          [&](const char *buf2, size_t n2) {
4724
419
                                            return receiver(buf2, n2, off, len);
4725
419
                                          });
4726
419
        };
4727
419
        return callback(std::move(out));
4728
419
      } else {
4729
0
        status = StatusCode::InternalServerError_500;
4730
0
        return false;
4731
0
      }
4732
419
    }
4733
2.27k
  }
4734
4735
1.85k
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
4736
1.85k
                                        uint64_t len) {
4737
1.85k
    return receiver(buf, n, off, len);
4738
1.85k
  };
4739
1.85k
  return callback(std::move(out));
4740
2.28k
}
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})
4741
4742
template <typename T>
4743
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
4744
                  DownloadProgress progress,
4745
2.28k
                  ContentReceiverWithProgress receiver, bool decompress) {
4746
2.28k
  return prepare_content_receiver(
4747
2.28k
      x, status, std::move(receiver), decompress,
4748
2.28k
      [&](const ContentReceiverWithProgress &out) {
4749
2.27k
        auto ret = true;
4750
2.27k
        auto exceed_payload_max_length = false;
4751
4752
2.27k
        if (is_chunked_transfer_encoding(x.headers)) {
4753
262
          ret = read_content_chunked(strm, x, out);
4754
2.01k
        } else if (!has_header(x.headers, "Content-Length")) {
4755
1.81k
          ret = read_content_without_length(strm, out);
4756
1.81k
        } else {
4757
196
          auto is_invalid_value = false;
4758
196
          auto len = get_header_value_u64(
4759
196
              x.headers, "Content-Length",
4760
196
              (std::numeric_limits<uint64_t>::max)(), 0, is_invalid_value);
4761
4762
196
          if (is_invalid_value) {
4763
5
            ret = false;
4764
191
          } else if (len > payload_max_length) {
4765
0
            exceed_payload_max_length = true;
4766
0
            skip_content_with_length(strm, len);
4767
0
            ret = false;
4768
191
          } else if (len > 0) {
4769
188
            ret = read_content_with_length(strm, len, std::move(progress), out);
4770
188
          }
4771
196
        }
4772
4773
2.27k
        if (!ret) {
4774
619
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
4775
619
                                             : StatusCode::BadRequest_400;
4776
619
        }
4777
2.27k
        return ret;
4778
2.27k
      });
4779
2.28k
}
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
4745
2.28k
                  ContentReceiverWithProgress receiver, bool decompress) {
4746
2.28k
  return prepare_content_receiver(
4747
2.28k
      x, status, std::move(receiver), decompress,
4748
2.28k
      [&](const ContentReceiverWithProgress &out) {
4749
2.28k
        auto ret = true;
4750
2.28k
        auto exceed_payload_max_length = false;
4751
4752
2.28k
        if (is_chunked_transfer_encoding(x.headers)) {
4753
2.28k
          ret = read_content_chunked(strm, x, out);
4754
2.28k
        } else if (!has_header(x.headers, "Content-Length")) {
4755
2.28k
          ret = read_content_without_length(strm, out);
4756
2.28k
        } else {
4757
2.28k
          auto is_invalid_value = false;
4758
2.28k
          auto len = get_header_value_u64(
4759
2.28k
              x.headers, "Content-Length",
4760
2.28k
              (std::numeric_limits<uint64_t>::max)(), 0, is_invalid_value);
4761
4762
2.28k
          if (is_invalid_value) {
4763
2.28k
            ret = false;
4764
2.28k
          } else if (len > payload_max_length) {
4765
2.28k
            exceed_payload_max_length = true;
4766
2.28k
            skip_content_with_length(strm, len);
4767
2.28k
            ret = false;
4768
2.28k
          } else if (len > 0) {
4769
2.28k
            ret = read_content_with_length(strm, len, std::move(progress), out);
4770
2.28k
          }
4771
2.28k
        }
4772
4773
2.28k
        if (!ret) {
4774
2.28k
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
4775
2.28k
                                             : StatusCode::BadRequest_400;
4776
2.28k
        }
4777
2.28k
        return ret;
4778
2.28k
      });
4779
2.28k
}
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)
4780
4781
inline ssize_t write_request_line(Stream &strm, const std::string &method,
4782
0
                                  const std::string &path) {
4783
0
  std::string s = method;
4784
0
  s += " ";
4785
0
  s += path;
4786
0
  s += " HTTP/1.1\r\n";
4787
0
  return strm.write(s.data(), s.size());
4788
0
}
4789
4790
3.90k
inline ssize_t write_response_line(Stream &strm, int status) {
4791
3.90k
  std::string s = "HTTP/1.1 ";
4792
3.90k
  s += std::to_string(status);
4793
3.90k
  s += " ";
4794
3.90k
  s += httplib::status_message(status);
4795
3.90k
  s += "\r\n";
4796
3.90k
  return strm.write(s.data(), s.size());
4797
3.90k
}
4798
4799
3.90k
inline ssize_t write_headers(Stream &strm, const Headers &headers) {
4800
3.90k
  ssize_t write_len = 0;
4801
9.32k
  for (const auto &x : headers) {
4802
9.32k
    std::string s;
4803
9.32k
    s = x.first;
4804
9.32k
    s += ": ";
4805
9.32k
    s += x.second;
4806
9.32k
    s += "\r\n";
4807
4808
9.32k
    auto len = strm.write(s.data(), s.size());
4809
9.32k
    if (len < 0) { return len; }
4810
9.32k
    write_len += len;
4811
9.32k
  }
4812
3.90k
  auto len = strm.write("\r\n");
4813
3.90k
  if (len < 0) { return len; }
4814
3.90k
  write_len += len;
4815
3.90k
  return write_len;
4816
3.90k
}
4817
4818
4.96k
inline bool write_data(Stream &strm, const char *d, size_t l) {
4819
4.96k
  size_t offset = 0;
4820
9.93k
  while (offset < l) {
4821
4.96k
    auto length = strm.write(d + offset, l - offset);
4822
4.96k
    if (length < 0) { return false; }
4823
4.96k
    offset += static_cast<size_t>(length);
4824
4.96k
  }
4825
4.96k
  return true;
4826
4.96k
}
4827
4828
template <typename T>
4829
inline bool write_content_with_progress(Stream &strm,
4830
                                        const ContentProvider &content_provider,
4831
                                        size_t offset, size_t length,
4832
                                        T is_shutting_down,
4833
                                        const UploadProgress &upload_progress,
4834
0
                                        Error &error) {
4835
0
  size_t end_offset = offset + length;
4836
0
  size_t start_offset = offset;
4837
0
  auto ok = true;
4838
0
  DataSink data_sink;
4839
4840
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
4841
0
    if (ok) {
4842
0
      if (write_data(strm, d, l)) {
4843
0
        offset += l;
4844
4845
0
        if (upload_progress && length > 0) {
4846
0
          size_t current_written = offset - start_offset;
4847
0
          if (!upload_progress(current_written, length)) {
4848
0
            ok = false;
4849
0
            return false;
4850
0
          }
4851
0
        }
4852
0
      } else {
4853
0
        ok = false;
4854
0
      }
4855
0
    }
4856
0
    return ok;
4857
0
  };
4858
4859
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
4860
4861
0
  while (offset < end_offset && !is_shutting_down()) {
4862
0
    if (!strm.wait_writable()) {
4863
0
      error = Error::Write;
4864
0
      return false;
4865
0
    } else if (!content_provider(offset, end_offset - offset, data_sink)) {
4866
0
      error = Error::Canceled;
4867
0
      return false;
4868
0
    } else if (!ok) {
4869
0
      error = Error::Write;
4870
0
      return false;
4871
0
    }
4872
0
  }
4873
4874
0
  error = Error::Success;
4875
0
  return true;
4876
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&)
4877
4878
template <typename T>
4879
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
4880
                          size_t offset, size_t length, T is_shutting_down,
4881
0
                          Error &error) {
4882
0
  return write_content_with_progress<T>(strm, content_provider, offset, length,
4883
0
                                        is_shutting_down, nullptr, error);
4884
0
}
4885
4886
template <typename T>
4887
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
4888
                          size_t offset, size_t length,
4889
0
                          const T &is_shutting_down) {
4890
0
  auto error = Error::Success;
4891
0
  return write_content(strm, content_provider, offset, length, is_shutting_down,
4892
0
                       error);
4893
0
}
4894
4895
template <typename T>
4896
inline bool
4897
write_content_without_length(Stream &strm,
4898
                             const ContentProvider &content_provider,
4899
0
                             const T &is_shutting_down) {
4900
0
  size_t offset = 0;
4901
0
  auto data_available = true;
4902
0
  auto ok = true;
4903
0
  DataSink data_sink;
4904
4905
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
4906
0
    if (ok) {
4907
0
      offset += l;
4908
0
      if (!write_data(strm, d, l)) { ok = false; }
4909
0
    }
4910
0
    return ok;
4911
0
  };
4912
4913
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
4914
4915
0
  data_sink.done = [&](void) { data_available = false; };
4916
4917
0
  while (data_available && !is_shutting_down()) {
4918
0
    if (!strm.wait_writable()) {
4919
0
      return false;
4920
0
    } else if (!content_provider(offset, 0, data_sink)) {
4921
0
      return false;
4922
0
    } else if (!ok) {
4923
0
      return false;
4924
0
    }
4925
0
  }
4926
0
  return true;
4927
0
}
4928
4929
template <typename T, typename U>
4930
inline bool
4931
write_content_chunked(Stream &strm, const ContentProvider &content_provider,
4932
0
                      const T &is_shutting_down, U &compressor, Error &error) {
4933
0
  size_t offset = 0;
4934
0
  auto data_available = true;
4935
0
  auto ok = true;
4936
0
  DataSink data_sink;
4937
4938
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
4939
0
    if (ok) {
4940
0
      data_available = l > 0;
4941
0
      offset += l;
4942
4943
0
      std::string payload;
4944
0
      if (compressor.compress(d, l, false,
4945
0
                              [&](const char *data, size_t data_len) {
4946
0
                                payload.append(data, data_len);
4947
0
                                return true;
4948
0
                              })) {
4949
0
        if (!payload.empty()) {
4950
          // Emit chunked response header and footer for each chunk
4951
0
          auto chunk =
4952
0
              from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
4953
0
          if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
4954
0
        }
4955
0
      } else {
4956
0
        ok = false;
4957
0
      }
4958
0
    }
4959
0
    return ok;
4960
0
  };
4961
4962
0
  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
4963
4964
0
  auto done_with_trailer = [&](const Headers *trailer) {
4965
0
    if (!ok) { return; }
4966
4967
0
    data_available = false;
4968
4969
0
    std::string payload;
4970
0
    if (!compressor.compress(nullptr, 0, true,
4971
0
                             [&](const char *data, size_t data_len) {
4972
0
                               payload.append(data, data_len);
4973
0
                               return true;
4974
0
                             })) {
4975
0
      ok = false;
4976
0
      return;
4977
0
    }
4978
4979
0
    if (!payload.empty()) {
4980
      // Emit chunked response header and footer for each chunk
4981
0
      auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
4982
0
      if (!write_data(strm, chunk.data(), chunk.size())) {
4983
0
        ok = false;
4984
0
        return;
4985
0
      }
4986
0
    }
4987
4988
0
    constexpr const char done_marker[] = "0\r\n";
4989
0
    if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
4990
4991
    // Trailer
4992
0
    if (trailer) {
4993
0
      for (const auto &kv : *trailer) {
4994
0
        std::string field_line = kv.first + ": " + kv.second + "\r\n";
4995
0
        if (!write_data(strm, field_line.data(), field_line.size())) {
4996
0
          ok = false;
4997
0
        }
4998
0
      }
4999
0
    }
5000
5001
0
    constexpr const char crlf[] = "\r\n";
5002
0
    if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
5003
0
  };
5004
5005
0
  data_sink.done = [&](void) { done_with_trailer(nullptr); };
5006
5007
0
  data_sink.done_with_trailer = [&](const Headers &trailer) {
5008
0
    done_with_trailer(&trailer);
5009
0
  };
5010
5011
0
  while (data_available && !is_shutting_down()) {
5012
0
    if (!strm.wait_writable()) {
5013
0
      error = Error::Write;
5014
0
      return false;
5015
0
    } else if (!content_provider(offset, 0, data_sink)) {
5016
0
      error = Error::Canceled;
5017
0
      return false;
5018
0
    } else if (!ok) {
5019
0
      error = Error::Write;
5020
0
      return false;
5021
0
    }
5022
0
  }
5023
5024
0
  error = Error::Success;
5025
0
  return true;
5026
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&)
5027
5028
template <typename T, typename U>
5029
inline bool write_content_chunked(Stream &strm,
5030
                                  const ContentProvider &content_provider,
5031
0
                                  const T &is_shutting_down, U &compressor) {
5032
0
  auto error = Error::Success;
5033
0
  return write_content_chunked(strm, content_provider, is_shutting_down,
5034
0
                               compressor, error);
5035
0
}
5036
5037
template <typename T>
5038
inline bool redirect(T &cli, Request &req, Response &res,
5039
                     const std::string &path, const std::string &location,
5040
0
                     Error &error) {
5041
0
  Request new_req = req;
5042
0
  new_req.path = path;
5043
0
  new_req.redirect_count_ -= 1;
5044
0
5045
0
  if (res.status == StatusCode::SeeOther_303 &&
5046
0
      (req.method != "GET" && req.method != "HEAD")) {
5047
0
    new_req.method = "GET";
5048
0
    new_req.body.clear();
5049
0
    new_req.headers.clear();
5050
0
  }
5051
0
5052
0
  Response new_res;
5053
0
5054
0
  auto ret = cli.send(new_req, new_res, error);
5055
0
  if (ret) {
5056
0
    req = new_req;
5057
0
    res = new_res;
5058
0
5059
0
    if (res.location.empty()) { res.location = location; }
5060
0
  }
5061
0
  return ret;
5062
0
}
5063
5064
0
inline std::string params_to_query_str(const Params &params) {
5065
0
  std::string query;
5066
0
5067
0
  for (auto it = params.begin(); it != params.end(); ++it) {
5068
0
    if (it != params.begin()) { query += "&"; }
5069
0
    query += it->first;
5070
0
    query += "=";
5071
0
    query += encode_query_param(it->second);
5072
0
  }
5073
0
  return query;
5074
0
}
5075
5076
inline void parse_query_text(const char *data, std::size_t size,
5077
3.73k
                             Params &params) {
5078
3.73k
  std::set<std::string> cache;
5079
25.2k
  split(data, data + size, '&', [&](const char *b, const char *e) {
5080
25.2k
    std::string kv(b, e);
5081
25.2k
    if (cache.find(kv) != cache.end()) { return; }
5082
4.91k
    cache.insert(std::move(kv));
5083
5084
4.91k
    std::string key;
5085
4.91k
    std::string val;
5086
4.91k
    divide(b, static_cast<std::size_t>(e - b), '=',
5087
4.91k
           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
5088
4.91k
               std::size_t rhs_size) {
5089
4.91k
             key.assign(lhs_data, lhs_size);
5090
4.91k
             val.assign(rhs_data, rhs_size);
5091
4.91k
           });
5092
5093
4.91k
    if (!key.empty()) {
5094
4.63k
      params.emplace(decode_url(key, true), decode_url(val, true));
5095
4.63k
    }
5096
4.91k
  });
5097
3.73k
}
5098
5099
117
inline void parse_query_text(const std::string &s, Params &params) {
5100
117
  parse_query_text(s.data(), s.size(), params);
5101
117
}
5102
5103
inline bool parse_multipart_boundary(const std::string &content_type,
5104
781
                                     std::string &boundary) {
5105
781
  auto boundary_keyword = "boundary=";
5106
781
  auto pos = content_type.find(boundary_keyword);
5107
781
  if (pos == std::string::npos) { return false; }
5108
775
  auto end = content_type.find(';', pos);
5109
775
  auto beg = pos + strlen(boundary_keyword);
5110
775
  boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
5111
775
  return !boundary.empty();
5112
781
}
5113
5114
3.67k
inline void parse_disposition_params(const std::string &s, Params &params) {
5115
3.67k
  std::set<std::string> cache;
5116
61.6k
  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
5117
61.6k
    std::string kv(b, e);
5118
61.6k
    if (cache.find(kv) != cache.end()) { return; }
5119
17.9k
    cache.insert(kv);
5120
5121
17.9k
    std::string key;
5122
17.9k
    std::string val;
5123
25.5k
    split(b, e, '=', [&](const char *b2, const char *e2) {
5124
25.5k
      if (key.empty()) {
5125
17.3k
        key.assign(b2, e2);
5126
17.3k
      } else {
5127
8.23k
        val.assign(b2, e2);
5128
8.23k
      }
5129
25.5k
    });
5130
5131
17.9k
    if (!key.empty()) {
5132
17.3k
      params.emplace(trim_double_quotes_copy((key)),
5133
17.3k
                     trim_double_quotes_copy((val)));
5134
17.3k
    }
5135
17.9k
  });
5136
3.67k
}
5137
5138
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
5139
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
5140
#else
5141
592
inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
5142
592
#endif
5143
16.4k
  auto is_valid = [](const std::string &str) {
5144
16.4k
    return std::all_of(str.cbegin(), str.cend(),
5145
16.4k
                       [](unsigned char c) { return std::isdigit(c); });
5146
16.4k
  };
5147
5148
592
  if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
5149
573
    const auto pos = static_cast<size_t>(6);
5150
573
    const auto len = static_cast<size_t>(s.size() - 6);
5151
573
    auto all_valid_ranges = true;
5152
13.4k
    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
5153
13.4k
      if (!all_valid_ranges) { return; }
5154
5155
8.24k
      const auto it = std::find(b, e, '-');
5156
8.24k
      if (it == e) {
5157
11
        all_valid_ranges = false;
5158
11
        return;
5159
11
      }
5160
5161
8.23k
      const auto lhs = std::string(b, it);
5162
8.23k
      const auto rhs = std::string(it + 1, e);
5163
8.23k
      if (!is_valid(lhs) || !is_valid(rhs)) {
5164
19
        all_valid_ranges = false;
5165
19
        return;
5166
19
      }
5167
5168
8.21k
      const auto first =
5169
8.21k
          static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
5170
8.21k
      const auto last =
5171
8.21k
          static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
5172
8.21k
      if ((first == -1 && last == -1) ||
5173
8.21k
          (first != -1 && last != -1 && first > last)) {
5174
48
        all_valid_ranges = false;
5175
48
        return;
5176
48
      }
5177
5178
8.16k
      ranges.emplace_back(first, last);
5179
8.16k
    });
5180
573
    return all_valid_ranges && !ranges.empty();
5181
573
  }
5182
19
  return false;
5183
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
5184
}
5185
#else
5186
592
} catch (...) { return false; }
5187
#endif
5188
5189
inline bool parse_accept_header(const std::string &s,
5190
459
                                std::vector<std::string> &content_types) {
5191
459
  content_types.clear();
5192
5193
  // Empty string is considered valid (no preference)
5194
459
  if (s.empty()) { return true; }
5195
5196
  // Check for invalid patterns: leading/trailing commas or consecutive commas
5197
456
  if (s.front() == ',' || s.back() == ',' ||
5198
456
      s.find(",,") != std::string::npos) {
5199
13
    return false;
5200
13
  }
5201
5202
443
  struct AcceptEntry {
5203
443
    std::string media_type;
5204
443
    double quality;
5205
443
    int order; // Original order in header
5206
443
  };
5207
5208
443
  std::vector<AcceptEntry> entries;
5209
443
  int order = 0;
5210
443
  bool has_invalid_entry = false;
5211
5212
  // Split by comma and parse each entry
5213
37.0k
  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
5214
37.0k
    std::string entry(b, e);
5215
37.0k
    entry = trim_copy(entry);
5216
5217
37.0k
    if (entry.empty()) {
5218
0
      has_invalid_entry = true;
5219
0
      return;
5220
0
    }
5221
5222
37.0k
    AcceptEntry accept_entry;
5223
37.0k
    accept_entry.quality = 1.0; // Default quality
5224
37.0k
    accept_entry.order = order++;
5225
5226
    // Find q= parameter
5227
37.0k
    auto q_pos = entry.find(";q=");
5228
37.0k
    if (q_pos == std::string::npos) { q_pos = entry.find("; q="); }
5229
5230
37.0k
    if (q_pos != std::string::npos) {
5231
      // Extract media type (before q parameter)
5232
2.61k
      accept_entry.media_type = trim_copy(entry.substr(0, q_pos));
5233
5234
      // Extract quality value
5235
2.61k
      auto q_start = entry.find('=', q_pos) + 1;
5236
2.61k
      auto q_end = entry.find(';', q_start);
5237
2.61k
      if (q_end == std::string::npos) { q_end = entry.length(); }
5238
5239
2.61k
      std::string quality_str =
5240
2.61k
          trim_copy(entry.substr(q_start, q_end - q_start));
5241
2.61k
      if (quality_str.empty()) {
5242
295
        has_invalid_entry = true;
5243
295
        return;
5244
295
      }
5245
5246
2.32k
      try {
5247
2.32k
        accept_entry.quality = std::stod(quality_str);
5248
        // Check if quality is in valid range [0.0, 1.0]
5249
2.32k
        if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
5250
444
          has_invalid_entry = true;
5251
444
          return;
5252
444
        }
5253
2.32k
      } catch (...) {
5254
252
        has_invalid_entry = true;
5255
252
        return;
5256
252
      }
5257
34.3k
    } else {
5258
      // No quality parameter, use entire entry as media type
5259
34.3k
      accept_entry.media_type = entry;
5260
34.3k
    }
5261
5262
    // Remove additional parameters from media type
5263
36.0k
    auto param_pos = accept_entry.media_type.find(';');
5264
36.0k
    if (param_pos != std::string::npos) {
5265
2.30k
      accept_entry.media_type =
5266
2.30k
          trim_copy(accept_entry.media_type.substr(0, param_pos));
5267
2.30k
    }
5268
5269
    // Basic validation of media type format
5270
36.0k
    if (accept_entry.media_type.empty()) {
5271
723
      has_invalid_entry = true;
5272
723
      return;
5273
723
    }
5274
5275
    // Check for basic media type format (should contain '/' or be '*')
5276
35.3k
    if (accept_entry.media_type != "*" &&
5277
35.3k
        accept_entry.media_type.find('/') == std::string::npos) {
5278
1.95k
      has_invalid_entry = true;
5279
1.95k
      return;
5280
1.95k
    }
5281
5282
33.3k
    entries.push_back(accept_entry);
5283
33.3k
  });
5284
5285
  // Return false if any invalid entry was found
5286
443
  if (has_invalid_entry) { return false; }
5287
5288
  // Sort by quality (descending), then by original order (ascending)
5289
299
  std::sort(entries.begin(), entries.end(),
5290
376k
            [](const AcceptEntry &a, const AcceptEntry &b) {
5291
376k
              if (a.quality != b.quality) {
5292
8.02k
                return a.quality > b.quality; // Higher quality first
5293
8.02k
              }
5294
368k
              return a.order < b.order; // Earlier order first for same quality
5295
376k
            });
5296
5297
  // Extract sorted media types
5298
299
  content_types.reserve(entries.size());
5299
31.3k
  for (const auto &entry : entries) {
5300
31.3k
    content_types.push_back(entry.media_type);
5301
31.3k
  }
5302
5303
299
  return true;
5304
443
}
5305
5306
class MultipartFormDataParser {
5307
public:
5308
2.30k
  MultipartFormDataParser() = default;
5309
5310
771
  void set_boundary(std::string &&boundary) {
5311
771
    boundary_ = boundary;
5312
771
    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
5313
771
    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
5314
771
  }
5315
5316
515
  bool is_valid() const { return is_valid_; }
5317
5318
  bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,
5319
24.5k
             const MultipartContentHeader &header_callback) {
5320
5321
24.5k
    buf_append(buf, n);
5322
5323
84.1k
    while (buf_size() > 0) {
5324
83.5k
      switch (state_) {
5325
1.66k
      case 0: { // Initial boundary
5326
1.66k
        auto pos = buf_find(dash_boundary_crlf_);
5327
1.66k
        if (pos == buf_size()) { return true; }
5328
648
        buf_erase(pos + dash_boundary_crlf_.size());
5329
648
        state_ = 1;
5330
648
        break;
5331
1.66k
      }
5332
15.0k
      case 1: { // New entry
5333
15.0k
        clear_file_info();
5334
15.0k
        state_ = 2;
5335
15.0k
        break;
5336
1.66k
      }
5337
15.3k
      case 2: { // Headers
5338
15.3k
        auto pos = buf_find(crlf_);
5339
15.3k
        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
5340
49.3k
        while (pos < buf_size()) {
5341
          // Empty line
5342
48.8k
          if (pos == 0) {
5343
14.6k
            if (!header_callback(file_)) {
5344
1
              is_valid_ = false;
5345
1
              return false;
5346
1
            }
5347
14.6k
            buf_erase(crlf_.size());
5348
14.6k
            state_ = 3;
5349
14.6k
            break;
5350
14.6k
          }
5351
5352
34.1k
          const auto header = buf_head(pos);
5353
5354
34.1k
          if (!parse_header(header.data(), header.data() + header.size(),
5355
34.1k
                            [&](const std::string &, const std::string &) {})) {
5356
102
            is_valid_ = false;
5357
102
            return false;
5358
102
          }
5359
5360
          // parse and emplace space trimmed headers into a map
5361
34.0k
          if (!parse_header(
5362
34.0k
                  header.data(), header.data() + header.size(),
5363
34.0k
                  [&](const std::string &key, const std::string &val) {
5364
34.0k
                    file_.headers.emplace(key, val);
5365
34.0k
                  })) {
5366
0
            is_valid_ = false;
5367
0
            return false;
5368
0
          }
5369
5370
34.0k
          constexpr const char header_content_type[] = "Content-Type:";
5371
5372
34.0k
          if (start_with_case_ignore(header, header_content_type)) {
5373
1.46k
            file_.content_type =
5374
1.46k
                trim_copy(header.substr(str_len(header_content_type)));
5375
32.5k
          } else {
5376
32.5k
            thread_local const std::regex re_content_disposition(
5377
32.5k
                R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
5378
32.5k
                std::regex_constants::icase);
5379
5380
32.5k
            std::smatch m;
5381
32.5k
            if (std::regex_match(header, m, re_content_disposition)) {
5382
3.67k
              Params params;
5383
3.67k
              parse_disposition_params(m[1], params);
5384
5385
3.67k
              auto it = params.find("name");
5386
3.67k
              if (it != params.end()) {
5387
3.62k
                file_.name = it->second;
5388
3.62k
              } else {
5389
54
                is_valid_ = false;
5390
54
                return false;
5391
54
              }
5392
5393
3.62k
              it = params.find("filename");
5394
3.62k
              if (it != params.end()) { file_.filename = it->second; }
5395
5396
3.62k
              it = params.find("filename*");
5397
3.62k
              if (it != params.end()) {
5398
                // Only allow UTF-8 encoding...
5399
8
                thread_local const std::regex re_rfc5987_encoding(
5400
8
                    R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
5401
5402
8
                std::smatch m2;
5403
8
                if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
5404
0
                  file_.filename = decode_url(m2[1], false); // override...
5405
8
                } else {
5406
8
                  is_valid_ = false;
5407
8
                  return false;
5408
8
                }
5409
8
              }
5410
3.62k
            }
5411
32.5k
          }
5412
33.9k
          buf_erase(pos + crlf_.size());
5413
33.9k
          pos = buf_find(crlf_);
5414
33.9k
        }
5415
15.1k
        if (state_ != 3) { return true; }
5416
14.6k
        break;
5417
15.1k
      }
5418
25.6k
      case 3: { // Body
5419
25.6k
        if (crlf_dash_boundary_.size() > buf_size()) { return true; }
5420
25.5k
        auto pos = buf_find(crlf_dash_boundary_);
5421
25.5k
        if (pos < buf_size()) {
5422
14.5k
          if (!content_callback(buf_data(), pos)) {
5423
0
            is_valid_ = false;
5424
0
            return false;
5425
0
          }
5426
14.5k
          buf_erase(pos + crlf_dash_boundary_.size());
5427
14.5k
          state_ = 4;
5428
14.5k
        } else {
5429
11.0k
          auto len = buf_size() - crlf_dash_boundary_.size();
5430
11.0k
          if (len > 0) {
5431
10.8k
            if (!content_callback(buf_data(), len)) {
5432
0
              is_valid_ = false;
5433
0
              return false;
5434
0
            }
5435
10.8k
            buf_erase(len);
5436
10.8k
          }
5437
11.0k
          return true;
5438
11.0k
        }
5439
14.5k
        break;
5440
25.5k
      }
5441
25.7k
      case 4: { // Boundary
5442
25.7k
        if (crlf_.size() > buf_size()) { return true; }
5443
25.5k
        if (buf_start_with(crlf_)) {
5444
14.4k
          buf_erase(crlf_.size());
5445
14.4k
          state_ = 1;
5446
14.4k
        } else {
5447
11.1k
          if (dash_.size() > buf_size()) { return true; }
5448
11.1k
          if (buf_start_with(dash_)) {
5449
333
            buf_erase(dash_.size());
5450
333
            is_valid_ = true;
5451
333
            buf_erase(buf_size()); // Remove epilogue
5452
10.8k
          } else {
5453
10.8k
            return true;
5454
10.8k
          }
5455
11.1k
        }
5456
14.7k
        break;
5457
25.5k
      }
5458
83.5k
      }
5459
83.5k
    }
5460
5461
634
    return true;
5462
24.5k
  }
5463
5464
private:
5465
15.0k
  void clear_file_info() {
5466
15.0k
    file_.name.clear();
5467
15.0k
    file_.filename.clear();
5468
15.0k
    file_.content_type.clear();
5469
15.0k
    file_.headers.clear();
5470
15.0k
  }
5471
5472
34.0k
  bool start_with_case_ignore(const std::string &a, const char *b) const {
5473
34.0k
    const auto b_len = strlen(b);
5474
34.0k
    if (a.size() < b_len) { return false; }
5475
115k
    for (size_t i = 0; i < b_len; i++) {
5476
114k
      if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
5477
17.4k
        return false;
5478
17.4k
      }
5479
114k
    }
5480
1.46k
    return true;
5481
18.9k
  }
5482
5483
  const std::string dash_ = "--";
5484
  const std::string crlf_ = "\r\n";
5485
  std::string boundary_;
5486
  std::string dash_boundary_crlf_;
5487
  std::string crlf_dash_boundary_;
5488
5489
  size_t state_ = 0;
5490
  bool is_valid_ = false;
5491
  MultipartFormData file_;
5492
5493
  // Buffer
5494
  bool start_with(const std::string &a, size_t spos, size_t epos,
5495
7.18M
                  const std::string &b) const {
5496
7.18M
    if (epos - spos < b.size()) { return false; }
5497
18.1M
    for (size_t i = 0; i < b.size(); i++) {
5498
18.1M
      if (a[i + spos] != b[i]) { return false; }
5499
18.1M
    }
5500
78.7k
    return true;
5501
7.18M
  }
5502
5503
271k
  size_t buf_size() const { return buf_epos_ - buf_spos_; }
5504
5505
25.3k
  const char *buf_data() const { return &buf_[buf_spos_]; }
5506
5507
34.1k
  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
5508
5509
36.7k
  bool buf_start_with(const std::string &s) const {
5510
36.7k
    return start_with(buf_, buf_spos_, buf_epos_, s);
5511
36.7k
  }
5512
5513
76.5k
  size_t buf_find(const std::string &s) const {
5514
76.5k
    auto c = s.front();
5515
5516
76.5k
    size_t off = buf_spos_;
5517
7.15M
    while (off < buf_epos_) {
5518
7.15M
      auto pos = off;
5519
531M
      while (true) {
5520
531M
        if (pos == buf_epos_) { return buf_size(); }
5521
531M
        if (buf_[pos] == c) { break; }
5522
523M
        pos++;
5523
523M
      }
5524
5525
7.14M
      auto remaining_size = buf_epos_ - pos;
5526
7.14M
      if (s.size() > remaining_size) { return buf_size(); }
5527
5528
7.14M
      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
5529
5530
7.08M
      off = pos + 1;
5531
7.08M
    }
5532
5533
65
    return buf_size();
5534
76.5k
  }
5535
5536
24.5k
  void buf_append(const char *data, size_t n) {
5537
24.5k
    auto remaining_size = buf_size();
5538
24.5k
    if (remaining_size > 0 && buf_spos_ > 0) {
5539
387k
      for (size_t i = 0; i < remaining_size; i++) {
5540
376k
        buf_[i] = buf_[buf_spos_ + i];
5541
376k
      }
5542
11.0k
    }
5543
24.5k
    buf_spos_ = 0;
5544
24.5k
    buf_epos_ = remaining_size;
5545
5546
24.5k
    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
5547
5548
360M
    for (size_t i = 0; i < n; i++) {
5549
360M
      buf_[buf_epos_ + i] = data[i];
5550
360M
    }
5551
24.5k
    buf_epos_ += n;
5552
24.5k
  }
5553
5554
89.7k
  void buf_erase(size_t size) { buf_spos_ += size; }
5555
5556
  std::string buf_;
5557
  size_t buf_spos_ = 0;
5558
  size_t buf_epos_ = 0;
5559
};
5560
5561
145
inline std::string random_string(size_t length) {
5562
145
  constexpr const char data[] =
5563
145
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5564
5565
145
  thread_local auto engine([]() {
5566
    // std::random_device might actually be deterministic on some
5567
    // platforms, but due to lack of support in the c++ standard library,
5568
    // doing better requires either some ugly hacks or breaking portability.
5569
1
    std::random_device seed_gen;
5570
    // Request 128 bits of entropy for initialization
5571
1
    std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
5572
1
    return std::mt19937(seed_sequence);
5573
1
  }());
5574
5575
145
  std::string result;
5576
2.46k
  for (size_t i = 0; i < length; i++) {
5577
2.32k
    result += data[engine() % (sizeof(data) - 1)];
5578
2.32k
  }
5579
145
  return result;
5580
145
}
5581
5582
145
inline std::string make_multipart_data_boundary() {
5583
145
  return "--cpp-httplib-multipart-data-" + detail::random_string(16);
5584
145
}
5585
5586
0
inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
5587
0
  auto valid = true;
5588
0
  for (size_t i = 0; i < boundary.size(); i++) {
5589
0
    auto c = boundary[i];
5590
0
    if (!std::isalnum(c) && c != '-' && c != '_') {
5591
0
      valid = false;
5592
0
      break;
5593
0
    }
5594
0
  }
5595
0
  return valid;
5596
0
}
5597
5598
template <typename T>
5599
inline std::string
5600
serialize_multipart_formdata_item_begin(const T &item,
5601
0
                                        const std::string &boundary) {
5602
0
  std::string body = "--" + boundary + "\r\n";
5603
0
  body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
5604
0
  if (!item.filename.empty()) {
5605
0
    body += "; filename=\"" + item.filename + "\"";
5606
0
  }
5607
0
  body += "\r\n";
5608
0
  if (!item.content_type.empty()) {
5609
0
    body += "Content-Type: " + item.content_type + "\r\n";
5610
0
  }
5611
0
  body += "\r\n";
5612
0
5613
0
  return body;
5614
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::MultipartFormDataForClientInput>(httplib::MultipartFormDataForClientInput 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::MultipartFormDataProvider>(httplib::MultipartFormDataProvider const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
5615
5616
0
inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
5617
5618
inline std::string
5619
0
serialize_multipart_formdata_finish(const std::string &boundary) {
5620
0
  return "--" + boundary + "--\r\n";
5621
0
}
5622
5623
inline std::string
5624
0
serialize_multipart_formdata_get_content_type(const std::string &boundary) {
5625
0
  return "multipart/form-data; boundary=" + boundary;
5626
0
}
5627
5628
inline std::string
5629
serialize_multipart_formdata(const MultipartFormDataItemsForClientInput &items,
5630
0
                             const std::string &boundary, bool finish = true) {
5631
0
  std::string body;
5632
0
5633
0
  for (const auto &item : items) {
5634
0
    body += serialize_multipart_formdata_item_begin(item, boundary);
5635
0
    body += item.content + serialize_multipart_formdata_item_end();
5636
0
  }
5637
0
5638
0
  if (finish) { body += serialize_multipart_formdata_finish(boundary); }
5639
0
5640
0
  return body;
5641
0
}
5642
5643
257
inline void coalesce_ranges(Ranges &ranges, size_t content_length) {
5644
257
  if (ranges.size() <= 1) return;
5645
5646
  // Sort ranges by start position
5647
194
  std::sort(ranges.begin(), ranges.end(),
5648
803
            [](const Range &a, const Range &b) { return a.first < b.first; });
5649
5650
194
  Ranges coalesced;
5651
194
  coalesced.reserve(ranges.size());
5652
5653
728
  for (auto &r : ranges) {
5654
728
    auto first_pos = r.first;
5655
728
    auto last_pos = r.second;
5656
5657
    // Handle special cases like in range_error
5658
728
    if (first_pos == -1 && last_pos == -1) {
5659
0
      first_pos = 0;
5660
0
      last_pos = static_cast<ssize_t>(content_length);
5661
0
    }
5662
5663
728
    if (first_pos == -1) {
5664
0
      first_pos = static_cast<ssize_t>(content_length) - last_pos;
5665
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
5666
0
    }
5667
5668
728
    if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
5669
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
5670
0
    }
5671
5672
    // Skip invalid ranges
5673
728
    if (!(0 <= first_pos && first_pos <= last_pos &&
5674
728
          last_pos < static_cast<ssize_t>(content_length))) {
5675
0
      continue;
5676
0
    }
5677
5678
    // Coalesce with previous range if overlapping or adjacent (but not
5679
    // identical)
5680
728
    if (!coalesced.empty()) {
5681
534
      auto &prev = coalesced.back();
5682
      // Check if current range overlaps or is adjacent to previous range
5683
      // but don't coalesce identical ranges (allow duplicates)
5684
534
      if (first_pos <= prev.second + 1 &&
5685
534
          !(first_pos == prev.first && last_pos == prev.second)) {
5686
        // Extend the previous range
5687
308
        prev.second = (std::max)(prev.second, last_pos);
5688
308
        continue;
5689
308
      }
5690
534
    }
5691
5692
    // Add new range
5693
420
    coalesced.emplace_back(first_pos, last_pos);
5694
420
  }
5695
5696
194
  ranges = std::move(coalesced);
5697
194
}
5698
5699
1.31k
inline bool range_error(Request &req, Response &res) {
5700
1.31k
  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
5701
467
    ssize_t content_len = static_cast<ssize_t>(
5702
467
        res.content_length_ ? res.content_length_ : res.body.size());
5703
5704
467
    std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
5705
467
    size_t overwrapping_count = 0;
5706
5707
    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
5708
    // 'HTTP Semantics' to avoid potential denial-of-service attacks.
5709
    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
5710
5711
    // Too many ranges
5712
467
    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }
5713
5714
1.15k
    for (auto &r : req.ranges) {
5715
1.15k
      auto &first_pos = r.first;
5716
1.15k
      auto &last_pos = r.second;
5717
5718
1.15k
      if (first_pos == -1 && last_pos == -1) {
5719
0
        first_pos = 0;
5720
0
        last_pos = content_len;
5721
0
      }
5722
5723
1.15k
      if (first_pos == -1) {
5724
308
        first_pos = content_len - last_pos;
5725
308
        last_pos = content_len - 1;
5726
308
      }
5727
5728
      // NOTE: RFC-9110 '14.1.2. Byte Ranges':
5729
      // A client can limit the number of bytes requested without knowing the
5730
      // size of the selected representation. If the last-pos value is absent,
5731
      // or if the value is greater than or equal to the current length of the
5732
      // representation data, the byte range is interpreted as the remainder of
5733
      // the representation (i.e., the server replaces the value of last-pos
5734
      // with a value that is one less than the current length of the selected
5735
      // representation).
5736
      // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
5737
1.15k
      if (last_pos == -1 || last_pos >= content_len) {
5738
304
        last_pos = content_len - 1;
5739
304
      }
5740
5741
      // Range must be within content length
5742
1.15k
      if (!(0 <= first_pos && first_pos <= last_pos &&
5743
1.15k
            last_pos <= content_len - 1)) {
5744
175
        return true;
5745
175
      }
5746
5747
      // Request must not have more than two overlapping ranges
5748
1.26k
      for (const auto &processed_range : processed_ranges) {
5749
1.26k
        if (!(last_pos < processed_range.first ||
5750
1.26k
              first_pos > processed_range.second)) {
5751
341
          overwrapping_count++;
5752
341
          if (overwrapping_count > 2) { return true; }
5753
307
          break; // Only count once per range
5754
341
        }
5755
1.26k
      }
5756
5757
943
      processed_ranges.emplace_back(first_pos, last_pos);
5758
943
    }
5759
5760
    // After validation, coalesce overlapping ranges as per RFC 9110
5761
257
    coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
5762
257
  }
5763
5764
1.10k
  return false;
5765
1.31k
}
5766
5767
inline std::pair<size_t, size_t>
5768
483
get_range_offset_and_length(Range r, size_t content_length) {
5769
483
  assert(r.first != -1 && r.second != -1);
5770
483
  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
5771
483
  assert(r.first <= r.second &&
5772
483
         r.second < static_cast<ssize_t>(content_length));
5773
483
  (void)(content_length);
5774
483
  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
5775
483
}
5776
5777
inline std::string make_content_range_header_field(
5778
483
    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
5779
483
  auto st = offset_and_length.first;
5780
483
  auto ed = st + offset_and_length.second - 1;
5781
5782
483
  std::string field = "bytes ";
5783
483
  field += std::to_string(st);
5784
483
  field += "-";
5785
483
  field += std::to_string(ed);
5786
483
  field += "/";
5787
483
  field += std::to_string(content_length);
5788
483
  return field;
5789
483
}
5790
5791
template <typename SToken, typename CToken, typename Content>
5792
bool process_multipart_ranges_data(const Request &req,
5793
                                   const std::string &boundary,
5794
                                   const std::string &content_type,
5795
                                   size_t content_length, SToken stoken,
5796
145
                                   CToken ctoken, Content content) {
5797
516
  for (size_t i = 0; i < req.ranges.size(); i++) {
5798
371
    ctoken("--");
5799
371
    stoken(boundary);
5800
371
    ctoken("\r\n");
5801
371
    if (!content_type.empty()) {
5802
371
      ctoken("Content-Type: ");
5803
371
      stoken(content_type);
5804
371
      ctoken("\r\n");
5805
371
    }
5806
5807
371
    auto offset_and_length =
5808
371
        get_range_offset_and_length(req.ranges[i], content_length);
5809
5810
371
    ctoken("Content-Range: ");
5811
371
    stoken(make_content_range_header_field(offset_and_length, content_length));
5812
371
    ctoken("\r\n");
5813
371
    ctoken("\r\n");
5814
5815
371
    if (!content(offset_and_length.first, offset_and_length.second)) {
5816
0
      return false;
5817
0
    }
5818
371
    ctoken("\r\n");
5819
371
  }
5820
5821
145
  ctoken("--");
5822
145
  stoken(boundary);
5823
145
  ctoken("--");
5824
5825
145
  return true;
5826
145
}
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
5796
145
                                   CToken ctoken, Content content) {
5797
516
  for (size_t i = 0; i < req.ranges.size(); i++) {
5798
371
    ctoken("--");
5799
371
    stoken(boundary);
5800
371
    ctoken("\r\n");
5801
371
    if (!content_type.empty()) {
5802
371
      ctoken("Content-Type: ");
5803
371
      stoken(content_type);
5804
371
      ctoken("\r\n");
5805
371
    }
5806
5807
371
    auto offset_and_length =
5808
371
        get_range_offset_and_length(req.ranges[i], content_length);
5809
5810
371
    ctoken("Content-Range: ");
5811
371
    stoken(make_content_range_header_field(offset_and_length, content_length));
5812
371
    ctoken("\r\n");
5813
371
    ctoken("\r\n");
5814
5815
371
    if (!content(offset_and_length.first, offset_and_length.second)) {
5816
0
      return false;
5817
0
    }
5818
371
    ctoken("\r\n");
5819
371
  }
5820
5821
145
  ctoken("--");
5822
145
  stoken(boundary);
5823
145
  ctoken("--");
5824
5825
145
  return true;
5826
145
}
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})
5827
5828
inline void make_multipart_ranges_data(const Request &req, Response &res,
5829
                                       const std::string &boundary,
5830
                                       const std::string &content_type,
5831
                                       size_t content_length,
5832
145
                                       std::string &data) {
5833
145
  process_multipart_ranges_data(
5834
145
      req, boundary, content_type, content_length,
5835
1.25k
      [&](const std::string &token) { data += token; },
5836
3.25k
      [&](const std::string &token) { data += token; },
5837
371
      [&](size_t offset, size_t length) {
5838
371
        assert(offset + length <= content_length);
5839
371
        data += res.body.substr(offset, length);
5840
371
        return true;
5841
371
      });
5842
145
}
5843
5844
inline size_t get_multipart_ranges_data_length(const Request &req,
5845
                                               const std::string &boundary,
5846
                                               const std::string &content_type,
5847
0
                                               size_t content_length) {
5848
0
  size_t data_length = 0;
5849
5850
0
  process_multipart_ranges_data(
5851
0
      req, boundary, content_type, content_length,
5852
0
      [&](const std::string &token) { data_length += token.size(); },
5853
0
      [&](const std::string &token) { data_length += token.size(); },
5854
0
      [&](size_t /*offset*/, size_t length) {
5855
0
        data_length += length;
5856
0
        return true;
5857
0
      });
5858
5859
0
  return data_length;
5860
0
}
5861
5862
template <typename T>
5863
inline bool
5864
write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
5865
                            const std::string &boundary,
5866
                            const std::string &content_type,
5867
0
                            size_t content_length, const T &is_shutting_down) {
5868
0
  return process_multipart_ranges_data(
5869
0
      req, boundary, content_type, content_length,
5870
0
      [&](const std::string &token) { strm.write(token); },
5871
0
      [&](const std::string &token) { strm.write(token); },
5872
0
      [&](size_t offset, size_t length) {
5873
0
        return write_content(strm, res.content_provider_, offset, length,
5874
0
                             is_shutting_down);
5875
0
      });
5876
0
}
5877
5878
2.56k
inline bool expect_content(const Request &req) {
5879
2.56k
  if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
5880
2.56k
      req.method == "DELETE") {
5881
2.12k
    return true;
5882
2.12k
  }
5883
442
  if (req.has_header("Content-Length") &&
5884
442
      req.get_header_value_u64("Content-Length") > 0) {
5885
129
    return true;
5886
129
  }
5887
313
  if (is_chunked_transfer_encoding(req.headers)) { return true; }
5888
262
  return false;
5889
313
}
5890
5891
0
inline bool has_crlf(const std::string &s) {
5892
0
  auto p = s.c_str();
5893
0
  while (*p) {
5894
0
    if (*p == '\r' || *p == '\n') { return true; }
5895
0
    p++;
5896
0
  }
5897
0
  return false;
5898
0
}
5899
5900
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5901
inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
5902
  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
5903
      EVP_MD_CTX_new(), EVP_MD_CTX_free);
5904
5905
  unsigned int hash_length = 0;
5906
  unsigned char hash[EVP_MAX_MD_SIZE];
5907
5908
  EVP_DigestInit_ex(context.get(), algo, nullptr);
5909
  EVP_DigestUpdate(context.get(), s.c_str(), s.size());
5910
  EVP_DigestFinal_ex(context.get(), hash, &hash_length);
5911
5912
  std::stringstream ss;
5913
  for (auto i = 0u; i < hash_length; ++i) {
5914
    ss << std::hex << std::setw(2) << std::setfill('0')
5915
       << static_cast<unsigned int>(hash[i]);
5916
  }
5917
5918
  return ss.str();
5919
}
5920
5921
inline std::string MD5(const std::string &s) {
5922
  return message_digest(s, EVP_md5());
5923
}
5924
5925
inline std::string SHA_256(const std::string &s) {
5926
  return message_digest(s, EVP_sha256());
5927
}
5928
5929
inline std::string SHA_512(const std::string &s) {
5930
  return message_digest(s, EVP_sha512());
5931
}
5932
5933
inline std::pair<std::string, std::string> make_digest_authentication_header(
5934
    const Request &req, const std::map<std::string, std::string> &auth,
5935
    size_t cnonce_count, const std::string &cnonce, const std::string &username,
5936
    const std::string &password, bool is_proxy = false) {
5937
  std::string nc;
5938
  {
5939
    std::stringstream ss;
5940
    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
5941
    nc = ss.str();
5942
  }
5943
5944
  std::string qop;
5945
  if (auth.find("qop") != auth.end()) {
5946
    qop = auth.at("qop");
5947
    if (qop.find("auth-int") != std::string::npos) {
5948
      qop = "auth-int";
5949
    } else if (qop.find("auth") != std::string::npos) {
5950
      qop = "auth";
5951
    } else {
5952
      qop.clear();
5953
    }
5954
  }
5955
5956
  std::string algo = "MD5";
5957
  if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
5958
5959
  std::string response;
5960
  {
5961
    auto H = algo == "SHA-256"   ? detail::SHA_256
5962
             : algo == "SHA-512" ? detail::SHA_512
5963
                                 : detail::MD5;
5964
5965
    auto A1 = username + ":" + auth.at("realm") + ":" + password;
5966
5967
    auto A2 = req.method + ":" + req.path;
5968
    if (qop == "auth-int") { A2 += ":" + H(req.body); }
5969
5970
    if (qop.empty()) {
5971
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
5972
    } else {
5973
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
5974
                   ":" + qop + ":" + H(A2));
5975
    }
5976
  }
5977
5978
  auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
5979
5980
  auto field = "Digest username=\"" + username + "\", realm=\"" +
5981
               auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
5982
               "\", uri=\"" + req.path + "\", algorithm=" + algo +
5983
               (qop.empty() ? ", response=\""
5984
                            : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
5985
                                  cnonce + "\", response=\"") +
5986
               response + "\"" +
5987
               (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
5988
5989
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5990
  return std::make_pair(key, field);
5991
}
5992
5993
inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
5994
  detail::set_nonblocking(sock, true);
5995
  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
5996
5997
  char buf[1];
5998
  return !SSL_peek(ssl, buf, 1) &&
5999
         SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
6000
}
6001
6002
#ifdef _WIN32
6003
// NOTE: This code came up with the following stackoverflow post:
6004
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
6005
inline bool load_system_certs_on_windows(X509_STORE *store) {
6006
  auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
6007
  if (!hStore) { return false; }
6008
6009
  auto result = false;
6010
  PCCERT_CONTEXT pContext = NULL;
6011
  while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
6012
         nullptr) {
6013
    auto encoded_cert =
6014
        static_cast<const unsigned char *>(pContext->pbCertEncoded);
6015
6016
    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
6017
    if (x509) {
6018
      X509_STORE_add_cert(store, x509);
6019
      X509_free(x509);
6020
      result = true;
6021
    }
6022
  }
6023
6024
  CertFreeCertificateContext(pContext);
6025
  CertCloseStore(hStore, 0);
6026
6027
  return result;
6028
}
6029
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
6030
#if TARGET_OS_OSX
6031
template <typename T>
6032
using CFObjectPtr =
6033
    std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
6034
6035
inline void cf_object_ptr_deleter(CFTypeRef obj) {
6036
  if (obj) { CFRelease(obj); }
6037
}
6038
6039
inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
6040
  CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
6041
  CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
6042
                        kCFBooleanTrue};
6043
6044
  CFObjectPtr<CFDictionaryRef> query(
6045
      CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
6046
                         sizeof(keys) / sizeof(keys[0]),
6047
                         &kCFTypeDictionaryKeyCallBacks,
6048
                         &kCFTypeDictionaryValueCallBacks),
6049
      cf_object_ptr_deleter);
6050
6051
  if (!query) { return false; }
6052
6053
  CFTypeRef security_items = nullptr;
6054
  if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
6055
      CFArrayGetTypeID() != CFGetTypeID(security_items)) {
6056
    return false;
6057
  }
6058
6059
  certs.reset(reinterpret_cast<CFArrayRef>(security_items));
6060
  return true;
6061
}
6062
6063
inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
6064
  CFArrayRef root_security_items = nullptr;
6065
  if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
6066
    return false;
6067
  }
6068
6069
  certs.reset(root_security_items);
6070
  return true;
6071
}
6072
6073
inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
6074
  auto result = false;
6075
  for (auto i = 0; i < CFArrayGetCount(certs); ++i) {
6076
    const auto cert = reinterpret_cast<const __SecCertificate *>(
6077
        CFArrayGetValueAtIndex(certs, i));
6078
6079
    if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
6080
6081
    CFDataRef cert_data = nullptr;
6082
    if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
6083
        errSecSuccess) {
6084
      continue;
6085
    }
6086
6087
    CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
6088
6089
    auto encoded_cert = static_cast<const unsigned char *>(
6090
        CFDataGetBytePtr(cert_data_ptr.get()));
6091
6092
    auto x509 =
6093
        d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
6094
6095
    if (x509) {
6096
      X509_STORE_add_cert(store, x509);
6097
      X509_free(x509);
6098
      result = true;
6099
    }
6100
  }
6101
6102
  return result;
6103
}
6104
6105
inline bool load_system_certs_on_macos(X509_STORE *store) {
6106
  auto result = false;
6107
  CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
6108
  if (retrieve_certs_from_keychain(certs) && certs) {
6109
    result = add_certs_to_x509_store(certs.get(), store);
6110
  }
6111
6112
  if (retrieve_root_certs_from_keychain(certs) && certs) {
6113
    result = add_certs_to_x509_store(certs.get(), store) || result;
6114
  }
6115
6116
  return result;
6117
}
6118
#endif // TARGET_OS_OSX
6119
#endif // _WIN32
6120
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
6121
6122
#ifdef _WIN32
6123
class WSInit {
6124
public:
6125
  WSInit() {
6126
    WSADATA wsaData;
6127
    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
6128
  }
6129
6130
  ~WSInit() {
6131
    if (is_valid_) WSACleanup();
6132
  }
6133
6134
  bool is_valid_ = false;
6135
};
6136
6137
static WSInit wsinit_;
6138
#endif
6139
6140
inline bool parse_www_authenticate(const Response &res,
6141
                                   std::map<std::string, std::string> &auth,
6142
0
                                   bool is_proxy) {
6143
0
  auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
6144
0
  if (res.has_header(auth_key)) {
6145
0
    thread_local auto re =
6146
0
        std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
6147
0
    auto s = res.get_header_value(auth_key);
6148
0
    auto pos = s.find(' ');
6149
0
    if (pos != std::string::npos) {
6150
0
      auto type = s.substr(0, pos);
6151
0
      if (type == "Basic") {
6152
0
        return false;
6153
0
      } else if (type == "Digest") {
6154
0
        s = s.substr(pos + 1);
6155
0
        auto beg = std::sregex_iterator(s.begin(), s.end(), re);
6156
0
        for (auto i = beg; i != std::sregex_iterator(); ++i) {
6157
0
          const auto &m = *i;
6158
0
          auto key = s.substr(static_cast<size_t>(m.position(1)),
6159
0
                              static_cast<size_t>(m.length(1)));
6160
0
          auto val = m.length(2) > 0
6161
0
                         ? s.substr(static_cast<size_t>(m.position(2)),
6162
0
                                    static_cast<size_t>(m.length(2)))
6163
0
                         : s.substr(static_cast<size_t>(m.position(3)),
6164
0
                                    static_cast<size_t>(m.length(3)));
6165
0
          auth[key] = val;
6166
0
        }
6167
0
        return true;
6168
0
      }
6169
0
    }
6170
0
  }
6171
0
  return false;
6172
0
}
6173
6174
class ContentProviderAdapter {
6175
public:
6176
  explicit ContentProviderAdapter(
6177
      ContentProviderWithoutLength &&content_provider)
6178
0
      : content_provider_(content_provider) {}
6179
6180
0
  bool operator()(size_t offset, size_t, DataSink &sink) {
6181
0
    return content_provider_(offset, sink);
6182
0
  }
6183
6184
private:
6185
  ContentProviderWithoutLength content_provider_;
6186
};
6187
6188
} // namespace detail
6189
6190
0
inline std::string hosted_at(const std::string &hostname) {
6191
0
  std::vector<std::string> addrs;
6192
0
  hosted_at(hostname, addrs);
6193
0
  if (addrs.empty()) { return std::string(); }
6194
0
  return addrs[0];
6195
0
}
6196
6197
inline void hosted_at(const std::string &hostname,
6198
0
                      std::vector<std::string> &addrs) {
6199
0
  struct addrinfo hints;
6200
0
  struct addrinfo *result;
6201
0
6202
0
  memset(&hints, 0, sizeof(struct addrinfo));
6203
0
  hints.ai_family = AF_UNSPEC;
6204
0
  hints.ai_socktype = SOCK_STREAM;
6205
0
  hints.ai_protocol = 0;
6206
0
6207
0
  if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
6208
0
                                       &result, 0)) {
6209
0
#if defined __linux__ && !defined __ANDROID__
6210
0
    res_init();
6211
0
#endif
6212
0
    return;
6213
0
  }
6214
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
6215
0
6216
0
  for (auto rp = result; rp; rp = rp->ai_next) {
6217
0
    const auto &addr =
6218
0
        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
6219
0
    std::string ip;
6220
0
    auto dummy = -1;
6221
0
    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
6222
0
                                dummy)) {
6223
0
      addrs.push_back(ip);
6224
0
    }
6225
0
  }
6226
0
}
6227
6228
inline std::string append_query_params(const std::string &path,
6229
0
                                       const Params &params) {
6230
0
  std::string path_with_query = path;
6231
0
  thread_local const std::regex re("[^?]+\\?.*");
6232
0
  auto delm = std::regex_match(path, re) ? '&' : '?';
6233
0
  path_with_query += delm + detail::params_to_query_str(params);
6234
0
  return path_with_query;
6235
0
}
6236
6237
// Header utilities
6238
inline std::pair<std::string, std::string>
6239
0
make_range_header(const Ranges &ranges) {
6240
0
  std::string field = "bytes=";
6241
0
  auto i = 0;
6242
0
  for (const auto &r : ranges) {
6243
0
    if (i != 0) { field += ", "; }
6244
0
    if (r.first != -1) { field += std::to_string(r.first); }
6245
0
    field += '-';
6246
0
    if (r.second != -1) { field += std::to_string(r.second); }
6247
0
    i++;
6248
0
  }
6249
0
  return std::make_pair("Range", std::move(field));
6250
0
}
6251
6252
inline std::pair<std::string, std::string>
6253
make_basic_authentication_header(const std::string &username,
6254
0
                                 const std::string &password, bool is_proxy) {
6255
0
  auto field = "Basic " + detail::base64_encode(username + ":" + password);
6256
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
6257
0
  return std::make_pair(key, std::move(field));
6258
0
}
6259
6260
inline std::pair<std::string, std::string>
6261
make_bearer_token_authentication_header(const std::string &token,
6262
0
                                        bool is_proxy = false) {
6263
0
  auto field = "Bearer " + token;
6264
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
6265
0
  return std::make_pair(key, std::move(field));
6266
0
}
6267
6268
// Request implementation
6269
5.94k
inline bool Request::has_header(const std::string &key) const {
6270
5.94k
  return detail::has_header(headers, key);
6271
5.94k
}
6272
6273
inline std::string Request::get_header_value(const std::string &key,
6274
21.6k
                                             const char *def, size_t id) const {
6275
21.6k
  return detail::get_header_value(headers, key, def, id);
6276
21.6k
}
6277
6278
0
inline size_t Request::get_header_value_count(const std::string &key) const {
6279
0
  auto r = headers.equal_range(key);
6280
0
  return static_cast<size_t>(std::distance(r.first, r.second));
6281
0
}
6282
6283
inline void Request::set_header(const std::string &key,
6284
11.2k
                                const std::string &val) {
6285
11.2k
  if (detail::fields::is_field_name(key) &&
6286
11.2k
      detail::fields::is_field_value(val)) {
6287
11.2k
    headers.emplace(key, val);
6288
11.2k
  }
6289
11.2k
}
6290
6291
0
inline bool Request::has_param(const std::string &key) const {
6292
0
  return params.find(key) != params.end();
6293
0
}
6294
6295
inline std::string Request::get_param_value(const std::string &key,
6296
0
                                            size_t id) const {
6297
0
  auto rng = params.equal_range(key);
6298
0
  auto it = rng.first;
6299
0
  std::advance(it, static_cast<ssize_t>(id));
6300
0
  if (it != rng.second) { return it->second; }
6301
0
  return std::string();
6302
0
}
6303
6304
0
inline size_t Request::get_param_value_count(const std::string &key) const {
6305
0
  auto r = params.equal_range(key);
6306
0
  return static_cast<size_t>(std::distance(r.first, r.second));
6307
0
}
6308
6309
3.95k
inline bool Request::is_multipart_form_data() const {
6310
3.95k
  const auto &content_type = get_header_value("Content-Type");
6311
3.95k
  return !content_type.rfind("multipart/form-data", 0);
6312
3.95k
}
6313
6314
0
inline bool Request::has_file(const std::string &key) const {
6315
0
  return files.find(key) != files.end();
6316
0
}
6317
6318
0
inline MultipartFormData Request::get_file_value(const std::string &key) const {
6319
0
  auto it = files.find(key);
6320
0
  if (it != files.end()) { return it->second; }
6321
0
  return MultipartFormData();
6322
0
}
6323
6324
inline std::vector<MultipartFormData>
6325
0
Request::get_file_values(const std::string &key) const {
6326
0
  std::vector<MultipartFormData> values;
6327
0
  auto rng = files.equal_range(key);
6328
0
  for (auto it = rng.first; it != rng.second; it++) {
6329
0
    values.push_back(it->second);
6330
0
  }
6331
0
  return values;
6332
0
}
6333
6334
// Response implementation
6335
3.98k
inline bool Response::has_header(const std::string &key) const {
6336
3.98k
  return headers.find(key) != headers.end();
6337
3.98k
}
6338
6339
inline std::string Response::get_header_value(const std::string &key,
6340
                                              const char *def,
6341
1.10k
                                              size_t id) const {
6342
1.10k
  return detail::get_header_value(headers, key, def, id);
6343
1.10k
}
6344
6345
0
inline size_t Response::get_header_value_count(const std::string &key) const {
6346
0
  auto r = headers.equal_range(key);
6347
0
  return static_cast<size_t>(std::distance(r.first, r.second));
6348
0
}
6349
6350
inline void Response::set_header(const std::string &key,
6351
9.46k
                                 const std::string &val) {
6352
9.46k
  if (detail::fields::is_field_name(key) &&
6353
9.46k
      detail::fields::is_field_value(val)) {
6354
9.46k
    headers.emplace(key, val);
6355
9.46k
  }
6356
9.46k
}
6357
6358
0
inline void Response::set_redirect(const std::string &url, int stat) {
6359
0
  if (detail::fields::is_field_value(url)) {
6360
0
    set_header("Location", url);
6361
0
    if (300 <= stat && stat < 400) {
6362
0
      this->status = stat;
6363
0
    } else {
6364
0
      this->status = StatusCode::Found_302;
6365
0
    }
6366
0
  }
6367
0
}
6368
6369
inline void Response::set_content(const char *s, size_t n,
6370
0
                                  const std::string &content_type) {
6371
0
  body.assign(s, n);
6372
0
6373
0
  auto rng = headers.equal_range("Content-Type");
6374
0
  headers.erase(rng.first, rng.second);
6375
0
  set_header("Content-Type", content_type);
6376
0
}
6377
6378
inline void Response::set_content(const std::string &s,
6379
0
                                  const std::string &content_type) {
6380
0
  set_content(s.data(), s.size(), content_type);
6381
0
}
6382
6383
inline void Response::set_content(std::string &&s,
6384
1.31k
                                  const std::string &content_type) {
6385
1.31k
  body = std::move(s);
6386
6387
1.31k
  auto rng = headers.equal_range("Content-Type");
6388
1.31k
  headers.erase(rng.first, rng.second);
6389
1.31k
  set_header("Content-Type", content_type);
6390
1.31k
}
6391
6392
inline void Response::set_content_provider(
6393
    size_t in_length, const std::string &content_type, ContentProvider provider,
6394
0
    ContentProviderResourceReleaser resource_releaser) {
6395
0
  set_header("Content-Type", content_type);
6396
0
  content_length_ = in_length;
6397
0
  if (in_length > 0) { content_provider_ = std::move(provider); }
6398
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
6399
0
  is_chunked_content_provider_ = false;
6400
0
}
6401
6402
inline void Response::set_content_provider(
6403
    const std::string &content_type, ContentProviderWithoutLength provider,
6404
0
    ContentProviderResourceReleaser resource_releaser) {
6405
0
  set_header("Content-Type", content_type);
6406
0
  content_length_ = 0;
6407
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
6408
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
6409
0
  is_chunked_content_provider_ = false;
6410
0
}
6411
6412
inline void Response::set_chunked_content_provider(
6413
    const std::string &content_type, ContentProviderWithoutLength provider,
6414
0
    ContentProviderResourceReleaser resource_releaser) {
6415
0
  set_header("Content-Type", content_type);
6416
0
  content_length_ = 0;
6417
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
6418
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
6419
0
  is_chunked_content_provider_ = true;
6420
0
}
6421
6422
inline void Response::set_file_content(const std::string &path,
6423
0
                                       const std::string &content_type) {
6424
0
  file_content_path_ = path;
6425
0
  file_content_content_type_ = content_type;
6426
0
}
6427
6428
0
inline void Response::set_file_content(const std::string &path) {
6429
0
  file_content_path_ = path;
6430
0
}
6431
6432
// Result implementation
6433
0
inline bool Result::has_request_header(const std::string &key) const {
6434
0
  return request_headers_.find(key) != request_headers_.end();
6435
0
}
6436
6437
inline std::string Result::get_request_header_value(const std::string &key,
6438
                                                    const char *def,
6439
0
                                                    size_t id) const {
6440
0
  return detail::get_header_value(request_headers_, key, def, id);
6441
0
}
6442
6443
inline size_t
6444
0
Result::get_request_header_value_count(const std::string &key) const {
6445
0
  auto r = request_headers_.equal_range(key);
6446
0
  return static_cast<size_t>(std::distance(r.first, r.second));
6447
0
}
6448
6449
// Stream implementation
6450
3.90k
inline ssize_t Stream::write(const char *ptr) {
6451
3.90k
  return write(ptr, strlen(ptr));
6452
3.90k
}
6453
6454
0
inline ssize_t Stream::write(const std::string &s) {
6455
0
  return write(s.data(), s.size());
6456
0
}
6457
6458
namespace detail {
6459
6460
inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
6461
                                time_t timeout_sec, time_t timeout_usec,
6462
                                time_t &actual_timeout_sec,
6463
0
                                time_t &actual_timeout_usec) {
6464
0
  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
6465
6466
0
  auto actual_timeout_msec =
6467
0
      (std::min)(max_timeout_msec - duration_msec, timeout_msec);
6468
6469
0
  if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
6470
6471
0
  actual_timeout_sec = actual_timeout_msec / 1000;
6472
0
  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
6473
0
}
6474
6475
// Socket stream implementation
6476
inline SocketStream::SocketStream(
6477
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
6478
    time_t write_timeout_sec, time_t write_timeout_usec,
6479
    time_t max_timeout_msec,
6480
    std::chrono::time_point<std::chrono::steady_clock> start_time)
6481
0
    : sock_(sock), read_timeout_sec_(read_timeout_sec),
6482
0
      read_timeout_usec_(read_timeout_usec),
6483
0
      write_timeout_sec_(write_timeout_sec),
6484
0
      write_timeout_usec_(write_timeout_usec),
6485
0
      max_timeout_msec_(max_timeout_msec), start_time_(start_time),
6486
0
      read_buff_(read_buff_size_, 0) {}
6487
6488
0
inline SocketStream::~SocketStream() = default;
6489
6490
0
inline bool SocketStream::is_readable() const {
6491
0
  return read_buff_off_ < read_buff_content_size_;
6492
0
}
6493
6494
0
inline bool SocketStream::wait_readable() const {
6495
0
  if (max_timeout_msec_ <= 0) {
6496
0
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
6497
0
  }
6498
6499
0
  time_t read_timeout_sec;
6500
0
  time_t read_timeout_usec;
6501
0
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
6502
0
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
6503
6504
0
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
6505
0
}
6506
6507
0
inline bool SocketStream::wait_writable() const {
6508
0
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
6509
0
         is_socket_alive(sock_);
6510
0
}
6511
6512
0
inline ssize_t SocketStream::read(char *ptr, size_t size) {
6513
#ifdef _WIN32
6514
  size =
6515
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
6516
#else
6517
0
  size = (std::min)(size,
6518
0
                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
6519
0
#endif
6520
6521
0
  if (read_buff_off_ < read_buff_content_size_) {
6522
0
    auto remaining_size = read_buff_content_size_ - read_buff_off_;
6523
0
    if (size <= remaining_size) {
6524
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, size);
6525
0
      read_buff_off_ += size;
6526
0
      return static_cast<ssize_t>(size);
6527
0
    } else {
6528
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
6529
0
      read_buff_off_ += remaining_size;
6530
0
      return static_cast<ssize_t>(remaining_size);
6531
0
    }
6532
0
  }
6533
6534
0
  if (!wait_readable()) { return -1; }
6535
6536
0
  read_buff_off_ = 0;
6537
0
  read_buff_content_size_ = 0;
6538
6539
0
  if (size < read_buff_size_) {
6540
0
    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
6541
0
                         CPPHTTPLIB_RECV_FLAGS);
6542
0
    if (n <= 0) {
6543
0
      return n;
6544
0
    } else if (n <= static_cast<ssize_t>(size)) {
6545
0
      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
6546
0
      return n;
6547
0
    } else {
6548
0
      memcpy(ptr, read_buff_.data(), size);
6549
0
      read_buff_off_ = size;
6550
0
      read_buff_content_size_ = static_cast<size_t>(n);
6551
0
      return static_cast<ssize_t>(size);
6552
0
    }
6553
0
  } else {
6554
0
    return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
6555
0
  }
6556
0
}
6557
6558
0
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
6559
0
  if (!wait_writable()) { return -1; }
6560
6561
#if defined(_WIN32) && !defined(_WIN64)
6562
  size =
6563
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
6564
#endif
6565
6566
0
  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
6567
0
}
6568
6569
inline void SocketStream::get_remote_ip_and_port(std::string &ip,
6570
0
                                                 int &port) const {
6571
0
  return detail::get_remote_ip_and_port(sock_, ip, port);
6572
0
}
6573
6574
inline void SocketStream::get_local_ip_and_port(std::string &ip,
6575
0
                                                int &port) const {
6576
0
  return detail::get_local_ip_and_port(sock_, ip, port);
6577
0
}
6578
6579
0
inline socket_t SocketStream::socket() const { return sock_; }
6580
6581
0
inline time_t SocketStream::duration() const {
6582
0
  return std::chrono::duration_cast<std::chrono::milliseconds>(
6583
0
             std::chrono::steady_clock::now() - start_time_)
6584
0
      .count();
6585
0
}
6586
6587
// Buffer stream implementation
6588
0
inline bool BufferStream::is_readable() const { return true; }
6589
6590
0
inline bool BufferStream::wait_readable() const { return true; }
6591
6592
0
inline bool BufferStream::wait_writable() const { return true; }
6593
6594
0
inline ssize_t BufferStream::read(char *ptr, size_t size) {
6595
#if defined(_MSC_VER) && _MSC_VER < 1910
6596
  auto len_read = buffer._Copy_s(ptr, size, size, position);
6597
#else
6598
0
  auto len_read = buffer.copy(ptr, size, position);
6599
0
#endif
6600
0
  position += static_cast<size_t>(len_read);
6601
0
  return static_cast<ssize_t>(len_read);
6602
0
}
6603
6604
17.1k
inline ssize_t BufferStream::write(const char *ptr, size_t size) {
6605
17.1k
  buffer.append(ptr, size);
6606
17.1k
  return static_cast<ssize_t>(size);
6607
17.1k
}
6608
6609
inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
6610
0
                                                 int & /*port*/) const {}
6611
6612
inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
6613
0
                                                int & /*port*/) const {}
6614
6615
0
inline socket_t BufferStream::socket() const { return 0; }
6616
6617
0
inline time_t BufferStream::duration() const { return 0; }
6618
6619
3.90k
inline const std::string &BufferStream::get_buffer() const { return buffer; }
6620
6621
inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
6622
0
    : MatcherBase(pattern) {
6623
0
  constexpr const char marker[] = "/:";
6624
6625
  // One past the last ending position of a path param substring
6626
0
  std::size_t last_param_end = 0;
6627
6628
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
6629
  // Needed to ensure that parameter names are unique during matcher
6630
  // construction
6631
  // If exceptions are disabled, only last duplicate path
6632
  // parameter will be set
6633
0
  std::unordered_set<std::string> param_name_set;
6634
0
#endif
6635
6636
0
  while (true) {
6637
0
    const auto marker_pos = pattern.find(
6638
0
        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
6639
0
    if (marker_pos == std::string::npos) { break; }
6640
6641
0
    static_fragments_.push_back(
6642
0
        pattern.substr(last_param_end, marker_pos - last_param_end + 1));
6643
6644
0
    const auto param_name_start = marker_pos + str_len(marker);
6645
6646
0
    auto sep_pos = pattern.find(separator, param_name_start);
6647
0
    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
6648
6649
0
    auto param_name =
6650
0
        pattern.substr(param_name_start, sep_pos - param_name_start);
6651
6652
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
6653
0
    if (param_name_set.find(param_name) != param_name_set.cend()) {
6654
0
      std::string msg = "Encountered path parameter '" + param_name +
6655
0
                        "' multiple times in route pattern '" + pattern + "'.";
6656
0
      throw std::invalid_argument(msg);
6657
0
    }
6658
0
#endif
6659
6660
0
    param_names_.push_back(std::move(param_name));
6661
6662
0
    last_param_end = sep_pos + 1;
6663
0
  }
6664
6665
0
  if (last_param_end < pattern.length()) {
6666
0
    static_fragments_.push_back(pattern.substr(last_param_end));
6667
0
  }
6668
0
}
6669
6670
0
inline bool PathParamsMatcher::match(Request &request) const {
6671
0
  request.matches = std::smatch();
6672
0
  request.path_params.clear();
6673
0
  request.path_params.reserve(param_names_.size());
6674
6675
  // One past the position at which the path matched the pattern last time
6676
0
  std::size_t starting_pos = 0;
6677
0
  for (size_t i = 0; i < static_fragments_.size(); ++i) {
6678
0
    const auto &fragment = static_fragments_[i];
6679
6680
0
    if (starting_pos + fragment.length() > request.path.length()) {
6681
0
      return false;
6682
0
    }
6683
6684
    // Avoid unnecessary allocation by using strncmp instead of substr +
6685
    // comparison
6686
0
    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
6687
0
                     fragment.length()) != 0) {
6688
0
      return false;
6689
0
    }
6690
6691
0
    starting_pos += fragment.length();
6692
6693
    // Should only happen when we have a static fragment after a param
6694
    // Example: '/users/:id/subscriptions'
6695
    // The 'subscriptions' fragment here does not have a corresponding param
6696
0
    if (i >= param_names_.size()) { continue; }
6697
6698
0
    auto sep_pos = request.path.find(separator, starting_pos);
6699
0
    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
6700
6701
0
    const auto &param_name = param_names_[i];
6702
6703
0
    request.path_params.emplace(
6704
0
        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
6705
6706
    // Mark everything up to '/' as matched
6707
0
    starting_pos = sep_pos + 1;
6708
0
  }
6709
  // Returns false if the path is longer than the pattern
6710
0
  return starting_pos >= request.path.length();
6711
0
}
6712
6713
1.37k
inline bool RegexMatcher::match(Request &request) const {
6714
1.37k
  request.path_params.clear();
6715
1.37k
  return std::regex_match(request.path, request.matches, regex_);
6716
1.37k
}
6717
6718
} // namespace detail
6719
6720
// HTTP server implementation
6721
inline Server::Server()
6722
2
    : new_task_queue(
6723
2
          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
6724
2
#ifndef _WIN32
6725
2
  signal(SIGPIPE, SIG_IGN);
6726
2
#endif
6727
2
}
6728
6729
0
inline Server::~Server() = default;
6730
6731
inline std::unique_ptr<detail::MatcherBase>
6732
12
Server::make_matcher(const std::string &pattern) {
6733
12
  if (pattern.find("/:") != std::string::npos) {
6734
0
    return detail::make_unique<detail::PathParamsMatcher>(pattern);
6735
12
  } else {
6736
12
    return detail::make_unique<detail::RegexMatcher>(pattern);
6737
12
  }
6738
12
}
6739
6740
2
inline Server &Server::Get(const std::string &pattern, Handler handler) {
6741
2
  get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6742
2
  return *this;
6743
2
}
6744
6745
2
inline Server &Server::Post(const std::string &pattern, Handler handler) {
6746
2
  post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6747
2
  return *this;
6748
2
}
6749
6750
inline Server &Server::Post(const std::string &pattern,
6751
0
                            HandlerWithContentReader handler) {
6752
0
  post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6753
0
                                                 std::move(handler));
6754
0
  return *this;
6755
0
}
6756
6757
2
inline Server &Server::Put(const std::string &pattern, Handler handler) {
6758
2
  put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6759
2
  return *this;
6760
2
}
6761
6762
inline Server &Server::Put(const std::string &pattern,
6763
0
                           HandlerWithContentReader handler) {
6764
0
  put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6765
0
                                                std::move(handler));
6766
0
  return *this;
6767
0
}
6768
6769
2
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
6770
2
  patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6771
2
  return *this;
6772
2
}
6773
6774
inline Server &Server::Patch(const std::string &pattern,
6775
0
                             HandlerWithContentReader handler) {
6776
0
  patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6777
0
                                                  std::move(handler));
6778
0
  return *this;
6779
0
}
6780
6781
2
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
6782
2
  delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6783
2
  return *this;
6784
2
}
6785
6786
inline Server &Server::Delete(const std::string &pattern,
6787
0
                              HandlerWithContentReader handler) {
6788
0
  delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
6789
0
                                                   std::move(handler));
6790
0
  return *this;
6791
0
}
6792
6793
2
inline Server &Server::Options(const std::string &pattern, Handler handler) {
6794
2
  options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
6795
2
  return *this;
6796
2
}
6797
6798
inline bool Server::set_base_dir(const std::string &dir,
6799
0
                                 const std::string &mount_point) {
6800
0
  return set_mount_point(mount_point, dir);
6801
0
}
6802
6803
inline bool Server::set_mount_point(const std::string &mount_point,
6804
0
                                    const std::string &dir, Headers headers) {
6805
0
  detail::FileStat stat(dir);
6806
0
  if (stat.is_dir()) {
6807
0
    std::string mnt = !mount_point.empty() ? mount_point : "/";
6808
0
    if (!mnt.empty() && mnt[0] == '/') {
6809
0
      base_dirs_.push_back({mnt, dir, std::move(headers)});
6810
0
      return true;
6811
0
    }
6812
0
  }
6813
0
  return false;
6814
0
}
6815
6816
0
inline bool Server::remove_mount_point(const std::string &mount_point) {
6817
0
  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
6818
0
    if (it->mount_point == mount_point) {
6819
0
      base_dirs_.erase(it);
6820
0
      return true;
6821
0
    }
6822
0
  }
6823
0
  return false;
6824
0
}
6825
6826
inline Server &
6827
Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
6828
0
                                                const std::string &mime) {
6829
0
  file_extension_and_mimetype_map_[ext] = mime;
6830
0
  return *this;
6831
0
}
6832
6833
0
inline Server &Server::set_default_file_mimetype(const std::string &mime) {
6834
0
  default_file_mimetype_ = mime;
6835
0
  return *this;
6836
0
}
6837
6838
0
inline Server &Server::set_file_request_handler(Handler handler) {
6839
0
  file_request_handler_ = std::move(handler);
6840
0
  return *this;
6841
0
}
6842
6843
inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
6844
0
                                              std::true_type) {
6845
0
  error_handler_ = std::move(handler);
6846
0
  return *this;
6847
0
}
6848
6849
inline Server &Server::set_error_handler_core(Handler handler,
6850
0
                                              std::false_type) {
6851
0
  error_handler_ = [handler](const Request &req, Response &res) {
6852
0
    handler(req, res);
6853
0
    return HandlerResponse::Handled;
6854
0
  };
6855
0
  return *this;
6856
0
}
6857
6858
0
inline Server &Server::set_exception_handler(ExceptionHandler handler) {
6859
0
  exception_handler_ = std::move(handler);
6860
0
  return *this;
6861
0
}
6862
6863
0
inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
6864
0
  pre_routing_handler_ = std::move(handler);
6865
0
  return *this;
6866
0
}
6867
6868
0
inline Server &Server::set_post_routing_handler(Handler handler) {
6869
0
  post_routing_handler_ = std::move(handler);
6870
0
  return *this;
6871
0
}
6872
6873
0
inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
6874
0
  pre_request_handler_ = std::move(handler);
6875
0
  return *this;
6876
0
}
6877
6878
0
inline Server &Server::set_logger(Logger logger) {
6879
0
  logger_ = std::move(logger);
6880
0
  return *this;
6881
0
}
6882
6883
inline Server &
6884
0
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
6885
0
  expect_100_continue_handler_ = std::move(handler);
6886
0
  return *this;
6887
0
}
6888
6889
0
inline Server &Server::set_address_family(int family) {
6890
0
  address_family_ = family;
6891
0
  return *this;
6892
0
}
6893
6894
0
inline Server &Server::set_tcp_nodelay(bool on) {
6895
0
  tcp_nodelay_ = on;
6896
0
  return *this;
6897
0
}
6898
6899
0
inline Server &Server::set_ipv6_v6only(bool on) {
6900
0
  ipv6_v6only_ = on;
6901
0
  return *this;
6902
0
}
6903
6904
0
inline Server &Server::set_socket_options(SocketOptions socket_options) {
6905
0
  socket_options_ = std::move(socket_options);
6906
0
  return *this;
6907
0
}
6908
6909
0
inline Server &Server::set_default_headers(Headers headers) {
6910
0
  default_headers_ = std::move(headers);
6911
0
  return *this;
6912
0
}
6913
6914
inline Server &Server::set_header_writer(
6915
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
6916
0
  header_writer_ = writer;
6917
0
  return *this;
6918
0
}
6919
6920
0
inline Server &Server::set_keep_alive_max_count(size_t count) {
6921
0
  keep_alive_max_count_ = count;
6922
0
  return *this;
6923
0
}
6924
6925
0
inline Server &Server::set_keep_alive_timeout(time_t sec) {
6926
0
  keep_alive_timeout_sec_ = sec;
6927
0
  return *this;
6928
0
}
6929
6930
0
inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
6931
0
  read_timeout_sec_ = sec;
6932
0
  read_timeout_usec_ = usec;
6933
0
  return *this;
6934
0
}
6935
6936
0
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
6937
0
  write_timeout_sec_ = sec;
6938
0
  write_timeout_usec_ = usec;
6939
0
  return *this;
6940
0
}
6941
6942
0
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
6943
0
  idle_interval_sec_ = sec;
6944
0
  idle_interval_usec_ = usec;
6945
0
  return *this;
6946
0
}
6947
6948
0
inline Server &Server::set_payload_max_length(size_t length) {
6949
0
  payload_max_length_ = length;
6950
0
  return *this;
6951
0
}
6952
6953
inline bool Server::bind_to_port(const std::string &host, int port,
6954
0
                                 int socket_flags) {
6955
0
  auto ret = bind_internal(host, port, socket_flags);
6956
0
  if (ret == -1) { is_decommissioned = true; }
6957
0
  return ret >= 0;
6958
0
}
6959
0
inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
6960
0
  auto ret = bind_internal(host, 0, socket_flags);
6961
0
  if (ret == -1) { is_decommissioned = true; }
6962
0
  return ret;
6963
0
}
6964
6965
0
inline bool Server::listen_after_bind() { return listen_internal(); }
6966
6967
inline bool Server::listen(const std::string &host, int port,
6968
0
                           int socket_flags) {
6969
0
  return bind_to_port(host, port, socket_flags) && listen_internal();
6970
0
}
6971
6972
0
inline bool Server::is_running() const { return is_running_; }
6973
6974
0
inline void Server::wait_until_ready() const {
6975
0
  while (!is_running_ && !is_decommissioned) {
6976
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
6977
0
  }
6978
0
}
6979
6980
0
inline void Server::stop() {
6981
0
  if (is_running_) {
6982
0
    assert(svr_sock_ != INVALID_SOCKET);
6983
0
    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
6984
0
    detail::shutdown_socket(sock);
6985
0
    detail::close_socket(sock);
6986
0
  }
6987
0
  is_decommissioned = false;
6988
0
}
6989
6990
0
inline void Server::decommission() { is_decommissioned = true; }
6991
6992
3.90k
inline bool Server::parse_request_line(const char *s, Request &req) const {
6993
3.90k
  auto len = strlen(s);
6994
3.90k
  if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
6995
3.83k
  len -= 2;
6996
6997
3.83k
  {
6998
3.83k
    size_t count = 0;
6999
7000
12.5k
    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
7001
12.5k
      switch (count) {
7002
3.81k
      case 0: req.method = std::string(b, e); break;
7003
3.74k
      case 1: req.target = std::string(b, e); break;
7004
3.72k
      case 2: req.version = std::string(b, e); break;
7005
1.27k
      default: break;
7006
12.5k
      }
7007
12.5k
      count++;
7008
12.5k
    });
7009
7010
3.83k
    if (count != 3) { return false; }
7011
3.83k
  }
7012
7013
3.70k
  thread_local const std::set<std::string> methods{
7014
3.70k
      "GET",     "HEAD",    "POST",  "PUT",   "DELETE",
7015
3.70k
      "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
7016
7017
3.70k
  if (methods.find(req.method) == methods.end()) { return false; }
7018
7019
3.65k
  if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
7020
7021
3.61k
  {
7022
    // Skip URL fragment
7023
598k
    for (size_t i = 0; i < req.target.size(); i++) {
7024
595k
      if (req.target[i] == '#') {
7025
18
        req.target.erase(i);
7026
18
        break;
7027
18
      }
7028
595k
    }
7029
7030
3.61k
    detail::divide(req.target, '?',
7031
3.61k
                   [&](const char *lhs_data, std::size_t lhs_size,
7032
3.61k
                       const char *rhs_data, std::size_t rhs_size) {
7033
3.61k
                     req.path = detail::decode_url(
7034
3.61k
                         std::string(lhs_data, lhs_size), false);
7035
3.61k
                     detail::parse_query_text(rhs_data, rhs_size, req.params);
7036
3.61k
                   });
7037
3.61k
  }
7038
7039
3.61k
  return true;
7040
3.65k
}
7041
7042
inline bool Server::write_response(Stream &strm, bool close_connection,
7043
2.80k
                                   Request &req, Response &res) {
7044
  // NOTE: `req.ranges` should be empty, otherwise it will be applied
7045
  // incorrectly to the error content.
7046
2.80k
  req.ranges.clear();
7047
2.80k
  return write_response_core(strm, close_connection, req, res, false);
7048
2.80k
}
7049
7050
inline bool Server::write_response_with_content(Stream &strm,
7051
                                                bool close_connection,
7052
                                                const Request &req,
7053
1.10k
                                                Response &res) {
7054
1.10k
  return write_response_core(strm, close_connection, req, res, true);
7055
1.10k
}
7056
7057
inline bool Server::write_response_core(Stream &strm, bool close_connection,
7058
                                        const Request &req, Response &res,
7059
3.90k
                                        bool need_apply_ranges) {
7060
3.90k
  assert(res.status != -1);
7061
7062
3.90k
  if (400 <= res.status && error_handler_ &&
7063
3.90k
      error_handler_(req, res) == HandlerResponse::Handled) {
7064
0
    need_apply_ranges = true;
7065
0
  }
7066
7067
3.90k
  std::string content_type;
7068
3.90k
  std::string boundary;
7069
3.90k
  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
7070
7071
  // Prepare additional headers
7072
3.90k
  if (close_connection || req.get_header_value("Connection") == "close") {
7073
4
    res.set_header("Connection", "close");
7074
3.90k
  } else {
7075
3.90k
    std::string s = "timeout=";
7076
3.90k
    s += std::to_string(keep_alive_timeout_sec_);
7077
3.90k
    s += ", max=";
7078
3.90k
    s += std::to_string(keep_alive_max_count_);
7079
3.90k
    res.set_header("Keep-Alive", s);
7080
3.90k
  }
7081
7082
3.90k
  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
7083
3.90k
      !res.has_header("Content-Type")) {
7084
0
    res.set_header("Content-Type", "text/plain");
7085
0
  }
7086
7087
3.90k
  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
7088
3.90k
      !res.has_header("Content-Length")) {
7089
2.80k
    res.set_header("Content-Length", "0");
7090
2.80k
  }
7091
7092
3.90k
  if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
7093
79
    res.set_header("Accept-Ranges", "bytes");
7094
79
  }
7095
7096
3.90k
  if (post_routing_handler_) { post_routing_handler_(req, res); }
7097
7098
  // Response line and headers
7099
3.90k
  {
7100
3.90k
    detail::BufferStream bstrm;
7101
3.90k
    if (!detail::write_response_line(bstrm, res.status)) { return false; }
7102
3.90k
    if (!header_writer_(bstrm, res.headers)) { return false; }
7103
7104
    // Flush buffer
7105
3.90k
    auto &data = bstrm.get_buffer();
7106
3.90k
    detail::write_data(strm, data.data(), data.size());
7107
3.90k
  }
7108
7109
  // Body
7110
0
  auto ret = true;
7111
3.90k
  if (req.method != "HEAD") {
7112
3.82k
    if (!res.body.empty()) {
7113
1.06k
      if (!detail::write_data(strm, res.body.data(), res.body.size())) {
7114
0
        ret = false;
7115
0
      }
7116
2.76k
    } else if (res.content_provider_) {
7117
0
      if (write_content_with_provider(strm, req, res, boundary, content_type)) {
7118
0
        res.content_provider_success_ = true;
7119
0
      } else {
7120
0
        ret = false;
7121
0
      }
7122
0
    }
7123
3.82k
  }
7124
7125
  // Log
7126
3.90k
  if (logger_) { logger_(req, res); }
7127
7128
3.90k
  return ret;
7129
3.90k
}
7130
7131
inline bool
7132
Server::write_content_with_provider(Stream &strm, const Request &req,
7133
                                    Response &res, const std::string &boundary,
7134
0
                                    const std::string &content_type) {
7135
0
  auto is_shutting_down = [this]() {
7136
0
    return this->svr_sock_ == INVALID_SOCKET;
7137
0
  };
7138
7139
0
  if (res.content_length_ > 0) {
7140
0
    if (req.ranges.empty()) {
7141
0
      return detail::write_content(strm, res.content_provider_, 0,
7142
0
                                   res.content_length_, is_shutting_down);
7143
0
    } else if (req.ranges.size() == 1) {
7144
0
      auto offset_and_length = detail::get_range_offset_and_length(
7145
0
          req.ranges[0], res.content_length_);
7146
7147
0
      return detail::write_content(strm, res.content_provider_,
7148
0
                                   offset_and_length.first,
7149
0
                                   offset_and_length.second, is_shutting_down);
7150
0
    } else {
7151
0
      return detail::write_multipart_ranges_data(
7152
0
          strm, req, res, boundary, content_type, res.content_length_,
7153
0
          is_shutting_down);
7154
0
    }
7155
0
  } else {
7156
0
    if (res.is_chunked_content_provider_) {
7157
0
      auto type = detail::encoding_type(req, res);
7158
7159
0
      std::unique_ptr<detail::compressor> compressor;
7160
0
      if (type == detail::EncodingType::Gzip) {
7161
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7162
0
        compressor = detail::make_unique<detail::gzip_compressor>();
7163
0
#endif
7164
0
      } else if (type == detail::EncodingType::Brotli) {
7165
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7166
        compressor = detail::make_unique<detail::brotli_compressor>();
7167
#endif
7168
0
      } else if (type == detail::EncodingType::Zstd) {
7169
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
7170
        compressor = detail::make_unique<detail::zstd_compressor>();
7171
#endif
7172
0
      } else {
7173
0
        compressor = detail::make_unique<detail::nocompressor>();
7174
0
      }
7175
0
      assert(compressor != nullptr);
7176
7177
0
      return detail::write_content_chunked(strm, res.content_provider_,
7178
0
                                           is_shutting_down, *compressor);
7179
0
    } else {
7180
0
      return detail::write_content_without_length(strm, res.content_provider_,
7181
0
                                                  is_shutting_down);
7182
0
    }
7183
0
  }
7184
0
}
7185
7186
2.30k
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
7187
2.30k
  MultipartFormDataMap::iterator cur;
7188
2.30k
  auto file_count = 0;
7189
2.30k
  if (read_content_core(
7190
2.30k
          strm, req, res,
7191
          // Regular
7192
45.0k
          [&](const char *buf, size_t n) {
7193
45.0k
            if (req.body.size() + n > req.body.max_size()) { return false; }
7194
45.0k
            req.body.append(buf, n);
7195
45.0k
            return true;
7196
45.0k
          },
7197
          // Multipart
7198
14.6k
          [&](const MultipartFormData &file) {
7199
14.6k
            if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
7200
1
              return false;
7201
1
            }
7202
14.6k
            cur = req.files.emplace(file.name, file);
7203
14.6k
            return true;
7204
14.6k
          },
7205
25.3k
          [&](const char *buf, size_t n) {
7206
25.3k
            auto &content = cur->second.content;
7207
25.3k
            if (content.size() + n > content.max_size()) { return false; }
7208
25.3k
            content.append(buf, n);
7209
25.3k
            return true;
7210
25.3k
          })) {
7211
1.17k
    const auto &content_type = req.get_header_value("Content-Type");
7212
1.17k
    if (!content_type.find("application/x-www-form-urlencoded")) {
7213
145
      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
7214
28
        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
7215
28
        return false;
7216
28
      }
7217
117
      detail::parse_query_text(req.body, req.params);
7218
117
    }
7219
1.14k
    return true;
7220
1.17k
  }
7221
1.13k
  return false;
7222
2.30k
}
7223
7224
inline bool Server::read_content_with_content_receiver(
7225
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
7226
    MultipartContentHeader multipart_header,
7227
0
    ContentReceiver multipart_receiver) {
7228
0
  return read_content_core(strm, req, res, std::move(receiver),
7229
0
                           std::move(multipart_header),
7230
0
                           std::move(multipart_receiver));
7231
0
}
7232
7233
inline bool
7234
Server::read_content_core(Stream &strm, Request &req, Response &res,
7235
                          ContentReceiver receiver,
7236
                          MultipartContentHeader multipart_header,
7237
2.30k
                          ContentReceiver multipart_receiver) const {
7238
2.30k
  detail::MultipartFormDataParser multipart_form_data_parser;
7239
2.30k
  ContentReceiverWithProgress out;
7240
7241
2.30k
  if (req.is_multipart_form_data()) {
7242
781
    const auto &content_type = req.get_header_value("Content-Type");
7243
781
    std::string boundary;
7244
781
    if (!detail::parse_multipart_boundary(content_type, boundary)) {
7245
10
      res.status = StatusCode::BadRequest_400;
7246
10
      return false;
7247
10
    }
7248
7249
771
    multipart_form_data_parser.set_boundary(std::move(boundary));
7250
24.5k
    out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
7251
      /* For debug
7252
      size_t pos = 0;
7253
      while (pos < n) {
7254
        auto read_size = (std::min)<size_t>(1, n - pos);
7255
        auto ret = multipart_form_data_parser.parse(
7256
            buf + pos, read_size, multipart_receiver, multipart_header);
7257
        if (!ret) { return false; }
7258
        pos += read_size;
7259
      }
7260
      return true;
7261
      */
7262
24.5k
      return multipart_form_data_parser.parse(buf, n, multipart_receiver,
7263
24.5k
                                              multipart_header);
7264
24.5k
    };
7265
1.52k
  } else {
7266
1.52k
    out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
7267
45.0k
                     uint64_t /*len*/) { return receiver(buf, n); };
7268
1.52k
  }
7269
7270
2.29k
  if (req.method == "DELETE" && !req.has_header("Content-Length")) {
7271
7
    return true;
7272
7
  }
7273
7274
2.28k
  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
7275
2.28k
                            out, true)) {
7276
633
    return false;
7277
633
  }
7278
7279
1.65k
  if (req.is_multipart_form_data()) {
7280
515
    if (!multipart_form_data_parser.is_valid()) {
7281
487
      res.status = StatusCode::BadRequest_400;
7282
487
      return false;
7283
487
    }
7284
515
  }
7285
7286
1.16k
  return true;
7287
1.65k
}
7288
7289
307
inline bool Server::handle_file_request(const Request &req, Response &res) {
7290
307
  for (const auto &entry : base_dirs_) {
7291
    // Prefix match
7292
0
    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
7293
0
      std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
7294
0
      if (detail::is_valid_path(sub_path)) {
7295
0
        auto path = entry.base_dir + sub_path;
7296
0
        if (path.back() == '/') { path += "index.html"; }
7297
7298
0
        detail::FileStat stat(path);
7299
7300
0
        if (stat.is_dir()) {
7301
0
          res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
7302
0
          return true;
7303
0
        }
7304
7305
0
        if (stat.is_file()) {
7306
0
          for (const auto &kv : entry.headers) {
7307
0
            res.set_header(kv.first, kv.second);
7308
0
          }
7309
7310
0
          auto mm = std::make_shared<detail::mmap>(path.c_str());
7311
0
          if (!mm->is_open()) { return false; }
7312
7313
0
          res.set_content_provider(
7314
0
              mm->size(),
7315
0
              detail::find_content_type(path, file_extension_and_mimetype_map_,
7316
0
                                        default_file_mimetype_),
7317
0
              [mm](size_t offset, size_t length, DataSink &sink) -> bool {
7318
0
                sink.write(mm->data() + offset, length);
7319
0
                return true;
7320
0
              });
7321
7322
0
          if (req.method != "HEAD" && file_request_handler_) {
7323
0
            file_request_handler_(req, res);
7324
0
          }
7325
7326
0
          return true;
7327
0
        }
7328
0
      }
7329
0
    }
7330
0
  }
7331
307
  return false;
7332
307
}
7333
7334
inline socket_t
7335
Server::create_server_socket(const std::string &host, int port,
7336
                             int socket_flags,
7337
0
                             SocketOptions socket_options) const {
7338
0
  return detail::create_socket(
7339
0
      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
7340
0
      ipv6_v6only_, std::move(socket_options),
7341
0
      [](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
7342
0
        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
7343
0
          return false;
7344
0
        }
7345
0
        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
7346
0
        return true;
7347
0
      });
7348
0
}
7349
7350
inline int Server::bind_internal(const std::string &host, int port,
7351
0
                                 int socket_flags) {
7352
0
  if (is_decommissioned) { return -1; }
7353
0
7354
0
  if (!is_valid()) { return -1; }
7355
0
7356
0
  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
7357
0
  if (svr_sock_ == INVALID_SOCKET) { return -1; }
7358
0
7359
0
  if (port == 0) {
7360
0
    struct sockaddr_storage addr;
7361
0
    socklen_t addr_len = sizeof(addr);
7362
0
    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
7363
0
                    &addr_len) == -1) {
7364
0
      return -1;
7365
0
    }
7366
0
    if (addr.ss_family == AF_INET) {
7367
0
      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
7368
0
    } else if (addr.ss_family == AF_INET6) {
7369
0
      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
7370
0
    } else {
7371
0
      return -1;
7372
0
    }
7373
0
  } else {
7374
0
    return port;
7375
0
  }
7376
0
}
7377
7378
0
inline bool Server::listen_internal() {
7379
0
  if (is_decommissioned) { return false; }
7380
0
7381
0
  auto ret = true;
7382
0
  is_running_ = true;
7383
0
  auto se = detail::scope_exit([&]() { is_running_ = false; });
7384
0
7385
0
  {
7386
0
    std::unique_ptr<TaskQueue> task_queue(new_task_queue());
7387
0
7388
0
    while (svr_sock_ != INVALID_SOCKET) {
7389
0
#ifndef _WIN32
7390
0
      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
7391
0
#endif
7392
0
        auto val = detail::select_read(svr_sock_, idle_interval_sec_,
7393
0
                                       idle_interval_usec_);
7394
0
        if (val == 0) { // Timeout
7395
0
          task_queue->on_idle();
7396
0
          continue;
7397
0
        }
7398
0
#ifndef _WIN32
7399
0
      }
7400
0
#endif
7401
0
7402
0
#if defined _WIN32
7403
0
      // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
7404
0
      // OVERLAPPED
7405
0
      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
7406
0
#elif defined SOCK_CLOEXEC
7407
0
      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
7408
0
#else
7409
0
      socket_t sock = accept(svr_sock_, nullptr, nullptr);
7410
0
#endif
7411
0
7412
0
      if (sock == INVALID_SOCKET) {
7413
0
        if (errno == EMFILE) {
7414
0
          // The per-process limit of open file descriptors has been reached.
7415
0
          // Try to accept new connections after a short sleep.
7416
0
          std::this_thread::sleep_for(std::chrono::microseconds{1});
7417
0
          continue;
7418
0
        } else if (errno == EINTR || errno == EAGAIN) {
7419
0
          continue;
7420
0
        }
7421
0
        if (svr_sock_ != INVALID_SOCKET) {
7422
0
          detail::close_socket(svr_sock_);
7423
0
          ret = false;
7424
0
        } else {
7425
0
          ; // The server socket was closed by user.
7426
0
        }
7427
0
        break;
7428
0
      }
7429
0
7430
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
7431
0
                                  read_timeout_sec_, read_timeout_usec_);
7432
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
7433
0
                                  write_timeout_sec_, write_timeout_usec_);
7434
0
7435
0
      if (!task_queue->enqueue(
7436
0
              [this, sock]() { process_and_close_socket(sock); })) {
7437
0
        detail::shutdown_socket(sock);
7438
0
        detail::close_socket(sock);
7439
0
      }
7440
0
    }
7441
0
7442
0
    task_queue->shutdown();
7443
0
  }
7444
0
7445
0
  is_decommissioned = !ret;
7446
0
  return ret;
7447
0
}
7448
7449
2.56k
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
7450
2.56k
  if (pre_routing_handler_ &&
7451
2.56k
      pre_routing_handler_(req, res) == HandlerResponse::Handled) {
7452
0
    return true;
7453
0
  }
7454
7455
  // File handler
7456
2.56k
  if ((req.method == "GET" || req.method == "HEAD") &&
7457
2.56k
      handle_file_request(req, res)) {
7458
0
    return true;
7459
0
  }
7460
7461
2.56k
  if (detail::expect_content(req)) {
7462
    // Content reader handler
7463
2.30k
    {
7464
2.30k
      ContentReader reader(
7465
2.30k
          [&](ContentReceiver receiver) {
7466
0
            return read_content_with_content_receiver(
7467
0
                strm, req, res, std::move(receiver), nullptr, nullptr);
7468
0
          },
7469
2.30k
          [&](MultipartContentHeader header, ContentReceiver receiver) {
7470
0
            return read_content_with_content_receiver(strm, req, res, nullptr,
7471
0
                                                      std::move(header),
7472
0
                                                      std::move(receiver));
7473
0
          });
7474
7475
2.30k
      if (req.method == "POST") {
7476
247
        if (dispatch_request_for_content_reader(
7477
247
                req, res, std::move(reader),
7478
247
                post_handlers_for_content_reader_)) {
7479
0
          return true;
7480
0
        }
7481
2.05k
      } else if (req.method == "PUT") {
7482
1.85k
        if (dispatch_request_for_content_reader(
7483
1.85k
                req, res, std::move(reader),
7484
1.85k
                put_handlers_for_content_reader_)) {
7485
0
          return true;
7486
0
        }
7487
1.85k
      } else if (req.method == "PATCH") {
7488
3
        if (dispatch_request_for_content_reader(
7489
3
                req, res, std::move(reader),
7490
3
                patch_handlers_for_content_reader_)) {
7491
0
          return true;
7492
0
        }
7493
194
      } else if (req.method == "DELETE") {
7494
14
        if (dispatch_request_for_content_reader(
7495
14
                req, res, std::move(reader),
7496
14
                delete_handlers_for_content_reader_)) {
7497
0
          return true;
7498
0
        }
7499
14
      }
7500
2.30k
    }
7501
7502
    // Read content into `req.body`
7503
2.30k
    if (!read_content(strm, req, res)) { return false; }
7504
2.30k
  }
7505
7506
  // Regular handler
7507
1.40k
  if (req.method == "GET" || req.method == "HEAD") {
7508
245
    return dispatch_request(req, res, get_handlers_);
7509
1.16k
  } else if (req.method == "POST") {
7510
187
    return dispatch_request(req, res, post_handlers_);
7511
975
  } else if (req.method == "PUT") {
7512
932
    return dispatch_request(req, res, put_handlers_);
7513
932
  } else if (req.method == "DELETE") {
7514
7
    return dispatch_request(req, res, delete_handlers_);
7515
36
  } else if (req.method == "OPTIONS") {
7516
1
    return dispatch_request(req, res, options_handlers_);
7517
35
  } else if (req.method == "PATCH") {
7518
3
    return dispatch_request(req, res, patch_handlers_);
7519
3
  }
7520
7521
32
  res.status = StatusCode::BadRequest_400;
7522
32
  return false;
7523
1.40k
}
7524
7525
inline bool Server::dispatch_request(Request &req, Response &res,
7526
1.37k
                                     const Handlers &handlers) const {
7527
1.37k
  for (const auto &x : handlers) {
7528
1.37k
    const auto &matcher = x.first;
7529
1.37k
    const auto &handler = x.second;
7530
7531
1.37k
    if (matcher->match(req)) {
7532
1.31k
      req.matched_route = matcher->pattern();
7533
1.31k
      if (!pre_request_handler_ ||
7534
1.31k
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
7535
1.31k
        handler(req, res);
7536
1.31k
      }
7537
1.31k
      return true;
7538
1.31k
    }
7539
1.37k
  }
7540
65
  return false;
7541
1.37k
}
7542
7543
inline void Server::apply_ranges(const Request &req, Response &res,
7544
                                 std::string &content_type,
7545
1.10k
                                 std::string &boundary) const {
7546
1.10k
  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
7547
145
    auto it = res.headers.find("Content-Type");
7548
145
    if (it != res.headers.end()) {
7549
145
      content_type = it->second;
7550
145
      res.headers.erase(it);
7551
145
    }
7552
7553
145
    boundary = detail::make_multipart_data_boundary();
7554
7555
145
    res.set_header("Content-Type",
7556
145
                   "multipart/byteranges; boundary=" + boundary);
7557
145
  }
7558
7559
1.10k
  auto type = detail::encoding_type(req, res);
7560
7561
1.10k
  if (res.body.empty()) {
7562
0
    if (res.content_length_ > 0) {
7563
0
      size_t length = 0;
7564
0
      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
7565
0
        length = res.content_length_;
7566
0
      } else if (req.ranges.size() == 1) {
7567
0
        auto offset_and_length = detail::get_range_offset_and_length(
7568
0
            req.ranges[0], res.content_length_);
7569
7570
0
        length = offset_and_length.second;
7571
7572
0
        auto content_range = detail::make_content_range_header_field(
7573
0
            offset_and_length, res.content_length_);
7574
0
        res.set_header("Content-Range", content_range);
7575
0
      } else {
7576
0
        length = detail::get_multipart_ranges_data_length(
7577
0
            req, boundary, content_type, res.content_length_);
7578
0
      }
7579
0
      res.set_header("Content-Length", std::to_string(length));
7580
0
    } else {
7581
0
      if (res.content_provider_) {
7582
0
        if (res.is_chunked_content_provider_) {
7583
0
          res.set_header("Transfer-Encoding", "chunked");
7584
0
          if (type == detail::EncodingType::Gzip) {
7585
0
            res.set_header("Content-Encoding", "gzip");
7586
0
          } else if (type == detail::EncodingType::Brotli) {
7587
0
            res.set_header("Content-Encoding", "br");
7588
0
          } else if (type == detail::EncodingType::Zstd) {
7589
0
            res.set_header("Content-Encoding", "zstd");
7590
0
          }
7591
0
        }
7592
0
      }
7593
0
    }
7594
1.10k
  } else {
7595
1.10k
    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
7596
843
      ;
7597
843
    } else if (req.ranges.size() == 1) {
7598
112
      auto offset_and_length =
7599
112
          detail::get_range_offset_and_length(req.ranges[0], res.body.size());
7600
112
      auto offset = offset_and_length.first;
7601
112
      auto length = offset_and_length.second;
7602
7603
112
      auto content_range = detail::make_content_range_header_field(
7604
112
          offset_and_length, res.body.size());
7605
112
      res.set_header("Content-Range", content_range);
7606
7607
112
      assert(offset + length <= res.body.size());
7608
112
      res.body = res.body.substr(offset, length);
7609
145
    } else {
7610
145
      std::string data;
7611
145
      detail::make_multipart_ranges_data(req, res, boundary, content_type,
7612
145
                                         res.body.size(), data);
7613
145
      res.body.swap(data);
7614
145
    }
7615
7616
1.10k
    if (type != detail::EncodingType::None) {
7617
10
      std::unique_ptr<detail::compressor> compressor;
7618
10
      std::string content_encoding;
7619
7620
10
      if (type == detail::EncodingType::Gzip) {
7621
10
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7622
10
        compressor = detail::make_unique<detail::gzip_compressor>();
7623
10
        content_encoding = "gzip";
7624
10
#endif
7625
10
      } else if (type == detail::EncodingType::Brotli) {
7626
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7627
        compressor = detail::make_unique<detail::brotli_compressor>();
7628
        content_encoding = "br";
7629
#endif
7630
0
      } else if (type == detail::EncodingType::Zstd) {
7631
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
7632
        compressor = detail::make_unique<detail::zstd_compressor>();
7633
        content_encoding = "zstd";
7634
#endif
7635
0
      }
7636
7637
10
      if (compressor) {
7638
10
        std::string compressed;
7639
10
        if (compressor->compress(res.body.data(), res.body.size(), true,
7640
10
                                 [&](const char *data, size_t data_len) {
7641
10
                                   compressed.append(data, data_len);
7642
10
                                   return true;
7643
10
                                 })) {
7644
10
          res.body.swap(compressed);
7645
10
          res.set_header("Content-Encoding", content_encoding);
7646
10
        }
7647
10
      }
7648
10
    }
7649
7650
1.10k
    auto length = std::to_string(res.body.size());
7651
1.10k
    res.set_header("Content-Length", length);
7652
1.10k
  }
7653
1.10k
}
7654
7655
inline bool Server::dispatch_request_for_content_reader(
7656
    Request &req, Response &res, ContentReader content_reader,
7657
2.12k
    const HandlersForContentReader &handlers) const {
7658
2.12k
  for (const auto &x : handlers) {
7659
0
    const auto &matcher = x.first;
7660
0
    const auto &handler = x.second;
7661
7662
0
    if (matcher->match(req)) {
7663
0
      req.matched_route = matcher->pattern();
7664
0
      if (!pre_request_handler_ ||
7665
0
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
7666
0
        handler(req, res, content_reader);
7667
0
      }
7668
0
      return true;
7669
0
    }
7670
0
  }
7671
2.12k
  return false;
7672
2.12k
}
7673
7674
inline bool
7675
Server::process_request(Stream &strm, const std::string &remote_addr,
7676
                        int remote_port, const std::string &local_addr,
7677
                        int local_port, bool close_connection,
7678
                        bool &connection_closed,
7679
3.90k
                        const std::function<void(Request &)> &setup_request) {
7680
3.90k
  std::array<char, 2048> buf{};
7681
7682
3.90k
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7683
7684
  // Connection has been closed on client
7685
3.90k
  if (!line_reader.getline()) { return false; }
7686
7687
3.90k
  Request req;
7688
7689
3.90k
  Response res;
7690
3.90k
  res.version = "HTTP/1.1";
7691
3.90k
  res.headers = default_headers_;
7692
7693
  // Request line and headers
7694
3.90k
  if (!parse_request_line(line_reader.ptr(), req) ||
7695
3.90k
      !detail::read_headers(strm, req.headers)) {
7696
1.07k
    res.status = StatusCode::BadRequest_400;
7697
1.07k
    return write_response(strm, close_connection, req, res);
7698
1.07k
  }
7699
7700
  // Check if the request URI doesn't exceed the limit
7701
2.83k
  if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
7702
10
    Headers dummy;
7703
10
    detail::read_headers(strm, dummy);
7704
10
    res.status = StatusCode::UriTooLong_414;
7705
10
    return write_response(strm, close_connection, req, res);
7706
10
  }
7707
7708
2.82k
  if (req.get_header_value("Connection") == "close") {
7709
2
    connection_closed = true;
7710
2
  }
7711
7712
2.82k
  if (req.version == "HTTP/1.0" &&
7713
2.82k
      req.get_header_value("Connection") != "Keep-Alive") {
7714
2.16k
    connection_closed = true;
7715
2.16k
  }
7716
7717
2.82k
  req.remote_addr = remote_addr;
7718
2.82k
  req.remote_port = remote_port;
7719
2.82k
  req.set_header("REMOTE_ADDR", req.remote_addr);
7720
2.82k
  req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
7721
7722
2.82k
  req.local_addr = local_addr;
7723
2.82k
  req.local_port = local_port;
7724
2.82k
  req.set_header("LOCAL_ADDR", req.local_addr);
7725
2.82k
  req.set_header("LOCAL_PORT", std::to_string(req.local_port));
7726
7727
2.82k
  if (req.has_header("Accept")) {
7728
459
    const auto &accept_header = req.get_header_value("Accept");
7729
459
    if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
7730
157
      res.status = StatusCode::BadRequest_400;
7731
157
      return write_response(strm, close_connection, req, res);
7732
157
    }
7733
459
  }
7734
7735
2.66k
  if (req.has_header("Range")) {
7736
592
    const auto &range_header_value = req.get_header_value("Range");
7737
592
    if (!detail::parse_range_header(range_header_value, req.ranges)) {
7738
99
      res.status = StatusCode::RangeNotSatisfiable_416;
7739
99
      return write_response(strm, close_connection, req, res);
7740
99
    }
7741
592
  }
7742
7743
2.56k
  if (setup_request) { setup_request(req); }
7744
7745
2.56k
  if (req.get_header_value("Expect") == "100-continue") {
7746
2
    int status = StatusCode::Continue_100;
7747
2
    if (expect_100_continue_handler_) {
7748
0
      status = expect_100_continue_handler_(req, res);
7749
0
    }
7750
2
    switch (status) {
7751
2
    case StatusCode::Continue_100:
7752
2
    case StatusCode::ExpectationFailed_417:
7753
2
      detail::write_response_line(strm, status);
7754
2
      strm.write("\r\n");
7755
2
      break;
7756
0
    default:
7757
0
      connection_closed = true;
7758
0
      return write_response(strm, true, req, res);
7759
2
    }
7760
2
  }
7761
7762
  // Setup `is_connection_closed` method
7763
2.56k
  auto sock = strm.socket();
7764
2.56k
  req.is_connection_closed = [sock]() {
7765
0
    return !detail::is_socket_alive(sock);
7766
0
  };
7767
7768
  // Routing
7769
2.56k
  auto routed = false;
7770
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7771
  routed = routing(req, res, strm);
7772
#else
7773
2.56k
  try {
7774
2.56k
    routed = routing(req, res, strm);
7775
2.56k
  } catch (std::exception &e) {
7776
0
    if (exception_handler_) {
7777
0
      auto ep = std::current_exception();
7778
0
      exception_handler_(req, res, ep);
7779
0
      routed = true;
7780
0
    } else {
7781
0
      res.status = StatusCode::InternalServerError_500;
7782
0
      std::string val;
7783
0
      auto s = e.what();
7784
0
      for (size_t i = 0; s[i]; i++) {
7785
0
        switch (s[i]) {
7786
0
        case '\r': val += "\\r"; break;
7787
0
        case '\n': val += "\\n"; break;
7788
0
        default: val += s[i]; break;
7789
0
        }
7790
0
      }
7791
0
      res.set_header("EXCEPTION_WHAT", val);
7792
0
    }
7793
0
  } catch (...) {
7794
0
    if (exception_handler_) {
7795
0
      auto ep = std::current_exception();
7796
0
      exception_handler_(req, res, ep);
7797
0
      routed = true;
7798
0
    } else {
7799
0
      res.status = StatusCode::InternalServerError_500;
7800
0
      res.set_header("EXCEPTION_WHAT", "UNKNOWN");
7801
0
    }
7802
0
  }
7803
0
#endif
7804
2.56k
  if (routed) {
7805
1.31k
    if (res.status == -1) {
7806
1.31k
      res.status = req.ranges.empty() ? StatusCode::OK_200
7807
1.31k
                                      : StatusCode::PartialContent_206;
7808
1.31k
    }
7809
7810
    // Serve file content by using a content provider
7811
1.31k
    if (!res.file_content_path_.empty()) {
7812
0
      const auto &path = res.file_content_path_;
7813
0
      auto mm = std::make_shared<detail::mmap>(path.c_str());
7814
0
      if (!mm->is_open()) {
7815
0
        res.body.clear();
7816
0
        res.content_length_ = 0;
7817
0
        res.content_provider_ = nullptr;
7818
0
        res.status = StatusCode::NotFound_404;
7819
0
        return write_response(strm, close_connection, req, res);
7820
0
      }
7821
7822
0
      auto content_type = res.file_content_content_type_;
7823
0
      if (content_type.empty()) {
7824
0
        content_type = detail::find_content_type(
7825
0
            path, file_extension_and_mimetype_map_, default_file_mimetype_);
7826
0
      }
7827
7828
0
      res.set_content_provider(
7829
0
          mm->size(), content_type,
7830
0
          [mm](size_t offset, size_t length, DataSink &sink) -> bool {
7831
0
            sink.write(mm->data() + offset, length);
7832
0
            return true;
7833
0
          });
7834
0
    }
7835
7836
1.31k
    if (detail::range_error(req, res)) {
7837
210
      res.body.clear();
7838
210
      res.content_length_ = 0;
7839
210
      res.content_provider_ = nullptr;
7840
210
      res.status = StatusCode::RangeNotSatisfiable_416;
7841
210
      return write_response(strm, close_connection, req, res);
7842
210
    }
7843
7844
1.10k
    return write_response_with_content(strm, close_connection, req, res);
7845
1.31k
  } else {
7846
1.25k
    if (res.status == -1) { res.status = StatusCode::NotFound_404; }
7847
7848
1.25k
    return write_response(strm, close_connection, req, res);
7849
1.25k
  }
7850
2.56k
}
7851
7852
0
inline bool Server::is_valid() const { return true; }
7853
7854
0
inline bool Server::process_and_close_socket(socket_t sock) {
7855
0
  std::string remote_addr;
7856
0
  int remote_port = 0;
7857
0
  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
7858
7859
0
  std::string local_addr;
7860
0
  int local_port = 0;
7861
0
  detail::get_local_ip_and_port(sock, local_addr, local_port);
7862
7863
0
  auto ret = detail::process_server_socket(
7864
0
      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
7865
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7866
0
      write_timeout_usec_,
7867
0
      [&](Stream &strm, bool close_connection, bool &connection_closed) {
7868
0
        return process_request(strm, remote_addr, remote_port, local_addr,
7869
0
                               local_port, close_connection, connection_closed,
7870
0
                               nullptr);
7871
0
      });
7872
7873
0
  detail::shutdown_socket(sock);
7874
0
  detail::close_socket(sock);
7875
0
  return ret;
7876
0
}
7877
7878
// HTTP client implementation
7879
inline ClientImpl::ClientImpl(const std::string &host)
7880
    : ClientImpl(host, 80, std::string(), std::string()) {}
7881
7882
inline ClientImpl::ClientImpl(const std::string &host, int port)
7883
    : ClientImpl(host, port, std::string(), std::string()) {}
7884
7885
inline ClientImpl::ClientImpl(const std::string &host, int port,
7886
                              const std::string &client_cert_path,
7887
                              const std::string &client_key_path)
7888
    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
7889
      host_and_port_(adjust_host_string(host_) + ":" + std::to_string(port)),
7890
      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
7891
7892
0
inline ClientImpl::~ClientImpl() {
7893
  // Wait until all the requests in flight are handled.
7894
0
  size_t retry_count = 10;
7895
0
  while (retry_count-- > 0) {
7896
0
    {
7897
0
      std::lock_guard<std::mutex> guard(socket_mutex_);
7898
0
      if (socket_requests_in_flight_ == 0) { break; }
7899
0
    }
7900
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
7901
0
  }
7902
7903
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
7904
0
  shutdown_socket(socket_);
7905
0
  close_socket(socket_);
7906
0
}
7907
7908
0
inline bool ClientImpl::is_valid() const { return true; }
7909
7910
0
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
7911
0
  client_cert_path_ = rhs.client_cert_path_;
7912
0
  client_key_path_ = rhs.client_key_path_;
7913
0
  connection_timeout_sec_ = rhs.connection_timeout_sec_;
7914
0
  read_timeout_sec_ = rhs.read_timeout_sec_;
7915
0
  read_timeout_usec_ = rhs.read_timeout_usec_;
7916
0
  write_timeout_sec_ = rhs.write_timeout_sec_;
7917
0
  write_timeout_usec_ = rhs.write_timeout_usec_;
7918
0
  max_timeout_msec_ = rhs.max_timeout_msec_;
7919
0
  basic_auth_username_ = rhs.basic_auth_username_;
7920
0
  basic_auth_password_ = rhs.basic_auth_password_;
7921
0
  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
7922
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7923
0
  digest_auth_username_ = rhs.digest_auth_username_;
7924
0
  digest_auth_password_ = rhs.digest_auth_password_;
7925
0
#endif
7926
0
  keep_alive_ = rhs.keep_alive_;
7927
0
  follow_location_ = rhs.follow_location_;
7928
0
  url_encode_ = rhs.url_encode_;
7929
0
  address_family_ = rhs.address_family_;
7930
0
  tcp_nodelay_ = rhs.tcp_nodelay_;
7931
0
  ipv6_v6only_ = rhs.ipv6_v6only_;
7932
0
  socket_options_ = rhs.socket_options_;
7933
0
  compress_ = rhs.compress_;
7934
0
  decompress_ = rhs.decompress_;
7935
0
  interface_ = rhs.interface_;
7936
0
  proxy_host_ = rhs.proxy_host_;
7937
0
  proxy_port_ = rhs.proxy_port_;
7938
0
  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
7939
0
  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
7940
0
  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
7941
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7942
0
  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7943
0
  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7944
0
#endif
7945
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7946
0
  ca_cert_file_path_ = rhs.ca_cert_file_path_;
7947
0
  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7948
0
  ca_cert_store_ = rhs.ca_cert_store_;
7949
0
#endif
7950
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7951
0
  server_certificate_verification_ = rhs.server_certificate_verification_;
7952
0
  server_hostname_verification_ = rhs.server_hostname_verification_;
7953
0
  server_certificate_verifier_ = rhs.server_certificate_verifier_;
7954
0
#endif
7955
0
  logger_ = rhs.logger_;
7956
0
}
7957
7958
0
inline socket_t ClientImpl::create_client_socket(Error &error) const {
7959
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
7960
0
    return detail::create_client_socket(
7961
0
        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
7962
0
        ipv6_v6only_, socket_options_, connection_timeout_sec_,
7963
0
        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
7964
0
        write_timeout_sec_, write_timeout_usec_, interface_, error);
7965
0
  }
7966
7967
  // Check is custom IP specified for host_
7968
0
  std::string ip;
7969
0
  auto it = addr_map_.find(host_);
7970
0
  if (it != addr_map_.end()) { ip = it->second; }
7971
7972
0
  return detail::create_client_socket(
7973
0
      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
7974
0
      socket_options_, connection_timeout_sec_, connection_timeout_usec_,
7975
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7976
0
      write_timeout_usec_, interface_, error);
7977
0
}
7978
7979
inline bool ClientImpl::create_and_connect_socket(Socket &socket,
7980
0
                                                  Error &error) {
7981
0
  auto sock = create_client_socket(error);
7982
0
  if (sock == INVALID_SOCKET) { return false; }
7983
0
  socket.sock = sock;
7984
0
  return true;
7985
0
}
7986
7987
inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
7988
0
                                     bool /*shutdown_gracefully*/) {
7989
  // If there are any requests in flight from threads other than us, then it's
7990
  // a thread-unsafe race because individual ssl* objects are not thread-safe.
7991
0
  assert(socket_requests_in_flight_ == 0 ||
7992
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
7993
0
}
7994
7995
0
inline void ClientImpl::shutdown_socket(Socket &socket) const {
7996
0
  if (socket.sock == INVALID_SOCKET) { return; }
7997
0
  detail::shutdown_socket(socket.sock);
7998
0
}
7999
8000
0
inline void ClientImpl::close_socket(Socket &socket) {
8001
  // If there are requests in flight in another thread, usually closing
8002
  // the socket will be fine and they will simply receive an error when
8003
  // using the closed socket, but it is still a bug since rarely the OS
8004
  // may reassign the socket id to be used for a new socket, and then
8005
  // suddenly they will be operating on a live socket that is different
8006
  // than the one they intended!
8007
0
  assert(socket_requests_in_flight_ == 0 ||
8008
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
8009
8010
  // It is also a bug if this happens while SSL is still active
8011
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8012
  assert(socket.ssl == nullptr);
8013
#endif
8014
0
  if (socket.sock == INVALID_SOCKET) { return; }
8015
0
  detail::close_socket(socket.sock);
8016
0
  socket.sock = INVALID_SOCKET;
8017
0
}
8018
8019
inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
8020
0
                                           Response &res) const {
8021
0
  std::array<char, 2048> buf{};
8022
0
8023
0
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
8024
0
8025
0
  if (!line_reader.getline()) { return false; }
8026
0
8027
0
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
8028
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
8029
0
#else
8030
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
8031
0
#endif
8032
0
8033
0
  std::cmatch m;
8034
0
  if (!std::regex_match(line_reader.ptr(), m, re)) {
8035
0
    return req.method == "CONNECT";
8036
0
  }
8037
0
  res.version = std::string(m[1]);
8038
0
  res.status = std::stoi(std::string(m[2]));
8039
0
  res.reason = std::string(m[3]);
8040
0
8041
0
  // Ignore '100 Continue'
8042
0
  while (res.status == StatusCode::Continue_100) {
8043
0
    if (!line_reader.getline()) { return false; } // CRLF
8044
0
    if (!line_reader.getline()) { return false; } // next response line
8045
0
8046
0
    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
8047
0
    res.version = std::string(m[1]);
8048
0
    res.status = std::stoi(std::string(m[2]));
8049
0
    res.reason = std::string(m[3]);
8050
0
  }
8051
0
8052
0
  return true;
8053
0
}
8054
8055
0
inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
8056
0
  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
8057
0
  auto ret = send_(req, res, error);
8058
0
  if (error == Error::SSLPeerCouldBeClosed_) {
8059
0
    assert(!ret);
8060
0
    ret = send_(req, res, error);
8061
0
  }
8062
0
  return ret;
8063
0
}
8064
8065
0
inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
8066
0
  {
8067
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
8068
0
8069
0
    // Set this to false immediately - if it ever gets set to true by the end of
8070
0
    // the request, we know another thread instructed us to close the socket.
8071
0
    socket_should_be_closed_when_request_is_done_ = false;
8072
0
8073
0
    auto is_alive = false;
8074
0
    if (socket_.is_open()) {
8075
0
      is_alive = detail::is_socket_alive(socket_.sock);
8076
0
8077
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8078
0
      if (is_alive && is_ssl()) {
8079
0
        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
8080
0
          is_alive = false;
8081
0
        }
8082
0
      }
8083
0
#endif
8084
0
8085
0
      if (!is_alive) {
8086
0
        // Attempt to avoid sigpipe by shutting down non-gracefully if it seems
8087
0
        // like the other side has already closed the connection Also, there
8088
0
        // cannot be any requests in flight from other threads since we locked
8089
0
        // request_mutex_, so safe to close everything immediately
8090
0
        const bool shutdown_gracefully = false;
8091
0
        shutdown_ssl(socket_, shutdown_gracefully);
8092
0
        shutdown_socket(socket_);
8093
0
        close_socket(socket_);
8094
0
      }
8095
0
    }
8096
0
8097
0
    if (!is_alive) {
8098
0
      if (!create_and_connect_socket(socket_, error)) { return false; }
8099
0
8100
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8101
0
      // TODO: refactoring
8102
0
      if (is_ssl()) {
8103
0
        auto &scli = static_cast<SSLClient &>(*this);
8104
0
        if (!proxy_host_.empty() && proxy_port_ != -1) {
8105
0
          auto success = false;
8106
0
          if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
8107
0
                                       error)) {
8108
0
            return success;
8109
0
          }
8110
0
        }
8111
0
8112
0
        if (!scli.initialize_ssl(socket_, error)) { return false; }
8113
0
      }
8114
0
#endif
8115
0
    }
8116
0
8117
0
    // Mark the current socket as being in use so that it cannot be closed by
8118
0
    // anyone else while this request is ongoing, even though we will be
8119
0
    // releasing the mutex.
8120
0
    if (socket_requests_in_flight_ > 1) {
8121
0
      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
8122
0
    }
8123
0
    socket_requests_in_flight_ += 1;
8124
0
    socket_requests_are_from_thread_ = std::this_thread::get_id();
8125
0
  }
8126
0
8127
0
  for (const auto &header : default_headers_) {
8128
0
    if (req.headers.find(header.first) == req.headers.end()) {
8129
0
      req.headers.insert(header);
8130
0
    }
8131
0
  }
8132
0
8133
0
  auto ret = false;
8134
0
  auto close_connection = !keep_alive_;
8135
0
8136
0
  auto se = detail::scope_exit([&]() {
8137
0
    // Briefly lock mutex in order to mark that a request is no longer ongoing
8138
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
8139
0
    socket_requests_in_flight_ -= 1;
8140
0
    if (socket_requests_in_flight_ <= 0) {
8141
0
      assert(socket_requests_in_flight_ == 0);
8142
0
      socket_requests_are_from_thread_ = std::thread::id();
8143
0
    }
8144
0
8145
0
    if (socket_should_be_closed_when_request_is_done_ || close_connection ||
8146
0
        !ret) {
8147
0
      shutdown_ssl(socket_, true);
8148
0
      shutdown_socket(socket_);
8149
0
      close_socket(socket_);
8150
0
    }
8151
0
  });
8152
0
8153
0
  ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
8154
0
    return handle_request(strm, req, res, close_connection, error);
8155
0
  });
8156
0
8157
0
  if (!ret) {
8158
0
    if (error == Error::Success) { error = Error::Unknown; }
8159
0
  }
8160
0
8161
0
  return ret;
8162
0
}
8163
8164
0
inline Result ClientImpl::send(const Request &req) {
8165
0
  auto req2 = req;
8166
0
  return send_(std::move(req2));
8167
0
}
8168
8169
0
inline Result ClientImpl::send_(Request &&req) {
8170
0
  auto res = detail::make_unique<Response>();
8171
0
  auto error = Error::Success;
8172
0
  auto ret = send(req, *res, error);
8173
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
8174
0
}
8175
8176
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
8177
                                       Response &res, bool close_connection,
8178
0
                                       Error &error) {
8179
0
  if (req.path.empty()) {
8180
0
    error = Error::Connection;
8181
0
    return false;
8182
0
  }
8183
0
8184
0
  auto req_save = req;
8185
0
8186
0
  bool ret;
8187
0
8188
0
  if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
8189
0
    auto req2 = req;
8190
0
    req2.path = "http://" + host_and_port_ + req.path;
8191
0
    ret = process_request(strm, req2, res, close_connection, error);
8192
0
    req = req2;
8193
0
    req.path = req_save.path;
8194
0
  } else {
8195
0
    ret = process_request(strm, req, res, close_connection, error);
8196
0
  }
8197
0
8198
0
  if (!ret) { return false; }
8199
0
8200
0
  if (res.get_header_value("Connection") == "close" ||
8201
0
      (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
8202
0
    // TODO this requires a not-entirely-obvious chain of calls to be correct
8203
0
    // for this to be safe.
8204
0
8205
0
    // This is safe to call because handle_request is only called by send_
8206
0
    // which locks the request mutex during the process. It would be a bug
8207
0
    // to call it from a different thread since it's a thread-safety issue
8208
0
    // to do these things to the socket if another thread is using the socket.
8209
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
8210
0
    shutdown_ssl(socket_, true);
8211
0
    shutdown_socket(socket_);
8212
0
    close_socket(socket_);
8213
0
  }
8214
0
8215
0
  if (300 < res.status && res.status < 400 && follow_location_) {
8216
0
    req = req_save;
8217
0
    ret = redirect(req, res, error);
8218
0
  }
8219
0
8220
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8221
0
  if ((res.status == StatusCode::Unauthorized_401 ||
8222
0
       res.status == StatusCode::ProxyAuthenticationRequired_407) &&
8223
0
      req.authorization_count_ < 5) {
8224
0
    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
8225
0
    const auto &username =
8226
0
        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
8227
0
    const auto &password =
8228
0
        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
8229
0
8230
0
    if (!username.empty() && !password.empty()) {
8231
0
      std::map<std::string, std::string> auth;
8232
0
      if (detail::parse_www_authenticate(res, auth, is_proxy)) {
8233
0
        Request new_req = req;
8234
0
        new_req.authorization_count_ += 1;
8235
0
        new_req.headers.erase(is_proxy ? "Proxy-Authorization"
8236
0
                                       : "Authorization");
8237
0
        new_req.headers.insert(detail::make_digest_authentication_header(
8238
0
            req, auth, new_req.authorization_count_, detail::random_string(10),
8239
0
            username, password, is_proxy));
8240
0
8241
0
        Response new_res;
8242
0
8243
0
        ret = send(new_req, new_res, error);
8244
0
        if (ret) { res = new_res; }
8245
0
      }
8246
0
    }
8247
0
  }
8248
0
#endif
8249
0
8250
0
  return ret;
8251
0
}
8252
8253
0
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
8254
0
  if (req.redirect_count_ == 0) {
8255
0
    error = Error::ExceedRedirectCount;
8256
0
    return false;
8257
0
  }
8258
0
8259
0
  auto location = res.get_header_value("location");
8260
0
  if (location.empty()) { return false; }
8261
0
8262
0
  thread_local const std::regex re(
8263
0
      R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
8264
0
8265
0
  std::smatch m;
8266
0
  if (!std::regex_match(location, m, re)) { return false; }
8267
0
8268
0
  auto scheme = is_ssl() ? "https" : "http";
8269
0
8270
0
  auto next_scheme = m[1].str();
8271
0
  auto next_host = m[2].str();
8272
0
  if (next_host.empty()) { next_host = m[3].str(); }
8273
0
  auto port_str = m[4].str();
8274
0
  auto next_path = m[5].str();
8275
0
  auto next_query = m[6].str();
8276
0
8277
0
  auto next_port = port_;
8278
0
  if (!port_str.empty()) {
8279
0
    next_port = std::stoi(port_str);
8280
0
  } else if (!next_scheme.empty()) {
8281
0
    next_port = next_scheme == "https" ? 443 : 80;
8282
0
  }
8283
0
8284
0
  if (next_scheme.empty()) { next_scheme = scheme; }
8285
0
  if (next_host.empty()) { next_host = host_; }
8286
0
  if (next_path.empty()) { next_path = "/"; }
8287
0
8288
0
  auto path = detail::decode_url(next_path, true) + next_query;
8289
0
8290
0
  // Same host redirect - use current client
8291
0
  if (next_scheme == scheme && next_host == host_ && next_port == port_) {
8292
0
    return detail::redirect(*this, req, res, path, location, error);
8293
0
  }
8294
0
8295
0
  // Cross-host/scheme redirect - create new client with robust setup
8296
0
  return create_redirect_client(next_scheme, next_host, next_port, req, res,
8297
0
                                path, location, error);
8298
0
}
8299
8300
// New method for robust redirect client creation
8301
inline bool ClientImpl::create_redirect_client(
8302
    const std::string &scheme, const std::string &host, int port, Request &req,
8303
    Response &res, const std::string &path, const std::string &location,
8304
0
    Error &error) {
8305
0
  // Determine if we need SSL
8306
0
  auto need_ssl = (scheme == "https");
8307
0
8308
0
  // Clean up request headers that are host/client specific
8309
0
  // Remove headers that should not be carried over to new host
8310
0
  auto headers_to_remove =
8311
0
      std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
8312
0
8313
0
  for (const auto &header_name : headers_to_remove) {
8314
0
    auto it = req.headers.find(header_name);
8315
0
    while (it != req.headers.end()) {
8316
0
      it = req.headers.erase(it);
8317
0
      it = req.headers.find(header_name);
8318
0
    }
8319
0
  }
8320
0
8321
0
  // Create appropriate client type and handle redirect
8322
0
  if (need_ssl) {
8323
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8324
0
    // Create SSL client for HTTPS redirect
8325
0
    SSLClient redirect_client(host, port);
8326
0
8327
0
    // Setup basic client configuration first
8328
0
    setup_redirect_client(redirect_client);
8329
0
8330
0
    // SSL-specific configuration for proxy environments
8331
0
    if (!proxy_host_.empty() && proxy_port_ != -1) {
8332
0
      // Critical: Disable SSL verification for proxy environments
8333
0
      redirect_client.enable_server_certificate_verification(false);
8334
0
      redirect_client.enable_server_hostname_verification(false);
8335
0
    } else {
8336
0
      // For direct SSL connections, copy SSL verification settings
8337
0
      redirect_client.enable_server_certificate_verification(
8338
0
          server_certificate_verification_);
8339
0
      redirect_client.enable_server_hostname_verification(
8340
0
          server_hostname_verification_);
8341
0
    }
8342
0
8343
0
    // Handle CA certificate store and paths if available
8344
0
    if (ca_cert_store_) { redirect_client.set_ca_cert_store(ca_cert_store_); }
8345
0
    if (!ca_cert_file_path_.empty()) {
8346
0
      redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
8347
0
    }
8348
0
8349
0
    // Client certificates are set through constructor for SSLClient
8350
0
    // NOTE: SSLClient constructor already takes client_cert_path and
8351
0
    // client_key_path so we need to create it properly if client certs are
8352
0
    // needed
8353
0
8354
0
    // Execute the redirect
8355
0
    return detail::redirect(redirect_client, req, res, path, location, error);
8356
0
#else
8357
0
    // SSL not supported - set appropriate error
8358
0
    error = Error::SSLConnection;
8359
0
    return false;
8360
0
#endif
8361
0
  } else {
8362
0
    // HTTP redirect
8363
0
    ClientImpl redirect_client(host, port);
8364
0
8365
0
    // Setup client with robust configuration
8366
0
    setup_redirect_client(redirect_client);
8367
0
8368
0
    // Execute the redirect
8369
0
    return detail::redirect(redirect_client, req, res, path, location, error);
8370
0
  }
8371
0
}
8372
8373
// New method for robust client setup (based on basic_manual_redirect.cpp logic)
8374
template <typename ClientType>
8375
0
inline void ClientImpl::setup_redirect_client(ClientType &client) {
8376
0
  // Copy basic settings first
8377
0
  client.set_connection_timeout(connection_timeout_sec_);
8378
0
  client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
8379
0
  client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
8380
0
  client.set_keep_alive(keep_alive_);
8381
0
  client.set_follow_location(
8382
0
      true); // Enable redirects to handle multi-step redirects
8383
0
  client.set_url_encode(url_encode_);
8384
0
  client.set_compress(compress_);
8385
0
  client.set_decompress(decompress_);
8386
0
8387
0
  // Copy authentication settings BEFORE proxy setup
8388
0
  if (!basic_auth_username_.empty()) {
8389
0
    client.set_basic_auth(basic_auth_username_, basic_auth_password_);
8390
0
  }
8391
0
  if (!bearer_token_auth_token_.empty()) {
8392
0
    client.set_bearer_token_auth(bearer_token_auth_token_);
8393
0
  }
8394
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8395
0
  if (!digest_auth_username_.empty()) {
8396
0
    client.set_digest_auth(digest_auth_username_, digest_auth_password_);
8397
0
  }
8398
0
#endif
8399
0
8400
0
  // Setup proxy configuration (CRITICAL ORDER - proxy must be set
8401
0
  // before proxy auth)
8402
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
8403
0
    // First set proxy host and port
8404
0
    client.set_proxy(proxy_host_, proxy_port_);
8405
0
8406
0
    // Then set proxy authentication (order matters!)
8407
0
    if (!proxy_basic_auth_username_.empty()) {
8408
0
      client.set_proxy_basic_auth(proxy_basic_auth_username_,
8409
0
                                  proxy_basic_auth_password_);
8410
0
    }
8411
0
    if (!proxy_bearer_token_auth_token_.empty()) {
8412
0
      client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
8413
0
    }
8414
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8415
0
    if (!proxy_digest_auth_username_.empty()) {
8416
0
      client.set_proxy_digest_auth(proxy_digest_auth_username_,
8417
0
                                   proxy_digest_auth_password_);
8418
0
    }
8419
0
#endif
8420
0
  }
8421
0
8422
0
  // Copy network and socket settings
8423
0
  client.set_address_family(address_family_);
8424
0
  client.set_tcp_nodelay(tcp_nodelay_);
8425
0
  client.set_ipv6_v6only(ipv6_v6only_);
8426
0
  if (socket_options_) { client.set_socket_options(socket_options_); }
8427
0
  if (!interface_.empty()) { client.set_interface(interface_); }
8428
0
8429
0
  // Copy logging and headers
8430
0
  if (logger_) { client.set_logger(logger_); }
8431
0
8432
0
  // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
8433
0
  // Each new client should generate its own headers based on its target host
8434
0
}
8435
8436
inline bool ClientImpl::write_content_with_provider(Stream &strm,
8437
                                                    const Request &req,
8438
0
                                                    Error &error) const {
8439
0
  auto is_shutting_down = []() { return false; };
8440
0
8441
0
  if (req.is_chunked_content_provider_) {
8442
0
    // TODO: Brotli support
8443
0
    std::unique_ptr<detail::compressor> compressor;
8444
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8445
0
    if (compress_) {
8446
0
      compressor = detail::make_unique<detail::gzip_compressor>();
8447
0
    } else
8448
0
#endif
8449
0
    {
8450
0
      compressor = detail::make_unique<detail::nocompressor>();
8451
0
    }
8452
0
8453
0
    return detail::write_content_chunked(strm, req.content_provider_,
8454
0
                                         is_shutting_down, *compressor, error);
8455
0
  } else {
8456
0
    return detail::write_content_with_progress(
8457
0
        strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
8458
0
        req.upload_progress, error);
8459
0
  }
8460
0
}
8461
8462
inline bool ClientImpl::write_request(Stream &strm, Request &req,
8463
0
                                      bool close_connection, Error &error) {
8464
0
  // Prepare additional headers
8465
0
  if (close_connection) {
8466
0
    if (!req.has_header("Connection")) {
8467
0
      req.set_header("Connection", "close");
8468
0
    }
8469
0
  }
8470
0
8471
0
  if (!req.has_header("Host")) {
8472
0
    if (is_ssl()) {
8473
0
      if (port_ == 443) {
8474
0
        req.set_header("Host", host_);
8475
0
      } else {
8476
0
        req.set_header("Host", host_and_port_);
8477
0
      }
8478
0
    } else {
8479
0
      if (port_ == 80) {
8480
0
        req.set_header("Host", host_);
8481
0
      } else {
8482
0
        req.set_header("Host", host_and_port_);
8483
0
      }
8484
0
    }
8485
0
  }
8486
0
8487
0
  if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }
8488
0
8489
0
  if (!req.content_receiver) {
8490
0
    if (!req.has_header("Accept-Encoding")) {
8491
0
      std::string accept_encoding;
8492
0
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
8493
0
      accept_encoding = "br";
8494
0
#endif
8495
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8496
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
8497
0
      accept_encoding += "gzip, deflate";
8498
0
#endif
8499
0
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
8500
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
8501
0
      accept_encoding += "zstd";
8502
0
#endif
8503
0
      req.set_header("Accept-Encoding", accept_encoding);
8504
0
    }
8505
0
8506
0
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
8507
0
    if (!req.has_header("User-Agent")) {
8508
0
      auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
8509
0
      req.set_header("User-Agent", agent);
8510
0
    }
8511
0
#endif
8512
0
  };
8513
0
8514
0
  if (req.body.empty()) {
8515
0
    if (req.content_provider_) {
8516
0
      if (!req.is_chunked_content_provider_) {
8517
0
        if (!req.has_header("Content-Length")) {
8518
0
          auto length = std::to_string(req.content_length_);
8519
0
          req.set_header("Content-Length", length);
8520
0
        }
8521
0
      }
8522
0
    } else {
8523
0
      if (req.method == "POST" || req.method == "PUT" ||
8524
0
          req.method == "PATCH") {
8525
0
        req.set_header("Content-Length", "0");
8526
0
      }
8527
0
    }
8528
0
  } else {
8529
0
    if (!req.has_header("Content-Type")) {
8530
0
      req.set_header("Content-Type", "text/plain");
8531
0
    }
8532
0
8533
0
    if (!req.has_header("Content-Length")) {
8534
0
      auto length = std::to_string(req.body.size());
8535
0
      req.set_header("Content-Length", length);
8536
0
    }
8537
0
  }
8538
0
8539
0
  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
8540
0
    if (!req.has_header("Authorization")) {
8541
0
      req.headers.insert(make_basic_authentication_header(
8542
0
          basic_auth_username_, basic_auth_password_, false));
8543
0
    }
8544
0
  }
8545
0
8546
0
  if (!proxy_basic_auth_username_.empty() &&
8547
0
      !proxy_basic_auth_password_.empty()) {
8548
0
    if (!req.has_header("Proxy-Authorization")) {
8549
0
      req.headers.insert(make_basic_authentication_header(
8550
0
          proxy_basic_auth_username_, proxy_basic_auth_password_, true));
8551
0
    }
8552
0
  }
8553
0
8554
0
  if (!bearer_token_auth_token_.empty()) {
8555
0
    if (!req.has_header("Authorization")) {
8556
0
      req.headers.insert(make_bearer_token_authentication_header(
8557
0
          bearer_token_auth_token_, false));
8558
0
    }
8559
0
  }
8560
0
8561
0
  if (!proxy_bearer_token_auth_token_.empty()) {
8562
0
    if (!req.has_header("Proxy-Authorization")) {
8563
0
      req.headers.insert(make_bearer_token_authentication_header(
8564
0
          proxy_bearer_token_auth_token_, true));
8565
0
    }
8566
0
  }
8567
0
8568
0
  // Request line and headers
8569
0
  {
8570
0
    detail::BufferStream bstrm;
8571
0
8572
0
    const auto &path_with_query =
8573
0
        req.params.empty() ? req.path
8574
0
                           : append_query_params(req.path, req.params);
8575
0
8576
0
    const auto &path =
8577
0
        url_encode_ ? detail::encode_url(path_with_query) : path_with_query;
8578
0
8579
0
    detail::write_request_line(bstrm, req.method, path);
8580
0
8581
0
    header_writer_(bstrm, req.headers);
8582
0
8583
0
    // Flush buffer
8584
0
    auto &data = bstrm.get_buffer();
8585
0
    if (!detail::write_data(strm, data.data(), data.size())) {
8586
0
      error = Error::Write;
8587
0
      return false;
8588
0
    }
8589
0
  }
8590
0
8591
0
  // Body
8592
0
  if (req.body.empty()) {
8593
0
    return write_content_with_provider(strm, req, error);
8594
0
  }
8595
0
8596
0
  if (req.upload_progress) {
8597
0
    auto body_size = req.body.size();
8598
0
    size_t written = 0;
8599
0
    auto data = req.body.data();
8600
0
8601
0
    while (written < body_size) {
8602
0
      size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
8603
0
      if (!detail::write_data(strm, data + written, to_write)) {
8604
0
        error = Error::Write;
8605
0
        return false;
8606
0
      }
8607
0
      written += to_write;
8608
0
8609
0
      if (!req.upload_progress(written, body_size)) {
8610
0
        error = Error::Canceled;
8611
0
        return false;
8612
0
      }
8613
0
    }
8614
0
  } else {
8615
0
    if (!detail::write_data(strm, req.body.data(), req.body.size())) {
8616
0
      error = Error::Write;
8617
0
      return false;
8618
0
    }
8619
0
  }
8620
0
8621
0
  return true;
8622
0
}
8623
8624
inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
8625
    Request &req, const char *body, size_t content_length,
8626
    ContentProvider content_provider,
8627
    ContentProviderWithoutLength content_provider_without_length,
8628
0
    const std::string &content_type, Error &error) {
8629
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
8630
0
8631
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8632
0
  if (compress_) { req.set_header("Content-Encoding", "gzip"); }
8633
0
#endif
8634
0
8635
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
8636
0
  if (compress_ && !content_provider_without_length) {
8637
0
    // TODO: Brotli support
8638
0
    detail::gzip_compressor compressor;
8639
0
8640
0
    if (content_provider) {
8641
0
      auto ok = true;
8642
0
      size_t offset = 0;
8643
0
      DataSink data_sink;
8644
0
8645
0
      data_sink.write = [&](const char *data, size_t data_len) -> bool {
8646
0
        if (ok) {
8647
0
          auto last = offset + data_len == content_length;
8648
0
8649
0
          auto ret = compressor.compress(
8650
0
              data, data_len, last,
8651
0
              [&](const char *compressed_data, size_t compressed_data_len) {
8652
0
                req.body.append(compressed_data, compressed_data_len);
8653
0
                return true;
8654
0
              });
8655
0
8656
0
          if (ret) {
8657
0
            offset += data_len;
8658
0
          } else {
8659
0
            ok = false;
8660
0
          }
8661
0
        }
8662
0
        return ok;
8663
0
      };
8664
0
8665
0
      while (ok && offset < content_length) {
8666
0
        if (!content_provider(offset, content_length - offset, data_sink)) {
8667
0
          error = Error::Canceled;
8668
0
          return nullptr;
8669
0
        }
8670
0
      }
8671
0
    } else {
8672
0
      if (!compressor.compress(body, content_length, true,
8673
0
                               [&](const char *data, size_t data_len) {
8674
0
                                 req.body.append(data, data_len);
8675
0
                                 return true;
8676
0
                               })) {
8677
0
        error = Error::Compression;
8678
0
        return nullptr;
8679
0
      }
8680
0
    }
8681
0
  } else
8682
0
#endif
8683
0
  {
8684
0
    if (content_provider) {
8685
0
      req.content_length_ = content_length;
8686
0
      req.content_provider_ = std::move(content_provider);
8687
0
      req.is_chunked_content_provider_ = false;
8688
0
    } else if (content_provider_without_length) {
8689
0
      req.content_length_ = 0;
8690
0
      req.content_provider_ = detail::ContentProviderAdapter(
8691
0
          std::move(content_provider_without_length));
8692
0
      req.is_chunked_content_provider_ = true;
8693
0
      req.set_header("Transfer-Encoding", "chunked");
8694
0
    } else {
8695
0
      req.body.assign(body, content_length);
8696
0
    }
8697
0
  }
8698
0
8699
0
  auto res = detail::make_unique<Response>();
8700
0
  return send(req, *res, error) ? std::move(res) : nullptr;
8701
0
}
8702
8703
inline Result ClientImpl::send_with_content_provider(
8704
    const std::string &method, const std::string &path, const Headers &headers,
8705
    const char *body, size_t content_length, ContentProvider content_provider,
8706
    ContentProviderWithoutLength content_provider_without_length,
8707
0
    const std::string &content_type, UploadProgress progress) {
8708
0
  Request req;
8709
0
  req.method = method;
8710
0
  req.headers = headers;
8711
0
  req.path = path;
8712
0
  req.upload_progress = std::move(progress);
8713
0
  if (max_timeout_msec_ > 0) {
8714
0
    req.start_time_ = std::chrono::steady_clock::now();
8715
0
  }
8716
0
8717
0
  auto error = Error::Success;
8718
0
8719
0
  auto res = send_with_content_provider(
8720
0
      req, body, content_length, std::move(content_provider),
8721
0
      std::move(content_provider_without_length), content_type, error);
8722
0
8723
0
  return Result{std::move(res), error, std::move(req.headers)};
8724
0
}
8725
8726
inline std::string
8727
0
ClientImpl::adjust_host_string(const std::string &host) const {
8728
0
  if (host.find(':') != std::string::npos) { return "[" + host + "]"; }
8729
0
  return host;
8730
0
}
8731
8732
inline bool ClientImpl::process_request(Stream &strm, Request &req,
8733
                                        Response &res, bool close_connection,
8734
0
                                        Error &error) {
8735
0
  // Send request
8736
0
  if (!write_request(strm, req, close_connection, error)) { return false; }
8737
0
8738
0
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8739
0
  if (is_ssl()) {
8740
0
    auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
8741
0
    if (!is_proxy_enabled) {
8742
0
      if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
8743
0
        error = Error::SSLPeerCouldBeClosed_;
8744
0
        return false;
8745
0
      }
8746
0
    }
8747
0
  }
8748
0
#endif
8749
0
8750
0
  // Receive response and headers
8751
0
  if (!read_response_line(strm, req, res) ||
8752
0
      !detail::read_headers(strm, res.headers)) {
8753
0
    error = Error::Read;
8754
0
    return false;
8755
0
  }
8756
0
8757
0
  // Body
8758
0
  if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
8759
0
      req.method != "CONNECT") {
8760
0
    auto redirect = 300 < res.status && res.status < 400 &&
8761
0
                    res.status != StatusCode::NotModified_304 &&
8762
0
                    follow_location_;
8763
0
8764
0
    if (req.response_handler && !redirect) {
8765
0
      if (!req.response_handler(res)) {
8766
0
        error = Error::Canceled;
8767
0
        return false;
8768
0
      }
8769
0
    }
8770
0
8771
0
    auto out =
8772
0
        req.content_receiver
8773
0
            ? static_cast<ContentReceiverWithProgress>(
8774
0
                  [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
8775
0
                    if (redirect) { return true; }
8776
0
                    auto ret = req.content_receiver(buf, n, off, len);
8777
0
                    if (!ret) { error = Error::Canceled; }
8778
0
                    return ret;
8779
0
                  })
8780
0
            : static_cast<ContentReceiverWithProgress>(
8781
0
                  [&](const char *buf, size_t n, uint64_t /*off*/,
8782
0
                      uint64_t /*len*/) {
8783
0
                    assert(res.body.size() + n <= res.body.max_size());
8784
0
                    res.body.append(buf, n);
8785
0
                    return true;
8786
0
                  });
8787
0
8788
0
    auto progress = [&](uint64_t current, uint64_t total) {
8789
0
      if (!req.download_progress || redirect) { return true; }
8790
0
      auto ret = req.download_progress(current, total);
8791
0
      if (!ret) { error = Error::Canceled; }
8792
0
      return ret;
8793
0
    };
8794
0
8795
0
    if (res.has_header("Content-Length")) {
8796
0
      if (!req.content_receiver) {
8797
0
        auto len = res.get_header_value_u64("Content-Length");
8798
0
        if (len > res.body.max_size()) {
8799
0
          error = Error::Read;
8800
0
          return false;
8801
0
        }
8802
0
        res.body.reserve(static_cast<size_t>(len));
8803
0
      }
8804
0
    }
8805
0
8806
0
    if (res.status != StatusCode::NotModified_304) {
8807
0
      int dummy_status;
8808
0
      if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
8809
0
                                dummy_status, std::move(progress),
8810
0
                                std::move(out), decompress_)) {
8811
0
        if (error != Error::Canceled) { error = Error::Read; }
8812
0
        return false;
8813
0
      }
8814
0
    }
8815
0
  }
8816
0
8817
0
  // Log
8818
0
  if (logger_) { logger_(req, res); }
8819
0
8820
0
  return true;
8821
0
}
8822
8823
inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
8824
    const std::string &boundary,
8825
    const MultipartFormDataItemsForClientInput &items,
8826
0
    const MultipartFormDataProviderItems &provider_items) const {
8827
0
  size_t cur_item = 0;
8828
0
  size_t cur_start = 0;
8829
0
  // cur_item and cur_start are copied to within the std::function and maintain
8830
0
  // state between successive calls
8831
0
  return [&, cur_item, cur_start](size_t offset,
8832
0
                                  DataSink &sink) mutable -> bool {
8833
0
    if (!offset && !items.empty()) {
8834
0
      sink.os << detail::serialize_multipart_formdata(items, boundary, false);
8835
0
      return true;
8836
0
    } else if (cur_item < provider_items.size()) {
8837
0
      if (!cur_start) {
8838
0
        const auto &begin = detail::serialize_multipart_formdata_item_begin(
8839
0
            provider_items[cur_item], boundary);
8840
0
        offset += begin.size();
8841
0
        cur_start = offset;
8842
0
        sink.os << begin;
8843
0
      }
8844
0
8845
0
      DataSink cur_sink;
8846
0
      auto has_data = true;
8847
0
      cur_sink.write = sink.write;
8848
0
      cur_sink.done = [&]() { has_data = false; };
8849
0
8850
0
      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
8851
0
        return false;
8852
0
      }
8853
0
8854
0
      if (!has_data) {
8855
0
        sink.os << detail::serialize_multipart_formdata_item_end();
8856
0
        cur_item++;
8857
0
        cur_start = 0;
8858
0
      }
8859
0
      return true;
8860
0
    } else {
8861
0
      sink.os << detail::serialize_multipart_formdata_finish(boundary);
8862
0
      sink.done();
8863
0
      return true;
8864
0
    }
8865
0
  };
8866
0
}
8867
8868
inline bool ClientImpl::process_socket(
8869
    const Socket &socket,
8870
    std::chrono::time_point<std::chrono::steady_clock> start_time,
8871
0
    std::function<bool(Stream &strm)> callback) {
8872
0
  return detail::process_client_socket(
8873
0
      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8874
0
      write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
8875
0
}
8876
8877
0
inline bool ClientImpl::is_ssl() const { return false; }
8878
8879
inline Result ClientImpl::Get(const std::string &path,
8880
0
                              DownloadProgress progress) {
8881
0
  return Get(path, Headers(), std::move(progress));
8882
0
}
8883
8884
inline Result ClientImpl::Get(const std::string &path, const Params &params,
8885
                              const Headers &headers,
8886
0
                              DownloadProgress progress) {
8887
0
  if (params.empty()) { return Get(path, headers); }
8888
0
8889
0
  std::string path_with_query = append_query_params(path, params);
8890
0
  return Get(path_with_query, headers, std::move(progress));
8891
0
}
8892
8893
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
8894
0
                              DownloadProgress progress) {
8895
0
  Request req;
8896
0
  req.method = "GET";
8897
0
  req.path = path;
8898
0
  req.headers = headers;
8899
0
  req.download_progress = std::move(progress);
8900
0
  if (max_timeout_msec_ > 0) {
8901
0
    req.start_time_ = std::chrono::steady_clock::now();
8902
0
  }
8903
0
8904
0
  return send_(std::move(req));
8905
0
}
8906
8907
inline Result ClientImpl::Get(const std::string &path,
8908
                              ContentReceiver content_receiver,
8909
0
                              DownloadProgress progress) {
8910
0
  return Get(path, Headers(), nullptr, std::move(content_receiver),
8911
0
             std::move(progress));
8912
0
}
8913
8914
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
8915
                              ContentReceiver content_receiver,
8916
0
                              DownloadProgress progress) {
8917
0
  return Get(path, headers, nullptr, std::move(content_receiver),
8918
0
             std::move(progress));
8919
0
}
8920
8921
inline Result ClientImpl::Get(const std::string &path,
8922
                              ResponseHandler response_handler,
8923
                              ContentReceiver content_receiver,
8924
0
                              DownloadProgress progress) {
8925
0
  return Get(path, Headers(), std::move(response_handler),
8926
0
             std::move(content_receiver), std::move(progress));
8927
0
}
8928
8929
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
8930
                              ResponseHandler response_handler,
8931
                              ContentReceiver content_receiver,
8932
0
                              DownloadProgress progress) {
8933
0
  Request req;
8934
0
  req.method = "GET";
8935
0
  req.path = path;
8936
0
  req.headers = headers;
8937
0
  req.response_handler = std::move(response_handler);
8938
0
  req.content_receiver =
8939
0
      [content_receiver](const char *data, size_t data_length,
8940
0
                         uint64_t /*offset*/, uint64_t /*total_length*/) {
8941
0
        return content_receiver(data, data_length);
8942
0
      };
8943
0
  req.download_progress = std::move(progress);
8944
0
  if (max_timeout_msec_ > 0) {
8945
0
    req.start_time_ = std::chrono::steady_clock::now();
8946
0
  }
8947
0
8948
0
  return send_(std::move(req));
8949
0
}
8950
8951
inline Result ClientImpl::Get(const std::string &path, const Params &params,
8952
                              const Headers &headers,
8953
                              ContentReceiver content_receiver,
8954
0
                              DownloadProgress progress) {
8955
0
  return Get(path, params, headers, nullptr, std::move(content_receiver),
8956
0
             std::move(progress));
8957
0
}
8958
8959
inline Result ClientImpl::Get(const std::string &path, const Params &params,
8960
                              const Headers &headers,
8961
                              ResponseHandler response_handler,
8962
                              ContentReceiver content_receiver,
8963
0
                              DownloadProgress progress) {
8964
0
  if (params.empty()) {
8965
0
    return Get(path, headers, std::move(response_handler),
8966
0
               std::move(content_receiver), std::move(progress));
8967
0
  }
8968
0
8969
0
  std::string path_with_query = append_query_params(path, params);
8970
0
  return Get(path_with_query, headers, std::move(response_handler),
8971
0
             std::move(content_receiver), std::move(progress));
8972
0
}
8973
8974
0
inline Result ClientImpl::Head(const std::string &path) {
8975
0
  return Head(path, Headers());
8976
0
}
8977
8978
inline Result ClientImpl::Head(const std::string &path,
8979
0
                               const Headers &headers) {
8980
0
  Request req;
8981
0
  req.method = "HEAD";
8982
0
  req.headers = headers;
8983
0
  req.path = path;
8984
0
  if (max_timeout_msec_ > 0) {
8985
0
    req.start_time_ = std::chrono::steady_clock::now();
8986
0
  }
8987
0
8988
0
  return send_(std::move(req));
8989
0
}
8990
8991
0
inline Result ClientImpl::Post(const std::string &path) {
8992
0
  return Post(path, std::string(), std::string());
8993
0
}
8994
8995
inline Result ClientImpl::Post(const std::string &path,
8996
0
                               const Headers &headers) {
8997
0
  return Post(path, headers, nullptr, 0, std::string());
8998
0
}
8999
9000
inline Result ClientImpl::Post(const std::string &path, const char *body,
9001
                               size_t content_length,
9002
                               const std::string &content_type,
9003
0
                               UploadProgress progress) {
9004
0
  return Post(path, Headers(), body, content_length, content_type, progress);
9005
0
}
9006
9007
inline Result ClientImpl::Post(const std::string &path, const std::string &body,
9008
                               const std::string &content_type,
9009
0
                               UploadProgress progress) {
9010
0
  return Post(path, Headers(), body, content_type, progress);
9011
0
}
9012
9013
0
inline Result ClientImpl::Post(const std::string &path, const Params &params) {
9014
0
  return Post(path, Headers(), params);
9015
0
}
9016
9017
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
9018
                               ContentProvider content_provider,
9019
                               const std::string &content_type,
9020
0
                               UploadProgress progress) {
9021
0
  return Post(path, Headers(), content_length, std::move(content_provider),
9022
0
              content_type, progress);
9023
0
}
9024
9025
inline Result ClientImpl::Post(const std::string &path,
9026
                               ContentProviderWithoutLength content_provider,
9027
                               const std::string &content_type,
9028
0
                               UploadProgress progress) {
9029
0
  return Post(path, Headers(), std::move(content_provider), content_type,
9030
0
              progress);
9031
0
}
9032
9033
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9034
0
                               const Params &params) {
9035
0
  auto query = detail::params_to_query_str(params);
9036
0
  return Post(path, headers, query, "application/x-www-form-urlencoded");
9037
0
}
9038
9039
inline Result
9040
ClientImpl::Post(const std::string &path,
9041
                 const MultipartFormDataItemsForClientInput &items,
9042
0
                 UploadProgress progress) {
9043
0
  return Post(path, Headers(), items, progress);
9044
0
}
9045
9046
inline Result
9047
ClientImpl::Post(const std::string &path, const Headers &headers,
9048
                 const MultipartFormDataItemsForClientInput &items,
9049
0
                 UploadProgress progress) {
9050
0
  const auto &boundary = detail::make_multipart_data_boundary();
9051
0
  const auto &content_type =
9052
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9053
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
9054
0
  return Post(path, headers, body, content_type, progress);
9055
0
}
9056
9057
inline Result
9058
ClientImpl::Post(const std::string &path, const Headers &headers,
9059
                 const MultipartFormDataItemsForClientInput &items,
9060
0
                 const std::string &boundary, UploadProgress progress) {
9061
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
9062
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
9063
0
  }
9064
0
9065
0
  const auto &content_type =
9066
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9067
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
9068
0
  return Post(path, headers, body, content_type, progress);
9069
0
}
9070
9071
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9072
                               const char *body, size_t content_length,
9073
                               const std::string &content_type,
9074
0
                               UploadProgress progress) {
9075
0
  return send_with_content_provider("POST", path, headers, body, content_length,
9076
0
                                    nullptr, nullptr, content_type, progress);
9077
0
}
9078
9079
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9080
                               const std::string &body,
9081
                               const std::string &content_type,
9082
0
                               UploadProgress progress) {
9083
0
  return send_with_content_provider("POST", path, headers, body.data(),
9084
0
                                    body.size(), nullptr, nullptr, content_type,
9085
0
                                    progress);
9086
0
}
9087
9088
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9089
                               size_t content_length,
9090
                               ContentProvider content_provider,
9091
                               const std::string &content_type,
9092
0
                               UploadProgress progress) {
9093
0
  return send_with_content_provider("POST", path, headers, nullptr,
9094
0
                                    content_length, std::move(content_provider),
9095
0
                                    nullptr, content_type, progress);
9096
0
}
9097
9098
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
9099
                               ContentProviderWithoutLength content_provider,
9100
                               const std::string &content_type,
9101
0
                               UploadProgress progress) {
9102
0
  return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
9103
0
                                    std::move(content_provider), content_type,
9104
0
                                    progress);
9105
0
}
9106
9107
inline Result
9108
ClientImpl::Post(const std::string &path, const Headers &headers,
9109
                 const MultipartFormDataItemsForClientInput &items,
9110
                 const MultipartFormDataProviderItems &provider_items,
9111
0
                 UploadProgress progress) {
9112
0
  const auto &boundary = detail::make_multipart_data_boundary();
9113
0
  const auto &content_type =
9114
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9115
0
  return send_with_content_provider(
9116
0
      "POST", path, headers, nullptr, 0, nullptr,
9117
0
      get_multipart_content_provider(boundary, items, provider_items),
9118
0
      content_type, progress);
9119
0
}
9120
9121
0
inline Result ClientImpl::Put(const std::string &path) {
9122
0
  return Put(path, std::string(), std::string());
9123
0
}
9124
9125
0
inline Result ClientImpl::Put(const std::string &path, const Headers &headers) {
9126
0
  return Put(path, headers, nullptr, 0, std::string());
9127
0
}
9128
9129
inline Result ClientImpl::Put(const std::string &path, const char *body,
9130
                              size_t content_length,
9131
                              const std::string &content_type,
9132
0
                              UploadProgress progress) {
9133
0
  return Put(path, Headers(), body, content_length, content_type, progress);
9134
0
}
9135
9136
inline Result ClientImpl::Put(const std::string &path, const std::string &body,
9137
                              const std::string &content_type,
9138
0
                              UploadProgress progress) {
9139
0
  return Put(path, Headers(), body, content_type, progress);
9140
0
}
9141
9142
0
inline Result ClientImpl::Put(const std::string &path, const Params &params) {
9143
0
  return Put(path, Headers(), params);
9144
0
}
9145
9146
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
9147
                              ContentProvider content_provider,
9148
                              const std::string &content_type,
9149
0
                              UploadProgress progress) {
9150
0
  return Put(path, Headers(), content_length, std::move(content_provider),
9151
0
             content_type, progress);
9152
0
}
9153
9154
inline Result ClientImpl::Put(const std::string &path,
9155
                              ContentProviderWithoutLength content_provider,
9156
                              const std::string &content_type,
9157
0
                              UploadProgress progress) {
9158
0
  return Put(path, Headers(), std::move(content_provider), content_type,
9159
0
             progress);
9160
0
}
9161
9162
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9163
0
                              const Params &params) {
9164
0
  auto query = detail::params_to_query_str(params);
9165
0
  return Put(path, headers, query, "application/x-www-form-urlencoded");
9166
0
}
9167
9168
inline Result ClientImpl::Put(const std::string &path,
9169
                              const MultipartFormDataItemsForClientInput &items,
9170
0
                              UploadProgress progress) {
9171
0
  return Put(path, Headers(), items, progress);
9172
0
}
9173
9174
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9175
                              const MultipartFormDataItemsForClientInput &items,
9176
0
                              UploadProgress progress) {
9177
0
  const auto &boundary = detail::make_multipart_data_boundary();
9178
0
  const auto &content_type =
9179
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9180
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
9181
0
  return Put(path, headers, body, content_type, progress);
9182
0
}
9183
9184
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9185
                              const MultipartFormDataItemsForClientInput &items,
9186
                              const std::string &boundary,
9187
0
                              UploadProgress progress) {
9188
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
9189
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
9190
0
  }
9191
0
9192
0
  const auto &content_type =
9193
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9194
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
9195
0
  return Put(path, headers, body, content_type, progress);
9196
0
}
9197
9198
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9199
                              const char *body, size_t content_length,
9200
                              const std::string &content_type,
9201
0
                              UploadProgress progress) {
9202
0
  return send_with_content_provider("PUT", path, headers, body, content_length,
9203
0
                                    nullptr, nullptr, content_type, progress);
9204
0
}
9205
9206
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9207
                              const std::string &body,
9208
                              const std::string &content_type,
9209
0
                              UploadProgress progress) {
9210
0
  return send_with_content_provider("PUT", path, headers, body.data(),
9211
0
                                    body.size(), nullptr, nullptr, content_type,
9212
0
                                    progress);
9213
0
}
9214
9215
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9216
                              size_t content_length,
9217
                              ContentProvider content_provider,
9218
                              const std::string &content_type,
9219
0
                              UploadProgress progress) {
9220
0
  return send_with_content_provider("PUT", path, headers, nullptr,
9221
0
                                    content_length, std::move(content_provider),
9222
0
                                    nullptr, content_type, progress);
9223
0
}
9224
9225
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
9226
                              ContentProviderWithoutLength content_provider,
9227
                              const std::string &content_type,
9228
0
                              UploadProgress progress) {
9229
0
  return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
9230
0
                                    std::move(content_provider), content_type,
9231
0
                                    progress);
9232
0
}
9233
9234
inline Result
9235
ClientImpl::Put(const std::string &path, const Headers &headers,
9236
                const MultipartFormDataItemsForClientInput &items,
9237
                const MultipartFormDataProviderItems &provider_items,
9238
0
                UploadProgress progress) {
9239
0
  const auto &boundary = detail::make_multipart_data_boundary();
9240
0
  const auto &content_type =
9241
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9242
0
  return send_with_content_provider(
9243
0
      "PUT", path, headers, nullptr, 0, nullptr,
9244
0
      get_multipart_content_provider(boundary, items, provider_items),
9245
0
      content_type, progress);
9246
0
}
9247
9248
0
inline Result ClientImpl::Patch(const std::string &path) {
9249
0
  return Patch(path, std::string(), std::string());
9250
0
}
9251
9252
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
9253
0
                                UploadProgress progress) {
9254
0
  return Patch(path, headers, nullptr, 0, std::string(), progress);
9255
0
}
9256
9257
inline Result ClientImpl::Patch(const std::string &path, const char *body,
9258
                                size_t content_length,
9259
                                const std::string &content_type,
9260
0
                                UploadProgress progress) {
9261
0
  return Patch(path, Headers(), body, content_length, content_type, progress);
9262
0
}
9263
9264
inline Result ClientImpl::Patch(const std::string &path,
9265
                                const std::string &body,
9266
                                const std::string &content_type,
9267
0
                                UploadProgress progress) {
9268
0
  return Patch(path, Headers(), body, content_type, progress);
9269
0
}
9270
9271
0
inline Result ClientImpl::Patch(const std::string &path, const Params &params) {
9272
0
  return Patch(path, Headers(), params);
9273
0
}
9274
9275
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
9276
                                ContentProvider content_provider,
9277
                                const std::string &content_type,
9278
0
                                UploadProgress progress) {
9279
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
9280
0
               content_type, progress);
9281
0
}
9282
9283
inline Result ClientImpl::Patch(const std::string &path,
9284
                                ContentProviderWithoutLength content_provider,
9285
                                const std::string &content_type,
9286
0
                                UploadProgress progress) {
9287
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
9288
0
               progress);
9289
0
}
9290
9291
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
9292
0
                                const Params &params) {
9293
0
  auto query = detail::params_to_query_str(params);
9294
0
  return Patch(path, headers, query, "application/x-www-form-urlencoded");
9295
0
}
9296
9297
inline Result
9298
ClientImpl::Patch(const std::string &path,
9299
                  const MultipartFormDataItemsForClientInput &items,
9300
0
                  UploadProgress progress) {
9301
0
  return Patch(path, Headers(), items, progress);
9302
0
}
9303
9304
inline Result
9305
ClientImpl::Patch(const std::string &path, const Headers &headers,
9306
                  const MultipartFormDataItemsForClientInput &items,
9307
0
                  UploadProgress progress) {
9308
0
  const auto &boundary = detail::make_multipart_data_boundary();
9309
0
  const auto &content_type =
9310
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9311
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
9312
0
  return Patch(path, headers, body, content_type, progress);
9313
0
}
9314
9315
inline Result
9316
ClientImpl::Patch(const std::string &path, const Headers &headers,
9317
                  const MultipartFormDataItemsForClientInput &items,
9318
0
                  const std::string &boundary, UploadProgress progress) {
9319
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
9320
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
9321
0
  }
9322
0
9323
0
  const auto &content_type =
9324
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9325
0
  const auto &body = detail::serialize_multipart_formdata(items, boundary);
9326
0
  return Patch(path, headers, body, content_type, progress);
9327
0
}
9328
9329
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
9330
                                const char *body, size_t content_length,
9331
                                const std::string &content_type,
9332
0
                                UploadProgress progress) {
9333
0
  return send_with_content_provider("PATCH", path, headers, body,
9334
0
                                    content_length, nullptr, nullptr,
9335
0
                                    content_type, progress);
9336
0
}
9337
9338
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
9339
                                const std::string &body,
9340
                                const std::string &content_type,
9341
0
                                UploadProgress progress) {
9342
0
  return send_with_content_provider("PATCH", path, headers, body.data(),
9343
0
                                    body.size(), nullptr, nullptr, content_type,
9344
0
                                    progress);
9345
0
}
9346
9347
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
9348
                                size_t content_length,
9349
                                ContentProvider content_provider,
9350
                                const std::string &content_type,
9351
0
                                UploadProgress progress) {
9352
0
  return send_with_content_provider("PATCH", path, headers, nullptr,
9353
0
                                    content_length, std::move(content_provider),
9354
0
                                    nullptr, content_type, progress);
9355
0
}
9356
9357
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
9358
                                ContentProviderWithoutLength content_provider,
9359
                                const std::string &content_type,
9360
0
                                UploadProgress progress) {
9361
0
  return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
9362
0
                                    std::move(content_provider), content_type,
9363
0
                                    progress);
9364
0
}
9365
9366
inline Result
9367
ClientImpl::Patch(const std::string &path, const Headers &headers,
9368
                  const MultipartFormDataItemsForClientInput &items,
9369
                  const MultipartFormDataProviderItems &provider_items,
9370
0
                  UploadProgress progress) {
9371
0
  const auto &boundary = detail::make_multipart_data_boundary();
9372
0
  const auto &content_type =
9373
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
9374
0
  return send_with_content_provider(
9375
0
      "PATCH", path, headers, nullptr, 0, nullptr,
9376
0
      get_multipart_content_provider(boundary, items, provider_items),
9377
0
      content_type, progress);
9378
0
}
9379
9380
inline Result ClientImpl::Delete(const std::string &path,
9381
0
                                 DownloadProgress progress) {
9382
0
  return Delete(path, Headers(), std::string(), std::string(), progress);
9383
0
}
9384
9385
inline Result ClientImpl::Delete(const std::string &path,
9386
                                 const Headers &headers,
9387
0
                                 DownloadProgress progress) {
9388
0
  return Delete(path, headers, std::string(), std::string(), progress);
9389
0
}
9390
9391
inline Result ClientImpl::Delete(const std::string &path, const char *body,
9392
                                 size_t content_length,
9393
                                 const std::string &content_type,
9394
0
                                 DownloadProgress progress) {
9395
0
  return Delete(path, Headers(), body, content_length, content_type, progress);
9396
0
}
9397
9398
inline Result ClientImpl::Delete(const std::string &path,
9399
                                 const std::string &body,
9400
                                 const std::string &content_type,
9401
0
                                 DownloadProgress progress) {
9402
0
  return Delete(path, Headers(), body.data(), body.size(), content_type,
9403
0
                progress);
9404
0
}
9405
9406
inline Result ClientImpl::Delete(const std::string &path,
9407
                                 const Headers &headers,
9408
                                 const std::string &body,
9409
                                 const std::string &content_type,
9410
0
                                 DownloadProgress progress) {
9411
0
  return Delete(path, headers, body.data(), body.size(), content_type,
9412
0
                progress);
9413
0
}
9414
9415
inline Result ClientImpl::Delete(const std::string &path, const Params &params,
9416
0
                                 DownloadProgress progress) {
9417
0
  return Delete(path, Headers(), params, progress);
9418
0
}
9419
9420
inline Result ClientImpl::Delete(const std::string &path,
9421
                                 const Headers &headers, const Params &params,
9422
0
                                 DownloadProgress progress) {
9423
0
  auto query = detail::params_to_query_str(params);
9424
0
  return Delete(path, headers, query, "application/x-www-form-urlencoded",
9425
0
                progress);
9426
0
}
9427
9428
inline Result ClientImpl::Delete(const std::string &path,
9429
                                 const Headers &headers, const char *body,
9430
                                 size_t content_length,
9431
                                 const std::string &content_type,
9432
0
                                 DownloadProgress progress) {
9433
0
  Request req;
9434
0
  req.method = "DELETE";
9435
0
  req.headers = headers;
9436
0
  req.path = path;
9437
0
  req.download_progress = std::move(progress);
9438
0
  if (max_timeout_msec_ > 0) {
9439
0
    req.start_time_ = std::chrono::steady_clock::now();
9440
0
  }
9441
0
9442
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
9443
0
  req.body.assign(body, content_length);
9444
0
9445
0
  return send_(std::move(req));
9446
0
}
9447
9448
0
inline Result ClientImpl::Options(const std::string &path) {
9449
0
  return Options(path, Headers());
9450
0
}
9451
9452
inline Result ClientImpl::Options(const std::string &path,
9453
0
                                  const Headers &headers) {
9454
0
  Request req;
9455
0
  req.method = "OPTIONS";
9456
0
  req.headers = headers;
9457
0
  req.path = path;
9458
0
  if (max_timeout_msec_ > 0) {
9459
0
    req.start_time_ = std::chrono::steady_clock::now();
9460
0
  }
9461
0
9462
0
  return send_(std::move(req));
9463
0
}
9464
9465
0
inline void ClientImpl::stop() {
9466
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
9467
0
9468
0
  // If there is anything ongoing right now, the ONLY thread-safe thing we can
9469
0
  // do is to shutdown_socket, so that threads using this socket suddenly
9470
0
  // discover they can't read/write any more and error out. Everything else
9471
0
  // (closing the socket, shutting ssl down) is unsafe because these actions are
9472
0
  // not thread-safe.
9473
0
  if (socket_requests_in_flight_ > 0) {
9474
0
    shutdown_socket(socket_);
9475
0
9476
0
    // Aside from that, we set a flag for the socket to be closed when we're
9477
0
    // done.
9478
0
    socket_should_be_closed_when_request_is_done_ = true;
9479
0
    return;
9480
0
  }
9481
0
9482
0
  // Otherwise, still holding the mutex, we can shut everything down ourselves
9483
0
  shutdown_ssl(socket_, true);
9484
0
  shutdown_socket(socket_);
9485
0
  close_socket(socket_);
9486
0
}
9487
9488
0
inline std::string ClientImpl::host() const { return host_; }
9489
9490
0
inline int ClientImpl::port() const { return port_; }
9491
9492
0
inline size_t ClientImpl::is_socket_open() const {
9493
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
9494
0
  return socket_.is_open();
9495
0
}
9496
9497
0
inline socket_t ClientImpl::socket() const { return socket_.sock; }
9498
9499
0
inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
9500
0
  connection_timeout_sec_ = sec;
9501
0
  connection_timeout_usec_ = usec;
9502
0
}
9503
9504
0
inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
9505
0
  read_timeout_sec_ = sec;
9506
0
  read_timeout_usec_ = usec;
9507
0
}
9508
9509
0
inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
9510
0
  write_timeout_sec_ = sec;
9511
0
  write_timeout_usec_ = usec;
9512
0
}
9513
9514
0
inline void ClientImpl::set_max_timeout(time_t msec) {
9515
0
  max_timeout_msec_ = msec;
9516
0
}
9517
9518
inline void ClientImpl::set_basic_auth(const std::string &username,
9519
0
                                       const std::string &password) {
9520
0
  basic_auth_username_ = username;
9521
0
  basic_auth_password_ = password;
9522
0
}
9523
9524
0
inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
9525
0
  bearer_token_auth_token_ = token;
9526
0
}
9527
9528
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9529
inline void ClientImpl::set_digest_auth(const std::string &username,
9530
                                        const std::string &password) {
9531
  digest_auth_username_ = username;
9532
  digest_auth_password_ = password;
9533
}
9534
#endif
9535
9536
0
inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
9537
9538
0
inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
9539
9540
0
inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
9541
9542
inline void
9543
0
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
9544
0
  addr_map_ = std::move(addr_map);
9545
0
}
9546
9547
0
inline void ClientImpl::set_default_headers(Headers headers) {
9548
0
  default_headers_ = std::move(headers);
9549
0
}
9550
9551
inline void ClientImpl::set_header_writer(
9552
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
9553
0
  header_writer_ = writer;
9554
0
}
9555
9556
0
inline void ClientImpl::set_address_family(int family) {
9557
0
  address_family_ = family;
9558
0
}
9559
9560
0
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
9561
9562
0
inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
9563
9564
0
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
9565
0
  socket_options_ = std::move(socket_options);
9566
0
}
9567
9568
0
inline void ClientImpl::set_compress(bool on) { compress_ = on; }
9569
9570
0
inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
9571
9572
0
inline void ClientImpl::set_interface(const std::string &intf) {
9573
0
  interface_ = intf;
9574
0
}
9575
9576
0
inline void ClientImpl::set_proxy(const std::string &host, int port) {
9577
0
  proxy_host_ = host;
9578
0
  proxy_port_ = port;
9579
0
}
9580
9581
inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
9582
0
                                             const std::string &password) {
9583
0
  proxy_basic_auth_username_ = username;
9584
0
  proxy_basic_auth_password_ = password;
9585
0
}
9586
9587
0
inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
9588
0
  proxy_bearer_token_auth_token_ = token;
9589
0
}
9590
9591
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9592
inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
9593
                                              const std::string &password) {
9594
  proxy_digest_auth_username_ = username;
9595
  proxy_digest_auth_password_ = password;
9596
}
9597
9598
inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
9599
                                         const std::string &ca_cert_dir_path) {
9600
  ca_cert_file_path_ = ca_cert_file_path;
9601
  ca_cert_dir_path_ = ca_cert_dir_path;
9602
}
9603
9604
inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
9605
  if (ca_cert_store && ca_cert_store != ca_cert_store_) {
9606
    ca_cert_store_ = ca_cert_store;
9607
  }
9608
}
9609
9610
inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
9611
                                                    std::size_t size) const {
9612
  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
9613
  auto se = detail::scope_exit([&] { BIO_free_all(mem); });
9614
  if (!mem) { return nullptr; }
9615
9616
  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
9617
  if (!inf) { return nullptr; }
9618
9619
  auto cts = X509_STORE_new();
9620
  if (cts) {
9621
    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
9622
      auto itmp = sk_X509_INFO_value(inf, i);
9623
      if (!itmp) { continue; }
9624
9625
      if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
9626
      if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
9627
    }
9628
  }
9629
9630
  sk_X509_INFO_pop_free(inf, X509_INFO_free);
9631
  return cts;
9632
}
9633
9634
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
9635
  server_certificate_verification_ = enabled;
9636
}
9637
9638
inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
9639
  server_hostname_verification_ = enabled;
9640
}
9641
9642
inline void ClientImpl::set_server_certificate_verifier(
9643
    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
9644
  server_certificate_verifier_ = verifier;
9645
}
9646
#endif
9647
9648
0
inline void ClientImpl::set_logger(Logger logger) {
9649
0
  logger_ = std::move(logger);
9650
0
}
9651
9652
/*
9653
 * SSL Implementation
9654
 */
9655
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9656
namespace detail {
9657
9658
template <typename U, typename V>
9659
inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
9660
                    U SSL_connect_or_accept, V setup) {
9661
  SSL *ssl = nullptr;
9662
  {
9663
    std::lock_guard<std::mutex> guard(ctx_mutex);
9664
    ssl = SSL_new(ctx);
9665
  }
9666
9667
  if (ssl) {
9668
    set_nonblocking(sock, true);
9669
    auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
9670
    BIO_set_nbio(bio, 1);
9671
    SSL_set_bio(ssl, bio, bio);
9672
9673
    if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
9674
      SSL_shutdown(ssl);
9675
      {
9676
        std::lock_guard<std::mutex> guard(ctx_mutex);
9677
        SSL_free(ssl);
9678
      }
9679
      set_nonblocking(sock, false);
9680
      return nullptr;
9681
    }
9682
    BIO_set_nbio(bio, 0);
9683
    set_nonblocking(sock, false);
9684
  }
9685
9686
  return ssl;
9687
}
9688
9689
inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
9690
                       bool shutdown_gracefully) {
9691
  // sometimes we may want to skip this to try to avoid SIGPIPE if we know
9692
  // the remote has closed the network connection
9693
  // Note that it is not always possible to avoid SIGPIPE, this is merely a
9694
  // best-efforts.
9695
  if (shutdown_gracefully) {
9696
    (void)(sock);
9697
    // SSL_shutdown() returns 0 on first call (indicating close_notify alert
9698
    // sent) and 1 on subsequent call (indicating close_notify alert received)
9699
    if (SSL_shutdown(ssl) == 0) {
9700
      // Expected to return 1, but even if it doesn't, we free ssl
9701
      SSL_shutdown(ssl);
9702
    }
9703
  }
9704
9705
  std::lock_guard<std::mutex> guard(ctx_mutex);
9706
  SSL_free(ssl);
9707
}
9708
9709
template <typename U>
9710
bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
9711
                                       U ssl_connect_or_accept,
9712
                                       time_t timeout_sec,
9713
                                       time_t timeout_usec) {
9714
  auto res = 0;
9715
  while ((res = ssl_connect_or_accept(ssl)) != 1) {
9716
    auto err = SSL_get_error(ssl, res);
9717
    switch (err) {
9718
    case SSL_ERROR_WANT_READ:
9719
      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
9720
      break;
9721
    case SSL_ERROR_WANT_WRITE:
9722
      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
9723
      break;
9724
    default: break;
9725
    }
9726
    return false;
9727
  }
9728
  return true;
9729
}
9730
9731
template <typename T>
9732
inline bool process_server_socket_ssl(
9733
    const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
9734
    size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
9735
    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
9736
    time_t write_timeout_usec, T callback) {
9737
  return process_server_socket_core(
9738
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
9739
      [&](bool close_connection, bool &connection_closed) {
9740
        SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
9741
                             write_timeout_sec, write_timeout_usec);
9742
        return callback(strm, close_connection, connection_closed);
9743
      });
9744
}
9745
9746
template <typename T>
9747
inline bool process_client_socket_ssl(
9748
    SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
9749
    time_t write_timeout_sec, time_t write_timeout_usec,
9750
    time_t max_timeout_msec,
9751
    std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
9752
  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
9753
                       write_timeout_sec, write_timeout_usec, max_timeout_msec,
9754
                       start_time);
9755
  return callback(strm);
9756
}
9757
9758
// SSL socket stream implementation
9759
inline SSLSocketStream::SSLSocketStream(
9760
    socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
9761
    time_t write_timeout_sec, time_t write_timeout_usec,
9762
    time_t max_timeout_msec,
9763
    std::chrono::time_point<std::chrono::steady_clock> start_time)
9764
    : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
9765
      read_timeout_usec_(read_timeout_usec),
9766
      write_timeout_sec_(write_timeout_sec),
9767
      write_timeout_usec_(write_timeout_usec),
9768
      max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
9769
  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
9770
}
9771
9772
inline SSLSocketStream::~SSLSocketStream() = default;
9773
9774
inline bool SSLSocketStream::is_readable() const {
9775
  return SSL_pending(ssl_) > 0;
9776
}
9777
9778
inline bool SSLSocketStream::wait_readable() const {
9779
  if (max_timeout_msec_ <= 0) {
9780
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
9781
  }
9782
9783
  time_t read_timeout_sec;
9784
  time_t read_timeout_usec;
9785
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
9786
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
9787
9788
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
9789
}
9790
9791
inline bool SSLSocketStream::wait_writable() const {
9792
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9793
         is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
9794
}
9795
9796
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
9797
  if (SSL_pending(ssl_) > 0) {
9798
    return SSL_read(ssl_, ptr, static_cast<int>(size));
9799
  } else if (wait_readable()) {
9800
    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
9801
    if (ret < 0) {
9802
      auto err = SSL_get_error(ssl_, ret);
9803
      auto n = 1000;
9804
#ifdef _WIN32
9805
      while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
9806
                          (err == SSL_ERROR_SYSCALL &&
9807
                           WSAGetLastError() == WSAETIMEDOUT))) {
9808
#else
9809
      while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
9810
#endif
9811
        if (SSL_pending(ssl_) > 0) {
9812
          return SSL_read(ssl_, ptr, static_cast<int>(size));
9813
        } else if (wait_readable()) {
9814
          std::this_thread::sleep_for(std::chrono::microseconds{10});
9815
          ret = SSL_read(ssl_, ptr, static_cast<int>(size));
9816
          if (ret >= 0) { return ret; }
9817
          err = SSL_get_error(ssl_, ret);
9818
        } else {
9819
          return -1;
9820
        }
9821
      }
9822
    }
9823
    return ret;
9824
  } else {
9825
    return -1;
9826
  }
9827
}
9828
9829
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
9830
  if (wait_writable()) {
9831
    auto handle_size = static_cast<int>(
9832
        std::min<size_t>(size, (std::numeric_limits<int>::max)()));
9833
9834
    auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
9835
    if (ret < 0) {
9836
      auto err = SSL_get_error(ssl_, ret);
9837
      auto n = 1000;
9838
#ifdef _WIN32
9839
      while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
9840
                          (err == SSL_ERROR_SYSCALL &&
9841
                           WSAGetLastError() == WSAETIMEDOUT))) {
9842
#else
9843
      while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
9844
#endif
9845
        if (wait_writable()) {
9846
          std::this_thread::sleep_for(std::chrono::microseconds{10});
9847
          ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
9848
          if (ret >= 0) { return ret; }
9849
          err = SSL_get_error(ssl_, ret);
9850
        } else {
9851
          return -1;
9852
        }
9853
      }
9854
    }
9855
    return ret;
9856
  }
9857
  return -1;
9858
}
9859
9860
inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
9861
                                                    int &port) const {
9862
  detail::get_remote_ip_and_port(sock_, ip, port);
9863
}
9864
9865
inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
9866
                                                   int &port) const {
9867
  detail::get_local_ip_and_port(sock_, ip, port);
9868
}
9869
9870
inline socket_t SSLSocketStream::socket() const { return sock_; }
9871
9872
inline time_t SSLSocketStream::duration() const {
9873
  return std::chrono::duration_cast<std::chrono::milliseconds>(
9874
             std::chrono::steady_clock::now() - start_time_)
9875
      .count();
9876
}
9877
9878
} // namespace detail
9879
9880
// SSL HTTP server implementation
9881
inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
9882
                            const char *client_ca_cert_file_path,
9883
                            const char *client_ca_cert_dir_path,
9884
                            const char *private_key_password) {
9885
  ctx_ = SSL_CTX_new(TLS_server_method());
9886
9887
  if (ctx_) {
9888
    SSL_CTX_set_options(ctx_,
9889
                        SSL_OP_NO_COMPRESSION |
9890
                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9891
9892
    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9893
9894
    if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
9895
      SSL_CTX_set_default_passwd_cb_userdata(
9896
          ctx_,
9897
          reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
9898
    }
9899
9900
    if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
9901
        SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
9902
            1 ||
9903
        SSL_CTX_check_private_key(ctx_) != 1) {
9904
      SSL_CTX_free(ctx_);
9905
      ctx_ = nullptr;
9906
    } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
9907
      SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
9908
                                    client_ca_cert_dir_path);
9909
9910
      SSL_CTX_set_verify(
9911
          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
9912
    }
9913
  }
9914
}
9915
9916
inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
9917
                            X509_STORE *client_ca_cert_store) {
9918
  ctx_ = SSL_CTX_new(TLS_server_method());
9919
9920
  if (ctx_) {
9921
    SSL_CTX_set_options(ctx_,
9922
                        SSL_OP_NO_COMPRESSION |
9923
                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
9924
9925
    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9926
9927
    if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
9928
        SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
9929
      SSL_CTX_free(ctx_);
9930
      ctx_ = nullptr;
9931
    } else if (client_ca_cert_store) {
9932
      SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9933
9934
      SSL_CTX_set_verify(
9935
          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
9936
    }
9937
  }
9938
}
9939
9940
inline SSLServer::SSLServer(
9941
    const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
9942
  ctx_ = SSL_CTX_new(TLS_method());
9943
  if (ctx_) {
9944
    if (!setup_ssl_ctx_callback(*ctx_)) {
9945
      SSL_CTX_free(ctx_);
9946
      ctx_ = nullptr;
9947
    }
9948
  }
9949
}
9950
9951
inline SSLServer::~SSLServer() {
9952
  if (ctx_) { SSL_CTX_free(ctx_); }
9953
}
9954
9955
inline bool SSLServer::is_valid() const { return ctx_; }
9956
9957
inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
9958
9959
inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
9960
                                    X509_STORE *client_ca_cert_store) {
9961
9962
  std::lock_guard<std::mutex> guard(ctx_mutex_);
9963
9964
  SSL_CTX_use_certificate(ctx_, cert);
9965
  SSL_CTX_use_PrivateKey(ctx_, private_key);
9966
9967
  if (client_ca_cert_store != nullptr) {
9968
    SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9969
  }
9970
}
9971
9972
inline bool SSLServer::process_and_close_socket(socket_t sock) {
9973
  auto ssl = detail::ssl_new(
9974
      sock, ctx_, ctx_mutex_,
9975
      [&](SSL *ssl2) {
9976
        return detail::ssl_connect_or_accept_nonblocking(
9977
            sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
9978
      },
9979
      [](SSL * /*ssl2*/) { return true; });
9980
9981
  auto ret = false;
9982
  if (ssl) {
9983
    std::string remote_addr;
9984
    int remote_port = 0;
9985
    detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
9986
9987
    std::string local_addr;
9988
    int local_port = 0;
9989
    detail::get_local_ip_and_port(sock, local_addr, local_port);
9990
9991
    ret = detail::process_server_socket_ssl(
9992
        svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
9993
        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
9994
        write_timeout_usec_,
9995
        [&](Stream &strm, bool close_connection, bool &connection_closed) {
9996
          return process_request(strm, remote_addr, remote_port, local_addr,
9997
                                 local_port, close_connection,
9998
                                 connection_closed,
9999
                                 [&](Request &req) { req.ssl = ssl; });
10000
        });
10001
10002
    // Shutdown gracefully if the result seemed successful, non-gracefully if
10003
    // the connection appeared to be closed.
10004
    const bool shutdown_gracefully = ret;
10005
    detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
10006
  }
10007
10008
  detail::shutdown_socket(sock);
10009
  detail::close_socket(sock);
10010
  return ret;
10011
}
10012
10013
// SSL HTTP client implementation
10014
inline SSLClient::SSLClient(const std::string &host)
10015
    : SSLClient(host, 443, std::string(), std::string()) {}
10016
10017
inline SSLClient::SSLClient(const std::string &host, int port)
10018
    : SSLClient(host, port, std::string(), std::string()) {}
10019
10020
inline SSLClient::SSLClient(const std::string &host, int port,
10021
                            const std::string &client_cert_path,
10022
                            const std::string &client_key_path,
10023
                            const std::string &private_key_password)
10024
    : ClientImpl(host, port, client_cert_path, client_key_path) {
10025
  ctx_ = SSL_CTX_new(TLS_client_method());
10026
10027
  SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
10028
10029
  detail::split(&host_[0], &host_[host_.size()], '.',
10030
                [&](const char *b, const char *e) {
10031
                  host_components_.emplace_back(b, e);
10032
                });
10033
10034
  if (!client_cert_path.empty() && !client_key_path.empty()) {
10035
    if (!private_key_password.empty()) {
10036
      SSL_CTX_set_default_passwd_cb_userdata(
10037
          ctx_, reinterpret_cast<void *>(
10038
                    const_cast<char *>(private_key_password.c_str())));
10039
    }
10040
10041
    if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
10042
                                     SSL_FILETYPE_PEM) != 1 ||
10043
        SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
10044
                                    SSL_FILETYPE_PEM) != 1) {
10045
      SSL_CTX_free(ctx_);
10046
      ctx_ = nullptr;
10047
    }
10048
  }
10049
}
10050
10051
inline SSLClient::SSLClient(const std::string &host, int port,
10052
                            X509 *client_cert, EVP_PKEY *client_key,
10053
                            const std::string &private_key_password)
10054
    : ClientImpl(host, port) {
10055
  ctx_ = SSL_CTX_new(TLS_client_method());
10056
10057
  detail::split(&host_[0], &host_[host_.size()], '.',
10058
                [&](const char *b, const char *e) {
10059
                  host_components_.emplace_back(b, e);
10060
                });
10061
10062
  if (client_cert != nullptr && client_key != nullptr) {
10063
    if (!private_key_password.empty()) {
10064
      SSL_CTX_set_default_passwd_cb_userdata(
10065
          ctx_, reinterpret_cast<void *>(
10066
                    const_cast<char *>(private_key_password.c_str())));
10067
    }
10068
10069
    if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
10070
        SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
10071
      SSL_CTX_free(ctx_);
10072
      ctx_ = nullptr;
10073
    }
10074
  }
10075
}
10076
10077
inline SSLClient::~SSLClient() {
10078
  if (ctx_) { SSL_CTX_free(ctx_); }
10079
  // Make sure to shut down SSL since shutdown_ssl will resolve to the
10080
  // base function rather than the derived function once we get to the
10081
  // base class destructor, and won't free the SSL (causing a leak).
10082
  shutdown_ssl_impl(socket_, true);
10083
}
10084
10085
inline bool SSLClient::is_valid() const { return ctx_; }
10086
10087
inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
10088
  if (ca_cert_store) {
10089
    if (ctx_) {
10090
      if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
10091
        // Free memory allocated for old cert and use new store `ca_cert_store`
10092
        SSL_CTX_set_cert_store(ctx_, ca_cert_store);
10093
      }
10094
    } else {
10095
      X509_STORE_free(ca_cert_store);
10096
    }
10097
  }
10098
}
10099
10100
inline void SSLClient::load_ca_cert_store(const char *ca_cert,
10101
                                          std::size_t size) {
10102
  set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
10103
}
10104
10105
inline long SSLClient::get_openssl_verify_result() const {
10106
  return verify_result_;
10107
}
10108
10109
inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
10110
10111
inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
10112
  return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
10113
}
10114
10115
// Assumes that socket_mutex_ is locked and that there are no requests in flight
10116
inline bool SSLClient::connect_with_proxy(
10117
    Socket &socket,
10118
    std::chrono::time_point<std::chrono::steady_clock> start_time,
10119
    Response &res, bool &success, Error &error) {
10120
  success = true;
10121
  Response proxy_res;
10122
  if (!detail::process_client_socket(
10123
          socket.sock, read_timeout_sec_, read_timeout_usec_,
10124
          write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
10125
          start_time, [&](Stream &strm) {
10126
            Request req2;
10127
            req2.method = "CONNECT";
10128
            req2.path = host_and_port_;
10129
            if (max_timeout_msec_ > 0) {
10130
              req2.start_time_ = std::chrono::steady_clock::now();
10131
            }
10132
            return process_request(strm, req2, proxy_res, false, error);
10133
          })) {
10134
    // Thread-safe to close everything because we are assuming there are no
10135
    // requests in flight
10136
    shutdown_ssl(socket, true);
10137
    shutdown_socket(socket);
10138
    close_socket(socket);
10139
    success = false;
10140
    return false;
10141
  }
10142
10143
  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
10144
    if (!proxy_digest_auth_username_.empty() &&
10145
        !proxy_digest_auth_password_.empty()) {
10146
      std::map<std::string, std::string> auth;
10147
      if (detail::parse_www_authenticate(proxy_res, auth, true)) {
10148
        // Close the current socket and create a new one for the authenticated
10149
        // request
10150
        shutdown_ssl(socket, true);
10151
        shutdown_socket(socket);
10152
        close_socket(socket);
10153
10154
        // Create a new socket for the authenticated CONNECT request
10155
        if (!create_and_connect_socket(socket, error)) {
10156
          success = false;
10157
          return false;
10158
        }
10159
10160
        proxy_res = Response();
10161
        if (!detail::process_client_socket(
10162
                socket.sock, read_timeout_sec_, read_timeout_usec_,
10163
                write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
10164
                start_time, [&](Stream &strm) {
10165
                  Request req3;
10166
                  req3.method = "CONNECT";
10167
                  req3.path = host_and_port_;
10168
                  req3.headers.insert(detail::make_digest_authentication_header(
10169
                      req3, auth, 1, detail::random_string(10),
10170
                      proxy_digest_auth_username_, proxy_digest_auth_password_,
10171
                      true));
10172
                  if (max_timeout_msec_ > 0) {
10173
                    req3.start_time_ = std::chrono::steady_clock::now();
10174
                  }
10175
                  return process_request(strm, req3, proxy_res, false, error);
10176
                })) {
10177
          // Thread-safe to close everything because we are assuming there are
10178
          // no requests in flight
10179
          shutdown_ssl(socket, true);
10180
          shutdown_socket(socket);
10181
          close_socket(socket);
10182
          success = false;
10183
          return false;
10184
        }
10185
      }
10186
    }
10187
  }
10188
10189
  // If status code is not 200, proxy request is failed.
10190
  // Set error to ProxyConnection and return proxy response
10191
  // as the response of the request
10192
  if (proxy_res.status != StatusCode::OK_200) {
10193
    error = Error::ProxyConnection;
10194
    res = std::move(proxy_res);
10195
    // Thread-safe to close everything because we are assuming there are
10196
    // no requests in flight
10197
    shutdown_ssl(socket, true);
10198
    shutdown_socket(socket);
10199
    close_socket(socket);
10200
    return false;
10201
  }
10202
10203
  return true;
10204
}
10205
10206
inline bool SSLClient::load_certs() {
10207
  auto ret = true;
10208
10209
  std::call_once(initialize_cert_, [&]() {
10210
    std::lock_guard<std::mutex> guard(ctx_mutex_);
10211
    if (!ca_cert_file_path_.empty()) {
10212
      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
10213
                                         nullptr)) {
10214
        ret = false;
10215
      }
10216
    } else if (!ca_cert_dir_path_.empty()) {
10217
      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
10218
                                         ca_cert_dir_path_.c_str())) {
10219
        ret = false;
10220
      }
10221
    } else {
10222
      auto loaded = false;
10223
#ifdef _WIN32
10224
      loaded =
10225
          detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
10226
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
10227
#if TARGET_OS_OSX
10228
      loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
10229
#endif // TARGET_OS_OSX
10230
#endif // _WIN32
10231
      if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
10232
    }
10233
  });
10234
10235
  return ret;
10236
}
10237
10238
inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
10239
  auto ssl = detail::ssl_new(
10240
      socket.sock, ctx_, ctx_mutex_,
10241
      [&](SSL *ssl2) {
10242
        if (server_certificate_verification_) {
10243
          if (!load_certs()) {
10244
            error = Error::SSLLoadingCerts;
10245
            return false;
10246
          }
10247
          SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
10248
        }
10249
10250
        if (!detail::ssl_connect_or_accept_nonblocking(
10251
                socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
10252
                connection_timeout_usec_)) {
10253
          error = Error::SSLConnection;
10254
          return false;
10255
        }
10256
10257
        if (server_certificate_verification_) {
10258
          auto verification_status = SSLVerifierResponse::NoDecisionMade;
10259
10260
          if (server_certificate_verifier_) {
10261
            verification_status = server_certificate_verifier_(ssl2);
10262
          }
10263
10264
          if (verification_status == SSLVerifierResponse::CertificateRejected) {
10265
            error = Error::SSLServerVerification;
10266
            return false;
10267
          }
10268
10269
          if (verification_status == SSLVerifierResponse::NoDecisionMade) {
10270
            verify_result_ = SSL_get_verify_result(ssl2);
10271
10272
            if (verify_result_ != X509_V_OK) {
10273
              error = Error::SSLServerVerification;
10274
              return false;
10275
            }
10276
10277
            auto server_cert = SSL_get1_peer_certificate(ssl2);
10278
            auto se = detail::scope_exit([&] { X509_free(server_cert); });
10279
10280
            if (server_cert == nullptr) {
10281
              error = Error::SSLServerVerification;
10282
              return false;
10283
            }
10284
10285
            if (server_hostname_verification_) {
10286
              if (!verify_host(server_cert)) {
10287
                error = Error::SSLServerHostnameVerification;
10288
                return false;
10289
              }
10290
            }
10291
          }
10292
        }
10293
10294
        return true;
10295
      },
10296
      [&](SSL *ssl2) {
10297
#if defined(OPENSSL_IS_BORINGSSL)
10298
        SSL_set_tlsext_host_name(ssl2, host_.c_str());
10299
#else
10300
        // NOTE: Direct call instead of using the OpenSSL macro to suppress
10301
        // -Wold-style-cast warning
10302
        SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
10303
                 static_cast<void *>(const_cast<char *>(host_.c_str())));
10304
#endif
10305
        return true;
10306
      });
10307
10308
  if (ssl) {
10309
    socket.ssl = ssl;
10310
    return true;
10311
  }
10312
10313
  shutdown_socket(socket);
10314
  close_socket(socket);
10315
  return false;
10316
}
10317
10318
inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
10319
  shutdown_ssl_impl(socket, shutdown_gracefully);
10320
}
10321
10322
inline void SSLClient::shutdown_ssl_impl(Socket &socket,
10323
                                         bool shutdown_gracefully) {
10324
  if (socket.sock == INVALID_SOCKET) {
10325
    assert(socket.ssl == nullptr);
10326
    return;
10327
  }
10328
  if (socket.ssl) {
10329
    detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
10330
                       shutdown_gracefully);
10331
    socket.ssl = nullptr;
10332
  }
10333
  assert(socket.ssl == nullptr);
10334
}
10335
10336
inline bool SSLClient::process_socket(
10337
    const Socket &socket,
10338
    std::chrono::time_point<std::chrono::steady_clock> start_time,
10339
    std::function<bool(Stream &strm)> callback) {
10340
  assert(socket.ssl);
10341
  return detail::process_client_socket_ssl(
10342
      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
10343
      write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
10344
      std::move(callback));
10345
}
10346
10347
inline bool SSLClient::is_ssl() const { return true; }
10348
10349
inline bool SSLClient::verify_host(X509 *server_cert) const {
10350
  /* Quote from RFC2818 section 3.1 "Server Identity"
10351
10352
     If a subjectAltName extension of type dNSName is present, that MUST
10353
     be used as the identity. Otherwise, the (most specific) Common Name
10354
     field in the Subject field of the certificate MUST be used. Although
10355
     the use of the Common Name is existing practice, it is deprecated and
10356
     Certification Authorities are encouraged to use the dNSName instead.
10357
10358
     Matching is performed using the matching rules specified by
10359
     [RFC2459].  If more than one identity of a given type is present in
10360
     the certificate (e.g., more than one dNSName name, a match in any one
10361
     of the set is considered acceptable.) Names may contain the wildcard
10362
     character * which is considered to match any single domain name
10363
     component or component fragment. E.g., *.a.com matches foo.a.com but
10364
     not bar.foo.a.com. f*.com matches foo.com but not bar.com.
10365
10366
     In some cases, the URI is specified as an IP address rather than a
10367
     hostname. In this case, the iPAddress subjectAltName must be present
10368
     in the certificate and must exactly match the IP in the URI.
10369
10370
  */
10371
  return verify_host_with_subject_alt_name(server_cert) ||
10372
         verify_host_with_common_name(server_cert);
10373
}
10374
10375
inline bool
10376
SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
10377
  auto ret = false;
10378
10379
  auto type = GEN_DNS;
10380
10381
  struct in6_addr addr6 = {};
10382
  struct in_addr addr = {};
10383
  size_t addr_len = 0;
10384
10385
#ifndef __MINGW32__
10386
  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
10387
    type = GEN_IPADD;
10388
    addr_len = sizeof(struct in6_addr);
10389
  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
10390
    type = GEN_IPADD;
10391
    addr_len = sizeof(struct in_addr);
10392
  }
10393
#endif
10394
10395
  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
10396
      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
10397
10398
  if (alt_names) {
10399
    auto dsn_matched = false;
10400
    auto ip_matched = false;
10401
10402
    auto count = sk_GENERAL_NAME_num(alt_names);
10403
10404
    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
10405
      auto val = sk_GENERAL_NAME_value(alt_names, i);
10406
      if (val->type == type) {
10407
        auto name =
10408
            reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
10409
        auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
10410
10411
        switch (type) {
10412
        case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
10413
10414
        case GEN_IPADD:
10415
          if (!memcmp(&addr6, name, addr_len) ||
10416
              !memcmp(&addr, name, addr_len)) {
10417
            ip_matched = true;
10418
          }
10419
          break;
10420
        }
10421
      }
10422
    }
10423
10424
    if (dsn_matched || ip_matched) { ret = true; }
10425
  }
10426
10427
  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
10428
      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
10429
  return ret;
10430
}
10431
10432
inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
10433
  const auto subject_name = X509_get_subject_name(server_cert);
10434
10435
  if (subject_name != nullptr) {
10436
    char name[BUFSIZ];
10437
    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
10438
                                              name, sizeof(name));
10439
10440
    if (name_len != -1) {
10441
      return check_host_name(name, static_cast<size_t>(name_len));
10442
    }
10443
  }
10444
10445
  return false;
10446
}
10447
10448
inline bool SSLClient::check_host_name(const char *pattern,
10449
                                       size_t pattern_len) const {
10450
  if (host_.size() == pattern_len && host_ == pattern) { return true; }
10451
10452
  // Wildcard match
10453
  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
10454
  std::vector<std::string> pattern_components;
10455
  detail::split(&pattern[0], &pattern[pattern_len], '.',
10456
                [&](const char *b, const char *e) {
10457
                  pattern_components.emplace_back(b, e);
10458
                });
10459
10460
  if (host_components_.size() != pattern_components.size()) { return false; }
10461
10462
  auto itr = pattern_components.begin();
10463
  for (const auto &h : host_components_) {
10464
    auto &p = *itr;
10465
    if (p != h && p != "*") {
10466
      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
10467
                            !p.compare(0, p.size() - 1, h));
10468
      if (!partial_match) { return false; }
10469
    }
10470
    ++itr;
10471
  }
10472
10473
  return true;
10474
}
10475
#endif
10476
10477
// Universal client implementation
10478
inline Client::Client(const std::string &scheme_host_port)
10479
    : Client(scheme_host_port, std::string(), std::string()) {}
10480
10481
inline Client::Client(const std::string &scheme_host_port,
10482
                      const std::string &client_cert_path,
10483
                      const std::string &client_key_path) {
10484
  const static std::regex re(
10485
      R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
10486
10487
  std::smatch m;
10488
  if (std::regex_match(scheme_host_port, m, re)) {
10489
    auto scheme = m[1].str();
10490
10491
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10492
    if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
10493
#else
10494
    if (!scheme.empty() && scheme != "http") {
10495
#endif
10496
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10497
      std::string msg = "'" + scheme + "' scheme is not supported.";
10498
      throw std::invalid_argument(msg);
10499
#endif
10500
      return;
10501
    }
10502
10503
    auto is_ssl = scheme == "https";
10504
10505
    auto host = m[2].str();
10506
    if (host.empty()) { host = m[3].str(); }
10507
10508
    auto port_str = m[4].str();
10509
    auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
10510
10511
    if (is_ssl) {
10512
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10513
      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
10514
                                            client_key_path);
10515
      is_ssl_ = is_ssl;
10516
#endif
10517
    } else {
10518
      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
10519
                                             client_key_path);
10520
    }
10521
  } else {
10522
    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
10523
    // if port param below changes.
10524
    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
10525
                                           client_cert_path, client_key_path);
10526
  }
10527
} // namespace detail
10528
10529
inline Client::Client(const std::string &host, int port)
10530
    : cli_(detail::make_unique<ClientImpl>(host, port)) {}
10531
10532
inline Client::Client(const std::string &host, int port,
10533
                      const std::string &client_cert_path,
10534
                      const std::string &client_key_path)
10535
    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
10536
                                           client_key_path)) {}
10537
10538
inline Client::~Client() = default;
10539
10540
0
inline bool Client::is_valid() const {
10541
0
  return cli_ != nullptr && cli_->is_valid();
10542
0
}
10543
10544
0
inline Result Client::Get(const std::string &path, DownloadProgress progress) {
10545
0
  return cli_->Get(path, std::move(progress));
10546
0
}
10547
inline Result Client::Get(const std::string &path, const Headers &headers,
10548
0
                          DownloadProgress progress) {
10549
0
  return cli_->Get(path, headers, std::move(progress));
10550
0
}
10551
inline Result Client::Get(const std::string &path,
10552
                          ContentReceiver content_receiver,
10553
0
                          DownloadProgress progress) {
10554
0
  return cli_->Get(path, std::move(content_receiver), std::move(progress));
10555
0
}
10556
inline Result Client::Get(const std::string &path, const Headers &headers,
10557
                          ContentReceiver content_receiver,
10558
0
                          DownloadProgress progress) {
10559
0
  return cli_->Get(path, headers, std::move(content_receiver),
10560
0
                   std::move(progress));
10561
0
}
10562
inline Result Client::Get(const std::string &path,
10563
                          ResponseHandler response_handler,
10564
                          ContentReceiver content_receiver,
10565
0
                          DownloadProgress progress) {
10566
0
  return cli_->Get(path, std::move(response_handler),
10567
0
                   std::move(content_receiver), std::move(progress));
10568
0
}
10569
inline Result Client::Get(const std::string &path, const Headers &headers,
10570
                          ResponseHandler response_handler,
10571
                          ContentReceiver content_receiver,
10572
0
                          DownloadProgress progress) {
10573
0
  return cli_->Get(path, headers, std::move(response_handler),
10574
0
                   std::move(content_receiver), std::move(progress));
10575
0
}
10576
inline Result Client::Get(const std::string &path, const Params &params,
10577
0
                          const Headers &headers, DownloadProgress progress) {
10578
0
  return cli_->Get(path, params, headers, std::move(progress));
10579
0
}
10580
inline Result Client::Get(const std::string &path, const Params &params,
10581
                          const Headers &headers,
10582
                          ContentReceiver content_receiver,
10583
0
                          DownloadProgress progress) {
10584
0
  return cli_->Get(path, params, headers, std::move(content_receiver),
10585
0
                   std::move(progress));
10586
0
}
10587
inline Result Client::Get(const std::string &path, const Params &params,
10588
                          const Headers &headers,
10589
                          ResponseHandler response_handler,
10590
                          ContentReceiver content_receiver,
10591
0
                          DownloadProgress progress) {
10592
0
  return cli_->Get(path, params, headers, std::move(response_handler),
10593
0
                   std::move(content_receiver), std::move(progress));
10594
0
}
10595
10596
0
inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
10597
0
inline Result Client::Head(const std::string &path, const Headers &headers) {
10598
0
  return cli_->Head(path, headers);
10599
0
}
10600
10601
0
inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
10602
0
inline Result Client::Post(const std::string &path, const Headers &headers) {
10603
0
  return cli_->Post(path, headers);
10604
0
}
10605
inline Result Client::Post(const std::string &path, const char *body,
10606
                           size_t content_length,
10607
                           const std::string &content_type,
10608
0
                           UploadProgress progress) {
10609
0
  return cli_->Post(path, body, content_length, content_type, progress);
10610
0
}
10611
inline Result Client::Post(const std::string &path, const Headers &headers,
10612
                           const char *body, size_t content_length,
10613
                           const std::string &content_type,
10614
0
                           UploadProgress progress) {
10615
0
  return cli_->Post(path, headers, body, content_length, content_type,
10616
0
                    progress);
10617
0
}
10618
inline Result Client::Post(const std::string &path, const std::string &body,
10619
                           const std::string &content_type,
10620
0
                           UploadProgress progress) {
10621
0
  return cli_->Post(path, body, content_type, progress);
10622
0
}
10623
inline Result Client::Post(const std::string &path, const Headers &headers,
10624
                           const std::string &body,
10625
                           const std::string &content_type,
10626
0
                           UploadProgress progress) {
10627
0
  return cli_->Post(path, headers, body, content_type, progress);
10628
0
}
10629
inline Result Client::Post(const std::string &path, size_t content_length,
10630
                           ContentProvider content_provider,
10631
                           const std::string &content_type,
10632
0
                           UploadProgress progress) {
10633
0
  return cli_->Post(path, content_length, std::move(content_provider),
10634
0
                    content_type, progress);
10635
0
}
10636
inline Result Client::Post(const std::string &path,
10637
                           ContentProviderWithoutLength content_provider,
10638
                           const std::string &content_type,
10639
0
                           UploadProgress progress) {
10640
0
  return cli_->Post(path, std::move(content_provider), content_type, progress);
10641
0
}
10642
inline Result Client::Post(const std::string &path, const Headers &headers,
10643
                           size_t content_length,
10644
                           ContentProvider content_provider,
10645
                           const std::string &content_type,
10646
0
                           UploadProgress progress) {
10647
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
10648
0
                    content_type, progress);
10649
0
}
10650
inline Result Client::Post(const std::string &path, const Headers &headers,
10651
                           ContentProviderWithoutLength content_provider,
10652
                           const std::string &content_type,
10653
0
                           UploadProgress progress) {
10654
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
10655
0
                    progress);
10656
0
}
10657
0
inline Result Client::Post(const std::string &path, const Params &params) {
10658
0
  return cli_->Post(path, params);
10659
0
}
10660
inline Result Client::Post(const std::string &path, const Headers &headers,
10661
0
                           const Params &params) {
10662
0
  return cli_->Post(path, headers, params);
10663
0
}
10664
inline Result Client::Post(const std::string &path,
10665
                           const MultipartFormDataItemsForClientInput &items,
10666
0
                           UploadProgress progress) {
10667
0
  return cli_->Post(path, items, progress);
10668
0
}
10669
inline Result Client::Post(const std::string &path, const Headers &headers,
10670
                           const MultipartFormDataItemsForClientInput &items,
10671
0
                           UploadProgress progress) {
10672
0
  return cli_->Post(path, headers, items, progress);
10673
0
}
10674
inline Result Client::Post(const std::string &path, const Headers &headers,
10675
                           const MultipartFormDataItemsForClientInput &items,
10676
                           const std::string &boundary,
10677
0
                           UploadProgress progress) {
10678
0
  return cli_->Post(path, headers, items, boundary, progress);
10679
0
}
10680
inline Result Client::Post(const std::string &path, const Headers &headers,
10681
                           const MultipartFormDataItemsForClientInput &items,
10682
                           const MultipartFormDataProviderItems &provider_items,
10683
0
                           UploadProgress progress) {
10684
0
  return cli_->Post(path, headers, items, provider_items, progress);
10685
0
}
10686
10687
0
inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
10688
0
inline Result Client::Put(const std::string &path, const Headers &headers) {
10689
0
  return cli_->Put(path, headers);
10690
0
}
10691
inline Result Client::Put(const std::string &path, const char *body,
10692
                          size_t content_length,
10693
                          const std::string &content_type,
10694
0
                          UploadProgress progress) {
10695
0
  return cli_->Put(path, body, content_length, content_type, progress);
10696
0
}
10697
inline Result Client::Put(const std::string &path, const Headers &headers,
10698
                          const char *body, size_t content_length,
10699
                          const std::string &content_type,
10700
0
                          UploadProgress progress) {
10701
0
  return cli_->Put(path, headers, body, content_length, content_type, progress);
10702
0
}
10703
inline Result Client::Put(const std::string &path, const std::string &body,
10704
                          const std::string &content_type,
10705
0
                          UploadProgress progress) {
10706
0
  return cli_->Put(path, body, content_type, progress);
10707
0
}
10708
inline Result Client::Put(const std::string &path, const Headers &headers,
10709
                          const std::string &body,
10710
                          const std::string &content_type,
10711
0
                          UploadProgress progress) {
10712
0
  return cli_->Put(path, headers, body, content_type, progress);
10713
0
}
10714
inline Result Client::Put(const std::string &path, size_t content_length,
10715
                          ContentProvider content_provider,
10716
                          const std::string &content_type,
10717
0
                          UploadProgress progress) {
10718
0
  return cli_->Put(path, content_length, std::move(content_provider),
10719
0
                   content_type, progress);
10720
0
}
10721
inline Result Client::Put(const std::string &path,
10722
                          ContentProviderWithoutLength content_provider,
10723
                          const std::string &content_type,
10724
0
                          UploadProgress progress) {
10725
0
  return cli_->Put(path, std::move(content_provider), content_type, progress);
10726
0
}
10727
inline Result Client::Put(const std::string &path, const Headers &headers,
10728
                          size_t content_length,
10729
                          ContentProvider content_provider,
10730
                          const std::string &content_type,
10731
0
                          UploadProgress progress) {
10732
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
10733
0
                   content_type, progress);
10734
0
}
10735
inline Result Client::Put(const std::string &path, const Headers &headers,
10736
                          ContentProviderWithoutLength content_provider,
10737
                          const std::string &content_type,
10738
0
                          UploadProgress progress) {
10739
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
10740
0
                   progress);
10741
0
}
10742
0
inline Result Client::Put(const std::string &path, const Params &params) {
10743
0
  return cli_->Put(path, params);
10744
0
}
10745
inline Result Client::Put(const std::string &path, const Headers &headers,
10746
0
                          const Params &params) {
10747
0
  return cli_->Put(path, headers, params);
10748
0
}
10749
inline Result Client::Put(const std::string &path,
10750
                          const MultipartFormDataItemsForClientInput &items,
10751
0
                          UploadProgress progress) {
10752
0
  return cli_->Put(path, items, progress);
10753
0
}
10754
inline Result Client::Put(const std::string &path, const Headers &headers,
10755
                          const MultipartFormDataItemsForClientInput &items,
10756
0
                          UploadProgress progress) {
10757
0
  return cli_->Put(path, headers, items, progress);
10758
0
}
10759
inline Result Client::Put(const std::string &path, const Headers &headers,
10760
                          const MultipartFormDataItemsForClientInput &items,
10761
                          const std::string &boundary,
10762
0
                          UploadProgress progress) {
10763
0
  return cli_->Put(path, headers, items, boundary, progress);
10764
0
}
10765
inline Result Client::Put(const std::string &path, const Headers &headers,
10766
                          const MultipartFormDataItemsForClientInput &items,
10767
                          const MultipartFormDataProviderItems &provider_items,
10768
0
                          UploadProgress progress) {
10769
0
  return cli_->Put(path, headers, items, provider_items, progress);
10770
0
}
10771
10772
0
inline Result Client::Patch(const std::string &path) {
10773
0
  return cli_->Patch(path);
10774
0
}
10775
0
inline Result Client::Patch(const std::string &path, const Headers &headers) {
10776
0
  return cli_->Patch(path, headers);
10777
0
}
10778
inline Result Client::Patch(const std::string &path, const char *body,
10779
                            size_t content_length,
10780
                            const std::string &content_type,
10781
0
                            UploadProgress progress) {
10782
0
  return cli_->Patch(path, body, content_length, content_type, progress);
10783
0
}
10784
inline Result Client::Patch(const std::string &path, const Headers &headers,
10785
                            const char *body, size_t content_length,
10786
                            const std::string &content_type,
10787
0
                            UploadProgress progress) {
10788
0
  return cli_->Patch(path, headers, body, content_length, content_type,
10789
0
                     progress);
10790
0
}
10791
inline Result Client::Patch(const std::string &path, const std::string &body,
10792
                            const std::string &content_type,
10793
0
                            UploadProgress progress) {
10794
0
  return cli_->Patch(path, body, content_type, progress);
10795
0
}
10796
inline Result Client::Patch(const std::string &path, const Headers &headers,
10797
                            const std::string &body,
10798
                            const std::string &content_type,
10799
0
                            UploadProgress progress) {
10800
0
  return cli_->Patch(path, headers, body, content_type, progress);
10801
0
}
10802
inline Result Client::Patch(const std::string &path, size_t content_length,
10803
                            ContentProvider content_provider,
10804
                            const std::string &content_type,
10805
0
                            UploadProgress progress) {
10806
0
  return cli_->Patch(path, content_length, std::move(content_provider),
10807
0
                     content_type, progress);
10808
0
}
10809
inline Result Client::Patch(const std::string &path,
10810
                            ContentProviderWithoutLength content_provider,
10811
                            const std::string &content_type,
10812
0
                            UploadProgress progress) {
10813
0
  return cli_->Patch(path, std::move(content_provider), content_type, progress);
10814
0
}
10815
inline Result Client::Patch(const std::string &path, const Headers &headers,
10816
                            size_t content_length,
10817
                            ContentProvider content_provider,
10818
                            const std::string &content_type,
10819
0
                            UploadProgress progress) {
10820
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
10821
0
                     content_type, progress);
10822
0
}
10823
inline Result Client::Patch(const std::string &path, const Headers &headers,
10824
                            ContentProviderWithoutLength content_provider,
10825
                            const std::string &content_type,
10826
0
                            UploadProgress progress) {
10827
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
10828
0
                     progress);
10829
0
}
10830
0
inline Result Client::Patch(const std::string &path, const Params &params) {
10831
0
  return cli_->Patch(path, params);
10832
0
}
10833
inline Result Client::Patch(const std::string &path, const Headers &headers,
10834
0
                            const Params &params) {
10835
0
  return cli_->Patch(path, headers, params);
10836
0
}
10837
inline Result Client::Patch(const std::string &path,
10838
                            const MultipartFormDataItemsForClientInput &items,
10839
0
                            UploadProgress progress) {
10840
0
  return cli_->Patch(path, items, progress);
10841
0
}
10842
inline Result Client::Patch(const std::string &path, const Headers &headers,
10843
                            const MultipartFormDataItemsForClientInput &items,
10844
0
                            UploadProgress progress) {
10845
0
  return cli_->Patch(path, headers, items, progress);
10846
0
}
10847
inline Result Client::Patch(const std::string &path, const Headers &headers,
10848
                            const MultipartFormDataItemsForClientInput &items,
10849
                            const std::string &boundary,
10850
0
                            UploadProgress progress) {
10851
0
  return cli_->Patch(path, headers, items, boundary, progress);
10852
0
}
10853
inline Result
10854
Client::Patch(const std::string &path, const Headers &headers,
10855
              const MultipartFormDataItemsForClientInput &items,
10856
              const MultipartFormDataProviderItems &provider_items,
10857
0
              UploadProgress progress) {
10858
0
  return cli_->Patch(path, headers, items, provider_items, progress);
10859
0
}
10860
10861
inline Result Client::Delete(const std::string &path,
10862
0
                             DownloadProgress progress) {
10863
0
  return cli_->Delete(path, progress);
10864
0
}
10865
inline Result Client::Delete(const std::string &path, const Headers &headers,
10866
0
                             DownloadProgress progress) {
10867
0
  return cli_->Delete(path, headers, progress);
10868
0
}
10869
inline Result Client::Delete(const std::string &path, const char *body,
10870
                             size_t content_length,
10871
                             const std::string &content_type,
10872
0
                             DownloadProgress progress) {
10873
0
  return cli_->Delete(path, body, content_length, content_type, progress);
10874
0
}
10875
inline Result Client::Delete(const std::string &path, const Headers &headers,
10876
                             const char *body, size_t content_length,
10877
                             const std::string &content_type,
10878
0
                             DownloadProgress progress) {
10879
0
  return cli_->Delete(path, headers, body, content_length, content_type,
10880
0
                      progress);
10881
0
}
10882
inline Result Client::Delete(const std::string &path, const std::string &body,
10883
                             const std::string &content_type,
10884
0
                             DownloadProgress progress) {
10885
0
  return cli_->Delete(path, body, content_type, progress);
10886
0
}
10887
inline Result Client::Delete(const std::string &path, const Headers &headers,
10888
                             const std::string &body,
10889
                             const std::string &content_type,
10890
0
                             DownloadProgress progress) {
10891
0
  return cli_->Delete(path, headers, body, content_type, progress);
10892
0
}
10893
inline Result Client::Delete(const std::string &path, const Params &params,
10894
0
                             DownloadProgress progress) {
10895
0
  return cli_->Delete(path, params, progress);
10896
0
}
10897
inline Result Client::Delete(const std::string &path, const Headers &headers,
10898
0
                             const Params &params, DownloadProgress progress) {
10899
0
  return cli_->Delete(path, headers, params, progress);
10900
0
}
10901
10902
0
inline Result Client::Options(const std::string &path) {
10903
0
  return cli_->Options(path);
10904
0
}
10905
0
inline Result Client::Options(const std::string &path, const Headers &headers) {
10906
0
  return cli_->Options(path, headers);
10907
0
}
10908
10909
0
inline bool Client::send(Request &req, Response &res, Error &error) {
10910
0
  return cli_->send(req, res, error);
10911
0
}
10912
10913
0
inline Result Client::send(const Request &req) { return cli_->send(req); }
10914
10915
0
inline void Client::stop() { cli_->stop(); }
10916
10917
0
inline std::string Client::host() const { return cli_->host(); }
10918
10919
0
inline int Client::port() const { return cli_->port(); }
10920
10921
0
inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
10922
10923
0
inline socket_t Client::socket() const { return cli_->socket(); }
10924
10925
inline void
10926
0
Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
10927
0
  cli_->set_hostname_addr_map(std::move(addr_map));
10928
0
}
10929
10930
0
inline void Client::set_default_headers(Headers headers) {
10931
0
  cli_->set_default_headers(std::move(headers));
10932
0
}
10933
10934
inline void Client::set_header_writer(
10935
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
10936
0
  cli_->set_header_writer(writer);
10937
0
}
10938
10939
0
inline void Client::set_address_family(int family) {
10940
0
  cli_->set_address_family(family);
10941
0
}
10942
10943
0
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
10944
10945
0
inline void Client::set_socket_options(SocketOptions socket_options) {
10946
0
  cli_->set_socket_options(std::move(socket_options));
10947
0
}
10948
10949
0
inline void Client::set_connection_timeout(time_t sec, time_t usec) {
10950
0
  cli_->set_connection_timeout(sec, usec);
10951
0
}
10952
10953
0
inline void Client::set_read_timeout(time_t sec, time_t usec) {
10954
0
  cli_->set_read_timeout(sec, usec);
10955
0
}
10956
10957
0
inline void Client::set_write_timeout(time_t sec, time_t usec) {
10958
0
  cli_->set_write_timeout(sec, usec);
10959
0
}
10960
10961
inline void Client::set_basic_auth(const std::string &username,
10962
0
                                   const std::string &password) {
10963
0
  cli_->set_basic_auth(username, password);
10964
0
}
10965
0
inline void Client::set_bearer_token_auth(const std::string &token) {
10966
0
  cli_->set_bearer_token_auth(token);
10967
0
}
10968
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10969
inline void Client::set_digest_auth(const std::string &username,
10970
                                    const std::string &password) {
10971
  cli_->set_digest_auth(username, password);
10972
}
10973
#endif
10974
10975
0
inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
10976
0
inline void Client::set_follow_location(bool on) {
10977
0
  cli_->set_follow_location(on);
10978
0
}
10979
10980
0
inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }
10981
10982
0
inline void Client::set_compress(bool on) { cli_->set_compress(on); }
10983
10984
0
inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
10985
10986
0
inline void Client::set_interface(const std::string &intf) {
10987
0
  cli_->set_interface(intf);
10988
0
}
10989
10990
0
inline void Client::set_proxy(const std::string &host, int port) {
10991
0
  cli_->set_proxy(host, port);
10992
0
}
10993
inline void Client::set_proxy_basic_auth(const std::string &username,
10994
0
                                         const std::string &password) {
10995
0
  cli_->set_proxy_basic_auth(username, password);
10996
0
}
10997
0
inline void Client::set_proxy_bearer_token_auth(const std::string &token) {
10998
0
  cli_->set_proxy_bearer_token_auth(token);
10999
0
}
11000
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11001
inline void Client::set_proxy_digest_auth(const std::string &username,
11002
                                          const std::string &password) {
11003
  cli_->set_proxy_digest_auth(username, password);
11004
}
11005
#endif
11006
11007
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11008
inline void Client::enable_server_certificate_verification(bool enabled) {
11009
  cli_->enable_server_certificate_verification(enabled);
11010
}
11011
11012
inline void Client::enable_server_hostname_verification(bool enabled) {
11013
  cli_->enable_server_hostname_verification(enabled);
11014
}
11015
11016
inline void Client::set_server_certificate_verifier(
11017
    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
11018
  cli_->set_server_certificate_verifier(verifier);
11019
}
11020
#endif
11021
11022
0
inline void Client::set_logger(Logger logger) {
11023
0
  cli_->set_logger(std::move(logger));
11024
0
}
11025
11026
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
11027
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
11028
                                     const std::string &ca_cert_dir_path) {
11029
  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
11030
}
11031
11032
inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
11033
  if (is_ssl_) {
11034
    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
11035
  } else {
11036
    cli_->set_ca_cert_store(ca_cert_store);
11037
  }
11038
}
11039
11040
inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
11041
  set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
11042
}
11043
11044
inline long Client::get_openssl_verify_result() const {
11045
  if (is_ssl_) {
11046
    return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
11047
  }
11048
  return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
11049
}
11050
11051
inline SSL_CTX *Client::ssl_context() const {
11052
  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
11053
  return nullptr;
11054
}
11055
#endif
11056
11057
// ----------------------------------------------------------------------------
11058
11059
} // namespace httplib
11060
11061
#endif // CPPHTTPLIB_HTTPLIB_H