Coverage Report

Created: 2026-05-31 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpp-httplib/httplib.h
Line
Count
Source
1
//
2
//  httplib.h
3
//
4
//  Copyright (c) 2026 Yuji Hirose. All rights reserved.
5
//  MIT License
6
//
7
8
#ifndef CPPHTTPLIB_HTTPLIB_H
9
#define CPPHTTPLIB_HTTPLIB_H
10
11
#define CPPHTTPLIB_VERSION "0.46.0"
12
#define CPPHTTPLIB_VERSION_NUM "0x002e00"
13
14
#ifdef _WIN32
15
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
16
#error                                                                         \
17
    "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later."
18
#endif
19
#endif
20
21
/*
22
 * Configuration
23
 */
24
25
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
26
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
27
#endif
28
29
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
30
0
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
31
#endif
32
33
#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
34
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
35
#endif
36
37
#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
38
#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
39
#endif
40
41
#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
42
#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
43
#endif
44
45
#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
46
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
47
#endif
48
49
#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
50
#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
51
#endif
52
53
#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
54
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
55
#endif
56
57
#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
58
#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
59
#endif
60
61
#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
62
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
63
#endif
64
65
#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
66
#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
67
#endif
68
69
#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
70
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
71
#endif
72
73
#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
74
#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
75
#endif
76
77
#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
78
#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
79
#endif
80
81
#ifndef CPPHTTPLIB_EXPECT_100_THRESHOLD
82
#define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024
83
#endif
84
85
#ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
86
#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000
87
#endif
88
89
#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
90
#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024)
91
#endif
92
93
#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND
94
#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50
95
#endif
96
97
#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
98
#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
99
#endif
100
101
#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
102
#ifdef _WIN32
103
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
104
#else
105
#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
106
#endif
107
#endif
108
109
#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
110
0
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
111
#endif
112
113
#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
114
0
#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
115
#endif
116
117
#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
118
0
#define CPPHTTPLIB_HEADER_MAX_COUNT 100
119
#endif
120
121
#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
122
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
123
#endif
124
125
#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
126
0
#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
127
#endif
128
129
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
130
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (100 * 1024 * 1024) // 100MB
131
#endif
132
133
#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
134
0
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
135
#endif
136
137
#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
138
0
#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
139
#endif
140
141
#ifndef CPPHTTPLIB_TCP_NODELAY
142
#define CPPHTTPLIB_TCP_NODELAY false
143
#endif
144
145
#ifndef CPPHTTPLIB_IPV6_V6ONLY
146
#define CPPHTTPLIB_IPV6_V6ONLY false
147
#endif
148
149
#ifndef CPPHTTPLIB_RECV_BUFSIZ
150
0
#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
151
#endif
152
153
#ifndef CPPHTTPLIB_SEND_BUFSIZ
154
#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
155
#endif
156
157
#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
158
#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
159
#endif
160
161
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
162
#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
163
  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \
164
                      ? std::thread::hardware_concurrency() - 1                \
165
                      : 0))
166
#endif
167
168
#ifndef CPPHTTPLIB_THREAD_POOL_MAX_COUNT
169
#define CPPHTTPLIB_THREAD_POOL_MAX_COUNT (CPPHTTPLIB_THREAD_POOL_COUNT * 4)
170
#endif
171
172
#ifndef CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT
173
0
#define CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT 3 // seconds
174
#endif
175
176
#ifndef CPPHTTPLIB_RECV_FLAGS
177
0
#define CPPHTTPLIB_RECV_FLAGS 0
178
#endif
179
180
#ifndef CPPHTTPLIB_SEND_FLAGS
181
0
#define CPPHTTPLIB_SEND_FLAGS 0
182
#endif
183
184
#ifndef CPPHTTPLIB_LISTEN_BACKLOG
185
#define CPPHTTPLIB_LISTEN_BACKLOG 5
186
#endif
187
188
#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
189
0
#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
190
#endif
191
192
#ifndef CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH
193
#define CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH 16777216
194
#endif
195
196
#ifndef CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND
197
0
#define CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 300
198
#endif
199
200
#ifndef CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND
201
0
#define CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND 5
202
#endif
203
204
#ifndef CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND
205
#define CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND 30
206
#endif
207
208
#ifndef CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS
209
#define CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS 0
210
#endif
211
212
/*
213
 * Headers
214
 */
215
216
#ifdef _WIN32
217
#ifndef _CRT_SECURE_NO_WARNINGS
218
#define _CRT_SECURE_NO_WARNINGS
219
#endif //_CRT_SECURE_NO_WARNINGS
220
221
#ifndef _CRT_NONSTDC_NO_DEPRECATE
222
#define _CRT_NONSTDC_NO_DEPRECATE
223
#endif //_CRT_NONSTDC_NO_DEPRECATE
224
225
#if defined(_MSC_VER)
226
#if _MSC_VER < 1900
227
#error Sorry, Visual Studio versions prior to 2015 are not supported
228
#endif
229
230
#pragma comment(lib, "ws2_32.lib")
231
232
#ifndef _SSIZE_T_DEFINED
233
using ssize_t = __int64;
234
#define _SSIZE_T_DEFINED
235
#endif
236
#endif // _MSC_VER
237
238
#ifndef S_ISREG
239
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
240
#endif // S_ISREG
241
242
#ifndef S_ISDIR
243
#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
244
#endif // S_ISDIR
245
246
#ifndef NOMINMAX
247
#define NOMINMAX
248
#endif // NOMINMAX
249
250
#include <io.h>
251
#include <winsock2.h>
252
#include <ws2tcpip.h>
253
254
#if defined(__has_include)
255
#if __has_include(<afunix.h>)
256
// afunix.h uses types declared in winsock2.h, so has to be included after it.
257
#include <afunix.h>
258
#define CPPHTTPLIB_HAVE_AFUNIX_H 1
259
#endif
260
#endif
261
262
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
263
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
264
#endif
265
266
using nfds_t = unsigned long;
267
using socket_t = SOCKET;
268
using socklen_t = int;
269
270
#else // not _WIN32
271
272
#include <arpa/inet.h>
273
#if !defined(_AIX) && !defined(__MVS__)
274
#include <ifaddrs.h>
275
#endif
276
#ifdef __MVS__
277
#include <strings.h>
278
#ifndef NI_MAXHOST
279
#define NI_MAXHOST 1025
280
#endif
281
#endif
282
#include <net/if.h>
283
#include <netdb.h>
284
#include <netinet/in.h>
285
#ifdef __linux__
286
#include <resolv.h>
287
#undef _res // Undefine _res macro to avoid conflicts with user code (#2278)
288
#endif
289
#include <csignal>
290
#include <netinet/tcp.h>
291
#include <poll.h>
292
#include <pthread.h>
293
#include <sys/mman.h>
294
#include <sys/socket.h>
295
#include <sys/un.h>
296
#include <unistd.h>
297
298
using socket_t = int;
299
#ifndef INVALID_SOCKET
300
0
#define INVALID_SOCKET (-1)
301
#endif
302
#endif //_WIN32
303
304
#if defined(__APPLE__)
305
#include <TargetConditionals.h>
306
#endif
307
308
#include <algorithm>
309
#include <array>
310
#include <atomic>
311
#include <cassert>
312
#include <cctype>
313
#include <chrono>
314
#include <climits>
315
#include <condition_variable>
316
#include <cstdlib>
317
#include <cstring>
318
#include <errno.h>
319
#include <exception>
320
#include <fcntl.h>
321
#include <fstream>
322
#include <functional>
323
#include <iomanip>
324
#include <iostream>
325
#include <list>
326
#include <map>
327
#include <memory>
328
#include <mutex>
329
#include <random>
330
#include <regex>
331
#include <set>
332
#include <sstream>
333
#include <string>
334
#include <sys/stat.h>
335
#include <system_error>
336
#include <thread>
337
#include <unordered_map>
338
#include <unordered_set>
339
#include <utility>
340
341
// On macOS with a TLS backend, enable Keychain root certificates by default
342
// unless the user explicitly opts out. Not enabled on iOS/tvOS/watchOS since
343
// the SecTrustSettings APIs used to enumerate anchor certificates are macOS
344
// only; on those platforms the user must provide a CA bundle explicitly.
345
#if defined(__APPLE__) && defined(__clang__) &&                                \
346
    !defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) &&         \
347
    (defined(CPPHTTPLIB_OPENSSL_SUPPORT) ||                                    \
348
     defined(CPPHTTPLIB_MBEDTLS_SUPPORT) ||                                    \
349
     defined(CPPHTTPLIB_WOLFSSL_SUPPORT))
350
#if TARGET_OS_OSX
351
#ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
352
#define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
353
#endif
354
#endif
355
#endif
356
357
#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) &&                      \
358
    defined(__APPLE__) && !TARGET_OS_OSX
359
#error                                                                         \
360
    "CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN is only supported on macOS. On iOS/tvOS/watchOS, supply a CA bundle via set_ca_cert_path()."
361
#endif
362
363
// On Windows, enable Schannel certificate verification by default
364
// unless the user explicitly opts out.
365
#if defined(_WIN32) &&                                                         \
366
    !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
367
#define CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
368
#endif
369
370
#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) ||                        \
371
    defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
372
#if TARGET_OS_MAC && defined(__clang__)
373
#include <CFNetwork/CFHost.h>
374
#include <CoreFoundation/CoreFoundation.h>
375
#endif
376
#endif
377
378
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
379
#ifdef _WIN32
380
#include <wincrypt.h>
381
382
// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
383
// used
384
#undef X509_NAME
385
#undef X509_CERT_PAIR
386
#undef X509_EXTENSIONS
387
#undef PKCS7_SIGNER_INFO
388
389
#ifdef _MSC_VER
390
#pragma comment(lib, "crypt32.lib")
391
#endif
392
#endif // _WIN32
393
394
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
395
#if TARGET_OS_OSX
396
#include <Security/Security.h>
397
#endif
398
#endif
399
400
#include <openssl/err.h>
401
#include <openssl/evp.h>
402
#include <openssl/ssl.h>
403
#include <openssl/x509v3.h>
404
405
#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
406
#include <openssl/applink.c>
407
#endif
408
409
#include <iostream>
410
#include <sstream>
411
412
#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
413
#if OPENSSL_VERSION_NUMBER < 0x1010107f
414
#error Please use OpenSSL or a current version of BoringSSL
415
#endif
416
#define SSL_get1_peer_certificate SSL_get_peer_certificate
417
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
418
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
419
#endif
420
421
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
422
423
#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
424
#include <mbedtls/ctr_drbg.h>
425
#include <mbedtls/entropy.h>
426
#include <mbedtls/error.h>
427
#include <mbedtls/md5.h>
428
#include <mbedtls/net_sockets.h>
429
#include <mbedtls/oid.h>
430
#include <mbedtls/pk.h>
431
#include <mbedtls/sha1.h>
432
#include <mbedtls/sha256.h>
433
#include <mbedtls/sha512.h>
434
#include <mbedtls/ssl.h>
435
#include <mbedtls/x509_crt.h>
436
#ifdef _WIN32
437
#include <wincrypt.h>
438
#ifdef _MSC_VER
439
#pragma comment(lib, "crypt32.lib")
440
#endif
441
#endif // _WIN32
442
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
443
#if TARGET_OS_OSX
444
#include <Security/Security.h>
445
#endif
446
#endif
447
448
// Mbed TLS 3.x API compatibility
449
#if MBEDTLS_VERSION_MAJOR >= 3
450
#define CPPHTTPLIB_MBEDTLS_V3
451
#endif
452
453
#endif // CPPHTTPLIB_MBEDTLS_SUPPORT
454
455
#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
456
#include <wolfssl/options.h>
457
458
#include <wolfssl/openssl/x509v3.h>
459
460
// Fallback definitions for older wolfSSL versions (e.g., 5.6.6)
461
#ifndef WOLFSSL_GEN_EMAIL
462
#define WOLFSSL_GEN_EMAIL 1
463
#endif
464
#ifndef WOLFSSL_GEN_DNS
465
#define WOLFSSL_GEN_DNS 2
466
#endif
467
#ifndef WOLFSSL_GEN_URI
468
#define WOLFSSL_GEN_URI 6
469
#endif
470
#ifndef WOLFSSL_GEN_IPADD
471
#define WOLFSSL_GEN_IPADD 7
472
#endif
473
474
#include <wolfssl/ssl.h>
475
#include <wolfssl/wolfcrypt/hash.h>
476
#include <wolfssl/wolfcrypt/md5.h>
477
#include <wolfssl/wolfcrypt/sha256.h>
478
#include <wolfssl/wolfcrypt/sha512.h>
479
#ifdef _WIN32
480
#include <wincrypt.h>
481
#ifdef _MSC_VER
482
#pragma comment(lib, "crypt32.lib")
483
#endif
484
#endif // _WIN32
485
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
486
#if TARGET_OS_OSX
487
#include <Security/Security.h>
488
#endif
489
#endif
490
#endif // CPPHTTPLIB_WOLFSSL_SUPPORT
491
492
// Define CPPHTTPLIB_SSL_ENABLED if any SSL backend is available
493
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) ||                                     \
494
    defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
495
#define CPPHTTPLIB_SSL_ENABLED
496
#endif
497
498
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
499
#include <zlib.h>
500
#endif
501
502
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
503
#include <brotli/decode.h>
504
#include <brotli/encode.h>
505
#endif
506
507
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
508
#include <zstd.h>
509
#endif
510
511
/*
512
 * Declaration
513
 */
514
namespace httplib {
515
516
namespace ws {
517
class WebSocket;
518
} // namespace ws
519
520
namespace detail {
521
522
/*
523
 * Backport std::make_unique from C++14.
524
 *
525
 * NOTE: This code came up with the following stackoverflow post:
526
 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
527
 *
528
 */
529
530
template <class T, class... Args>
531
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
532
0
make_unique(Args &&...args) {
533
0
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
534
0
}
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_15gzip_compressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_12nocompressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_17gzip_decompressorEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_17PathParamsMatcherEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISD_NS3_14default_deleteISD_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_12RegexMatcherEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEEEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISD_NS3_14default_deleteISD_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_8ResponseEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_16ClientConnectionEJEEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrIS5_NS3_14default_deleteIS5_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS0_12SocketStreamEJRiRlS4_S4_S4_EEENSt3__19enable_ifIXntsr3std8is_arrayIT_EE5valueENS5_10unique_ptrIS7_NS5_14default_deleteIS7_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEERiRKS9_SD_EEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISF_NS3_14default_deleteISF_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEiSB_SB_EEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISD_NS3_14default_deleteISD_EEEEE4typeEDpOT0_
Unexecuted instantiation: _ZN7httplib6detail11make_uniqueINS_10ClientImplEJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEERiSB_SB_EEENS3_9enable_ifIXntsr3std8is_arrayIT_EE5valueENS3_10unique_ptrISE_NS3_14default_deleteISE_EEEEE4typeEDpOT0_
535
536
template <class T>
537
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
538
make_unique(std::size_t n) {
539
  typedef typename std::remove_extent<T>::type RT;
540
  return std::unique_ptr<T>(new RT[n]);
541
}
542
543
namespace case_ignore {
544
545
0
inline unsigned char to_lower(int c) {
546
0
  const static unsigned char table[256] = {
547
0
      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
548
0
      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
549
0
      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
550
0
      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
551
0
      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,
552
0
      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
553
0
      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
554
0
      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
555
0
      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
556
0
      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
557
0
      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
558
0
      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
559
0
      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
560
0
      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
561
0
      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
562
0
      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
563
0
      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
564
0
      255,
565
0
  };
566
0
  return table[(unsigned char)(char)c];
567
0
}
568
569
0
inline std::string to_lower(const std::string &s) {
570
0
  std::string result = s;
571
0
  std::transform(
572
0
      result.begin(), result.end(), result.begin(),
573
0
      [](unsigned char c) { return static_cast<char>(to_lower(c)); });
574
0
  return result;
575
0
}
576
577
0
inline bool equal(const std::string &a, const std::string &b) {
578
0
  return a.size() == b.size() &&
579
0
         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
580
0
           return to_lower(ca) == to_lower(cb);
581
0
         });
582
0
}
583
584
struct equal_to {
585
0
  bool operator()(const std::string &a, const std::string &b) const {
586
0
    return equal(a, b);
587
0
  }
588
};
589
590
struct hash {
591
0
  size_t operator()(const std::string &key) const {
592
0
    return hash_core(key.data(), key.size(), 0);
593
0
  }
594
595
0
  size_t hash_core(const char *s, size_t l, size_t h) const {
596
0
    return (l == 0) ? h
597
0
                    : hash_core(s + 1, l - 1,
598
                                // Unsets the 6 high bits of h, therefore no
599
                                // overflow happens
600
0
                                (((std::numeric_limits<size_t>::max)() >> 6) &
601
0
                                 h * 33) ^
602
0
                                    static_cast<unsigned char>(to_lower(*s)));
603
0
  }
604
};
605
606
template <typename T>
607
using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
608
                                         detail::case_ignore::equal_to>;
609
610
} // namespace case_ignore
611
612
// This is based on
613
// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
614
615
struct scope_exit {
616
  explicit scope_exit(std::function<void(void)> &&f)
617
0
      : exit_function(std::move(f)), execute_on_destruction{true} {}
618
619
  scope_exit(scope_exit &&rhs) noexcept
620
      : exit_function(std::move(rhs.exit_function)),
621
0
        execute_on_destruction{rhs.execute_on_destruction} {
622
0
    rhs.release();
623
0
  }
624
625
0
  ~scope_exit() {
626
0
    if (execute_on_destruction) { this->exit_function(); }
627
0
  }
628
629
0
  void release() { this->execute_on_destruction = false; }
630
631
private:
632
  scope_exit(const scope_exit &) = delete;
633
  void operator=(const scope_exit &) = delete;
634
  scope_exit &operator=(scope_exit &&) = delete;
635
636
  std::function<void(void)> exit_function;
637
  bool execute_on_destruction;
638
};
639
640
// Simple from_chars implementation for integer and double types (C++17
641
// substitute)
642
template <typename T> struct from_chars_result {
643
  const char *ptr;
644
  std::errc ec;
645
};
646
647
template <typename T>
648
inline from_chars_result<T> from_chars(const char *first, const char *last,
649
0
                                       T &value, int base = 10) {
650
0
  value = 0;
651
0
  const char *p = first;
652
0
  bool negative = false;
653
654
0
  if (p != last && *p == '-') {
655
0
    negative = true;
656
0
    ++p;
657
0
  }
658
0
  if (p == last) { return {first, std::errc::invalid_argument}; }
659
660
0
  T result = 0;
661
0
  for (; p != last; ++p) {
662
0
    char c = *p;
663
0
    int digit = -1;
664
0
    if ('0' <= c && c <= '9') {
665
0
      digit = c - '0';
666
0
    } else if ('a' <= c && c <= 'z') {
667
0
      digit = c - 'a' + 10;
668
0
    } else if ('A' <= c && c <= 'Z') {
669
0
      digit = c - 'A' + 10;
670
0
    } else {
671
0
      break;
672
0
    }
673
674
0
    if (digit < 0 || digit >= base) { break; }
675
0
    if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
676
0
      return {p, std::errc::result_out_of_range};
677
0
    }
678
0
    result = result * base + digit;
679
0
  }
680
681
0
  if (p == first || (negative && p == first + 1)) {
682
0
    return {first, std::errc::invalid_argument};
683
0
  }
684
685
0
  value = negative ? -result : result;
686
0
  return {p, std::errc{}};
687
0
}
Unexecuted instantiation: httplib::detail::from_chars_result<long> httplib::detail::from_chars<long>(char const*, char const*, long&, int)
Unexecuted instantiation: httplib::detail::from_chars_result<int> httplib::detail::from_chars<int>(char const*, char const*, int&, int)
688
689
// from_chars for double (simple wrapper for strtod)
690
inline from_chars_result<double> from_chars(const char *first, const char *last,
691
0
                                            double &value) {
692
0
  std::string s(first, last);
693
0
  char *endptr = nullptr;
694
0
  errno = 0;
695
0
  value = std::strtod(s.c_str(), &endptr);
696
0
  if (endptr == s.c_str()) { return {first, std::errc::invalid_argument}; }
697
0
  if (errno == ERANGE) {
698
0
    return {first + (endptr - s.c_str()), std::errc::result_out_of_range};
699
0
  }
700
0
  return {first + (endptr - s.c_str()), std::errc{}};
701
0
}
702
703
0
inline bool parse_port(const char *s, size_t len, int &port) {
704
0
  int val = 0;
705
0
  auto r = from_chars(s, s + len, val);
706
0
  if (r.ec != std::errc{} || val < 1 || val > 65535) { return false; }
707
0
  port = val;
708
0
  return true;
709
0
}
710
711
0
inline bool parse_port(const std::string &s, int &port) {
712
0
  return parse_port(s.data(), s.size(), port);
713
0
}
714
715
struct UrlComponents {
716
  std::string scheme;
717
  std::string host;
718
  std::string port;
719
  std::string path;
720
  std::string query;
721
};
722
723
0
inline bool parse_url(const std::string &url, UrlComponents &uc) {
724
0
  uc = {};
725
0
  size_t pos = 0;
726
0
727
0
  auto sep = url.find("://");
728
0
  if (sep != std::string::npos) {
729
0
    uc.scheme = url.substr(0, sep);
730
0
731
0
    // Scheme must be [a-z]+ only
732
0
    if (uc.scheme.empty()) { return false; }
733
0
    for (auto c : uc.scheme) {
734
0
      if (c < 'a' || c > 'z') { return false; }
735
0
    }
736
0
737
0
    pos = sep + 3;
738
0
  } else if (url.compare(0, 2, "//") == 0) {
739
0
    pos = 2;
740
0
  }
741
0
742
0
  auto has_authority_prefix = pos > 0;
743
0
  auto has_authority = has_authority_prefix || (!url.empty() && url[0] != '/' &&
744
0
                                                url[0] != '?' && url[0] != '#');
745
0
  if (has_authority) {
746
0
    if (pos < url.size() && url[pos] == '[') {
747
0
      auto close = url.find(']', pos);
748
0
      if (close == std::string::npos) { return false; }
749
0
      uc.host = url.substr(pos + 1, close - pos - 1);
750
0
751
0
      // IPv6 host must be [a-fA-F0-9:]+ only
752
0
      if (uc.host.empty()) { return false; }
753
0
      for (auto c : uc.host) {
754
0
        if (!((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
755
0
              (c >= '0' && c <= '9') || c == ':')) {
756
0
          return false;
757
0
        }
758
0
      }
759
0
760
0
      pos = close + 1;
761
0
    } else {
762
0
      auto end = url.find_first_of(":/?#", pos);
763
0
      if (end == std::string::npos) { end = url.size(); }
764
0
      uc.host = url.substr(pos, end - pos);
765
0
      pos = end;
766
0
    }
767
0
768
0
    if (pos < url.size() && url[pos] == ':') {
769
0
      ++pos;
770
0
      auto end = url.find_first_of("/?#", pos);
771
0
      if (end == std::string::npos) { end = url.size(); }
772
0
      uc.port = url.substr(pos, end - pos);
773
0
      pos = end;
774
0
    }
775
0
776
0
    // Without :// or //, the entire input must be consumed as host[:port].
777
0
    // If there is leftover (path, query, etc.), this is not a valid
778
0
    // host[:port] string — clear and reparse as a plain path.
779
0
    if (!has_authority_prefix && pos < url.size()) {
780
0
      uc.host.clear();
781
0
      uc.port.clear();
782
0
      pos = 0;
783
0
    }
784
0
  }
785
0
786
0
  if (pos < url.size() && url[pos] != '?' && url[pos] != '#') {
787
0
    auto end = url.find_first_of("?#", pos);
788
0
    if (end == std::string::npos) { end = url.size(); }
789
0
    uc.path = url.substr(pos, end - pos);
790
0
    pos = end;
791
0
  }
792
0
793
0
  if (pos < url.size() && url[pos] == '?') {
794
0
    auto end = url.find('#', pos);
795
0
    if (end == std::string::npos) { end = url.size(); }
796
0
    uc.query = url.substr(pos, end - pos);
797
0
  }
798
0
799
0
  return true;
800
0
}
801
802
} // namespace detail
803
804
enum class SSLVerifierResponse {
805
  // no decision has been made, use the built-in certificate verifier
806
  NoDecisionMade,
807
  // connection certificate is verified and accepted
808
  CertificateAccepted,
809
  // connection certificate was processed but is rejected
810
  CertificateRejected
811
};
812
813
enum StatusCode {
814
  // Information responses
815
  Continue_100 = 100,
816
  SwitchingProtocol_101 = 101,
817
  Processing_102 = 102,
818
  EarlyHints_103 = 103,
819
820
  // Successful responses
821
  OK_200 = 200,
822
  Created_201 = 201,
823
  Accepted_202 = 202,
824
  NonAuthoritativeInformation_203 = 203,
825
  NoContent_204 = 204,
826
  ResetContent_205 = 205,
827
  PartialContent_206 = 206,
828
  MultiStatus_207 = 207,
829
  AlreadyReported_208 = 208,
830
  IMUsed_226 = 226,
831
832
  // Redirection messages
833
  MultipleChoices_300 = 300,
834
  MovedPermanently_301 = 301,
835
  Found_302 = 302,
836
  SeeOther_303 = 303,
837
  NotModified_304 = 304,
838
  UseProxy_305 = 305,
839
  unused_306 = 306,
840
  TemporaryRedirect_307 = 307,
841
  PermanentRedirect_308 = 308,
842
843
  // Client error responses
844
  BadRequest_400 = 400,
845
  Unauthorized_401 = 401,
846
  PaymentRequired_402 = 402,
847
  Forbidden_403 = 403,
848
  NotFound_404 = 404,
849
  MethodNotAllowed_405 = 405,
850
  NotAcceptable_406 = 406,
851
  ProxyAuthenticationRequired_407 = 407,
852
  RequestTimeout_408 = 408,
853
  Conflict_409 = 409,
854
  Gone_410 = 410,
855
  LengthRequired_411 = 411,
856
  PreconditionFailed_412 = 412,
857
  PayloadTooLarge_413 = 413,
858
  UriTooLong_414 = 414,
859
  UnsupportedMediaType_415 = 415,
860
  RangeNotSatisfiable_416 = 416,
861
  ExpectationFailed_417 = 417,
862
  ImATeapot_418 = 418,
863
  MisdirectedRequest_421 = 421,
864
  UnprocessableContent_422 = 422,
865
  Locked_423 = 423,
866
  FailedDependency_424 = 424,
867
  TooEarly_425 = 425,
868
  UpgradeRequired_426 = 426,
869
  PreconditionRequired_428 = 428,
870
  TooManyRequests_429 = 429,
871
  RequestHeaderFieldsTooLarge_431 = 431,
872
  UnavailableForLegalReasons_451 = 451,
873
874
  // Server error responses
875
  InternalServerError_500 = 500,
876
  NotImplemented_501 = 501,
877
  BadGateway_502 = 502,
878
  ServiceUnavailable_503 = 503,
879
  GatewayTimeout_504 = 504,
880
  HttpVersionNotSupported_505 = 505,
881
  VariantAlsoNegotiates_506 = 506,
882
  InsufficientStorage_507 = 507,
883
  LoopDetected_508 = 508,
884
  NotExtended_510 = 510,
885
  NetworkAuthenticationRequired_511 = 511,
886
};
887
888
using Headers =
889
    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
890
                            detail::case_ignore::equal_to>;
891
892
using Params = std::multimap<std::string, std::string>;
893
using Match = std::smatch;
894
895
using DownloadProgress = std::function<bool(size_t current, size_t total)>;
896
using UploadProgress = std::function<bool(size_t current, size_t total)>;
897
898
/*
899
 * detail: type-erased storage used by UserData.
900
 * ABI-stable regardless of C++ standard — always uses this custom
901
 * implementation instead of std::any.
902
 */
903
namespace detail {
904
905
using any_type_id = const void *;
906
907
template <typename T> any_type_id any_typeid() noexcept {
908
  static const char id = 0;
909
  return &id;
910
}
911
912
struct any_storage {
913
  virtual ~any_storage() = default;
914
  virtual std::unique_ptr<any_storage> clone() const = 0;
915
  virtual any_type_id type_id() const noexcept = 0;
916
};
917
918
template <typename T> struct any_value final : any_storage {
919
  T value;
920
  template <typename U> explicit any_value(U &&v) : value(std::forward<U>(v)) {}
921
  std::unique_ptr<any_storage> clone() const override {
922
    return std::unique_ptr<any_storage>(new any_value<T>(value));
923
  }
924
  any_type_id type_id() const noexcept override { return any_typeid<T>(); }
925
};
926
927
} // namespace detail
928
929
class UserData {
930
public:
931
0
  UserData() = default;
932
  UserData(UserData &&) noexcept = default;
933
  UserData &operator=(UserData &&) noexcept = default;
934
935
0
  UserData(const UserData &o) {
936
0
    for (const auto &e : o.entries_) {
937
0
      if (e.second) { entries_[e.first] = e.second->clone(); }
938
0
    }
939
0
  }
940
941
0
  UserData &operator=(const UserData &o) {
942
0
    if (this != &o) {
943
0
      entries_.clear();
944
0
      for (const auto &e : o.entries_) {
945
0
        if (e.second) { entries_[e.first] = e.second->clone(); }
946
0
      }
947
0
    }
948
0
    return *this;
949
0
  }
950
951
  template <typename T> void set(const std::string &key, T &&value) {
952
    using D = typename std::decay<T>::type;
953
    entries_[key].reset(new detail::any_value<D>(std::forward<T>(value)));
954
  }
955
956
  template <typename T> T *get(const std::string &key) noexcept {
957
    auto it = entries_.find(key);
958
    if (it == entries_.end() || !it->second) { return nullptr; }
959
    if (it->second->type_id() != detail::any_typeid<T>()) { return nullptr; }
960
    return &static_cast<detail::any_value<T> *>(it->second.get())->value;
961
  }
962
963
  template <typename T> const T *get(const std::string &key) const noexcept {
964
    auto it = entries_.find(key);
965
    if (it == entries_.end() || !it->second) { return nullptr; }
966
    if (it->second->type_id() != detail::any_typeid<T>()) { return nullptr; }
967
    return &static_cast<const detail::any_value<T> *>(it->second.get())->value;
968
  }
969
970
0
  bool has(const std::string &key) const noexcept {
971
0
    return entries_.find(key) != entries_.end();
972
0
  }
973
974
0
  void erase(const std::string &key) { entries_.erase(key); }
975
976
0
  void clear() noexcept { entries_.clear(); }
977
978
private:
979
  std::unordered_map<std::string, std::unique_ptr<detail::any_storage>>
980
      entries_;
981
};
982
983
struct Response;
984
using ResponseHandler = std::function<bool(const Response &response)>;
985
986
struct FormData {
987
  std::string name;
988
  std::string content;
989
  std::string filename;
990
  std::string content_type;
991
  Headers headers;
992
};
993
994
struct FormField {
995
  std::string name;
996
  std::string content;
997
  Headers headers;
998
};
999
using FormFields = std::multimap<std::string, FormField>;
1000
1001
using FormFiles = std::multimap<std::string, FormData>;
1002
1003
struct MultipartFormData {
1004
  FormFields fields; // Text fields from multipart
1005
  FormFiles files;   // Files from multipart
1006
1007
  // Text field access
1008
  std::string get_field(const std::string &key, size_t id = 0) const;
1009
  std::vector<std::string> get_fields(const std::string &key) const;
1010
  bool has_field(const std::string &key) const;
1011
  size_t get_field_count(const std::string &key) const;
1012
1013
  // File access
1014
  FormData get_file(const std::string &key, size_t id = 0) const;
1015
  std::vector<FormData> get_files(const std::string &key) const;
1016
  bool has_file(const std::string &key) const;
1017
  size_t get_file_count(const std::string &key) const;
1018
};
1019
1020
struct UploadFormData {
1021
  std::string name;
1022
  std::string content;
1023
  std::string filename;
1024
  std::string content_type;
1025
};
1026
using UploadFormDataItems = std::vector<UploadFormData>;
1027
1028
class DataSink {
1029
public:
1030
0
  DataSink() : os(&sb_), sb_(*this) {}
1031
1032
  DataSink(const DataSink &) = delete;
1033
  DataSink &operator=(const DataSink &) = delete;
1034
  DataSink(DataSink &&) = delete;
1035
  DataSink &operator=(DataSink &&) = delete;
1036
1037
  std::function<bool(const char *data, size_t data_len)> write;
1038
  std::function<bool()> is_writable;
1039
  std::function<void()> done;
1040
  std::function<void(const Headers &trailer)> done_with_trailer;
1041
  std::ostream os;
1042
1043
private:
1044
  class data_sink_streambuf final : public std::streambuf {
1045
  public:
1046
0
    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
1047
1048
  protected:
1049
0
    std::streamsize xsputn(const char *s, std::streamsize n) override {
1050
0
      if (sink_.write(s, static_cast<size_t>(n))) { return n; }
1051
0
      return 0;
1052
0
    }
1053
1054
  private:
1055
    DataSink &sink_;
1056
  };
1057
1058
  data_sink_streambuf sb_;
1059
};
1060
1061
using ContentProvider =
1062
    std::function<bool(size_t offset, size_t length, DataSink &sink)>;
1063
1064
using ContentProviderWithoutLength =
1065
    std::function<bool(size_t offset, DataSink &sink)>;
1066
1067
using ContentProviderResourceReleaser = std::function<void(bool success)>;
1068
1069
struct FormDataProvider {
1070
  std::string name;
1071
  ContentProviderWithoutLength provider;
1072
  std::string filename;
1073
  std::string content_type;
1074
};
1075
using FormDataProviderItems = std::vector<FormDataProvider>;
1076
1077
inline FormDataProvider
1078
make_file_provider(const std::string &name, const std::string &filepath,
1079
                   const std::string &filename = std::string(),
1080
0
                   const std::string &content_type = std::string()) {
1081
0
  FormDataProvider fdp;
1082
0
  fdp.name = name;
1083
0
  fdp.filename = filename.empty() ? filepath : filename;
1084
0
  fdp.content_type = content_type;
1085
0
  fdp.provider = [filepath](size_t offset, DataSink &sink) -> bool {
1086
0
    std::ifstream f(filepath, std::ios::binary);
1087
0
    if (!f) { return false; }
1088
0
    if (offset > 0) {
1089
0
      f.seekg(static_cast<std::streamoff>(offset));
1090
0
      if (!f.good()) {
1091
0
        sink.done();
1092
0
        return true;
1093
0
      }
1094
0
    }
1095
0
    char buf[8192];
1096
0
    f.read(buf, sizeof(buf));
1097
0
    auto n = static_cast<size_t>(f.gcount());
1098
0
    if (n > 0) { return sink.write(buf, n); }
1099
0
    sink.done(); // EOF
1100
0
    return true;
1101
0
  };
1102
0
  return fdp;
1103
0
}
1104
1105
inline std::pair<size_t, ContentProvider>
1106
0
make_file_body(const std::string &filepath) {
1107
0
  size_t size = 0;
1108
0
  {
1109
0
    std::ifstream f(filepath, std::ios::binary | std::ios::ate);
1110
0
    if (!f) { return {0, ContentProvider{}}; }
1111
0
    size = static_cast<size_t>(f.tellg());
1112
0
  }
1113
0
1114
0
  ContentProvider provider = [filepath](size_t offset, size_t length,
1115
0
                                        DataSink &sink) -> bool {
1116
0
    std::ifstream f(filepath, std::ios::binary);
1117
0
    if (!f) { return false; }
1118
0
    f.seekg(static_cast<std::streamoff>(offset));
1119
0
    if (!f.good()) { return false; }
1120
0
    char buf[8192];
1121
0
    while (length > 0) {
1122
0
      auto to_read = (std::min)(sizeof(buf), length);
1123
0
      f.read(buf, static_cast<std::streamsize>(to_read));
1124
0
      auto n = static_cast<size_t>(f.gcount());
1125
0
      if (n == 0) { break; }
1126
0
      if (!sink.write(buf, n)) { return false; }
1127
0
      length -= n;
1128
0
    }
1129
0
    return true;
1130
0
  };
1131
0
  return {size, std::move(provider)};
1132
0
}
1133
1134
using ContentReceiverWithProgress = std::function<bool(
1135
    const char *data, size_t data_length, size_t offset, size_t total_length)>;
1136
1137
using ContentReceiver =
1138
    std::function<bool(const char *data, size_t data_length)>;
1139
1140
using FormDataHeader = std::function<bool(const FormData &file)>;
1141
1142
class ContentReader {
1143
public:
1144
  using Reader = std::function<bool(ContentReceiver receiver)>;
1145
  using FormDataReader =
1146
      std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
1147
1148
  ContentReader(Reader reader, FormDataReader multipart_reader)
1149
0
      : reader_(std::move(reader)),
1150
0
        formdata_reader_(std::move(multipart_reader)) {}
1151
1152
0
  bool operator()(FormDataHeader header, ContentReceiver receiver) const {
1153
0
    return formdata_reader_(std::move(header), std::move(receiver));
1154
0
  }
1155
1156
0
  bool operator()(ContentReceiver receiver) const {
1157
0
    return reader_(std::move(receiver));
1158
0
  }
1159
1160
  Reader reader_;
1161
  FormDataReader formdata_reader_;
1162
};
1163
1164
using Range = std::pair<ssize_t, ssize_t>;
1165
using Ranges = std::vector<Range>;
1166
1167
#ifdef CPPHTTPLIB_SSL_ENABLED
1168
// TLS abstraction layer - public type definitions and API
1169
namespace tls {
1170
1171
// Opaque handles (defined as void* for abstraction)
1172
using ctx_t = void *;
1173
using session_t = void *;
1174
using const_session_t = const void *; // For read-only session access
1175
using cert_t = void *;
1176
using ca_store_t = void *;
1177
1178
// TLS versions
1179
enum class Version {
1180
  TLS1_2 = 0x0303,
1181
  TLS1_3 = 0x0304,
1182
};
1183
1184
// Subject Alternative Names (SAN) entry types
1185
enum class SanType { DNS, IP, EMAIL, URI, OTHER };
1186
1187
// SAN entry structure
1188
struct SanEntry {
1189
  SanType type;
1190
  std::string value;
1191
};
1192
1193
// Verification context for certificate verification callback
1194
struct VerifyContext {
1195
  session_t session;        // TLS session handle
1196
  cert_t cert;              // Current certificate being verified
1197
  int depth;                // Certificate chain depth (0 = leaf)
1198
  bool preverify_ok;        // OpenSSL/Mbed TLS pre-verification result
1199
  long error_code;          // Backend-specific error code (0 = no error)
1200
  const char *error_string; // Human-readable error description
1201
1202
  // Certificate introspection methods
1203
  std::string subject_cn() const;
1204
  std::string issuer_name() const;
1205
  bool check_hostname(const char *hostname) const;
1206
  std::vector<SanEntry> sans() const;
1207
  bool validity(time_t &not_before, time_t &not_after) const;
1208
  std::string serial() const;
1209
};
1210
1211
using VerifyCallback = std::function<bool(const VerifyContext &ctx)>;
1212
1213
// TlsError codes for TLS operations (backend-independent)
1214
enum class ErrorCode : int {
1215
  Success = 0,
1216
  WantRead,         // Non-blocking: need to wait for read
1217
  WantWrite,        // Non-blocking: need to wait for write
1218
  PeerClosed,       // Peer closed the connection
1219
  Fatal,            // Unrecoverable error
1220
  SyscallError,     // System call error (check sys_errno)
1221
  CertVerifyFailed, // Certificate verification failed
1222
  HostnameMismatch, // Hostname verification failed
1223
};
1224
1225
// TLS error information
1226
struct TlsError {
1227
  ErrorCode code = ErrorCode::Fatal;
1228
  uint64_t backend_code = 0; // OpenSSL: ERR_get_error(), mbedTLS: return value
1229
  int sys_errno = 0;         // errno when SyscallError
1230
1231
  // Convert verification error code to human-readable string
1232
  static std::string verify_error_to_string(long error_code);
1233
};
1234
1235
// RAII wrapper for peer certificate
1236
class PeerCert {
1237
public:
1238
  PeerCert();
1239
  PeerCert(PeerCert &&other) noexcept;
1240
  PeerCert &operator=(PeerCert &&other) noexcept;
1241
  ~PeerCert();
1242
1243
  PeerCert(const PeerCert &) = delete;
1244
  PeerCert &operator=(const PeerCert &) = delete;
1245
1246
  explicit operator bool() const;
1247
  std::string subject_cn() const;
1248
  std::string issuer_name() const;
1249
  bool check_hostname(const char *hostname) const;
1250
  std::vector<SanEntry> sans() const;
1251
  bool validity(time_t &not_before, time_t &not_after) const;
1252
  std::string serial() const;
1253
1254
private:
1255
  explicit PeerCert(cert_t cert);
1256
  cert_t cert_ = nullptr;
1257
  friend PeerCert get_peer_cert_from_session(const_session_t session);
1258
};
1259
1260
// Callback for TLS context setup (used by SSLServer constructor)
1261
using ContextSetupCallback = std::function<bool(ctx_t ctx)>;
1262
1263
} // namespace tls
1264
#endif
1265
1266
struct Request {
1267
  std::string method;
1268
  std::string path;
1269
  std::string matched_route;
1270
  Params params;
1271
  Headers headers;
1272
  Headers trailers;
1273
  std::string body;
1274
1275
  std::string remote_addr;
1276
  int remote_port = -1;
1277
  std::string local_addr;
1278
  int local_port = -1;
1279
1280
  // for server
1281
  std::string version;
1282
  std::string target;
1283
  MultipartFormData form;
1284
  Ranges ranges;
1285
  Match matches;
1286
  std::unordered_map<std::string, std::string> path_params;
1287
0
  std::function<bool()> is_connection_closed = []() { return true; };
1288
1289
  // for client
1290
  std::vector<std::string> accept_content_types;
1291
  ResponseHandler response_handler;
1292
  ContentReceiverWithProgress content_receiver;
1293
  DownloadProgress download_progress;
1294
  UploadProgress upload_progress;
1295
1296
  bool has_header(const std::string &key) const;
1297
  std::string get_header_value(const std::string &key, const char *def = "",
1298
                               size_t id = 0) const;
1299
  size_t get_header_value_u64(const std::string &key, size_t def = 0,
1300
                              size_t id = 0) const;
1301
  size_t get_header_value_count(const std::string &key) const;
1302
  void set_header(const std::string &key, const std::string &val);
1303
1304
  bool has_trailer(const std::string &key) const;
1305
  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
1306
  size_t get_trailer_value_count(const std::string &key) const;
1307
1308
  bool has_param(const std::string &key) const;
1309
  std::string get_param_value(const std::string &key, size_t id = 0) const;
1310
  std::vector<std::string> get_param_values(const std::string &key) const;
1311
  size_t get_param_value_count(const std::string &key) const;
1312
1313
  bool is_multipart_form_data() const;
1314
1315
  // private members...
1316
  bool body_consumed_ = false;
1317
  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
1318
  size_t content_length_ = 0;
1319
  ContentProvider content_provider_;
1320
  bool is_chunked_content_provider_ = false;
1321
  size_t authorization_count_ = 0;
1322
  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
1323
      (std::chrono::steady_clock::time_point::min)();
1324
1325
#ifdef CPPHTTPLIB_SSL_ENABLED
1326
  tls::const_session_t ssl = nullptr;
1327
  tls::PeerCert peer_cert() const;
1328
  std::string sni() const;
1329
#endif
1330
};
1331
1332
struct Response {
1333
  std::string version;
1334
  int status = -1;
1335
  std::string reason;
1336
  Headers headers;
1337
  Headers trailers;
1338
  std::string body;
1339
  std::string location; // Redirect location
1340
1341
  // User-defined context — set by pre-routing/pre-request handlers and read
1342
  // by route handlers to pass arbitrary data (e.g. decoded auth tokens).
1343
  UserData user_data;
1344
1345
  bool has_header(const std::string &key) const;
1346
  std::string get_header_value(const std::string &key, const char *def = "",
1347
                               size_t id = 0) const;
1348
  size_t get_header_value_u64(const std::string &key, size_t def = 0,
1349
                              size_t id = 0) const;
1350
  size_t get_header_value_count(const std::string &key) const;
1351
  void set_header(const std::string &key, const std::string &val);
1352
1353
  bool has_trailer(const std::string &key) const;
1354
  std::string get_trailer_value(const std::string &key, size_t id = 0) const;
1355
  size_t get_trailer_value_count(const std::string &key) const;
1356
1357
  void set_redirect(const std::string &url, int status = StatusCode::Found_302);
1358
  void set_content(const char *s, size_t n, const std::string &content_type);
1359
  void set_content(const std::string &s, const std::string &content_type);
1360
  void set_content(std::string &&s, const std::string &content_type);
1361
1362
  void set_content_provider(
1363
      size_t length, const std::string &content_type, ContentProvider provider,
1364
      ContentProviderResourceReleaser resource_releaser = nullptr);
1365
1366
  void set_content_provider(
1367
      const std::string &content_type, ContentProviderWithoutLength provider,
1368
      ContentProviderResourceReleaser resource_releaser = nullptr);
1369
1370
  void set_chunked_content_provider(
1371
      const std::string &content_type, ContentProviderWithoutLength provider,
1372
      ContentProviderResourceReleaser resource_releaser = nullptr);
1373
1374
  void set_file_content(const std::string &path,
1375
                        const std::string &content_type);
1376
  void set_file_content(const std::string &path);
1377
1378
0
  Response() = default;
1379
  Response(const Response &) = default;
1380
  Response &operator=(const Response &) = default;
1381
  Response(Response &&) = default;
1382
  Response &operator=(Response &&) = default;
1383
0
  ~Response() {
1384
0
    if (content_provider_resource_releaser_) {
1385
0
      content_provider_resource_releaser_(content_provider_success_);
1386
0
    }
1387
0
  }
1388
1389
  // private members...
1390
  size_t content_length_ = 0;
1391
  ContentProvider content_provider_;
1392
  ContentProviderResourceReleaser content_provider_resource_releaser_;
1393
  bool is_chunked_content_provider_ = false;
1394
  bool content_provider_success_ = false;
1395
  std::string file_content_path_;
1396
  std::string file_content_content_type_;
1397
};
1398
1399
enum class Error {
1400
  Success = 0,
1401
  Unknown,
1402
  Connection,
1403
  BindIPAddress,
1404
  Read,
1405
  Write,
1406
  ExceedRedirectCount,
1407
  Canceled,
1408
  SSLConnection,
1409
  SSLLoadingCerts,
1410
  SSLServerVerification,
1411
  SSLServerHostnameVerification,
1412
  UnsupportedMultipartBoundaryChars,
1413
  Compression,
1414
  ConnectionTimeout,
1415
  ProxyConnection,
1416
  ConnectionClosed,
1417
  Timeout,
1418
  ResourceExhaustion,
1419
  TooManyFormDataFiles,
1420
  ExceedMaxPayloadSize,
1421
  ExceedUriMaxLength,
1422
  ExceedMaxSocketDescriptorCount,
1423
  InvalidRequestLine,
1424
  InvalidHTTPMethod,
1425
  InvalidHTTPVersion,
1426
  InvalidHeaders,
1427
  MultipartParsing,
1428
  OpenFile,
1429
  Listen,
1430
  GetSockName,
1431
  UnsupportedAddressFamily,
1432
  HTTPParsing,
1433
  InvalidRangeHeader,
1434
1435
  // For internal use only
1436
  SSLPeerCouldBeClosed_,
1437
};
1438
1439
std::string to_string(Error error);
1440
1441
std::ostream &operator<<(std::ostream &os, const Error &obj);
1442
1443
class Stream {
1444
public:
1445
0
  virtual ~Stream() = default;
1446
1447
  virtual bool is_readable() const = 0;
1448
  virtual bool wait_readable() const = 0;
1449
  virtual bool wait_writable() const = 0;
1450
0
  virtual bool is_peer_alive() const { return wait_writable(); }
1451
1452
  virtual ssize_t read(char *ptr, size_t size) = 0;
1453
  virtual ssize_t write(const char *ptr, size_t size) = 0;
1454
  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
1455
  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
1456
  virtual socket_t socket() const = 0;
1457
1458
  virtual time_t duration() const = 0;
1459
1460
0
  virtual void set_read_timeout(time_t sec, time_t usec = 0) {
1461
0
    (void)sec;
1462
0
    (void)usec;
1463
0
  }
1464
1465
  ssize_t write(const char *ptr);
1466
  ssize_t write(const std::string &s);
1467
1468
0
  Error get_error() const { return error_; }
1469
1470
protected:
1471
  Error error_ = Error::Success;
1472
};
1473
1474
class TaskQueue {
1475
public:
1476
  TaskQueue() = default;
1477
0
  virtual ~TaskQueue() = default;
1478
1479
  virtual bool enqueue(std::function<void()> fn) = 0;
1480
  virtual void shutdown() = 0;
1481
1482
0
  virtual void on_idle() {}
1483
};
1484
1485
class ThreadPool final : public TaskQueue {
1486
public:
1487
  explicit ThreadPool(size_t n, size_t max_n = 0, size_t mqr = 0);
1488
  ThreadPool(const ThreadPool &) = delete;
1489
0
  ~ThreadPool() override = default;
1490
1491
  bool enqueue(std::function<void()> fn) override;
1492
  void shutdown() override;
1493
1494
private:
1495
  void worker(bool is_dynamic);
1496
  void move_to_finished(std::thread::id id);
1497
  void cleanup_finished_threads();
1498
1499
  size_t base_thread_count_;
1500
  size_t max_thread_count_;
1501
  size_t max_queued_requests_;
1502
  size_t idle_thread_count_;
1503
1504
  bool shutdown_;
1505
1506
  std::list<std::function<void()>> jobs_;
1507
  std::vector<std::thread> threads_;       // base threads
1508
  std::list<std::thread> dynamic_threads_; // dynamic threads
1509
  std::vector<std::thread>
1510
      finished_threads_; // exited dynamic threads awaiting join
1511
1512
  std::condition_variable cond_;
1513
  std::mutex mutex_;
1514
};
1515
1516
using Logger = std::function<void(const Request &, const Response &)>;
1517
1518
// Forward declaration for Error type
1519
enum class Error;
1520
using ErrorLogger = std::function<void(const Error &, const Request *)>;
1521
1522
using SocketOptions = std::function<void(socket_t sock)>;
1523
1524
void default_socket_options(socket_t sock);
1525
1526
bool set_socket_opt(socket_t sock, int level, int optname, int optval);
1527
1528
const char *status_message(int status);
1529
1530
std::string to_string(Error error);
1531
1532
std::ostream &operator<<(std::ostream &os, const Error &obj);
1533
1534
std::string get_bearer_token_auth(const Request &req);
1535
1536
namespace detail {
1537
1538
class MatcherBase {
1539
public:
1540
0
  MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
1541
0
  virtual ~MatcherBase() = default;
1542
1543
0
  const std::string &pattern() const { return pattern_; }
1544
1545
  // Match request path and populate its matches and
1546
  virtual bool match(Request &request) const = 0;
1547
1548
private:
1549
  std::string pattern_;
1550
};
1551
1552
/**
1553
 * Captures parameters in request path and stores them in Request::path_params
1554
 *
1555
 * Capture name is a substring of a pattern from : to /.
1556
 * The rest of the pattern is matched against the request path directly
1557
 * Parameters are captured starting from the next character after
1558
 * the end of the last matched static pattern fragment until the next /.
1559
 *
1560
 * Example pattern:
1561
 * "/path/fragments/:capture/more/fragments/:second_capture"
1562
 * Static fragments:
1563
 * "/path/fragments/", "more/fragments/"
1564
 *
1565
 * Given the following request path:
1566
 * "/path/fragments/:1/more/fragments/:2"
1567
 * the resulting capture will be
1568
 * {{"capture", "1"}, {"second_capture", "2"}}
1569
 */
1570
class PathParamsMatcher final : public MatcherBase {
1571
public:
1572
  PathParamsMatcher(const std::string &pattern);
1573
1574
  bool match(Request &request) const override;
1575
1576
private:
1577
  // Treat segment separators as the end of path parameter capture
1578
  // Does not need to handle query parameters as they are parsed before path
1579
  // matching
1580
  static constexpr char separator = '/';
1581
1582
  // Contains static path fragments to match against, excluding the '/' after
1583
  // path params
1584
  // Fragments are separated by path params
1585
  std::vector<std::string> static_fragments_;
1586
  // Stores the names of the path parameters to be used as keys in the
1587
  // Request::path_params map
1588
  std::vector<std::string> param_names_;
1589
};
1590
1591
/**
1592
 * Performs std::regex_match on request path
1593
 * and stores the result in Request::matches
1594
 *
1595
 * Note that regex match is performed directly on the whole request.
1596
 * This means that wildcard patterns may match multiple path segments with /:
1597
 * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end".
1598
 */
1599
class RegexMatcher final : public MatcherBase {
1600
public:
1601
  RegexMatcher(const std::string &pattern)
1602
0
      : MatcherBase(pattern), regex_(pattern) {}
1603
1604
  bool match(Request &request) const override;
1605
1606
private:
1607
  std::regex regex_;
1608
};
1609
1610
int close_socket(socket_t sock) noexcept;
1611
1612
ssize_t write_headers(Stream &strm, const Headers &headers);
1613
1614
bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,
1615
                         time_t usec);
1616
1617
size_t get_multipart_content_length(const UploadFormDataItems &items,
1618
                                    const std::string &boundary);
1619
1620
ContentProvider
1621
make_multipart_content_provider(const UploadFormDataItems &items,
1622
                                const std::string &boundary);
1623
1624
} // namespace detail
1625
1626
class Server {
1627
public:
1628
  using Handler = std::function<void(const Request &, Response &)>;
1629
1630
  using ExceptionHandler =
1631
      std::function<void(const Request &, Response &, std::exception_ptr ep)>;
1632
1633
  enum class HandlerResponse {
1634
    Handled,
1635
    Unhandled,
1636
  };
1637
  using HandlerWithResponse =
1638
      std::function<HandlerResponse(const Request &, Response &)>;
1639
1640
  using HandlerWithContentReader = std::function<void(
1641
      const Request &, Response &, const ContentReader &content_reader)>;
1642
1643
  using Expect100ContinueHandler =
1644
      std::function<int(const Request &, Response &)>;
1645
1646
  using WebSocketHandler =
1647
      std::function<void(const Request &, ws::WebSocket &)>;
1648
  using SubProtocolSelector =
1649
      std::function<std::string(const std::vector<std::string> &protocols)>;
1650
1651
  Server();
1652
1653
  virtual ~Server();
1654
1655
  virtual bool is_valid() const;
1656
1657
  Server &Get(const std::string &pattern, Handler handler);
1658
  Server &Post(const std::string &pattern, Handler handler);
1659
  Server &Post(const std::string &pattern, HandlerWithContentReader handler);
1660
  Server &Put(const std::string &pattern, Handler handler);
1661
  Server &Put(const std::string &pattern, HandlerWithContentReader handler);
1662
  Server &Patch(const std::string &pattern, Handler handler);
1663
  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
1664
  Server &Delete(const std::string &pattern, Handler handler);
1665
  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
1666
  Server &Options(const std::string &pattern, Handler handler);
1667
1668
  Server &WebSocket(const std::string &pattern, WebSocketHandler handler);
1669
  Server &WebSocket(const std::string &pattern, WebSocketHandler handler,
1670
                    SubProtocolSelector sub_protocol_selector);
1671
1672
  bool set_base_dir(const std::string &dir,
1673
                    const std::string &mount_point = std::string());
1674
  bool set_mount_point(const std::string &mount_point, const std::string &dir,
1675
                       Headers headers = Headers());
1676
  bool remove_mount_point(const std::string &mount_point);
1677
  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
1678
                                                  const std::string &mime);
1679
  Server &set_default_file_mimetype(const std::string &mime);
1680
  Server &set_file_request_handler(Handler handler);
1681
1682
  template <class ErrorHandlerFunc>
1683
  Server &set_error_handler(ErrorHandlerFunc &&handler) {
1684
    return set_error_handler_core(
1685
        std::forward<ErrorHandlerFunc>(handler),
1686
        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
1687
  }
1688
1689
  Server &set_exception_handler(ExceptionHandler handler);
1690
1691
  Server &set_pre_routing_handler(HandlerWithResponse handler);
1692
  Server &set_post_routing_handler(Handler handler);
1693
1694
  Server &set_pre_request_handler(HandlerWithResponse handler);
1695
1696
  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
1697
  Server &set_logger(Logger logger);
1698
  Server &set_pre_compression_logger(Logger logger);
1699
  Server &set_error_logger(ErrorLogger error_logger);
1700
1701
  Server &set_address_family(int family);
1702
  Server &set_tcp_nodelay(bool on);
1703
  Server &set_ipv6_v6only(bool on);
1704
  Server &set_socket_options(SocketOptions socket_options);
1705
1706
  Server &set_default_headers(Headers headers);
1707
  Server &
1708
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1709
1710
  Server &set_trusted_proxies(const std::vector<std::string> &proxies);
1711
1712
  Server &set_keep_alive_max_count(size_t count);
1713
  Server &set_keep_alive_timeout(time_t sec);
1714
  template <class Rep, class Period>
1715
  Server &
1716
  set_keep_alive_timeout(const std::chrono::duration<Rep, Period> &duration);
1717
1718
  Server &set_read_timeout(time_t sec, time_t usec = 0);
1719
  template <class Rep, class Period>
1720
  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1721
1722
  Server &set_write_timeout(time_t sec, time_t usec = 0);
1723
  template <class Rep, class Period>
1724
  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1725
1726
  Server &set_idle_interval(time_t sec, time_t usec = 0);
1727
  template <class Rep, class Period>
1728
  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
1729
1730
  Server &set_payload_max_length(size_t length);
1731
1732
  Server &set_websocket_ping_interval(time_t sec);
1733
  template <class Rep, class Period>
1734
  Server &set_websocket_ping_interval(
1735
      const std::chrono::duration<Rep, Period> &duration);
1736
1737
  Server &set_websocket_max_missed_pongs(int count);
1738
1739
  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
1740
  int bind_to_any_port(const std::string &host, int socket_flags = 0);
1741
  bool listen_after_bind();
1742
1743
  bool listen(const std::string &host, int port, int socket_flags = 0);
1744
1745
  bool is_running() const;
1746
  void wait_until_ready() const;
1747
  void stop() noexcept;
1748
  void decommission();
1749
1750
  std::function<TaskQueue *(void)> new_task_queue;
1751
1752
protected:
1753
  bool process_request(Stream &strm, const std::string &remote_addr,
1754
                       int remote_port, const std::string &local_addr,
1755
                       int local_port, bool close_connection,
1756
                       bool &connection_closed,
1757
                       const std::function<void(Request &)> &setup_request,
1758
                       bool *websocket_upgraded = nullptr);
1759
1760
  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
1761
1762
  std::vector<std::string> trusted_proxies_;
1763
1764
  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
1765
  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
1766
  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1767
  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1768
  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1769
  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
1770
  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
1771
  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
1772
  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
1773
  time_t websocket_ping_interval_sec_ =
1774
      CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND;
1775
  int websocket_max_missed_pongs_ = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS;
1776
1777
private:
1778
  using Handlers =
1779
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
1780
  using HandlersForContentReader =
1781
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
1782
                            HandlerWithContentReader>>;
1783
1784
  static std::unique_ptr<detail::MatcherBase>
1785
  make_matcher(const std::string &pattern);
1786
1787
  template <typename H>
1788
  Server &add_handler(
1789
      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, H>> &handlers,
1790
0
      const std::string &pattern, H handler) {
1791
0
    handlers.emplace_back(make_matcher(pattern), std::move(handler));
1792
0
    return *this;
1793
0
  }
Unexecuted instantiation: httplib::Server& httplib::Server::add_handler<std::__1::function<void (httplib::Request const&, httplib::Response&)> >(std::__1::vector<std::__1::pair<std::__1::unique_ptr<httplib::detail::MatcherBase, std::__1::default_delete<httplib::detail::MatcherBase> >, std::__1::function<void (httplib::Request const&, httplib::Response&)> >, std::__1::allocator<std::__1::pair<std::__1::unique_ptr<httplib::detail::MatcherBase, std::__1::default_delete<httplib::detail::MatcherBase> >, std::__1::function<void (httplib::Request const&, httplib::Response&)> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (httplib::Request const&, httplib::Response&)>)
Unexecuted instantiation: httplib::Server& httplib::Server::add_handler<std::__1::function<void (httplib::Request const&, httplib::Response&, httplib::ContentReader const&)> >(std::__1::vector<std::__1::pair<std::__1::unique_ptr<httplib::detail::MatcherBase, std::__1::default_delete<httplib::detail::MatcherBase> >, std::__1::function<void (httplib::Request const&, httplib::Response&, httplib::ContentReader const&)> >, std::__1::allocator<std::__1::pair<std::__1::unique_ptr<httplib::detail::MatcherBase, std::__1::default_delete<httplib::detail::MatcherBase> >, std::__1::function<void (httplib::Request const&, httplib::Response&, httplib::ContentReader const&)> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (httplib::Request const&, httplib::Response&, httplib::ContentReader const&)>)
1794
1795
  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1796
  Server &set_error_handler_core(Handler handler, std::false_type);
1797
1798
  socket_t create_server_socket(const std::string &host, int port,
1799
                                int socket_flags,
1800
                                SocketOptions socket_options) const;
1801
  int bind_internal(const std::string &host, int port, int socket_flags);
1802
  bool listen_internal();
1803
1804
  bool routing(Request &req, Response &res, Stream &strm);
1805
  bool handle_file_request(Request &req, Response &res);
1806
  bool check_if_not_modified(const Request &req, Response &res,
1807
                             const std::string &etag, time_t mtime) const;
1808
  bool check_if_range(Request &req, const std::string &etag,
1809
                      time_t mtime) const;
1810
  bool dispatch_request(Request &req, Response &res,
1811
                        const Handlers &handlers) const;
1812
  bool dispatch_request_for_content_reader(
1813
      Request &req, Response &res, ContentReader content_reader,
1814
      const HandlersForContentReader &handlers) const;
1815
1816
  bool parse_request_line(const char *s, Request &req) const;
1817
  void apply_ranges(const Request &req, Response &res,
1818
                    std::string &content_type, std::string &boundary) const;
1819
  bool write_response(Stream &strm, bool close_connection, Request &req,
1820
                      Response &res);
1821
  bool write_response_with_content(Stream &strm, bool close_connection,
1822
                                   const Request &req, Response &res);
1823
  bool write_response_core(Stream &strm, bool close_connection,
1824
                           const Request &req, Response &res,
1825
                           bool need_apply_ranges);
1826
  bool write_content_with_provider(Stream &strm, const Request &req,
1827
                                   Response &res, const std::string &boundary,
1828
                                   const std::string &content_type);
1829
  bool read_content(Stream &strm, Request &req, Response &res);
1830
  bool read_content_with_content_receiver(Stream &strm, Request &req,
1831
                                          Response &res,
1832
                                          ContentReceiver receiver,
1833
                                          FormDataHeader multipart_header,
1834
                                          ContentReceiver multipart_receiver);
1835
  bool read_content_core(Stream &strm, Request &req, Response &res,
1836
                         ContentReceiver receiver,
1837
                         FormDataHeader multipart_header,
1838
                         ContentReceiver multipart_receiver) const;
1839
1840
  virtual bool process_and_close_socket(socket_t sock);
1841
1842
  void output_log(const Request &req, const Response &res) const;
1843
  void output_pre_compression_log(const Request &req,
1844
                                  const Response &res) const;
1845
  void output_error_log(const Error &err, const Request *req) const;
1846
1847
  std::atomic<bool> is_running_{false};
1848
  std::atomic<bool> is_decommissioned{false};
1849
1850
  struct MountPointEntry {
1851
    std::string mount_point;
1852
    std::string base_dir;
1853
    std::string resolved_base_dir;
1854
    Headers headers;
1855
  };
1856
  std::vector<MountPointEntry> base_dirs_;
1857
  std::map<std::string, std::string> file_extension_and_mimetype_map_;
1858
  std::string default_file_mimetype_ = "application/octet-stream";
1859
  Handler file_request_handler_;
1860
1861
  Handlers get_handlers_;
1862
  Handlers post_handlers_;
1863
  HandlersForContentReader post_handlers_for_content_reader_;
1864
  Handlers put_handlers_;
1865
  HandlersForContentReader put_handlers_for_content_reader_;
1866
  Handlers patch_handlers_;
1867
  HandlersForContentReader patch_handlers_for_content_reader_;
1868
  Handlers delete_handlers_;
1869
  HandlersForContentReader delete_handlers_for_content_reader_;
1870
  Handlers options_handlers_;
1871
1872
  struct WebSocketHandlerEntry {
1873
    std::unique_ptr<detail::MatcherBase> matcher;
1874
    WebSocketHandler handler;
1875
    SubProtocolSelector sub_protocol_selector;
1876
  };
1877
  using WebSocketHandlers = std::vector<WebSocketHandlerEntry>;
1878
  WebSocketHandlers websocket_handlers_;
1879
1880
  HandlerWithResponse error_handler_;
1881
  ExceptionHandler exception_handler_;
1882
  HandlerWithResponse pre_routing_handler_;
1883
  Handler post_routing_handler_;
1884
  HandlerWithResponse pre_request_handler_;
1885
  Expect100ContinueHandler expect_100_continue_handler_;
1886
1887
  mutable std::mutex logger_mutex_;
1888
  Logger logger_;
1889
  Logger pre_compression_logger_;
1890
  ErrorLogger error_logger_;
1891
1892
  int address_family_ = AF_UNSPEC;
1893
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1894
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1895
  SocketOptions socket_options_ = default_socket_options;
1896
1897
  Headers default_headers_;
1898
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
1899
      detail::write_headers;
1900
};
1901
1902
class Result {
1903
public:
1904
  Result() = default;
1905
  Result(std::unique_ptr<Response> &&res, Error err,
1906
         Headers &&request_headers = Headers{})
1907
      : res_(std::move(res)), err_(err),
1908
0
        request_headers_(std::move(request_headers)) {}
1909
  // Response
1910
0
  operator bool() const { return res_ != nullptr; }
1911
0
  bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1912
0
  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1913
0
  const Response &value() const { return *res_; }
1914
0
  Response &value() { return *res_; }
1915
0
  const Response &operator*() const { return *res_; }
1916
0
  Response &operator*() { return *res_; }
1917
0
  const Response *operator->() const { return res_.get(); }
1918
0
  Response *operator->() { return res_.get(); }
1919
1920
  // Error
1921
0
  Error error() const { return err_; }
1922
1923
  // Request Headers
1924
  bool has_request_header(const std::string &key) const;
1925
  std::string get_request_header_value(const std::string &key,
1926
                                       const char *def = "",
1927
                                       size_t id = 0) const;
1928
  size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
1929
                                      size_t id = 0) const;
1930
  size_t get_request_header_value_count(const std::string &key) const;
1931
1932
private:
1933
  std::unique_ptr<Response> res_;
1934
  Error err_ = Error::Unknown;
1935
  Headers request_headers_;
1936
1937
#ifdef CPPHTTPLIB_SSL_ENABLED
1938
public:
1939
  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1940
         int ssl_error)
1941
      : res_(std::move(res)), err_(err),
1942
        request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
1943
  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
1944
         int ssl_error, uint64_t ssl_backend_error)
1945
      : res_(std::move(res)), err_(err),
1946
        request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
1947
        ssl_backend_error_(ssl_backend_error) {}
1948
1949
  int ssl_error() const { return ssl_error_; }
1950
  uint64_t ssl_backend_error() const { return ssl_backend_error_; }
1951
1952
private:
1953
  int ssl_error_ = 0;
1954
  uint64_t ssl_backend_error_ = 0;
1955
#endif
1956
};
1957
1958
struct ClientConnection {
1959
  socket_t sock = INVALID_SOCKET;
1960
1961
0
  bool is_open() const { return sock != INVALID_SOCKET; }
1962
1963
  ClientConnection() = default;
1964
1965
  ~ClientConnection();
1966
1967
  ClientConnection(const ClientConnection &) = delete;
1968
  ClientConnection &operator=(const ClientConnection &) = delete;
1969
1970
  ClientConnection(ClientConnection &&other) noexcept
1971
      : sock(other.sock)
1972
#ifdef CPPHTTPLIB_SSL_ENABLED
1973
        ,
1974
        session(other.session)
1975
#endif
1976
0
  {
1977
0
    other.sock = INVALID_SOCKET;
1978
0
#ifdef CPPHTTPLIB_SSL_ENABLED
1979
0
    other.session = nullptr;
1980
0
#endif
1981
0
  }
1982
1983
0
  ClientConnection &operator=(ClientConnection &&other) noexcept {
1984
0
    if (this != &other) {
1985
0
      sock = other.sock;
1986
0
      other.sock = INVALID_SOCKET;
1987
0
#ifdef CPPHTTPLIB_SSL_ENABLED
1988
0
      session = other.session;
1989
0
      other.session = nullptr;
1990
0
#endif
1991
0
    }
1992
0
    return *this;
1993
0
  }
1994
1995
#ifdef CPPHTTPLIB_SSL_ENABLED
1996
  tls::session_t session = nullptr;
1997
#endif
1998
};
1999
2000
namespace detail {
2001
2002
struct ChunkedDecoder;
2003
2004
struct BodyReader {
2005
  Stream *stream = nullptr;
2006
  bool has_content_length = false;
2007
  size_t content_length = 0;
2008
  size_t payload_max_length = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
2009
  size_t bytes_read = 0;
2010
  bool chunked = false;
2011
  bool eof = false;
2012
  std::unique_ptr<ChunkedDecoder> chunked_decoder;
2013
  Error last_error = Error::Success;
2014
2015
  ssize_t read(char *buf, size_t len);
2016
0
  bool has_error() const { return last_error != Error::Success; }
2017
};
2018
2019
inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
2020
0
                                 size_t len) {
2021
0
  (void)stream;
2022
0
  return br.read(buf, len);
2023
0
}
2024
2025
class decompressor;
2026
2027
enum class NoProxyKind {
2028
  Wildcard,       // "*"
2029
  HostnameSuffix, // "example.com" or ".example.com"
2030
  IPv4Cidr,       // "10.0.0.0/8" (or single IP, treated as /32)
2031
  IPv6Cidr,       // "fe80::/10" (or single IP, treated as /128)
2032
};
2033
2034
// Unified 16-byte buffer holding either a v4 (first 4 bytes) or v6 address.
2035
// Lets one CIDR matcher cover both families.
2036
using IPBytes = std::array<uint8_t, 16>;
2037
2038
struct NoProxyEntry {
2039
  NoProxyKind kind = NoProxyKind::Wildcard;
2040
  std::string hostname_pattern; // lowercased, leading/trailing dot stripped
2041
  IPBytes net{};
2042
  int prefix_bits = 0;
2043
};
2044
2045
struct NormalizedTarget {
2046
  std::string hostname; // lowercase; brackets and trailing dot removed
2047
  bool is_ipv4 = false;
2048
  bool is_ipv6 = false;
2049
  IPBytes ip{};
2050
};
2051
2052
} // namespace detail
2053
2054
class ClientImpl {
2055
public:
2056
  explicit ClientImpl(const std::string &host);
2057
2058
  explicit ClientImpl(const std::string &host, int port);
2059
2060
  explicit ClientImpl(const std::string &host, int port,
2061
                      const std::string &client_cert_path,
2062
                      const std::string &client_key_path);
2063
2064
  virtual ~ClientImpl();
2065
2066
  virtual bool is_valid() const;
2067
2068
  struct StreamHandle {
2069
    std::unique_ptr<Response> response;
2070
    Error error = Error::Success;
2071
2072
    StreamHandle() = default;
2073
    StreamHandle(const StreamHandle &) = delete;
2074
    StreamHandle &operator=(const StreamHandle &) = delete;
2075
    StreamHandle(StreamHandle &&) = default;
2076
    StreamHandle &operator=(StreamHandle &&) = default;
2077
    ~StreamHandle() = default;
2078
2079
0
    bool is_valid() const {
2080
0
      return response != nullptr && error == Error::Success;
2081
0
    }
2082
2083
    ssize_t read(char *buf, size_t len);
2084
    void parse_trailers_if_needed();
2085
0
    Error get_read_error() const { return body_reader_.last_error; }
2086
0
    bool has_read_error() const { return body_reader_.has_error(); }
2087
2088
    bool trailers_parsed_ = false;
2089
2090
  private:
2091
    friend class ClientImpl;
2092
2093
    ssize_t read_with_decompression(char *buf, size_t len);
2094
2095
    std::unique_ptr<ClientConnection> connection_;
2096
    std::unique_ptr<Stream> socket_stream_;
2097
    Stream *stream_ = nullptr;
2098
    detail::BodyReader body_reader_;
2099
2100
    std::unique_ptr<detail::decompressor> decompressor_;
2101
    std::string decompress_buffer_;
2102
    size_t decompress_offset_ = 0;
2103
    size_t decompressed_bytes_read_ = 0;
2104
  };
2105
2106
  // clang-format off
2107
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
2108
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2109
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2110
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2111
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2112
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2113
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
2114
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2115
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2116
2117
  Result Head(const std::string &path);
2118
  Result Head(const std::string &path, const Headers &headers);
2119
2120
  Result Post(const std::string &path);
2121
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2122
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2123
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2124
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2125
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2126
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2127
  Result Post(const std::string &path, const Params &params);
2128
  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2129
  Result Post(const std::string &path, const Headers &headers);
2130
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2131
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2132
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2133
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2134
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2135
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2136
  Result Post(const std::string &path, const Headers &headers, const Params &params);
2137
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2138
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2139
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2140
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2141
2142
  Result Put(const std::string &path);
2143
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2144
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2145
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2146
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2147
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2148
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2149
  Result Put(const std::string &path, const Params &params);
2150
  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2151
  Result Put(const std::string &path, const Headers &headers);
2152
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2153
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2154
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2155
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2156
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2157
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2158
  Result Put(const std::string &path, const Headers &headers, const Params &params);
2159
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2160
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2161
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2162
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2163
2164
  Result Patch(const std::string &path);
2165
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2166
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2167
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2168
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2169
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2170
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2171
  Result Patch(const std::string &path, const Params &params);
2172
  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2173
  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
2174
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2175
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2176
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2177
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2178
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2179
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2180
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
2181
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2182
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2183
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2184
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2185
2186
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
2187
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2188
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2189
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2190
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2191
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2192
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2193
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2194
2195
  Result Options(const std::string &path);
2196
  Result Options(const std::string &path, const Headers &headers);
2197
  // clang-format on
2198
2199
  // Streaming API: Open a stream for reading response body incrementally
2200
  // Socket ownership is transferred to StreamHandle for true streaming
2201
  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
2202
  StreamHandle open_stream(const std::string &method, const std::string &path,
2203
                           const Params &params = {},
2204
                           const Headers &headers = {},
2205
                           const std::string &body = {},
2206
                           const std::string &content_type = {});
2207
2208
  bool send(Request &req, Response &res, Error &error);
2209
  Result send(const Request &req);
2210
2211
  void stop();
2212
2213
  std::string host() const;
2214
  int port() const;
2215
2216
  size_t is_socket_open() const;
2217
  socket_t socket() const;
2218
2219
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2220
2221
  void set_default_headers(Headers headers);
2222
2223
  void
2224
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2225
2226
  void set_address_family(int family);
2227
  void set_tcp_nodelay(bool on);
2228
  void set_ipv6_v6only(bool on);
2229
  void set_socket_options(SocketOptions socket_options);
2230
2231
  void set_connection_timeout(time_t sec, time_t usec = 0);
2232
  template <class Rep, class Period>
2233
  void
2234
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2235
2236
  void set_read_timeout(time_t sec, time_t usec = 0);
2237
  template <class Rep, class Period>
2238
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2239
2240
  void set_write_timeout(time_t sec, time_t usec = 0);
2241
  template <class Rep, class Period>
2242
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2243
2244
  void set_max_timeout(time_t msec);
2245
  template <class Rep, class Period>
2246
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2247
2248
  void set_basic_auth(const std::string &username, const std::string &password);
2249
  void set_bearer_token_auth(const std::string &token);
2250
2251
  void set_keep_alive(bool on);
2252
  void set_follow_location(bool on);
2253
2254
  void set_path_encode(bool on);
2255
2256
  void set_compress(bool on);
2257
2258
  void set_decompress(bool on);
2259
2260
  void set_payload_max_length(size_t length);
2261
2262
  void set_interface(const std::string &intf);
2263
2264
  void set_proxy(const std::string &host, int port);
2265
  void set_proxy_basic_auth(const std::string &username,
2266
                            const std::string &password);
2267
  void set_proxy_bearer_token_auth(const std::string &token);
2268
  void set_no_proxy(const std::vector<std::string> &patterns);
2269
2270
  void set_logger(Logger logger);
2271
  void set_error_logger(ErrorLogger error_logger);
2272
2273
protected:
2274
  struct Socket {
2275
    socket_t sock = INVALID_SOCKET;
2276
2277
    // For Mbed TLS compatibility: start_time for request timeout tracking
2278
    std::chrono::time_point<std::chrono::steady_clock> start_time_;
2279
2280
0
    bool is_open() const { return sock != INVALID_SOCKET; }
2281
2282
#ifdef CPPHTTPLIB_SSL_ENABLED
2283
    tls::session_t ssl = nullptr;
2284
#endif
2285
  };
2286
2287
  virtual bool create_and_connect_socket(Socket &socket, Error &error);
2288
  virtual bool ensure_socket_connection(Socket &socket, Error &error);
2289
  virtual bool setup_proxy_connection(
2290
      Socket &socket,
2291
      std::chrono::time_point<std::chrono::steady_clock> start_time,
2292
      Response &res, bool &success, Error &error);
2293
2294
  bool is_proxy_enabled_for_host(const std::string &host) const;
2295
2296
  // All of:
2297
  //   shutdown_ssl
2298
  //   shutdown_socket
2299
  //   close_socket
2300
  //   disconnect
2301
  // should ONLY be called when socket_mutex_ is locked, and only when
2302
  // no other thread is using the socket.
2303
  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
2304
  void shutdown_socket(Socket &socket) const;
2305
  void close_socket(Socket &socket);
2306
  void disconnect(bool gracefully);
2307
2308
  bool process_request(Stream &strm, Request &req, Response &res,
2309
                       bool close_connection, Error &error);
2310
2311
  bool write_content_with_provider(Stream &strm, const Request &req,
2312
                                   Error &error) const;
2313
2314
  void copy_settings(const ClientImpl &rhs);
2315
2316
  void output_log(const Request &req, const Response &res) const;
2317
  void output_error_log(const Error &err, const Request *req) const;
2318
2319
  // Socket endpoint information
2320
  const std::string host_;
2321
  const int port_;
2322
2323
  // Current open socket
2324
  Socket socket_;
2325
  mutable std::mutex socket_mutex_;
2326
  std::recursive_mutex request_mutex_;
2327
2328
  // These are all protected under socket_mutex
2329
  size_t socket_requests_in_flight_ = 0;
2330
  std::thread::id socket_requests_are_from_thread_ = std::thread::id();
2331
  bool socket_should_be_closed_when_request_is_done_ = false;
2332
2333
  // Hostname-IP map
2334
  std::map<std::string, std::string> addr_map_;
2335
2336
  // Default headers
2337
  Headers default_headers_;
2338
2339
  // Header writer
2340
  std::function<ssize_t(Stream &, Headers &)> header_writer_ =
2341
      detail::write_headers;
2342
2343
  // Settings
2344
  std::string client_cert_path_;
2345
  std::string client_key_path_;
2346
2347
  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
2348
  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
2349
  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
2350
  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
2351
  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
2352
  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
2353
  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
2354
2355
  std::string basic_auth_username_;
2356
  std::string basic_auth_password_;
2357
  std::string bearer_token_auth_token_;
2358
2359
  bool keep_alive_ = false;
2360
  bool follow_location_ = false;
2361
2362
  bool path_encode_ = true;
2363
2364
  int address_family_ = AF_UNSPEC;
2365
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
2366
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
2367
  SocketOptions socket_options_ = nullptr;
2368
2369
  bool compress_ = false;
2370
  bool decompress_ = true;
2371
2372
  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
2373
  bool has_payload_max_length_ = false;
2374
2375
  std::string interface_;
2376
2377
  std::string proxy_host_;
2378
  int proxy_port_ = -1;
2379
2380
  std::string proxy_basic_auth_username_;
2381
  std::string proxy_basic_auth_password_;
2382
  std::string proxy_bearer_token_auth_token_;
2383
2384
  std::vector<detail::NoProxyEntry> no_proxy_entries_;
2385
2386
  mutable detail::NormalizedTarget host_normalized_;
2387
  mutable bool host_normalized_valid_ = false;
2388
2389
  mutable std::mutex logger_mutex_;
2390
  Logger logger_;
2391
  ErrorLogger error_logger_;
2392
2393
private:
2394
  bool send_(Request &req, Response &res, Error &error);
2395
  Result send_(Request &&req);
2396
2397
  socket_t create_client_socket(Error &error) const;
2398
  bool read_response_line(Stream &strm, const Request &req, Response &res,
2399
                          bool skip_100_continue = true) const;
2400
  bool write_request(Stream &strm, Request &req, bool close_connection,
2401
                     Error &error, bool skip_body = false);
2402
  bool write_request_body(Stream &strm, Request &req, Error &error);
2403
  void prepare_default_headers(Request &r, bool for_stream,
2404
                               const std::string &ct);
2405
  bool redirect(Request &req, Response &res, Error &error);
2406
  bool create_redirect_client(const std::string &scheme,
2407
                              const std::string &host, int port, Request &req,
2408
                              Response &res, const std::string &path,
2409
                              const std::string &location, Error &error);
2410
  template <typename ClientType> void setup_redirect_client(ClientType &client);
2411
  bool handle_request(Stream &strm, Request &req, Response &res,
2412
                      bool close_connection, Error &error);
2413
  std::unique_ptr<Response> send_with_content_provider_and_receiver(
2414
      Request &req, const char *body, size_t content_length,
2415
      ContentProvider content_provider,
2416
      ContentProviderWithoutLength content_provider_without_length,
2417
      const std::string &content_type, ContentReceiver content_receiver,
2418
      Error &error);
2419
  Result send_with_content_provider_and_receiver(
2420
      const std::string &method, const std::string &path,
2421
      const Headers &headers, const char *body, size_t content_length,
2422
      ContentProvider content_provider,
2423
      ContentProviderWithoutLength content_provider_without_length,
2424
      const std::string &content_type, ContentReceiver content_receiver,
2425
      UploadProgress progress);
2426
  ContentProviderWithoutLength get_multipart_content_provider(
2427
      const std::string &boundary, const UploadFormDataItems &items,
2428
      const FormDataProviderItems &provider_items) const;
2429
2430
  virtual bool
2431
  process_socket(const Socket &socket,
2432
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
2433
                 std::function<bool(Stream &strm)> callback);
2434
  virtual bool is_ssl() const;
2435
2436
  void transfer_socket_ownership_to_handle(StreamHandle &handle);
2437
2438
#ifdef CPPHTTPLIB_SSL_ENABLED
2439
public:
2440
  void set_digest_auth(const std::string &username,
2441
                       const std::string &password);
2442
  void set_proxy_digest_auth(const std::string &username,
2443
                             const std::string &password);
2444
  void set_ca_cert_path(const std::string &ca_cert_file_path,
2445
                        const std::string &ca_cert_dir_path = std::string());
2446
  void enable_server_certificate_verification(bool enabled);
2447
  void enable_server_hostname_verification(bool enabled);
2448
2449
protected:
2450
  std::string digest_auth_username_;
2451
  std::string digest_auth_password_;
2452
  std::string proxy_digest_auth_username_;
2453
  std::string proxy_digest_auth_password_;
2454
  std::string ca_cert_file_path_;
2455
  std::string ca_cert_dir_path_;
2456
  bool server_certificate_verification_ = true;
2457
  bool server_hostname_verification_ = true;
2458
  std::string ca_cert_pem_; // Store CA cert PEM for redirect transfer
2459
  int last_ssl_error_ = 0;
2460
  uint64_t last_backend_error_ = 0;
2461
#endif
2462
};
2463
2464
class Client {
2465
public:
2466
  // Universal interface
2467
  explicit Client(const std::string &scheme_host_port);
2468
2469
  explicit Client(const std::string &scheme_host_port,
2470
                  const std::string &client_cert_path,
2471
                  const std::string &client_key_path);
2472
2473
  // HTTP only interface
2474
  explicit Client(const std::string &host, int port);
2475
2476
  explicit Client(const std::string &host, int port,
2477
                  const std::string &client_cert_path,
2478
                  const std::string &client_key_path);
2479
2480
  Client(Client &&) = default;
2481
  Client &operator=(Client &&) = default;
2482
2483
  ~Client();
2484
2485
  bool is_valid() const;
2486
2487
  // clang-format off
2488
  Result Get(const std::string &path, DownloadProgress progress = nullptr);
2489
  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2490
  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2491
  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2492
  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2493
  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2494
  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);
2495
  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2496
  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2497
2498
  Result Head(const std::string &path);
2499
  Result Head(const std::string &path, const Headers &headers);
2500
2501
  Result Post(const std::string &path);
2502
  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2503
  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2504
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2505
  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2506
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2507
  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2508
  Result Post(const std::string &path, const Params &params);
2509
  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2510
  Result Post(const std::string &path, const Headers &headers);
2511
  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2512
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2513
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2514
  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2515
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2516
  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2517
  Result Post(const std::string &path, const Headers &headers, const Params &params);
2518
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2519
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2520
  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2521
  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2522
2523
  Result Put(const std::string &path);
2524
  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2525
  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2526
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2527
  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2528
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2529
  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2530
  Result Put(const std::string &path, const Params &params);
2531
  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2532
  Result Put(const std::string &path, const Headers &headers);
2533
  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2534
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2535
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2536
  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2537
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2538
  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2539
  Result Put(const std::string &path, const Headers &headers, const Params &params);
2540
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2541
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2542
  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2543
  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2544
2545
  Result Patch(const std::string &path);
2546
  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2547
  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2548
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2549
  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2550
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2551
  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2552
  Result Patch(const std::string &path, const Params &params);
2553
  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2554
  Result Patch(const std::string &path, const Headers &headers);
2555
  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
2556
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
2557
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2558
  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2559
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
2560
  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
2561
  Result Patch(const std::string &path, const Headers &headers, const Params &params);
2562
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
2563
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
2564
  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
2565
  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
2566
2567
  Result Delete(const std::string &path, DownloadProgress progress = nullptr);
2568
  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2569
  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2570
  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);
2571
  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
2572
  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
2573
  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
2574
  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);
2575
2576
  Result Options(const std::string &path);
2577
  Result Options(const std::string &path, const Headers &headers);
2578
  // clang-format on
2579
2580
  // Streaming API: Open a stream for reading response body incrementally
2581
  // Socket ownership is transferred to StreamHandle for true streaming
2582
  // Supports all HTTP methods (GET, POST, PUT, PATCH, DELETE, etc.)
2583
  ClientImpl::StreamHandle open_stream(const std::string &method,
2584
                                       const std::string &path,
2585
                                       const Params &params = {},
2586
                                       const Headers &headers = {},
2587
                                       const std::string &body = {},
2588
                                       const std::string &content_type = {});
2589
2590
  bool send(Request &req, Response &res, Error &error);
2591
  Result send(const Request &req);
2592
2593
  void stop();
2594
2595
  std::string host() const;
2596
  int port() const;
2597
2598
  size_t is_socket_open() const;
2599
  socket_t socket() const;
2600
2601
  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
2602
2603
  void set_default_headers(Headers headers);
2604
2605
  void
2606
  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
2607
2608
  void set_address_family(int family);
2609
  void set_tcp_nodelay(bool on);
2610
  void set_socket_options(SocketOptions socket_options);
2611
2612
  void set_connection_timeout(time_t sec, time_t usec = 0);
2613
  template <class Rep, class Period>
2614
  void
2615
  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
2616
2617
  void set_read_timeout(time_t sec, time_t usec = 0);
2618
  template <class Rep, class Period>
2619
  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
2620
2621
  void set_write_timeout(time_t sec, time_t usec = 0);
2622
  template <class Rep, class Period>
2623
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
2624
2625
  void set_max_timeout(time_t msec);
2626
  template <class Rep, class Period>
2627
  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
2628
2629
  void set_basic_auth(const std::string &username, const std::string &password);
2630
  void set_bearer_token_auth(const std::string &token);
2631
2632
  void set_keep_alive(bool on);
2633
  void set_follow_location(bool on);
2634
2635
  void set_path_encode(bool on);
2636
2637
  void set_compress(bool on);
2638
2639
  void set_decompress(bool on);
2640
2641
  void set_payload_max_length(size_t length);
2642
2643
  void set_interface(const std::string &intf);
2644
2645
  void set_proxy(const std::string &host, int port);
2646
  void set_proxy_basic_auth(const std::string &username,
2647
                            const std::string &password);
2648
  void set_proxy_bearer_token_auth(const std::string &token);
2649
  void set_no_proxy(const std::vector<std::string> &patterns);
2650
  void set_logger(Logger logger);
2651
  void set_error_logger(ErrorLogger error_logger);
2652
2653
private:
2654
  std::unique_ptr<ClientImpl> cli_;
2655
2656
#ifdef CPPHTTPLIB_SSL_ENABLED
2657
public:
2658
  void set_digest_auth(const std::string &username,
2659
                       const std::string &password);
2660
  void set_proxy_digest_auth(const std::string &username,
2661
                             const std::string &password);
2662
  void enable_server_certificate_verification(bool enabled);
2663
  void enable_server_hostname_verification(bool enabled);
2664
  void set_ca_cert_path(const std::string &ca_cert_file_path,
2665
                        const std::string &ca_cert_dir_path = std::string());
2666
2667
  void set_ca_cert_store(tls::ca_store_t ca_cert_store);
2668
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2669
2670
  void set_server_certificate_verifier(tls::VerifyCallback verifier);
2671
2672
  void set_session_verifier(
2673
      std::function<SSLVerifierResponse(tls::session_t)> verifier);
2674
2675
  tls::ctx_t tls_context() const;
2676
2677
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2678
  void enable_windows_certificate_verification(bool enabled);
2679
#endif
2680
2681
private:
2682
  bool is_ssl_ = false;
2683
#endif
2684
};
2685
2686
#ifdef CPPHTTPLIB_SSL_ENABLED
2687
class SSLServer : public Server {
2688
public:
2689
  SSLServer(const char *cert_path, const char *private_key_path,
2690
            const char *client_ca_cert_file_path = nullptr,
2691
            const char *client_ca_cert_dir_path = nullptr,
2692
            const char *private_key_password = nullptr);
2693
2694
  struct PemMemory {
2695
    const char *cert_pem;
2696
    size_t cert_pem_len;
2697
    const char *key_pem;
2698
    size_t key_pem_len;
2699
    const char *client_ca_pem;
2700
    size_t client_ca_pem_len;
2701
    const char *private_key_password;
2702
  };
2703
  explicit SSLServer(const PemMemory &pem);
2704
2705
  // The callback receives the ctx_t handle which can be cast to the
2706
  // appropriate backend type (SSL_CTX* for OpenSSL,
2707
  // tls::impl::MbedTlsContext* for Mbed TLS)
2708
  explicit SSLServer(const tls::ContextSetupCallback &setup_callback);
2709
2710
  ~SSLServer() override;
2711
2712
  bool is_valid() const override;
2713
2714
  bool update_certs_pem(const char *cert_pem, const char *key_pem,
2715
                        const char *client_ca_pem = nullptr,
2716
                        const char *password = nullptr);
2717
2718
  tls::ctx_t tls_context() const { return ctx_; }
2719
2720
  int ssl_last_error() const { return last_ssl_error_; }
2721
2722
private:
2723
  bool process_and_close_socket(socket_t sock) override;
2724
2725
  tls::ctx_t ctx_ = nullptr;
2726
  std::mutex ctx_mutex_;
2727
2728
  int last_ssl_error_ = 0;
2729
};
2730
2731
class SSLClient final : public ClientImpl {
2732
public:
2733
  explicit SSLClient(const std::string &host);
2734
2735
  explicit SSLClient(const std::string &host, int port);
2736
2737
  explicit SSLClient(const std::string &host, int port,
2738
                     const std::string &client_cert_path,
2739
                     const std::string &client_key_path,
2740
                     const std::string &private_key_password = std::string());
2741
2742
  struct PemMemory {
2743
    const char *cert_pem;
2744
    size_t cert_pem_len;
2745
    const char *key_pem;
2746
    size_t key_pem_len;
2747
    const char *private_key_password;
2748
  };
2749
  explicit SSLClient(const std::string &host, int port, const PemMemory &pem);
2750
2751
  ~SSLClient() override;
2752
2753
  bool is_valid() const override;
2754
2755
  void set_ca_cert_store(tls::ca_store_t ca_cert_store);
2756
  void load_ca_cert_store(const char *ca_cert, std::size_t size);
2757
2758
  void set_server_certificate_verifier(tls::VerifyCallback verifier);
2759
2760
  // Post-handshake session verifier (backend-independent)
2761
  void set_session_verifier(
2762
      std::function<SSLVerifierResponse(tls::session_t)> verifier);
2763
2764
  tls::ctx_t tls_context() const { return ctx_; }
2765
2766
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2767
  void enable_windows_certificate_verification(bool enabled);
2768
#endif
2769
2770
private:
2771
  bool create_and_connect_socket(Socket &socket, Error &error) override;
2772
  bool ensure_socket_connection(Socket &socket, Error &error) override;
2773
  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
2774
  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
2775
2776
  bool
2777
  process_socket(const Socket &socket,
2778
                 std::chrono::time_point<std::chrono::steady_clock> start_time,
2779
                 std::function<bool(Stream &strm)> callback) override;
2780
  bool is_ssl() const override;
2781
2782
  bool setup_proxy_connection(
2783
      Socket &socket,
2784
      std::chrono::time_point<std::chrono::steady_clock> start_time,
2785
      Response &res, bool &success, Error &error) override;
2786
  bool connect_with_proxy(
2787
      Socket &sock,
2788
      std::chrono::time_point<std::chrono::steady_clock> start_time,
2789
      Response &res, bool &success, Error &error);
2790
  bool initialize_ssl(Socket &socket, Error &error);
2791
2792
  void init_ctx();
2793
  void reset_ctx_on_error();
2794
2795
  bool load_certs();
2796
2797
  tls::ctx_t ctx_ = nullptr;
2798
  std::mutex ctx_mutex_;
2799
  std::once_flag initialize_cert_;
2800
2801
  long verify_result_ = 0;
2802
2803
  std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
2804
2805
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
2806
  bool enable_windows_cert_verification_ = true;
2807
#endif
2808
2809
  friend class ClientImpl;
2810
2811
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2812
private:
2813
  bool verify_host(X509 *server_cert) const;
2814
  bool verify_host_with_subject_alt_name(X509 *server_cert) const;
2815
  bool verify_host_with_common_name(X509 *server_cert) const;
2816
#endif
2817
};
2818
#endif // CPPHTTPLIB_SSL_ENABLED
2819
2820
namespace detail {
2821
2822
template <typename T, typename U>
2823
inline void duration_to_sec_and_usec(const T &duration, U callback) {
2824
  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
2825
  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
2826
                  duration - std::chrono::seconds(sec))
2827
                  .count();
2828
  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2829
}
2830
2831
0
template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2832
0
  return N - 1;
2833
0
}
Unexecuted instantiation: unsigned long httplib::detail::str_len<4ul>(char const (&) [4ul])
Unexecuted instantiation: unsigned long httplib::detail::str_len<3ul>(char const (&) [3ul])
Unexecuted instantiation: unsigned long httplib::detail::str_len<14ul>(char const (&) [14ul])
Unexecuted instantiation: unsigned long httplib::detail::str_len<8ul>(char const (&) [8ul])
Unexecuted instantiation: unsigned long httplib::detail::str_len<21ul>(char const (&) [21ul])
Unexecuted instantiation: unsigned long httplib::detail::str_len<11ul>(char const (&) [11ul])
2834
2835
0
inline bool is_numeric(const std::string &str) {
2836
0
  return !str.empty() &&
2837
0
         std::all_of(str.cbegin(), str.cend(),
2838
0
                     [](unsigned char c) { return std::isdigit(c); });
2839
0
}
2840
2841
inline size_t get_header_value_u64(const Headers &headers,
2842
                                   const std::string &key, size_t def,
2843
0
                                   size_t id, bool &is_invalid_value) {
2844
0
  is_invalid_value = false;
2845
0
  auto rng = headers.equal_range(key);
2846
0
  auto it = rng.first;
2847
0
  std::advance(it, static_cast<ssize_t>(id));
2848
0
  if (it != rng.second) {
2849
0
    if (is_numeric(it->second)) {
2850
0
      return static_cast<size_t>(std::strtoull(it->second.data(), nullptr, 10));
2851
0
    } else {
2852
0
      is_invalid_value = true;
2853
0
    }
2854
0
  }
2855
0
  return def;
2856
0
}
2857
2858
inline size_t get_header_value_u64(const Headers &headers,
2859
                                   const std::string &key, size_t def,
2860
0
                                   size_t id) {
2861
0
  auto dummy = false;
2862
0
  return get_header_value_u64(headers, key, def, id, dummy);
2863
0
}
2864
2865
} // namespace detail
2866
2867
template <class Rep, class Period>
2868
inline Server &
2869
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2870
  detail::duration_to_sec_and_usec(
2871
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2872
  return *this;
2873
}
2874
2875
template <class Rep, class Period>
2876
inline Server &
2877
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2878
  detail::duration_to_sec_and_usec(
2879
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2880
  return *this;
2881
}
2882
2883
template <class Rep, class Period>
2884
inline Server &
2885
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
2886
  detail::duration_to_sec_and_usec(
2887
      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
2888
  return *this;
2889
}
2890
2891
template <class Rep, class Period>
2892
inline void ClientImpl::set_connection_timeout(
2893
    const std::chrono::duration<Rep, Period> &duration) {
2894
  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
2895
    set_connection_timeout(sec, usec);
2896
  });
2897
}
2898
2899
template <class Rep, class Period>
2900
inline void ClientImpl::set_read_timeout(
2901
    const std::chrono::duration<Rep, Period> &duration) {
2902
  detail::duration_to_sec_and_usec(
2903
      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2904
}
2905
2906
template <class Rep, class Period>
2907
inline void ClientImpl::set_write_timeout(
2908
    const std::chrono::duration<Rep, Period> &duration) {
2909
  detail::duration_to_sec_and_usec(
2910
      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2911
}
2912
2913
template <class Rep, class Period>
2914
inline void ClientImpl::set_max_timeout(
2915
    const std::chrono::duration<Rep, Period> &duration) {
2916
  auto msec =
2917
      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2918
  set_max_timeout(msec);
2919
}
2920
2921
template <class Rep, class Period>
2922
inline void Client::set_connection_timeout(
2923
    const std::chrono::duration<Rep, Period> &duration) {
2924
  cli_->set_connection_timeout(duration);
2925
}
2926
2927
template <class Rep, class Period>
2928
inline void
2929
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2930
  cli_->set_read_timeout(duration);
2931
}
2932
2933
template <class Rep, class Period>
2934
inline void
2935
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2936
  cli_->set_write_timeout(duration);
2937
}
2938
2939
0
inline void Client::set_max_timeout(time_t msec) {
2940
0
  cli_->set_max_timeout(msec);
2941
0
}
2942
2943
template <class Rep, class Period>
2944
inline void
2945
Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2946
  cli_->set_max_timeout(duration);
2947
}
2948
2949
/*
2950
 * Forward declarations and types that will be part of the .h file if split into
2951
 * .h + .cc.
2952
 */
2953
2954
std::string hosted_at(const std::string &hostname);
2955
2956
void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2957
2958
// JavaScript-style URL encoding/decoding functions
2959
std::string encode_uri_component(const std::string &value);
2960
std::string encode_uri(const std::string &value);
2961
std::string decode_uri_component(const std::string &value);
2962
std::string decode_uri(const std::string &value);
2963
2964
// RFC 3986 compliant URL component encoding/decoding functions
2965
std::string encode_path_component(const std::string &component);
2966
std::string decode_path_component(const std::string &component);
2967
std::string encode_query_component(const std::string &component,
2968
                                   bool space_as_plus = true);
2969
std::string decode_query_component(const std::string &component,
2970
                                   bool plus_as_space = true);
2971
2972
std::string sanitize_filename(const std::string &filename);
2973
2974
std::string append_query_params(const std::string &path, const Params &params);
2975
2976
std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2977
2978
std::pair<std::string, std::string>
2979
make_basic_authentication_header(const std::string &username,
2980
                                 const std::string &password,
2981
                                 bool is_proxy = false);
2982
2983
namespace detail {
2984
2985
#if defined(_WIN32)
2986
inline std::wstring u8string_to_wstring(const char *s) {
2987
  if (!s) { return std::wstring(); }
2988
2989
  auto len = static_cast<int>(strlen(s));
2990
  if (!len) { return std::wstring(); }
2991
2992
  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2993
  if (!wlen) { return std::wstring(); }
2994
2995
  std::wstring ws;
2996
  ws.resize(wlen);
2997
  wlen = ::MultiByteToWideChar(
2998
      CP_UTF8, 0, s, len,
2999
      const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
3000
  if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
3001
  return ws;
3002
}
3003
#endif
3004
3005
struct FileStat {
3006
  FileStat(const std::string &path);
3007
  bool is_file() const;
3008
  bool is_dir() const;
3009
  time_t mtime() const;
3010
  size_t size() const;
3011
3012
private:
3013
#if defined(_WIN32)
3014
  struct _stat st_;
3015
#else
3016
  struct stat st_;
3017
#endif
3018
  int ret_ = -1;
3019
};
3020
3021
std::string make_host_and_port_string(const std::string &host, int port,
3022
                                      bool is_ssl);
3023
3024
std::string trim_copy(const std::string &s);
3025
3026
void divide(
3027
    const char *data, std::size_t size, char d,
3028
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
3029
        fn);
3030
3031
void divide(
3032
    const std::string &str, char d,
3033
    std::function<void(const char *, std::size_t, const char *, std::size_t)>
3034
        fn);
3035
3036
void split(const char *b, const char *e, char d,
3037
           std::function<void(const char *, const char *)> fn);
3038
3039
void split(const char *b, const char *e, char d, size_t m,
3040
           std::function<void(const char *, const char *)> fn);
3041
3042
bool process_client_socket(
3043
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3044
    time_t write_timeout_sec, time_t write_timeout_usec,
3045
    time_t max_timeout_msec,
3046
    std::chrono::time_point<std::chrono::steady_clock> start_time,
3047
    std::function<bool(Stream &)> callback);
3048
3049
socket_t create_client_socket(const std::string &host, const std::string &ip,
3050
                              int port, int address_family, bool tcp_nodelay,
3051
                              bool ipv6_v6only, SocketOptions socket_options,
3052
                              time_t connection_timeout_sec,
3053
                              time_t connection_timeout_usec,
3054
                              time_t read_timeout_sec, time_t read_timeout_usec,
3055
                              time_t write_timeout_sec,
3056
                              time_t write_timeout_usec,
3057
                              const std::string &intf, Error &error);
3058
3059
const char *get_header_value(const Headers &headers, const std::string &key,
3060
                             const char *def, size_t id);
3061
3062
std::string params_to_query_str(const Params &params);
3063
3064
void parse_query_text(const char *data, std::size_t size, Params &params);
3065
3066
void parse_query_text(const std::string &s, Params &params);
3067
3068
bool parse_multipart_boundary(const std::string &content_type,
3069
                              std::string &boundary);
3070
3071
bool parse_range_header(const std::string &s, Ranges &ranges);
3072
3073
bool parse_accept_header(const std::string &s,
3074
                         std::vector<std::string> &content_types);
3075
3076
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
3077
3078
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
3079
3080
enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
3081
3082
EncodingType encoding_type(const Request &req, const Response &res);
3083
3084
class BufferStream final : public Stream {
3085
public:
3086
0
  BufferStream() = default;
3087
0
  ~BufferStream() override = default;
3088
3089
  bool is_readable() const override;
3090
  bool wait_readable() const override;
3091
  bool wait_writable() const override;
3092
  ssize_t read(char *ptr, size_t size) override;
3093
  ssize_t write(const char *ptr, size_t size) override;
3094
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
3095
  void get_local_ip_and_port(std::string &ip, int &port) const override;
3096
  socket_t socket() const override;
3097
  time_t duration() const override;
3098
3099
  const std::string &get_buffer() const;
3100
3101
private:
3102
  std::string buffer;
3103
  size_t position = 0;
3104
};
3105
3106
class compressor {
3107
public:
3108
0
  virtual ~compressor() = default;
3109
3110
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
3111
  virtual bool compress(const char *data, size_t data_length, bool last,
3112
                        Callback callback) = 0;
3113
};
3114
3115
class decompressor {
3116
public:
3117
0
  virtual ~decompressor() = default;
3118
3119
  virtual bool is_valid() const = 0;
3120
3121
  typedef std::function<bool(const char *data, size_t data_len)> Callback;
3122
  virtual bool decompress(const char *data, size_t data_length,
3123
                          Callback callback) = 0;
3124
};
3125
3126
class nocompressor final : public compressor {
3127
public:
3128
0
  ~nocompressor() override = default;
3129
3130
  bool compress(const char *data, size_t data_length, bool /*last*/,
3131
                Callback callback) override;
3132
};
3133
3134
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3135
class gzip_compressor final : public compressor {
3136
public:
3137
  gzip_compressor();
3138
  ~gzip_compressor() override;
3139
3140
  bool compress(const char *data, size_t data_length, bool last,
3141
                Callback callback) override;
3142
3143
private:
3144
  bool is_valid_ = false;
3145
  z_stream strm_;
3146
};
3147
3148
class gzip_decompressor final : public decompressor {
3149
public:
3150
  gzip_decompressor();
3151
  ~gzip_decompressor() override;
3152
3153
  bool is_valid() const override;
3154
3155
  bool decompress(const char *data, size_t data_length,
3156
                  Callback callback) override;
3157
3158
private:
3159
  bool is_valid_ = false;
3160
  z_stream strm_;
3161
};
3162
#endif
3163
3164
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3165
class brotli_compressor final : public compressor {
3166
public:
3167
  brotli_compressor();
3168
  ~brotli_compressor();
3169
3170
  bool compress(const char *data, size_t data_length, bool last,
3171
                Callback callback) override;
3172
3173
private:
3174
  BrotliEncoderState *state_ = nullptr;
3175
};
3176
3177
class brotli_decompressor final : public decompressor {
3178
public:
3179
  brotli_decompressor();
3180
  ~brotli_decompressor();
3181
3182
  bool is_valid() const override;
3183
3184
  bool decompress(const char *data, size_t data_length,
3185
                  Callback callback) override;
3186
3187
private:
3188
  BrotliDecoderResult decoder_r;
3189
  BrotliDecoderState *decoder_s = nullptr;
3190
};
3191
#endif
3192
3193
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
3194
class zstd_compressor : public compressor {
3195
public:
3196
  zstd_compressor();
3197
  ~zstd_compressor();
3198
3199
  bool compress(const char *data, size_t data_length, bool last,
3200
                Callback callback) override;
3201
3202
private:
3203
  ZSTD_CCtx *ctx_ = nullptr;
3204
};
3205
3206
class zstd_decompressor : public decompressor {
3207
public:
3208
  zstd_decompressor();
3209
  ~zstd_decompressor();
3210
3211
  bool is_valid() const override;
3212
3213
  bool decompress(const char *data, size_t data_length,
3214
                  Callback callback) override;
3215
3216
private:
3217
  ZSTD_DCtx *ctx_ = nullptr;
3218
};
3219
#endif
3220
3221
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
3222
// to store data. The call can set memory on stack for performance.
3223
class stream_line_reader {
3224
public:
3225
  stream_line_reader(Stream &strm, char *fixed_buffer,
3226
                     size_t fixed_buffer_size);
3227
  const char *ptr() const;
3228
  size_t size() const;
3229
  bool end_with_crlf() const;
3230
  bool getline();
3231
3232
private:
3233
  void append(char c);
3234
3235
  Stream &strm_;
3236
  char *fixed_buffer_;
3237
  const size_t fixed_buffer_size_;
3238
  size_t fixed_buffer_used_size_ = 0;
3239
  std::string growable_buffer_;
3240
};
3241
3242
bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
3243
                    const Headers &src_headers);
3244
3245
struct ChunkedDecoder {
3246
  Stream &strm;
3247
  size_t chunk_remaining = 0;
3248
  bool finished = false;
3249
  char line_buf[64];
3250
  size_t last_chunk_total = 0;
3251
  size_t last_chunk_offset = 0;
3252
3253
  explicit ChunkedDecoder(Stream &s);
3254
3255
  ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
3256
                       size_t &out_chunk_total);
3257
3258
  bool parse_trailers_into(Headers &dest, const Headers &src_headers);
3259
};
3260
3261
class mmap {
3262
public:
3263
  mmap(const char *path);
3264
  ~mmap();
3265
3266
  bool open(const char *path);
3267
  void close();
3268
3269
  bool is_open() const;
3270
  size_t size() const;
3271
  const char *data() const;
3272
3273
private:
3274
#if defined(_WIN32)
3275
  HANDLE hFile_ = NULL;
3276
  HANDLE hMapping_ = NULL;
3277
#else
3278
  int fd_ = -1;
3279
#endif
3280
  size_t size_ = 0;
3281
  void *addr_ = nullptr;
3282
  bool is_open_empty_file = false;
3283
};
3284
3285
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
3286
namespace fields {
3287
3288
bool is_token_char(char c);
3289
bool is_token(const std::string &s);
3290
bool is_field_name(const std::string &s);
3291
bool is_vchar(char c);
3292
bool is_obs_text(char c);
3293
bool is_field_vchar(char c);
3294
bool is_field_content(const std::string &s);
3295
bool is_field_value(const std::string &s);
3296
3297
} // namespace fields
3298
} // namespace detail
3299
3300
/*
3301
 * TLS Abstraction Layer Declarations
3302
 */
3303
3304
#ifdef CPPHTTPLIB_SSL_ENABLED
3305
// TLS abstraction layer - backend-specific type declarations
3306
#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
3307
namespace tls {
3308
namespace impl {
3309
3310
// Mbed TLS context wrapper (holds config, entropy, DRBG, CA chain, own
3311
// cert/key). This struct is accessible via tls::impl for use in SSL context
3312
// setup callbacks (cast ctx_t to tls::impl::MbedTlsContext*).
3313
struct MbedTlsContext {
3314
  mbedtls_ssl_config conf;
3315
  mbedtls_entropy_context entropy;
3316
  mbedtls_ctr_drbg_context ctr_drbg;
3317
  mbedtls_x509_crt ca_chain;
3318
  mbedtls_x509_crt own_cert;
3319
  mbedtls_pk_context own_key;
3320
  bool is_server = false;
3321
  bool verify_client = false;
3322
  bool has_verify_callback = false;
3323
3324
  MbedTlsContext();
3325
  ~MbedTlsContext();
3326
3327
  MbedTlsContext(const MbedTlsContext &) = delete;
3328
  MbedTlsContext &operator=(const MbedTlsContext &) = delete;
3329
};
3330
3331
} // namespace impl
3332
} // namespace tls
3333
#endif
3334
3335
#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
3336
namespace tls {
3337
namespace impl {
3338
3339
// wolfSSL context wrapper (holds WOLFSSL_CTX and related state).
3340
// This struct is accessible via tls::impl for use in SSL context
3341
// setup callbacks (cast ctx_t to tls::impl::WolfSSLContext*).
3342
struct WolfSSLContext {
3343
  WOLFSSL_CTX *ctx = nullptr;
3344
  bool is_server = false;
3345
  bool verify_client = false;
3346
  bool has_verify_callback = false;
3347
  std::string ca_pem_data_; // accumulated PEM for get_ca_names/get_ca_certs
3348
3349
  WolfSSLContext();
3350
  ~WolfSSLContext();
3351
3352
  WolfSSLContext(const WolfSSLContext &) = delete;
3353
  WolfSSLContext &operator=(const WolfSSLContext &) = delete;
3354
};
3355
3356
// CA store for wolfSSL: holds raw PEM bytes to allow reloading into any ctx
3357
struct WolfSSLCAStore {
3358
  std::string pem_data;
3359
};
3360
3361
} // namespace impl
3362
} // namespace tls
3363
#endif
3364
3365
#endif // CPPHTTPLIB_SSL_ENABLED
3366
3367
namespace stream {
3368
3369
class Result {
3370
public:
3371
  Result();
3372
  explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192);
3373
  Result(Result &&other) noexcept;
3374
  Result &operator=(Result &&other) noexcept;
3375
  Result(const Result &) = delete;
3376
  Result &operator=(const Result &) = delete;
3377
3378
  // Response info
3379
  bool is_valid() const;
3380
  explicit operator bool() const;
3381
  int status() const;
3382
  const Headers &headers() const;
3383
  std::string get_header_value(const std::string &key,
3384
                               const char *def = "") const;
3385
  bool has_header(const std::string &key) const;
3386
  Error error() const;
3387
  Error read_error() const;
3388
  bool has_read_error() const;
3389
3390
  // Stream reading
3391
  bool next();
3392
  const char *data() const;
3393
  size_t size() const;
3394
  std::string read_all();
3395
3396
private:
3397
  ClientImpl::StreamHandle handle_;
3398
  std::string buffer_;
3399
  size_t current_size_ = 0;
3400
  size_t chunk_size_;
3401
  bool finished_ = false;
3402
};
3403
3404
// GET
3405
template <typename ClientType>
3406
inline Result Get(ClientType &cli, const std::string &path,
3407
                  size_t chunk_size = 8192) {
3408
  return Result{cli.open_stream("GET", path), chunk_size};
3409
}
3410
3411
template <typename ClientType>
3412
inline Result Get(ClientType &cli, const std::string &path,
3413
0
                  const Headers &headers, size_t chunk_size = 8192) {
3414
0
  return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
3415
0
}
3416
3417
template <typename ClientType>
3418
inline Result Get(ClientType &cli, const std::string &path,
3419
                  const Params &params, size_t chunk_size = 8192) {
3420
  return Result{cli.open_stream("GET", path, params), chunk_size};
3421
}
3422
3423
template <typename ClientType>
3424
inline Result Get(ClientType &cli, const std::string &path,
3425
                  const Params &params, const Headers &headers,
3426
                  size_t chunk_size = 8192) {
3427
  return Result{cli.open_stream("GET", path, params, headers), chunk_size};
3428
}
3429
3430
// POST
3431
template <typename ClientType>
3432
inline Result Post(ClientType &cli, const std::string &path,
3433
                   const std::string &body, const std::string &content_type,
3434
                   size_t chunk_size = 8192) {
3435
  return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
3436
                chunk_size};
3437
}
3438
3439
template <typename ClientType>
3440
inline Result Post(ClientType &cli, const std::string &path,
3441
                   const Headers &headers, const std::string &body,
3442
                   const std::string &content_type, size_t chunk_size = 8192) {
3443
  return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
3444
                chunk_size};
3445
}
3446
3447
template <typename ClientType>
3448
inline Result Post(ClientType &cli, const std::string &path,
3449
                   const Params &params, const std::string &body,
3450
                   const std::string &content_type, size_t chunk_size = 8192) {
3451
  return Result{cli.open_stream("POST", path, params, {}, body, content_type),
3452
                chunk_size};
3453
}
3454
3455
template <typename ClientType>
3456
inline Result Post(ClientType &cli, const std::string &path,
3457
                   const Params &params, const Headers &headers,
3458
                   const std::string &body, const std::string &content_type,
3459
                   size_t chunk_size = 8192) {
3460
  return Result{
3461
      cli.open_stream("POST", path, params, headers, body, content_type),
3462
      chunk_size};
3463
}
3464
3465
// PUT
3466
template <typename ClientType>
3467
inline Result Put(ClientType &cli, const std::string &path,
3468
                  const std::string &body, const std::string &content_type,
3469
                  size_t chunk_size = 8192) {
3470
  return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
3471
                chunk_size};
3472
}
3473
3474
template <typename ClientType>
3475
inline Result Put(ClientType &cli, const std::string &path,
3476
                  const Headers &headers, const std::string &body,
3477
                  const std::string &content_type, size_t chunk_size = 8192) {
3478
  return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
3479
                chunk_size};
3480
}
3481
3482
template <typename ClientType>
3483
inline Result Put(ClientType &cli, const std::string &path,
3484
                  const Params &params, const std::string &body,
3485
                  const std::string &content_type, size_t chunk_size = 8192) {
3486
  return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
3487
                chunk_size};
3488
}
3489
3490
template <typename ClientType>
3491
inline Result Put(ClientType &cli, const std::string &path,
3492
                  const Params &params, const Headers &headers,
3493
                  const std::string &body, const std::string &content_type,
3494
                  size_t chunk_size = 8192) {
3495
  return Result{
3496
      cli.open_stream("PUT", path, params, headers, body, content_type),
3497
      chunk_size};
3498
}
3499
3500
// PATCH
3501
template <typename ClientType>
3502
inline Result Patch(ClientType &cli, const std::string &path,
3503
                    const std::string &body, const std::string &content_type,
3504
                    size_t chunk_size = 8192) {
3505
  return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
3506
                chunk_size};
3507
}
3508
3509
template <typename ClientType>
3510
inline Result Patch(ClientType &cli, const std::string &path,
3511
                    const Headers &headers, const std::string &body,
3512
                    const std::string &content_type, size_t chunk_size = 8192) {
3513
  return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
3514
                chunk_size};
3515
}
3516
3517
template <typename ClientType>
3518
inline Result Patch(ClientType &cli, const std::string &path,
3519
                    const Params &params, const std::string &body,
3520
                    const std::string &content_type, size_t chunk_size = 8192) {
3521
  return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
3522
                chunk_size};
3523
}
3524
3525
template <typename ClientType>
3526
inline Result Patch(ClientType &cli, const std::string &path,
3527
                    const Params &params, const Headers &headers,
3528
                    const std::string &body, const std::string &content_type,
3529
                    size_t chunk_size = 8192) {
3530
  return Result{
3531
      cli.open_stream("PATCH", path, params, headers, body, content_type),
3532
      chunk_size};
3533
}
3534
3535
// DELETE
3536
template <typename ClientType>
3537
inline Result Delete(ClientType &cli, const std::string &path,
3538
                     size_t chunk_size = 8192) {
3539
  return Result{cli.open_stream("DELETE", path), chunk_size};
3540
}
3541
3542
template <typename ClientType>
3543
inline Result Delete(ClientType &cli, const std::string &path,
3544
                     const Headers &headers, size_t chunk_size = 8192) {
3545
  return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
3546
}
3547
3548
template <typename ClientType>
3549
inline Result Delete(ClientType &cli, const std::string &path,
3550
                     const std::string &body, const std::string &content_type,
3551
                     size_t chunk_size = 8192) {
3552
  return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
3553
                chunk_size};
3554
}
3555
3556
template <typename ClientType>
3557
inline Result Delete(ClientType &cli, const std::string &path,
3558
                     const Headers &headers, const std::string &body,
3559
                     const std::string &content_type,
3560
                     size_t chunk_size = 8192) {
3561
  return Result{
3562
      cli.open_stream("DELETE", path, {}, headers, body, content_type),
3563
      chunk_size};
3564
}
3565
3566
template <typename ClientType>
3567
inline Result Delete(ClientType &cli, const std::string &path,
3568
                     const Params &params, size_t chunk_size = 8192) {
3569
  return Result{cli.open_stream("DELETE", path, params), chunk_size};
3570
}
3571
3572
template <typename ClientType>
3573
inline Result Delete(ClientType &cli, const std::string &path,
3574
                     const Params &params, const Headers &headers,
3575
                     size_t chunk_size = 8192) {
3576
  return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
3577
}
3578
3579
template <typename ClientType>
3580
inline Result Delete(ClientType &cli, const std::string &path,
3581
                     const Params &params, const std::string &body,
3582
                     const std::string &content_type,
3583
                     size_t chunk_size = 8192) {
3584
  return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
3585
                chunk_size};
3586
}
3587
3588
template <typename ClientType>
3589
inline Result Delete(ClientType &cli, const std::string &path,
3590
                     const Params &params, const Headers &headers,
3591
                     const std::string &body, const std::string &content_type,
3592
                     size_t chunk_size = 8192) {
3593
  return Result{
3594
      cli.open_stream("DELETE", path, params, headers, body, content_type),
3595
      chunk_size};
3596
}
3597
3598
// HEAD
3599
template <typename ClientType>
3600
inline Result Head(ClientType &cli, const std::string &path,
3601
                   size_t chunk_size = 8192) {
3602
  return Result{cli.open_stream("HEAD", path), chunk_size};
3603
}
3604
3605
template <typename ClientType>
3606
inline Result Head(ClientType &cli, const std::string &path,
3607
                   const Headers &headers, size_t chunk_size = 8192) {
3608
  return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
3609
}
3610
3611
template <typename ClientType>
3612
inline Result Head(ClientType &cli, const std::string &path,
3613
                   const Params &params, size_t chunk_size = 8192) {
3614
  return Result{cli.open_stream("HEAD", path, params), chunk_size};
3615
}
3616
3617
template <typename ClientType>
3618
inline Result Head(ClientType &cli, const std::string &path,
3619
                   const Params &params, const Headers &headers,
3620
                   size_t chunk_size = 8192) {
3621
  return Result{cli.open_stream("HEAD", path, params, headers), chunk_size};
3622
}
3623
3624
// OPTIONS
3625
template <typename ClientType>
3626
inline Result Options(ClientType &cli, const std::string &path,
3627
                      size_t chunk_size = 8192) {
3628
  return Result{cli.open_stream("OPTIONS", path), chunk_size};
3629
}
3630
3631
template <typename ClientType>
3632
inline Result Options(ClientType &cli, const std::string &path,
3633
                      const Headers &headers, size_t chunk_size = 8192) {
3634
  return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
3635
}
3636
3637
template <typename ClientType>
3638
inline Result Options(ClientType &cli, const std::string &path,
3639
                      const Params &params, size_t chunk_size = 8192) {
3640
  return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
3641
}
3642
3643
template <typename ClientType>
3644
inline Result Options(ClientType &cli, const std::string &path,
3645
                      const Params &params, const Headers &headers,
3646
                      size_t chunk_size = 8192) {
3647
  return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
3648
}
3649
3650
} // namespace stream
3651
3652
namespace sse {
3653
3654
struct SSEMessage {
3655
  std::string event; // Event type (default: "message")
3656
  std::string data;  // Event payload
3657
  std::string id;    // Event ID for Last-Event-ID header
3658
3659
  SSEMessage();
3660
  void clear();
3661
};
3662
3663
class SSEClient {
3664
public:
3665
  using MessageHandler = std::function<void(const SSEMessage &)>;
3666
  using ErrorHandler = std::function<void(Error)>;
3667
  using OpenHandler = std::function<void()>;
3668
3669
  SSEClient(Client &client, const std::string &path);
3670
  SSEClient(Client &client, const std::string &path, const Headers &headers);
3671
  ~SSEClient();
3672
3673
  SSEClient(const SSEClient &) = delete;
3674
  SSEClient &operator=(const SSEClient &) = delete;
3675
3676
  // Event handlers
3677
  SSEClient &on_message(MessageHandler handler);
3678
  SSEClient &on_event(const std::string &type, MessageHandler handler);
3679
  SSEClient &on_open(OpenHandler handler);
3680
  SSEClient &on_error(ErrorHandler handler);
3681
  SSEClient &set_reconnect_interval(int ms);
3682
  SSEClient &set_max_reconnect_attempts(int n);
3683
3684
  // Update headers (thread-safe)
3685
  SSEClient &set_headers(const Headers &headers);
3686
3687
  // State accessors
3688
  bool is_connected() const;
3689
  const std::string &last_event_id() const;
3690
3691
  // Blocking start - runs event loop with auto-reconnect
3692
  void start();
3693
3694
  // Non-blocking start - runs in background thread
3695
  void start_async();
3696
3697
  // Stop the client (thread-safe)
3698
  void stop();
3699
3700
private:
3701
  bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms);
3702
  void run_event_loop();
3703
  void dispatch_event(const SSEMessage &msg);
3704
  bool should_reconnect(int count) const;
3705
  void wait_for_reconnect();
3706
3707
  // Client and path
3708
  Client &client_;
3709
  std::string path_;
3710
  Headers headers_;
3711
  mutable std::mutex headers_mutex_;
3712
3713
  // Callbacks
3714
  MessageHandler on_message_;
3715
  std::map<std::string, MessageHandler> event_handlers_;
3716
  OpenHandler on_open_;
3717
  ErrorHandler on_error_;
3718
3719
  // Configuration
3720
  int reconnect_interval_ms_ = 3000;
3721
  int max_reconnect_attempts_ = 0; // 0 = unlimited
3722
3723
  // State
3724
  std::atomic<bool> running_{false};
3725
  std::atomic<bool> connected_{false};
3726
  std::string last_event_id_;
3727
3728
  // Async support
3729
  std::thread async_thread_;
3730
};
3731
3732
} // namespace sse
3733
3734
namespace ws {
3735
3736
enum class Opcode : uint8_t {
3737
  Continuation = 0x0,
3738
  Text = 0x1,
3739
  Binary = 0x2,
3740
  Close = 0x8,
3741
  Ping = 0x9,
3742
  Pong = 0xA,
3743
};
3744
3745
enum class CloseStatus : uint16_t {
3746
  Normal = 1000,
3747
  GoingAway = 1001,
3748
  ProtocolError = 1002,
3749
  UnsupportedData = 1003,
3750
  NoStatus = 1005,
3751
  Abnormal = 1006,
3752
  InvalidPayload = 1007,
3753
  PolicyViolation = 1008,
3754
  MessageTooBig = 1009,
3755
  MandatoryExtension = 1010,
3756
  InternalError = 1011,
3757
};
3758
3759
enum ReadResult : int { Fail = 0, Text = 1, Binary = 2 };
3760
3761
class WebSocket {
3762
public:
3763
  WebSocket(const WebSocket &) = delete;
3764
  WebSocket &operator=(const WebSocket &) = delete;
3765
  ~WebSocket();
3766
3767
  ReadResult read(std::string &msg);
3768
  bool send(const std::string &data);
3769
  bool send(const char *data, size_t len);
3770
  void close(CloseStatus status = CloseStatus::Normal,
3771
             const std::string &reason = "");
3772
  const Request &request() const;
3773
  bool is_open() const;
3774
3775
private:
3776
  friend class httplib::Server;
3777
  friend class WebSocketClient;
3778
3779
  WebSocket(
3780
      Stream &strm, const Request &req, bool is_server,
3781
      time_t ping_interval_sec = CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND,
3782
      int max_missed_pongs = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS)
3783
0
      : strm_(strm), req_(req), is_server_(is_server),
3784
0
        ping_interval_sec_(ping_interval_sec),
3785
0
        max_missed_pongs_(max_missed_pongs) {
3786
0
    start_heartbeat();
3787
0
  }
3788
3789
  WebSocket(
3790
      std::unique_ptr<Stream> &&owned_strm, const Request &req, bool is_server,
3791
      time_t ping_interval_sec = CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND,
3792
      int max_missed_pongs = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS)
3793
      : strm_(*owned_strm), owned_strm_(std::move(owned_strm)), req_(req),
3794
        is_server_(is_server), ping_interval_sec_(ping_interval_sec),
3795
0
        max_missed_pongs_(max_missed_pongs) {
3796
0
    start_heartbeat();
3797
0
  }
3798
3799
  void start_heartbeat();
3800
  bool send_frame(Opcode op, const char *data, size_t len, bool fin = true);
3801
3802
  Stream &strm_;
3803
  std::unique_ptr<Stream> owned_strm_;
3804
  Request req_;
3805
  bool is_server_;
3806
  time_t ping_interval_sec_;
3807
  int max_missed_pongs_;
3808
  int unacked_pings_ = 0;
3809
  std::atomic<bool> closed_{false};
3810
  std::mutex write_mutex_;
3811
  std::thread ping_thread_;
3812
  std::mutex ping_mutex_;
3813
  std::condition_variable ping_cv_;
3814
};
3815
3816
class WebSocketClient {
3817
public:
3818
  explicit WebSocketClient(const std::string &scheme_host_port_path,
3819
                           const Headers &headers = {});
3820
3821
  ~WebSocketClient();
3822
  WebSocketClient(const WebSocketClient &) = delete;
3823
  WebSocketClient &operator=(const WebSocketClient &) = delete;
3824
3825
  bool is_valid() const;
3826
3827
  bool connect();
3828
  ReadResult read(std::string &msg);
3829
  bool send(const std::string &data);
3830
  bool send(const char *data, size_t len);
3831
  void close(CloseStatus status = CloseStatus::Normal,
3832
             const std::string &reason = "");
3833
  bool is_open() const;
3834
  const std::string &subprotocol() const;
3835
  void set_read_timeout(time_t sec, time_t usec = 0);
3836
  void set_write_timeout(time_t sec, time_t usec = 0);
3837
  void set_websocket_ping_interval(time_t sec);
3838
  void set_websocket_max_missed_pongs(int count);
3839
  void set_tcp_nodelay(bool on);
3840
  void set_address_family(int family);
3841
  void set_ipv6_v6only(bool on);
3842
  void set_socket_options(SocketOptions socket_options);
3843
  void set_connection_timeout(time_t sec, time_t usec = 0);
3844
  void set_interface(const std::string &intf);
3845
3846
#ifdef CPPHTTPLIB_SSL_ENABLED
3847
  void set_ca_cert_path(const std::string &path);
3848
  void set_ca_cert_store(tls::ca_store_t store);
3849
  void enable_server_certificate_verification(bool enabled);
3850
#endif
3851
3852
private:
3853
  void shutdown_and_close();
3854
  bool create_stream(std::unique_ptr<Stream> &strm);
3855
3856
  std::string host_;
3857
  int port_;
3858
  std::string path_;
3859
  Headers headers_;
3860
  std::string subprotocol_;
3861
  bool is_valid_ = false;
3862
  socket_t sock_ = INVALID_SOCKET;
3863
  std::unique_ptr<WebSocket> ws_;
3864
  time_t read_timeout_sec_ = CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND;
3865
  time_t read_timeout_usec_ = 0;
3866
  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
3867
  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
3868
  time_t websocket_ping_interval_sec_ =
3869
      CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND;
3870
  int websocket_max_missed_pongs_ = CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS;
3871
  int address_family_ = AF_UNSPEC;
3872
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
3873
  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
3874
  SocketOptions socket_options_ = nullptr;
3875
  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
3876
  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
3877
  std::string interface_;
3878
3879
#ifdef CPPHTTPLIB_SSL_ENABLED
3880
  bool is_ssl_ = false;
3881
  tls::ctx_t tls_ctx_ = nullptr;
3882
  tls::session_t tls_session_ = nullptr;
3883
  std::string ca_cert_file_path_;
3884
  tls::ca_store_t ca_cert_store_ = nullptr;
3885
  bool server_certificate_verification_ = true;
3886
#endif
3887
};
3888
3889
namespace impl {
3890
3891
bool is_valid_utf8(const std::string &s);
3892
3893
bool read_websocket_frame(Stream &strm, Opcode &opcode, std::string &payload,
3894
                          bool &fin, bool expect_masked, size_t max_len);
3895
3896
} // namespace impl
3897
3898
} // namespace ws
3899
3900
// ----------------------------------------------------------------------------
3901
3902
/*
3903
 * Implementation that will be part of the .cc file if split into .h + .cc.
3904
 */
3905
3906
namespace stream {
3907
3908
// stream::Result implementations
3909
inline Result::Result() : chunk_size_(8192) {}
3910
3911
inline Result::Result(ClientImpl::StreamHandle &&handle, size_t chunk_size)
3912
    : handle_(std::move(handle)), chunk_size_(chunk_size) {}
3913
3914
inline Result::Result(Result &&other) noexcept
3915
    : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),
3916
      current_size_(other.current_size_), chunk_size_(other.chunk_size_),
3917
      finished_(other.finished_) {
3918
  other.current_size_ = 0;
3919
  other.finished_ = true;
3920
}
3921
3922
0
inline Result &Result::operator=(Result &&other) noexcept {
3923
0
  if (this != &other) {
3924
0
    handle_ = std::move(other.handle_);
3925
0
    buffer_ = std::move(other.buffer_);
3926
0
    current_size_ = other.current_size_;
3927
0
    chunk_size_ = other.chunk_size_;
3928
0
    finished_ = other.finished_;
3929
0
    other.current_size_ = 0;
3930
0
    other.finished_ = true;
3931
0
  }
3932
0
  return *this;
3933
0
}
3934
3935
0
inline bool Result::is_valid() const { return handle_.is_valid(); }
3936
0
inline Result::operator bool() const { return is_valid(); }
3937
3938
0
inline int Result::status() const {
3939
0
  return handle_.response ? handle_.response->status : -1;
3940
0
}
3941
3942
0
inline const Headers &Result::headers() const {
3943
0
  static const Headers empty_headers;
3944
0
  return handle_.response ? handle_.response->headers : empty_headers;
3945
0
}
3946
3947
inline std::string Result::get_header_value(const std::string &key,
3948
0
                                            const char *def) const {
3949
0
  return handle_.response ? handle_.response->get_header_value(key, def) : def;
3950
0
}
3951
3952
0
inline bool Result::has_header(const std::string &key) const {
3953
0
  return handle_.response ? handle_.response->has_header(key) : false;
3954
0
}
3955
3956
0
inline Error Result::error() const { return handle_.error; }
3957
0
inline Error Result::read_error() const { return handle_.get_read_error(); }
3958
0
inline bool Result::has_read_error() const { return handle_.has_read_error(); }
3959
3960
0
inline bool Result::next() {
3961
0
  if (!handle_.is_valid() || finished_) { return false; }
3962
0
3963
0
  if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }
3964
0
3965
0
  ssize_t n = handle_.read(&buffer_[0], chunk_size_);
3966
0
  if (n > 0) {
3967
0
    current_size_ = static_cast<size_t>(n);
3968
0
    return true;
3969
0
  }
3970
0
3971
0
  current_size_ = 0;
3972
0
  finished_ = true;
3973
0
  return false;
3974
0
}
3975
3976
0
inline const char *Result::data() const { return buffer_.data(); }
3977
0
inline size_t Result::size() const { return current_size_; }
3978
3979
0
inline std::string Result::read_all() {
3980
0
  std::string result;
3981
0
  while (next()) {
3982
0
    result.append(data(), size());
3983
0
  }
3984
0
  return result;
3985
0
}
3986
3987
} // namespace stream
3988
3989
namespace sse {
3990
3991
// SSEMessage implementations
3992
inline SSEMessage::SSEMessage() : event("message") {}
3993
3994
0
inline void SSEMessage::clear() {
3995
0
  event = "message";
3996
0
  data.clear();
3997
0
  id.clear();
3998
0
}
3999
4000
// SSEClient implementations
4001
inline SSEClient::SSEClient(Client &client, const std::string &path)
4002
    : client_(client), path_(path) {}
4003
4004
inline SSEClient::SSEClient(Client &client, const std::string &path,
4005
                            const Headers &headers)
4006
    : client_(client), path_(path), headers_(headers) {}
4007
4008
inline SSEClient::~SSEClient() { stop(); }
4009
4010
0
inline SSEClient &SSEClient::on_message(MessageHandler handler) {
4011
0
  on_message_ = std::move(handler);
4012
0
  return *this;
4013
0
}
4014
4015
inline SSEClient &SSEClient::on_event(const std::string &type,
4016
0
                                      MessageHandler handler) {
4017
0
  event_handlers_[type] = std::move(handler);
4018
0
  return *this;
4019
0
}
4020
4021
0
inline SSEClient &SSEClient::on_open(OpenHandler handler) {
4022
0
  on_open_ = std::move(handler);
4023
0
  return *this;
4024
0
}
4025
4026
0
inline SSEClient &SSEClient::on_error(ErrorHandler handler) {
4027
0
  on_error_ = std::move(handler);
4028
0
  return *this;
4029
0
}
4030
4031
0
inline SSEClient &SSEClient::set_reconnect_interval(int ms) {
4032
0
  reconnect_interval_ms_ = ms;
4033
0
  return *this;
4034
0
}
4035
4036
0
inline SSEClient &SSEClient::set_max_reconnect_attempts(int n) {
4037
0
  max_reconnect_attempts_ = n;
4038
0
  return *this;
4039
0
}
4040
4041
0
inline SSEClient &SSEClient::set_headers(const Headers &headers) {
4042
0
  std::lock_guard<std::mutex> lock(headers_mutex_);
4043
0
  headers_ = headers;
4044
0
  return *this;
4045
0
}
4046
4047
0
inline bool SSEClient::is_connected() const { return connected_.load(); }
4048
4049
0
inline const std::string &SSEClient::last_event_id() const {
4050
0
  return last_event_id_;
4051
0
}
4052
4053
0
inline void SSEClient::start() {
4054
0
  running_.store(true);
4055
0
  run_event_loop();
4056
0
}
4057
4058
0
inline void SSEClient::start_async() {
4059
0
  running_.store(true);
4060
0
  async_thread_ = std::thread([this]() { run_event_loop(); });
4061
0
}
4062
4063
0
inline void SSEClient::stop() {
4064
0
  running_.store(false);
4065
0
  client_.stop(); // Cancel any pending operations
4066
0
  if (async_thread_.joinable()) { async_thread_.join(); }
4067
0
}
4068
4069
inline bool SSEClient::parse_sse_line(const std::string &line, SSEMessage &msg,
4070
0
                                      int &retry_ms) {
4071
0
  // Blank line signals end of event
4072
0
  if (line.empty() || line == "\r") { return true; }
4073
0
4074
0
  // Lines starting with ':' are comments (ignored)
4075
0
  if (!line.empty() && line[0] == ':') { return false; }
4076
0
4077
0
  // Find the colon separator
4078
0
  auto colon_pos = line.find(':');
4079
0
  if (colon_pos == std::string::npos) {
4080
0
    // Line with no colon is treated as field name with empty value
4081
0
    return false;
4082
0
  }
4083
0
4084
0
  auto field = line.substr(0, colon_pos);
4085
0
  std::string value;
4086
0
4087
0
  // Value starts after colon, skip optional single space
4088
0
  if (colon_pos + 1 < line.size()) {
4089
0
    auto value_start = colon_pos + 1;
4090
0
    if (line[value_start] == ' ') { value_start++; }
4091
0
    value = line.substr(value_start);
4092
0
    // Remove trailing \r if present
4093
0
    if (!value.empty() && value.back() == '\r') { value.pop_back(); }
4094
0
  }
4095
0
4096
0
  // Handle known fields
4097
0
  if (field == "event") {
4098
0
    msg.event = value;
4099
0
  } else if (field == "data") {
4100
0
    // Multiple data lines are concatenated with newlines
4101
0
    if (!msg.data.empty()) { msg.data += "\n"; }
4102
0
    msg.data += value;
4103
0
  } else if (field == "id") {
4104
0
    // Empty id is valid (clears the last event ID)
4105
0
    msg.id = value;
4106
0
  } else if (field == "retry") {
4107
0
    // Parse retry interval in milliseconds
4108
0
    {
4109
0
      int v = 0;
4110
0
      auto res =
4111
0
          detail::from_chars(value.data(), value.data() + value.size(), v);
4112
0
      if (res.ec == std::errc{}) { retry_ms = v; }
4113
0
    }
4114
0
  }
4115
0
  // Unknown fields are ignored per SSE spec
4116
0
4117
0
  return false;
4118
0
}
4119
4120
0
inline void SSEClient::run_event_loop() {
4121
0
  auto reconnect_count = 0;
4122
0
4123
0
  while (running_.load()) {
4124
0
    // Build headers, including Last-Event-ID if we have one
4125
0
    Headers request_headers;
4126
0
    {
4127
0
      std::lock_guard<std::mutex> lock(headers_mutex_);
4128
0
      request_headers = headers_;
4129
0
    }
4130
0
    if (!last_event_id_.empty()) {
4131
0
      request_headers.emplace("Last-Event-ID", last_event_id_);
4132
0
    }
4133
0
4134
0
    // Open streaming connection
4135
0
    auto result = stream::Get(client_, path_, request_headers);
4136
0
4137
0
    // Connection error handling
4138
0
    if (!result) {
4139
0
      connected_.store(false);
4140
0
      if (on_error_) { on_error_(result.error()); }
4141
0
4142
0
      if (!should_reconnect(reconnect_count)) { break; }
4143
0
      wait_for_reconnect();
4144
0
      reconnect_count++;
4145
0
      continue;
4146
0
    }
4147
0
4148
0
    if (result.status() != StatusCode::OK_200) {
4149
0
      connected_.store(false);
4150
0
      if (on_error_) { on_error_(Error::Connection); }
4151
0
4152
0
      // For certain errors, don't reconnect.
4153
0
      // Note: 401 is intentionally absent so that handlers can refresh
4154
0
      // credentials via set_headers() and let the client reconnect.
4155
0
      if (result.status() == StatusCode::NoContent_204 ||
4156
0
          result.status() == StatusCode::NotFound_404 ||
4157
0
          result.status() == StatusCode::Forbidden_403) {
4158
0
        break;
4159
0
      }
4160
0
4161
0
      if (!should_reconnect(reconnect_count)) { break; }
4162
0
      wait_for_reconnect();
4163
0
      reconnect_count++;
4164
0
      continue;
4165
0
    }
4166
0
4167
0
    // Connection successful
4168
0
    connected_.store(true);
4169
0
    reconnect_count = 0;
4170
0
    if (on_open_) { on_open_(); }
4171
0
4172
0
    // Event receiving loop
4173
0
    std::string buffer;
4174
0
    SSEMessage current_msg;
4175
0
4176
0
    while (running_.load() && result.next()) {
4177
0
      buffer.append(result.data(), result.size());
4178
0
4179
0
      // Process complete lines in the buffer
4180
0
      size_t line_start = 0;
4181
0
      size_t newline_pos;
4182
0
4183
0
      while ((newline_pos = buffer.find('\n', line_start)) !=
4184
0
             std::string::npos) {
4185
0
        auto line = buffer.substr(line_start, newline_pos - line_start);
4186
0
        line_start = newline_pos + 1;
4187
0
4188
0
        // Parse the line and check if event is complete
4189
0
        auto event_complete =
4190
0
            parse_sse_line(line, current_msg, reconnect_interval_ms_);
4191
0
4192
0
        if (event_complete && !current_msg.data.empty()) {
4193
0
          // Update last_event_id for reconnection
4194
0
          if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
4195
0
4196
0
          // Dispatch event to appropriate handler
4197
0
          dispatch_event(current_msg);
4198
0
4199
0
          current_msg.clear();
4200
0
        }
4201
0
      }
4202
0
4203
0
      // Keep unprocessed data in buffer
4204
0
      buffer.erase(0, line_start);
4205
0
    }
4206
0
4207
0
    // Connection ended
4208
0
    connected_.store(false);
4209
0
4210
0
    if (!running_.load()) { break; }
4211
0
4212
0
    // Check for read errors
4213
0
    if (result.has_read_error()) {
4214
0
      if (on_error_) { on_error_(result.read_error()); }
4215
0
    }
4216
0
4217
0
    if (!should_reconnect(reconnect_count)) { break; }
4218
0
    wait_for_reconnect();
4219
0
    reconnect_count++;
4220
0
  }
4221
0
4222
0
  connected_.store(false);
4223
0
}
4224
4225
0
inline void SSEClient::dispatch_event(const SSEMessage &msg) {
4226
0
  // Check for specific event type handler first
4227
0
  auto it = event_handlers_.find(msg.event);
4228
0
  if (it != event_handlers_.end()) {
4229
0
    it->second(msg);
4230
0
    return;
4231
0
  }
4232
0
4233
0
  // Fall back to generic message handler
4234
0
  if (on_message_) { on_message_(msg); }
4235
0
}
4236
4237
0
inline bool SSEClient::should_reconnect(int count) const {
4238
0
  if (!running_.load()) { return false; }
4239
0
  if (max_reconnect_attempts_ == 0) { return true; } // unlimited
4240
0
  return count < max_reconnect_attempts_;
4241
0
}
4242
4243
0
inline void SSEClient::wait_for_reconnect() {
4244
0
  // Use small increments to check running_ flag frequently
4245
0
  auto waited = 0;
4246
0
  while (running_.load() && waited < reconnect_interval_ms_) {
4247
0
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
4248
0
    waited += 100;
4249
0
  }
4250
0
}
4251
4252
} // namespace sse
4253
4254
#ifdef CPPHTTPLIB_SSL_ENABLED
4255
/*
4256
 * TLS abstraction layer - internal function declarations
4257
 * These are implementation details and not part of the public API.
4258
 */
4259
namespace tls {
4260
4261
// Client context
4262
ctx_t create_client_context();
4263
void free_context(ctx_t ctx);
4264
bool set_min_version(ctx_t ctx, Version version);
4265
bool load_ca_pem(ctx_t ctx, const char *pem, size_t len);
4266
bool load_ca_file(ctx_t ctx, const char *file_path);
4267
bool load_ca_dir(ctx_t ctx, const char *dir_path);
4268
bool load_system_certs(ctx_t ctx);
4269
bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key,
4270
                         const char *password);
4271
bool set_client_cert_file(ctx_t ctx, const char *cert_path,
4272
                          const char *key_path, const char *password);
4273
4274
// Server context
4275
ctx_t create_server_context();
4276
bool set_server_cert_pem(ctx_t ctx, const char *cert, const char *key,
4277
                         const char *password);
4278
bool set_server_cert_file(ctx_t ctx, const char *cert_path,
4279
                          const char *key_path, const char *password);
4280
bool set_client_ca_file(ctx_t ctx, const char *ca_file, const char *ca_dir);
4281
void set_verify_client(ctx_t ctx, bool require);
4282
4283
// Session management
4284
session_t create_session(ctx_t ctx, socket_t sock);
4285
void free_session(session_t session);
4286
bool set_sni(session_t session, const char *hostname);
4287
bool set_hostname(session_t session, const char *hostname);
4288
4289
// Handshake (non-blocking capable)
4290
TlsError connect(session_t session);
4291
TlsError accept(session_t session);
4292
4293
// Handshake with timeout (blocking until timeout)
4294
bool connect_nonblocking(session_t session, socket_t sock, time_t timeout_sec,
4295
                         time_t timeout_usec, TlsError *err);
4296
bool accept_nonblocking(session_t session, socket_t sock, time_t timeout_sec,
4297
                        time_t timeout_usec, TlsError *err);
4298
4299
// I/O (non-blocking capable)
4300
ssize_t read(session_t session, void *buf, size_t len, TlsError &err);
4301
ssize_t write(session_t session, const void *buf, size_t len, TlsError &err);
4302
int pending(const_session_t session);
4303
void shutdown(session_t session, bool graceful);
4304
4305
// Connection state
4306
bool is_peer_closed(session_t session, socket_t sock);
4307
4308
// Certificate verification
4309
cert_t get_peer_cert(const_session_t session);
4310
void free_cert(cert_t cert);
4311
bool verify_hostname(cert_t cert, const char *hostname);
4312
uint64_t hostname_mismatch_code();
4313
long get_verify_result(const_session_t session);
4314
4315
// Certificate introspection
4316
std::string get_cert_subject_cn(cert_t cert);
4317
std::string get_cert_issuer_name(cert_t cert);
4318
bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans);
4319
bool get_cert_validity(cert_t cert, time_t &not_before, time_t &not_after);
4320
std::string get_cert_serial(cert_t cert);
4321
bool get_cert_der(cert_t cert, std::vector<unsigned char> &der);
4322
const char *get_sni(const_session_t session);
4323
4324
// CA store management
4325
ca_store_t create_ca_store(const char *pem, size_t len);
4326
void free_ca_store(ca_store_t store);
4327
bool set_ca_store(ctx_t ctx, ca_store_t store);
4328
size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs);
4329
std::vector<std::string> get_ca_names(ctx_t ctx);
4330
4331
// Dynamic certificate update (for servers)
4332
bool update_server_cert(ctx_t ctx, const char *cert_pem, const char *key_pem,
4333
                        const char *password);
4334
bool update_server_client_ca(ctx_t ctx, const char *ca_pem);
4335
4336
// Certificate verification callback
4337
bool set_verify_callback(ctx_t ctx, VerifyCallback callback);
4338
long get_verify_error(const_session_t session);
4339
std::string verify_error_string(long error_code);
4340
4341
// TlsError information
4342
uint64_t peek_error();
4343
uint64_t get_error();
4344
std::string error_string(uint64_t code);
4345
4346
} // namespace tls
4347
#endif // CPPHTTPLIB_SSL_ENABLED
4348
4349
/*
4350
 * Group 1: detail namespace - Non-SSL utilities
4351
 */
4352
4353
namespace detail {
4354
4355
inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
4356
0
                                const void *optval, socklen_t optlen) {
4357
0
  return setsockopt(sock, level, optname,
4358
#ifdef _WIN32
4359
                    reinterpret_cast<const char *>(optval),
4360
#else
4361
0
                    optval,
4362
0
#endif
4363
0
                    optlen) == 0;
4364
0
}
4365
4366
inline bool set_socket_opt_time(socket_t sock, int level, int optname,
4367
0
                                time_t sec, time_t usec) {
4368
#ifdef _WIN32
4369
  auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
4370
#else
4371
0
  timeval timeout;
4372
0
  timeout.tv_sec = static_cast<long>(sec);
4373
0
  timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
4374
0
#endif
4375
0
  return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
4376
0
}
4377
4378
482k
inline bool is_hex(char c, int &v) {
4379
482k
  if (isdigit(c)) {
4380
11.9k
    v = c - '0';
4381
11.9k
    return true;
4382
470k
  } else if ('A' <= c && c <= 'F') {
4383
28.5k
    v = c - 'A' + 10;
4384
28.5k
    return true;
4385
441k
  } else if ('a' <= c && c <= 'f') {
4386
223k
    v = c - 'a' + 10;
4387
223k
    return true;
4388
223k
  }
4389
218k
  return false;
4390
482k
}
4391
4392
inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
4393
338k
                          int &val) {
4394
338k
  if (i >= s.size()) { return false; }
4395
4396
338k
  val = 0;
4397
602k
  for (; cnt; i++, cnt--) {
4398
483k
    if (!s[i]) { return false; }
4399
482k
    auto v = 0;
4400
482k
    if (is_hex(s[i], v)) {
4401
264k
      val = val * 16 + v;
4402
264k
    } else {
4403
218k
      return false;
4404
218k
    }
4405
482k
  }
4406
119k
  return true;
4407
338k
}
4408
4409
0
inline std::string from_i_to_hex(size_t n) {
4410
0
  static const auto charset = "0123456789abcdef";
4411
0
  std::string ret;
4412
0
  do {
4413
0
    ret = charset[n & 15] + ret;
4414
0
    n >>= 4;
4415
0
  } while (n > 0);
4416
0
  return ret;
4417
0
}
4418
4419
0
inline std::string compute_etag(const FileStat &fs) {
4420
0
  if (!fs.is_file()) { return std::string(); }
4421
4422
  // If mtime cannot be determined (negative value indicates an error
4423
  // or sentinel), do not generate an ETag. Returning a neutral / fixed
4424
  // value like 0 could collide with a real file that legitimately has
4425
  // mtime == 0 (epoch) and lead to misleading validators.
4426
0
  auto mtime_raw = fs.mtime();
4427
0
  if (mtime_raw < 0) { return std::string(); }
4428
4429
0
  auto mtime = static_cast<size_t>(mtime_raw);
4430
0
  auto size = fs.size();
4431
4432
0
  return std::string("W/\"") + from_i_to_hex(mtime) + "-" +
4433
0
         from_i_to_hex(size) + "\"";
4434
0
}
4435
4436
// Format time_t as HTTP-date (RFC 9110 Section 5.6.7): "Sun, 06 Nov 1994
4437
// 08:49:37 GMT" This implementation is defensive: it validates `mtime`, checks
4438
// return values from `gmtime_r`/`gmtime_s`, and ensures `strftime` succeeds.
4439
0
inline std::string file_mtime_to_http_date(time_t mtime) {
4440
0
  if (mtime < 0) { return std::string(); }
4441
4442
0
  struct tm tm_buf;
4443
#ifdef _WIN32
4444
  if (gmtime_s(&tm_buf, &mtime) != 0) { return std::string(); }
4445
#else
4446
0
  if (gmtime_r(&mtime, &tm_buf) == nullptr) { return std::string(); }
4447
0
#endif
4448
0
  char buf[64];
4449
0
  if (strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {
4450
0
    return std::string();
4451
0
  }
4452
4453
0
  return std::string(buf);
4454
0
}
4455
4456
// Parse HTTP-date (RFC 9110 Section 5.6.7) to time_t. Returns -1 on failure.
4457
0
inline time_t parse_http_date(const std::string &date_str) {
4458
0
  struct tm tm_buf;
4459
4460
  // Create a classic locale object once for all parsing attempts
4461
0
  const std::locale classic_locale = std::locale::classic();
4462
4463
  // Try to parse using std::get_time (C++11, cross-platform)
4464
0
  auto try_parse = [&](const char *fmt) -> bool {
4465
0
    std::istringstream ss(date_str);
4466
0
    ss.imbue(classic_locale);
4467
4468
0
    memset(&tm_buf, 0, sizeof(tm_buf));
4469
0
    ss >> std::get_time(&tm_buf, fmt);
4470
4471
0
    return !ss.fail();
4472
0
  };
4473
4474
  // RFC 9110 preferred format (HTTP-date): "Sun, 06 Nov 1994 08:49:37 GMT"
4475
0
  if (!try_parse("%a, %d %b %Y %H:%M:%S")) {
4476
    // RFC 850 format: "Sunday, 06-Nov-94 08:49:37 GMT"
4477
0
    if (!try_parse("%A, %d-%b-%y %H:%M:%S")) {
4478
      // asctime format: "Sun Nov  6 08:49:37 1994"
4479
0
      if (!try_parse("%a %b %d %H:%M:%S %Y")) {
4480
0
        return static_cast<time_t>(-1);
4481
0
      }
4482
0
    }
4483
0
  }
4484
4485
#ifdef _WIN32
4486
  return _mkgmtime(&tm_buf);
4487
#elif defined _AIX
4488
  return mktime(&tm_buf);
4489
#else
4490
0
  return timegm(&tm_buf);
4491
0
#endif
4492
0
}
4493
4494
0
inline bool is_weak_etag(const std::string &s) {
4495
  // Check if the string is a weak ETag (starts with 'W/"')
4496
0
  return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';
4497
0
}
4498
4499
0
inline bool is_strong_etag(const std::string &s) {
4500
  // Check if the string is a strong ETag (starts and ends with '"', at least 2
4501
  // chars)
4502
0
  return s.size() >= 2 && s[0] == '"' && s.back() == '"';
4503
0
}
4504
4505
2.80k
inline size_t to_utf8(int code, char *buff) {
4506
2.80k
  if (code < 0x0080) {
4507
1.52k
    buff[0] = static_cast<char>(code & 0x7F);
4508
1.52k
    return 1;
4509
1.52k
  } else if (code < 0x0800) {
4510
214
    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
4511
214
    buff[1] = static_cast<char>(0x80 | (code & 0x3F));
4512
214
    return 2;
4513
1.07k
  } else if (code < 0xD800) {
4514
445
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4515
445
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4516
445
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
4517
445
    return 3;
4518
628
  } else if (code < 0xE000) { // D800 - DFFF is invalid...
4519
395
    return 0;
4520
395
  } else if (code < 0x10000) {
4521
233
    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
4522
233
    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4523
233
    buff[2] = static_cast<char>(0x80 | (code & 0x3F));
4524
233
    return 3;
4525
233
  } else if (code < 0x110000) {
4526
0
    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
4527
0
    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
4528
0
    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
4529
0
    buff[3] = static_cast<char>(0x80 | (code & 0x3F));
4530
0
    return 4;
4531
0
  }
4532
4533
  // NOTREACHED
4534
0
  return 0;
4535
2.80k
}
4536
4537
} // namespace detail
4538
4539
namespace ws {
4540
namespace impl {
4541
4542
0
inline bool is_valid_utf8(const std::string &s) {
4543
0
  size_t i = 0;
4544
0
  auto n = s.size();
4545
0
  while (i < n) {
4546
0
    auto c = static_cast<unsigned char>(s[i]);
4547
0
    size_t len;
4548
0
    uint32_t cp;
4549
0
    if (c < 0x80) {
4550
0
      i++;
4551
0
      continue;
4552
0
    } else if ((c & 0xE0) == 0xC0) {
4553
0
      len = 2;
4554
0
      cp = c & 0x1F;
4555
0
    } else if ((c & 0xF0) == 0xE0) {
4556
0
      len = 3;
4557
0
      cp = c & 0x0F;
4558
0
    } else if ((c & 0xF8) == 0xF0) {
4559
0
      len = 4;
4560
0
      cp = c & 0x07;
4561
0
    } else {
4562
0
      return false;
4563
0
    }
4564
0
    if (i + len > n) { return false; }
4565
0
    for (size_t j = 1; j < len; j++) {
4566
0
      auto b = static_cast<unsigned char>(s[i + j]);
4567
0
      if ((b & 0xC0) != 0x80) { return false; }
4568
0
      cp = (cp << 6) | (b & 0x3F);
4569
0
    }
4570
0
    // Overlong encoding check
4571
0
    if (len == 2 && cp < 0x80) { return false; }
4572
0
    if (len == 3 && cp < 0x800) { return false; }
4573
0
    if (len == 4 && cp < 0x10000) { return false; }
4574
0
    // Surrogate halves (U+D800..U+DFFF) and beyond U+10FFFF are invalid
4575
0
    if (cp >= 0xD800 && cp <= 0xDFFF) { return false; }
4576
0
    if (cp > 0x10FFFF) { return false; }
4577
0
    i += len;
4578
0
  }
4579
0
  return true;
4580
0
}
4581
4582
} // namespace impl
4583
} // namespace ws
4584
4585
namespace detail {
4586
4587
// NOTE: This code came up with the following stackoverflow post:
4588
// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
4589
0
inline std::string base64_encode(const std::string &in) {
4590
0
  static const auto lookup =
4591
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4592
4593
0
  std::string out;
4594
0
  out.reserve(in.size());
4595
4596
0
  auto val = 0;
4597
0
  auto valb = -6;
4598
4599
0
  for (auto c : in) {
4600
0
    val = (val << 8) + static_cast<uint8_t>(c);
4601
0
    valb += 8;
4602
0
    while (valb >= 0) {
4603
0
      out.push_back(lookup[(val >> valb) & 0x3F]);
4604
0
      valb -= 6;
4605
0
    }
4606
0
  }
4607
4608
0
  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
4609
4610
0
  while (out.size() % 4) {
4611
0
    out.push_back('=');
4612
0
  }
4613
4614
0
  return out;
4615
0
}
4616
4617
0
inline std::string sha1(const std::string &input) {
4618
  // RFC 3174 SHA-1 implementation
4619
0
  auto left_rotate = [](uint32_t x, uint32_t n) -> uint32_t {
4620
0
    return (x << n) | (x >> (32 - n));
4621
0
  };
4622
4623
0
  uint32_t h0 = 0x67452301;
4624
0
  uint32_t h1 = 0xEFCDAB89;
4625
0
  uint32_t h2 = 0x98BADCFE;
4626
0
  uint32_t h3 = 0x10325476;
4627
0
  uint32_t h4 = 0xC3D2E1F0;
4628
4629
  // Pre-processing: adding padding bits
4630
0
  std::string msg = input;
4631
0
  uint64_t original_bit_len = static_cast<uint64_t>(msg.size()) * 8;
4632
0
  msg.push_back(static_cast<char>(0x80));
4633
0
  while (msg.size() % 64 != 56) {
4634
0
    msg.push_back(0);
4635
0
  }
4636
4637
  // Append original length in bits as 64-bit big-endian
4638
0
  for (int i = 56; i >= 0; i -= 8) {
4639
0
    msg.push_back(static_cast<char>((original_bit_len >> i) & 0xFF));
4640
0
  }
4641
4642
  // Process each 512-bit chunk
4643
0
  for (size_t offset = 0; offset < msg.size(); offset += 64) {
4644
0
    uint32_t w[80];
4645
4646
0
    for (size_t i = 0; i < 16; i++) {
4647
0
      w[i] =
4648
0
          (static_cast<uint32_t>(static_cast<uint8_t>(msg[offset + i * 4]))
4649
0
           << 24) |
4650
0
          (static_cast<uint32_t>(static_cast<uint8_t>(msg[offset + i * 4 + 1]))
4651
0
           << 16) |
4652
0
          (static_cast<uint32_t>(static_cast<uint8_t>(msg[offset + i * 4 + 2]))
4653
0
           << 8) |
4654
0
          (static_cast<uint32_t>(
4655
0
              static_cast<uint8_t>(msg[offset + i * 4 + 3])));
4656
0
    }
4657
4658
0
    for (int i = 16; i < 80; i++) {
4659
0
      w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
4660
0
    }
4661
4662
0
    uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
4663
4664
0
    for (int i = 0; i < 80; i++) {
4665
0
      uint32_t f, k;
4666
0
      if (i < 20) {
4667
0
        f = (b & c) | ((~b) & d);
4668
0
        k = 0x5A827999;
4669
0
      } else if (i < 40) {
4670
0
        f = b ^ c ^ d;
4671
0
        k = 0x6ED9EBA1;
4672
0
      } else if (i < 60) {
4673
0
        f = (b & c) | (b & d) | (c & d);
4674
0
        k = 0x8F1BBCDC;
4675
0
      } else {
4676
0
        f = b ^ c ^ d;
4677
0
        k = 0xCA62C1D6;
4678
0
      }
4679
4680
0
      uint32_t temp = left_rotate(a, 5) + f + e + k + w[i];
4681
0
      e = d;
4682
0
      d = c;
4683
0
      c = left_rotate(b, 30);
4684
0
      b = a;
4685
0
      a = temp;
4686
0
    }
4687
4688
0
    h0 += a;
4689
0
    h1 += b;
4690
0
    h2 += c;
4691
0
    h3 += d;
4692
0
    h4 += e;
4693
0
  }
4694
4695
  // Produce the final hash as a 20-byte binary string
4696
0
  std::string hash(20, '\0');
4697
0
  for (size_t i = 0; i < 4; i++) {
4698
0
    hash[i] = static_cast<char>((h0 >> (24 - i * 8)) & 0xFF);
4699
0
    hash[4 + i] = static_cast<char>((h1 >> (24 - i * 8)) & 0xFF);
4700
0
    hash[8 + i] = static_cast<char>((h2 >> (24 - i * 8)) & 0xFF);
4701
0
    hash[12 + i] = static_cast<char>((h3 >> (24 - i * 8)) & 0xFF);
4702
0
    hash[16 + i] = static_cast<char>((h4 >> (24 - i * 8)) & 0xFF);
4703
0
  }
4704
0
  return hash;
4705
0
}
4706
4707
0
inline std::string websocket_accept_key(const std::string &client_key) {
4708
0
  const std::string magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
4709
0
  return base64_encode(sha1(client_key + magic));
4710
0
}
4711
4712
0
inline bool is_websocket_upgrade(const Request &req) {
4713
0
  if (req.method != "GET") { return false; }
4714
4715
  // Check Upgrade: websocket (case-insensitive)
4716
0
  auto upgrade_it = req.headers.find("Upgrade");
4717
0
  if (upgrade_it == req.headers.end()) { return false; }
4718
0
  auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
4719
0
  if (upgrade_val != "websocket") { return false; }
4720
4721
  // Check Connection header contains "Upgrade"
4722
0
  auto connection_it = req.headers.find("Connection");
4723
0
  if (connection_it == req.headers.end()) { return false; }
4724
0
  auto connection_val = case_ignore::to_lower(connection_it->second);
4725
0
  if (connection_val.find("upgrade") == std::string::npos) { return false; }
4726
4727
  // Check Sec-WebSocket-Key is a valid base64-encoded 16-byte value (24 chars)
4728
  // RFC 6455 Section 4.2.1
4729
0
  auto ws_key = req.get_header_value("Sec-WebSocket-Key");
4730
0
  if (ws_key.size() != 24 || ws_key[22] != '=' || ws_key[23] != '=') {
4731
0
    return false;
4732
0
  }
4733
0
  static const std::string b64chars =
4734
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4735
0
  for (size_t i = 0; i < 22; i++) {
4736
0
    if (b64chars.find(ws_key[i]) == std::string::npos) { return false; }
4737
0
  }
4738
4739
  // Check Sec-WebSocket-Version: 13
4740
0
  auto version = req.get_header_value("Sec-WebSocket-Version");
4741
0
  if (version != "13") { return false; }
4742
4743
0
  return true;
4744
0
}
4745
4746
inline bool write_websocket_frame(Stream &strm, ws::Opcode opcode,
4747
                                  const char *data, size_t len, bool fin,
4748
0
                                  bool mask) {
4749
  // First byte: FIN + opcode
4750
0
  uint8_t header[2];
4751
0
  header[0] = static_cast<uint8_t>((fin ? 0x80 : 0x00) |
4752
0
                                   (static_cast<uint8_t>(opcode) & 0x0F));
4753
4754
  // Second byte: MASK + payload length
4755
0
  if (len < 126) {
4756
0
    header[1] = static_cast<uint8_t>(len);
4757
0
    if (mask) { header[1] |= 0x80; }
4758
0
    if (strm.write(reinterpret_cast<char *>(header), 2) < 0) { return false; }
4759
0
  } else if (len <= 0xFFFF) {
4760
0
    header[1] = 126;
4761
0
    if (mask) { header[1] |= 0x80; }
4762
0
    if (strm.write(reinterpret_cast<char *>(header), 2) < 0) { return false; }
4763
0
    uint8_t ext[2];
4764
0
    ext[0] = static_cast<uint8_t>((len >> 8) & 0xFF);
4765
0
    ext[1] = static_cast<uint8_t>(len & 0xFF);
4766
0
    if (strm.write(reinterpret_cast<char *>(ext), 2) < 0) { return false; }
4767
0
  } else {
4768
0
    header[1] = 127;
4769
0
    if (mask) { header[1] |= 0x80; }
4770
0
    if (strm.write(reinterpret_cast<char *>(header), 2) < 0) { return false; }
4771
0
    uint8_t ext[8];
4772
0
    for (int i = 7; i >= 0; i--) {
4773
0
      ext[7 - i] =
4774
0
          static_cast<uint8_t>((static_cast<uint64_t>(len) >> (i * 8)) & 0xFF);
4775
0
    }
4776
0
    if (strm.write(reinterpret_cast<char *>(ext), 8) < 0) { return false; }
4777
0
  }
4778
4779
0
  if (mask) {
4780
    // Generate random mask key
4781
0
    thread_local std::mt19937 rng(std::random_device{}());
4782
0
    uint8_t mask_key[4];
4783
0
    auto r = rng();
4784
0
    std::memcpy(mask_key, &r, 4);
4785
0
    if (strm.write(reinterpret_cast<char *>(mask_key), 4) < 0) { return false; }
4786
4787
    // Write masked payload in chunks
4788
0
    const size_t chunk_size = 4096;
4789
0
    std::vector<char> buf((std::min)(len, chunk_size));
4790
0
    for (size_t offset = 0; offset < len; offset += chunk_size) {
4791
0
      size_t n = (std::min)(chunk_size, len - offset);
4792
0
      for (size_t i = 0; i < n; i++) {
4793
0
        buf[i] =
4794
0
            data[offset + i] ^ static_cast<char>(mask_key[(offset + i) % 4]);
4795
0
      }
4796
0
      if (strm.write(buf.data(), n) < 0) { return false; }
4797
0
    }
4798
0
  } else {
4799
0
    if (len > 0) {
4800
0
      if (strm.write(data, len) < 0) { return false; }
4801
0
    }
4802
0
  }
4803
4804
0
  return true;
4805
0
}
4806
4807
} // namespace detail
4808
4809
namespace ws {
4810
namespace impl {
4811
4812
inline bool read_websocket_frame(Stream &strm, Opcode &opcode,
4813
                                 std::string &payload, bool &fin,
4814
0
                                 bool expect_masked, size_t max_len) {
4815
  // Read first 2 bytes
4816
0
  uint8_t header[2];
4817
0
  if (strm.read(reinterpret_cast<char *>(header), 2) != 2) { return false; }
4818
4819
0
  fin = (header[0] & 0x80) != 0;
4820
4821
  // RSV1, RSV2, RSV3 must be 0 when no extension is negotiated
4822
0
  if (header[0] & 0x70) { return false; }
4823
4824
0
  opcode = static_cast<Opcode>(header[0] & 0x0F);
4825
0
  bool masked = (header[1] & 0x80) != 0;
4826
0
  uint64_t payload_len = header[1] & 0x7F;
4827
4828
  // RFC 6455 Section 5.5: control frames MUST NOT be fragmented and
4829
  // MUST have a payload length of 125 bytes or less
4830
0
  bool is_control = (static_cast<uint8_t>(opcode) & 0x08) != 0;
4831
0
  if (is_control) {
4832
0
    if (!fin) { return false; }
4833
0
    if (payload_len > 125) { return false; }
4834
0
  }
4835
4836
0
  if (masked != expect_masked) { return false; }
4837
4838
  // Extended payload length
4839
0
  if (payload_len == 126) {
4840
0
    uint8_t ext[2];
4841
0
    if (strm.read(reinterpret_cast<char *>(ext), 2) != 2) { return false; }
4842
0
    payload_len = (static_cast<uint64_t>(ext[0]) << 8) | ext[1];
4843
0
  } else if (payload_len == 127) {
4844
0
    uint8_t ext[8];
4845
0
    if (strm.read(reinterpret_cast<char *>(ext), 8) != 8) { return false; }
4846
    // RFC 6455 Section 5.2: the most significant bit MUST be 0
4847
0
    if (ext[0] & 0x80) { return false; }
4848
0
    payload_len = 0;
4849
0
    for (int i = 0; i < 8; i++) {
4850
0
      payload_len = (payload_len << 8) | ext[i];
4851
0
    }
4852
0
  }
4853
4854
0
  if (payload_len > max_len) { return false; }
4855
4856
  // Read mask key if present
4857
0
  uint8_t mask_key[4] = {0};
4858
0
  if (masked) {
4859
0
    if (strm.read(reinterpret_cast<char *>(mask_key), 4) != 4) { return false; }
4860
0
  }
4861
4862
  // Read payload
4863
0
  payload.resize(static_cast<size_t>(payload_len));
4864
0
  if (payload_len > 0) {
4865
0
    size_t total_read = 0;
4866
0
    while (total_read < payload_len) {
4867
0
      auto n = strm.read(&payload[total_read],
4868
0
                         static_cast<size_t>(payload_len - total_read));
4869
0
      if (n <= 0) { return false; }
4870
0
      total_read += static_cast<size_t>(n);
4871
0
    }
4872
0
  }
4873
4874
  // Unmask if needed
4875
0
  if (masked) {
4876
0
    for (size_t i = 0; i < payload.size(); i++) {
4877
0
      payload[i] ^= static_cast<char>(mask_key[i % 4]);
4878
0
    }
4879
0
  }
4880
4881
0
  return true;
4882
0
}
4883
4884
} // namespace impl
4885
} // namespace ws
4886
4887
namespace detail {
4888
4889
217
inline bool is_valid_path(const std::string &path) {
4890
217
  size_t level = 0;
4891
217
  size_t i = 0;
4892
4893
  // Skip slash
4894
1.04M
  while (i < path.size() && path[i] == '/') {
4895
1.04M
    i++;
4896
1.04M
  }
4897
4898
789k
  while (i < path.size()) {
4899
    // Read component
4900
789k
    auto beg = i;
4901
4.04M
    while (i < path.size() && path[i] != '/') {
4902
3.25M
      if (path[i] == '\0') {
4903
6
        return false;
4904
3.25M
      } else if (path[i] == '\\') {
4905
2
        return false;
4906
2
      }
4907
3.25M
      i++;
4908
3.25M
    }
4909
4910
789k
    auto len = i - beg;
4911
789k
    assert(len > 0);
4912
4913
789k
    if (!path.compare(beg, len, ".")) {
4914
36.7k
      ;
4915
752k
    } else if (!path.compare(beg, len, "..")) {
4916
42.5k
      if (level == 0) { return false; }
4917
42.5k
      level--;
4918
710k
    } else {
4919
710k
      level++;
4920
710k
    }
4921
4922
    // Skip slash
4923
1.65M
    while (i < path.size() && path[i] == '/') {
4924
867k
      i++;
4925
867k
    }
4926
789k
  }
4927
4928
208
  return true;
4929
217
}
4930
4931
0
inline bool canonicalize_path(const char *path, std::string &resolved) {
4932
#if defined(_WIN32)
4933
  char buf[_MAX_PATH];
4934
  if (_fullpath(buf, path, _MAX_PATH) == nullptr) { return false; }
4935
  resolved = buf;
4936
#elif defined(PATH_MAX)
4937
  char buf[PATH_MAX];
4938
0
  if (realpath(path, buf) == nullptr) { return false; }
4939
0
  resolved = buf;
4940
#else
4941
  auto buf = realpath(path, nullptr);
4942
  auto guard = scope_exit([&]() { std::free(buf); });
4943
  if (buf == nullptr) { return false; }
4944
  resolved = buf;
4945
#endif
4946
0
  return true;
4947
0
}
4948
4949
inline bool is_path_within_base(const std::string &resolved_path,
4950
0
                                const std::string &resolved_base) {
4951
#if defined(_WIN32)
4952
  return _strnicmp(resolved_path.c_str(), resolved_base.c_str(),
4953
                   resolved_base.size()) == 0;
4954
#else
4955
0
  return strncmp(resolved_path.c_str(), resolved_base.c_str(),
4956
0
                 resolved_base.size()) == 0;
4957
0
#endif
4958
0
}
4959
4960
0
inline FileStat::FileStat(const std::string &path) {
4961
#if defined(_WIN32)
4962
  auto wpath = u8string_to_wstring(path.c_str());
4963
  ret_ = _wstat(wpath.c_str(), &st_);
4964
#else
4965
0
  ret_ = stat(path.c_str(), &st_);
4966
0
#endif
4967
0
}
4968
0
inline bool FileStat::is_file() const {
4969
0
  return ret_ >= 0 && S_ISREG(st_.st_mode);
4970
0
}
4971
0
inline bool FileStat::is_dir() const {
4972
0
  return ret_ >= 0 && S_ISDIR(st_.st_mode);
4973
0
}
4974
4975
0
inline time_t FileStat::mtime() const {
4976
0
  return ret_ >= 0 ? static_cast<time_t>(st_.st_mtime)
4977
0
                   : static_cast<time_t>(-1);
4978
0
}
4979
4980
0
inline size_t FileStat::size() const {
4981
0
  return ret_ >= 0 ? static_cast<size_t>(st_.st_size) : 0;
4982
0
}
4983
4984
0
inline std::string encode_path(const std::string &s) {
4985
0
  std::string result;
4986
0
  result.reserve(s.size());
4987
0
4988
0
  for (size_t i = 0; s[i]; i++) {
4989
0
    switch (s[i]) {
4990
0
    case ' ': result += "%20"; break;
4991
0
    case '+': result += "%2B"; break;
4992
0
    case '\r': result += "%0D"; break;
4993
0
    case '\n': result += "%0A"; break;
4994
0
    case '\'': result += "%27"; break;
4995
0
    case ',': result += "%2C"; break;
4996
0
    // case ':': result += "%3A"; break; // ok? probably...
4997
0
    case ';': result += "%3B"; break;
4998
0
    default:
4999
0
      auto c = static_cast<uint8_t>(s[i]);
5000
0
      if (c >= 0x80) {
5001
0
        result += '%';
5002
0
        char hex[4];
5003
0
        auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
5004
0
        assert(len == 2);
5005
0
        result.append(hex, static_cast<size_t>(len));
5006
0
      } else {
5007
0
        result += s[i];
5008
0
      }
5009
0
      break;
5010
0
    }
5011
0
  }
5012
0
5013
0
  return result;
5014
0
}
5015
5016
0
inline std::string file_extension(const std::string &path) {
5017
0
  std::smatch m;
5018
0
  thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
5019
0
  if (std::regex_search(path, m, re)) { return m[1].str(); }
5020
0
  return std::string();
5021
0
}
5022
5023
13.5M
inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
5024
5025
template <typename T>
5026
inline bool parse_header(const char *beg, const char *end, T fn);
5027
5028
template <typename T>
5029
0
inline bool parse_header(const char *beg, const char *end, T fn) {
5030
  // Skip trailing spaces and tabs.
5031
0
  while (beg < end && is_space_or_tab(end[-1])) {
5032
0
    end--;
5033
0
  }
5034
5035
0
  auto p = beg;
5036
0
  while (p < end && *p != ':') {
5037
0
    p++;
5038
0
  }
5039
5040
0
  auto name = std::string(beg, p);
5041
0
  if (!detail::fields::is_field_name(name)) { return false; }
5042
5043
0
  if (p == end) { return false; }
5044
5045
0
  auto key_end = p;
5046
5047
0
  if (*p++ != ':') { return false; }
5048
5049
0
  while (p < end && is_space_or_tab(*p)) {
5050
0
    p++;
5051
0
  }
5052
5053
0
  if (p <= end) {
5054
0
    auto key_len = key_end - beg;
5055
0
    if (!key_len) { return false; }
5056
5057
0
    auto key = std::string(beg, key_end);
5058
0
    auto val = std::string(p, end);
5059
5060
0
    if (!detail::fields::is_field_value(val)) { return false; }
5061
5062
    // RFC 9110 §5.5: header field values are opaque octets and MUST NOT be
5063
    // percent-decoded by the recipient. Applications that need to interpret a
5064
    // value as a URI component should call httplib::decode_uri_component()
5065
    // (or decode_path_component()) explicitly.
5066
0
    fn(key, val);
5067
5068
0
    return true;
5069
0
  }
5070
5071
0
  return false;
5072
0
}
Unexecuted instantiation: 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})
Unexecuted instantiation: bool httplib::detail::parse_header<httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}>(char const*, char const*, httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1})
Unexecuted instantiation: bool httplib::detail::parse_header<httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2}>(char const*, char const*, httplib::detail::FormDataParser::parse(char const*, unsigned long, std::__1::function<bool (httplib::FormData const&)> const&, std::__1::function<bool (char const*, unsigned long)> const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#2})
Unexecuted instantiation: bool httplib::detail::parse_header<httplib::detail::parse_trailers(httplib::detail::stream_line_reader&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1}>(char const*, char const, httplib::detail::parse_trailers(httplib::detail::stream_line_reader&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >&, std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)#1})
5073
5074
inline bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
5075
0
                           const Headers &src_headers) {
5076
  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
5077
  // transfer coding is complete when a chunk with a chunk-size of zero is
5078
  // received, possibly followed by a trailer section, and finally terminated by
5079
  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
5080
  //
5081
  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
5082
  // doesn't care for the existence of the final CRLF. In other words, it seems
5083
  // to be ok whether the final CRLF exists or not in the chunked data.
5084
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
5085
  //
5086
  // According to the reference code in RFC 9112, cpp-httplib now allows
5087
  // chunked transfer coding data without the final CRLF.
5088
5089
  // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
5090
0
  thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
5091
0
      "transfer-encoding",
5092
0
      "content-length",
5093
0
      "host",
5094
0
      "authorization",
5095
0
      "www-authenticate",
5096
0
      "proxy-authenticate",
5097
0
      "proxy-authorization",
5098
0
      "cookie",
5099
0
      "set-cookie",
5100
0
      "cache-control",
5101
0
      "expect",
5102
0
      "max-forwards",
5103
0
      "pragma",
5104
0
      "range",
5105
0
      "te",
5106
0
      "age",
5107
0
      "expires",
5108
0
      "date",
5109
0
      "location",
5110
0
      "retry-after",
5111
0
      "vary",
5112
0
      "warning",
5113
0
      "content-encoding",
5114
0
      "content-type",
5115
0
      "content-range",
5116
0
      "trailer"};
5117
5118
0
  case_ignore::unordered_set<std::string> declared_trailers;
5119
0
  auto trailer_header = get_header_value(src_headers, "Trailer", "", 0);
5120
0
  if (trailer_header && std::strlen(trailer_header)) {
5121
0
    auto len = std::strlen(trailer_header);
5122
0
    split(trailer_header, trailer_header + len, ',',
5123
0
          [&](const char *b, const char *e) {
5124
0
            const char *kbeg = b;
5125
0
            const char *kend = e;
5126
0
            while (kbeg < kend && (*kbeg == ' ' || *kbeg == '\t')) {
5127
0
              ++kbeg;
5128
0
            }
5129
0
            while (kend > kbeg && (kend[-1] == ' ' || kend[-1] == '\t')) {
5130
0
              --kend;
5131
0
            }
5132
0
            std::string key(kbeg, static_cast<size_t>(kend - kbeg));
5133
0
            if (!key.empty() &&
5134
0
                prohibited_trailers.find(key) == prohibited_trailers.end()) {
5135
0
              declared_trailers.insert(key);
5136
0
            }
5137
0
          });
5138
0
  }
5139
5140
0
  size_t trailer_header_count = 0;
5141
0
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
5142
0
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
5143
0
    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
5144
5145
0
    constexpr auto line_terminator_len = 2;
5146
0
    auto line_beg = line_reader.ptr();
5147
0
    auto line_end =
5148
0
        line_reader.ptr() + line_reader.size() - line_terminator_len;
5149
5150
0
    if (!parse_header(line_beg, line_end,
5151
0
                      [&](const std::string &key, const std::string &val) {
5152
0
                        if (declared_trailers.find(key) !=
5153
0
                            declared_trailers.end()) {
5154
0
                          dest.emplace(key, val);
5155
0
                          trailer_header_count++;
5156
0
                        }
5157
0
                      })) {
5158
0
      return false;
5159
0
    }
5160
5161
0
    if (!line_reader.getline()) { return false; }
5162
0
  }
5163
5164
0
  return true;
5165
0
}
5166
5167
inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
5168
4.64M
                                      size_t right) {
5169
6.74M
  while (b + left < e && is_space_or_tab(b[left])) {
5170
2.10M
    left++;
5171
2.10M
  }
5172
6.76M
  while (right > 0 && is_space_or_tab(b[right - 1])) {
5173
2.11M
    right--;
5174
2.11M
  }
5175
4.64M
  return std::make_pair(left, right);
5176
4.64M
}
5177
5178
0
inline std::string trim_copy(const std::string &s) {
5179
0
  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
5180
0
  return s.substr(r.first, r.second - r.first);
5181
0
}
5182
5183
0
inline std::string trim_double_quotes_copy(const std::string &s) {
5184
0
  if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
5185
0
    return s.substr(1, s.size() - 2);
5186
0
  }
5187
0
  return s;
5188
0
}
5189
5190
inline void
5191
divide(const char *data, std::size_t size, char d,
5192
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
5193
40.2k
           fn) {
5194
40.2k
  const auto it = std::find(data, data + size, d);
5195
40.2k
  const auto found = static_cast<std::size_t>(it != data + size);
5196
40.2k
  const auto lhs_data = data;
5197
40.2k
  const auto lhs_size = static_cast<std::size_t>(it - data);
5198
40.2k
  const auto rhs_data = it + found;
5199
40.2k
  const auto rhs_size = size - lhs_size - found;
5200
5201
40.2k
  fn(lhs_data, lhs_size, rhs_data, rhs_size);
5202
40.2k
}
5203
5204
inline void
5205
divide(const std::string &str, char d,
5206
       std::function<void(const char *, std::size_t, const char *, std::size_t)>
5207
0
           fn) {
5208
0
  divide(str.data(), str.size(), d, std::move(fn));
5209
0
}
5210
5211
inline void split(const char *b, const char *e, char d,
5212
914
                  std::function<void(const char *, const char *)> fn) {
5213
914
  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
5214
914
}
5215
5216
inline void split(const char *b, const char *e, char d, size_t m,
5217
914
                  std::function<void(const char *, const char *)> fn) {
5218
914
  size_t i = 0;
5219
914
  size_t beg = 0;
5220
914
  size_t count = 1;
5221
5222
39.6M
  while (e ? (b + i < e) : (b[i] != '\0')) {
5223
39.6M
    if (b[i] == d && count < m) {
5224
4.64M
      auto r = trim(b, e, beg, i);
5225
4.64M
      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
5226
4.64M
      beg = i + 1;
5227
4.64M
      count++;
5228
4.64M
    }
5229
39.6M
    i++;
5230
39.6M
  }
5231
5232
914
  if (i) {
5233
914
    auto r = trim(b, e, beg, i);
5234
914
    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
5235
914
  }
5236
914
}
5237
5238
inline bool split_find(const char *b, const char *e, char d, size_t m,
5239
0
                       std::function<bool(const char *, const char *)> fn) {
5240
0
  size_t i = 0;
5241
0
  size_t beg = 0;
5242
0
  size_t count = 1;
5243
5244
0
  while (e ? (b + i < e) : (b[i] != '\0')) {
5245
0
    if (b[i] == d && count < m) {
5246
0
      auto r = trim(b, e, beg, i);
5247
0
      if (r.first < r.second) {
5248
0
        auto found = fn(&b[r.first], &b[r.second]);
5249
0
        if (found) { return true; }
5250
0
      }
5251
0
      beg = i + 1;
5252
0
      count++;
5253
0
    }
5254
0
    i++;
5255
0
  }
5256
5257
0
  if (i) {
5258
0
    auto r = trim(b, e, beg, i);
5259
0
    if (r.first < r.second) {
5260
0
      auto found = fn(&b[r.first], &b[r.second]);
5261
0
      if (found) { return true; }
5262
0
    }
5263
0
  }
5264
5265
0
  return false;
5266
0
}
5267
5268
inline bool split_find(const char *b, const char *e, char d,
5269
0
                       std::function<bool(const char *, const char *)> fn) {
5270
0
  return split_find(b, e, d, (std::numeric_limits<size_t>::max)(),
5271
0
                    std::move(fn));
5272
0
}
5273
5274
inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
5275
                                              size_t fixed_buffer_size)
5276
0
    : strm_(strm), fixed_buffer_(fixed_buffer),
5277
0
      fixed_buffer_size_(fixed_buffer_size) {}
5278
5279
0
inline const char *stream_line_reader::ptr() const {
5280
0
  if (growable_buffer_.empty()) {
5281
0
    return fixed_buffer_;
5282
0
  } else {
5283
0
    return growable_buffer_.data();
5284
0
  }
5285
0
}
5286
5287
0
inline size_t stream_line_reader::size() const {
5288
0
  if (growable_buffer_.empty()) {
5289
0
    return fixed_buffer_used_size_;
5290
0
  } else {
5291
0
    return growable_buffer_.size();
5292
0
  }
5293
0
}
5294
5295
0
inline bool stream_line_reader::end_with_crlf() const {
5296
0
  auto end = ptr() + size();
5297
0
  return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
5298
0
}
5299
5300
0
inline bool stream_line_reader::getline() {
5301
0
  fixed_buffer_used_size_ = 0;
5302
0
  growable_buffer_.clear();
5303
5304
0
#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5305
0
  char prev_byte = 0;
5306
0
#endif
5307
5308
0
  for (size_t i = 0;; i++) {
5309
0
    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {
5310
      // Treat exceptionally long lines as an error to
5311
      // prevent infinite loops/memory exhaustion
5312
0
      return false;
5313
0
    }
5314
0
    char byte;
5315
0
    auto n = strm_.read(&byte, 1);
5316
5317
0
    if (n < 0) {
5318
0
      return false;
5319
0
    } else if (n == 0) {
5320
0
      if (i == 0) {
5321
0
        return false;
5322
0
      } else {
5323
0
        break;
5324
0
      }
5325
0
    }
5326
5327
0
    append(byte);
5328
5329
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
5330
    if (byte == '\n') { break; }
5331
#else
5332
0
    if (prev_byte == '\r' && byte == '\n') { break; }
5333
0
    prev_byte = byte;
5334
0
#endif
5335
0
  }
5336
5337
0
  return true;
5338
0
}
5339
5340
0
inline void stream_line_reader::append(char c) {
5341
0
  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
5342
0
    fixed_buffer_[fixed_buffer_used_size_++] = c;
5343
0
    fixed_buffer_[fixed_buffer_used_size_] = '\0';
5344
0
  } else {
5345
0
    if (growable_buffer_.empty()) {
5346
0
      assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
5347
0
      growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
5348
0
    }
5349
0
    growable_buffer_ += c;
5350
0
  }
5351
0
}
5352
5353
0
inline mmap::mmap(const char *path) { open(path); }
5354
5355
0
inline mmap::~mmap() { close(); }
5356
5357
0
inline bool mmap::open(const char *path) {
5358
0
  close();
5359
5360
#if defined(_WIN32)
5361
  auto wpath = u8string_to_wstring(path);
5362
  if (wpath.empty()) { return false; }
5363
5364
  hFile_ =
5365
      ::CreateFile2(wpath.c_str(), GENERIC_READ,
5366
                    FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, NULL);
5367
5368
  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
5369
5370
  LARGE_INTEGER size{};
5371
  if (!::GetFileSizeEx(hFile_, &size)) { return false; }
5372
  // If the following line doesn't compile due to QuadPart, update Windows SDK.
5373
  // See:
5374
  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
5375
  if (static_cast<ULONGLONG>(size.QuadPart) >
5376
      (std::numeric_limits<decltype(size_)>::max)()) {
5377
    // `size_t` might be 32-bits, on 32-bits Windows.
5378
    return false;
5379
  }
5380
  size_ = static_cast<size_t>(size.QuadPart);
5381
5382
  hMapping_ =
5383
      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
5384
5385
  // Special treatment for an empty file...
5386
  if (hMapping_ == NULL && size_ == 0) {
5387
    close();
5388
    is_open_empty_file = true;
5389
    return true;
5390
  }
5391
5392
  if (hMapping_ == NULL) {
5393
    close();
5394
    return false;
5395
  }
5396
5397
  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
5398
5399
  if (addr_ == nullptr) {
5400
    close();
5401
    return false;
5402
  }
5403
#else
5404
0
  fd_ = ::open(path, O_RDONLY);
5405
0
  if (fd_ == -1) { return false; }
5406
5407
0
  struct stat sb;
5408
0
  if (fstat(fd_, &sb) == -1) {
5409
0
    close();
5410
0
    return false;
5411
0
  }
5412
0
  size_ = static_cast<size_t>(sb.st_size);
5413
5414
0
  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
5415
5416
  // Special treatment for an empty file...
5417
0
  if (addr_ == MAP_FAILED && size_ == 0) {
5418
0
    close();
5419
0
    is_open_empty_file = true;
5420
0
    return false;
5421
0
  }
5422
0
#endif
5423
5424
0
  return true;
5425
0
}
5426
5427
0
inline bool mmap::is_open() const {
5428
0
  return is_open_empty_file ? true : addr_ != nullptr;
5429
0
}
5430
5431
0
inline size_t mmap::size() const { return size_; }
5432
5433
0
inline const char *mmap::data() const {
5434
0
  return is_open_empty_file ? "" : static_cast<const char *>(addr_);
5435
0
}
5436
5437
0
inline void mmap::close() {
5438
#if defined(_WIN32)
5439
  if (addr_) {
5440
    ::UnmapViewOfFile(addr_);
5441
    addr_ = nullptr;
5442
  }
5443
5444
  if (hMapping_) {
5445
    ::CloseHandle(hMapping_);
5446
    hMapping_ = NULL;
5447
  }
5448
5449
  if (hFile_ != INVALID_HANDLE_VALUE) {
5450
    ::CloseHandle(hFile_);
5451
    hFile_ = INVALID_HANDLE_VALUE;
5452
  }
5453
5454
  is_open_empty_file = false;
5455
#else
5456
0
  if (addr_ != nullptr) {
5457
0
    munmap(addr_, size_);
5458
0
    addr_ = nullptr;
5459
0
  }
5460
5461
0
  if (fd_ != -1) {
5462
0
    ::close(fd_);
5463
0
    fd_ = -1;
5464
0
  }
5465
0
#endif
5466
0
  size_ = 0;
5467
0
}
5468
0
inline int close_socket(socket_t sock) noexcept {
5469
#ifdef _WIN32
5470
  return closesocket(sock);
5471
#else
5472
0
  return close(sock);
5473
0
#endif
5474
0
}
5475
5476
0
template <typename T> inline ssize_t handle_EINTR(T fn) {
5477
0
  ssize_t res = 0;
5478
0
  while (true) {
5479
0
    res = fn();
5480
0
    if (res < 0 && errno == EINTR) {
5481
0
      std::this_thread::sleep_for(std::chrono::microseconds{1});
5482
0
      continue;
5483
0
    }
5484
0
    break;
5485
0
  }
5486
0
  return res;
5487
0
}
Unexecuted instantiation: long httplib::detail::handle_EINTR<httplib::detail::select_impl(int, short, long, long)::{lambda()#1}>(httplib::detail::select_impl(int, short, 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::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})
5488
5489
0
inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
5490
0
  return handle_EINTR([&]() {
5491
0
    return recv(sock,
5492
#ifdef _WIN32
5493
                static_cast<char *>(ptr), static_cast<int>(size),
5494
#else
5495
0
                ptr, size,
5496
0
#endif
5497
0
                flags);
5498
0
  });
5499
0
}
5500
5501
inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
5502
0
                           int flags) {
5503
0
  return handle_EINTR([&]() {
5504
0
    return send(sock,
5505
#ifdef _WIN32
5506
                static_cast<const char *>(ptr), static_cast<int>(size),
5507
#else
5508
0
                ptr, size,
5509
0
#endif
5510
0
                flags);
5511
0
  });
5512
0
}
5513
5514
0
inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
5515
#ifdef _WIN32
5516
  return ::WSAPoll(fds, nfds, timeout);
5517
#else
5518
0
  return ::poll(fds, nfds, timeout);
5519
0
#endif
5520
0
}
5521
5522
inline ssize_t select_impl(socket_t sock, short events, time_t sec,
5523
0
                           time_t usec) {
5524
0
  struct pollfd pfd;
5525
0
  pfd.fd = sock;
5526
0
  pfd.events = events;
5527
0
  pfd.revents = 0;
5528
5529
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
5530
5531
0
  return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
5532
0
}
5533
5534
0
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
5535
0
  return select_impl(sock, POLLIN, sec, usec);
5536
0
}
5537
5538
0
inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
5539
0
  return select_impl(sock, POLLOUT, sec, usec);
5540
0
}
5541
5542
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
5543
0
                                        time_t usec) {
5544
0
  struct pollfd pfd_read;
5545
0
  pfd_read.fd = sock;
5546
0
  pfd_read.events = POLLIN | POLLOUT;
5547
0
  pfd_read.revents = 0;
5548
5549
0
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
5550
5551
0
  auto poll_res =
5552
0
      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
5553
5554
0
  if (poll_res == 0) { return Error::ConnectionTimeout; }
5555
5556
0
  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
5557
0
    auto error = 0;
5558
0
    socklen_t len = sizeof(error);
5559
0
    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
5560
0
                          reinterpret_cast<char *>(&error), &len);
5561
0
    auto successful = res >= 0 && !error;
5562
0
    return successful ? Error::Success : Error::Connection;
5563
0
  }
5564
5565
0
  return Error::Connection;
5566
0
}
5567
5568
0
inline bool is_socket_alive(socket_t sock) {
5569
0
  const auto val = detail::select_read(sock, 0, 0);
5570
0
  if (val == 0) {
5571
0
    return true;
5572
0
  } else if (val < 0 && errno == EBADF) {
5573
0
    return false;
5574
0
  }
5575
0
  char buf[1];
5576
0
  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
5577
0
}
5578
5579
class SocketStream final : public Stream {
5580
public:
5581
  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5582
               time_t write_timeout_sec, time_t write_timeout_usec,
5583
               time_t max_timeout_msec = 0,
5584
               std::chrono::time_point<std::chrono::steady_clock> start_time =
5585
                   (std::chrono::steady_clock::time_point::min)());
5586
  ~SocketStream() override;
5587
5588
  bool is_readable() const override;
5589
  bool wait_readable() const override;
5590
  bool wait_writable() const override;
5591
  bool is_peer_alive() const override;
5592
  ssize_t read(char *ptr, size_t size) override;
5593
  ssize_t write(const char *ptr, size_t size) override;
5594
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
5595
  void get_local_ip_and_port(std::string &ip, int &port) const override;
5596
  socket_t socket() const override;
5597
  time_t duration() const override;
5598
  void set_read_timeout(time_t sec, time_t usec = 0) override;
5599
5600
private:
5601
  socket_t sock_;
5602
  time_t read_timeout_sec_;
5603
  time_t read_timeout_usec_;
5604
  time_t write_timeout_sec_;
5605
  time_t write_timeout_usec_;
5606
  time_t max_timeout_msec_;
5607
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
5608
5609
  std::vector<char> read_buff_;
5610
  size_t read_buff_off_ = 0;
5611
  size_t read_buff_content_size_ = 0;
5612
5613
  static const size_t read_buff_size_ = 1024l * 4;
5614
};
5615
5616
inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
5617
0
                       time_t keep_alive_timeout_sec) {
5618
0
  using namespace std::chrono;
5619
5620
0
  const auto interval_usec =
5621
0
      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
5622
5623
  // Avoid expensive `steady_clock::now()` call for the first time
5624
0
  if (select_read(sock, 0, interval_usec) > 0) { return true; }
5625
5626
0
  const auto start = steady_clock::now() - microseconds{interval_usec};
5627
0
  const auto timeout = seconds{keep_alive_timeout_sec};
5628
5629
0
  while (true) {
5630
0
    if (svr_sock == INVALID_SOCKET) {
5631
0
      break; // Server socket is closed
5632
0
    }
5633
5634
0
    auto val = select_read(sock, 0, interval_usec);
5635
0
    if (val < 0) {
5636
0
      break; // Ssocket error
5637
0
    } else if (val == 0) {
5638
0
      if (steady_clock::now() - start > timeout) {
5639
0
        break; // Timeout
5640
0
      }
5641
0
    } else {
5642
0
      return true; // Ready for read
5643
0
    }
5644
0
  }
5645
5646
0
  return false;
5647
0
}
5648
5649
template <typename T>
5650
inline bool
5651
process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
5652
                           size_t keep_alive_max_count,
5653
0
                           time_t keep_alive_timeout_sec, T callback) {
5654
0
  assert(keep_alive_max_count > 0);
5655
0
  auto ret = false;
5656
0
  auto count = keep_alive_max_count;
5657
0
  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
5658
0
    auto close_connection = count == 1;
5659
0
    auto connection_closed = false;
5660
0
    ret = callback(close_connection, connection_closed);
5661
0
    if (!ret || connection_closed) { break; }
5662
0
    count--;
5663
0
  }
5664
0
  return ret;
5665
0
}
5666
5667
template <typename T>
5668
inline bool
5669
process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
5670
                      size_t keep_alive_max_count,
5671
                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,
5672
                      time_t read_timeout_usec, time_t write_timeout_sec,
5673
0
                      time_t write_timeout_usec, T callback) {
5674
0
  return process_server_socket_core(
5675
0
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
5676
0
      [&](bool close_connection, bool &connection_closed) {
5677
0
        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5678
0
                          write_timeout_sec, write_timeout_usec);
5679
0
        return callback(strm, close_connection, connection_closed);
5680
0
      });
5681
0
}
5682
5683
inline bool process_client_socket(
5684
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5685
    time_t write_timeout_sec, time_t write_timeout_usec,
5686
    time_t max_timeout_msec,
5687
    std::chrono::time_point<std::chrono::steady_clock> start_time,
5688
0
    std::function<bool(Stream &)> callback) {
5689
0
  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
5690
0
                    write_timeout_sec, write_timeout_usec, max_timeout_msec,
5691
0
                    start_time);
5692
0
  return callback(strm);
5693
0
}
5694
5695
0
inline int shutdown_socket(socket_t sock) noexcept {
5696
#ifdef _WIN32
5697
  return shutdown(sock, SD_BOTH);
5698
#else
5699
0
  return shutdown(sock, SHUT_RDWR);
5700
0
#endif
5701
0
}
5702
5703
0
inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
5704
0
  if (s.size() > 1 && s[0] == '\0') {
5705
0
    auto ret = s;
5706
0
    ret[0] = '@';
5707
0
    return ret;
5708
0
  }
5709
0
  return s;
5710
0
}
5711
5712
inline std::string
5713
0
unescape_abstract_namespace_unix_domain(const std::string &s) {
5714
0
  if (s.size() > 1 && s[0] == '@') {
5715
0
    auto ret = s;
5716
0
    ret[0] = '\0';
5717
0
    return ret;
5718
0
  }
5719
0
  return s;
5720
0
}
5721
5722
inline int getaddrinfo_with_timeout(const char *node, const char *service,
5723
                                    const struct addrinfo *hints,
5724
0
                                    struct addrinfo **res, time_t timeout_sec) {
5725
#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO
5726
  if (timeout_sec <= 0) {
5727
    // No timeout specified, use standard getaddrinfo
5728
    return getaddrinfo(node, service, hints, res);
5729
  }
5730
5731
#ifdef _WIN32
5732
  // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
5733
  OVERLAPPED overlapped = {};
5734
  HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
5735
  if (!event) { return EAI_FAIL; }
5736
5737
  overlapped.hEvent = event;
5738
5739
  PADDRINFOEXW result_addrinfo = nullptr;
5740
  HANDLE cancel_handle = nullptr;
5741
5742
  ADDRINFOEXW hints_ex = {};
5743
  if (hints) {
5744
    hints_ex.ai_flags = hints->ai_flags;
5745
    hints_ex.ai_family = hints->ai_family;
5746
    hints_ex.ai_socktype = hints->ai_socktype;
5747
    hints_ex.ai_protocol = hints->ai_protocol;
5748
  }
5749
5750
  auto wnode = u8string_to_wstring(node);
5751
  auto wservice = u8string_to_wstring(service);
5752
5753
  auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
5754
                              hints ? &hints_ex : nullptr, &result_addrinfo,
5755
                              nullptr, &overlapped, nullptr, &cancel_handle);
5756
5757
  if (ret == WSA_IO_PENDING) {
5758
    auto wait_result =
5759
        ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
5760
    if (wait_result == WAIT_TIMEOUT) {
5761
      if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
5762
      ::CloseHandle(event);
5763
      return EAI_AGAIN;
5764
    }
5765
5766
    DWORD bytes_returned;
5767
    if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
5768
                               &bytes_returned, FALSE)) {
5769
      ::CloseHandle(event);
5770
      return ::WSAGetLastError();
5771
    }
5772
  }
5773
5774
  ::CloseHandle(event);
5775
5776
  if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
5777
    *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
5778
    return 0;
5779
  }
5780
5781
  return ret;
5782
#elif TARGET_OS_MAC && defined(__clang__)
5783
  if (!node) { return EAI_NONAME; }
5784
  // macOS implementation using CFHost API for asynchronous DNS resolution
5785
  CFStringRef hostname_ref = CFStringCreateWithCString(
5786
      kCFAllocatorDefault, node, kCFStringEncodingUTF8);
5787
  if (!hostname_ref) { return EAI_MEMORY; }
5788
5789
  CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
5790
  CFRelease(hostname_ref);
5791
  if (!host_ref) { return EAI_MEMORY; }
5792
5793
  // Set up context for callback
5794
  struct CFHostContext {
5795
    bool completed = false;
5796
    bool success = false;
5797
    CFArrayRef addresses = nullptr;
5798
    std::mutex mutex;
5799
    std::condition_variable cv;
5800
  } context;
5801
5802
  CFHostClientContext client_context;
5803
  memset(&client_context, 0, sizeof(client_context));
5804
  client_context.info = &context;
5805
5806
  // Set callback
5807
  auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
5808
                     const CFStreamError *error, void *info) {
5809
    auto ctx = static_cast<CFHostContext *>(info);
5810
    std::lock_guard<std::mutex> lock(ctx->mutex);
5811
5812
    if (error && error->error != 0) {
5813
      ctx->success = false;
5814
    } else {
5815
      Boolean hasBeenResolved;
5816
      ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
5817
      if (ctx->addresses && hasBeenResolved) {
5818
        CFRetain(ctx->addresses);
5819
        ctx->success = true;
5820
      } else {
5821
        ctx->success = false;
5822
      }
5823
    }
5824
    ctx->completed = true;
5825
    ctx->cv.notify_one();
5826
  };
5827
5828
  if (!CFHostSetClient(host_ref, callback, &client_context)) {
5829
    CFRelease(host_ref);
5830
    return EAI_SYSTEM;
5831
  }
5832
5833
  // Schedule on run loop
5834
  CFRunLoopRef run_loop = CFRunLoopGetCurrent();
5835
  CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5836
5837
  // Start resolution
5838
  CFStreamError stream_error;
5839
  if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
5840
    CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5841
    CFRelease(host_ref);
5842
    return EAI_FAIL;
5843
  }
5844
5845
  // Wait for completion with timeout
5846
  auto timeout_time =
5847
      std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
5848
  bool timed_out = false;
5849
5850
  {
5851
    std::unique_lock<std::mutex> lock(context.mutex);
5852
5853
    while (!context.completed) {
5854
      auto now = std::chrono::steady_clock::now();
5855
      if (now >= timeout_time) {
5856
        timed_out = true;
5857
        break;
5858
      }
5859
5860
      // Run the runloop for a short time
5861
      lock.unlock();
5862
      CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
5863
      lock.lock();
5864
    }
5865
  }
5866
5867
  // Clean up
5868
  CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
5869
  CFHostSetClient(host_ref, nullptr, nullptr);
5870
5871
  if (timed_out || !context.completed) {
5872
    CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
5873
    CFRelease(host_ref);
5874
    return EAI_AGAIN;
5875
  }
5876
5877
  if (!context.success || !context.addresses) {
5878
    CFRelease(host_ref);
5879
    return EAI_NODATA;
5880
  }
5881
5882
  // Convert CFArray to addrinfo
5883
  CFIndex count = CFArrayGetCount(context.addresses);
5884
  if (count == 0) {
5885
    CFRelease(context.addresses);
5886
    CFRelease(host_ref);
5887
    return EAI_NODATA;
5888
  }
5889
5890
  struct addrinfo *result_addrinfo = nullptr;
5891
  struct addrinfo **current = &result_addrinfo;
5892
5893
  for (CFIndex i = 0; i < count; i++) {
5894
    CFDataRef addr_data =
5895
        static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
5896
    if (!addr_data) continue;
5897
5898
    const struct sockaddr *sockaddr_ptr =
5899
        reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
5900
    socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
5901
5902
    // Allocate addrinfo structure
5903
    *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
5904
    if (!*current) {
5905
      freeaddrinfo(result_addrinfo);
5906
      CFRelease(context.addresses);
5907
      CFRelease(host_ref);
5908
      return EAI_MEMORY;
5909
    }
5910
5911
    memset(*current, 0, sizeof(struct addrinfo));
5912
5913
    // Set up addrinfo fields
5914
    (*current)->ai_family = sockaddr_ptr->sa_family;
5915
    (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
5916
    (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
5917
    (*current)->ai_addrlen = sockaddr_len;
5918
5919
    // Copy sockaddr
5920
    (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
5921
    if (!(*current)->ai_addr) {
5922
      freeaddrinfo(result_addrinfo);
5923
      CFRelease(context.addresses);
5924
      CFRelease(host_ref);
5925
      return EAI_MEMORY;
5926
    }
5927
    memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
5928
5929
    // Set port if service is specified
5930
    if (service && *service) {
5931
      int port = 0;
5932
      if (parse_port(service, strlen(service), port)) {
5933
        if (sockaddr_ptr->sa_family == AF_INET) {
5934
          reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
5935
              ->sin_port = htons(static_cast<uint16_t>(port));
5936
        } else if (sockaddr_ptr->sa_family == AF_INET6) {
5937
          reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
5938
              ->sin6_port = htons(static_cast<uint16_t>(port));
5939
        }
5940
      }
5941
    }
5942
5943
    current = &((*current)->ai_next);
5944
  }
5945
5946
  CFRelease(context.addresses);
5947
  CFRelease(host_ref);
5948
5949
  *res = result_addrinfo;
5950
  return 0;
5951
#elif defined(_GNU_SOURCE) && defined(__GLIBC__) &&                            \
5952
    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
5953
  // #2431: gai_cancel() is non-blocking and may return EAI_NOTCANCELED while
5954
  // the resolver worker still references the stack-local gaicb. The cancel
5955
  // path therefore waits (gai_suspend with no timeout) for the worker to
5956
  // actually finish before letting the stack frame go. The trade-off is that
5957
  // a wedged DNS server can hold this thread for the system resolver timeout
5958
  // (~30s by default) past the caller's connection timeout.
5959
  struct gaicb request {};
5960
  struct gaicb *requests[1] = {&request};
5961
  struct sigevent sevp {};
5962
  struct timespec timeout {
5963
    timeout_sec, 0
5964
  };
5965
5966
  request.ar_name = node;
5967
  request.ar_service = service;
5968
  request.ar_request = hints;
5969
  sevp.sigev_notify = SIGEV_NONE;
5970
5971
  int rc = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
5972
  if (rc != 0) { return rc; }
5973
5974
  auto cleanup = scope_exit([&] {
5975
    if (request.ar_result) { freeaddrinfo(request.ar_result); }
5976
  });
5977
5978
  int wait_result = gai_suspend(requests, 1, &timeout);
5979
5980
  if (wait_result == 0 || wait_result == EAI_ALLDONE) {
5981
    int gai_result = gai_error(&request);
5982
    if (gai_result == 0) {
5983
      *res = request.ar_result;
5984
      request.ar_result = nullptr;
5985
      return 0;
5986
    }
5987
    return gai_result;
5988
  }
5989
5990
  gai_cancel(&request);
5991
  while (gai_error(&request) == EAI_INPROGRESS) {
5992
    gai_suspend(requests, 1, nullptr);
5993
  }
5994
  return wait_result;
5995
#else
5996
  // Fallback implementation using thread-based timeout for other Unix systems.
5997
5998
  struct GetAddrInfoState {
5999
    ~GetAddrInfoState() {
6000
      if (info) { freeaddrinfo(info); }
6001
    }
6002
6003
    std::mutex mutex;
6004
    std::condition_variable result_cv;
6005
    bool completed = false;
6006
    int result = EAI_SYSTEM;
6007
    std::string node;
6008
    std::string service;
6009
    struct addrinfo hints;
6010
    struct addrinfo *info = nullptr;
6011
  };
6012
6013
  // Allocate on the heap, so the resolver thread can keep using the data.
6014
  auto state = std::make_shared<GetAddrInfoState>();
6015
  if (node) { state->node = node; }
6016
  state->service = service;
6017
  state->hints = *hints;
6018
6019
  std::thread resolve_thread([state]() {
6020
    auto thread_result =
6021
        getaddrinfo(state->node.c_str(), state->service.c_str(), &state->hints,
6022
                    &state->info);
6023
6024
    std::lock_guard<std::mutex> lock(state->mutex);
6025
    state->result = thread_result;
6026
    state->completed = true;
6027
    state->result_cv.notify_one();
6028
  });
6029
6030
  // Wait for completion or timeout
6031
  std::unique_lock<std::mutex> lock(state->mutex);
6032
  auto finished =
6033
      state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
6034
                                [&] { return state->completed; });
6035
6036
  if (finished) {
6037
    // Operation completed within timeout
6038
    resolve_thread.join();
6039
    *res = state->info;
6040
    state->info = nullptr; // Pass ownership to caller
6041
    return state->result;
6042
  } else {
6043
    // Timeout occurred
6044
    resolve_thread.detach(); // Let the thread finish in background
6045
    return EAI_AGAIN;        // Return timeout error
6046
  }
6047
#endif
6048
#else
6049
0
  (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo
6050
0
  return getaddrinfo(node, service, hints, res);
6051
0
#endif
6052
0
}
6053
6054
template <typename BindOrConnect>
6055
socket_t create_socket(const std::string &host, const std::string &ip, int port,
6056
                       int address_family, int socket_flags, bool tcp_nodelay,
6057
                       bool ipv6_v6only, SocketOptions socket_options,
6058
0
                       BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
6059
  // Get address info
6060
0
  const char *node = nullptr;
6061
0
  struct addrinfo hints;
6062
0
  struct addrinfo *result;
6063
6064
0
  memset(&hints, 0, sizeof(struct addrinfo));
6065
0
  hints.ai_socktype = SOCK_STREAM;
6066
0
  hints.ai_protocol = IPPROTO_IP;
6067
6068
0
  if (!ip.empty()) {
6069
0
    node = ip.c_str();
6070
    // Ask getaddrinfo to convert IP in c-string to address
6071
0
    hints.ai_family = AF_UNSPEC;
6072
0
    hints.ai_flags = AI_NUMERICHOST;
6073
0
  } else {
6074
0
    if (!host.empty()) { node = host.c_str(); }
6075
0
    hints.ai_family = address_family;
6076
0
    hints.ai_flags = socket_flags;
6077
0
  }
6078
6079
0
#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)
6080
0
  if (hints.ai_family == AF_UNIX) {
6081
0
    const auto addrlen = host.length();
6082
0
    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
6083
6084
0
#ifdef SOCK_CLOEXEC
6085
0
    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
6086
0
                       hints.ai_protocol);
6087
#else
6088
    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
6089
#endif
6090
6091
0
    if (sock != INVALID_SOCKET) {
6092
0
      sockaddr_un addr{};
6093
0
      addr.sun_family = AF_UNIX;
6094
6095
0
      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
6096
0
      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
6097
6098
0
      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
6099
0
      hints.ai_addrlen = static_cast<socklen_t>(
6100
0
          sizeof(addr) - sizeof(addr.sun_path) + addrlen);
6101
6102
#ifndef SOCK_CLOEXEC
6103
#ifndef _WIN32
6104
      fcntl(sock, F_SETFD, FD_CLOEXEC);
6105
#endif
6106
#endif
6107
6108
0
      if (socket_options) { socket_options(sock); }
6109
6110
#ifdef _WIN32
6111
      // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
6112
      // remove the option.
6113
      set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
6114
#endif
6115
6116
0
      bool dummy;
6117
0
      if (!bind_or_connect(sock, hints, dummy)) {
6118
0
        close_socket(sock);
6119
0
        sock = INVALID_SOCKET;
6120
0
      }
6121
0
    }
6122
0
    return sock;
6123
0
  }
6124
0
#endif
6125
6126
0
  auto service = std::to_string(port);
6127
6128
0
  if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
6129
0
                               timeout_sec)) {
6130
0
#if defined __linux__ && !defined __ANDROID__
6131
0
    res_init();
6132
0
#endif
6133
0
    return INVALID_SOCKET;
6134
0
  }
6135
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
6136
6137
0
  for (auto rp = result; rp; rp = rp->ai_next) {
6138
    // Create a socket
6139
#ifdef _WIN32
6140
    auto sock =
6141
        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
6142
                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
6143
    /**
6144
     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
6145
     * and above the socket creation fails on older Windows Systems.
6146
     *
6147
     * Let's try to create a socket the old way in this case.
6148
     *
6149
     * Reference:
6150
     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
6151
     *
6152
     * WSA_FLAG_NO_HANDLE_INHERIT:
6153
     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
6154
     * SP1, and later
6155
     *
6156
     */
6157
    if (sock == INVALID_SOCKET) {
6158
      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
6159
    }
6160
#else
6161
6162
0
#ifdef SOCK_CLOEXEC
6163
0
    auto sock =
6164
0
        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
6165
#else
6166
    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
6167
#endif
6168
6169
0
#endif
6170
0
    if (sock == INVALID_SOCKET) { continue; }
6171
6172
#if !defined _WIN32 && !defined SOCK_CLOEXEC
6173
    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
6174
      close_socket(sock);
6175
      continue;
6176
    }
6177
#endif
6178
6179
0
    if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
6180
6181
0
    if (rp->ai_family == AF_INET6) {
6182
0
      set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
6183
0
    }
6184
6185
0
    if (socket_options) { socket_options(sock); }
6186
6187
    // bind or connect
6188
0
    auto quit = false;
6189
0
    if (bind_or_connect(sock, *rp, quit)) { return sock; }
6190
6191
0
    close_socket(sock);
6192
6193
0
    if (quit) { break; }
6194
0
  }
6195
6196
0
  return INVALID_SOCKET;
6197
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)
6198
6199
0
inline void set_nonblocking(socket_t sock, bool nonblocking) {
6200
#ifdef _WIN32
6201
  auto flags = nonblocking ? 1UL : 0UL;
6202
  ioctlsocket(sock, FIONBIO, &flags);
6203
#else
6204
0
  auto flags = fcntl(sock, F_GETFL, 0);
6205
0
  fcntl(sock, F_SETFL,
6206
0
        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
6207
0
#endif
6208
0
}
6209
6210
0
inline bool is_connection_error() {
6211
#ifdef _WIN32
6212
  return WSAGetLastError() != WSAEWOULDBLOCK;
6213
#else
6214
0
  return errno != EINPROGRESS;
6215
0
#endif
6216
0
}
6217
6218
0
inline bool bind_ip_address(socket_t sock, const std::string &host) {
6219
0
  struct addrinfo hints;
6220
0
  struct addrinfo *result;
6221
6222
0
  memset(&hints, 0, sizeof(struct addrinfo));
6223
0
  hints.ai_family = AF_UNSPEC;
6224
0
  hints.ai_socktype = SOCK_STREAM;
6225
0
  hints.ai_protocol = 0;
6226
6227
0
  if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
6228
0
    return false;
6229
0
  }
6230
6231
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
6232
6233
0
  auto ret = false;
6234
0
  for (auto rp = result; rp; rp = rp->ai_next) {
6235
0
    const auto &ai = *rp;
6236
0
    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
6237
0
      ret = true;
6238
0
      break;
6239
0
    }
6240
0
  }
6241
6242
0
  return ret;
6243
0
}
6244
6245
#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
6246
#define USE_IF2IP
6247
#endif
6248
6249
#ifdef USE_IF2IP
6250
0
inline std::string if2ip(int address_family, const std::string &ifn) {
6251
0
  struct ifaddrs *ifap;
6252
0
  getifaddrs(&ifap);
6253
0
  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
6254
6255
0
  std::string addr_candidate;
6256
0
  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
6257
0
    if (ifa->ifa_addr && ifn == ifa->ifa_name &&
6258
0
        (AF_UNSPEC == address_family ||
6259
0
         ifa->ifa_addr->sa_family == address_family)) {
6260
0
      if (ifa->ifa_addr->sa_family == AF_INET) {
6261
0
        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
6262
0
        char buf[INET_ADDRSTRLEN];
6263
0
        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
6264
0
          return std::string(buf, INET_ADDRSTRLEN);
6265
0
        }
6266
0
      } else if (ifa->ifa_addr->sa_family == AF_INET6) {
6267
0
        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
6268
0
        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
6269
0
          char buf[INET6_ADDRSTRLEN] = {};
6270
0
          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
6271
            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
6272
0
            auto s6_addr_head = sa->sin6_addr.s6_addr[0];
6273
0
            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
6274
0
              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
6275
0
            } else {
6276
0
              return std::string(buf, INET6_ADDRSTRLEN);
6277
0
            }
6278
0
          }
6279
0
        }
6280
0
      }
6281
0
    }
6282
0
  }
6283
0
  return addr_candidate;
6284
0
}
6285
#endif
6286
6287
inline socket_t create_client_socket(
6288
    const std::string &host, const std::string &ip, int port,
6289
    int address_family, bool tcp_nodelay, bool ipv6_v6only,
6290
    SocketOptions socket_options, time_t connection_timeout_sec,
6291
    time_t connection_timeout_usec, time_t read_timeout_sec,
6292
    time_t read_timeout_usec, time_t write_timeout_sec,
6293
0
    time_t write_timeout_usec, const std::string &intf, Error &error) {
6294
0
  auto sock = create_socket(
6295
0
      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
6296
0
      std::move(socket_options),
6297
0
      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
6298
0
        if (!intf.empty()) {
6299
0
#ifdef USE_IF2IP
6300
0
          auto ip_from_if = if2ip(address_family, intf);
6301
0
          if (ip_from_if.empty()) { ip_from_if = intf; }
6302
0
          if (!bind_ip_address(sock2, ip_from_if)) {
6303
0
            error = Error::BindIPAddress;
6304
0
            return false;
6305
0
          }
6306
0
#endif
6307
0
        }
6308
6309
0
        set_nonblocking(sock2, true);
6310
6311
0
        auto ret =
6312
0
            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
6313
6314
0
        if (ret < 0) {
6315
0
          if (is_connection_error()) {
6316
0
            error = Error::Connection;
6317
0
            return false;
6318
0
          }
6319
0
          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
6320
0
                                             connection_timeout_usec);
6321
0
          if (error != Error::Success) {
6322
0
            if (error == Error::ConnectionTimeout) { quit = true; }
6323
0
            return false;
6324
0
          }
6325
0
        }
6326
6327
0
        set_nonblocking(sock2, false);
6328
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
6329
0
                            read_timeout_usec);
6330
0
        set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
6331
0
                            write_timeout_usec);
6332
6333
0
        error = Error::Success;
6334
0
        return true;
6335
0
      },
6336
0
      connection_timeout_sec); // Pass DNS timeout
6337
6338
0
  if (sock != INVALID_SOCKET) {
6339
0
    error = Error::Success;
6340
0
  } else {
6341
0
    if (error == Error::Success) { error = Error::Connection; }
6342
0
  }
6343
6344
0
  return sock;
6345
0
}
6346
6347
inline bool get_ip_and_port(const struct sockaddr_storage &addr,
6348
0
                            socklen_t addr_len, std::string &ip, int &port) {
6349
0
  if (addr.ss_family == AF_INET) {
6350
0
    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
6351
0
  } else if (addr.ss_family == AF_INET6) {
6352
0
    port =
6353
0
        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
6354
0
  } else {
6355
0
    return false;
6356
0
  }
6357
6358
0
  std::array<char, NI_MAXHOST> ipstr{};
6359
0
  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
6360
0
                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
6361
0
                  0, NI_NUMERICHOST)) {
6362
0
    return false;
6363
0
  }
6364
6365
0
  ip = ipstr.data();
6366
0
  return true;
6367
0
}
6368
6369
0
inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
6370
0
  struct sockaddr_storage addr;
6371
0
  socklen_t addr_len = sizeof(addr);
6372
0
  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
6373
0
                   &addr_len)) {
6374
0
    get_ip_and_port(addr, addr_len, ip, port);
6375
0
  }
6376
0
}
6377
6378
0
inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
6379
0
  struct sockaddr_storage addr;
6380
0
  socklen_t addr_len = sizeof(addr);
6381
6382
0
  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
6383
0
                   &addr_len)) {
6384
0
#ifndef _WIN32
6385
0
    if (addr.ss_family == AF_UNIX) {
6386
0
#if defined(__linux__)
6387
0
      struct ucred ucred;
6388
0
      socklen_t len = sizeof(ucred);
6389
0
      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
6390
0
        port = ucred.pid;
6391
0
      }
6392
#elif defined(SOL_LOCAL) && defined(SO_PEERPID)
6393
      pid_t pid;
6394
      socklen_t len = sizeof(pid);
6395
      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
6396
        port = pid;
6397
      }
6398
#endif
6399
0
      return;
6400
0
    }
6401
0
#endif
6402
0
    get_ip_and_port(addr, addr_len, ip, port);
6403
0
  }
6404
0
}
6405
6406
// Recursive form retained so operator""_t below can compute hashes for
6407
// switch-case labels at compile time (C++11 constexpr forbids loops). Do not
6408
// call from runtime paths with arbitrary-length inputs — use str2tag()
6409
// instead, which is iterative and stack-safe.
6410
inline constexpr unsigned int str2tag_core(const char *s, size_t l,
6411
0
                                           unsigned int h) {
6412
0
  return (l == 0)
6413
0
             ? h
6414
0
             : str2tag_core(
6415
0
                   s + 1, l - 1,
6416
0
                   // Unsets the 6 high bits of h, therefore no overflow happens
6417
0
                   (((std::numeric_limits<unsigned int>::max)() >> 6) &
6418
0
                    h * 33) ^
6419
0
                       static_cast<unsigned char>(*s));
6420
0
}
6421
6422
0
inline unsigned int str2tag(const std::string &s) {
6423
  // Iterative form of str2tag_core: the recursive constexpr version is kept
6424
  // for compile-time UDL evaluation of short string literals, but at runtime
6425
  // we may receive arbitrarily long inputs (e.g. fuzzed Content-Type) that
6426
  // would blow the stack with one frame per character.
6427
0
  unsigned int h = 0;
6428
0
  for (auto c : s) {
6429
0
    h = (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^
6430
0
        static_cast<unsigned char>(c);
6431
0
  }
6432
0
  return h;
6433
0
}
6434
6435
namespace udl {
6436
6437
0
inline constexpr unsigned int operator""_t(const char *s, size_t l) {
6438
0
  return str2tag_core(s, l, 0);
6439
0
}
6440
6441
} // namespace udl
6442
6443
inline std::string
6444
find_content_type(const std::string &path,
6445
                  const std::map<std::string, std::string> &user_data,
6446
0
                  const std::string &default_content_type) {
6447
0
  auto ext = file_extension(path);
6448
6449
0
  auto it = user_data.find(ext);
6450
0
  if (it != user_data.end()) { return it->second; }
6451
6452
0
  using udl::operator""_t;
6453
6454
0
  switch (str2tag(ext)) {
6455
0
  default: return default_content_type;
6456
6457
0
  case "css"_t: return "text/css";
6458
0
  case "csv"_t: return "text/csv";
6459
0
  case "htm"_t:
6460
0
  case "html"_t: return "text/html";
6461
0
  case "js"_t:
6462
0
  case "mjs"_t: return "text/javascript";
6463
0
  case "txt"_t: return "text/plain";
6464
0
  case "vtt"_t: return "text/vtt";
6465
6466
0
  case "apng"_t: return "image/apng";
6467
0
  case "avif"_t: return "image/avif";
6468
0
  case "bmp"_t: return "image/bmp";
6469
0
  case "gif"_t: return "image/gif";
6470
0
  case "png"_t: return "image/png";
6471
0
  case "svg"_t: return "image/svg+xml";
6472
0
  case "webp"_t: return "image/webp";
6473
0
  case "ico"_t: return "image/x-icon";
6474
0
  case "tif"_t: return "image/tiff";
6475
0
  case "tiff"_t: return "image/tiff";
6476
0
  case "jpg"_t:
6477
0
  case "jpeg"_t: return "image/jpeg";
6478
6479
0
  case "mp4"_t: return "video/mp4";
6480
0
  case "mpeg"_t: return "video/mpeg";
6481
0
  case "webm"_t: return "video/webm";
6482
6483
0
  case "mp3"_t: return "audio/mp3";
6484
0
  case "mpga"_t: return "audio/mpeg";
6485
0
  case "weba"_t: return "audio/webm";
6486
0
  case "wav"_t: return "audio/wave";
6487
6488
0
  case "otf"_t: return "font/otf";
6489
0
  case "ttf"_t: return "font/ttf";
6490
0
  case "woff"_t: return "font/woff";
6491
0
  case "woff2"_t: return "font/woff2";
6492
6493
0
  case "7z"_t: return "application/x-7z-compressed";
6494
0
  case "atom"_t: return "application/atom+xml";
6495
0
  case "pdf"_t: return "application/pdf";
6496
0
  case "json"_t: return "application/json";
6497
0
  case "rss"_t: return "application/rss+xml";
6498
0
  case "tar"_t: return "application/x-tar";
6499
0
  case "xht"_t:
6500
0
  case "xhtml"_t: return "application/xhtml+xml";
6501
0
  case "xslt"_t: return "application/xslt+xml";
6502
0
  case "xml"_t: return "application/xml";
6503
0
  case "gz"_t: return "application/gzip";
6504
0
  case "zip"_t: return "application/zip";
6505
0
  case "wasm"_t: return "application/wasm";
6506
0
  }
6507
0
}
6508
6509
inline std::string
6510
extract_media_type(const std::string &content_type,
6511
0
                   std::map<std::string, std::string> *params = nullptr) {
6512
  // Extract type/subtype from Content-Type value (RFC 2045)
6513
  // e.g. "application/json; charset=utf-8" -> "application/json"
6514
0
  auto media_type = content_type;
6515
0
  auto semicolon_pos = media_type.find(';');
6516
0
  if (semicolon_pos != std::string::npos) {
6517
0
    auto param_str = media_type.substr(semicolon_pos + 1);
6518
0
    media_type = media_type.substr(0, semicolon_pos);
6519
6520
0
    if (params) {
6521
      // Parse parameters: key=value pairs separated by ';'
6522
0
      split(param_str.data(), param_str.data() + param_str.size(), ';',
6523
0
            [&](const char *b, const char *e) {
6524
0
              std::string key;
6525
0
              std::string val;
6526
0
              split(b, e, '=', [&](const char *b2, const char *e2) {
6527
0
                if (key.empty()) {
6528
0
                  key.assign(b2, e2);
6529
0
                } else {
6530
0
                  val.assign(b2, e2);
6531
0
                }
6532
0
              });
6533
0
              if (!key.empty()) {
6534
0
                params->emplace(trim_copy(key), trim_double_quotes_copy(val));
6535
0
              }
6536
0
            });
6537
0
    }
6538
0
  }
6539
6540
  // Trim whitespace from media type
6541
0
  return trim_copy(media_type);
6542
0
}
6543
6544
0
inline bool can_compress_content_type(const std::string &content_type) {
6545
0
  using udl::operator""_t;
6546
6547
0
  auto mime_type = extract_media_type(content_type);
6548
0
  auto tag = str2tag(mime_type);
6549
6550
0
  switch (tag) {
6551
0
  case "image/svg+xml"_t:
6552
0
  case "application/javascript"_t:
6553
0
  case "application/x-javascript"_t:
6554
0
  case "application/json"_t:
6555
0
  case "application/ld+json"_t:
6556
0
  case "application/xml"_t:
6557
0
  case "application/xhtml+xml"_t:
6558
0
  case "application/rss+xml"_t:
6559
0
  case "application/atom+xml"_t:
6560
0
  case "application/xslt+xml"_t:
6561
0
  case "application/protobuf"_t: return true;
6562
6563
0
  case "text/event-stream"_t: return false;
6564
6565
0
  default: return !mime_type.rfind("text/", 0);
6566
0
  }
6567
0
}
6568
6569
inline bool parse_quality(const char *b, const char *e, std::string &token,
6570
0
                          double &quality) {
6571
0
  quality = 1.0;
6572
0
  token.clear();
6573
6574
  // Split on first ';': left = token name, right = parameters
6575
0
  const char *params_b = nullptr;
6576
0
  std::size_t params_len = 0;
6577
6578
0
  divide(
6579
0
      b, static_cast<std::size_t>(e - b), ';',
6580
0
      [&](const char *lb, std::size_t llen, const char *rb, std::size_t rlen) {
6581
0
        auto r = trim(lb, lb + llen, 0, llen);
6582
0
        if (r.first < r.second) { token.assign(lb + r.first, lb + r.second); }
6583
0
        params_b = rb;
6584
0
        params_len = rlen;
6585
0
      });
6586
6587
0
  if (token.empty()) { return false; }
6588
0
  if (params_len == 0) { return true; }
6589
6590
  // Scan parameters for q= (stops on first match)
6591
0
  bool invalid = false;
6592
0
  split_find(params_b, params_b + params_len, ';',
6593
0
             (std::numeric_limits<size_t>::max)(),
6594
0
             [&](const char *pb, const char *pe) -> bool {
6595
               // Match exactly "q=" or "Q=" (not "query=" etc.)
6596
0
               auto len = static_cast<size_t>(pe - pb);
6597
0
               if (len < 2) { return false; }
6598
0
               if ((pb[0] != 'q' && pb[0] != 'Q') || pb[1] != '=') {
6599
0
                 return false;
6600
0
               }
6601
6602
               // Trim the value portion
6603
0
               auto r = trim(pb, pe, 2, len);
6604
0
               if (r.first >= r.second) {
6605
0
                 invalid = true;
6606
0
                 return true;
6607
0
               }
6608
6609
0
               double v = 0.0;
6610
0
               auto res = from_chars(pb + r.first, pb + r.second, v);
6611
0
               if (res.ec != std::errc{} || v < 0.0 || v > 1.0) {
6612
0
                 invalid = true;
6613
0
                 return true;
6614
0
               }
6615
0
               quality = v;
6616
0
               return true;
6617
0
             });
6618
6619
0
  return !invalid;
6620
0
}
6621
6622
0
inline EncodingType encoding_type(const Request &req, const Response &res) {
6623
0
  if (!can_compress_content_type(res.get_header_value("Content-Type"))) {
6624
0
    return EncodingType::None;
6625
0
  }
6626
6627
0
  const auto &s = req.get_header_value("Accept-Encoding");
6628
0
  if (s.empty()) { return EncodingType::None; }
6629
6630
  // Single-pass: iterate tokens and track the best supported encoding.
6631
  // Server preference breaks ties (br > gzip > zstd).
6632
0
  EncodingType best = EncodingType::None;
6633
0
  double best_q = 0.0; // q=0 means "not acceptable"
6634
6635
  // Server preference: Brotli > Gzip > Zstd (lower = more preferred)
6636
0
  auto priority = [](EncodingType t) -> int {
6637
0
    switch (t) {
6638
0
    case EncodingType::Brotli: return 0;
6639
0
    case EncodingType::Gzip: return 1;
6640
0
    case EncodingType::Zstd: return 2;
6641
0
    default: return 3;
6642
0
    }
6643
0
  };
6644
6645
0
  std::string name;
6646
0
  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
6647
0
    double quality = 1.0;
6648
0
    if (!parse_quality(b, e, name, quality)) { return; }
6649
0
    if (quality <= 0.0) { return; }
6650
6651
0
    EncodingType type = EncodingType::None;
6652
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6653
    if (case_ignore::equal(name, "br")) { type = EncodingType::Brotli; }
6654
#endif
6655
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6656
0
    if (type == EncodingType::None && case_ignore::equal(name, "gzip")) {
6657
0
      type = EncodingType::Gzip;
6658
0
    }
6659
0
#endif
6660
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6661
    if (type == EncodingType::None && case_ignore::equal(name, "zstd")) {
6662
      type = EncodingType::Zstd;
6663
    }
6664
#endif
6665
6666
0
    if (type == EncodingType::None) { return; }
6667
6668
    // Higher q-value wins; for equal q, server preference breaks ties
6669
0
    if (quality > best_q ||
6670
0
        (quality == best_q && priority(type) < priority(best))) {
6671
0
      best_q = quality;
6672
0
      best = type;
6673
0
    }
6674
0
  });
6675
6676
0
  return best;
6677
0
}
6678
6679
0
inline std::unique_ptr<compressor> make_compressor(EncodingType type) {
6680
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6681
0
  if (type == EncodingType::Gzip) {
6682
0
    return detail::make_unique<gzip_compressor>();
6683
0
  }
6684
0
#endif
6685
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6686
  if (type == EncodingType::Brotli) {
6687
    return detail::make_unique<brotli_compressor>();
6688
  }
6689
#endif
6690
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6691
  if (type == EncodingType::Zstd) {
6692
    return detail::make_unique<zstd_compressor>();
6693
  }
6694
#endif
6695
0
  (void)type;
6696
0
  return nullptr;
6697
0
}
6698
6699
0
inline const char *encoding_name(EncodingType type) {
6700
0
  switch (type) {
6701
0
  case EncodingType::Gzip: return "gzip";
6702
0
  case EncodingType::Brotli: return "br";
6703
0
  case EncodingType::Zstd: return "zstd";
6704
0
  default: return "";
6705
0
  }
6706
0
}
6707
6708
inline bool nocompressor::compress(const char *data, size_t data_length,
6709
0
                                   bool /*last*/, Callback callback) {
6710
0
  if (!data_length) { return true; }
6711
0
  return callback(data, data_length);
6712
0
}
6713
6714
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6715
0
inline gzip_compressor::gzip_compressor() {
6716
0
  std::memset(&strm_, 0, sizeof(strm_));
6717
0
  strm_.zalloc = Z_NULL;
6718
0
  strm_.zfree = Z_NULL;
6719
0
  strm_.opaque = Z_NULL;
6720
6721
0
  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
6722
0
                           Z_DEFAULT_STRATEGY) == Z_OK;
6723
0
}
6724
6725
0
inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
6726
6727
inline bool gzip_compressor::compress(const char *data, size_t data_length,
6728
0
                                      bool last, Callback callback) {
6729
0
  assert(is_valid_);
6730
6731
0
  do {
6732
0
    constexpr size_t max_avail_in =
6733
0
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
6734
6735
0
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
6736
0
        (std::min)(data_length, max_avail_in));
6737
0
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
6738
6739
0
    data_length -= strm_.avail_in;
6740
0
    data += strm_.avail_in;
6741
6742
0
    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
6743
0
    auto ret = Z_OK;
6744
6745
0
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6746
0
    do {
6747
0
      strm_.avail_out = static_cast<uInt>(buff.size());
6748
0
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
6749
6750
0
      ret = deflate(&strm_, flush);
6751
0
      if (ret == Z_STREAM_ERROR) { return false; }
6752
6753
0
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
6754
0
        return false;
6755
0
      }
6756
0
    } while (strm_.avail_out == 0);
6757
6758
0
    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
6759
0
           (flush == Z_NO_FLUSH && ret == Z_OK));
6760
0
    assert(strm_.avail_in == 0);
6761
0
  } while (data_length > 0);
6762
6763
0
  return true;
6764
0
}
6765
6766
0
inline gzip_decompressor::gzip_decompressor() {
6767
0
  std::memset(&strm_, 0, sizeof(strm_));
6768
0
  strm_.zalloc = Z_NULL;
6769
0
  strm_.zfree = Z_NULL;
6770
0
  strm_.opaque = Z_NULL;
6771
6772
  // 15 is the value of wbits, which should be at the maximum possible value
6773
  // to ensure that any gzip stream can be decoded. The offset of 32 specifies
6774
  // that the stream type should be automatically detected either gzip or
6775
  // deflate.
6776
0
  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
6777
0
}
6778
6779
0
inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
6780
6781
0
inline bool gzip_decompressor::is_valid() const { return is_valid_; }
6782
6783
inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
6784
0
                                          Callback callback) {
6785
0
  assert(is_valid_);
6786
6787
0
  auto ret = Z_OK;
6788
6789
0
  do {
6790
0
    constexpr size_t max_avail_in =
6791
0
        (std::numeric_limits<decltype(strm_.avail_in)>::max)();
6792
6793
0
    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
6794
0
        (std::min)(data_length, max_avail_in));
6795
0
    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
6796
6797
0
    data_length -= strm_.avail_in;
6798
0
    data += strm_.avail_in;
6799
6800
0
    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6801
0
    while (strm_.avail_in > 0 && ret == Z_OK) {
6802
0
      strm_.avail_out = static_cast<uInt>(buff.size());
6803
0
      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
6804
6805
0
      ret = inflate(&strm_, Z_NO_FLUSH);
6806
6807
0
      assert(ret != Z_STREAM_ERROR);
6808
0
      switch (ret) {
6809
0
      case Z_NEED_DICT:
6810
0
      case Z_DATA_ERROR:
6811
0
      case Z_MEM_ERROR: inflateEnd(&strm_); return false;
6812
0
      }
6813
6814
0
      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
6815
0
        return false;
6816
0
      }
6817
0
    }
6818
6819
0
    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
6820
6821
0
  } while (data_length > 0);
6822
6823
0
  return true;
6824
0
}
6825
#endif
6826
6827
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6828
inline brotli_compressor::brotli_compressor() {
6829
  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
6830
}
6831
6832
inline brotli_compressor::~brotli_compressor() {
6833
  BrotliEncoderDestroyInstance(state_);
6834
}
6835
6836
inline bool brotli_compressor::compress(const char *data, size_t data_length,
6837
                                        bool last, Callback callback) {
6838
  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6839
6840
  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
6841
  auto available_in = data_length;
6842
  auto next_in = reinterpret_cast<const uint8_t *>(data);
6843
6844
  for (;;) {
6845
    if (last) {
6846
      if (BrotliEncoderIsFinished(state_)) { break; }
6847
    } else {
6848
      if (!available_in) { break; }
6849
    }
6850
6851
    auto available_out = buff.size();
6852
    auto next_out = buff.data();
6853
6854
    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
6855
                                     &available_out, &next_out, nullptr)) {
6856
      return false;
6857
    }
6858
6859
    auto output_bytes = buff.size() - available_out;
6860
    if (output_bytes) {
6861
      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
6862
    }
6863
  }
6864
6865
  return true;
6866
}
6867
6868
inline brotli_decompressor::brotli_decompressor() {
6869
  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
6870
  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
6871
                        : BROTLI_DECODER_RESULT_ERROR;
6872
}
6873
6874
inline brotli_decompressor::~brotli_decompressor() {
6875
  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
6876
}
6877
6878
inline bool brotli_decompressor::is_valid() const { return decoder_s; }
6879
6880
inline bool brotli_decompressor::decompress(const char *data,
6881
                                            size_t data_length,
6882
                                            Callback callback) {
6883
  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6884
      decoder_r == BROTLI_DECODER_RESULT_ERROR) {
6885
    return 0;
6886
  }
6887
6888
  auto next_in = reinterpret_cast<const uint8_t *>(data);
6889
  size_t avail_in = data_length;
6890
  size_t total_out;
6891
6892
  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
6893
6894
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6895
  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
6896
    char *next_out = buff.data();
6897
    size_t avail_out = buff.size();
6898
6899
    decoder_r = BrotliDecoderDecompressStream(
6900
        decoder_s, &avail_in, &next_in, &avail_out,
6901
        reinterpret_cast<uint8_t **>(&next_out), &total_out);
6902
6903
    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
6904
6905
    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
6906
  }
6907
6908
  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
6909
         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
6910
}
6911
#endif
6912
6913
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6914
inline zstd_compressor::zstd_compressor() {
6915
  ctx_ = ZSTD_createCCtx();
6916
  ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
6917
}
6918
6919
inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
6920
6921
inline bool zstd_compressor::compress(const char *data, size_t data_length,
6922
                                      bool last, Callback callback) {
6923
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6924
6925
  ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
6926
  ZSTD_inBuffer input = {data, data_length, 0};
6927
6928
  bool finished;
6929
  do {
6930
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
6931
    size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
6932
6933
    if (ZSTD_isError(remaining)) { return false; }
6934
6935
    if (!callback(buff.data(), output.pos)) { return false; }
6936
6937
    finished = last ? (remaining == 0) : (input.pos == input.size);
6938
6939
  } while (!finished);
6940
6941
  return true;
6942
}
6943
6944
inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
6945
6946
inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
6947
6948
inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
6949
6950
inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
6951
                                          Callback callback) {
6952
  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
6953
  ZSTD_inBuffer input = {data, data_length, 0};
6954
6955
  while (input.pos < input.size) {
6956
    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
6957
    size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
6958
6959
    if (ZSTD_isError(remaining)) { return false; }
6960
6961
    if (!callback(buff.data(), output.pos)) { return false; }
6962
  }
6963
6964
  return true;
6965
}
6966
#endif
6967
6968
inline std::unique_ptr<decompressor>
6969
0
create_decompressor(const std::string &encoding) {
6970
0
  std::unique_ptr<decompressor> decompressor;
6971
6972
0
  if (encoding == "gzip" || encoding == "deflate") {
6973
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6974
0
    decompressor = detail::make_unique<gzip_decompressor>();
6975
0
#endif
6976
0
  } else if (encoding.find("br") != std::string::npos) {
6977
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6978
    decompressor = detail::make_unique<brotli_decompressor>();
6979
#endif
6980
0
  } else if (encoding == "zstd" || encoding.find("zstd") != std::string::npos) {
6981
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
6982
    decompressor = detail::make_unique<zstd_decompressor>();
6983
#endif
6984
0
  }
6985
6986
0
  return decompressor;
6987
0
}
6988
6989
// Returns the best available compressor and its Content-Encoding name.
6990
// Priority: Brotli > Gzip > Zstd (matches server-side preference).
6991
inline std::pair<std::unique_ptr<compressor>, const char *>
6992
0
create_compressor() {
6993
0
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6994
0
  return {detail::make_unique<brotli_compressor>(), "br"};
6995
0
#elif defined(CPPHTTPLIB_ZLIB_SUPPORT)
6996
0
  return {detail::make_unique<gzip_compressor>(), "gzip"};
6997
0
#elif defined(CPPHTTPLIB_ZSTD_SUPPORT)
6998
0
  return {detail::make_unique<zstd_compressor>(), "zstd"};
6999
0
#else
7000
0
  return {nullptr, nullptr};
7001
0
#endif
7002
0
}
7003
7004
0
inline bool is_prohibited_header_name(const std::string &name) {
7005
0
  using udl::operator""_t;
7006
7007
0
  switch (str2tag(name)) {
7008
0
  case "REMOTE_ADDR"_t:
7009
0
  case "REMOTE_PORT"_t:
7010
0
  case "LOCAL_ADDR"_t:
7011
0
  case "LOCAL_PORT"_t: return true;
7012
0
  default: return false;
7013
0
  }
7014
0
}
7015
7016
0
inline bool has_header(const Headers &headers, const std::string &key) {
7017
0
  if (is_prohibited_header_name(key)) { return false; }
7018
0
  return headers.find(key) != headers.end();
7019
0
}
7020
7021
inline const char *get_header_value(const Headers &headers,
7022
                                    const std::string &key, const char *def,
7023
0
                                    size_t id) {
7024
0
  if (is_prohibited_header_name(key)) {
7025
0
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
7026
0
    std::string msg = "Prohibited header name '" + key + "' is specified.";
7027
0
    throw std::invalid_argument(msg);
7028
#else
7029
    return "";
7030
#endif
7031
0
  }
7032
7033
0
  auto rng = headers.equal_range(key);
7034
0
  auto it = rng.first;
7035
0
  std::advance(it, static_cast<ssize_t>(id));
7036
0
  if (it != rng.second) { return it->second.c_str(); }
7037
0
  return def;
7038
0
}
7039
7040
inline size_t get_header_value_count(const Headers &headers,
7041
0
                                     const std::string &key) {
7042
0
  auto r = headers.equal_range(key);
7043
0
  return static_cast<size_t>(std::distance(r.first, r.second));
7044
0
}
7045
7046
template <typename Map>
7047
inline typename Map::mapped_type
7048
0
get_multimap_value(const Map &m, const std::string &key, size_t id) {
7049
0
  auto rng = m.equal_range(key);
7050
0
  auto it = rng.first;
7051
0
  std::advance(it, static_cast<ssize_t>(id));
7052
0
  if (it != rng.second) { return it->second; }
7053
0
  return typename Map::mapped_type();
7054
0
}
Unexecuted instantiation: 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> > > > >::mapped_type httplib::detail::get_multimap_value<std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > >(std::__1::unordered_multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::detail::case_ignore::hash, httplib::detail::case_ignore::equal_to, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)
Unexecuted instantiation: std::__1::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> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, 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> > > > >::mapped_type httplib::detail::get_multimap_value<std::__1::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> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > >(std::__1::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> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)
Unexecuted instantiation: std::__1::multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::FormData, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, httplib::FormData> > >::mapped_type httplib::detail::get_multimap_value<std::__1::multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::FormData, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, httplib::FormData> > > >(std::__1::multimap<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, httplib::FormData, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, httplib::FormData> > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long)
7055
7056
inline void set_header(Headers &headers, const std::string &key,
7057
0
                       const std::string &val) {
7058
0
  if (fields::is_field_name(key) && fields::is_field_value(val)) {
7059
0
    headers.emplace(key, val);
7060
0
  }
7061
0
}
7062
7063
0
inline bool read_headers(Stream &strm, Headers &headers) {
7064
0
  const auto bufsiz = 2048;
7065
0
  char buf[bufsiz];
7066
0
  stream_line_reader line_reader(strm, buf, bufsiz);
7067
7068
0
  size_t header_count = 0;
7069
7070
0
  for (;;) {
7071
0
    if (!line_reader.getline()) { return false; }
7072
7073
    // Check if the line ends with CRLF.
7074
0
    auto line_terminator_len = 2;
7075
0
    if (line_reader.end_with_crlf()) {
7076
      // Blank line indicates end of headers.
7077
0
      if (line_reader.size() == 2) { break; }
7078
0
    } else {
7079
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7080
      // Blank line indicates end of headers.
7081
      if (line_reader.size() == 1) { break; }
7082
      line_terminator_len = 1;
7083
#else
7084
0
      continue; // Skip invalid line.
7085
0
#endif
7086
0
    }
7087
7088
0
    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
7089
7090
    // Check header count limit
7091
0
    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }
7092
7093
    // Exclude line terminator
7094
0
    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
7095
7096
0
    if (!parse_header(line_reader.ptr(), end,
7097
0
                      [&](const std::string &key, const std::string &val) {
7098
0
                        headers.emplace(key, val);
7099
0
                      })) {
7100
0
      return false;
7101
0
    }
7102
7103
0
    header_count++;
7104
0
  }
7105
7106
  // RFC 9110 Section 8.6: Reject requests with multiple Content-Length
7107
  // headers that have different values to prevent request smuggling.
7108
0
  auto cl_range = headers.equal_range("Content-Length");
7109
0
  if (cl_range.first != cl_range.second) {
7110
0
    const auto &first_val = cl_range.first->second;
7111
0
    for (auto it = std::next(cl_range.first); it != cl_range.second; ++it) {
7112
0
      if (it->second != first_val) { return false; }
7113
0
    }
7114
0
  }
7115
7116
0
  return true;
7117
0
}
7118
7119
inline bool read_websocket_upgrade_response(Stream &strm,
7120
                                            const std::string &expected_accept,
7121
0
                                            std::string &selected_subprotocol) {
7122
0
  // Read status line
7123
0
  const auto bufsiz = 2048;
7124
0
  char buf[bufsiz];
7125
0
  stream_line_reader line_reader(strm, buf, bufsiz);
7126
0
  if (!line_reader.getline()) { return false; }
7127
0
7128
0
  // Check for "HTTP/1.1 101"
7129
0
  auto line = std::string(line_reader.ptr(), line_reader.size());
7130
0
  if (line.find("HTTP/1.1 101") == std::string::npos) { return false; }
7131
0
7132
0
  // Parse headers using existing read_headers
7133
0
  Headers headers;
7134
0
  if (!read_headers(strm, headers)) { return false; }
7135
0
7136
0
  // Verify Upgrade: websocket (case-insensitive)
7137
0
  auto upgrade_it = headers.find("Upgrade");
7138
0
  if (upgrade_it == headers.end()) { return false; }
7139
0
  auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
7140
0
  if (upgrade_val != "websocket") { return false; }
7141
0
7142
0
  // Verify Connection header contains "Upgrade" (case-insensitive)
7143
0
  auto connection_it = headers.find("Connection");
7144
0
  if (connection_it == headers.end()) { return false; }
7145
0
  auto connection_val = case_ignore::to_lower(connection_it->second);
7146
0
  if (connection_val.find("upgrade") == std::string::npos) { return false; }
7147
0
7148
0
  // Verify Sec-WebSocket-Accept header value
7149
0
  auto it = headers.find("Sec-WebSocket-Accept");
7150
0
  if (it == headers.end() || it->second != expected_accept) { return false; }
7151
0
7152
0
  // Extract negotiated subprotocol
7153
0
  auto proto_it = headers.find("Sec-WebSocket-Protocol");
7154
0
  if (proto_it != headers.end()) { selected_subprotocol = proto_it->second; }
7155
0
7156
0
  return true;
7157
0
}
7158
7159
enum class ReadContentResult {
7160
  Success,         // Successfully read the content
7161
  PayloadTooLarge, // The content exceeds the specified payload limit
7162
  Error            // An error occurred while reading the content
7163
};
7164
7165
inline ReadContentResult read_content_with_length(
7166
    Stream &strm, size_t len, DownloadProgress progress,
7167
    ContentReceiverWithProgress out,
7168
0
    size_t payload_max_length = (std::numeric_limits<size_t>::max)()) {
7169
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
7170
7171
0
  detail::BodyReader br;
7172
0
  br.stream = &strm;
7173
0
  br.has_content_length = true;
7174
0
  br.content_length = len;
7175
0
  br.payload_max_length = payload_max_length;
7176
0
  br.chunked = false;
7177
0
  br.bytes_read = 0;
7178
0
  br.last_error = Error::Success;
7179
7180
0
  size_t r = 0;
7181
0
  while (r < len) {
7182
0
    auto read_len = static_cast<size_t>(len - r);
7183
0
    auto to_read = (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ);
7184
0
    auto n = detail::read_body_content(&strm, br, buf, to_read);
7185
0
    if (n <= 0) {
7186
      // Check if it was a payload size error
7187
0
      if (br.last_error == Error::ExceedMaxPayloadSize) {
7188
0
        return ReadContentResult::PayloadTooLarge;
7189
0
      }
7190
0
      return ReadContentResult::Error;
7191
0
    }
7192
7193
0
    if (!out(buf, static_cast<size_t>(n), r, len)) {
7194
0
      return ReadContentResult::Error;
7195
0
    }
7196
0
    r += static_cast<size_t>(n);
7197
7198
0
    if (progress) {
7199
0
      if (!progress(r, len)) { return ReadContentResult::Error; }
7200
0
    }
7201
0
  }
7202
7203
0
  return ReadContentResult::Success;
7204
0
}
7205
7206
inline ReadContentResult
7207
read_content_without_length(Stream &strm, size_t payload_max_length,
7208
0
                            ContentReceiverWithProgress out) {
7209
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
7210
0
  size_t r = 0;
7211
0
  for (;;) {
7212
0
    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
7213
0
    if (n == 0) { return ReadContentResult::Success; }
7214
0
    if (n < 0) { return ReadContentResult::Error; }
7215
7216
    // Check if adding this data would exceed the payload limit
7217
0
    if (r > payload_max_length ||
7218
0
        payload_max_length - r < static_cast<size_t>(n)) {
7219
0
      return ReadContentResult::PayloadTooLarge;
7220
0
    }
7221
7222
0
    if (!out(buf, static_cast<size_t>(n), r, 0)) {
7223
0
      return ReadContentResult::Error;
7224
0
    }
7225
0
    r += static_cast<size_t>(n);
7226
0
  }
7227
7228
0
  return ReadContentResult::Success;
7229
0
}
7230
7231
template <typename T>
7232
inline ReadContentResult read_content_chunked(Stream &strm, T &x,
7233
                                              size_t payload_max_length,
7234
0
                                              ContentReceiverWithProgress out) {
7235
0
  detail::ChunkedDecoder dec(strm);
7236
7237
0
  char buf[CPPHTTPLIB_RECV_BUFSIZ];
7238
0
  size_t total_len = 0;
7239
7240
0
  for (;;) {
7241
0
    size_t chunk_offset = 0;
7242
0
    size_t chunk_total = 0;
7243
0
    auto n = dec.read_payload(buf, sizeof(buf), chunk_offset, chunk_total);
7244
0
    if (n < 0) { return ReadContentResult::Error; }
7245
7246
0
    if (n == 0) {
7247
0
      if (!dec.parse_trailers_into(x.trailers, x.headers)) {
7248
0
        return ReadContentResult::Error;
7249
0
      }
7250
0
      return ReadContentResult::Success;
7251
0
    }
7252
7253
0
    if (total_len > payload_max_length ||
7254
0
        payload_max_length - total_len < static_cast<size_t>(n)) {
7255
0
      return ReadContentResult::PayloadTooLarge;
7256
0
    }
7257
7258
0
    if (!out(buf, static_cast<size_t>(n), chunk_offset, chunk_total)) {
7259
0
      return ReadContentResult::Error;
7260
0
    }
7261
7262
0
    total_len += static_cast<size_t>(n);
7263
0
  }
7264
0
}
Unexecuted instantiation: httplib::detail::ReadContentResult httplib::detail::read_content_chunked<httplib::Request>(httplib::Stream&, httplib::Request&, unsigned long, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)
Unexecuted instantiation: httplib::detail::ReadContentResult httplib::detail::read_content_chunked<httplib::Response>(httplib::Stream&, httplib::Response&, unsigned long, std::__1::function<bool (char const*, unsigned long, unsigned long, unsigned long)>)
7265
7266
0
inline bool is_chunked_transfer_encoding(const Headers &headers) {
7267
0
  return case_ignore::equal(
7268
0
      get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
7269
0
}
7270
7271
template <typename T, typename U>
7272
bool prepare_content_receiver(T &x, int &status,
7273
                              ContentReceiverWithProgress receiver,
7274
                              bool decompress, size_t payload_max_length,
7275
0
                              bool &exceed_payload_max_length, U callback) {
7276
0
  if (decompress) {
7277
0
    std::string encoding = x.get_header_value("Content-Encoding");
7278
0
    std::unique_ptr<decompressor> decompressor;
7279
7280
0
    if (!encoding.empty()) {
7281
0
      decompressor = detail::create_decompressor(encoding);
7282
0
      if (!decompressor) {
7283
        // Unsupported encoding or no support compiled in
7284
0
        status = StatusCode::UnsupportedMediaType_415;
7285
0
        return false;
7286
0
      }
7287
0
    }
7288
7289
0
    if (decompressor) {
7290
0
      if (decompressor->is_valid()) {
7291
0
        size_t decompressed_size = 0;
7292
0
        ContentReceiverWithProgress out = [&](const char *buf, size_t n,
7293
0
                                              size_t off, size_t len) {
7294
0
          return decompressor->decompress(
7295
0
              buf, n, [&](const char *buf2, size_t n2) {
7296
                // Guard against zip-bomb: check
7297
                // decompressed size against limit.
7298
0
                if (payload_max_length > 0 &&
7299
0
                    (decompressed_size >= payload_max_length ||
7300
0
                     n2 > payload_max_length - decompressed_size)) {
7301
0
                  exceed_payload_max_length = true;
7302
0
                  return false;
7303
0
                }
7304
0
                decompressed_size += n2;
7305
0
                return receiver(buf2, n2, off, len);
7306
0
              });
7307
0
        };
7308
0
        return callback(std::move(out));
7309
0
      } else {
7310
0
        status = StatusCode::InternalServerError_500;
7311
0
        return false;
7312
0
      }
7313
0
    }
7314
0
  }
7315
7316
0
  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,
7317
0
                                        size_t len) {
7318
0
    return receiver(buf, n, off, len);
7319
0
  };
7320
0
  return callback(std::move(out));
7321
0
}
Unexecuted instantiation: 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, 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})
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, 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})
7322
7323
template <typename T>
7324
bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
7325
                  DownloadProgress progress,
7326
0
                  ContentReceiverWithProgress receiver, bool decompress) {
7327
0
  bool exceed_payload_max_length = false;
7328
0
  return prepare_content_receiver(
7329
0
      x, status, std::move(receiver), decompress, payload_max_length,
7330
0
      exceed_payload_max_length, [&](const ContentReceiverWithProgress &out) {
7331
0
        auto ret = true;
7332
        // Note: exceed_payload_max_length may also be set by the decompressor
7333
        // wrapper in prepare_content_receiver when the decompressed payload
7334
        // size exceeds the limit.
7335
7336
0
        if (is_chunked_transfer_encoding(x.headers)) {
7337
0
          auto result = read_content_chunked(strm, x, payload_max_length, out);
7338
0
          if (result == ReadContentResult::Success) {
7339
0
            ret = true;
7340
0
          } else if (result == ReadContentResult::PayloadTooLarge) {
7341
0
            exceed_payload_max_length = true;
7342
0
            ret = false;
7343
0
          } else {
7344
0
            ret = false;
7345
0
          }
7346
0
        } else if (!has_header(x.headers, "Content-Length")) {
7347
0
          auto result =
7348
0
              read_content_without_length(strm, payload_max_length, out);
7349
0
          if (result == ReadContentResult::Success) {
7350
0
            ret = true;
7351
0
          } else if (result == ReadContentResult::PayloadTooLarge) {
7352
0
            exceed_payload_max_length = true;
7353
0
            ret = false;
7354
0
          } else {
7355
0
            ret = false;
7356
0
          }
7357
0
        } else {
7358
0
          auto is_invalid_value = false;
7359
0
          auto len = get_header_value_u64(x.headers, "Content-Length",
7360
0
                                          (std::numeric_limits<size_t>::max)(),
7361
0
                                          0, is_invalid_value);
7362
7363
0
          if (is_invalid_value) {
7364
0
            ret = false;
7365
0
          } else if (len > 0) {
7366
0
            auto result = read_content_with_length(
7367
0
                strm, len, std::move(progress), out, payload_max_length);
7368
0
            ret = (result == ReadContentResult::Success);
7369
0
            if (result == ReadContentResult::PayloadTooLarge) {
7370
0
              exceed_payload_max_length = true;
7371
0
            }
7372
0
          }
7373
0
        }
7374
7375
0
        if (!ret) {
7376
0
          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413
7377
0
                                             : StatusCode::BadRequest_400;
7378
0
        }
7379
0
        return ret;
7380
0
      });
7381
0
}
Unexecuted instantiation: 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)
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)
7382
7383
inline ssize_t write_request_line(Stream &strm, const std::string &method,
7384
0
                                  const std::string &path) {
7385
0
  std::string s = method;
7386
0
  s += ' ';
7387
0
  s += path;
7388
0
  s += " HTTP/1.1\r\n";
7389
0
  return strm.write(s.data(), s.size());
7390
0
}
7391
7392
0
inline ssize_t write_response_line(Stream &strm, int status) {
7393
0
  std::string s = "HTTP/1.1 ";
7394
0
  s += std::to_string(status);
7395
0
  s += ' ';
7396
0
  s += httplib::status_message(status);
7397
0
  s += "\r\n";
7398
0
  return strm.write(s.data(), s.size());
7399
0
}
7400
7401
0
inline ssize_t write_headers(Stream &strm, const Headers &headers) {
7402
0
  ssize_t write_len = 0;
7403
0
  for (const auto &x : headers) {
7404
0
    std::string s;
7405
0
    s = x.first;
7406
0
    s += ": ";
7407
0
    s += x.second;
7408
0
    s += "\r\n";
7409
0
7410
0
    auto len = strm.write(s.data(), s.size());
7411
0
    if (len < 0) { return len; }
7412
0
    write_len += len;
7413
0
  }
7414
0
  auto len = strm.write("\r\n");
7415
0
  if (len < 0) { return len; }
7416
0
  write_len += len;
7417
0
  return write_len;
7418
0
}
7419
7420
0
inline bool write_data(Stream &strm, const char *d, size_t l) {
7421
0
  size_t offset = 0;
7422
0
  while (offset < l) {
7423
0
    auto length = strm.write(d + offset, l - offset);
7424
0
    if (length < 0) { return false; }
7425
0
    offset += static_cast<size_t>(length);
7426
0
  }
7427
0
  return true;
7428
0
}
7429
7430
template <typename T>
7431
inline bool write_content_with_progress(Stream &strm,
7432
                                        const ContentProvider &content_provider,
7433
                                        size_t offset, size_t length,
7434
                                        T is_shutting_down,
7435
                                        const UploadProgress &upload_progress,
7436
0
                                        Error &error) {
7437
0
  size_t end_offset = offset + length;
7438
0
  size_t start_offset = offset;
7439
0
  auto ok = true;
7440
0
  DataSink data_sink;
7441
7442
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
7443
0
    if (ok) {
7444
0
      if (write_data(strm, d, l)) {
7445
0
        offset += l;
7446
7447
0
        if (upload_progress && length > 0) {
7448
0
          size_t current_written = offset - start_offset;
7449
0
          if (!upload_progress(current_written, length)) {
7450
0
            ok = false;
7451
0
            return false;
7452
0
          }
7453
0
        }
7454
0
      } else {
7455
0
        ok = false;
7456
0
      }
7457
0
    }
7458
0
    return ok;
7459
0
  };
7460
7461
0
  data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive(); };
7462
7463
0
  while (offset < end_offset && !is_shutting_down()) {
7464
0
    if (!strm.wait_writable() || !strm.is_peer_alive()) {
7465
0
      error = Error::Write;
7466
0
      return false;
7467
0
    } else if (!content_provider(offset, end_offset - offset, data_sink)) {
7468
0
      error = Error::Canceled;
7469
0
      return false;
7470
0
    } else if (!ok) {
7471
0
      error = Error::Write;
7472
0
      return false;
7473
0
    }
7474
0
  }
7475
7476
0
  if (offset < end_offset) { // exited due to is_shutting_down(), not completion
7477
0
    error = Error::Write;
7478
0
    return false;
7479
0
  }
7480
7481
0
  error = Error::Success;
7482
0
  return true;
7483
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&)
7484
7485
template <typename T>
7486
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
7487
                          size_t offset, size_t length, T is_shutting_down,
7488
0
                          Error &error) {
7489
0
  return write_content_with_progress<T>(strm, content_provider, offset, length,
7490
0
                                        is_shutting_down, nullptr, error);
7491
0
}
7492
7493
template <typename T>
7494
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
7495
                          size_t offset, size_t length,
7496
0
                          const T &is_shutting_down) {
7497
0
  auto error = Error::Success;
7498
0
  return write_content(strm, content_provider, offset, length, is_shutting_down,
7499
0
                       error);
7500
0
}
7501
7502
template <typename T>
7503
inline bool
7504
write_content_without_length(Stream &strm,
7505
                             const ContentProvider &content_provider,
7506
0
                             const T &is_shutting_down) {
7507
0
  size_t offset = 0;
7508
0
  auto data_available = true;
7509
0
  auto ok = true;
7510
0
  DataSink data_sink;
7511
7512
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
7513
0
    if (ok) {
7514
0
      offset += l;
7515
0
      if (!write_data(strm, d, l)) { ok = false; }
7516
0
    }
7517
0
    return ok;
7518
0
  };
7519
7520
0
  data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive(); };
7521
7522
0
  data_sink.done = [&](void) { data_available = false; };
7523
7524
0
  while (data_available && !is_shutting_down()) {
7525
0
    if (!strm.wait_writable() || !strm.is_peer_alive()) {
7526
0
      return false;
7527
0
    } else if (!content_provider(offset, 0, data_sink)) {
7528
0
      return false;
7529
0
    } else if (!ok) {
7530
0
      return false;
7531
0
    }
7532
0
  }
7533
0
  return !data_available; // true only if done() was called, false if shutting
7534
                          // down
7535
0
}
7536
7537
template <typename T, typename U>
7538
inline bool
7539
write_content_chunked(Stream &strm, const ContentProvider &content_provider,
7540
0
                      const T &is_shutting_down, U &compressor, Error &error) {
7541
0
  size_t offset = 0;
7542
0
  auto data_available = true;
7543
0
  auto ok = true;
7544
0
  DataSink data_sink;
7545
7546
0
  data_sink.write = [&](const char *d, size_t l) -> bool {
7547
0
    if (ok) {
7548
0
      data_available = l > 0;
7549
0
      offset += l;
7550
7551
0
      std::string payload;
7552
0
      if (compressor.compress(d, l, false,
7553
0
                              [&](const char *data, size_t data_len) {
7554
0
                                payload.append(data, data_len);
7555
0
                                return true;
7556
0
                              })) {
7557
0
        if (!payload.empty()) {
7558
          // Emit chunked response header and footer for each chunk
7559
0
          auto chunk =
7560
0
              from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
7561
0
          if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
7562
0
        }
7563
0
      } else {
7564
0
        ok = false;
7565
0
      }
7566
0
    }
7567
0
    return ok;
7568
0
  };
7569
7570
0
  data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive(); };
7571
7572
0
  auto done_with_trailer = [&](const Headers *trailer) {
7573
0
    if (!ok) { return; }
7574
7575
0
    data_available = false;
7576
7577
0
    std::string payload;
7578
0
    if (!compressor.compress(nullptr, 0, true,
7579
0
                             [&](const char *data, size_t data_len) {
7580
0
                               payload.append(data, data_len);
7581
0
                               return true;
7582
0
                             })) {
7583
0
      ok = false;
7584
0
      return;
7585
0
    }
7586
7587
0
    if (!payload.empty()) {
7588
      // Emit chunked response header and footer for each chunk
7589
0
      auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
7590
0
      if (!write_data(strm, chunk.data(), chunk.size())) {
7591
0
        ok = false;
7592
0
        return;
7593
0
      }
7594
0
    }
7595
7596
0
    constexpr const char done_marker[] = "0\r\n";
7597
0
    if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
7598
7599
    // Trailer
7600
0
    if (trailer) {
7601
0
      for (const auto &kv : *trailer) {
7602
0
        std::string field_line = kv.first + ": " + kv.second + "\r\n";
7603
0
        if (!write_data(strm, field_line.data(), field_line.size())) {
7604
0
          ok = false;
7605
0
        }
7606
0
      }
7607
0
    }
7608
7609
0
    constexpr const char crlf[] = "\r\n";
7610
0
    if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
7611
0
  };
7612
7613
0
  data_sink.done = [&](void) { done_with_trailer(nullptr); };
7614
7615
0
  data_sink.done_with_trailer = [&](const Headers &trailer) {
7616
0
    done_with_trailer(&trailer);
7617
0
  };
7618
7619
0
  while (data_available && !is_shutting_down()) {
7620
0
    if (!strm.wait_writable() || !strm.is_peer_alive()) {
7621
0
      error = Error::Write;
7622
0
      return false;
7623
0
    } else if (!content_provider(offset, 0, data_sink)) {
7624
0
      error = Error::Canceled;
7625
0
      return false;
7626
0
    } else if (!ok) {
7627
0
      error = Error::Write;
7628
0
      return false;
7629
0
    }
7630
0
  }
7631
7632
0
  if (data_available) { // exited due to is_shutting_down(), not done()
7633
0
    error = Error::Write;
7634
0
    return false;
7635
0
  }
7636
7637
0
  error = Error::Success;
7638
0
  return true;
7639
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&)
7640
7641
template <typename T, typename U>
7642
inline bool write_content_chunked(Stream &strm,
7643
                                  const ContentProvider &content_provider,
7644
0
                                  const T &is_shutting_down, U &compressor) {
7645
0
  auto error = Error::Success;
7646
0
  return write_content_chunked(strm, content_provider, is_shutting_down,
7647
0
                               compressor, error);
7648
0
}
7649
7650
template <typename T>
7651
inline bool redirect(T &cli, Request &req, Response &res,
7652
                     const std::string &path, const std::string &location,
7653
0
                     Error &error) {
7654
0
  Request new_req = req;
7655
0
  new_req.path = path;
7656
0
  new_req.redirect_count_ -= 1;
7657
0
7658
0
  if (res.status == StatusCode::SeeOther_303 &&
7659
0
      (req.method != "GET" && req.method != "HEAD")) {
7660
0
    new_req.method = "GET";
7661
0
    new_req.body.clear();
7662
0
    new_req.headers.clear();
7663
0
  }
7664
0
7665
0
  Response new_res;
7666
0
7667
0
  auto ret = cli.send(new_req, new_res, error);
7668
0
  if (ret) {
7669
0
    req = std::move(new_req);
7670
0
    res = std::move(new_res);
7671
0
7672
0
    if (res.location.empty()) { res.location = location; }
7673
0
  }
7674
0
  return ret;
7675
0
}
7676
7677
0
inline std::string params_to_query_str(const Params &params) {
7678
0
  std::string query;
7679
0
7680
0
  for (auto it = params.begin(); it != params.end(); ++it) {
7681
0
    if (it != params.begin()) { query += '&'; }
7682
0
    query += encode_query_component(it->first);
7683
0
    query += '=';
7684
0
    query += encode_query_component(it->second);
7685
0
  }
7686
0
  return query;
7687
0
}
7688
7689
inline void parse_query_text(const char *data, std::size_t size,
7690
652
                             Params &params) {
7691
652
  std::set<std::string> cache;
7692
52.2k
  split(data, data + size, '&', [&](const char *b, const char *e) {
7693
52.2k
    std::string kv(b, e);
7694
52.2k
    if (cache.find(kv) != cache.end()) { return; }
7695
17.4k
    cache.insert(std::move(kv));
7696
7697
17.4k
    std::string key;
7698
17.4k
    std::string val;
7699
17.4k
    divide(b, static_cast<std::size_t>(e - b), '=',
7700
17.4k
           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
7701
17.4k
               std::size_t rhs_size) {
7702
17.4k
             key.assign(lhs_data, lhs_size);
7703
17.4k
             val.assign(rhs_data, rhs_size);
7704
17.4k
           });
7705
7706
17.4k
    if (!key.empty()) {
7707
16.8k
      params.emplace(decode_query_component(key), decode_query_component(val));
7708
16.8k
    }
7709
17.4k
  });
7710
652
}
7711
7712
0
inline void parse_query_text(const std::string &s, Params &params) {
7713
0
  parse_query_text(s.data(), s.size(), params);
7714
0
}
7715
7716
// Normalize a query string by decoding and re-encoding each key/value pair
7717
// while preserving the original parameter order. This avoids double-encoding
7718
// and ensures consistent encoding without reordering (unlike Params which
7719
// uses std::multimap and sorts keys).
7720
262
inline std::string normalize_query_string(const std::string &query) {
7721
262
  std::string result;
7722
262
  split(query.data(), query.data() + query.size(), '&',
7723
22.8k
        [&](const char *b, const char *e) {
7724
22.8k
          std::string key;
7725
22.8k
          std::string val;
7726
22.8k
          divide(b, static_cast<std::size_t>(e - b), '=',
7727
22.8k
                 [&](const char *lhs_data, std::size_t lhs_size,
7728
22.8k
                     const char *rhs_data, std::size_t rhs_size) {
7729
22.8k
                   key.assign(lhs_data, lhs_size);
7730
22.8k
                   val.assign(rhs_data, rhs_size);
7731
22.8k
                 });
7732
7733
22.8k
          if (!key.empty()) {
7734
22.2k
            auto dec_key = decode_query_component(key);
7735
22.2k
            auto dec_val = decode_query_component(val);
7736
7737
22.2k
            if (!result.empty()) { result += '&'; }
7738
22.2k
            result += encode_query_component(dec_key);
7739
22.2k
            if (!val.empty() || std::find(b, e, '=') != e) {
7740
1.21k
              result += '=';
7741
1.21k
              result += encode_query_component(dec_val);
7742
1.21k
            }
7743
22.2k
          }
7744
22.8k
        });
7745
262
  return result;
7746
262
}
7747
7748
inline bool parse_multipart_boundary(const std::string &content_type,
7749
0
                                     std::string &boundary) {
7750
0
  std::map<std::string, std::string> params;
7751
0
  extract_media_type(content_type, &params);
7752
0
  auto it = params.find("boundary");
7753
0
  if (it == params.end()) { return false; }
7754
0
  boundary = it->second;
7755
0
  return !boundary.empty();
7756
0
}
7757
7758
0
inline void parse_disposition_params(const std::string &s, Params &params) {
7759
0
  std::set<std::string> cache;
7760
0
  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
7761
0
    std::string kv(b, e);
7762
0
    if (cache.find(kv) != cache.end()) { return; }
7763
0
    cache.insert(kv);
7764
7765
0
    std::string key;
7766
0
    std::string val;
7767
0
    split(b, e, '=', [&](const char *b2, const char *e2) {
7768
0
      if (key.empty()) {
7769
0
        key.assign(b2, e2);
7770
0
      } else {
7771
0
        val.assign(b2, e2);
7772
0
      }
7773
0
    });
7774
7775
0
    if (!key.empty()) {
7776
0
      params.emplace(trim_double_quotes_copy((key)),
7777
0
                     trim_double_quotes_copy((val)));
7778
0
    }
7779
0
  });
7780
0
}
7781
7782
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7783
inline bool parse_range_header(const std::string &s, Ranges &ranges) {
7784
#else
7785
0
inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
7786
0
#endif
7787
0
  auto is_valid = [](const std::string &str) {
7788
0
    return std::all_of(str.cbegin(), str.cend(),
7789
0
                       [](unsigned char c) { return std::isdigit(c); });
7790
0
  };
7791
7792
0
  if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
7793
0
    const auto pos = static_cast<size_t>(6);
7794
0
    const auto len = static_cast<size_t>(s.size() - 6);
7795
0
    auto all_valid_ranges = true;
7796
0
    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
7797
0
      if (!all_valid_ranges) { return; }
7798
7799
0
      const auto it = std::find(b, e, '-');
7800
0
      if (it == e) {
7801
0
        all_valid_ranges = false;
7802
0
        return;
7803
0
      }
7804
7805
0
      const auto lhs = std::string(b, it);
7806
0
      const auto rhs = std::string(it + 1, e);
7807
0
      if (!is_valid(lhs) || !is_valid(rhs)) {
7808
0
        all_valid_ranges = false;
7809
0
        return;
7810
0
      }
7811
7812
0
      ssize_t first = -1;
7813
0
      if (!lhs.empty()) {
7814
0
        ssize_t v;
7815
0
        auto res = detail::from_chars(lhs.data(), lhs.data() + lhs.size(), v);
7816
0
        if (res.ec == std::errc{}) { first = v; }
7817
0
      }
7818
7819
0
      ssize_t last = -1;
7820
0
      if (!rhs.empty()) {
7821
0
        ssize_t v;
7822
0
        auto res = detail::from_chars(rhs.data(), rhs.data() + rhs.size(), v);
7823
0
        if (res.ec == std::errc{}) { last = v; }
7824
0
      }
7825
7826
0
      if ((first == -1 && last == -1) ||
7827
0
          (first != -1 && last != -1 && first > last)) {
7828
0
        all_valid_ranges = false;
7829
0
        return;
7830
0
      }
7831
7832
0
      ranges.emplace_back(first, last);
7833
0
    });
7834
0
    return all_valid_ranges && !ranges.empty();
7835
0
  }
7836
0
  return false;
7837
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
7838
}
7839
#else
7840
0
} catch (...) { return false; }
7841
#endif
7842
7843
inline bool parse_accept_header(const std::string &s,
7844
0
                                std::vector<std::string> &content_types) {
7845
0
  content_types.clear();
7846
7847
  // Empty string is considered valid (no preference)
7848
0
  if (s.empty()) { return true; }
7849
7850
  // Check for invalid patterns: leading/trailing commas or consecutive commas
7851
0
  if (s.front() == ',' || s.back() == ',' ||
7852
0
      s.find(",,") != std::string::npos) {
7853
0
    return false;
7854
0
  }
7855
7856
0
  struct AcceptEntry {
7857
0
    std::string media_type;
7858
0
    double quality;
7859
0
    int order;
7860
0
  };
7861
7862
0
  std::vector<AcceptEntry> entries;
7863
0
  int order = 0;
7864
0
  bool has_invalid_entry = false;
7865
7866
  // Split by comma and parse each entry
7867
0
  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {
7868
0
    std::string entry(b, e);
7869
0
    entry = trim_copy(entry);
7870
7871
0
    if (entry.empty()) {
7872
0
      has_invalid_entry = true;
7873
0
      return;
7874
0
    }
7875
7876
0
    AcceptEntry accept_entry;
7877
0
    accept_entry.order = order++;
7878
7879
0
    if (!parse_quality(entry.data(), entry.data() + entry.size(),
7880
0
                       accept_entry.media_type, accept_entry.quality)) {
7881
0
      has_invalid_entry = true;
7882
0
      return;
7883
0
    }
7884
7885
    // Remove additional parameters from media type
7886
0
    accept_entry.media_type = extract_media_type(accept_entry.media_type);
7887
7888
    // Basic validation of media type format
7889
0
    if (accept_entry.media_type.empty()) {
7890
0
      has_invalid_entry = true;
7891
0
      return;
7892
0
    }
7893
7894
    // Check for basic media type format (should contain '/' or be '*')
7895
0
    if (accept_entry.media_type != "*" &&
7896
0
        accept_entry.media_type.find('/') == std::string::npos) {
7897
0
      has_invalid_entry = true;
7898
0
      return;
7899
0
    }
7900
7901
0
    entries.push_back(std::move(accept_entry));
7902
0
  });
7903
7904
  // Return false if any invalid entry was found
7905
0
  if (has_invalid_entry) { return false; }
7906
7907
  // Sort by quality (descending), then by original order (ascending)
7908
0
  std::sort(entries.begin(), entries.end(),
7909
0
            [](const AcceptEntry &a, const AcceptEntry &b) {
7910
0
              if (a.quality != b.quality) {
7911
0
                return a.quality > b.quality; // Higher quality first
7912
0
              }
7913
0
              return a.order < b.order; // Earlier order first for same quality
7914
0
            });
7915
7916
  // Extract sorted media types
7917
0
  content_types.reserve(entries.size());
7918
0
  for (auto &entry : entries) {
7919
0
    content_types.push_back(std::move(entry.media_type));
7920
0
  }
7921
7922
0
  return true;
7923
0
}
7924
7925
class FormDataParser {
7926
public:
7927
0
  FormDataParser() = default;
7928
7929
0
  void set_boundary(std::string &&boundary) {
7930
0
    boundary_ = std::move(boundary);
7931
0
    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
7932
0
    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
7933
0
  }
7934
7935
0
  bool is_valid() const { return is_valid_; }
7936
7937
  bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,
7938
0
             const ContentReceiver &content_callback) {
7939
7940
0
    buf_append(buf, n);
7941
7942
0
    while (buf_size() > 0) {
7943
0
      switch (state_) {
7944
0
      case 0: { // Initial boundary
7945
0
        auto pos = buf_find(dash_boundary_crlf_);
7946
0
        if (pos == buf_size()) { return true; }
7947
0
        buf_erase(pos + dash_boundary_crlf_.size());
7948
0
        state_ = 1;
7949
0
        break;
7950
0
      }
7951
0
      case 1: { // New entry
7952
0
        clear_file_info();
7953
0
        state_ = 2;
7954
0
        break;
7955
0
      }
7956
0
      case 2: { // Headers
7957
0
        auto pos = buf_find(crlf_);
7958
0
        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
7959
0
        while (pos < buf_size()) {
7960
          // Empty line
7961
0
          if (pos == 0) {
7962
0
            if (!header_callback(file_)) {
7963
0
              is_valid_ = false;
7964
0
              return false;
7965
0
            }
7966
0
            buf_erase(crlf_.size());
7967
0
            state_ = 3;
7968
0
            break;
7969
0
          }
7970
7971
0
          const auto header = buf_head(pos);
7972
7973
0
          if (!parse_header(header.data(), header.data() + header.size(),
7974
0
                            [&](const std::string &, const std::string &) {})) {
7975
0
            is_valid_ = false;
7976
0
            return false;
7977
0
          }
7978
7979
          // Parse and emplace space trimmed headers into a map
7980
0
          if (!parse_header(
7981
0
                  header.data(), header.data() + header.size(),
7982
0
                  [&](const std::string &key, const std::string &val) {
7983
0
                    file_.headers.emplace(key, val);
7984
0
                  })) {
7985
0
            is_valid_ = false;
7986
0
            return false;
7987
0
          }
7988
7989
0
          constexpr const char header_content_type[] = "Content-Type:";
7990
7991
0
          if (start_with_case_ignore(header, header_content_type)) {
7992
0
            file_.content_type =
7993
0
                trim_copy(header.substr(str_len(header_content_type)));
7994
0
          } else {
7995
0
            std::string disposition_params;
7996
0
            if (parse_content_disposition(header, disposition_params)) {
7997
0
              Params params;
7998
0
              parse_disposition_params(disposition_params, params);
7999
8000
0
              auto it = params.find("name");
8001
0
              if (it != params.end()) {
8002
0
                file_.name = it->second;
8003
0
              } else {
8004
0
                is_valid_ = false;
8005
0
                return false;
8006
0
              }
8007
8008
0
              it = params.find("filename");
8009
0
              if (it != params.end()) { file_.filename = it->second; }
8010
8011
0
              it = params.find("filename*");
8012
0
              if (it != params.end()) {
8013
                // RFC 5987: only UTF-8 encoding is allowed
8014
0
                const auto &val = it->second;
8015
0
                constexpr const char utf8_prefix[] = "UTF-8''";
8016
0
                constexpr size_t prefix_len = str_len(utf8_prefix);
8017
0
                if (val.size() > prefix_len &&
8018
0
                    start_with_case_ignore(val, utf8_prefix)) {
8019
0
                  file_.filename = decode_path_component(
8020
0
                      val.substr(prefix_len)); // override...
8021
0
                } else {
8022
0
                  is_valid_ = false;
8023
0
                  return false;
8024
0
                }
8025
0
              }
8026
0
            }
8027
0
          }
8028
0
          buf_erase(pos + crlf_.size());
8029
0
          pos = buf_find(crlf_);
8030
0
        }
8031
0
        if (state_ != 3) { return true; }
8032
0
        break;
8033
0
      }
8034
0
      case 3: { // Body
8035
0
        if (crlf_dash_boundary_.size() > buf_size()) { return true; }
8036
0
        auto pos = buf_find(crlf_dash_boundary_);
8037
0
        if (pos < buf_size()) {
8038
0
          if (!content_callback(buf_data(), pos)) {
8039
0
            is_valid_ = false;
8040
0
            return false;
8041
0
          }
8042
0
          buf_erase(pos + crlf_dash_boundary_.size());
8043
0
          state_ = 4;
8044
0
        } else {
8045
0
          auto len = buf_size() - crlf_dash_boundary_.size();
8046
0
          if (len > 0) {
8047
0
            if (!content_callback(buf_data(), len)) {
8048
0
              is_valid_ = false;
8049
0
              return false;
8050
0
            }
8051
0
            buf_erase(len);
8052
0
          }
8053
0
          return true;
8054
0
        }
8055
0
        break;
8056
0
      }
8057
0
      case 4: { // Boundary
8058
0
        if (crlf_.size() > buf_size()) { return true; }
8059
0
        if (buf_start_with(crlf_)) {
8060
0
          buf_erase(crlf_.size());
8061
0
          state_ = 1;
8062
0
        } else {
8063
0
          if (dash_.size() > buf_size()) { return true; }
8064
0
          if (buf_start_with(dash_)) {
8065
0
            buf_erase(dash_.size());
8066
0
            is_valid_ = true;
8067
0
            buf_erase(buf_size()); // Remove epilogue
8068
0
          } else {
8069
0
            return true;
8070
0
          }
8071
0
        }
8072
0
        break;
8073
0
      }
8074
0
      }
8075
0
    }
8076
8077
0
    return true;
8078
0
  }
8079
8080
private:
8081
0
  void clear_file_info() {
8082
0
    file_.name.clear();
8083
0
    file_.filename.clear();
8084
0
    file_.content_type.clear();
8085
0
    file_.headers.clear();
8086
0
  }
8087
8088
  bool start_with_case_ignore(const std::string &a, const char *b,
8089
0
                              size_t offset = 0) const {
8090
0
    const auto b_len = strlen(b);
8091
0
    if (a.size() < offset + b_len) { return false; }
8092
0
    for (size_t i = 0; i < b_len; i++) {
8093
0
      if (case_ignore::to_lower(a[offset + i]) != case_ignore::to_lower(b[i])) {
8094
0
        return false;
8095
0
      }
8096
0
    }
8097
0
    return true;
8098
0
  }
8099
8100
  // Parses "Content-Disposition: form-data; <params>" without std::regex.
8101
  // Returns true if header matches, with the params portion in `params_out`.
8102
  bool parse_content_disposition(const std::string &header,
8103
0
                                 std::string &params_out) const {
8104
0
    constexpr const char prefix[] = "Content-Disposition:";
8105
0
    constexpr size_t prefix_len = str_len(prefix);
8106
8107
0
    if (!start_with_case_ignore(header, prefix)) { return false; }
8108
8109
    // Skip whitespace after "Content-Disposition:"
8110
0
    auto pos = prefix_len;
8111
0
    while (pos < header.size() && (header[pos] == ' ' || header[pos] == '\t')) {
8112
0
      pos++;
8113
0
    }
8114
8115
    // Match "form-data;" (case-insensitive)
8116
0
    constexpr const char form_data[] = "form-data;";
8117
0
    constexpr size_t form_data_len = str_len(form_data);
8118
0
    if (!start_with_case_ignore(header, form_data, pos)) { return false; }
8119
0
    pos += form_data_len;
8120
8121
    // Skip whitespace after "form-data;"
8122
0
    while (pos < header.size() && (header[pos] == ' ' || header[pos] == '\t')) {
8123
0
      pos++;
8124
0
    }
8125
8126
0
    params_out = header.substr(pos);
8127
0
    return true;
8128
0
  }
8129
8130
  const std::string dash_ = "--";
8131
  const std::string crlf_ = "\r\n";
8132
  std::string boundary_;
8133
  std::string dash_boundary_crlf_;
8134
  std::string crlf_dash_boundary_;
8135
8136
  size_t state_ = 0;
8137
  bool is_valid_ = false;
8138
  FormData file_;
8139
8140
  // Buffer
8141
  bool start_with(const std::string &a, size_t spos, size_t epos,
8142
0
                  const std::string &b) const {
8143
0
    if (epos - spos < b.size()) { return false; }
8144
0
    for (size_t i = 0; i < b.size(); i++) {
8145
0
      if (a[i + spos] != b[i]) { return false; }
8146
0
    }
8147
0
    return true;
8148
0
  }
8149
8150
0
  size_t buf_size() const { return buf_epos_ - buf_spos_; }
8151
8152
0
  const char *buf_data() const { return &buf_[buf_spos_]; }
8153
8154
0
  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
8155
8156
0
  bool buf_start_with(const std::string &s) const {
8157
0
    return start_with(buf_, buf_spos_, buf_epos_, s);
8158
0
  }
8159
8160
0
  size_t buf_find(const std::string &s) const {
8161
0
    auto c = s.front();
8162
8163
0
    size_t off = buf_spos_;
8164
0
    while (off < buf_epos_) {
8165
0
      auto pos = off;
8166
0
      while (true) {
8167
0
        if (pos == buf_epos_) { return buf_size(); }
8168
0
        if (buf_[pos] == c) { break; }
8169
0
        pos++;
8170
0
      }
8171
8172
0
      auto remaining_size = buf_epos_ - pos;
8173
0
      if (s.size() > remaining_size) { return buf_size(); }
8174
8175
0
      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
8176
8177
0
      off = pos + 1;
8178
0
    }
8179
8180
0
    return buf_size();
8181
0
  }
8182
8183
0
  void buf_append(const char *data, size_t n) {
8184
0
    auto remaining_size = buf_size();
8185
0
    if (remaining_size > 0 && buf_spos_ > 0) {
8186
0
      for (size_t i = 0; i < remaining_size; i++) {
8187
0
        buf_[i] = buf_[buf_spos_ + i];
8188
0
      }
8189
0
    }
8190
0
    buf_spos_ = 0;
8191
0
    buf_epos_ = remaining_size;
8192
8193
0
    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
8194
8195
0
    for (size_t i = 0; i < n; i++) {
8196
0
      buf_[buf_epos_ + i] = data[i];
8197
0
    }
8198
0
    buf_epos_ += n;
8199
0
  }
8200
8201
0
  void buf_erase(size_t size) { buf_spos_ += size; }
8202
8203
  std::string buf_;
8204
  size_t buf_spos_ = 0;
8205
  size_t buf_epos_ = 0;
8206
};
8207
8208
0
inline std::string random_string(size_t length) {
8209
0
  constexpr const char data[] =
8210
0
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8211
8212
0
  thread_local auto engine([]() {
8213
    // std::random_device might actually be deterministic on some
8214
    // platforms, but due to lack of support in the c++ standard library,
8215
    // doing better requires either some ugly hacks or breaking portability.
8216
0
    std::random_device seed_gen;
8217
    // Request 128 bits of entropy for initialization
8218
0
    std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
8219
0
    return std::mt19937(seed_sequence);
8220
0
  }());
8221
8222
0
  std::string result;
8223
0
  for (size_t i = 0; i < length; i++) {
8224
0
    result += data[engine() % (sizeof(data) - 1)];
8225
0
  }
8226
0
  return result;
8227
0
}
8228
8229
0
inline std::string make_multipart_data_boundary() {
8230
0
  return "--cpp-httplib-multipart-data-" + detail::random_string(16);
8231
0
}
8232
8233
0
inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
8234
0
  auto valid = true;
8235
0
  for (size_t i = 0; i < boundary.size(); i++) {
8236
0
    auto c = boundary[i];
8237
0
    if (!std::isalnum(c) && c != '-' && c != '_') {
8238
0
      valid = false;
8239
0
      break;
8240
0
    }
8241
0
  }
8242
0
  return valid;
8243
0
}
8244
8245
template <typename T>
8246
inline std::string
8247
serialize_multipart_formdata_item_begin(const T &item,
8248
0
                                        const std::string &boundary) {
8249
0
  std::string body = "--" + boundary + "\r\n";
8250
0
  body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
8251
0
  if (!item.filename.empty()) {
8252
0
    body += "; filename=\"" + item.filename + "\"";
8253
0
  }
8254
0
  body += "\r\n";
8255
0
  if (!item.content_type.empty()) {
8256
0
    body += "Content-Type: " + item.content_type + "\r\n";
8257
0
  }
8258
0
  body += "\r\n";
8259
0
8260
0
  return body;
8261
0
}
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > httplib::detail::serialize_multipart_formdata_item_begin<httplib::UploadFormData>(httplib::UploadFormData const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > httplib::detail::serialize_multipart_formdata_item_begin<httplib::FormDataProvider>(httplib::FormDataProvider const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
8262
8263
0
inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
8264
8265
inline std::string
8266
0
serialize_multipart_formdata_finish(const std::string &boundary) {
8267
0
  return "--" + boundary + "--\r\n";
8268
0
}
8269
8270
inline std::string
8271
0
serialize_multipart_formdata_get_content_type(const std::string &boundary) {
8272
0
  return "multipart/form-data; boundary=" + boundary;
8273
0
}
8274
8275
inline std::string
8276
serialize_multipart_formdata(const UploadFormDataItems &items,
8277
0
                             const std::string &boundary, bool finish = true) {
8278
0
  std::string body;
8279
0
8280
0
  for (const auto &item : items) {
8281
0
    body += serialize_multipart_formdata_item_begin(item, boundary);
8282
0
    body += item.content + serialize_multipart_formdata_item_end();
8283
0
  }
8284
0
8285
0
  if (finish) { body += serialize_multipart_formdata_finish(boundary); }
8286
0
8287
0
  return body;
8288
0
}
8289
8290
inline size_t get_multipart_content_length(const UploadFormDataItems &items,
8291
0
                                           const std::string &boundary) {
8292
0
  size_t total = 0;
8293
0
  for (const auto &item : items) {
8294
0
    total += serialize_multipart_formdata_item_begin(item, boundary).size();
8295
0
    total += item.content.size();
8296
0
    total += serialize_multipart_formdata_item_end().size();
8297
0
  }
8298
0
  total += serialize_multipart_formdata_finish(boundary).size();
8299
0
  return total;
8300
0
}
8301
8302
struct MultipartSegment {
8303
  const char *data;
8304
  size_t size;
8305
};
8306
8307
// NOTE: items must outlive the returned ContentProvider
8308
//       (safe for synchronous use inside Post/Put/Patch)
8309
inline ContentProvider
8310
make_multipart_content_provider(const UploadFormDataItems &items,
8311
0
                                const std::string &boundary) {
8312
0
  // Own the per-item header strings and the finish string
8313
0
  std::vector<std::string> owned;
8314
0
  owned.reserve(items.size() + 1);
8315
0
  for (const auto &item : items)
8316
0
    owned.push_back(serialize_multipart_formdata_item_begin(item, boundary));
8317
0
  owned.push_back(serialize_multipart_formdata_finish(boundary));
8318
0
8319
0
  // Flat segment list: [header, content, "\r\n"] * N + [finish]
8320
0
  std::vector<MultipartSegment> segs;
8321
0
  segs.reserve(items.size() * 3 + 1);
8322
0
  static const char crlf[] = "\r\n";
8323
0
  for (size_t i = 0; i < items.size(); i++) {
8324
0
    segs.push_back({owned[i].data(), owned[i].size()});
8325
0
    segs.push_back({items[i].content.data(), items[i].content.size()});
8326
0
    segs.push_back({crlf, 2});
8327
0
  }
8328
0
  segs.push_back({owned.back().data(), owned.back().size()});
8329
0
8330
0
  struct MultipartState {
8331
0
    std::vector<std::string> owned;
8332
0
    std::vector<MultipartSegment> segs;
8333
0
    std::vector<char> buf = std::vector<char>(CPPHTTPLIB_SEND_BUFSIZ);
8334
0
  };
8335
0
  auto state = std::make_shared<MultipartState>();
8336
0
  state->owned = std::move(owned);
8337
0
  // `segs` holds raw pointers into owned strings; std::string move preserves
8338
0
  // the data pointer, so these pointers remain valid after the move above.
8339
0
  state->segs = std::move(segs);
8340
0
8341
0
  return [state](size_t offset, size_t length, DataSink &sink) -> bool {
8342
0
    // Buffer multiple small segments into fewer, larger writes to avoid
8343
0
    // excessive TCP packets when there are many form data items (#2410)
8344
0
    auto &buf = state->buf;
8345
0
    auto buf_size = buf.size();
8346
0
    size_t buf_len = 0;
8347
0
    size_t remaining = length;
8348
0
8349
0
    // Find the first segment containing 'offset'
8350
0
    size_t pos = 0;
8351
0
    size_t seg_idx = 0;
8352
0
    for (; seg_idx < state->segs.size(); seg_idx++) {
8353
0
      const auto &seg = state->segs[seg_idx];
8354
0
      if (seg.size > 0 && offset - pos < seg.size) { break; }
8355
0
      pos += seg.size;
8356
0
    }
8357
0
8358
0
    size_t seg_offset = (seg_idx < state->segs.size()) ? offset - pos : 0;
8359
0
8360
0
    for (; seg_idx < state->segs.size() && remaining > 0; seg_idx++) {
8361
0
      const auto &seg = state->segs[seg_idx];
8362
0
      size_t available = seg.size - seg_offset;
8363
0
      size_t to_copy = (std::min)(available, remaining);
8364
0
      const char *src = seg.data + seg_offset;
8365
0
      seg_offset = 0; // only the first segment has a non-zero offset
8366
0
8367
0
      while (to_copy > 0) {
8368
0
        size_t space = buf_size - buf_len;
8369
0
        size_t chunk = (std::min)(to_copy, space);
8370
0
        std::memcpy(buf.data() + buf_len, src, chunk);
8371
0
        buf_len += chunk;
8372
0
        src += chunk;
8373
0
        to_copy -= chunk;
8374
0
        remaining -= chunk;
8375
0
8376
0
        if (buf_len == buf_size) {
8377
0
          if (!sink.write(buf.data(), buf_len)) { return false; }
8378
0
          buf_len = 0;
8379
0
        }
8380
0
      }
8381
0
    }
8382
0
8383
0
    if (buf_len > 0) { return sink.write(buf.data(), buf_len); }
8384
0
    return true;
8385
0
  };
8386
0
}
8387
8388
0
inline void coalesce_ranges(Ranges &ranges, size_t content_length) {
8389
0
  if (ranges.size() <= 1) return;
8390
8391
  // Sort ranges by start position
8392
0
  std::sort(ranges.begin(), ranges.end(),
8393
0
            [](const Range &a, const Range &b) { return a.first < b.first; });
8394
8395
0
  Ranges coalesced;
8396
0
  coalesced.reserve(ranges.size());
8397
8398
0
  for (auto &r : ranges) {
8399
0
    auto first_pos = r.first;
8400
0
    auto last_pos = r.second;
8401
8402
    // Handle special cases like in range_error
8403
0
    if (first_pos == -1 && last_pos == -1) {
8404
0
      first_pos = 0;
8405
0
      last_pos = static_cast<ssize_t>(content_length);
8406
0
    }
8407
8408
0
    if (first_pos == -1) {
8409
0
      first_pos = static_cast<ssize_t>(content_length) - last_pos;
8410
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
8411
0
    }
8412
8413
0
    if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {
8414
0
      last_pos = static_cast<ssize_t>(content_length) - 1;
8415
0
    }
8416
8417
    // Skip invalid ranges
8418
0
    if (!(0 <= first_pos && first_pos <= last_pos &&
8419
0
          last_pos < static_cast<ssize_t>(content_length))) {
8420
0
      continue;
8421
0
    }
8422
8423
    // Coalesce with previous range if overlapping or adjacent (but not
8424
    // identical)
8425
0
    if (!coalesced.empty()) {
8426
0
      auto &prev = coalesced.back();
8427
      // Check if current range overlaps or is adjacent to previous range
8428
      // but don't coalesce identical ranges (allow duplicates)
8429
0
      if (first_pos <= prev.second + 1 &&
8430
0
          !(first_pos == prev.first && last_pos == prev.second)) {
8431
        // Extend the previous range
8432
0
        prev.second = (std::max)(prev.second, last_pos);
8433
0
        continue;
8434
0
      }
8435
0
    }
8436
8437
    // Add new range
8438
0
    coalesced.emplace_back(first_pos, last_pos);
8439
0
  }
8440
8441
0
  ranges = std::move(coalesced);
8442
0
}
8443
8444
0
inline bool range_error(Request &req, Response &res) {
8445
0
  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
8446
0
    ssize_t content_len = static_cast<ssize_t>(
8447
0
        res.content_length_ ? res.content_length_ : res.body.size());
8448
8449
0
    std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;
8450
0
    size_t overwrapping_count = 0;
8451
8452
    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
8453
    // 'HTTP Semantics' to avoid potential denial-of-service attacks.
8454
    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
8455
8456
    // Too many ranges
8457
0
    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }
8458
8459
0
    for (auto &r : req.ranges) {
8460
0
      auto &first_pos = r.first;
8461
0
      auto &last_pos = r.second;
8462
8463
0
      if (first_pos == -1 && last_pos == -1) {
8464
0
        first_pos = 0;
8465
0
        last_pos = content_len;
8466
0
      }
8467
8468
0
      if (first_pos == -1) {
8469
0
        first_pos = content_len - last_pos;
8470
0
        last_pos = content_len - 1;
8471
0
      }
8472
8473
      // NOTE: RFC-9110 '14.1.2. Byte Ranges':
8474
      // A client can limit the number of bytes requested without knowing the
8475
      // size of the selected representation. If the last-pos value is absent,
8476
      // or if the value is greater than or equal to the current length of the
8477
      // representation data, the byte range is interpreted as the remainder of
8478
      // the representation (i.e., the server replaces the value of last-pos
8479
      // with a value that is one less than the current length of the selected
8480
      // representation).
8481
      // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
8482
0
      if (last_pos == -1 || last_pos >= content_len) {
8483
0
        last_pos = content_len - 1;
8484
0
      }
8485
8486
      // Range must be within content length
8487
0
      if (!(0 <= first_pos && first_pos <= last_pos &&
8488
0
            last_pos <= content_len - 1)) {
8489
0
        return true;
8490
0
      }
8491
8492
      // Request must not have more than two overlapping ranges
8493
0
      for (const auto &processed_range : processed_ranges) {
8494
0
        if (!(last_pos < processed_range.first ||
8495
0
              first_pos > processed_range.second)) {
8496
0
          overwrapping_count++;
8497
0
          if (overwrapping_count > 2) { return true; }
8498
0
          break; // Only count once per range
8499
0
        }
8500
0
      }
8501
8502
0
      processed_ranges.emplace_back(first_pos, last_pos);
8503
0
    }
8504
8505
    // After validation, coalesce overlapping ranges as per RFC 9110
8506
0
    coalesce_ranges(req.ranges, static_cast<size_t>(content_len));
8507
0
  }
8508
8509
0
  return false;
8510
0
}
8511
8512
inline std::pair<size_t, size_t>
8513
0
get_range_offset_and_length(Range r, size_t content_length) {
8514
0
  assert(r.first != -1 && r.second != -1);
8515
0
  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
8516
0
  assert(r.first <= r.second &&
8517
0
         r.second < static_cast<ssize_t>(content_length));
8518
0
  (void)(content_length);
8519
0
  return std::make_pair(static_cast<size_t>(r.first),
8520
0
                        static_cast<size_t>(r.second - r.first) + 1);
8521
0
}
8522
8523
inline std::string make_content_range_header_field(
8524
0
    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {
8525
0
  auto st = offset_and_length.first;
8526
0
  auto ed = st + offset_and_length.second - 1;
8527
8528
0
  std::string field = "bytes ";
8529
0
  field += std::to_string(st);
8530
0
  field += '-';
8531
0
  field += std::to_string(ed);
8532
0
  field += '/';
8533
0
  field += std::to_string(content_length);
8534
0
  return field;
8535
0
}
8536
8537
template <typename SToken, typename CToken, typename Content>
8538
bool process_multipart_ranges_data(const Request &req,
8539
                                   const std::string &boundary,
8540
                                   const std::string &content_type,
8541
                                   size_t content_length, SToken stoken,
8542
0
                                   CToken ctoken, Content content) {
8543
0
  for (size_t i = 0; i < req.ranges.size(); i++) {
8544
0
    ctoken("--");
8545
0
    stoken(boundary);
8546
0
    ctoken("\r\n");
8547
0
    if (!content_type.empty()) {
8548
0
      ctoken("Content-Type: ");
8549
0
      stoken(content_type);
8550
0
      ctoken("\r\n");
8551
0
    }
8552
8553
0
    auto offset_and_length =
8554
0
        get_range_offset_and_length(req.ranges[i], content_length);
8555
8556
0
    ctoken("Content-Range: ");
8557
0
    stoken(make_content_range_header_field(offset_and_length, content_length));
8558
0
    ctoken("\r\n");
8559
0
    ctoken("\r\n");
8560
8561
0
    if (!content(offset_and_length.first, offset_and_length.second)) {
8562
0
      return false;
8563
0
    }
8564
0
    ctoken("\r\n");
8565
0
  }
8566
8567
0
  ctoken("--");
8568
0
  stoken(boundary);
8569
0
  ctoken("--");
8570
8571
0
  return true;
8572
0
}
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})
Unexecuted instantiation: 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})
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})
8573
8574
inline void make_multipart_ranges_data(const Request &req, Response &res,
8575
                                       const std::string &boundary,
8576
                                       const std::string &content_type,
8577
                                       size_t content_length,
8578
0
                                       std::string &data) {
8579
0
  process_multipart_ranges_data(
8580
0
      req, boundary, content_type, content_length,
8581
0
      [&](const std::string &token) { data += token; },
8582
0
      [&](const std::string &token) { data += token; },
8583
0
      [&](size_t offset, size_t length) {
8584
0
        assert(offset + length <= content_length);
8585
0
        data += res.body.substr(offset, length);
8586
0
        return true;
8587
0
      });
8588
0
}
8589
8590
inline size_t get_multipart_ranges_data_length(const Request &req,
8591
                                               const std::string &boundary,
8592
                                               const std::string &content_type,
8593
0
                                               size_t content_length) {
8594
0
  size_t data_length = 0;
8595
8596
0
  process_multipart_ranges_data(
8597
0
      req, boundary, content_type, content_length,
8598
0
      [&](const std::string &token) { data_length += token.size(); },
8599
0
      [&](const std::string &token) { data_length += token.size(); },
8600
0
      [&](size_t /*offset*/, size_t length) {
8601
0
        data_length += length;
8602
0
        return true;
8603
0
      });
8604
8605
0
  return data_length;
8606
0
}
8607
8608
template <typename T>
8609
inline bool
8610
write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
8611
                            const std::string &boundary,
8612
                            const std::string &content_type,
8613
0
                            size_t content_length, const T &is_shutting_down) {
8614
0
  return process_multipart_ranges_data(
8615
0
      req, boundary, content_type, content_length,
8616
0
      [&](const std::string &token) { strm.write(token); },
8617
0
      [&](const std::string &token) { strm.write(token); },
8618
0
      [&](size_t offset, size_t length) {
8619
0
        return write_content(strm, res.content_provider_, offset, length,
8620
0
                             is_shutting_down);
8621
0
      });
8622
0
}
8623
8624
0
inline bool has_framed_body(const Request &req) {
8625
0
  return is_chunked_transfer_encoding(req.headers) ||
8626
0
         req.get_header_value_u64("Content-Length") > 0;
8627
0
}
8628
8629
0
inline bool is_connection_persistent(const Request &req) {
8630
0
  auto conn = req.get_header_value("Connection");
8631
0
  if (conn == "close") { return false; }
8632
0
  if (req.version == "HTTP/1.0" && conn != "Keep-Alive") { return false; }
8633
0
  return true;
8634
0
}
8635
8636
0
inline bool expect_content(const Request &req) {
8637
0
  if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
8638
0
      req.method == "DELETE") {
8639
0
    return true;
8640
0
  }
8641
0
  return has_framed_body(req);
8642
0
}
8643
8644
#ifdef _WIN32
8645
class WSInit {
8646
public:
8647
  WSInit() {
8648
    WSADATA wsaData;
8649
    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
8650
  }
8651
8652
  ~WSInit() {
8653
    if (is_valid_) WSACleanup();
8654
  }
8655
8656
  bool is_valid_ = false;
8657
};
8658
8659
static WSInit wsinit_;
8660
#endif
8661
8662
inline bool parse_www_authenticate(const Response &res,
8663
                                   std::map<std::string, std::string> &auth,
8664
0
                                   bool is_proxy) {
8665
0
  auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
8666
0
  if (res.has_header(auth_key)) {
8667
0
    thread_local auto re =
8668
0
        std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
8669
0
    auto s = res.get_header_value(auth_key);
8670
0
    auto pos = s.find(' ');
8671
0
    if (pos != std::string::npos) {
8672
0
      auto type = s.substr(0, pos);
8673
0
      if (type == "Basic") {
8674
0
        return false;
8675
0
      } else if (type == "Digest") {
8676
0
        s = s.substr(pos + 1);
8677
0
        auto beg = std::sregex_iterator(s.begin(), s.end(), re);
8678
0
        for (auto i = beg; i != std::sregex_iterator(); ++i) {
8679
0
          const auto &m = *i;
8680
0
          auto key = s.substr(static_cast<size_t>(m.position(1)),
8681
0
                              static_cast<size_t>(m.length(1)));
8682
0
          auto val = m.length(2) > 0
8683
0
                         ? s.substr(static_cast<size_t>(m.position(2)),
8684
0
                                    static_cast<size_t>(m.length(2)))
8685
0
                         : s.substr(static_cast<size_t>(m.position(3)),
8686
0
                                    static_cast<size_t>(m.length(3)));
8687
0
          auth[std::move(key)] = std::move(val);
8688
0
        }
8689
0
        return true;
8690
0
      }
8691
0
    }
8692
0
  }
8693
0
  return false;
8694
0
}
8695
8696
class ContentProviderAdapter {
8697
public:
8698
  explicit ContentProviderAdapter(
8699
      ContentProviderWithoutLength &&content_provider)
8700
0
      : content_provider_(std::move(content_provider)) {}
8701
8702
0
  bool operator()(size_t offset, size_t, DataSink &sink) {
8703
0
    return content_provider_(offset, sink);
8704
0
  }
8705
8706
private:
8707
  ContentProviderWithoutLength content_provider_;
8708
};
8709
8710
// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
8711
namespace fields {
8712
8713
0
inline bool is_token_char(char c) {
8714
0
  return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
8715
0
         c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
8716
0
         c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
8717
0
}
8718
8719
0
inline bool is_token(const std::string &s) {
8720
0
  if (s.empty()) { return false; }
8721
0
  for (auto c : s) {
8722
0
    if (!is_token_char(c)) { return false; }
8723
0
  }
8724
0
  return true;
8725
0
}
8726
8727
0
inline bool is_field_name(const std::string &s) { return is_token(s); }
8728
8729
0
inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
8730
8731
0
inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
8732
8733
0
inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
8734
8735
0
inline bool is_field_content(const std::string &s) {
8736
0
  if (s.empty()) { return true; }
8737
8738
0
  if (s.size() == 1) {
8739
0
    return is_field_vchar(s[0]);
8740
0
  } else if (s.size() == 2) {
8741
0
    return is_field_vchar(s[0]) && is_field_vchar(s[1]);
8742
0
  } else {
8743
0
    size_t i = 0;
8744
8745
0
    if (!is_field_vchar(s[i])) { return false; }
8746
0
    i++;
8747
8748
0
    while (i < s.size() - 1) {
8749
0
      auto c = s[i++];
8750
0
      if (c == ' ' || c == '\t' || is_field_vchar(c)) {
8751
0
      } else {
8752
0
        return false;
8753
0
      }
8754
0
    }
8755
8756
0
    return is_field_vchar(s[i]);
8757
0
  }
8758
0
}
8759
8760
0
inline bool is_field_value(const std::string &s) { return is_field_content(s); }
8761
8762
} // namespace fields
8763
8764
inline bool perform_websocket_handshake(Stream &strm, const std::string &host,
8765
                                        int port, const std::string &path,
8766
                                        const Headers &headers,
8767
0
                                        std::string &selected_subprotocol) {
8768
0
  // Validate path and host
8769
0
  if (!fields::is_field_value(path) || !fields::is_field_value(host)) {
8770
0
    return false;
8771
0
  }
8772
0
8773
0
  // Validate user-provided headers
8774
0
  for (const auto &h : headers) {
8775
0
    if (!fields::is_field_name(h.first) || !fields::is_field_value(h.second)) {
8776
0
      return false;
8777
0
    }
8778
0
  }
8779
0
8780
0
  // Generate random Sec-WebSocket-Key
8781
0
  thread_local std::mt19937 rng(std::random_device{}());
8782
0
  std::string key_bytes(16, '\0');
8783
0
  for (size_t i = 0; i < 16; i += 4) {
8784
0
    auto r = rng();
8785
0
    std::memcpy(&key_bytes[i], &r, (std::min)(size_t(4), size_t(16 - i)));
8786
0
  }
8787
0
  auto client_key = base64_encode(key_bytes);
8788
0
8789
0
  // Build upgrade request
8790
0
  std::string req_str = "GET " + path + " HTTP/1.1\r\n";
8791
0
  req_str += "Host: " + host + ":" + std::to_string(port) + "\r\n";
8792
0
  req_str += "Upgrade: websocket\r\n";
8793
0
  req_str += "Connection: Upgrade\r\n";
8794
0
  req_str += "Sec-WebSocket-Key: " + client_key + "\r\n";
8795
0
  req_str += "Sec-WebSocket-Version: 13\r\n";
8796
0
  for (const auto &h : headers) {
8797
0
    req_str += h.first + ": " + h.second + "\r\n";
8798
0
  }
8799
0
  req_str += "\r\n";
8800
0
8801
0
  if (strm.write(req_str.data(), req_str.size()) < 0) { return false; }
8802
0
8803
0
  // Verify 101 response and Sec-WebSocket-Accept header
8804
0
  auto expected_accept = websocket_accept_key(client_key);
8805
0
  return read_websocket_upgrade_response(strm, expected_accept,
8806
0
                                         selected_subprotocol);
8807
0
}
8808
8809
} // namespace detail
8810
8811
/*
8812
 * Group 2: detail namespace - SSL common utilities
8813
 */
8814
8815
#ifdef CPPHTTPLIB_SSL_ENABLED
8816
namespace detail {
8817
8818
class SSLSocketStream final : public Stream {
8819
public:
8820
  SSLSocketStream(
8821
      socket_t sock, tls::session_t session, time_t read_timeout_sec,
8822
      time_t read_timeout_usec, time_t write_timeout_sec,
8823
      time_t write_timeout_usec, time_t max_timeout_msec = 0,
8824
      std::chrono::time_point<std::chrono::steady_clock> start_time =
8825
          (std::chrono::steady_clock::time_point::min)());
8826
  ~SSLSocketStream() override;
8827
8828
  bool is_readable() const override;
8829
  bool wait_readable() const override;
8830
  bool wait_writable() const override;
8831
  bool is_peer_alive() const override;
8832
  ssize_t read(char *ptr, size_t size) override;
8833
  ssize_t write(const char *ptr, size_t size) override;
8834
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
8835
  void get_local_ip_and_port(std::string &ip, int &port) const override;
8836
  socket_t socket() const override;
8837
  time_t duration() const override;
8838
  void set_read_timeout(time_t sec, time_t usec = 0) override;
8839
8840
private:
8841
  socket_t sock_;
8842
  tls::session_t session_;
8843
  time_t read_timeout_sec_;
8844
  time_t read_timeout_usec_;
8845
  time_t write_timeout_sec_;
8846
  time_t write_timeout_usec_;
8847
  time_t max_timeout_msec_;
8848
  const std::chrono::time_point<std::chrono::steady_clock> start_time_;
8849
};
8850
8851
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8852
inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
8853
  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
8854
      EVP_MD_CTX_new(), EVP_MD_CTX_free);
8855
8856
  unsigned int hash_length = 0;
8857
  unsigned char hash[EVP_MAX_MD_SIZE];
8858
8859
  EVP_DigestInit_ex(context.get(), algo, nullptr);
8860
  EVP_DigestUpdate(context.get(), s.c_str(), s.size());
8861
  EVP_DigestFinal_ex(context.get(), hash, &hash_length);
8862
8863
  std::stringstream ss;
8864
  for (auto i = 0u; i < hash_length; ++i) {
8865
    ss << std::hex << std::setw(2) << std::setfill('0')
8866
       << static_cast<unsigned int>(hash[i]);
8867
  }
8868
8869
  return ss.str();
8870
}
8871
8872
inline std::string MD5(const std::string &s) {
8873
  return message_digest(s, EVP_md5());
8874
}
8875
8876
inline std::string SHA_256(const std::string &s) {
8877
  return message_digest(s, EVP_sha256());
8878
}
8879
8880
inline std::string SHA_512(const std::string &s) {
8881
  return message_digest(s, EVP_sha512());
8882
}
8883
#elif defined(CPPHTTPLIB_MBEDTLS_SUPPORT)
8884
namespace {
8885
template <size_t N>
8886
inline std::string hash_to_hex(const unsigned char (&hash)[N]) {
8887
  std::stringstream ss;
8888
  for (size_t i = 0; i < N; ++i) {
8889
    ss << std::hex << std::setw(2) << std::setfill('0')
8890
       << static_cast<unsigned int>(hash[i]);
8891
  }
8892
  return ss.str();
8893
}
8894
} // namespace
8895
8896
inline std::string MD5(const std::string &s) {
8897
  unsigned char hash[16];
8898
#ifdef CPPHTTPLIB_MBEDTLS_V3
8899
  mbedtls_md5(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(),
8900
              hash);
8901
#else
8902
  mbedtls_md5_ret(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(),
8903
                  hash);
8904
#endif
8905
  return hash_to_hex(hash);
8906
}
8907
8908
inline std::string SHA_256(const std::string &s) {
8909
  unsigned char hash[32];
8910
#ifdef CPPHTTPLIB_MBEDTLS_V3
8911
  mbedtls_sha256(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(),
8912
                 hash, 0);
8913
#else
8914
  mbedtls_sha256_ret(reinterpret_cast<const unsigned char *>(s.c_str()),
8915
                     s.size(), hash, 0);
8916
#endif
8917
  return hash_to_hex(hash);
8918
}
8919
8920
inline std::string SHA_512(const std::string &s) {
8921
  unsigned char hash[64];
8922
#ifdef CPPHTTPLIB_MBEDTLS_V3
8923
  mbedtls_sha512(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(),
8924
                 hash, 0);
8925
#else
8926
  mbedtls_sha512_ret(reinterpret_cast<const unsigned char *>(s.c_str()),
8927
                     s.size(), hash, 0);
8928
#endif
8929
  return hash_to_hex(hash);
8930
}
8931
#elif defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
8932
namespace {
8933
template <size_t N>
8934
inline std::string hash_to_hex(const unsigned char (&hash)[N]) {
8935
  std::stringstream ss;
8936
  for (size_t i = 0; i < N; ++i) {
8937
    ss << std::hex << std::setw(2) << std::setfill('0')
8938
       << static_cast<unsigned int>(hash[i]);
8939
  }
8940
  return ss.str();
8941
}
8942
} // namespace
8943
8944
inline std::string MD5(const std::string &s) {
8945
  unsigned char hash[WC_MD5_DIGEST_SIZE];
8946
  wc_Md5Hash(reinterpret_cast<const unsigned char *>(s.c_str()),
8947
             static_cast<word32>(s.size()), hash);
8948
  return hash_to_hex(hash);
8949
}
8950
8951
inline std::string SHA_256(const std::string &s) {
8952
  unsigned char hash[WC_SHA256_DIGEST_SIZE];
8953
  wc_Sha256Hash(reinterpret_cast<const unsigned char *>(s.c_str()),
8954
                static_cast<word32>(s.size()), hash);
8955
  return hash_to_hex(hash);
8956
}
8957
8958
inline std::string SHA_512(const std::string &s) {
8959
  unsigned char hash[WC_SHA512_DIGEST_SIZE];
8960
  wc_Sha512Hash(reinterpret_cast<const unsigned char *>(s.c_str()),
8961
                static_cast<word32>(s.size()), hash);
8962
  return hash_to_hex(hash);
8963
}
8964
#endif
8965
8966
inline bool is_ip_address(const std::string &host) {
8967
  struct in_addr addr4;
8968
  struct in6_addr addr6;
8969
  return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||
8970
         inet_pton(AF_INET6, host.c_str(), &addr6) == 1;
8971
}
8972
8973
template <typename T>
8974
inline bool process_server_socket_ssl(
8975
    const std::atomic<socket_t> &svr_sock, tls::session_t session,
8976
    socket_t sock, size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
8977
    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
8978
    time_t write_timeout_usec, T callback) {
8979
  return process_server_socket_core(
8980
      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
8981
      [&](bool close_connection, bool &connection_closed) {
8982
        SSLSocketStream strm(sock, session, read_timeout_sec, read_timeout_usec,
8983
                             write_timeout_sec, write_timeout_usec);
8984
        return callback(strm, close_connection, connection_closed);
8985
      });
8986
}
8987
8988
template <typename T>
8989
inline bool process_client_socket_ssl(
8990
    tls::session_t session, socket_t sock, time_t read_timeout_sec,
8991
    time_t read_timeout_usec, time_t write_timeout_sec,
8992
    time_t write_timeout_usec, time_t max_timeout_msec,
8993
    std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
8994
  SSLSocketStream strm(sock, session, read_timeout_sec, read_timeout_usec,
8995
                       write_timeout_sec, write_timeout_usec, max_timeout_msec,
8996
                       start_time);
8997
  return callback(strm);
8998
}
8999
9000
inline std::pair<std::string, std::string> make_digest_authentication_header(
9001
    const Request &req, const std::map<std::string, std::string> &auth,
9002
    size_t cnonce_count, const std::string &cnonce, const std::string &username,
9003
    const std::string &password, bool is_proxy = false) {
9004
  std::string nc;
9005
  {
9006
    std::stringstream ss;
9007
    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
9008
    nc = ss.str();
9009
  }
9010
9011
  std::string qop;
9012
  if (auth.find("qop") != auth.end()) {
9013
    qop = auth.at("qop");
9014
    if (qop.find("auth-int") != std::string::npos) {
9015
      qop = "auth-int";
9016
    } else if (qop.find("auth") != std::string::npos) {
9017
      qop = "auth";
9018
    } else {
9019
      qop.clear();
9020
    }
9021
  }
9022
9023
  std::string algo = "MD5";
9024
  if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
9025
9026
  std::string response;
9027
  {
9028
    auto H = algo == "SHA-256"   ? detail::SHA_256
9029
             : algo == "SHA-512" ? detail::SHA_512
9030
                                 : detail::MD5;
9031
9032
    auto A1 = username + ":" + auth.at("realm") + ":" + password;
9033
9034
    auto A2 = req.method + ":" + req.path;
9035
    if (qop == "auth-int") { A2 += ":" + H(req.body); }
9036
9037
    if (qop.empty()) {
9038
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
9039
    } else {
9040
      response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
9041
                   ":" + qop + ":" + H(A2));
9042
    }
9043
  }
9044
9045
  auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
9046
9047
  auto field = "Digest username=\"" + username + "\", realm=\"" +
9048
               auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
9049
               "\", uri=\"" + req.path + "\", algorithm=" + algo +
9050
               (qop.empty() ? ", response=\""
9051
                            : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
9052
                                  cnonce + "\", response=\"") +
9053
               response + "\"" +
9054
               (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
9055
9056
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
9057
  return std::make_pair(key, field);
9058
}
9059
9060
inline bool match_hostname(const std::string &pattern,
9061
                           const std::string &hostname) {
9062
  // Exact match (case-insensitive)
9063
  if (detail::case_ignore::equal(hostname, pattern)) { return true; }
9064
9065
  // Split both pattern and hostname into components by '.'
9066
  std::vector<std::string> pattern_components;
9067
  if (!pattern.empty()) {
9068
    split(pattern.data(), pattern.data() + pattern.size(), '.',
9069
          [&](const char *b, const char *e) {
9070
            pattern_components.emplace_back(b, e);
9071
          });
9072
  }
9073
9074
  std::vector<std::string> host_components;
9075
  if (!hostname.empty()) {
9076
    split(hostname.data(), hostname.data() + hostname.size(), '.',
9077
          [&](const char *b, const char *e) {
9078
            host_components.emplace_back(b, e);
9079
          });
9080
  }
9081
9082
  // Component count must match
9083
  if (host_components.size() != pattern_components.size()) { return false; }
9084
9085
  // Compare each component with wildcard support
9086
  // Supports: "*" (full wildcard), "prefix*" (partial wildcard)
9087
  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
9088
  auto itr = pattern_components.begin();
9089
  for (const auto &h : host_components) {
9090
    auto &p = *itr;
9091
    if (!detail::case_ignore::equal(p, h) && p != "*") {
9092
      bool partial_match = false;
9093
      if (!p.empty() && p[p.size() - 1] == '*') {
9094
        const auto prefix_length = p.size() - 1;
9095
        if (prefix_length == 0) {
9096
          partial_match = true;
9097
        } else if (h.size() >= prefix_length) {
9098
          partial_match =
9099
              std::equal(p.begin(),
9100
                         p.begin() + static_cast<std::string::difference_type>(
9101
                                         prefix_length),
9102
                         h.begin(), [](const char ca, const char cb) {
9103
                           return detail::case_ignore::to_lower(ca) ==
9104
                                  detail::case_ignore::to_lower(cb);
9105
                         });
9106
        }
9107
      }
9108
      if (!partial_match) { return false; }
9109
    }
9110
    ++itr;
9111
  }
9112
9113
  return true;
9114
}
9115
9116
#ifdef _WIN32
9117
// Verify certificate using Windows CertGetCertificateChain API.
9118
// This provides real-time certificate validation with Windows Update
9119
// integration, independent of the TLS backend (OpenSSL or MbedTLS).
9120
inline bool
9121
verify_cert_with_windows_schannel(const std::vector<unsigned char> &der_cert,
9122
                                  const std::string &hostname,
9123
                                  bool verify_hostname, uint64_t &out_error) {
9124
  if (der_cert.empty()) { return false; }
9125
9126
  out_error = 0;
9127
9128
  // Create Windows certificate context from DER data
9129
  auto cert_context = CertCreateCertificateContext(
9130
      X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, der_cert.data(),
9131
      static_cast<DWORD>(der_cert.size()));
9132
9133
  if (!cert_context) {
9134
    out_error = GetLastError();
9135
    return false;
9136
  }
9137
9138
  auto cert_guard =
9139
      scope_exit([&] { CertFreeCertificateContext(cert_context); });
9140
9141
  // Setup chain parameters
9142
  CERT_CHAIN_PARA chain_para = {};
9143
  chain_para.cbSize = sizeof(chain_para);
9144
9145
  // Build certificate chain with revocation checking
9146
  PCCERT_CHAIN_CONTEXT chain_context = nullptr;
9147
  auto chain_result = CertGetCertificateChain(
9148
      nullptr, cert_context, nullptr, cert_context->hCertStore, &chain_para,
9149
      CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_END_CERT |
9150
          CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
9151
      nullptr, &chain_context);
9152
9153
  if (!chain_result || !chain_context) {
9154
    out_error = GetLastError();
9155
    return false;
9156
  }
9157
9158
  auto chain_guard =
9159
      scope_exit([&] { CertFreeCertificateChain(chain_context); });
9160
9161
  // Check if chain has errors
9162
  if (chain_context->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) {
9163
    out_error = chain_context->TrustStatus.dwErrorStatus;
9164
    return false;
9165
  }
9166
9167
  // Verify SSL policy
9168
  SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para = {};
9169
  extra_policy_para.cbSize = sizeof(extra_policy_para);
9170
#ifdef AUTHTYPE_SERVER
9171
  extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
9172
#endif
9173
9174
  std::wstring whost;
9175
  if (verify_hostname) {
9176
    whost = u8string_to_wstring(hostname.c_str());
9177
    extra_policy_para.pwszServerName = const_cast<wchar_t *>(whost.c_str());
9178
  }
9179
9180
  CERT_CHAIN_POLICY_PARA policy_para = {};
9181
  policy_para.cbSize = sizeof(policy_para);
9182
#ifdef CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS
9183
  policy_para.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
9184
#else
9185
  policy_para.dwFlags = 0;
9186
#endif
9187
  policy_para.pvExtraPolicyPara = &extra_policy_para;
9188
9189
  CERT_CHAIN_POLICY_STATUS policy_status = {};
9190
  policy_status.cbSize = sizeof(policy_status);
9191
9192
  if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chain_context,
9193
                                        &policy_para, &policy_status)) {
9194
    out_error = GetLastError();
9195
    return false;
9196
  }
9197
9198
  if (policy_status.dwError != 0) {
9199
    out_error = policy_status.dwError;
9200
    return false;
9201
  }
9202
9203
  return true;
9204
}
9205
#endif // _WIN32
9206
9207
inline bool setup_client_tls_session(const std::string &host, tls::ctx_t &ctx,
9208
                                     tls::session_t &session, socket_t sock,
9209
                                     bool server_certificate_verification,
9210
                                     const std::string &ca_cert_file_path,
9211
                                     tls::ca_store_t ca_cert_store,
9212
                                     time_t timeout_sec, time_t timeout_usec) {
9213
  using namespace tls;
9214
9215
  ctx = create_client_context();
9216
  if (!ctx) { return false; }
9217
9218
  if (server_certificate_verification) {
9219
    if (!ca_cert_file_path.empty()) {
9220
      load_ca_file(ctx, ca_cert_file_path.c_str());
9221
    }
9222
    if (ca_cert_store) { set_ca_store(ctx, ca_cert_store); }
9223
    load_system_certs(ctx);
9224
  }
9225
9226
  bool is_ip = is_ip_address(host);
9227
9228
#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
9229
  if (is_ip && server_certificate_verification) {
9230
    set_verify_client(ctx, false);
9231
  } else {
9232
    set_verify_client(ctx, server_certificate_verification);
9233
  }
9234
#endif
9235
9236
  session = create_session(ctx, sock);
9237
  if (!session) { return false; }
9238
9239
  // RFC 6066: SNI must not be set for IP addresses
9240
  if (!is_ip) { set_sni(session, host.c_str()); }
9241
  if (server_certificate_verification) { set_hostname(session, host.c_str()); }
9242
9243
  if (!connect_nonblocking(session, sock, timeout_sec, timeout_usec, nullptr)) {
9244
    return false;
9245
  }
9246
9247
  if (server_certificate_verification) {
9248
    if (get_verify_result(session) != 0) { return false; }
9249
  }
9250
9251
  return true;
9252
}
9253
9254
} // namespace detail
9255
#endif // CPPHTTPLIB_SSL_ENABLED
9256
9257
/*
9258
 * Group 3: httplib namespace - Non-SSL public API implementations
9259
 */
9260
9261
0
inline void default_socket_options(socket_t sock) {
9262
0
  set_socket_opt(sock, SOL_SOCKET,
9263
0
#ifdef SO_REUSEPORT
9264
0
                 SO_REUSEPORT,
9265
0
#else
9266
0
                 SO_REUSEADDR,
9267
0
#endif
9268
0
                 1);
9269
0
}
9270
9271
0
inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
9272
0
  return detail::set_socket_opt_impl(sock, level, optname, &optval,
9273
0
                                     sizeof(optval));
9274
0
}
9275
9276
0
inline std::string get_bearer_token_auth(const Request &req) {
9277
0
  if (req.has_header("Authorization")) {
9278
0
    constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
9279
0
    return req.get_header_value("Authorization")
9280
0
        .substr(bearer_header_prefix_len);
9281
0
  }
9282
0
  return "";
9283
0
}
9284
9285
0
inline const char *status_message(int status) {
9286
0
  switch (status) {
9287
0
  case StatusCode::Continue_100: return "Continue";
9288
0
  case StatusCode::SwitchingProtocol_101: return "Switching Protocol";
9289
0
  case StatusCode::Processing_102: return "Processing";
9290
0
  case StatusCode::EarlyHints_103: return "Early Hints";
9291
0
  case StatusCode::OK_200: return "OK";
9292
0
  case StatusCode::Created_201: return "Created";
9293
0
  case StatusCode::Accepted_202: return "Accepted";
9294
0
  case StatusCode::NonAuthoritativeInformation_203:
9295
0
    return "Non-Authoritative Information";
9296
0
  case StatusCode::NoContent_204: return "No Content";
9297
0
  case StatusCode::ResetContent_205: return "Reset Content";
9298
0
  case StatusCode::PartialContent_206: return "Partial Content";
9299
0
  case StatusCode::MultiStatus_207: return "Multi-Status";
9300
0
  case StatusCode::AlreadyReported_208: return "Already Reported";
9301
0
  case StatusCode::IMUsed_226: return "IM Used";
9302
0
  case StatusCode::MultipleChoices_300: return "Multiple Choices";
9303
0
  case StatusCode::MovedPermanently_301: return "Moved Permanently";
9304
0
  case StatusCode::Found_302: return "Found";
9305
0
  case StatusCode::SeeOther_303: return "See Other";
9306
0
  case StatusCode::NotModified_304: return "Not Modified";
9307
0
  case StatusCode::UseProxy_305: return "Use Proxy";
9308
0
  case StatusCode::unused_306: return "unused";
9309
0
  case StatusCode::TemporaryRedirect_307: return "Temporary Redirect";
9310
0
  case StatusCode::PermanentRedirect_308: return "Permanent Redirect";
9311
0
  case StatusCode::BadRequest_400: return "Bad Request";
9312
0
  case StatusCode::Unauthorized_401: return "Unauthorized";
9313
0
  case StatusCode::PaymentRequired_402: return "Payment Required";
9314
0
  case StatusCode::Forbidden_403: return "Forbidden";
9315
0
  case StatusCode::NotFound_404: return "Not Found";
9316
0
  case StatusCode::MethodNotAllowed_405: return "Method Not Allowed";
9317
0
  case StatusCode::NotAcceptable_406: return "Not Acceptable";
9318
0
  case StatusCode::ProxyAuthenticationRequired_407:
9319
0
    return "Proxy Authentication Required";
9320
0
  case StatusCode::RequestTimeout_408: return "Request Timeout";
9321
0
  case StatusCode::Conflict_409: return "Conflict";
9322
0
  case StatusCode::Gone_410: return "Gone";
9323
0
  case StatusCode::LengthRequired_411: return "Length Required";
9324
0
  case StatusCode::PreconditionFailed_412: return "Precondition Failed";
9325
0
  case StatusCode::PayloadTooLarge_413: return "Payload Too Large";
9326
0
  case StatusCode::UriTooLong_414: return "URI Too Long";
9327
0
  case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type";
9328
0
  case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable";
9329
0
  case StatusCode::ExpectationFailed_417: return "Expectation Failed";
9330
0
  case StatusCode::ImATeapot_418: return "I'm a teapot";
9331
0
  case StatusCode::MisdirectedRequest_421: return "Misdirected Request";
9332
0
  case StatusCode::UnprocessableContent_422: return "Unprocessable Content";
9333
0
  case StatusCode::Locked_423: return "Locked";
9334
0
  case StatusCode::FailedDependency_424: return "Failed Dependency";
9335
0
  case StatusCode::TooEarly_425: return "Too Early";
9336
0
  case StatusCode::UpgradeRequired_426: return "Upgrade Required";
9337
0
  case StatusCode::PreconditionRequired_428: return "Precondition Required";
9338
0
  case StatusCode::TooManyRequests_429: return "Too Many Requests";
9339
0
  case StatusCode::RequestHeaderFieldsTooLarge_431:
9340
0
    return "Request Header Fields Too Large";
9341
0
  case StatusCode::UnavailableForLegalReasons_451:
9342
0
    return "Unavailable For Legal Reasons";
9343
0
  case StatusCode::NotImplemented_501: return "Not Implemented";
9344
0
  case StatusCode::BadGateway_502: return "Bad Gateway";
9345
0
  case StatusCode::ServiceUnavailable_503: return "Service Unavailable";
9346
0
  case StatusCode::GatewayTimeout_504: return "Gateway Timeout";
9347
0
  case StatusCode::HttpVersionNotSupported_505:
9348
0
    return "HTTP Version Not Supported";
9349
0
  case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates";
9350
0
  case StatusCode::InsufficientStorage_507: return "Insufficient Storage";
9351
0
  case StatusCode::LoopDetected_508: return "Loop Detected";
9352
0
  case StatusCode::NotExtended_510: return "Not Extended";
9353
0
  case StatusCode::NetworkAuthenticationRequired_511:
9354
0
    return "Network Authentication Required";
9355
9356
0
  default:
9357
0
  case StatusCode::InternalServerError_500: return "Internal Server Error";
9358
0
  }
9359
0
}
9360
9361
0
inline std::string to_string(const Error error) {
9362
0
  switch (error) {
9363
0
  case Error::Success: return "Success (no error)";
9364
0
  case Error::Unknown: return "Unknown";
9365
0
  case Error::Connection: return "Could not establish connection";
9366
0
  case Error::BindIPAddress: return "Failed to bind IP address";
9367
0
  case Error::Read: return "Failed to read connection";
9368
0
  case Error::Write: return "Failed to write connection";
9369
0
  case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
9370
0
  case Error::Canceled: return "Connection handling canceled";
9371
0
  case Error::SSLConnection: return "SSL connection failed";
9372
0
  case Error::SSLLoadingCerts: return "SSL certificate loading failed";
9373
0
  case Error::SSLServerVerification: return "SSL server verification failed";
9374
0
  case Error::SSLServerHostnameVerification:
9375
0
    return "SSL server hostname verification failed";
9376
0
  case Error::UnsupportedMultipartBoundaryChars:
9377
0
    return "Unsupported HTTP multipart boundary characters";
9378
0
  case Error::Compression: return "Compression failed";
9379
0
  case Error::ConnectionTimeout: return "Connection timed out";
9380
0
  case Error::ProxyConnection: return "Proxy connection failed";
9381
0
  case Error::ConnectionClosed: return "Connection closed by server";
9382
0
  case Error::Timeout: return "Read timeout";
9383
0
  case Error::ResourceExhaustion: return "Resource exhaustion";
9384
0
  case Error::TooManyFormDataFiles: return "Too many form data files";
9385
0
  case Error::ExceedMaxPayloadSize: return "Exceeded maximum payload size";
9386
0
  case Error::ExceedUriMaxLength: return "Exceeded maximum URI length";
9387
0
  case Error::ExceedMaxSocketDescriptorCount:
9388
0
    return "Exceeded maximum socket descriptor count";
9389
0
  case Error::InvalidRequestLine: return "Invalid request line";
9390
0
  case Error::InvalidHTTPMethod: return "Invalid HTTP method";
9391
0
  case Error::InvalidHTTPVersion: return "Invalid HTTP version";
9392
0
  case Error::InvalidHeaders: return "Invalid headers";
9393
0
  case Error::MultipartParsing: return "Multipart parsing failed";
9394
0
  case Error::OpenFile: return "Failed to open file";
9395
0
  case Error::Listen: return "Failed to listen on socket";
9396
0
  case Error::GetSockName: return "Failed to get socket name";
9397
0
  case Error::UnsupportedAddressFamily: return "Unsupported address family";
9398
0
  case Error::HTTPParsing: return "HTTP parsing failed";
9399
0
  case Error::InvalidRangeHeader: return "Invalid Range header";
9400
0
  default: break;
9401
0
  }
9402
0
9403
0
  return "Invalid";
9404
0
}
9405
9406
0
inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
9407
0
  os << to_string(obj);
9408
0
  os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
9409
0
  return os;
9410
0
}
9411
9412
0
inline std::string hosted_at(const std::string &hostname) {
9413
0
  std::vector<std::string> addrs;
9414
0
  hosted_at(hostname, addrs);
9415
0
  if (addrs.empty()) { return std::string(); }
9416
0
  return addrs[0];
9417
0
}
9418
9419
inline void hosted_at(const std::string &hostname,
9420
0
                      std::vector<std::string> &addrs) {
9421
0
  struct addrinfo hints;
9422
0
  struct addrinfo *result;
9423
0
9424
0
  memset(&hints, 0, sizeof(struct addrinfo));
9425
0
  hints.ai_family = AF_UNSPEC;
9426
0
  hints.ai_socktype = SOCK_STREAM;
9427
0
  hints.ai_protocol = 0;
9428
0
9429
0
  if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
9430
0
                                       &result, 0)) {
9431
0
#if defined __linux__ && !defined __ANDROID__
9432
0
    res_init();
9433
0
#endif
9434
0
    return;
9435
0
  }
9436
0
  auto se = detail::scope_exit([&] { freeaddrinfo(result); });
9437
0
9438
0
  for (auto rp = result; rp; rp = rp->ai_next) {
9439
0
    const auto &addr =
9440
0
        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
9441
0
    std::string ip;
9442
0
    auto dummy = -1;
9443
0
    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
9444
0
                                dummy)) {
9445
0
      addrs.emplace_back(std::move(ip));
9446
0
    }
9447
0
  }
9448
0
}
9449
9450
0
inline std::string encode_uri_component(const std::string &value) {
9451
0
  std::ostringstream escaped;
9452
0
  escaped.fill('0');
9453
0
  escaped << std::hex;
9454
0
9455
0
  for (auto c : value) {
9456
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
9457
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
9458
0
        c == ')') {
9459
0
      escaped << c;
9460
0
    } else {
9461
0
      escaped << std::uppercase;
9462
0
      escaped << '%' << std::setw(2)
9463
0
              << static_cast<int>(static_cast<unsigned char>(c));
9464
0
      escaped << std::nouppercase;
9465
0
    }
9466
0
  }
9467
0
9468
0
  return escaped.str();
9469
0
}
9470
9471
0
inline std::string encode_uri(const std::string &value) {
9472
0
  std::ostringstream escaped;
9473
0
  escaped.fill('0');
9474
0
  escaped << std::hex;
9475
0
9476
0
  for (auto c : value) {
9477
0
    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
9478
0
        c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
9479
0
        c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||
9480
0
        c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {
9481
0
      escaped << c;
9482
0
    } else {
9483
0
      escaped << std::uppercase;
9484
0
      escaped << '%' << std::setw(2)
9485
0
              << static_cast<int>(static_cast<unsigned char>(c));
9486
0
      escaped << std::nouppercase;
9487
0
    }
9488
0
  }
9489
0
9490
0
  return escaped.str();
9491
0
}
9492
9493
0
inline std::string decode_uri_component(const std::string &value) {
9494
0
  std::string result;
9495
0
9496
0
  for (size_t i = 0; i < value.size(); i++) {
9497
0
    if (value[i] == '%' && i + 2 < value.size()) {
9498
0
      auto val = 0;
9499
0
      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
9500
0
        result += static_cast<char>(val);
9501
0
        i += 2;
9502
0
      } else {
9503
0
        result += value[i];
9504
0
      }
9505
0
    } else {
9506
0
      result += value[i];
9507
0
    }
9508
0
  }
9509
0
9510
0
  return result;
9511
0
}
9512
9513
0
inline std::string decode_uri(const std::string &value) {
9514
0
  std::string result;
9515
0
9516
0
  for (size_t i = 0; i < value.size(); i++) {
9517
0
    if (value[i] == '%' && i + 2 < value.size()) {
9518
0
      auto val = 0;
9519
0
      if (detail::from_hex_to_i(value, i + 1, 2, val)) {
9520
0
        result += static_cast<char>(val);
9521
0
        i += 2;
9522
0
      } else {
9523
0
        result += value[i];
9524
0
      }
9525
0
    } else {
9526
0
      result += value[i];
9527
0
    }
9528
0
  }
9529
0
9530
0
  return result;
9531
0
}
9532
9533
0
inline std::string encode_path_component(const std::string &component) {
9534
0
  std::string result;
9535
0
  result.reserve(component.size() * 3);
9536
0
9537
0
  for (size_t i = 0; i < component.size(); i++) {
9538
0
    auto c = static_cast<unsigned char>(component[i]);
9539
0
9540
0
    // Unreserved characters per RFC 3986: ALPHA / DIGIT / "-" / "." / "_" / "~"
9541
0
    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
9542
0
      result += static_cast<char>(c);
9543
0
    }
9544
0
    // Path-safe sub-delimiters: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" /
9545
0
    // "," / ";" / "="
9546
0
    else if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' ||
9547
0
             c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
9548
0
             c == '=') {
9549
0
      result += static_cast<char>(c);
9550
0
    }
9551
0
    // Colon is allowed in path segments except first segment
9552
0
    else if (c == ':') {
9553
0
      result += static_cast<char>(c);
9554
0
    }
9555
0
    // @ is allowed in path
9556
0
    else if (c == '@') {
9557
0
      result += static_cast<char>(c);
9558
0
    } else {
9559
0
      result += '%';
9560
0
      char hex[3];
9561
0
      snprintf(hex, sizeof(hex), "%02X", c);
9562
0
      result.append(hex, 2);
9563
0
    }
9564
0
  }
9565
0
  return result;
9566
0
}
9567
9568
257
inline std::string decode_path_component(const std::string &component) {
9569
257
  std::string result;
9570
257
  result.reserve(component.size());
9571
9572
4.32M
  for (size_t i = 0; i < component.size(); i++) {
9573
4.32M
    if (component[i] == '%' && i + 1 < component.size()) {
9574
338k
      if (component[i + 1] == 'u') {
9575
        // Unicode %uXXXX encoding
9576
6.55k
        auto val = 0;
9577
6.55k
        if (detail::from_hex_to_i(component, i + 2, 4, val)) {
9578
          // 4 digits Unicode codes: val is 0x0000-0xFFFF (from 4 hex digits),
9579
          // so to_utf8 writes at most 3 bytes. buff[4] is safe.
9580
2.80k
          char buff[4];
9581
2.80k
          size_t len = detail::to_utf8(val, buff);
9582
2.80k
          if (len > 0) { result.append(buff, len); }
9583
2.80k
          i += 5; // 'u0000'
9584
3.74k
        } else {
9585
3.74k
          result += component[i];
9586
3.74k
        }
9587
332k
      } else {
9588
        // Standard %XX encoding
9589
332k
        auto val = 0;
9590
332k
        if (detail::from_hex_to_i(component, i + 1, 2, val)) {
9591
          // 2 digits hex codes
9592
116k
          result += static_cast<char>(val);
9593
116k
          i += 2; // 'XX'
9594
215k
        } else {
9595
215k
          result += component[i];
9596
215k
        }
9597
332k
      }
9598
3.98M
    } else {
9599
3.98M
      result += component[i];
9600
3.98M
    }
9601
4.32M
  }
9602
257
  return result;
9603
257
}
9604
9605
inline std::string encode_query_component(const std::string &component,
9606
23.5k
                                          bool space_as_plus) {
9607
23.5k
  std::string result;
9608
23.5k
  result.reserve(component.size() * 3);
9609
9610
5.27M
  for (size_t i = 0; i < component.size(); i++) {
9611
5.25M
    auto c = static_cast<unsigned char>(component[i]);
9612
9613
    // Unreserved characters per RFC 3986
9614
5.25M
    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {
9615
432k
      result += static_cast<char>(c);
9616
432k
    }
9617
    // Space handling
9618
4.82M
    else if (c == ' ') {
9619
18.0k
      if (space_as_plus) {
9620
18.0k
        result += '+';
9621
18.0k
      } else {
9622
0
        result += "%20";
9623
0
      }
9624
18.0k
    }
9625
    // Plus sign handling
9626
4.80M
    else if (c == '+') {
9627
2.64k
      if (space_as_plus) {
9628
2.64k
        result += "%2B";
9629
2.64k
      } else {
9630
0
        result += static_cast<char>(c);
9631
0
      }
9632
2.64k
    }
9633
    // Query-safe sub-delimiters (excluding & and = which are query delimiters)
9634
4.80M
    else if (c == '!' || c == '$' || c == '\'' || c == '(' || c == ')' ||
9635
4.78M
             c == '*' || c == ',' || c == ';') {
9636
35.3k
      result += static_cast<char>(c);
9637
35.3k
    }
9638
    // Colon and @ are allowed in query
9639
4.76M
    else if (c == ':' || c == '@') {
9640
141k
      result += static_cast<char>(c);
9641
141k
    }
9642
    // Forward slash is allowed in query values
9643
4.62M
    else if (c == '/') {
9644
6.92k
      result += static_cast<char>(c);
9645
6.92k
    }
9646
    // Question mark is allowed in query values (after first ?)
9647
4.61M
    else if (c == '?') {
9648
2.68k
      result += static_cast<char>(c);
9649
4.61M
    } else {
9650
4.61M
      result += '%';
9651
4.61M
      char hex[3];
9652
4.61M
      snprintf(hex, sizeof(hex), "%02X", c);
9653
4.61M
      result.append(hex, 2);
9654
4.61M
    }
9655
5.25M
  }
9656
23.5k
  return result;
9657
23.5k
}
9658
9659
inline std::string decode_query_component(const std::string &component,
9660
78.2k
                                          bool plus_as_space) {
9661
78.2k
  std::string result;
9662
78.2k
  result.reserve(component.size());
9663
9664
37.7M
  for (size_t i = 0; i < component.size(); i++) {
9665
37.6M
    if (component[i] == '%' && i + 2 < component.size()) {
9666
550k
      std::string hex = component.substr(i + 1, 2);
9667
550k
      char *end;
9668
550k
      unsigned long value = std::strtoul(hex.c_str(), &end, 16);
9669
550k
      if (end == hex.c_str() + 2) {
9670
118k
        result += static_cast<char>(value);
9671
118k
        i += 2;
9672
432k
      } else {
9673
432k
        result += component[i];
9674
432k
      }
9675
37.0M
    } else if (component[i] == '+' && plus_as_space) {
9676
4.22k
      result += ' '; // + becomes space in form-urlencoded
9677
37.0M
    } else {
9678
37.0M
      result += component[i];
9679
37.0M
    }
9680
37.6M
  }
9681
78.2k
  return result;
9682
78.2k
}
9683
9684
0
inline std::string sanitize_filename(const std::string &filename) {
9685
0
  // Extract basename: find the last path separator (/ or \)
9686
0
  auto pos = filename.find_last_of("/\\");
9687
0
  auto result =
9688
0
      (pos != std::string::npos) ? filename.substr(pos + 1) : filename;
9689
0
9690
0
  // Strip null bytes
9691
0
  result.erase(std::remove(result.begin(), result.end(), '\0'), result.end());
9692
0
9693
0
  // Trim whitespace
9694
0
  {
9695
0
    auto start = result.find_first_not_of(" \t");
9696
0
    auto end = result.find_last_not_of(" \t");
9697
0
    result = (start == std::string::npos)
9698
0
                 ? ""
9699
0
                 : result.substr(start, end - start + 1);
9700
0
  }
9701
0
9702
0
  // Reject . and ..
9703
0
  if (result == "." || result == "..") { return ""; }
9704
0
9705
0
  return result;
9706
0
}
9707
9708
inline std::string append_query_params(const std::string &path,
9709
0
                                       const Params &params) {
9710
0
  std::string path_with_query = path;
9711
0
  thread_local const std::regex re("[^?]+\\?.*");
9712
0
  auto delm = std::regex_match(path, re) ? '&' : '?';
9713
0
  path_with_query += delm + detail::params_to_query_str(params);
9714
0
  return path_with_query;
9715
0
}
9716
9717
// Header utilities
9718
inline std::pair<std::string, std::string>
9719
0
make_range_header(const Ranges &ranges) {
9720
0
  std::string field = "bytes=";
9721
0
  auto i = 0;
9722
0
  for (const auto &r : ranges) {
9723
0
    if (i != 0) { field += ", "; }
9724
0
    if (r.first != -1) { field += std::to_string(r.first); }
9725
0
    field += '-';
9726
0
    if (r.second != -1) { field += std::to_string(r.second); }
9727
0
    i++;
9728
0
  }
9729
0
  return std::make_pair("Range", std::move(field));
9730
0
}
9731
9732
inline std::pair<std::string, std::string>
9733
make_basic_authentication_header(const std::string &username,
9734
0
                                 const std::string &password, bool is_proxy) {
9735
0
  auto field = "Basic " + detail::base64_encode(username + ":" + password);
9736
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
9737
0
  return std::make_pair(key, std::move(field));
9738
0
}
9739
9740
inline std::pair<std::string, std::string>
9741
make_bearer_token_authentication_header(const std::string &token,
9742
0
                                        bool is_proxy = false) {
9743
0
  auto field = "Bearer " + token;
9744
0
  auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
9745
0
  return std::make_pair(key, std::move(field));
9746
0
}
9747
9748
// Request implementation
9749
inline size_t Request::get_header_value_u64(const std::string &key, size_t def,
9750
0
                                            size_t id) const {
9751
0
  return detail::get_header_value_u64(headers, key, def, id);
9752
0
}
9753
9754
0
inline bool Request::has_header(const std::string &key) const {
9755
0
  return detail::has_header(headers, key);
9756
0
}
9757
9758
inline std::string Request::get_header_value(const std::string &key,
9759
0
                                             const char *def, size_t id) const {
9760
0
  return detail::get_header_value(headers, key, def, id);
9761
0
}
9762
9763
0
inline size_t Request::get_header_value_count(const std::string &key) const {
9764
0
  return detail::get_header_value_count(headers, key);
9765
0
}
9766
9767
inline void Request::set_header(const std::string &key,
9768
0
                                const std::string &val) {
9769
0
  detail::set_header(headers, key, val);
9770
0
}
9771
9772
0
inline bool Request::has_trailer(const std::string &key) const {
9773
0
  return trailers.find(key) != trailers.end();
9774
0
}
9775
9776
inline std::string Request::get_trailer_value(const std::string &key,
9777
0
                                              size_t id) const {
9778
0
  return detail::get_multimap_value(trailers, key, id);
9779
0
}
9780
9781
0
inline size_t Request::get_trailer_value_count(const std::string &key) const {
9782
0
  auto r = trailers.equal_range(key);
9783
0
  return static_cast<size_t>(std::distance(r.first, r.second));
9784
0
}
9785
9786
0
inline bool Request::has_param(const std::string &key) const {
9787
0
  return params.find(key) != params.end();
9788
0
}
9789
9790
inline std::string Request::get_param_value(const std::string &key,
9791
0
                                            size_t id) const {
9792
0
  return detail::get_multimap_value(params, key, id);
9793
0
}
9794
9795
inline std::vector<std::string>
9796
0
Request::get_param_values(const std::string &key) const {
9797
0
  auto rng = params.equal_range(key);
9798
0
  std::vector<std::string> values;
9799
0
  values.reserve(static_cast<size_t>(std::distance(rng.first, rng.second)));
9800
0
  for (auto it = rng.first; it != rng.second; ++it) {
9801
0
    values.push_back(it->second);
9802
0
  }
9803
0
  return values;
9804
0
}
9805
9806
0
inline size_t Request::get_param_value_count(const std::string &key) const {
9807
0
  auto r = params.equal_range(key);
9808
0
  return static_cast<size_t>(std::distance(r.first, r.second));
9809
0
}
9810
9811
0
inline bool Request::is_multipart_form_data() const {
9812
0
  const auto &content_type = get_header_value("Content-Type");
9813
0
  return detail::extract_media_type(content_type) == "multipart/form-data";
9814
0
}
9815
9816
// Multipart FormData implementation
9817
inline std::string MultipartFormData::get_field(const std::string &key,
9818
0
                                                size_t id) const {
9819
0
  auto rng = fields.equal_range(key);
9820
0
  auto it = rng.first;
9821
0
  std::advance(it, static_cast<ssize_t>(id));
9822
0
  if (it != rng.second) { return it->second.content; }
9823
0
  return std::string();
9824
0
}
9825
9826
inline std::vector<std::string>
9827
0
MultipartFormData::get_fields(const std::string &key) const {
9828
0
  std::vector<std::string> values;
9829
0
  auto rng = fields.equal_range(key);
9830
0
  for (auto it = rng.first; it != rng.second; it++) {
9831
0
    values.push_back(it->second.content);
9832
0
  }
9833
0
  return values;
9834
0
}
9835
9836
0
inline bool MultipartFormData::has_field(const std::string &key) const {
9837
0
  return fields.find(key) != fields.end();
9838
0
}
9839
9840
0
inline size_t MultipartFormData::get_field_count(const std::string &key) const {
9841
0
  auto r = fields.equal_range(key);
9842
0
  return static_cast<size_t>(std::distance(r.first, r.second));
9843
0
}
9844
9845
inline FormData MultipartFormData::get_file(const std::string &key,
9846
0
                                            size_t id) const {
9847
0
  return detail::get_multimap_value(files, key, id);
9848
0
}
9849
9850
inline std::vector<FormData>
9851
0
MultipartFormData::get_files(const std::string &key) const {
9852
0
  std::vector<FormData> values;
9853
0
  auto rng = files.equal_range(key);
9854
0
  for (auto it = rng.first; it != rng.second; it++) {
9855
0
    values.push_back(it->second);
9856
0
  }
9857
0
  return values;
9858
0
}
9859
9860
0
inline bool MultipartFormData::has_file(const std::string &key) const {
9861
0
  return files.find(key) != files.end();
9862
0
}
9863
9864
0
inline size_t MultipartFormData::get_file_count(const std::string &key) const {
9865
0
  auto r = files.equal_range(key);
9866
0
  return static_cast<size_t>(std::distance(r.first, r.second));
9867
0
}
9868
9869
// Response implementation
9870
inline size_t Response::get_header_value_u64(const std::string &key, size_t def,
9871
0
                                             size_t id) const {
9872
0
  return detail::get_header_value_u64(headers, key, def, id);
9873
0
}
9874
9875
0
inline bool Response::has_header(const std::string &key) const {
9876
0
  return headers.find(key) != headers.end();
9877
0
}
9878
9879
inline std::string Response::get_header_value(const std::string &key,
9880
                                              const char *def,
9881
0
                                              size_t id) const {
9882
0
  return detail::get_header_value(headers, key, def, id);
9883
0
}
9884
9885
0
inline size_t Response::get_header_value_count(const std::string &key) const {
9886
0
  return detail::get_header_value_count(headers, key);
9887
0
}
9888
9889
inline void Response::set_header(const std::string &key,
9890
0
                                 const std::string &val) {
9891
0
  detail::set_header(headers, key, val);
9892
0
}
9893
0
inline bool Response::has_trailer(const std::string &key) const {
9894
0
  return trailers.find(key) != trailers.end();
9895
0
}
9896
9897
inline std::string Response::get_trailer_value(const std::string &key,
9898
0
                                               size_t id) const {
9899
0
  return detail::get_multimap_value(trailers, key, id);
9900
0
}
9901
9902
0
inline size_t Response::get_trailer_value_count(const std::string &key) const {
9903
0
  auto r = trailers.equal_range(key);
9904
0
  return static_cast<size_t>(std::distance(r.first, r.second));
9905
0
}
9906
9907
0
inline void Response::set_redirect(const std::string &url, int stat) {
9908
0
  if (detail::fields::is_field_value(url)) {
9909
0
    set_header("Location", url);
9910
0
    if (300 <= stat && stat < 400) {
9911
0
      this->status = stat;
9912
0
    } else {
9913
0
      this->status = StatusCode::Found_302;
9914
0
    }
9915
0
  }
9916
0
}
9917
9918
inline void Response::set_content(const char *s, size_t n,
9919
0
                                  const std::string &content_type) {
9920
0
  body.assign(s, n);
9921
0
9922
0
  auto rng = headers.equal_range("Content-Type");
9923
0
  headers.erase(rng.first, rng.second);
9924
0
  set_header("Content-Type", content_type);
9925
0
}
9926
9927
inline void Response::set_content(const std::string &s,
9928
0
                                  const std::string &content_type) {
9929
0
  set_content(s.data(), s.size(), content_type);
9930
0
}
9931
9932
inline void Response::set_content(std::string &&s,
9933
0
                                  const std::string &content_type) {
9934
0
  body = std::move(s);
9935
0
9936
0
  auto rng = headers.equal_range("Content-Type");
9937
0
  headers.erase(rng.first, rng.second);
9938
0
  set_header("Content-Type", content_type);
9939
0
}
9940
9941
inline void Response::set_content_provider(
9942
    size_t in_length, const std::string &content_type, ContentProvider provider,
9943
0
    ContentProviderResourceReleaser resource_releaser) {
9944
0
  set_header("Content-Type", content_type);
9945
0
  content_length_ = in_length;
9946
0
  if (in_length > 0) { content_provider_ = std::move(provider); }
9947
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
9948
0
  is_chunked_content_provider_ = false;
9949
0
}
9950
9951
inline void Response::set_content_provider(
9952
    const std::string &content_type, ContentProviderWithoutLength provider,
9953
0
    ContentProviderResourceReleaser resource_releaser) {
9954
0
  set_header("Content-Type", content_type);
9955
0
  content_length_ = 0;
9956
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
9957
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
9958
0
  is_chunked_content_provider_ = false;
9959
0
}
9960
9961
inline void Response::set_chunked_content_provider(
9962
    const std::string &content_type, ContentProviderWithoutLength provider,
9963
0
    ContentProviderResourceReleaser resource_releaser) {
9964
0
  set_header("Content-Type", content_type);
9965
0
  content_length_ = 0;
9966
0
  content_provider_ = detail::ContentProviderAdapter(std::move(provider));
9967
0
  content_provider_resource_releaser_ = std::move(resource_releaser);
9968
0
  is_chunked_content_provider_ = true;
9969
0
}
9970
9971
inline void Response::set_file_content(const std::string &path,
9972
0
                                       const std::string &content_type) {
9973
0
  file_content_path_ = path;
9974
0
  file_content_content_type_ = content_type;
9975
0
}
9976
9977
0
inline void Response::set_file_content(const std::string &path) {
9978
0
  file_content_path_ = path;
9979
0
}
9980
9981
// Result implementation
9982
inline size_t Result::get_request_header_value_u64(const std::string &key,
9983
                                                   size_t def,
9984
0
                                                   size_t id) const {
9985
0
  return detail::get_header_value_u64(request_headers_, key, def, id);
9986
0
}
9987
9988
0
inline bool Result::has_request_header(const std::string &key) const {
9989
0
  return request_headers_.find(key) != request_headers_.end();
9990
0
}
9991
9992
inline std::string Result::get_request_header_value(const std::string &key,
9993
                                                    const char *def,
9994
0
                                                    size_t id) const {
9995
0
  return detail::get_header_value(request_headers_, key, def, id);
9996
0
}
9997
9998
inline size_t
9999
0
Result::get_request_header_value_count(const std::string &key) const {
10000
0
  auto r = request_headers_.equal_range(key);
10001
0
  return static_cast<size_t>(std::distance(r.first, r.second));
10002
0
}
10003
10004
// Stream implementation
10005
0
inline ssize_t Stream::write(const char *ptr) {
10006
0
  return write(ptr, strlen(ptr));
10007
0
}
10008
10009
0
inline ssize_t Stream::write(const std::string &s) {
10010
0
  return write(s.data(), s.size());
10011
0
}
10012
10013
// BodyReader implementation
10014
0
inline ssize_t detail::BodyReader::read(char *buf, size_t len) {
10015
0
  if (!stream) {
10016
0
    last_error = Error::Connection;
10017
0
    return -1;
10018
0
  }
10019
0
  if (eof) { return 0; }
10020
10021
0
  if (!chunked) {
10022
    // Content-Length based reading
10023
0
    if (has_content_length && bytes_read >= content_length) {
10024
0
      eof = true;
10025
0
      return 0;
10026
0
    }
10027
10028
0
    auto to_read = len;
10029
0
    if (has_content_length) {
10030
0
      auto remaining = content_length - bytes_read;
10031
0
      to_read = (std::min)(len, remaining);
10032
0
    }
10033
0
    auto n = stream->read(buf, to_read);
10034
10035
0
    if (n < 0) {
10036
0
      last_error = stream->get_error();
10037
0
      if (last_error == Error::Success) { last_error = Error::Read; }
10038
0
      eof = true;
10039
0
      return n;
10040
0
    }
10041
0
    if (n == 0) {
10042
      // Unexpected EOF before content_length
10043
0
      last_error = stream->get_error();
10044
0
      if (last_error == Error::Success) { last_error = Error::Read; }
10045
0
      eof = true;
10046
0
      return 0;
10047
0
    }
10048
10049
0
    bytes_read += static_cast<size_t>(n);
10050
0
    if (has_content_length && bytes_read >= content_length) { eof = true; }
10051
0
    if (payload_max_length > 0 && bytes_read > payload_max_length) {
10052
0
      last_error = Error::ExceedMaxPayloadSize;
10053
0
      eof = true;
10054
0
      return -1;
10055
0
    }
10056
0
    return n;
10057
0
  }
10058
10059
  // Chunked transfer encoding: delegate to shared decoder instance.
10060
0
  if (!chunked_decoder) { chunked_decoder.reset(new ChunkedDecoder(*stream)); }
10061
10062
0
  size_t chunk_offset = 0;
10063
0
  size_t chunk_total = 0;
10064
0
  auto n = chunked_decoder->read_payload(buf, len, chunk_offset, chunk_total);
10065
0
  if (n < 0) {
10066
0
    last_error = stream->get_error();
10067
0
    if (last_error == Error::Success) { last_error = Error::Read; }
10068
0
    eof = true;
10069
0
    return n;
10070
0
  }
10071
10072
0
  if (n == 0) {
10073
    // Final chunk observed. Leave trailer parsing to the caller (StreamHandle).
10074
0
    eof = true;
10075
0
    return 0;
10076
0
  }
10077
10078
0
  bytes_read += static_cast<size_t>(n);
10079
0
  if (payload_max_length > 0 && bytes_read > payload_max_length) {
10080
0
    last_error = Error::ExceedMaxPayloadSize;
10081
0
    eof = true;
10082
0
    return -1;
10083
0
  }
10084
0
  return n;
10085
0
}
10086
10087
// ThreadPool implementation
10088
inline ThreadPool::ThreadPool(size_t n, size_t max_n, size_t mqr)
10089
    : base_thread_count_(n), max_queued_requests_(mqr), idle_thread_count_(0),
10090
      shutdown_(false) {
10091
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10092
  if (max_n != 0 && max_n < n) {
10093
    std::string msg = "max_threads must be >= base_threads";
10094
    throw std::invalid_argument(msg);
10095
  }
10096
#endif
10097
  max_thread_count_ = max_n == 0 ? n : max_n;
10098
  threads_.reserve(base_thread_count_);
10099
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10100
  try {
10101
#endif
10102
    for (size_t i = 0; i < base_thread_count_; i++) {
10103
      threads_.emplace_back(std::thread([this]() { worker(false); }));
10104
    }
10105
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10106
  } catch (...) {
10107
    // If thread creation fails partway (e.g., pthread_create returns EAGAIN),
10108
    // signal the workers we already spawned to exit and join them so the
10109
    // vector destructor does not see joinable threads (which would call
10110
    // std::terminate). Then rethrow so the caller learns of the failure.
10111
    {
10112
      std::unique_lock<std::mutex> lock(mutex_);
10113
      shutdown_ = true;
10114
    }
10115
    cond_.notify_all();
10116
    for (auto &t : threads_) {
10117
      if (t.joinable()) { t.join(); }
10118
    }
10119
    throw;
10120
  }
10121
#endif
10122
}
10123
10124
0
inline bool ThreadPool::enqueue(std::function<void()> fn) {
10125
0
  {
10126
0
    std::unique_lock<std::mutex> lock(mutex_);
10127
0
    if (shutdown_) { return false; }
10128
0
    if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
10129
0
      return false;
10130
0
    }
10131
0
    jobs_.push_back(std::move(fn));
10132
10133
    // Spawn a dynamic thread if no idle threads and under max
10134
0
    if (idle_thread_count_ == 0 &&
10135
0
        threads_.size() + dynamic_threads_.size() < max_thread_count_) {
10136
0
      cleanup_finished_threads();
10137
0
      dynamic_threads_.emplace_back(std::thread([this]() { worker(true); }));
10138
0
    }
10139
0
  }
10140
10141
0
  cond_.notify_one();
10142
0
  return true;
10143
0
}
10144
10145
0
inline void ThreadPool::shutdown() {
10146
0
  {
10147
0
    std::unique_lock<std::mutex> lock(mutex_);
10148
0
    shutdown_ = true;
10149
0
  }
10150
10151
0
  cond_.notify_all();
10152
10153
0
  for (auto &t : threads_) {
10154
0
    if (t.joinable()) { t.join(); }
10155
0
  }
10156
10157
  // Move dynamic_threads_ to a local list under the lock to avoid racing
10158
  // with worker threads that call move_to_finished() concurrently.
10159
0
  std::list<std::thread> remaining_dynamic;
10160
0
  {
10161
0
    std::unique_lock<std::mutex> lock(mutex_);
10162
0
    remaining_dynamic = std::move(dynamic_threads_);
10163
0
  }
10164
0
  for (auto &t : remaining_dynamic) {
10165
0
    if (t.joinable()) { t.join(); }
10166
0
  }
10167
10168
0
  std::unique_lock<std::mutex> lock(mutex_);
10169
0
  cleanup_finished_threads();
10170
0
}
10171
10172
0
inline void ThreadPool::move_to_finished(std::thread::id id) {
10173
  // Must be called with mutex_ held
10174
0
  for (auto it = dynamic_threads_.begin(); it != dynamic_threads_.end(); ++it) {
10175
0
    if (it->get_id() == id) {
10176
0
      finished_threads_.push_back(std::move(*it));
10177
0
      dynamic_threads_.erase(it);
10178
0
      return;
10179
0
    }
10180
0
  }
10181
0
}
10182
10183
0
inline void ThreadPool::cleanup_finished_threads() {
10184
  // Must be called with mutex_ held
10185
0
  for (auto &t : finished_threads_) {
10186
0
    if (t.joinable()) { t.join(); }
10187
0
  }
10188
0
  finished_threads_.clear();
10189
0
}
10190
10191
0
inline void ThreadPool::worker(bool is_dynamic) {
10192
0
  for (;;) {
10193
0
    std::function<void()> fn;
10194
0
    {
10195
0
      std::unique_lock<std::mutex> lock(mutex_);
10196
0
      idle_thread_count_++;
10197
10198
0
      if (is_dynamic) {
10199
0
        auto has_work = cond_.wait_for(
10200
0
            lock, std::chrono::seconds(CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT),
10201
0
            [&] { return !jobs_.empty() || shutdown_; });
10202
0
        if (!has_work) {
10203
          // Timed out with no work - exit this dynamic thread
10204
0
          idle_thread_count_--;
10205
0
          move_to_finished(std::this_thread::get_id());
10206
0
          break;
10207
0
        }
10208
0
      } else {
10209
0
        cond_.wait(lock, [&] { return !jobs_.empty() || shutdown_; });
10210
0
      }
10211
10212
0
      idle_thread_count_--;
10213
10214
0
      if (shutdown_ && jobs_.empty()) { break; }
10215
10216
0
      fn = std::move(jobs_.front());
10217
0
      jobs_.pop_front();
10218
0
    }
10219
10220
0
    assert(true == static_cast<bool>(fn));
10221
0
    fn();
10222
0
  }
10223
10224
#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \
10225
    !defined(LIBRESSL_VERSION_NUMBER)
10226
  OPENSSL_thread_stop();
10227
#endif
10228
0
}
10229
10230
/*
10231
 * Group 1 (continued): detail namespace - Stream implementations
10232
 */
10233
10234
namespace detail {
10235
10236
inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
10237
                                time_t timeout_sec, time_t timeout_usec,
10238
                                time_t &actual_timeout_sec,
10239
0
                                time_t &actual_timeout_usec) {
10240
0
  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
10241
10242
0
  auto actual_timeout_msec =
10243
0
      (std::min)(max_timeout_msec - duration_msec, timeout_msec);
10244
10245
0
  if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }
10246
10247
0
  actual_timeout_sec = actual_timeout_msec / 1000;
10248
0
  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
10249
0
}
10250
10251
// Socket stream implementation
10252
inline SocketStream::SocketStream(
10253
    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
10254
    time_t write_timeout_sec, time_t write_timeout_usec,
10255
    time_t max_timeout_msec,
10256
    std::chrono::time_point<std::chrono::steady_clock> start_time)
10257
0
    : sock_(sock), read_timeout_sec_(read_timeout_sec),
10258
0
      read_timeout_usec_(read_timeout_usec),
10259
0
      write_timeout_sec_(write_timeout_sec),
10260
0
      write_timeout_usec_(write_timeout_usec),
10261
0
      max_timeout_msec_(max_timeout_msec), start_time_(start_time),
10262
0
      read_buff_(read_buff_size_, 0) {}
10263
10264
0
inline SocketStream::~SocketStream() = default;
10265
10266
0
inline bool SocketStream::is_readable() const {
10267
0
  return read_buff_off_ < read_buff_content_size_;
10268
0
}
10269
10270
0
inline bool SocketStream::wait_readable() const {
10271
0
  if (max_timeout_msec_ <= 0) {
10272
0
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10273
0
  }
10274
10275
0
  time_t read_timeout_sec;
10276
0
  time_t read_timeout_usec;
10277
0
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10278
0
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10279
10280
0
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10281
0
}
10282
10283
0
inline bool SocketStream::wait_writable() const {
10284
0
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
10285
0
}
10286
10287
0
inline bool SocketStream::is_peer_alive() const {
10288
0
  return detail::is_socket_alive(sock_);
10289
0
}
10290
10291
0
inline ssize_t SocketStream::read(char *ptr, size_t size) {
10292
#ifdef _WIN32
10293
  size =
10294
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
10295
#else
10296
0
  size = (std::min)(size,
10297
0
                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
10298
0
#endif
10299
10300
0
  if (read_buff_off_ < read_buff_content_size_) {
10301
0
    auto remaining_size = read_buff_content_size_ - read_buff_off_;
10302
0
    if (size <= remaining_size) {
10303
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, size);
10304
0
      read_buff_off_ += size;
10305
0
      return static_cast<ssize_t>(size);
10306
0
    } else {
10307
0
      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
10308
0
      read_buff_off_ += remaining_size;
10309
0
      return static_cast<ssize_t>(remaining_size);
10310
0
    }
10311
0
  }
10312
10313
0
  if (!wait_readable()) {
10314
0
    error_ = Error::Timeout;
10315
0
    return -1;
10316
0
  }
10317
10318
0
  read_buff_off_ = 0;
10319
0
  read_buff_content_size_ = 0;
10320
10321
0
  if (size < read_buff_size_) {
10322
0
    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
10323
0
                         CPPHTTPLIB_RECV_FLAGS);
10324
0
    if (n <= 0) {
10325
0
      if (n == 0) {
10326
0
        error_ = Error::ConnectionClosed;
10327
0
      } else {
10328
0
        error_ = Error::Read;
10329
0
      }
10330
0
      return n;
10331
0
    } else if (n <= static_cast<ssize_t>(size)) {
10332
0
      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
10333
0
      return n;
10334
0
    } else {
10335
0
      memcpy(ptr, read_buff_.data(), size);
10336
0
      read_buff_off_ = size;
10337
0
      read_buff_content_size_ = static_cast<size_t>(n);
10338
0
      return static_cast<ssize_t>(size);
10339
0
    }
10340
0
  } else {
10341
0
    auto n = read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
10342
0
    if (n <= 0) {
10343
0
      if (n == 0) {
10344
0
        error_ = Error::ConnectionClosed;
10345
0
      } else {
10346
0
        error_ = Error::Read;
10347
0
      }
10348
0
    }
10349
0
    return n;
10350
0
  }
10351
0
}
10352
10353
0
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
10354
0
  if (!wait_writable()) { return -1; }
10355
10356
#if defined(_WIN32) && !defined(_WIN64)
10357
  size =
10358
      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
10359
#endif
10360
10361
0
  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
10362
0
}
10363
10364
inline void SocketStream::get_remote_ip_and_port(std::string &ip,
10365
0
                                                 int &port) const {
10366
0
  return detail::get_remote_ip_and_port(sock_, ip, port);
10367
0
}
10368
10369
inline void SocketStream::get_local_ip_and_port(std::string &ip,
10370
0
                                                int &port) const {
10371
0
  return detail::get_local_ip_and_port(sock_, ip, port);
10372
0
}
10373
10374
0
inline socket_t SocketStream::socket() const { return sock_; }
10375
10376
0
inline time_t SocketStream::duration() const {
10377
0
  return std::chrono::duration_cast<std::chrono::milliseconds>(
10378
0
             std::chrono::steady_clock::now() - start_time_)
10379
0
      .count();
10380
0
}
10381
10382
0
inline void SocketStream::set_read_timeout(time_t sec, time_t usec) {
10383
0
  read_timeout_sec_ = sec;
10384
0
  read_timeout_usec_ = usec;
10385
0
}
10386
10387
// Buffer stream implementation
10388
0
inline bool BufferStream::is_readable() const { return true; }
10389
10390
0
inline bool BufferStream::wait_readable() const { return true; }
10391
10392
0
inline bool BufferStream::wait_writable() const { return true; }
10393
10394
0
inline ssize_t BufferStream::read(char *ptr, size_t size) {
10395
#if defined(_MSC_VER) && _MSC_VER < 1910
10396
  auto len_read = buffer._Copy_s(ptr, size, size, position);
10397
#else
10398
0
  auto len_read = buffer.copy(ptr, size, position);
10399
0
#endif
10400
0
  position += static_cast<size_t>(len_read);
10401
0
  return static_cast<ssize_t>(len_read);
10402
0
}
10403
10404
0
inline ssize_t BufferStream::write(const char *ptr, size_t size) {
10405
0
  buffer.append(ptr, size);
10406
0
  return static_cast<ssize_t>(size);
10407
0
}
10408
10409
inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
10410
0
                                                 int & /*port*/) const {}
10411
10412
inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
10413
0
                                                int & /*port*/) const {}
10414
10415
0
inline socket_t BufferStream::socket() const { return 0; }
10416
10417
0
inline time_t BufferStream::duration() const { return 0; }
10418
10419
0
inline const std::string &BufferStream::get_buffer() const { return buffer; }
10420
10421
inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)
10422
    : MatcherBase(pattern) {
10423
  constexpr const char marker[] = "/:";
10424
10425
  // One past the last ending position of a path param substring
10426
  std::size_t last_param_end = 0;
10427
10428
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10429
  // Needed to ensure that parameter names are unique during matcher
10430
  // construction
10431
  // If exceptions are disabled, only last duplicate path
10432
  // parameter will be set
10433
  std::unordered_set<std::string> param_name_set;
10434
#endif
10435
10436
  while (true) {
10437
    const auto marker_pos = pattern.find(
10438
        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
10439
    if (marker_pos == std::string::npos) { break; }
10440
10441
    static_fragments_.push_back(
10442
        pattern.substr(last_param_end, marker_pos - last_param_end + 1));
10443
10444
    const auto param_name_start = marker_pos + str_len(marker);
10445
10446
    auto sep_pos = pattern.find(separator, param_name_start);
10447
    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
10448
10449
    auto param_name =
10450
        pattern.substr(param_name_start, sep_pos - param_name_start);
10451
10452
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
10453
    if (param_name_set.find(param_name) != param_name_set.cend()) {
10454
      std::string msg = "Encountered path parameter '" + param_name +
10455
                        "' multiple times in route pattern '" + pattern + "'.";
10456
      throw std::invalid_argument(msg);
10457
    }
10458
#endif
10459
10460
    param_names_.push_back(std::move(param_name));
10461
10462
    last_param_end = sep_pos + 1;
10463
  }
10464
10465
  if (last_param_end < pattern.length()) {
10466
    static_fragments_.push_back(pattern.substr(last_param_end));
10467
  }
10468
}
10469
10470
0
inline bool PathParamsMatcher::match(Request &request) const {
10471
0
  request.matches = std::smatch();
10472
0
  request.path_params.clear();
10473
0
  request.path_params.reserve(param_names_.size());
10474
10475
  // One past the position at which the path matched the pattern last time
10476
0
  std::size_t starting_pos = 0;
10477
0
  for (size_t i = 0; i < static_fragments_.size(); ++i) {
10478
0
    const auto &fragment = static_fragments_[i];
10479
10480
0
    if (starting_pos + fragment.length() > request.path.length()) {
10481
0
      return false;
10482
0
    }
10483
10484
    // Avoid unnecessary allocation by using strncmp instead of substr +
10485
    // comparison
10486
0
    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),
10487
0
                     fragment.length()) != 0) {
10488
0
      return false;
10489
0
    }
10490
10491
0
    starting_pos += fragment.length();
10492
10493
    // Should only happen when we have a static fragment after a param
10494
    // Example: '/users/:id/subscriptions'
10495
    // The 'subscriptions' fragment here does not have a corresponding param
10496
0
    if (i >= param_names_.size()) { continue; }
10497
10498
0
    auto sep_pos = request.path.find(separator, starting_pos);
10499
0
    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }
10500
10501
0
    const auto &param_name = param_names_[i];
10502
10503
0
    request.path_params.emplace(
10504
0
        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
10505
10506
    // Mark everything up to '/' as matched
10507
0
    starting_pos = sep_pos + 1;
10508
0
  }
10509
  // Returns false if the path is longer than the pattern
10510
0
  return starting_pos >= request.path.length();
10511
0
}
10512
10513
0
inline bool RegexMatcher::match(Request &request) const {
10514
0
  request.path_params.clear();
10515
0
  return std::regex_match(request.path, request.matches, regex_);
10516
0
}
10517
10518
// Enclose IPv6 address in brackets if needed
10519
0
inline std::string prepare_host_string(const std::string &host) {
10520
0
  // Enclose IPv6 address in brackets (but not if already enclosed)
10521
0
  if (host.find(':') == std::string::npos ||
10522
0
      (!host.empty() && host[0] == '[')) {
10523
0
    // IPv4, hostname, or already bracketed IPv6
10524
0
    return host;
10525
0
  } else {
10526
0
    // IPv6 address without brackets
10527
0
    return "[" + host + "]";
10528
0
  }
10529
0
}
10530
10531
inline std::string make_host_and_port_string(const std::string &host, int port,
10532
0
                                             bool is_ssl) {
10533
0
  auto result = prepare_host_string(host);
10534
0
10535
0
  // Append port if not default
10536
0
  if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {
10537
0
    ; // do nothing
10538
0
  } else {
10539
0
    result += ":" + std::to_string(port);
10540
0
  }
10541
0
10542
0
  return result;
10543
0
}
10544
10545
// Create "host:port" string always including port number (for CONNECT method)
10546
inline std::string
10547
0
make_host_and_port_string_always_port(const std::string &host, int port) {
10548
0
  return prepare_host_string(host) + ":" + std::to_string(port);
10549
0
}
10550
10551
bool parse_no_proxy_entry(const std::string &token, NoProxyEntry &out);
10552
NormalizedTarget normalize_target(const std::string &host);
10553
bool ip_in_cidr(const IPBytes &ip, const IPBytes &net, int prefix_bits);
10554
bool host_matches_no_proxy(const NormalizedTarget &target,
10555
                           const std::vector<NoProxyEntry> &entries);
10556
10557
0
inline bool ip_in_cidr(const IPBytes &ip, const IPBytes &net, int prefix_bits) {
10558
0
  if (prefix_bits < 0 || prefix_bits > 128) { return false; }
10559
0
  if (prefix_bits == 0) { return true; }
10560
0
  int full_bytes = prefix_bits / 8;
10561
0
  int rem_bits = prefix_bits % 8;
10562
0
  if (full_bytes > 0 && std::memcmp(ip.data(), net.data(),
10563
0
                                    static_cast<size_t>(full_bytes)) != 0) {
10564
0
    return false;
10565
0
  }
10566
0
  if (rem_bits == 0) { return true; }
10567
0
  auto i = static_cast<size_t>(full_bytes);
10568
0
  auto mask = static_cast<uint8_t>(0xFFu << (8 - rem_bits));
10569
0
  return (ip[i] & mask) == (net[i] & mask);
10570
0
}
10571
10572
0
inline bool parse_no_proxy_entry(const std::string &token, NoProxyEntry &out) {
10573
0
  if (token.empty()) { return false; }
10574
0
10575
0
  if (token == "*") {
10576
0
    out.kind = NoProxyKind::Wildcard;
10577
0
    return true;
10578
0
  }
10579
0
10580
0
  auto slash = token.find('/');
10581
0
  std::string addr_part =
10582
0
      (slash == std::string::npos) ? token : token.substr(0, slash);
10583
0
  std::string prefix_part =
10584
0
      (slash == std::string::npos) ? std::string() : token.substr(slash + 1);
10585
0
10586
0
  // A bare slash or trailing-slash CIDR like "10.0.0.0/" is malformed;
10587
0
  // don't silently treat it as a /32 (or /128).
10588
0
  if (slash != std::string::npos && prefix_part.empty()) { return false; }
10589
0
10590
0
  // Accept the bracketed IPv6 form ("[::1]", "[fe80::]/10") as well as the
10591
0
  // bare form. Brackets have no meaning for IPv4, so skip the IPv4 attempt
10592
0
  // when brackets are present.
10593
0
  bool bracketed = addr_part.size() >= 2 && addr_part.front() == '[' &&
10594
0
                   addr_part.back() == ']';
10595
0
  if (bracketed) { addr_part = addr_part.substr(1, addr_part.size() - 2); }
10596
0
10597
0
  if (!bracketed) {
10598
0
    struct in_addr v4;
10599
0
    if (inet_pton(AF_INET, addr_part.c_str(), &v4) == 1) {
10600
0
      int prefix = 32;
10601
0
      if (!prefix_part.empty()) {
10602
0
        auto r = from_chars(prefix_part.data(),
10603
0
                            prefix_part.data() + prefix_part.size(), prefix);
10604
0
        if (r.ec != std::errc{} ||
10605
0
            r.ptr != prefix_part.data() + prefix_part.size()) {
10606
0
          return false;
10607
0
        }
10608
0
        if (prefix < 0 || prefix > 32) { return false; }
10609
0
      }
10610
0
      out.kind = NoProxyKind::IPv4Cidr;
10611
0
      std::memcpy(out.net.data(), &v4, sizeof(v4));
10612
0
      out.prefix_bits = prefix;
10613
0
      return true;
10614
0
    }
10615
0
  }
10616
0
10617
0
  struct in6_addr v6;
10618
0
  if (inet_pton(AF_INET6, addr_part.c_str(), &v6) == 1) {
10619
0
    int prefix = 128;
10620
0
    if (!prefix_part.empty()) {
10621
0
      auto r = from_chars(prefix_part.data(),
10622
0
                          prefix_part.data() + prefix_part.size(), prefix);
10623
0
      if (r.ec != std::errc{} ||
10624
0
          r.ptr != prefix_part.data() + prefix_part.size()) {
10625
0
        return false;
10626
0
      }
10627
0
      if (prefix < 0 || prefix > 128) { return false; }
10628
0
    }
10629
0
    out.kind = NoProxyKind::IPv6Cidr;
10630
0
    std::memcpy(out.net.data(), &v6, sizeof(v6));
10631
0
    out.prefix_bits = prefix;
10632
0
    return true;
10633
0
  }
10634
0
10635
0
  // Bracketed entries can only be IPv6. If the IPv6 parse above failed,
10636
0
  // the entry is malformed — don't fall through to the hostname branch.
10637
0
  if (bracketed) { return false; }
10638
0
10639
0
  // A '/' on a non-IP token means a CIDR prefix without an address. Reject.
10640
0
  if (slash != std::string::npos) { return false; }
10641
0
  // Port-specific entries (host:port) are not supported.
10642
0
  if (token.find(':') != std::string::npos) { return false; }
10643
0
10644
0
  std::string hostname = case_ignore::to_lower(token);
10645
0
  while (!hostname.empty() && hostname.front() == '.') {
10646
0
    hostname.erase(hostname.begin());
10647
0
  }
10648
0
  while (!hostname.empty() && hostname.back() == '.') {
10649
0
    hostname.pop_back();
10650
0
  }
10651
0
  if (hostname.empty()) { return false; }
10652
0
10653
0
  out.kind = NoProxyKind::HostnameSuffix;
10654
0
  out.hostname_pattern = std::move(hostname);
10655
0
  return true;
10656
0
}
10657
10658
0
inline NormalizedTarget normalize_target(const std::string &host) {
10659
0
  NormalizedTarget t;
10660
0
  std::string h = host;
10661
10662
0
  if (h.size() >= 2 && h.front() == '[' && h.back() == ']') {
10663
0
    h = h.substr(1, h.size() - 2);
10664
0
  }
10665
10666
  // Strip a single trailing dot so "example.com." canonicalizes to
10667
  // "example.com".
10668
0
  if (!h.empty() && h.back() == '.') { h.pop_back(); }
10669
10670
0
  t.hostname = case_ignore::to_lower(h);
10671
10672
0
  if (!t.hostname.empty()) {
10673
0
    struct in_addr v4;
10674
0
    struct in6_addr v6;
10675
0
    if (inet_pton(AF_INET, t.hostname.c_str(), &v4) == 1) {
10676
0
      t.is_ipv4 = true;
10677
0
      std::memcpy(t.ip.data(), &v4, sizeof(v4));
10678
0
    } else if (inet_pton(AF_INET6, t.hostname.c_str(), &v6) == 1) {
10679
0
      t.is_ipv6 = true;
10680
0
      std::memcpy(t.ip.data(), &v6, sizeof(v6));
10681
0
    }
10682
0
  }
10683
0
  return t;
10684
0
}
10685
10686
inline bool host_matches_no_proxy(const NormalizedTarget &target,
10687
0
                                  const std::vector<NoProxyEntry> &entries) {
10688
0
  if (target.hostname.empty()) { return false; }
10689
0
  for (const auto &e : entries) {
10690
0
    switch (e.kind) {
10691
0
    case NoProxyKind::Wildcard: return true;
10692
0
    case NoProxyKind::IPv4Cidr:
10693
0
      if (target.is_ipv4 && ip_in_cidr(target.ip, e.net, e.prefix_bits)) {
10694
0
        return true;
10695
0
      }
10696
0
      break;
10697
0
    case NoProxyKind::IPv6Cidr:
10698
0
      if (target.is_ipv6 && ip_in_cidr(target.ip, e.net, e.prefix_bits)) {
10699
0
        return true;
10700
0
      }
10701
0
      break;
10702
0
    case NoProxyKind::HostnameSuffix:
10703
0
      if (target.is_ipv4 || target.is_ipv6) { break; }
10704
0
      if (target.hostname == e.hostname_pattern) { return true; }
10705
      // Dot-boundary suffix match: prevents "evilexample.com" from matching
10706
      // an entry of "example.com".
10707
0
      if (target.hostname.size() > e.hostname_pattern.size() + 1) {
10708
0
        auto offset = target.hostname.size() - e.hostname_pattern.size();
10709
0
        if (target.hostname[offset - 1] == '.' &&
10710
0
            target.hostname.compare(offset, e.hostname_pattern.size(),
10711
0
                                    e.hostname_pattern) == 0) {
10712
0
          return true;
10713
0
        }
10714
0
      }
10715
0
      break;
10716
0
    }
10717
0
  }
10718
0
  return false;
10719
0
}
10720
10721
template <typename T>
10722
inline bool check_and_write_headers(Stream &strm, Headers &headers,
10723
0
                                    T header_writer, Error &error) {
10724
0
  for (const auto &h : headers) {
10725
0
    if (!detail::fields::is_field_name(h.first) ||
10726
0
        !detail::fields::is_field_value(h.second)) {
10727
0
      error = Error::InvalidHeaders;
10728
0
      return false;
10729
0
    }
10730
0
  }
10731
0
  if (header_writer(strm, headers) <= 0) {
10732
0
    error = Error::Write;
10733
0
    return false;
10734
0
  }
10735
0
  return true;
10736
0
}
10737
10738
} // namespace detail
10739
10740
/*
10741
 * Group 2 (continued): detail namespace - SSLSocketStream implementation
10742
 */
10743
10744
#ifdef CPPHTTPLIB_SSL_ENABLED
10745
namespace detail {
10746
10747
// SSL socket stream implementation
10748
inline SSLSocketStream::SSLSocketStream(
10749
    socket_t sock, tls::session_t session, time_t read_timeout_sec,
10750
    time_t read_timeout_usec, time_t write_timeout_sec,
10751
    time_t write_timeout_usec, time_t max_timeout_msec,
10752
    std::chrono::time_point<std::chrono::steady_clock> start_time)
10753
    : sock_(sock), session_(session), read_timeout_sec_(read_timeout_sec),
10754
      read_timeout_usec_(read_timeout_usec),
10755
      write_timeout_sec_(write_timeout_sec),
10756
      write_timeout_usec_(write_timeout_usec),
10757
      max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
10758
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
10759
  // Clear AUTO_RETRY for proper non-blocking I/O timeout handling
10760
  // Note: create_session() also clears this, but SSLClient currently
10761
  // uses ssl_new() which does not. Until full TLS API migration is complete,
10762
  // we need to ensure AUTO_RETRY is cleared here regardless of how the
10763
  // SSL session was created.
10764
  SSL_clear_mode(static_cast<SSL *>(session), SSL_MODE_AUTO_RETRY);
10765
#endif
10766
}
10767
10768
inline SSLSocketStream::~SSLSocketStream() = default;
10769
10770
inline bool SSLSocketStream::is_readable() const {
10771
  return tls::pending(session_) > 0;
10772
}
10773
10774
inline bool SSLSocketStream::wait_readable() const {
10775
  if (max_timeout_msec_ <= 0) {
10776
    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
10777
  }
10778
10779
  time_t read_timeout_sec;
10780
  time_t read_timeout_usec;
10781
  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
10782
                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);
10783
10784
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
10785
}
10786
10787
inline bool SSLSocketStream::wait_writable() const {
10788
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
10789
         !tls::is_peer_closed(session_, sock_);
10790
}
10791
10792
inline bool SSLSocketStream::is_peer_alive() const {
10793
  return !tls::is_peer_closed(session_, sock_);
10794
}
10795
10796
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
10797
  if (tls::pending(session_) > 0) {
10798
    tls::TlsError err;
10799
    auto ret = tls::read(session_, ptr, size, err);
10800
    if (ret == 0 || err.code == tls::ErrorCode::PeerClosed) {
10801
      error_ = Error::ConnectionClosed;
10802
    }
10803
    return ret;
10804
  } else if (wait_readable()) {
10805
    tls::TlsError err;
10806
    auto ret = tls::read(session_, ptr, size, err);
10807
    if (ret < 0) {
10808
      auto n = 1000;
10809
#ifdef _WIN32
10810
      while (--n >= 0 && (err.code == tls::ErrorCode::WantRead ||
10811
                          (err.code == tls::ErrorCode::SyscallError &&
10812
                           WSAGetLastError() == WSAETIMEDOUT))) {
10813
#else
10814
      while (--n >= 0 && err.code == tls::ErrorCode::WantRead) {
10815
#endif
10816
        if (tls::pending(session_) > 0) {
10817
          return tls::read(session_, ptr, size, err);
10818
        } else if (wait_readable()) {
10819
          std::this_thread::sleep_for(std::chrono::microseconds{10});
10820
          ret = tls::read(session_, ptr, size, err);
10821
          if (ret >= 0) { return ret; }
10822
        } else {
10823
          break;
10824
        }
10825
      }
10826
      assert(ret < 0);
10827
    } else if (ret == 0 || err.code == tls::ErrorCode::PeerClosed) {
10828
      error_ = Error::ConnectionClosed;
10829
    }
10830
    return ret;
10831
  } else {
10832
    error_ = Error::Timeout;
10833
    return -1;
10834
  }
10835
}
10836
10837
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
10838
  if (wait_writable()) {
10839
    auto handle_size =
10840
        std::min<size_t>(size, (std::numeric_limits<int>::max)());
10841
10842
    tls::TlsError err;
10843
    auto ret = tls::write(session_, ptr, handle_size, err);
10844
    if (ret < 0) {
10845
      auto n = 1000;
10846
#ifdef _WIN32
10847
      while (--n >= 0 && (err.code == tls::ErrorCode::WantWrite ||
10848
                          (err.code == tls::ErrorCode::SyscallError &&
10849
                           WSAGetLastError() == WSAETIMEDOUT))) {
10850
#else
10851
      while (--n >= 0 && err.code == tls::ErrorCode::WantWrite) {
10852
#endif
10853
        if (wait_writable()) {
10854
          std::this_thread::sleep_for(std::chrono::microseconds{10});
10855
          ret = tls::write(session_, ptr, handle_size, err);
10856
          if (ret >= 0) { return ret; }
10857
        } else {
10858
          break;
10859
        }
10860
      }
10861
      assert(ret < 0);
10862
    }
10863
    return ret;
10864
  }
10865
  return -1;
10866
}
10867
10868
inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
10869
                                                    int &port) const {
10870
  detail::get_remote_ip_and_port(sock_, ip, port);
10871
}
10872
10873
inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
10874
                                                   int &port) const {
10875
  detail::get_local_ip_and_port(sock_, ip, port);
10876
}
10877
10878
inline socket_t SSLSocketStream::socket() const { return sock_; }
10879
10880
inline time_t SSLSocketStream::duration() const {
10881
  return std::chrono::duration_cast<std::chrono::milliseconds>(
10882
             std::chrono::steady_clock::now() - start_time_)
10883
      .count();
10884
}
10885
10886
inline void SSLSocketStream::set_read_timeout(time_t sec, time_t usec) {
10887
  read_timeout_sec_ = sec;
10888
  read_timeout_usec_ = usec;
10889
}
10890
10891
} // namespace detail
10892
#endif // CPPHTTPLIB_SSL_ENABLED
10893
10894
/*
10895
 * Group 4: Server implementation
10896
 */
10897
10898
// HTTP server implementation
10899
inline Server::Server()
10900
    : new_task_queue([] {
10901
        return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT,
10902
                              CPPHTTPLIB_THREAD_POOL_MAX_COUNT);
10903
      }) {
10904
#ifndef _WIN32
10905
  signal(SIGPIPE, SIG_IGN);
10906
#endif
10907
}
10908
10909
0
inline Server::~Server() = default;
10910
10911
inline std::unique_ptr<detail::MatcherBase>
10912
0
Server::make_matcher(const std::string &pattern) {
10913
0
  if (pattern.find("/:") != std::string::npos) {
10914
0
    return detail::make_unique<detail::PathParamsMatcher>(pattern);
10915
0
  } else {
10916
0
    return detail::make_unique<detail::RegexMatcher>(pattern);
10917
0
  }
10918
0
}
10919
10920
0
inline Server &Server::Get(const std::string &pattern, Handler handler) {
10921
0
  return add_handler(get_handlers_, pattern, std::move(handler));
10922
0
}
10923
10924
0
inline Server &Server::Post(const std::string &pattern, Handler handler) {
10925
0
  return add_handler(post_handlers_, pattern, std::move(handler));
10926
0
}
10927
10928
inline Server &Server::Post(const std::string &pattern,
10929
0
                            HandlerWithContentReader handler) {
10930
0
  return add_handler(post_handlers_for_content_reader_, pattern,
10931
0
                     std::move(handler));
10932
0
}
10933
10934
0
inline Server &Server::Put(const std::string &pattern, Handler handler) {
10935
0
  return add_handler(put_handlers_, pattern, std::move(handler));
10936
0
}
10937
10938
inline Server &Server::Put(const std::string &pattern,
10939
0
                           HandlerWithContentReader handler) {
10940
0
  return add_handler(put_handlers_for_content_reader_, pattern,
10941
0
                     std::move(handler));
10942
0
}
10943
10944
0
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
10945
0
  return add_handler(patch_handlers_, pattern, std::move(handler));
10946
0
}
10947
10948
inline Server &Server::Patch(const std::string &pattern,
10949
0
                             HandlerWithContentReader handler) {
10950
0
  return add_handler(patch_handlers_for_content_reader_, pattern,
10951
0
                     std::move(handler));
10952
0
}
10953
10954
0
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
10955
0
  return add_handler(delete_handlers_, pattern, std::move(handler));
10956
0
}
10957
10958
inline Server &Server::Delete(const std::string &pattern,
10959
0
                              HandlerWithContentReader handler) {
10960
0
  return add_handler(delete_handlers_for_content_reader_, pattern,
10961
0
                     std::move(handler));
10962
0
}
10963
10964
0
inline Server &Server::Options(const std::string &pattern, Handler handler) {
10965
0
  return add_handler(options_handlers_, pattern, std::move(handler));
10966
0
}
10967
10968
inline Server &Server::WebSocket(const std::string &pattern,
10969
0
                                 WebSocketHandler handler) {
10970
0
  websocket_handlers_.push_back(
10971
0
      {make_matcher(pattern), std::move(handler), nullptr});
10972
0
  return *this;
10973
0
}
10974
10975
inline Server &Server::WebSocket(const std::string &pattern,
10976
                                 WebSocketHandler handler,
10977
0
                                 SubProtocolSelector sub_protocol_selector) {
10978
0
  websocket_handlers_.push_back({make_matcher(pattern), std::move(handler),
10979
0
                                 std::move(sub_protocol_selector)});
10980
0
  return *this;
10981
0
}
10982
10983
inline bool Server::set_base_dir(const std::string &dir,
10984
0
                                 const std::string &mount_point) {
10985
0
  return set_mount_point(mount_point, dir);
10986
0
}
10987
10988
inline bool Server::set_mount_point(const std::string &mount_point,
10989
0
                                    const std::string &dir, Headers headers) {
10990
0
  detail::FileStat stat(dir);
10991
0
  if (stat.is_dir()) {
10992
0
    std::string mnt = !mount_point.empty() ? mount_point : "/";
10993
0
    if (!mnt.empty() && mnt[0] == '/') {
10994
0
      std::string resolved_base;
10995
0
      if (detail::canonicalize_path(dir.c_str(), resolved_base)) {
10996
0
#if defined(_WIN32)
10997
0
        if (resolved_base.back() != '\\' && resolved_base.back() != '/') {
10998
0
          resolved_base += '\\';
10999
0
        }
11000
0
#else
11001
0
        if (resolved_base.back() != '/') { resolved_base += '/'; }
11002
0
#endif
11003
0
      }
11004
0
      base_dirs_.push_back(
11005
0
          {std::move(mnt), dir, std::move(resolved_base), std::move(headers)});
11006
0
      return true;
11007
0
    }
11008
0
  }
11009
0
  return false;
11010
0
}
11011
11012
0
inline bool Server::remove_mount_point(const std::string &mount_point) {
11013
0
  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
11014
0
    if (it->mount_point == mount_point) {
11015
0
      base_dirs_.erase(it);
11016
0
      return true;
11017
0
    }
11018
0
  }
11019
0
  return false;
11020
0
}
11021
11022
inline Server &
11023
Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
11024
0
                                                const std::string &mime) {
11025
0
  file_extension_and_mimetype_map_[ext] = mime;
11026
0
  return *this;
11027
0
}
11028
11029
0
inline Server &Server::set_default_file_mimetype(const std::string &mime) {
11030
0
  default_file_mimetype_ = mime;
11031
0
  return *this;
11032
0
}
11033
11034
0
inline Server &Server::set_file_request_handler(Handler handler) {
11035
0
  file_request_handler_ = std::move(handler);
11036
0
  return *this;
11037
0
}
11038
11039
inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
11040
0
                                              std::true_type) {
11041
0
  error_handler_ = std::move(handler);
11042
0
  return *this;
11043
0
}
11044
11045
inline Server &Server::set_error_handler_core(Handler handler,
11046
0
                                              std::false_type) {
11047
0
  error_handler_ = [handler](const Request &req, Response &res) {
11048
0
    handler(req, res);
11049
0
    return HandlerResponse::Handled;
11050
0
  };
11051
0
  return *this;
11052
0
}
11053
11054
0
inline Server &Server::set_exception_handler(ExceptionHandler handler) {
11055
0
  exception_handler_ = std::move(handler);
11056
0
  return *this;
11057
0
}
11058
11059
0
inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
11060
0
  pre_routing_handler_ = std::move(handler);
11061
0
  return *this;
11062
0
}
11063
11064
0
inline Server &Server::set_post_routing_handler(Handler handler) {
11065
0
  post_routing_handler_ = std::move(handler);
11066
0
  return *this;
11067
0
}
11068
11069
0
inline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {
11070
0
  pre_request_handler_ = std::move(handler);
11071
0
  return *this;
11072
0
}
11073
11074
0
inline Server &Server::set_logger(Logger logger) {
11075
0
  logger_ = std::move(logger);
11076
0
  return *this;
11077
0
}
11078
11079
0
inline Server &Server::set_error_logger(ErrorLogger error_logger) {
11080
0
  error_logger_ = std::move(error_logger);
11081
0
  return *this;
11082
0
}
11083
11084
0
inline Server &Server::set_pre_compression_logger(Logger logger) {
11085
0
  pre_compression_logger_ = std::move(logger);
11086
0
  return *this;
11087
0
}
11088
11089
inline Server &
11090
0
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
11091
0
  expect_100_continue_handler_ = std::move(handler);
11092
0
  return *this;
11093
0
}
11094
11095
0
inline Server &Server::set_address_family(int family) {
11096
0
  address_family_ = family;
11097
0
  return *this;
11098
0
}
11099
11100
0
inline Server &Server::set_tcp_nodelay(bool on) {
11101
0
  tcp_nodelay_ = on;
11102
0
  return *this;
11103
0
}
11104
11105
0
inline Server &Server::set_ipv6_v6only(bool on) {
11106
0
  ipv6_v6only_ = on;
11107
0
  return *this;
11108
0
}
11109
11110
0
inline Server &Server::set_socket_options(SocketOptions socket_options) {
11111
0
  socket_options_ = std::move(socket_options);
11112
0
  return *this;
11113
0
}
11114
11115
0
inline Server &Server::set_default_headers(Headers headers) {
11116
0
  default_headers_ = std::move(headers);
11117
0
  return *this;
11118
0
}
11119
11120
inline Server &Server::set_header_writer(
11121
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
11122
0
  header_writer_ = writer;
11123
0
  return *this;
11124
0
}
11125
11126
inline Server &
11127
0
Server::set_trusted_proxies(const std::vector<std::string> &proxies) {
11128
0
  trusted_proxies_ = proxies;
11129
0
  return *this;
11130
0
}
11131
11132
0
inline Server &Server::set_keep_alive_max_count(size_t count) {
11133
0
  keep_alive_max_count_ = count;
11134
0
  return *this;
11135
0
}
11136
11137
0
inline Server &Server::set_keep_alive_timeout(time_t sec) {
11138
0
  keep_alive_timeout_sec_ = sec;
11139
0
  return *this;
11140
0
}
11141
11142
template <class Rep, class Period>
11143
inline Server &Server::set_keep_alive_timeout(
11144
    const std::chrono::duration<Rep, Period> &duration) {
11145
  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t /*usec*/) {
11146
    set_keep_alive_timeout(sec);
11147
  });
11148
  return *this;
11149
}
11150
11151
0
inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
11152
0
  read_timeout_sec_ = sec;
11153
0
  read_timeout_usec_ = usec;
11154
0
  return *this;
11155
0
}
11156
11157
0
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
11158
0
  write_timeout_sec_ = sec;
11159
0
  write_timeout_usec_ = usec;
11160
0
  return *this;
11161
0
}
11162
11163
0
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
11164
0
  idle_interval_sec_ = sec;
11165
0
  idle_interval_usec_ = usec;
11166
0
  return *this;
11167
0
}
11168
11169
0
inline Server &Server::set_payload_max_length(size_t length) {
11170
0
  payload_max_length_ = length;
11171
0
  return *this;
11172
0
}
11173
11174
0
inline Server &Server::set_websocket_max_missed_pongs(int count) {
11175
0
  websocket_max_missed_pongs_ = count;
11176
0
  return *this;
11177
0
}
11178
11179
0
inline Server &Server::set_websocket_ping_interval(time_t sec) {
11180
0
  websocket_ping_interval_sec_ = sec;
11181
0
  return *this;
11182
0
}
11183
11184
template <class Rep, class Period>
11185
inline Server &Server::set_websocket_ping_interval(
11186
    const std::chrono::duration<Rep, Period> &duration) {
11187
  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t /*usec*/) {
11188
    set_websocket_ping_interval(sec);
11189
  });
11190
  return *this;
11191
}
11192
11193
inline bool Server::bind_to_port(const std::string &host, int port,
11194
0
                                 int socket_flags) {
11195
0
  auto ret = bind_internal(host, port, socket_flags);
11196
0
  if (ret == -1) { is_decommissioned = true; }
11197
0
  return ret >= 0;
11198
0
}
11199
0
inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
11200
0
  auto ret = bind_internal(host, 0, socket_flags);
11201
0
  if (ret == -1) { is_decommissioned = true; }
11202
0
  return ret;
11203
0
}
11204
11205
0
inline bool Server::listen_after_bind() { return listen_internal(); }
11206
11207
inline bool Server::listen(const std::string &host, int port,
11208
0
                           int socket_flags) {
11209
0
  return bind_to_port(host, port, socket_flags) && listen_internal();
11210
0
}
11211
11212
0
inline bool Server::is_running() const { return is_running_; }
11213
11214
0
inline void Server::wait_until_ready() const {
11215
0
  while (!is_running_ && !is_decommissioned) {
11216
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
11217
0
  }
11218
0
}
11219
11220
0
inline void Server::stop() noexcept {
11221
0
  if (is_running_) {
11222
0
    assert(svr_sock_ != INVALID_SOCKET);
11223
0
    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
11224
0
    detail::shutdown_socket(sock);
11225
0
    detail::close_socket(sock);
11226
0
  }
11227
0
  is_decommissioned = false;
11228
0
}
11229
11230
0
inline void Server::decommission() { is_decommissioned = true; }
11231
11232
0
inline bool Server::parse_request_line(const char *s, Request &req) const {
11233
0
  auto len = strlen(s);
11234
0
  if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
11235
0
  len -= 2;
11236
11237
0
  {
11238
0
    size_t count = 0;
11239
11240
0
    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
11241
0
      switch (count) {
11242
0
      case 0: req.method = std::string(b, e); break;
11243
0
      case 1: req.target = std::string(b, e); break;
11244
0
      case 2: req.version = std::string(b, e); break;
11245
0
      default: break;
11246
0
      }
11247
0
      count++;
11248
0
    });
11249
11250
0
    if (count != 3) { return false; }
11251
0
  }
11252
11253
0
  thread_local const std::set<std::string> methods{
11254
0
      "GET",     "HEAD",    "POST",  "PUT",   "DELETE",
11255
0
      "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
11256
11257
0
  if (methods.find(req.method) == methods.end()) {
11258
0
    output_error_log(Error::InvalidHTTPMethod, &req);
11259
0
    return false;
11260
0
  }
11261
11262
0
  if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
11263
0
    output_error_log(Error::InvalidHTTPVersion, &req);
11264
0
    return false;
11265
0
  }
11266
11267
0
  {
11268
    // Skip URL fragment
11269
0
    for (size_t i = 0; i < req.target.size(); i++) {
11270
0
      if (req.target[i] == '#') {
11271
0
        req.target.erase(i);
11272
0
        break;
11273
0
      }
11274
0
    }
11275
11276
0
    detail::divide(req.target, '?',
11277
0
                   [&](const char *lhs_data, std::size_t lhs_size,
11278
0
                       const char *rhs_data, std::size_t rhs_size) {
11279
0
                     req.path =
11280
0
                         decode_path_component(std::string(lhs_data, lhs_size));
11281
0
                     detail::parse_query_text(rhs_data, rhs_size, req.params);
11282
0
                   });
11283
0
  }
11284
11285
0
  return true;
11286
0
}
11287
11288
inline bool Server::write_response(Stream &strm, bool close_connection,
11289
0
                                   Request &req, Response &res) {
11290
  // NOTE: `req.ranges` should be empty, otherwise it will be applied
11291
  // incorrectly to the error content.
11292
0
  req.ranges.clear();
11293
0
  return write_response_core(strm, close_connection, req, res, false);
11294
0
}
11295
11296
inline bool Server::write_response_with_content(Stream &strm,
11297
                                                bool close_connection,
11298
                                                const Request &req,
11299
0
                                                Response &res) {
11300
0
  return write_response_core(strm, close_connection, req, res, true);
11301
0
}
11302
11303
inline bool Server::write_response_core(Stream &strm, bool close_connection,
11304
                                        const Request &req, Response &res,
11305
0
                                        bool need_apply_ranges) {
11306
0
  assert(res.status != -1);
11307
11308
0
  if (400 <= res.status && error_handler_ &&
11309
0
      error_handler_(req, res) == HandlerResponse::Handled) {
11310
0
    need_apply_ranges = true;
11311
0
  }
11312
11313
0
  std::string content_type;
11314
0
  std::string boundary;
11315
0
  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
11316
11317
  // Prepare additional headers
11318
0
  if (close_connection || req.get_header_value("Connection") == "close" ||
11319
0
      400 <= res.status) { // Don't leave connections open after errors
11320
0
    res.set_header("Connection", "close");
11321
0
  } else {
11322
0
    std::string s = "timeout=";
11323
0
    s += std::to_string(keep_alive_timeout_sec_);
11324
0
    s += ", max=";
11325
0
    s += std::to_string(keep_alive_max_count_);
11326
0
    res.set_header("Keep-Alive", s);
11327
0
  }
11328
11329
0
  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
11330
0
      !res.has_header("Content-Type")) {
11331
0
    res.set_header("Content-Type", "text/plain");
11332
0
  }
11333
11334
0
  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
11335
0
      !res.has_header("Content-Length")) {
11336
0
    res.set_header("Content-Length", "0");
11337
0
  }
11338
11339
0
  if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
11340
0
    res.set_header("Accept-Ranges", "bytes");
11341
0
  }
11342
11343
0
  if (post_routing_handler_) { post_routing_handler_(req, res); }
11344
11345
  // Response line and headers
11346
0
  detail::BufferStream bstrm;
11347
0
  if (!detail::write_response_line(bstrm, res.status)) { return false; }
11348
0
  if (header_writer_(bstrm, res.headers) <= 0) { return false; }
11349
11350
  // Combine small body with headers to reduce write syscalls
11351
0
  if (req.method != "HEAD" && !res.body.empty() && !res.content_provider_) {
11352
0
    bstrm.write(res.body.data(), res.body.size());
11353
0
  }
11354
11355
  // Log before writing to avoid race condition with client-side code that
11356
  // accesses logger-captured data immediately after receiving the response.
11357
0
  output_log(req, res);
11358
11359
  // Flush buffer
11360
0
  auto &data = bstrm.get_buffer();
11361
0
  if (!detail::write_data(strm, data.data(), data.size())) { return false; }
11362
11363
  // Streaming body
11364
0
  auto ret = true;
11365
0
  if (req.method != "HEAD" && res.content_provider_) {
11366
0
    if (write_content_with_provider(strm, req, res, boundary, content_type)) {
11367
0
      res.content_provider_success_ = true;
11368
0
    } else {
11369
0
      ret = false;
11370
0
    }
11371
0
  }
11372
11373
0
  return ret;
11374
0
}
11375
11376
inline bool
11377
Server::write_content_with_provider(Stream &strm, const Request &req,
11378
                                    Response &res, const std::string &boundary,
11379
0
                                    const std::string &content_type) {
11380
0
  auto is_shutting_down = [this]() {
11381
0
    return this->svr_sock_ == INVALID_SOCKET;
11382
0
  };
11383
11384
0
  if (res.content_length_ > 0) {
11385
0
    if (req.ranges.empty()) {
11386
0
      return detail::write_content(strm, res.content_provider_, 0,
11387
0
                                   res.content_length_, is_shutting_down);
11388
0
    } else if (req.ranges.size() == 1) {
11389
0
      auto offset_and_length = detail::get_range_offset_and_length(
11390
0
          req.ranges[0], res.content_length_);
11391
11392
0
      return detail::write_content(strm, res.content_provider_,
11393
0
                                   offset_and_length.first,
11394
0
                                   offset_and_length.second, is_shutting_down);
11395
0
    } else {
11396
0
      return detail::write_multipart_ranges_data(
11397
0
          strm, req, res, boundary, content_type, res.content_length_,
11398
0
          is_shutting_down);
11399
0
    }
11400
0
  } else {
11401
0
    if (res.is_chunked_content_provider_) {
11402
0
      auto type = detail::encoding_type(req, res);
11403
11404
0
      auto compressor = detail::make_compressor(type);
11405
0
      if (!compressor) {
11406
0
        compressor = detail::make_unique<detail::nocompressor>();
11407
0
      }
11408
11409
0
      return detail::write_content_chunked(strm, res.content_provider_,
11410
0
                                           is_shutting_down, *compressor);
11411
0
    } else {
11412
0
      return detail::write_content_without_length(strm, res.content_provider_,
11413
0
                                                  is_shutting_down);
11414
0
    }
11415
0
  }
11416
0
}
11417
11418
0
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
11419
0
  FormFields::iterator cur_field;
11420
0
  FormFiles::iterator cur_file;
11421
0
  auto is_text_field = false;
11422
0
  size_t count = 0;
11423
0
  if (read_content_core(
11424
0
          strm, req, res,
11425
          // Regular
11426
0
          [&](const char *buf, size_t n) {
11427
            // Prevent arithmetic overflow when checking sizes.
11428
            // Avoid computing (req.body.size() + n) directly because
11429
            // adding two unsigned `size_t` values can wrap around and
11430
            // produce a small result instead of indicating overflow.
11431
            // Instead, check using subtraction: ensure `n` does not
11432
            // exceed the remaining capacity `max_size() - size()`.
11433
0
            if (req.body.size() >= req.body.max_size() ||
11434
0
                n > req.body.max_size() - req.body.size()) {
11435
0
              return false;
11436
0
            }
11437
11438
            // Limit decompressed body size to payload_max_length_ to protect
11439
            // against "zip bomb" attacks where a small compressed payload
11440
            // decompresses to a massive size.
11441
0
            if (payload_max_length_ > 0 &&
11442
0
                (req.body.size() >= payload_max_length_ ||
11443
0
                 n > payload_max_length_ - req.body.size())) {
11444
0
              return false;
11445
0
            }
11446
11447
0
            req.body.append(buf, n);
11448
0
            return true;
11449
0
          },
11450
          // Multipart FormData
11451
0
          [&](const FormData &file) {
11452
0
            if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
11453
0
              output_error_log(Error::TooManyFormDataFiles, &req);
11454
0
              return false;
11455
0
            }
11456
11457
0
            if (file.filename.empty()) {
11458
0
              cur_field = req.form.fields.emplace(
11459
0
                  file.name, FormField{file.name, file.content, file.headers});
11460
0
              is_text_field = true;
11461
0
            } else {
11462
0
              cur_file = req.form.files.emplace(file.name, file);
11463
0
              is_text_field = false;
11464
0
            }
11465
0
            return true;
11466
0
          },
11467
0
          [&](const char *buf, size_t n) {
11468
0
            if (is_text_field) {
11469
0
              auto &content = cur_field->second.content;
11470
0
              if (content.size() + n > content.max_size()) { return false; }
11471
0
              content.append(buf, n);
11472
0
            } else {
11473
0
              auto &content = cur_file->second.content;
11474
0
              if (content.size() + n > content.max_size()) { return false; }
11475
0
              content.append(buf, n);
11476
0
            }
11477
0
            return true;
11478
0
          })) {
11479
0
    const auto &content_type = req.get_header_value("Content-Type");
11480
0
    if (detail::extract_media_type(content_type) ==
11481
0
        "application/x-www-form-urlencoded") {
11482
0
      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
11483
0
        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
11484
0
        output_error_log(Error::ExceedMaxPayloadSize, &req);
11485
0
        return false;
11486
0
      }
11487
0
      detail::parse_query_text(req.body, req.params);
11488
0
    }
11489
0
    return true;
11490
0
  }
11491
0
  return false;
11492
0
}
11493
11494
inline bool Server::read_content_with_content_receiver(
11495
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
11496
0
    FormDataHeader multipart_header, ContentReceiver multipart_receiver) {
11497
0
  return read_content_core(strm, req, res, std::move(receiver),
11498
0
                           std::move(multipart_header),
11499
0
                           std::move(multipart_receiver));
11500
0
}
11501
11502
inline bool Server::read_content_core(
11503
    Stream &strm, Request &req, Response &res, ContentReceiver receiver,
11504
0
    FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {
11505
0
  detail::FormDataParser multipart_form_data_parser;
11506
0
  ContentReceiverWithProgress out;
11507
11508
0
  if (req.is_multipart_form_data()) {
11509
0
    const auto &content_type = req.get_header_value("Content-Type");
11510
0
    std::string boundary;
11511
0
    if (!detail::parse_multipart_boundary(content_type, boundary)) {
11512
0
      res.status = StatusCode::BadRequest_400;
11513
0
      output_error_log(Error::MultipartParsing, &req);
11514
0
      return false;
11515
0
    }
11516
11517
0
    multipart_form_data_parser.set_boundary(std::move(boundary));
11518
0
    out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {
11519
0
      return multipart_form_data_parser.parse(buf, n, multipart_header,
11520
0
                                              multipart_receiver);
11521
0
    };
11522
0
  } else {
11523
0
    out = [receiver](const char *buf, size_t n, size_t /*off*/,
11524
0
                     size_t /*len*/) { return receiver(buf, n); };
11525
0
  }
11526
11527
  // RFC 9112 §6: no Transfer-Encoding and no Content-Length means no body.
11528
  // For non-SSL builds we still scan non-persistent connections for stray
11529
  // body bytes so the payload limit is enforced (413). On keep-alive,
11530
  // pending bytes may be the next request (issue #2450), so skip.
11531
0
#if !defined(CPPHTTPLIB_SSL_ENABLED)
11532
0
  if (!req.has_header("Content-Length") &&
11533
0
      !detail::is_chunked_transfer_encoding(req.headers)) {
11534
0
    if (!detail::is_connection_persistent(req) && payload_max_length_ > 0 &&
11535
0
        payload_max_length_ < (std::numeric_limits<size_t>::max)()) {
11536
0
      auto has_data = strm.is_readable();
11537
0
      if (!has_data) {
11538
0
        auto s = strm.socket();
11539
0
        if (s != INVALID_SOCKET) {
11540
0
          has_data = detail::select_read(s, 0, 0) > 0;
11541
0
        }
11542
0
      }
11543
0
      if (has_data) {
11544
0
        auto result =
11545
0
            detail::read_content_without_length(strm, payload_max_length_, out);
11546
0
        if (result == detail::ReadContentResult::PayloadTooLarge) {
11547
0
          res.status = StatusCode::PayloadTooLarge_413;
11548
0
          return false;
11549
0
        } else if (result != detail::ReadContentResult::Success) {
11550
0
          return false;
11551
0
        }
11552
0
        return true;
11553
0
      }
11554
0
    }
11555
0
    return true;
11556
0
  }
11557
#else
11558
  if (!req.has_header("Content-Length") &&
11559
      !detail::is_chunked_transfer_encoding(req.headers)) {
11560
    return true;
11561
  }
11562
#endif
11563
11564
0
  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
11565
0
                            out, true)) {
11566
0
    return false;
11567
0
  }
11568
11569
0
  req.body_consumed_ = true;
11570
11571
0
  if (req.is_multipart_form_data()) {
11572
0
    if (!multipart_form_data_parser.is_valid()) {
11573
0
      res.status = StatusCode::BadRequest_400;
11574
0
      output_error_log(Error::MultipartParsing, &req);
11575
0
      return false;
11576
0
    }
11577
0
  }
11578
11579
0
  return true;
11580
0
}
11581
11582
0
inline bool Server::handle_file_request(Request &req, Response &res) {
11583
0
  for (const auto &entry : base_dirs_) {
11584
    // Prefix match
11585
0
    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
11586
0
      std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
11587
0
      if (detail::is_valid_path(sub_path)) {
11588
0
        auto path = entry.base_dir + sub_path;
11589
0
        if (path.back() == '/') { path += "index.html"; }
11590
11591
        // Defense-in-depth: is_valid_path blocks ".." traversal in the URL,
11592
        // but symlinks/junctions can still escape the base directory.
11593
0
        if (!entry.resolved_base_dir.empty()) {
11594
0
          std::string resolved_path;
11595
0
          if (detail::canonicalize_path(path.c_str(), resolved_path) &&
11596
0
              !detail::is_path_within_base(resolved_path,
11597
0
                                           entry.resolved_base_dir)) {
11598
0
            res.status = StatusCode::Forbidden_403;
11599
0
            return true;
11600
0
          }
11601
0
        }
11602
11603
0
        detail::FileStat stat(path);
11604
11605
0
        if (stat.is_dir()) {
11606
0
          res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
11607
0
          return true;
11608
0
        }
11609
11610
0
        if (stat.is_file()) {
11611
0
          for (const auto &kv : entry.headers) {
11612
0
            res.set_header(kv.first, kv.second);
11613
0
          }
11614
11615
0
          auto etag = detail::compute_etag(stat);
11616
0
          if (!etag.empty()) { res.set_header("ETag", etag); }
11617
11618
0
          auto mtime = stat.mtime();
11619
11620
0
          auto last_modified = detail::file_mtime_to_http_date(mtime);
11621
0
          if (!last_modified.empty()) {
11622
0
            res.set_header("Last-Modified", last_modified);
11623
0
          }
11624
11625
0
          if (check_if_not_modified(req, res, etag, mtime)) { return true; }
11626
11627
0
          check_if_range(req, etag, mtime);
11628
11629
0
          auto mm = std::make_shared<detail::mmap>(path.c_str());
11630
0
          if (!mm->is_open()) {
11631
0
            output_error_log(Error::OpenFile, &req);
11632
0
            return false;
11633
0
          }
11634
11635
0
          res.set_content_provider(
11636
0
              mm->size(),
11637
0
              detail::find_content_type(path, file_extension_and_mimetype_map_,
11638
0
                                        default_file_mimetype_),
11639
0
              [mm](size_t offset, size_t length, DataSink &sink) -> bool {
11640
0
                sink.write(mm->data() + offset, length);
11641
0
                return true;
11642
0
              });
11643
11644
0
          if (req.method != "HEAD" && file_request_handler_) {
11645
0
            file_request_handler_(req, res);
11646
0
          }
11647
11648
0
          return true;
11649
0
        } else {
11650
0
          output_error_log(Error::OpenFile, &req);
11651
0
        }
11652
0
      }
11653
0
    }
11654
0
  }
11655
0
  return false;
11656
0
}
11657
11658
inline bool Server::check_if_not_modified(const Request &req, Response &res,
11659
                                          const std::string &etag,
11660
0
                                          time_t mtime) const {
11661
  // Handle conditional GET:
11662
  // 1. If-None-Match takes precedence (RFC 9110 Section 13.1.2)
11663
  // 2. If-Modified-Since is checked only when If-None-Match is absent
11664
0
  if (req.has_header("If-None-Match")) {
11665
0
    if (!etag.empty()) {
11666
0
      auto val = req.get_header_value("If-None-Match");
11667
11668
      // NOTE: We use exact string matching here. This works correctly
11669
      // because our server always generates weak ETags (W/"..."), and
11670
      // clients typically send back the same ETag they received.
11671
      // RFC 9110 Section 8.8.3.2 allows weak comparison for
11672
      // If-None-Match, where W/"x" and "x" would match, but this
11673
      // simplified implementation requires exact matches.
11674
0
      auto ret = detail::split_find(val.data(), val.data() + val.size(), ',',
11675
0
                                    [&](const char *b, const char *e) {
11676
0
                                      auto seg_len = static_cast<size_t>(e - b);
11677
0
                                      return (seg_len == 1 && *b == '*') ||
11678
0
                                             (seg_len == etag.size() &&
11679
0
                                              std::equal(b, e, etag.begin()));
11680
0
                                    });
11681
11682
0
      if (ret) {
11683
0
        res.status = StatusCode::NotModified_304;
11684
0
        return true;
11685
0
      }
11686
0
    }
11687
0
  } else if (req.has_header("If-Modified-Since")) {
11688
0
    auto val = req.get_header_value("If-Modified-Since");
11689
0
    auto t = detail::parse_http_date(val);
11690
11691
0
    if (t != static_cast<time_t>(-1) && mtime <= t) {
11692
0
      res.status = StatusCode::NotModified_304;
11693
0
      return true;
11694
0
    }
11695
0
  }
11696
0
  return false;
11697
0
}
11698
11699
inline bool Server::check_if_range(Request &req, const std::string &etag,
11700
0
                                   time_t mtime) const {
11701
  // Handle If-Range for partial content requests (RFC 9110
11702
  // Section 13.1.5). If-Range is only evaluated when Range header is
11703
  // present. If the validator matches, serve partial content; otherwise
11704
  // serve full content.
11705
0
  if (!req.ranges.empty() && req.has_header("If-Range")) {
11706
0
    auto val = req.get_header_value("If-Range");
11707
11708
0
    auto is_valid_range = [&]() {
11709
0
      if (detail::is_strong_etag(val)) {
11710
        // RFC 9110 Section 13.1.5: If-Range requires strong ETag
11711
        // comparison.
11712
0
        return (!etag.empty() && val == etag);
11713
0
      } else if (detail::is_weak_etag(val)) {
11714
        // Weak ETags are not valid for If-Range (RFC 9110 Section 13.1.5)
11715
0
        return false;
11716
0
      } else {
11717
        // HTTP-date comparison
11718
0
        auto t = detail::parse_http_date(val);
11719
0
        return (t != static_cast<time_t>(-1) && mtime <= t);
11720
0
      }
11721
0
    };
11722
11723
0
    if (!is_valid_range()) {
11724
      // Validator doesn't match: ignore Range and serve full content
11725
0
      req.ranges.clear();
11726
0
      return false;
11727
0
    }
11728
0
  }
11729
11730
0
  return true;
11731
0
}
11732
11733
inline socket_t
11734
Server::create_server_socket(const std::string &host, int port,
11735
                             int socket_flags,
11736
0
                             SocketOptions socket_options) const {
11737
0
  return detail::create_socket(
11738
0
      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
11739
0
      ipv6_v6only_, std::move(socket_options),
11740
0
      [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
11741
0
        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
11742
0
          output_error_log(Error::BindIPAddress, nullptr);
11743
0
          return false;
11744
0
        }
11745
0
        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
11746
0
          output_error_log(Error::Listen, nullptr);
11747
0
          return false;
11748
0
        }
11749
0
        return true;
11750
0
      });
11751
0
}
11752
11753
inline int Server::bind_internal(const std::string &host, int port,
11754
0
                                 int socket_flags) {
11755
0
  if (is_decommissioned) { return -1; }
11756
0
11757
0
  if (!is_valid()) { return -1; }
11758
0
11759
0
  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
11760
0
  if (svr_sock_ == INVALID_SOCKET) { return -1; }
11761
0
11762
0
  if (port == 0) {
11763
0
    struct sockaddr_storage addr;
11764
0
    socklen_t addr_len = sizeof(addr);
11765
0
    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
11766
0
                    &addr_len) == -1) {
11767
0
      output_error_log(Error::GetSockName, nullptr);
11768
0
      return -1;
11769
0
    }
11770
0
    if (addr.ss_family == AF_INET) {
11771
0
      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
11772
0
    } else if (addr.ss_family == AF_INET6) {
11773
0
      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
11774
0
    } else {
11775
0
      output_error_log(Error::UnsupportedAddressFamily, nullptr);
11776
0
      return -1;
11777
0
    }
11778
0
  } else {
11779
0
    return port;
11780
0
  }
11781
0
}
11782
11783
0
inline bool Server::listen_internal() {
11784
0
  if (is_decommissioned) { return false; }
11785
0
11786
0
  auto ret = true;
11787
0
  is_running_ = true;
11788
0
  auto se = detail::scope_exit([&]() { is_running_ = false; });
11789
0
11790
0
  {
11791
0
    std::unique_ptr<TaskQueue> task_queue(new_task_queue());
11792
0
11793
0
    while (svr_sock_ != INVALID_SOCKET) {
11794
0
#ifndef _WIN32
11795
0
      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
11796
0
#endif
11797
0
        auto val = detail::select_read(svr_sock_, idle_interval_sec_,
11798
0
                                       idle_interval_usec_);
11799
0
        if (val == 0) { // Timeout
11800
0
          task_queue->on_idle();
11801
0
          continue;
11802
0
        }
11803
0
#ifndef _WIN32
11804
0
      }
11805
0
#endif
11806
0
11807
0
#if defined _WIN32
11808
0
      // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
11809
0
      // OVERLAPPED
11810
0
      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
11811
0
#elif defined SOCK_CLOEXEC
11812
0
      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
11813
0
#else
11814
0
      socket_t sock = accept(svr_sock_, nullptr, nullptr);
11815
0
#endif
11816
0
11817
0
      if (sock == INVALID_SOCKET) {
11818
0
        if (errno == EMFILE) {
11819
0
          // The per-process limit of open file descriptors has been reached.
11820
0
          // Try to accept new connections after a short sleep.
11821
0
          std::this_thread::sleep_for(std::chrono::microseconds{1});
11822
0
          continue;
11823
0
        } else if (errno == EINTR || errno == EAGAIN) {
11824
0
          continue;
11825
0
        }
11826
0
        if (svr_sock_ != INVALID_SOCKET) {
11827
0
          detail::close_socket(svr_sock_);
11828
0
          ret = false;
11829
0
          output_error_log(Error::Connection, nullptr);
11830
0
        } else {
11831
0
          ; // The server socket was closed by user.
11832
0
        }
11833
0
        break;
11834
0
      }
11835
0
11836
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
11837
0
                                  read_timeout_sec_, read_timeout_usec_);
11838
0
      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
11839
0
                                  write_timeout_sec_, write_timeout_usec_);
11840
0
11841
0
      if (tcp_nodelay_) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
11842
0
11843
0
      if (!task_queue->enqueue(
11844
0
              [this, sock]() { process_and_close_socket(sock); })) {
11845
0
        output_error_log(Error::ResourceExhaustion, nullptr);
11846
0
        detail::shutdown_socket(sock);
11847
0
        detail::close_socket(sock);
11848
0
      }
11849
0
    }
11850
0
11851
0
    task_queue->shutdown();
11852
0
  }
11853
0
11854
0
  is_decommissioned = !ret;
11855
0
  return ret;
11856
0
}
11857
11858
0
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
11859
0
  if (pre_routing_handler_ &&
11860
0
      pre_routing_handler_(req, res) == HandlerResponse::Handled) {
11861
0
    return true;
11862
0
  }
11863
11864
  // File handler
11865
0
  if ((req.method == "GET" || req.method == "HEAD") &&
11866
0
      handle_file_request(req, res)) {
11867
0
    return true;
11868
0
  }
11869
11870
0
  if (detail::expect_content(req)) {
11871
    // Content reader handler
11872
0
    {
11873
      // Track whether the ContentReader was aborted due to the decompressed
11874
      // payload exceeding `payload_max_length_`.
11875
      // The user handler runs after the lambda returns, so we must restore the
11876
      // 413 status if the handler overwrites it.
11877
0
      bool content_reader_payload_too_large = false;
11878
11879
0
      ContentReader reader(
11880
0
          [&](ContentReceiver receiver) {
11881
0
            auto result = read_content_with_content_receiver(
11882
0
                strm, req, res, std::move(receiver), nullptr, nullptr);
11883
0
            if (!result) {
11884
0
              output_error_log(Error::Read, &req);
11885
0
              if (res.status == StatusCode::PayloadTooLarge_413) {
11886
0
                content_reader_payload_too_large = true;
11887
0
              }
11888
0
            }
11889
0
            return result;
11890
0
          },
11891
0
          [&](FormDataHeader header, ContentReceiver receiver) {
11892
0
            auto result = read_content_with_content_receiver(
11893
0
                strm, req, res, nullptr, std::move(header),
11894
0
                std::move(receiver));
11895
0
            if (!result) {
11896
0
              output_error_log(Error::Read, &req);
11897
0
              if (res.status == StatusCode::PayloadTooLarge_413) {
11898
0
                content_reader_payload_too_large = true;
11899
0
              }
11900
0
            }
11901
0
            return result;
11902
0
          });
11903
11904
0
      bool dispatched = false;
11905
0
      if (req.method == "POST") {
11906
0
        dispatched = dispatch_request_for_content_reader(
11907
0
            req, res, std::move(reader), post_handlers_for_content_reader_);
11908
0
      } else if (req.method == "PUT") {
11909
0
        dispatched = dispatch_request_for_content_reader(
11910
0
            req, res, std::move(reader), put_handlers_for_content_reader_);
11911
0
      } else if (req.method == "PATCH") {
11912
0
        dispatched = dispatch_request_for_content_reader(
11913
0
            req, res, std::move(reader), patch_handlers_for_content_reader_);
11914
0
      } else if (req.method == "DELETE") {
11915
0
        dispatched = dispatch_request_for_content_reader(
11916
0
            req, res, std::move(reader), delete_handlers_for_content_reader_);
11917
0
      }
11918
11919
0
      if (dispatched) {
11920
0
        if (content_reader_payload_too_large) {
11921
          // Enforce the limit: override any status the handler may have set
11922
          // and return false so the error path sends a plain 413 response.
11923
0
          res.status = StatusCode::PayloadTooLarge_413;
11924
0
          res.body.clear();
11925
0
          res.content_length_ = 0;
11926
0
          res.content_provider_ = nullptr;
11927
0
          return false;
11928
0
        }
11929
0
        return true;
11930
0
      }
11931
0
    }
11932
11933
    // Read content into `req.body`
11934
0
    if (!read_content(strm, req, res)) {
11935
0
      output_error_log(Error::Read, &req);
11936
0
      return false;
11937
0
    }
11938
0
  }
11939
11940
  // Regular handler
11941
0
  if (req.method == "GET" || req.method == "HEAD") {
11942
0
    return dispatch_request(req, res, get_handlers_);
11943
0
  } else if (req.method == "POST") {
11944
0
    return dispatch_request(req, res, post_handlers_);
11945
0
  } else if (req.method == "PUT") {
11946
0
    return dispatch_request(req, res, put_handlers_);
11947
0
  } else if (req.method == "DELETE") {
11948
0
    return dispatch_request(req, res, delete_handlers_);
11949
0
  } else if (req.method == "OPTIONS") {
11950
0
    return dispatch_request(req, res, options_handlers_);
11951
0
  } else if (req.method == "PATCH") {
11952
0
    return dispatch_request(req, res, patch_handlers_);
11953
0
  }
11954
11955
0
  res.status = StatusCode::BadRequest_400;
11956
0
  return false;
11957
0
}
11958
11959
inline bool Server::dispatch_request(Request &req, Response &res,
11960
0
                                     const Handlers &handlers) const {
11961
0
  for (const auto &x : handlers) {
11962
0
    const auto &matcher = x.first;
11963
0
    const auto &handler = x.second;
11964
11965
0
    if (matcher->match(req)) {
11966
0
      req.matched_route = matcher->pattern();
11967
0
      if (!pre_request_handler_ ||
11968
0
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
11969
0
        handler(req, res);
11970
0
      }
11971
0
      return true;
11972
0
    }
11973
0
  }
11974
0
  return false;
11975
0
}
11976
11977
inline void Server::apply_ranges(const Request &req, Response &res,
11978
                                 std::string &content_type,
11979
0
                                 std::string &boundary) const {
11980
0
  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
11981
0
    auto it = res.headers.find("Content-Type");
11982
0
    if (it != res.headers.end()) {
11983
0
      content_type = it->second;
11984
0
      res.headers.erase(it);
11985
0
    }
11986
11987
0
    boundary = detail::make_multipart_data_boundary();
11988
11989
0
    res.set_header("Content-Type",
11990
0
                   "multipart/byteranges; boundary=" + boundary);
11991
0
  }
11992
11993
0
  auto type = detail::encoding_type(req, res);
11994
11995
0
  if (res.body.empty()) {
11996
0
    if (res.content_length_ > 0) {
11997
0
      size_t length = 0;
11998
0
      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
11999
0
        length = res.content_length_;
12000
0
      } else if (req.ranges.size() == 1) {
12001
0
        auto offset_and_length = detail::get_range_offset_and_length(
12002
0
            req.ranges[0], res.content_length_);
12003
12004
0
        length = offset_and_length.second;
12005
12006
0
        auto content_range = detail::make_content_range_header_field(
12007
0
            offset_and_length, res.content_length_);
12008
0
        res.set_header("Content-Range", content_range);
12009
0
      } else {
12010
0
        length = detail::get_multipart_ranges_data_length(
12011
0
            req, boundary, content_type, res.content_length_);
12012
0
      }
12013
0
      res.set_header("Content-Length", std::to_string(length));
12014
0
    } else {
12015
0
      if (res.content_provider_) {
12016
0
        if (res.is_chunked_content_provider_) {
12017
0
          res.set_header("Transfer-Encoding", "chunked");
12018
0
          if (type != detail::EncodingType::None) {
12019
0
            res.set_header("Content-Encoding", detail::encoding_name(type));
12020
0
            res.set_header("Vary", "Accept-Encoding");
12021
0
          }
12022
0
        }
12023
0
      }
12024
0
    }
12025
0
  } else {
12026
0
    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
12027
0
      ;
12028
0
    } else if (req.ranges.size() == 1) {
12029
0
      auto offset_and_length =
12030
0
          detail::get_range_offset_and_length(req.ranges[0], res.body.size());
12031
0
      auto offset = offset_and_length.first;
12032
0
      auto length = offset_and_length.second;
12033
12034
0
      auto content_range = detail::make_content_range_header_field(
12035
0
          offset_and_length, res.body.size());
12036
0
      res.set_header("Content-Range", content_range);
12037
12038
0
      assert(offset + length <= res.body.size());
12039
0
      res.body = res.body.substr(offset, length);
12040
0
    } else {
12041
0
      std::string data;
12042
0
      detail::make_multipart_ranges_data(req, res, boundary, content_type,
12043
0
                                         res.body.size(), data);
12044
0
      res.body.swap(data);
12045
0
    }
12046
12047
0
    if (type != detail::EncodingType::None) {
12048
0
      output_pre_compression_log(req, res);
12049
12050
0
      if (auto compressor = detail::make_compressor(type)) {
12051
0
        std::string compressed;
12052
0
        if (compressor->compress(res.body.data(), res.body.size(), true,
12053
0
                                 [&](const char *data, size_t data_len) {
12054
0
                                   compressed.append(data, data_len);
12055
0
                                   return true;
12056
0
                                 })) {
12057
0
          res.body.swap(compressed);
12058
0
          res.set_header("Content-Encoding", detail::encoding_name(type));
12059
0
          res.set_header("Vary", "Accept-Encoding");
12060
0
        }
12061
0
      }
12062
0
    }
12063
12064
0
    auto length = std::to_string(res.body.size());
12065
0
    res.set_header("Content-Length", length);
12066
0
  }
12067
0
}
12068
12069
inline bool Server::dispatch_request_for_content_reader(
12070
    Request &req, Response &res, ContentReader content_reader,
12071
0
    const HandlersForContentReader &handlers) const {
12072
0
  for (const auto &x : handlers) {
12073
0
    const auto &matcher = x.first;
12074
0
    const auto &handler = x.second;
12075
12076
0
    if (matcher->match(req)) {
12077
0
      req.matched_route = matcher->pattern();
12078
0
      if (!pre_request_handler_ ||
12079
0
          pre_request_handler_(req, res) != HandlerResponse::Handled) {
12080
0
        handler(req, res, content_reader);
12081
0
      }
12082
0
      return true;
12083
0
    }
12084
0
  }
12085
0
  return false;
12086
0
}
12087
12088
inline std::string
12089
get_client_ip(const std::string &x_forwarded_for,
12090
0
              const std::vector<std::string> &trusted_proxies) {
12091
  // X-Forwarded-For is a comma-separated list per RFC 7239
12092
0
  std::vector<std::string> ip_list;
12093
0
  detail::split(x_forwarded_for.data(),
12094
0
                x_forwarded_for.data() + x_forwarded_for.size(), ',',
12095
0
                [&](const char *b, const char *e) {
12096
0
                  auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
12097
0
                  ip_list.emplace_back(std::string(b + r.first, b + r.second));
12098
0
                });
12099
12100
  // A malformed X-Forwarded-For (empty, comma-only, whitespace-only) yields
12101
  // no segments. Signal "no client IP derived" with an empty string so the
12102
  // caller can fall back to the connection-level remote address.
12103
0
  if (ip_list.empty()) { return std::string(); }
12104
12105
0
  for (size_t i = 0; i < ip_list.size(); ++i) {
12106
0
    auto ip = ip_list[i];
12107
12108
0
    auto is_trusted_proxy =
12109
0
        std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
12110
0
                    [&](const std::string &proxy) { return ip == proxy; });
12111
12112
0
    if (is_trusted_proxy) {
12113
0
      if (i == 0) {
12114
        // If the trusted proxy is the first IP, there's no preceding client IP
12115
0
        return ip;
12116
0
      } else {
12117
        // Return the IP immediately before the trusted proxy
12118
0
        return ip_list[i - 1];
12119
0
      }
12120
0
    }
12121
0
  }
12122
12123
  // If no trusted proxy is found, return the first IP in the list
12124
0
  return ip_list.front();
12125
0
}
12126
12127
inline bool
12128
Server::process_request(Stream &strm, const std::string &remote_addr,
12129
                        int remote_port, const std::string &local_addr,
12130
                        int local_port, bool close_connection,
12131
                        bool &connection_closed,
12132
                        const std::function<void(Request &)> &setup_request,
12133
0
                        bool *websocket_upgraded) {
12134
0
  std::array<char, 2048> buf{};
12135
12136
0
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12137
12138
  // Connection has been closed on client
12139
0
  if (!line_reader.getline()) { return false; }
12140
12141
0
  Request req;
12142
0
  req.start_time_ = std::chrono::steady_clock::now();
12143
0
  req.remote_addr = remote_addr;
12144
0
  req.remote_port = remote_port;
12145
0
  req.local_addr = local_addr;
12146
0
  req.local_port = local_port;
12147
12148
0
  Response res;
12149
0
  res.version = "HTTP/1.1";
12150
0
  res.headers = default_headers_;
12151
12152
  // Request line and headers
12153
0
  if (!parse_request_line(line_reader.ptr(), req)) {
12154
0
    res.status = StatusCode::BadRequest_400;
12155
0
    output_error_log(Error::InvalidRequestLine, &req);
12156
0
    return write_response(strm, close_connection, req, res);
12157
0
  }
12158
12159
  // Request headers
12160
0
  if (!detail::read_headers(strm, req.headers)) {
12161
0
    res.status = StatusCode::BadRequest_400;
12162
0
    output_error_log(Error::InvalidHeaders, &req);
12163
0
    return write_response(strm, close_connection, req, res);
12164
0
  }
12165
12166
  // RFC 9112 §6.3: Reject requests with both a non-zero Content-Length and
12167
  // any Transfer-Encoding to prevent request smuggling. Content-Length: 0 is
12168
  // tolerated for compatibility with existing clients.
12169
0
  if (req.get_header_value_u64("Content-Length") > 0 &&
12170
0
      req.has_header("Transfer-Encoding")) {
12171
0
    connection_closed = true;
12172
0
    res.status = StatusCode::BadRequest_400;
12173
0
    return write_response(strm, close_connection, req, res);
12174
0
  }
12175
12176
  // Check if the request URI doesn't exceed the limit
12177
0
  if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
12178
0
    connection_closed = true;
12179
0
    res.status = StatusCode::UriTooLong_414;
12180
0
    output_error_log(Error::ExceedUriMaxLength, &req);
12181
0
    return write_response(strm, close_connection, req, res);
12182
0
  }
12183
12184
0
  if (req.get_header_value("Connection") == "close") {
12185
0
    connection_closed = true;
12186
0
  }
12187
12188
0
  if (req.version == "HTTP/1.0" &&
12189
0
      req.get_header_value("Connection") != "Keep-Alive") {
12190
0
    connection_closed = true;
12191
0
  }
12192
12193
0
  if (!trusted_proxies_.empty() && req.has_header("X-Forwarded-For")) {
12194
0
    auto x_forwarded_for = req.get_header_value("X-Forwarded-For");
12195
0
    auto derived = get_client_ip(x_forwarded_for, trusted_proxies_);
12196
0
    req.remote_addr = derived.empty() ? remote_addr : derived;
12197
0
  } else {
12198
0
    req.remote_addr = remote_addr;
12199
0
  }
12200
0
  req.remote_port = remote_port;
12201
12202
0
  req.local_addr = local_addr;
12203
0
  req.local_port = local_port;
12204
12205
0
  if (req.has_header("Accept")) {
12206
0
    const auto &accept_header = req.get_header_value("Accept");
12207
0
    if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {
12208
0
      connection_closed = true;
12209
0
      res.status = StatusCode::BadRequest_400;
12210
0
      output_error_log(Error::HTTPParsing, &req);
12211
0
      return write_response(strm, close_connection, req, res);
12212
0
    }
12213
0
  }
12214
12215
0
  if (req.has_header("Range")) {
12216
0
    const auto &range_header_value = req.get_header_value("Range");
12217
0
    if (!detail::parse_range_header(range_header_value, req.ranges)) {
12218
0
      connection_closed = true;
12219
0
      res.status = StatusCode::RangeNotSatisfiable_416;
12220
0
      output_error_log(Error::InvalidRangeHeader, &req);
12221
0
      return write_response(strm, close_connection, req, res);
12222
0
    }
12223
0
  }
12224
12225
0
  if (setup_request) { setup_request(req); }
12226
12227
0
  if (req.get_header_value("Expect") == "100-continue") {
12228
0
    int status = StatusCode::Continue_100;
12229
0
    if (expect_100_continue_handler_) {
12230
0
      status = expect_100_continue_handler_(req, res);
12231
0
    }
12232
0
    switch (status) {
12233
0
    case StatusCode::Continue_100:
12234
0
    case StatusCode::ExpectationFailed_417:
12235
0
      detail::write_response_line(strm, status);
12236
0
      strm.write("\r\n");
12237
0
      break;
12238
0
    default:
12239
0
      connection_closed = true;
12240
0
      return write_response(strm, true, req, res);
12241
0
    }
12242
0
  }
12243
12244
  // Setup `is_connection_closed` method
12245
0
  auto sock = strm.socket();
12246
0
  req.is_connection_closed = [sock]() {
12247
0
    return !detail::is_socket_alive(sock);
12248
0
  };
12249
12250
  // WebSocket upgrade
12251
  // Check pre_routing_handler_ before upgrading so that authentication
12252
  // and other middleware can reject the request with an HTTP response
12253
  // (e.g., 401) before the protocol switches.
12254
0
  if (detail::is_websocket_upgrade(req)) {
12255
0
    if (pre_routing_handler_ &&
12256
0
        pre_routing_handler_(req, res) == HandlerResponse::Handled) {
12257
0
      if (res.status == -1) { res.status = StatusCode::OK_200; }
12258
0
      return write_response(strm, close_connection, req, res);
12259
0
    }
12260
    // Find matching WebSocket handler
12261
0
    for (const auto &entry : websocket_handlers_) {
12262
0
      if (entry.matcher->match(req)) {
12263
        // Compute accept key
12264
0
        auto client_key = req.get_header_value("Sec-WebSocket-Key");
12265
0
        auto accept_key = detail::websocket_accept_key(client_key);
12266
12267
        // Negotiate subprotocol
12268
0
        std::string selected_subprotocol;
12269
0
        if (entry.sub_protocol_selector) {
12270
0
          auto protocol_header = req.get_header_value("Sec-WebSocket-Protocol");
12271
0
          if (!protocol_header.empty()) {
12272
0
            std::vector<std::string> protocols;
12273
0
            std::istringstream iss(protocol_header);
12274
0
            std::string token;
12275
0
            while (std::getline(iss, token, ',')) {
12276
              // Trim whitespace
12277
0
              auto start = token.find_first_not_of(' ');
12278
0
              auto end = token.find_last_not_of(' ');
12279
0
              if (start != std::string::npos) {
12280
0
                protocols.push_back(token.substr(start, end - start + 1));
12281
0
              }
12282
0
            }
12283
0
            selected_subprotocol = entry.sub_protocol_selector(protocols);
12284
0
          }
12285
0
        }
12286
12287
        // Send 101 Switching Protocols
12288
0
        std::string handshake_response = "HTTP/1.1 101 Switching Protocols\r\n"
12289
0
                                         "Upgrade: websocket\r\n"
12290
0
                                         "Connection: Upgrade\r\n"
12291
0
                                         "Sec-WebSocket-Accept: " +
12292
0
                                         accept_key + "\r\n";
12293
0
        if (!selected_subprotocol.empty()) {
12294
0
          if (!detail::fields::is_field_value(selected_subprotocol)) {
12295
0
            return false;
12296
0
          }
12297
0
          handshake_response +=
12298
0
              "Sec-WebSocket-Protocol: " + selected_subprotocol + "\r\n";
12299
0
        }
12300
0
        handshake_response += "\r\n";
12301
0
        if (strm.write(handshake_response.data(), handshake_response.size()) <
12302
0
            0) {
12303
0
          return false;
12304
0
        }
12305
12306
0
        connection_closed = true;
12307
0
        if (websocket_upgraded) { *websocket_upgraded = true; }
12308
12309
0
        {
12310
          // Use WebSocket-specific read timeout instead of HTTP timeout
12311
0
          strm.set_read_timeout(CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND, 0);
12312
0
          ws::WebSocket ws(strm, req, true, websocket_ping_interval_sec_,
12313
0
                           websocket_max_missed_pongs_);
12314
0
          entry.handler(req, ws);
12315
0
        }
12316
0
        return true;
12317
0
      }
12318
0
    }
12319
    // No matching handler - fall through to 404
12320
0
  }
12321
12322
  // Routing
12323
0
  auto routed = false;
12324
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
12325
  routed = routing(req, res, strm);
12326
#else
12327
0
  try {
12328
0
    routed = routing(req, res, strm);
12329
0
  } catch (std::exception &) {
12330
0
    if (exception_handler_) {
12331
0
      auto ep = std::current_exception();
12332
0
      exception_handler_(req, res, ep);
12333
0
      routed = true;
12334
0
    } else {
12335
0
      res.status = StatusCode::InternalServerError_500;
12336
0
    }
12337
0
  } catch (...) {
12338
0
    if (exception_handler_) {
12339
0
      auto ep = std::current_exception();
12340
0
      exception_handler_(req, res, ep);
12341
0
      routed = true;
12342
0
    } else {
12343
0
      res.status = StatusCode::InternalServerError_500;
12344
0
    }
12345
0
  }
12346
0
#endif
12347
0
  auto ret = false;
12348
0
  if (routed) {
12349
0
    if (res.status == -1) {
12350
0
      res.status = req.ranges.empty() ? StatusCode::OK_200
12351
0
                                      : StatusCode::PartialContent_206;
12352
0
    }
12353
12354
    // Serve file content by using a content provider
12355
0
    auto file_open_error = false;
12356
0
    if (!res.file_content_path_.empty()) {
12357
0
      const auto &path = res.file_content_path_;
12358
0
      auto mm = std::make_shared<detail::mmap>(path.c_str());
12359
0
      if (!mm->is_open()) {
12360
0
        res.body.clear();
12361
0
        res.content_length_ = 0;
12362
0
        res.content_provider_ = nullptr;
12363
0
        res.status = StatusCode::NotFound_404;
12364
0
        output_error_log(Error::OpenFile, &req);
12365
0
        file_open_error = true;
12366
0
      } else {
12367
0
        auto content_type = res.file_content_content_type_;
12368
0
        if (content_type.empty()) {
12369
0
          content_type = detail::find_content_type(
12370
0
              path, file_extension_and_mimetype_map_, default_file_mimetype_);
12371
0
        }
12372
12373
0
        res.set_content_provider(
12374
0
            mm->size(), content_type,
12375
0
            [mm](size_t offset, size_t length, DataSink &sink) -> bool {
12376
0
              sink.write(mm->data() + offset, length);
12377
0
              return true;
12378
0
            });
12379
0
      }
12380
0
    }
12381
12382
0
    if (file_open_error) {
12383
0
      ret = write_response(strm, close_connection, req, res);
12384
0
    } else if (detail::range_error(req, res)) {
12385
0
      res.body.clear();
12386
0
      res.content_length_ = 0;
12387
0
      res.content_provider_ = nullptr;
12388
0
      res.status = StatusCode::RangeNotSatisfiable_416;
12389
0
      ret = write_response(strm, close_connection, req, res);
12390
0
    } else {
12391
0
      ret = write_response_with_content(strm, close_connection, req, res);
12392
0
    }
12393
0
  } else {
12394
0
    if (res.status == -1) { res.status = StatusCode::NotFound_404; }
12395
0
    ret = write_response(strm, close_connection, req, res);
12396
0
  }
12397
12398
  // Drain any unconsumed framed body to prevent request smuggling on
12399
  // keep-alive. Without framing there is no body to drain — reading would
12400
  // consume the next request (issue #2450).
12401
0
  if (!req.body_consumed_ && detail::has_framed_body(req)) {
12402
0
    int dummy_status;
12403
0
    if (!detail::read_content(
12404
0
            strm, req, payload_max_length_, dummy_status, nullptr,
12405
0
            [](const char *, size_t, size_t, size_t) { return true; }, false)) {
12406
0
      connection_closed = true;
12407
0
    }
12408
0
  }
12409
12410
0
  return ret;
12411
0
}
12412
12413
0
inline bool Server::is_valid() const { return true; }
12414
12415
0
inline bool Server::process_and_close_socket(socket_t sock) {
12416
0
  std::string remote_addr;
12417
0
  int remote_port = 0;
12418
0
  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
12419
12420
0
  std::string local_addr;
12421
0
  int local_port = 0;
12422
0
  detail::get_local_ip_and_port(sock, local_addr, local_port);
12423
12424
0
  bool websocket_upgraded = false;
12425
0
  auto ret = detail::process_server_socket(
12426
0
      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
12427
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12428
0
      write_timeout_usec_,
12429
0
      [&](Stream &strm, bool close_connection, bool &connection_closed) {
12430
0
        return process_request(strm, remote_addr, remote_port, local_addr,
12431
0
                               local_port, close_connection, connection_closed,
12432
0
                               nullptr, &websocket_upgraded);
12433
0
      });
12434
12435
0
  detail::shutdown_socket(sock);
12436
0
  detail::close_socket(sock);
12437
0
  return ret;
12438
0
}
12439
12440
0
inline void Server::output_log(const Request &req, const Response &res) const {
12441
0
  if (logger_) {
12442
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
12443
0
    logger_(req, res);
12444
0
  }
12445
0
}
12446
12447
inline void Server::output_pre_compression_log(const Request &req,
12448
0
                                               const Response &res) const {
12449
0
  if (pre_compression_logger_) {
12450
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
12451
0
    pre_compression_logger_(req, res);
12452
0
  }
12453
0
}
12454
12455
inline void Server::output_error_log(const Error &err,
12456
0
                                     const Request *req) const {
12457
0
  if (error_logger_) {
12458
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
12459
0
    error_logger_(err, req);
12460
0
  }
12461
0
}
12462
12463
/*
12464
 * Group 5: ClientImpl and Client (Universal) implementation
12465
 */
12466
// HTTP client implementation
12467
inline ClientImpl::ClientImpl(const std::string &host)
12468
    : ClientImpl(host, 80, std::string(), std::string()) {}
12469
12470
inline ClientImpl::ClientImpl(const std::string &host, int port)
12471
    : ClientImpl(host, port, std::string(), std::string()) {}
12472
12473
inline ClientImpl::ClientImpl(const std::string &host, int port,
12474
                              const std::string &client_cert_path,
12475
                              const std::string &client_key_path)
12476
    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
12477
      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
12478
12479
0
inline ClientImpl::~ClientImpl() {
12480
  // Wait until all the requests in flight are handled.
12481
0
  size_t retry_count = 10;
12482
0
  while (retry_count-- > 0) {
12483
0
    {
12484
0
      std::lock_guard<std::mutex> guard(socket_mutex_);
12485
0
      if (socket_requests_in_flight_ == 0) { break; }
12486
0
    }
12487
0
    std::this_thread::sleep_for(std::chrono::milliseconds{1});
12488
0
  }
12489
12490
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
12491
0
  shutdown_socket(socket_);
12492
0
  close_socket(socket_);
12493
0
}
12494
12495
0
inline bool ClientImpl::is_valid() const { return true; }
12496
12497
0
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
12498
0
  client_cert_path_ = rhs.client_cert_path_;
12499
0
  client_key_path_ = rhs.client_key_path_;
12500
0
  connection_timeout_sec_ = rhs.connection_timeout_sec_;
12501
0
  read_timeout_sec_ = rhs.read_timeout_sec_;
12502
0
  read_timeout_usec_ = rhs.read_timeout_usec_;
12503
0
  write_timeout_sec_ = rhs.write_timeout_sec_;
12504
0
  write_timeout_usec_ = rhs.write_timeout_usec_;
12505
0
  max_timeout_msec_ = rhs.max_timeout_msec_;
12506
0
  basic_auth_username_ = rhs.basic_auth_username_;
12507
0
  basic_auth_password_ = rhs.basic_auth_password_;
12508
0
  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
12509
0
  keep_alive_ = rhs.keep_alive_;
12510
0
  follow_location_ = rhs.follow_location_;
12511
0
  path_encode_ = rhs.path_encode_;
12512
0
  address_family_ = rhs.address_family_;
12513
0
  tcp_nodelay_ = rhs.tcp_nodelay_;
12514
0
  ipv6_v6only_ = rhs.ipv6_v6only_;
12515
0
  socket_options_ = rhs.socket_options_;
12516
0
  compress_ = rhs.compress_;
12517
0
  decompress_ = rhs.decompress_;
12518
0
  payload_max_length_ = rhs.payload_max_length_;
12519
0
  has_payload_max_length_ = rhs.has_payload_max_length_;
12520
0
  interface_ = rhs.interface_;
12521
0
  proxy_host_ = rhs.proxy_host_;
12522
0
  proxy_port_ = rhs.proxy_port_;
12523
0
  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
12524
0
  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
12525
0
  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
12526
0
  no_proxy_entries_ = rhs.no_proxy_entries_;
12527
0
  logger_ = rhs.logger_;
12528
0
  error_logger_ = rhs.error_logger_;
12529
0
12530
0
#ifdef CPPHTTPLIB_SSL_ENABLED
12531
0
  digest_auth_username_ = rhs.digest_auth_username_;
12532
0
  digest_auth_password_ = rhs.digest_auth_password_;
12533
0
  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
12534
0
  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
12535
0
  ca_cert_file_path_ = rhs.ca_cert_file_path_;
12536
0
  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
12537
0
  server_certificate_verification_ = rhs.server_certificate_verification_;
12538
0
  server_hostname_verification_ = rhs.server_hostname_verification_;
12539
0
#endif
12540
0
}
12541
12542
inline bool
12543
0
ClientImpl::is_proxy_enabled_for_host(const std::string &host) const {
12544
0
  if (proxy_host_.empty() || proxy_port_ == -1) { return false; }
12545
0
  if (no_proxy_entries_.empty()) { return true; }
12546
  // host_ is const so its normalized form is invariant; cache it. The
12547
  // cross-host path (setup_redirect_client passing next_host) re-normalizes.
12548
0
  if (host == host_) {
12549
0
    if (!host_normalized_valid_) {
12550
0
      host_normalized_ = detail::normalize_target(host_);
12551
0
      host_normalized_valid_ = true;
12552
0
    }
12553
0
    return !detail::host_matches_no_proxy(host_normalized_, no_proxy_entries_);
12554
0
  }
12555
0
  auto target = detail::normalize_target(host);
12556
0
  return !detail::host_matches_no_proxy(target, no_proxy_entries_);
12557
0
}
12558
12559
0
inline socket_t ClientImpl::create_client_socket(Error &error) const {
12560
0
  if (is_proxy_enabled_for_host(host_)) {
12561
0
    return detail::create_client_socket(
12562
0
        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
12563
0
        ipv6_v6only_, socket_options_, connection_timeout_sec_,
12564
0
        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
12565
0
        write_timeout_sec_, write_timeout_usec_, interface_, error);
12566
0
  }
12567
12568
  // Check is custom IP specified for host_
12569
0
  std::string ip;
12570
0
  auto it = addr_map_.find(host_);
12571
0
  if (it != addr_map_.end()) { ip = it->second; }
12572
12573
0
  return detail::create_client_socket(
12574
0
      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
12575
0
      socket_options_, connection_timeout_sec_, connection_timeout_usec_,
12576
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12577
0
      write_timeout_usec_, interface_, error);
12578
0
}
12579
12580
inline bool ClientImpl::create_and_connect_socket(Socket &socket,
12581
0
                                                  Error &error) {
12582
0
  auto sock = create_client_socket(error);
12583
0
  if (sock == INVALID_SOCKET) { return false; }
12584
0
  socket.sock = sock;
12585
0
  return true;
12586
0
}
12587
12588
0
inline bool ClientImpl::ensure_socket_connection(Socket &socket, Error &error) {
12589
0
  return create_and_connect_socket(socket, error);
12590
0
}
12591
12592
inline bool ClientImpl::setup_proxy_connection(
12593
    Socket & /*socket*/,
12594
    std::chrono::time_point<std::chrono::steady_clock> /*start_time*/,
12595
0
    Response & /*res*/, bool & /*success*/, Error & /*error*/) {
12596
0
  return true;
12597
0
}
12598
12599
inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
12600
0
                                     bool /*shutdown_gracefully*/) {
12601
  // If there are any requests in flight from threads other than us, then it's
12602
  // a thread-unsafe race because individual ssl* objects are not thread-safe.
12603
0
  assert(socket_requests_in_flight_ == 0 ||
12604
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
12605
0
}
12606
12607
0
inline void ClientImpl::shutdown_socket(Socket &socket) const {
12608
0
  if (socket.sock == INVALID_SOCKET) { return; }
12609
0
  detail::shutdown_socket(socket.sock);
12610
0
}
12611
12612
0
inline void ClientImpl::close_socket(Socket &socket) {
12613
  // If there are requests in flight in another thread, usually closing
12614
  // the socket will be fine and they will simply receive an error when
12615
  // using the closed socket, but it is still a bug since rarely the OS
12616
  // may reassign the socket id to be used for a new socket, and then
12617
  // suddenly they will be operating on a live socket that is different
12618
  // than the one they intended!
12619
0
  assert(socket_requests_in_flight_ == 0 ||
12620
0
         socket_requests_are_from_thread_ == std::this_thread::get_id());
12621
12622
  // It is also a bug if this happens while SSL is still active
12623
#ifdef CPPHTTPLIB_SSL_ENABLED
12624
  assert(socket.ssl == nullptr);
12625
#endif
12626
12627
0
  if (socket.sock == INVALID_SOCKET) { return; }
12628
0
  detail::close_socket(socket.sock);
12629
0
  socket.sock = INVALID_SOCKET;
12630
0
}
12631
12632
0
inline void ClientImpl::disconnect(bool gracefully) {
12633
0
  shutdown_ssl(socket_, gracefully);
12634
0
  shutdown_socket(socket_);
12635
0
  close_socket(socket_);
12636
0
}
12637
12638
inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
12639
                                           Response &res,
12640
0
                                           bool skip_100_continue) const {
12641
0
  std::array<char, 2048> buf{};
12642
0
12643
0
  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
12644
0
12645
0
  if (!line_reader.getline()) { return false; }
12646
0
12647
0
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
12648
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
12649
0
#else
12650
0
  thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
12651
0
#endif
12652
0
12653
0
  std::cmatch m;
12654
0
  if (!std::regex_match(line_reader.ptr(), m, re)) {
12655
0
    return req.method == "CONNECT";
12656
0
  }
12657
0
  res.version = std::string(m[1]);
12658
0
  res.status = std::stoi(std::string(m[2]));
12659
0
  res.reason = std::string(m[3]);
12660
0
12661
0
  // Ignore '100 Continue' (only when not using Expect: 100-continue explicitly)
12662
0
  while (skip_100_continue && res.status == StatusCode::Continue_100) {
12663
0
    if (!line_reader.getline()) { return false; } // CRLF
12664
0
    if (!line_reader.getline()) { return false; } // next response line
12665
0
12666
0
    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
12667
0
    res.version = std::string(m[1]);
12668
0
    res.status = std::stoi(std::string(m[2]));
12669
0
    res.reason = std::string(m[3]);
12670
0
  }
12671
0
12672
0
  return true;
12673
0
}
12674
12675
0
inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
12676
0
  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
12677
0
  auto ret = send_(req, res, error);
12678
0
  if (error == Error::SSLPeerCouldBeClosed_) {
12679
0
    assert(!ret);
12680
0
    ret = send_(req, res, error);
12681
0
    // If still failing with SSLPeerCouldBeClosed_, convert to Read error
12682
0
    if (error == Error::SSLPeerCouldBeClosed_) { error = Error::Read; }
12683
0
  }
12684
0
  return ret;
12685
0
}
12686
12687
0
inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
12688
0
  {
12689
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
12690
0
12691
0
    // Set this to false immediately - if it ever gets set to true by the end
12692
0
    // of the request, we know another thread instructed us to close the
12693
0
    // socket.
12694
0
    socket_should_be_closed_when_request_is_done_ = false;
12695
0
12696
0
    auto is_alive = false;
12697
0
    if (socket_.is_open()) {
12698
0
      is_alive = detail::is_socket_alive(socket_.sock);
12699
0
12700
0
#ifdef CPPHTTPLIB_SSL_ENABLED
12701
0
      if (is_alive && is_ssl()) {
12702
0
        if (tls::is_peer_closed(socket_.ssl, socket_.sock)) {
12703
0
          is_alive = false;
12704
0
        }
12705
0
      }
12706
0
#endif
12707
0
12708
0
      if (!is_alive) {
12709
0
        // Peer seems gone — non-graceful shutdown to avoid SIGPIPE.
12710
0
        disconnect(/*gracefully=*/false);
12711
0
      }
12712
0
    }
12713
0
12714
0
    if (!is_alive) {
12715
0
      if (!ensure_socket_connection(socket_, error)) {
12716
0
        output_error_log(error, &req);
12717
0
        return false;
12718
0
      }
12719
0
12720
0
      {
12721
0
        auto success = true;
12722
0
        if (!setup_proxy_connection(socket_, req.start_time_, res, success,
12723
0
                                    error)) {
12724
0
          if (!success) { output_error_log(error, &req); }
12725
0
          return success;
12726
0
        }
12727
0
      }
12728
0
    }
12729
0
12730
0
    // Mark the current socket as being in use so that it cannot be closed by
12731
0
    // anyone else while this request is ongoing, even though we will be
12732
0
    // releasing the mutex.
12733
0
    if (socket_requests_in_flight_ > 1) {
12734
0
      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
12735
0
    }
12736
0
    socket_requests_in_flight_ += 1;
12737
0
    socket_requests_are_from_thread_ = std::this_thread::get_id();
12738
0
  }
12739
0
12740
0
  for (const auto &header : default_headers_) {
12741
0
    if (req.headers.find(header.first) == req.headers.end()) {
12742
0
      req.headers.insert(header);
12743
0
    }
12744
0
  }
12745
0
12746
0
  auto ret = false;
12747
0
  auto close_connection = !keep_alive_;
12748
0
12749
0
  auto se = detail::scope_exit([&]() {
12750
0
    // Briefly lock mutex in order to mark that a request is no longer ongoing
12751
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
12752
0
    socket_requests_in_flight_ -= 1;
12753
0
    if (socket_requests_in_flight_ <= 0) {
12754
0
      assert(socket_requests_in_flight_ == 0);
12755
0
      socket_requests_are_from_thread_ = std::thread::id();
12756
0
    }
12757
0
12758
0
    if (socket_should_be_closed_when_request_is_done_ || close_connection ||
12759
0
        !ret) {
12760
0
      disconnect(/*gracefully=*/true);
12761
0
    }
12762
0
  });
12763
0
12764
0
  ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
12765
0
    return handle_request(strm, req, res, close_connection, error);
12766
0
  });
12767
0
12768
0
  if (!ret) {
12769
0
    if (error == Error::Success) {
12770
0
      error = Error::Unknown;
12771
0
      output_error_log(error, &req);
12772
0
    }
12773
0
  }
12774
0
12775
0
  return ret;
12776
0
}
12777
12778
0
inline Result ClientImpl::send(const Request &req) {
12779
0
  auto req2 = req;
12780
0
  return send_(std::move(req2));
12781
0
}
12782
12783
0
inline Result ClientImpl::send_(Request &&req) {
12784
0
  auto res = detail::make_unique<Response>();
12785
0
  auto error = Error::Success;
12786
0
  auto ret = send(req, *res, error);
12787
0
#ifdef CPPHTTPLIB_SSL_ENABLED
12788
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),
12789
0
                last_ssl_error_, last_backend_error_};
12790
0
#else
12791
0
  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
12792
0
#endif
12793
0
}
12794
12795
inline void ClientImpl::prepare_default_headers(Request &r, bool for_stream,
12796
0
                                                const std::string &ct) {
12797
0
  (void)for_stream;
12798
0
  for (const auto &header : default_headers_) {
12799
0
    if (!r.has_header(header.first)) { r.headers.insert(header); }
12800
0
  }
12801
0
12802
0
  if (!r.has_header("Host")) {
12803
0
    if (address_family_ == AF_UNIX) {
12804
0
      r.headers.emplace("Host", "localhost");
12805
0
    } else {
12806
0
      r.headers.emplace(
12807
0
          "Host", detail::make_host_and_port_string(host_, port_, is_ssl()));
12808
0
    }
12809
0
  }
12810
0
12811
0
  if (!r.has_header("Accept")) { r.headers.emplace("Accept", "*/*"); }
12812
0
12813
0
  if (!r.content_receiver) {
12814
0
    if (!r.has_header("Accept-Encoding")) {
12815
0
      std::string accept_encoding;
12816
0
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
12817
0
      accept_encoding = "br";
12818
0
#endif
12819
0
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
12820
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
12821
0
      accept_encoding += "gzip, deflate";
12822
0
#endif
12823
0
#ifdef CPPHTTPLIB_ZSTD_SUPPORT
12824
0
      if (!accept_encoding.empty()) { accept_encoding += ", "; }
12825
0
      accept_encoding += "zstd";
12826
0
#endif
12827
0
      r.set_header("Accept-Encoding", accept_encoding);
12828
0
    }
12829
0
12830
0
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
12831
0
    if (!r.has_header("User-Agent")) {
12832
0
      auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
12833
0
      r.set_header("User-Agent", agent);
12834
0
    }
12835
0
#endif
12836
0
  }
12837
0
12838
0
  if (!r.body.empty()) {
12839
0
    if (!ct.empty() && !r.has_header("Content-Type")) {
12840
0
      r.headers.emplace("Content-Type", ct);
12841
0
    }
12842
0
    if (!r.has_header("Content-Length")) {
12843
0
      r.headers.emplace("Content-Length", std::to_string(r.body.size()));
12844
0
    }
12845
0
  }
12846
0
}
12847
12848
inline ClientImpl::StreamHandle
12849
ClientImpl::open_stream(const std::string &method, const std::string &path,
12850
                        const Params &params, const Headers &headers,
12851
                        const std::string &body,
12852
0
                        const std::string &content_type) {
12853
0
  StreamHandle handle;
12854
0
  handle.response = detail::make_unique<Response>();
12855
0
  handle.error = Error::Success;
12856
0
12857
0
  auto query_path = params.empty() ? path : append_query_params(path, params);
12858
0
  handle.connection_ = detail::make_unique<ClientConnection>();
12859
0
12860
0
  {
12861
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
12862
0
12863
0
    auto is_alive = false;
12864
0
    if (socket_.is_open()) {
12865
0
      is_alive = detail::is_socket_alive(socket_.sock);
12866
0
#ifdef CPPHTTPLIB_SSL_ENABLED
12867
0
      if (is_alive && is_ssl()) {
12868
0
        if (tls::is_peer_closed(socket_.ssl, socket_.sock)) {
12869
0
          is_alive = false;
12870
0
        }
12871
0
      }
12872
0
#endif
12873
0
      if (!is_alive) { disconnect(/*gracefully=*/false); }
12874
0
    }
12875
0
12876
0
    if (!is_alive) {
12877
0
      if (!ensure_socket_connection(socket_, handle.error)) {
12878
0
        handle.response.reset();
12879
0
        return handle;
12880
0
      }
12881
0
12882
0
      {
12883
0
        auto success = true;
12884
0
        auto start_time = std::chrono::steady_clock::now();
12885
0
        if (!setup_proxy_connection(socket_, start_time, *handle.response,
12886
0
                                    success, handle.error)) {
12887
0
          if (!success) { handle.response.reset(); }
12888
0
          return handle;
12889
0
        }
12890
0
      }
12891
0
    }
12892
0
12893
0
    transfer_socket_ownership_to_handle(handle);
12894
0
  }
12895
0
12896
0
#ifdef CPPHTTPLIB_SSL_ENABLED
12897
0
  if (is_ssl() && handle.connection_->session) {
12898
0
    handle.socket_stream_ = detail::make_unique<detail::SSLSocketStream>(
12899
0
        handle.connection_->sock, handle.connection_->session,
12900
0
        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
12901
0
        write_timeout_usec_);
12902
0
  } else {
12903
0
    handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
12904
0
        handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
12905
0
        write_timeout_sec_, write_timeout_usec_);
12906
0
  }
12907
0
#else
12908
0
  handle.socket_stream_ = detail::make_unique<detail::SocketStream>(
12909
0
      handle.connection_->sock, read_timeout_sec_, read_timeout_usec_,
12910
0
      write_timeout_sec_, write_timeout_usec_);
12911
0
#endif
12912
0
  handle.stream_ = handle.socket_stream_.get();
12913
0
12914
0
  Request req;
12915
0
  req.method = method;
12916
0
  req.path = query_path;
12917
0
  req.headers = headers;
12918
0
  req.body = body;
12919
0
12920
0
  prepare_default_headers(req, true, content_type);
12921
0
12922
0
  auto &strm = *handle.stream_;
12923
0
  if (detail::write_request_line(strm, req.method, req.path) < 0) {
12924
0
    handle.error = Error::Write;
12925
0
    handle.response.reset();
12926
0
    return handle;
12927
0
  }
12928
0
12929
0
  if (!detail::check_and_write_headers(strm, req.headers, header_writer_,
12930
0
                                       handle.error)) {
12931
0
    handle.response.reset();
12932
0
    return handle;
12933
0
  }
12934
0
12935
0
  if (!body.empty()) {
12936
0
    if (strm.write(body.data(), body.size()) < 0) {
12937
0
      handle.error = Error::Write;
12938
0
      handle.response.reset();
12939
0
      return handle;
12940
0
    }
12941
0
  }
12942
0
12943
0
  if (!read_response_line(strm, req, *handle.response) ||
12944
0
      !detail::read_headers(strm, handle.response->headers)) {
12945
0
    handle.error = Error::Read;
12946
0
    handle.response.reset();
12947
0
    return handle;
12948
0
  }
12949
0
12950
0
  handle.body_reader_.stream = handle.stream_;
12951
0
  handle.body_reader_.payload_max_length = payload_max_length_;
12952
0
12953
0
  if (handle.response->has_header("Content-Length")) {
12954
0
    bool is_invalid = false;
12955
0
    auto content_length = detail::get_header_value_u64(
12956
0
        handle.response->headers, "Content-Length", 0, 0, is_invalid);
12957
0
    if (is_invalid) {
12958
0
      handle.error = Error::Read;
12959
0
      handle.response.reset();
12960
0
      return handle;
12961
0
    }
12962
0
    handle.body_reader_.has_content_length = true;
12963
0
    handle.body_reader_.content_length = content_length;
12964
0
  }
12965
0
12966
0
  auto transfer_encoding =
12967
0
      handle.response->get_header_value("Transfer-Encoding");
12968
0
  handle.body_reader_.chunked = (transfer_encoding == "chunked");
12969
0
12970
0
  auto content_encoding = handle.response->get_header_value("Content-Encoding");
12971
0
  if (!content_encoding.empty()) {
12972
0
    handle.decompressor_ = detail::create_decompressor(content_encoding);
12973
0
  }
12974
0
12975
0
  return handle;
12976
0
}
12977
12978
0
inline ssize_t ClientImpl::StreamHandle::read(char *buf, size_t len) {
12979
0
  if (!is_valid() || !response) { return -1; }
12980
0
12981
0
  if (decompressor_) { return read_with_decompression(buf, len); }
12982
0
  auto n = detail::read_body_content(stream_, body_reader_, buf, len);
12983
0
12984
0
  if (n <= 0 && body_reader_.chunked && !trailers_parsed_ && stream_) {
12985
0
    trailers_parsed_ = true;
12986
0
    if (body_reader_.chunked_decoder) {
12987
0
      if (!body_reader_.chunked_decoder->parse_trailers_into(
12988
0
              response->trailers, response->headers)) {
12989
0
        return n;
12990
0
      }
12991
0
    } else {
12992
0
      detail::ChunkedDecoder dec(*stream_);
12993
0
      if (!dec.parse_trailers_into(response->trailers, response->headers)) {
12994
0
        return n;
12995
0
      }
12996
0
    }
12997
0
  }
12998
0
12999
0
  return n;
13000
0
}
13001
13002
inline ssize_t ClientImpl::StreamHandle::read_with_decompression(char *buf,
13003
0
                                                                 size_t len) {
13004
0
  if (decompress_offset_ < decompress_buffer_.size()) {
13005
0
    auto available = decompress_buffer_.size() - decompress_offset_;
13006
0
    auto to_copy = (std::min)(len, available);
13007
0
    std::memcpy(buf, decompress_buffer_.data() + decompress_offset_, to_copy);
13008
0
    decompress_offset_ += to_copy;
13009
0
    decompressed_bytes_read_ += to_copy;
13010
0
    return static_cast<ssize_t>(to_copy);
13011
0
  }
13012
0
13013
0
  decompress_buffer_.clear();
13014
0
  decompress_offset_ = 0;
13015
0
13016
0
  constexpr size_t kDecompressionBufferSize = 8192;
13017
0
  char compressed_buf[kDecompressionBufferSize];
13018
0
13019
0
  while (true) {
13020
0
    auto n = detail::read_body_content(stream_, body_reader_, compressed_buf,
13021
0
                                       sizeof(compressed_buf));
13022
0
13023
0
    if (n <= 0) { return n; }
13024
0
13025
0
    bool decompress_ok = decompressor_->decompress(
13026
0
        compressed_buf, static_cast<size_t>(n),
13027
0
        [this](const char *data, size_t data_len) {
13028
0
          decompress_buffer_.append(data, data_len);
13029
0
          auto limit = body_reader_.payload_max_length;
13030
0
          if (decompressed_bytes_read_ + decompress_buffer_.size() > limit) {
13031
0
            return false;
13032
0
          }
13033
0
          return true;
13034
0
        });
13035
0
13036
0
    if (!decompress_ok) {
13037
0
      body_reader_.last_error = Error::Read;
13038
0
      return -1;
13039
0
    }
13040
0
13041
0
    if (!decompress_buffer_.empty()) { break; }
13042
0
  }
13043
0
13044
0
  auto to_copy = (std::min)(len, decompress_buffer_.size());
13045
0
  std::memcpy(buf, decompress_buffer_.data(), to_copy);
13046
0
  decompress_offset_ = to_copy;
13047
0
  decompressed_bytes_read_ += to_copy;
13048
0
  return static_cast<ssize_t>(to_copy);
13049
0
}
13050
13051
0
inline void ClientImpl::StreamHandle::parse_trailers_if_needed() {
13052
0
  if (!response || !stream_ || !body_reader_.chunked || trailers_parsed_) {
13053
0
    return;
13054
0
  }
13055
0
13056
0
  trailers_parsed_ = true;
13057
0
13058
0
  const auto bufsiz = 128;
13059
0
  char line_buf[bufsiz];
13060
0
  detail::stream_line_reader line_reader(*stream_, line_buf, bufsiz);
13061
0
13062
0
  if (!line_reader.getline()) { return; }
13063
0
13064
0
  if (!detail::parse_trailers(line_reader, response->trailers,
13065
0
                              response->headers)) {
13066
0
    return;
13067
0
  }
13068
0
}
13069
13070
namespace detail {
13071
13072
0
inline ChunkedDecoder::ChunkedDecoder(Stream &s) : strm(s) {}
13073
13074
inline ssize_t ChunkedDecoder::read_payload(char *buf, size_t len,
13075
                                            size_t &out_chunk_offset,
13076
0
                                            size_t &out_chunk_total) {
13077
0
  if (finished) { return 0; }
13078
13079
0
  if (chunk_remaining == 0) {
13080
0
    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
13081
0
    if (!lr.getline()) { return -1; }
13082
13083
    // RFC 9112 §7.1: chunk-size = 1*HEXDIG
13084
0
    const char *p = lr.ptr();
13085
0
    int v = 0;
13086
0
    if (!is_hex(*p, v)) { return -1; }
13087
13088
0
    size_t chunk_len = 0;
13089
0
    constexpr size_t chunk_len_max = (std::numeric_limits<size_t>::max)();
13090
0
    for (; is_hex(*p, v); ++p) {
13091
0
      if (chunk_len > (chunk_len_max >> 4)) { return -1; }
13092
0
      chunk_len = (chunk_len << 4) | static_cast<size_t>(v);
13093
0
    }
13094
13095
0
    while (is_space_or_tab(*p)) {
13096
0
      ++p;
13097
0
    }
13098
0
    if (*p != '\0' && *p != ';' && *p != '\r' && *p != '\n') { return -1; }
13099
13100
0
    if (chunk_len == 0) {
13101
0
      chunk_remaining = 0;
13102
0
      finished = true;
13103
0
      out_chunk_offset = 0;
13104
0
      out_chunk_total = 0;
13105
0
      return 0;
13106
0
    }
13107
13108
0
    chunk_remaining = chunk_len;
13109
0
    last_chunk_total = chunk_remaining;
13110
0
    last_chunk_offset = 0;
13111
0
  }
13112
13113
0
  auto to_read = (std::min)(chunk_remaining, len);
13114
0
  auto n = strm.read(buf, to_read);
13115
0
  if (n <= 0) { return -1; }
13116
13117
0
  auto offset_before = last_chunk_offset;
13118
0
  last_chunk_offset += static_cast<size_t>(n);
13119
0
  chunk_remaining -= static_cast<size_t>(n);
13120
13121
0
  out_chunk_offset = offset_before;
13122
0
  out_chunk_total = last_chunk_total;
13123
13124
0
  if (chunk_remaining == 0) {
13125
0
    stream_line_reader lr(strm, line_buf, sizeof(line_buf));
13126
0
    if (!lr.getline()) { return -1; }
13127
0
    if (std::strcmp(lr.ptr(), "\r\n") != 0) { return -1; }
13128
0
  }
13129
13130
0
  return n;
13131
0
}
13132
13133
inline bool ChunkedDecoder::parse_trailers_into(Headers &dest,
13134
0
                                                const Headers &src_headers) {
13135
0
  stream_line_reader lr(strm, line_buf, sizeof(line_buf));
13136
0
  if (!lr.getline()) { return false; }
13137
0
  return parse_trailers(lr, dest, src_headers);
13138
0
}
13139
13140
} // namespace detail
13141
13142
inline void
13143
0
ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
13144
0
  handle.connection_->sock = socket_.sock;
13145
0
#ifdef CPPHTTPLIB_SSL_ENABLED
13146
0
  handle.connection_->session = socket_.ssl;
13147
0
  socket_.ssl = nullptr;
13148
0
#endif
13149
0
  socket_.sock = INVALID_SOCKET;
13150
0
}
13151
13152
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
13153
                                       Response &res, bool close_connection,
13154
0
                                       Error &error) {
13155
0
  if (req.path.empty()) {
13156
0
    error = Error::Connection;
13157
0
    output_error_log(error, &req);
13158
0
    return false;
13159
0
  }
13160
0
13161
0
  auto req_save = req;
13162
0
13163
0
  bool ret;
13164
0
13165
0
  if (!is_ssl() && is_proxy_enabled_for_host(host_)) {
13166
0
    auto req2 = req;
13167
0
    req2.path = "http://" +
13168
0
                detail::make_host_and_port_string(host_, port_, false) +
13169
0
                req.path;
13170
0
    ret = process_request(strm, req2, res, close_connection, error);
13171
0
    req = std::move(req2);
13172
0
    req.path = req_save.path;
13173
0
  } else {
13174
0
    ret = process_request(strm, req, res, close_connection, error);
13175
0
  }
13176
0
13177
0
  if (!ret) { return false; }
13178
0
13179
0
  if (res.get_header_value("Connection") == "close" ||
13180
0
      (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
13181
0
    // NOTE: this requires a not-entirely-obvious chain of calls to be correct
13182
0
    // for this to be safe.
13183
0
13184
0
    // This is safe to call because handle_request is only called by send_
13185
0
    // which locks the request mutex during the process. It would be a bug
13186
0
    // to call it from a different thread since it's a thread-safety issue
13187
0
    // to do these things to the socket if another thread is using the socket.
13188
0
    std::lock_guard<std::mutex> guard(socket_mutex_);
13189
0
    disconnect(/*gracefully=*/true);
13190
0
  }
13191
0
13192
0
  if (300 < res.status && res.status < 400 && follow_location_) {
13193
0
    req = std::move(req_save);
13194
0
    ret = redirect(req, res, error);
13195
0
  }
13196
0
13197
0
#ifdef CPPHTTPLIB_SSL_ENABLED
13198
0
  if ((res.status == StatusCode::Unauthorized_401 ||
13199
0
       res.status == StatusCode::ProxyAuthenticationRequired_407) &&
13200
0
      req.authorization_count_ < 5) {
13201
0
    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
13202
0
13203
0
    // Only retry when the 407 actually came from a proxy hop: plain HTTP
13204
0
    // through an enabled proxy. HTTPS via CONNECT tunnels the 407 from the
13205
0
    // origin (#2457); direct/bypassed origins have no proxy hop at all.
13206
0
    if (is_proxy && !(!is_ssl() && is_proxy_enabled_for_host(host_))) {
13207
0
      return ret;
13208
0
    }
13209
0
13210
0
    const auto &username =
13211
0
        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
13212
0
    const auto &password =
13213
0
        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
13214
0
13215
0
    if (!username.empty() && !password.empty()) {
13216
0
      std::map<std::string, std::string> auth;
13217
0
      if (detail::parse_www_authenticate(res, auth, is_proxy)) {
13218
0
        Request new_req = req;
13219
0
        new_req.authorization_count_ += 1;
13220
0
        new_req.headers.erase(is_proxy ? "Proxy-Authorization"
13221
0
                                       : "Authorization");
13222
0
        new_req.headers.insert(detail::make_digest_authentication_header(
13223
0
            req, auth, new_req.authorization_count_, detail::random_string(10),
13224
0
            username, password, is_proxy));
13225
0
13226
0
        Response new_res;
13227
0
13228
0
        ret = send(new_req, new_res, error);
13229
0
        if (ret) { res = std::move(new_res); }
13230
0
      }
13231
0
    }
13232
0
  }
13233
0
#endif
13234
0
13235
0
  return ret;
13236
0
}
13237
13238
0
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
13239
0
  if (req.redirect_count_ == 0) {
13240
0
    error = Error::ExceedRedirectCount;
13241
0
    output_error_log(error, &req);
13242
0
    return false;
13243
0
  }
13244
0
13245
0
  auto location = res.get_header_value("location");
13246
0
  if (location.empty()) { return false; }
13247
0
13248
0
  detail::UrlComponents uc;
13249
0
  if (!detail::parse_url(location, uc)) { return false; }
13250
0
13251
0
  // Only follow http/https redirects
13252
0
  if (!uc.scheme.empty() && uc.scheme != "http" && uc.scheme != "https") {
13253
0
    return false;
13254
0
  }
13255
0
13256
0
  auto scheme = is_ssl() ? "https" : "http";
13257
0
13258
0
  auto next_scheme = std::move(uc.scheme);
13259
0
  auto next_host = std::move(uc.host);
13260
0
  auto port_str = std::move(uc.port);
13261
0
  auto next_path = std::move(uc.path);
13262
0
  auto next_query = std::move(uc.query);
13263
0
13264
0
  auto next_port = port_;
13265
0
  if (!port_str.empty()) {
13266
0
    if (!detail::parse_port(port_str, next_port)) { return false; }
13267
0
  } else if (!next_scheme.empty()) {
13268
0
    next_port = next_scheme == "https" ? 443 : 80;
13269
0
  }
13270
0
13271
0
  if (next_scheme.empty()) { next_scheme = scheme; }
13272
0
  if (next_host.empty()) { next_host = host_; }
13273
0
  if (next_path.empty()) { next_path = "/"; }
13274
0
13275
0
  auto path = decode_path_component(next_path) + next_query;
13276
0
13277
0
  // Same host redirect - use current client
13278
0
  if (next_scheme == scheme && next_host == host_ && next_port == port_) {
13279
0
    return detail::redirect(*this, req, res, path, location, error);
13280
0
  }
13281
0
13282
0
  // Cross-host/scheme redirect - create new client with robust setup
13283
0
  return create_redirect_client(next_scheme, next_host, next_port, req, res,
13284
0
                                path, location, error);
13285
0
}
13286
13287
// New method for robust redirect client creation
13288
inline bool ClientImpl::create_redirect_client(
13289
    const std::string &scheme, const std::string &host, int port, Request &req,
13290
    Response &res, const std::string &path, const std::string &location,
13291
0
    Error &error) {
13292
0
  // Determine if we need SSL
13293
0
  auto need_ssl = (scheme == "https");
13294
0
13295
0
  // Clean up request headers that are host/client specific
13296
0
  // Remove headers that should not be carried over to new host
13297
0
  auto headers_to_remove =
13298
0
      std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
13299
0
13300
0
  for (const auto &header_name : headers_to_remove) {
13301
0
    auto it = req.headers.find(header_name);
13302
0
    while (it != req.headers.end()) {
13303
0
      it = req.headers.erase(it);
13304
0
      it = req.headers.find(header_name);
13305
0
    }
13306
0
  }
13307
0
13308
0
  // Create appropriate client type and handle redirect
13309
0
  if (need_ssl) {
13310
0
#ifdef CPPHTTPLIB_SSL_ENABLED
13311
0
    // Create SSL client for HTTPS redirect
13312
0
    SSLClient redirect_client(host, port);
13313
0
13314
0
    // Setup basic client configuration first
13315
0
    setup_redirect_client(redirect_client);
13316
0
13317
0
    redirect_client.enable_server_certificate_verification(
13318
0
        server_certificate_verification_);
13319
0
    redirect_client.enable_server_hostname_verification(
13320
0
        server_hostname_verification_);
13321
0
13322
0
    // Transfer CA certificate to redirect client
13323
0
    if (!ca_cert_pem_.empty()) {
13324
0
      redirect_client.load_ca_cert_store(ca_cert_pem_.c_str(),
13325
0
                                         ca_cert_pem_.size());
13326
0
    }
13327
0
    if (!ca_cert_file_path_.empty()) {
13328
0
      redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);
13329
0
    }
13330
0
13331
0
    // Client certificates are set through constructor for SSLClient
13332
0
    // NOTE: SSLClient constructor already takes client_cert_path and
13333
0
    // client_key_path so we need to create it properly if client certs are
13334
0
    // needed
13335
0
13336
0
    // Execute the redirect
13337
0
    return detail::redirect(redirect_client, req, res, path, location, error);
13338
0
#else
13339
0
    // SSL not supported - set appropriate error
13340
0
    error = Error::SSLConnection;
13341
0
    output_error_log(error, &req);
13342
0
    return false;
13343
0
#endif
13344
0
  } else {
13345
0
    // HTTP redirect
13346
0
    ClientImpl redirect_client(host, port);
13347
0
13348
0
    // Setup client with robust configuration
13349
0
    setup_redirect_client(redirect_client);
13350
0
13351
0
    // Execute the redirect
13352
0
    return detail::redirect(redirect_client, req, res, path, location, error);
13353
0
  }
13354
0
}
13355
13356
// New method for robust client setup (based on basic_manual_redirect.cpp
13357
// logic)
13358
template <typename ClientType>
13359
0
inline void ClientImpl::setup_redirect_client(ClientType &client) {
13360
0
  // Copy basic settings first
13361
0
  client.set_connection_timeout(connection_timeout_sec_);
13362
0
  client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
13363
0
  client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);
13364
0
  client.set_keep_alive(keep_alive_);
13365
0
  client.set_follow_location(
13366
0
      true); // Enable redirects to handle multi-step redirects
13367
0
  client.set_path_encode(path_encode_);
13368
0
  client.set_compress(compress_);
13369
0
  client.set_decompress(decompress_);
13370
0
13371
0
  // NOTE: Authentication credentials (basic auth, bearer token, digest auth)
13372
0
  // are intentionally NOT copied to the redirect client. Per RFC 9110 Section
13373
0
  // 15.4, credentials must not be forwarded when redirecting to a different
13374
0
  // host. This function is only called for cross-host redirects; same-host
13375
0
  // redirects are handled directly in ClientImpl::redirect().
13376
0
13377
0
  // Copy the proxy configuration unconditionally; the per-target bypass is
13378
0
  // re-evaluated at send time, so a later hop to a non-bypassed host can
13379
0
  // still use the proxy.
13380
0
  client.no_proxy_entries_ = no_proxy_entries_;
13381
0
  if (!proxy_host_.empty() && proxy_port_ != -1) {
13382
0
    client.set_proxy(proxy_host_, proxy_port_);
13383
0
13384
0
    if (!proxy_basic_auth_username_.empty()) {
13385
0
      client.set_proxy_basic_auth(proxy_basic_auth_username_,
13386
0
                                  proxy_basic_auth_password_);
13387
0
    }
13388
0
    if (!proxy_bearer_token_auth_token_.empty()) {
13389
0
      client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);
13390
0
    }
13391
0
#ifdef CPPHTTPLIB_SSL_ENABLED
13392
0
    if (!proxy_digest_auth_username_.empty()) {
13393
0
      client.set_proxy_digest_auth(proxy_digest_auth_username_,
13394
0
                                   proxy_digest_auth_password_);
13395
0
    }
13396
0
#endif
13397
0
  }
13398
0
13399
0
  // Copy network and socket settings
13400
0
  client.set_address_family(address_family_);
13401
0
  client.set_tcp_nodelay(tcp_nodelay_);
13402
0
  client.set_ipv6_v6only(ipv6_v6only_);
13403
0
  if (socket_options_) { client.set_socket_options(socket_options_); }
13404
0
  if (!interface_.empty()) { client.set_interface(interface_); }
13405
0
13406
0
  // Copy logging and headers
13407
0
  if (logger_) { client.set_logger(logger_); }
13408
0
  if (error_logger_) { client.set_error_logger(error_logger_); }
13409
0
13410
0
  // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers
13411
0
  // Each new client should generate its own headers based on its target host
13412
0
}
13413
13414
inline bool ClientImpl::write_content_with_provider(Stream &strm,
13415
                                                    const Request &req,
13416
0
                                                    Error &error) const {
13417
0
  auto is_shutting_down = []() { return false; };
13418
0
13419
0
  if (req.is_chunked_content_provider_) {
13420
0
    auto compressor = compress_ ? detail::create_compressor().first
13421
0
                                : std::unique_ptr<detail::compressor>();
13422
0
    if (!compressor) {
13423
0
      compressor = detail::make_unique<detail::nocompressor>();
13424
0
    }
13425
0
13426
0
    return detail::write_content_chunked(strm, req.content_provider_,
13427
0
                                         is_shutting_down, *compressor, error);
13428
0
  } else {
13429
0
    return detail::write_content_with_progress(
13430
0
        strm, req.content_provider_, 0, req.content_length_, is_shutting_down,
13431
0
        req.upload_progress, error);
13432
0
  }
13433
0
}
13434
13435
inline bool ClientImpl::write_request(Stream &strm, Request &req,
13436
                                      bool close_connection, Error &error,
13437
0
                                      bool skip_body) {
13438
0
  // Prepare additional headers
13439
0
  if (close_connection) {
13440
0
    if (!req.has_header("Connection")) {
13441
0
      req.set_header("Connection", "close");
13442
0
    }
13443
0
  }
13444
0
13445
0
  std::string ct_for_defaults;
13446
0
  if (!req.has_header("Content-Type") && !req.body.empty()) {
13447
0
    ct_for_defaults = "text/plain";
13448
0
  }
13449
0
  prepare_default_headers(req, false, ct_for_defaults);
13450
0
13451
0
  if (req.body.empty()) {
13452
0
    if (req.content_provider_) {
13453
0
      if (!req.is_chunked_content_provider_) {
13454
0
        if (!req.has_header("Content-Length")) {
13455
0
          auto length = std::to_string(req.content_length_);
13456
0
          req.set_header("Content-Length", length);
13457
0
        }
13458
0
      }
13459
0
    } else {
13460
0
      if (req.method == "POST" || req.method == "PUT" ||
13461
0
          req.method == "PATCH") {
13462
0
        req.set_header("Content-Length", "0");
13463
0
      }
13464
0
    }
13465
0
  }
13466
0
13467
0
  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
13468
0
    if (!req.has_header("Authorization")) {
13469
0
      req.headers.insert(make_basic_authentication_header(
13470
0
          basic_auth_username_, basic_auth_password_, false));
13471
0
    }
13472
0
  }
13473
0
13474
0
  if (!bearer_token_auth_token_.empty()) {
13475
0
    if (!req.has_header("Authorization")) {
13476
0
      req.headers.insert(make_bearer_token_authentication_header(
13477
0
          bearer_token_auth_token_, false));
13478
0
    }
13479
0
  }
13480
0
13481
0
  // Proxy-Authorization is only sent when the proxy is actually used for
13482
0
  // this target — otherwise NO_PROXY-matched requests would leak proxy
13483
0
  // credentials directly to the destination server.
13484
0
  if (is_proxy_enabled_for_host(host_)) {
13485
0
    if (!proxy_basic_auth_username_.empty() &&
13486
0
        !proxy_basic_auth_password_.empty() &&
13487
0
        !req.has_header("Proxy-Authorization")) {
13488
0
      req.headers.insert(make_basic_authentication_header(
13489
0
          proxy_basic_auth_username_, proxy_basic_auth_password_, true));
13490
0
    }
13491
0
    if (!proxy_bearer_token_auth_token_.empty() &&
13492
0
        !req.has_header("Proxy-Authorization")) {
13493
0
      req.headers.insert(make_bearer_token_authentication_header(
13494
0
          proxy_bearer_token_auth_token_, true));
13495
0
    }
13496
0
  }
13497
0
13498
0
  // Request line and headers
13499
0
  {
13500
0
    detail::BufferStream bstrm;
13501
0
13502
0
    // Extract path and query from req.path
13503
0
    std::string path_part, query_part;
13504
0
    auto query_pos = req.path.find('?');
13505
0
    if (query_pos != std::string::npos) {
13506
0
      path_part = req.path.substr(0, query_pos);
13507
0
      query_part = req.path.substr(query_pos + 1);
13508
0
    } else {
13509
0
      path_part = req.path;
13510
0
      query_part = "";
13511
0
    }
13512
0
13513
0
    // Encode path part. If the original `req.path` already contained a
13514
0
    // query component, preserve its raw query string (including parameter
13515
0
    // order) instead of reparsing and reassembling it which may reorder
13516
0
    // parameters due to container ordering (e.g. `Params` uses
13517
0
    // `std::multimap`). When there is no query in `req.path`, fall back to
13518
0
    // building a query from `req.params` so existing callers that pass
13519
0
    // `Params` continue to work.
13520
0
    auto path_with_query =
13521
0
        path_encode_ ? detail::encode_path(path_part) : path_part;
13522
0
13523
0
    if (!query_part.empty()) {
13524
0
      // Normalize the query string (decode then re-encode) while preserving
13525
0
      // the original parameter order.
13526
0
      auto normalized = detail::normalize_query_string(query_part);
13527
0
      if (!normalized.empty()) { path_with_query += '?' + normalized; }
13528
0
13529
0
      // Still populate req.params for handlers/users who read them.
13530
0
      detail::parse_query_text(query_part, req.params);
13531
0
    } else {
13532
0
      // No query in path; parse any query_part (empty) and append params
13533
0
      // from `req.params` when present (preserves prior behavior for
13534
0
      // callers who provide Params separately).
13535
0
      detail::parse_query_text(query_part, req.params);
13536
0
      if (!req.params.empty()) {
13537
0
        path_with_query = append_query_params(path_with_query, req.params);
13538
0
      }
13539
0
    }
13540
0
13541
0
    // Write request line and headers
13542
0
    detail::write_request_line(bstrm, req.method, path_with_query);
13543
0
    if (!detail::check_and_write_headers(bstrm, req.headers, header_writer_,
13544
0
                                         error)) {
13545
0
      output_error_log(error, &req);
13546
0
      return false;
13547
0
    }
13548
0
13549
0
    // Flush buffer
13550
0
    auto &data = bstrm.get_buffer();
13551
0
    if (!detail::write_data(strm, data.data(), data.size())) {
13552
0
      error = Error::Write;
13553
0
      output_error_log(error, &req);
13554
0
      return false;
13555
0
    }
13556
0
  }
13557
0
13558
0
  // After sending request line and headers, wait briefly for an early server
13559
0
  // response (e.g. 4xx) and avoid sending a potentially large request body
13560
0
  // unnecessarily. This workaround is only enabled on Windows because Unix
13561
0
  // platforms surface write errors (EPIPE) earlier; on Windows kernel send
13562
0
  // buffering can accept large writes even when the peer already responded.
13563
0
  // Check the stream first (which covers SSL via `is_readable()`), then
13564
0
  // fall back to select on the socket. Only perform the wait for very large
13565
0
  // request bodies to avoid interfering with normal small requests and
13566
0
  // reduce side-effects. Poll briefly (up to 50ms as default) for an early
13567
0
  // response. Skip this check when using Expect: 100-continue, as the protocol
13568
0
  // handles early responses properly.
13569
0
#if defined(_WIN32)
13570
0
  if (!skip_body &&
13571
0
      req.body.size() > CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD &&
13572
0
      req.path.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
13573
0
    auto start = std::chrono::high_resolution_clock::now();
13574
0
13575
0
    for (;;) {
13576
0
      // Prefer socket-level readiness to avoid SSL_pending() false-positives
13577
0
      // from SSL internals. If the underlying socket is readable, assume an
13578
0
      // early response may be present.
13579
0
      auto sock = strm.socket();
13580
0
      if (sock != INVALID_SOCKET && detail::select_read(sock, 0, 0) > 0) {
13581
0
        return false;
13582
0
      }
13583
0
13584
0
      // Fallback to stream-level check for non-socket streams or when the
13585
0
      // socket isn't reporting readable. Avoid using `is_readable()` for
13586
0
      // SSL, since `SSL_pending()` may report buffered records that do not
13587
0
      // indicate a complete application-level response yet.
13588
0
      if (!is_ssl() && strm.is_readable()) { return false; }
13589
0
13590
0
      auto now = std::chrono::high_resolution_clock::now();
13591
0
      auto elapsed =
13592
0
          std::chrono::duration_cast<std::chrono::milliseconds>(now - start)
13593
0
              .count();
13594
0
      if (elapsed >= CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND) {
13595
0
        break;
13596
0
      }
13597
0
13598
0
      std::this_thread::sleep_for(std::chrono::milliseconds(1));
13599
0
    }
13600
0
  }
13601
0
#endif
13602
0
13603
0
  // Body
13604
0
  if (skip_body) { return true; }
13605
0
13606
0
  return write_request_body(strm, req, error);
13607
0
}
13608
13609
inline bool ClientImpl::write_request_body(Stream &strm, Request &req,
13610
0
                                           Error &error) {
13611
0
  if (req.body.empty()) {
13612
0
    return write_content_with_provider(strm, req, error);
13613
0
  }
13614
0
13615
0
  if (req.upload_progress) {
13616
0
    auto body_size = req.body.size();
13617
0
    size_t written = 0;
13618
0
    auto data = req.body.data();
13619
0
13620
0
    while (written < body_size) {
13621
0
      size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);
13622
0
      if (!detail::write_data(strm, data + written, to_write)) {
13623
0
        error = Error::Write;
13624
0
        output_error_log(error, &req);
13625
0
        return false;
13626
0
      }
13627
0
      written += to_write;
13628
0
13629
0
      if (!req.upload_progress(written, body_size)) {
13630
0
        error = Error::Canceled;
13631
0
        output_error_log(error, &req);
13632
0
        return false;
13633
0
      }
13634
0
    }
13635
0
  } else {
13636
0
    if (!detail::write_data(strm, req.body.data(), req.body.size())) {
13637
0
      error = Error::Write;
13638
0
      output_error_log(error, &req);
13639
0
      return false;
13640
0
    }
13641
0
  }
13642
0
13643
0
  return true;
13644
0
}
13645
13646
inline std::unique_ptr<Response>
13647
ClientImpl::send_with_content_provider_and_receiver(
13648
    Request &req, const char *body, size_t content_length,
13649
    ContentProvider content_provider,
13650
    ContentProviderWithoutLength content_provider_without_length,
13651
    const std::string &content_type, ContentReceiver content_receiver,
13652
0
    Error &error) {
13653
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
13654
0
13655
0
  auto enc = compress_
13656
0
                 ? detail::create_compressor()
13657
0
                 : std::pair<std::unique_ptr<detail::compressor>, const char *>(
13658
0
                       nullptr, nullptr);
13659
0
13660
0
  if (enc.second) { req.set_header("Content-Encoding", enc.second); }
13661
0
13662
0
  if (enc.first && !content_provider_without_length) {
13663
0
    auto &compressor = enc.first;
13664
0
13665
0
    if (content_provider) {
13666
0
      auto ok = true;
13667
0
      size_t offset = 0;
13668
0
      DataSink data_sink;
13669
0
13670
0
      data_sink.write = [&](const char *data, size_t data_len) -> bool {
13671
0
        if (ok) {
13672
0
          auto last = offset + data_len == content_length;
13673
0
13674
0
          auto ret = compressor->compress(
13675
0
              data, data_len, last,
13676
0
              [&](const char *compressed_data, size_t compressed_data_len) {
13677
0
                req.body.append(compressed_data, compressed_data_len);
13678
0
                return true;
13679
0
              });
13680
0
13681
0
          if (ret) {
13682
0
            offset += data_len;
13683
0
          } else {
13684
0
            ok = false;
13685
0
          }
13686
0
        }
13687
0
        return ok;
13688
0
      };
13689
0
13690
0
      while (ok && offset < content_length) {
13691
0
        if (!content_provider(offset, content_length - offset, data_sink)) {
13692
0
          error = Error::Canceled;
13693
0
          output_error_log(error, &req);
13694
0
          return nullptr;
13695
0
        }
13696
0
      }
13697
0
    } else {
13698
0
      if (!compressor->compress(body, content_length, true,
13699
0
                                [&](const char *data, size_t data_len) {
13700
0
                                  req.body.append(data, data_len);
13701
0
                                  return true;
13702
0
                                })) {
13703
0
        error = Error::Compression;
13704
0
        output_error_log(error, &req);
13705
0
        return nullptr;
13706
0
      }
13707
0
    }
13708
0
  } else {
13709
0
    if (content_provider) {
13710
0
      req.content_length_ = content_length;
13711
0
      req.content_provider_ = std::move(content_provider);
13712
0
      req.is_chunked_content_provider_ = false;
13713
0
    } else if (content_provider_without_length) {
13714
0
      req.content_length_ = 0;
13715
0
      req.content_provider_ = detail::ContentProviderAdapter(
13716
0
          std::move(content_provider_without_length));
13717
0
      req.is_chunked_content_provider_ = true;
13718
0
      req.set_header("Transfer-Encoding", "chunked");
13719
0
    } else {
13720
0
      req.body.assign(body, content_length);
13721
0
    }
13722
0
  }
13723
0
13724
0
  if (content_receiver) {
13725
0
    req.content_receiver =
13726
0
        [content_receiver](const char *data, size_t data_length,
13727
0
                           size_t /*offset*/, size_t /*total_length*/) {
13728
0
          return content_receiver(data, data_length);
13729
0
        };
13730
0
  }
13731
0
13732
0
  auto res = detail::make_unique<Response>();
13733
0
  return send(req, *res, error) ? std::move(res) : nullptr;
13734
0
}
13735
13736
inline Result ClientImpl::send_with_content_provider_and_receiver(
13737
    const std::string &method, const std::string &path, const Headers &headers,
13738
    const char *body, size_t content_length, ContentProvider content_provider,
13739
    ContentProviderWithoutLength content_provider_without_length,
13740
    const std::string &content_type, ContentReceiver content_receiver,
13741
0
    UploadProgress progress) {
13742
0
  Request req;
13743
0
  req.method = method;
13744
0
  req.headers = headers;
13745
0
  req.path = path;
13746
0
  req.upload_progress = std::move(progress);
13747
0
  if (max_timeout_msec_ > 0) {
13748
0
    req.start_time_ = std::chrono::steady_clock::now();
13749
0
  }
13750
0
13751
0
  auto error = Error::Success;
13752
0
13753
0
  auto res = send_with_content_provider_and_receiver(
13754
0
      req, body, content_length, std::move(content_provider),
13755
0
      std::move(content_provider_without_length), content_type,
13756
0
      std::move(content_receiver), error);
13757
0
13758
0
#ifdef CPPHTTPLIB_SSL_ENABLED
13759
0
  return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,
13760
0
                last_backend_error_};
13761
0
#else
13762
0
  return Result{std::move(res), error, std::move(req.headers)};
13763
0
#endif
13764
0
}
13765
13766
inline void ClientImpl::output_log(const Request &req,
13767
0
                                   const Response &res) const {
13768
0
  if (logger_) {
13769
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
13770
0
    logger_(req, res);
13771
0
  }
13772
0
}
13773
13774
inline void ClientImpl::output_error_log(const Error &err,
13775
0
                                         const Request *req) const {
13776
0
  if (error_logger_) {
13777
0
    std::lock_guard<std::mutex> guard(logger_mutex_);
13778
0
    error_logger_(err, req);
13779
0
  }
13780
0
}
13781
13782
inline bool ClientImpl::process_request(Stream &strm, Request &req,
13783
                                        Response &res, bool close_connection,
13784
0
                                        Error &error) {
13785
0
  // Auto-add Expect: 100-continue for large bodies
13786
0
  if (CPPHTTPLIB_EXPECT_100_THRESHOLD > 0 && !req.has_header("Expect")) {
13787
0
    auto body_size = req.body.empty() ? req.content_length_ : req.body.size();
13788
0
    if (body_size >= CPPHTTPLIB_EXPECT_100_THRESHOLD) {
13789
0
      req.set_header("Expect", "100-continue");
13790
0
    }
13791
0
  }
13792
0
13793
0
  // Check for Expect: 100-continue
13794
0
  auto expect_100_continue = req.get_header_value("Expect") == "100-continue";
13795
0
13796
0
  // Send request (skip body if using Expect: 100-continue)
13797
0
  auto write_request_success =
13798
0
      write_request(strm, req, close_connection, error, expect_100_continue);
13799
0
13800
0
#ifdef CPPHTTPLIB_SSL_ENABLED
13801
0
  if (is_ssl() && !expect_100_continue) {
13802
0
    auto is_proxy_enabled = is_proxy_enabled_for_host(host_);
13803
0
    if (!is_proxy_enabled) {
13804
0
      if (tls::is_peer_closed(socket_.ssl, socket_.sock)) {
13805
0
        error = Error::SSLPeerCouldBeClosed_;
13806
0
        output_error_log(error, &req);
13807
0
        return false;
13808
0
      }
13809
0
    }
13810
0
  }
13811
0
#endif
13812
0
13813
0
  // Handle Expect: 100-continue.
13814
0
  //
13815
0
  // Wait for an interim/early response by attempting to read the status line
13816
0
  // under a short timeout, instead of trusting raw socket readability. Over
13817
0
  // TLS, post-handshake records (e.g. session tickets) make the socket
13818
0
  // readable without any HTTP response being available; relying on
13819
0
  // `select_read` there caused the body to be withheld forever and the
13820
0
  // request to fail with `Read` (#2458). If no status line arrives within the
13821
0
  // timeout, send the body anyway (matching curl's behavior).
13822
0
  auto status_line_read = false;
13823
0
  if (expect_100_continue && write_request_success) {
13824
0
    if (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND > 0) {
13825
0
      time_t sec = CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND / 1000;
13826
0
      time_t usec = (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND % 1000) * 1000;
13827
0
      strm.set_read_timeout(sec, usec);
13828
0
      status_line_read = read_response_line(strm, req, res, false);
13829
0
      strm.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
13830
0
    }
13831
0
13832
0
    if (!status_line_read) {
13833
0
      // No interim response within the timeout: send the body and handle the
13834
0
      // response as usual.
13835
0
      if (!write_request_body(strm, req, error)) { return false; }
13836
0
      expect_100_continue = false; // Switch to normal response handling
13837
0
    }
13838
0
  }
13839
0
13840
0
  // Receive response and headers
13841
0
  // When using Expect: 100-continue, don't auto-skip `100 Continue` response
13842
0
  if ((!status_line_read &&
13843
0
       !read_response_line(strm, req, res, !expect_100_continue)) ||
13844
0
      !detail::read_headers(strm, res.headers)) {
13845
0
    if (write_request_success) { error = Error::Read; }
13846
0
    output_error_log(error, &req);
13847
0
    return false;
13848
0
  }
13849
0
13850
0
  if (!write_request_success) { return false; }
13851
0
13852
0
  // Handle Expect: 100-continue response
13853
0
  if (expect_100_continue) {
13854
0
    if (res.status == StatusCode::Continue_100) {
13855
0
      // Server accepted, send the body
13856
0
      if (!write_request_body(strm, req, error)) { return false; }
13857
0
13858
0
      // Read the actual response
13859
0
      res.headers.clear();
13860
0
      res.body.clear();
13861
0
      if (!read_response_line(strm, req, res) ||
13862
0
          !detail::read_headers(strm, res.headers)) {
13863
0
        error = Error::Read;
13864
0
        output_error_log(error, &req);
13865
0
        return false;
13866
0
      }
13867
0
    }
13868
0
    // If not 100 Continue, server returned an error; proceed with that response
13869
0
  }
13870
0
13871
0
  // Body
13872
0
  if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
13873
0
      req.method != "CONNECT") {
13874
0
    auto redirect = 300 < res.status && res.status < 400 &&
13875
0
                    res.status != StatusCode::NotModified_304 &&
13876
0
                    follow_location_;
13877
0
13878
0
    if (req.response_handler && !redirect) {
13879
0
      if (!req.response_handler(res)) {
13880
0
        error = Error::Canceled;
13881
0
        output_error_log(error, &req);
13882
0
        return false;
13883
0
      }
13884
0
    }
13885
0
13886
0
    auto out =
13887
0
        req.content_receiver
13888
0
            ? static_cast<ContentReceiverWithProgress>(
13889
0
                  [&](const char *buf, size_t n, size_t off, size_t len) {
13890
0
                    if (redirect) { return true; }
13891
0
                    auto ret = req.content_receiver(buf, n, off, len);
13892
0
                    if (!ret) {
13893
0
                      error = Error::Canceled;
13894
0
                      output_error_log(error, &req);
13895
0
                    }
13896
0
                    return ret;
13897
0
                  })
13898
0
            : static_cast<ContentReceiverWithProgress>(
13899
0
                  [&](const char *buf, size_t n, size_t /*off*/,
13900
0
                      size_t /*len*/) {
13901
0
                    assert(res.body.size() + n <= res.body.max_size());
13902
0
                    if (payload_max_length_ > 0 &&
13903
0
                        (res.body.size() >= payload_max_length_ ||
13904
0
                         n > payload_max_length_ - res.body.size())) {
13905
0
                      return false;
13906
0
                    }
13907
0
                    res.body.append(buf, n);
13908
0
                    return true;
13909
0
                  });
13910
0
13911
0
    auto progress = [&](size_t current, size_t total) {
13912
0
      if (!req.download_progress || redirect) { return true; }
13913
0
      auto ret = req.download_progress(current, total);
13914
0
      if (!ret) {
13915
0
        error = Error::Canceled;
13916
0
        output_error_log(error, &req);
13917
0
      }
13918
0
      return ret;
13919
0
    };
13920
0
13921
0
    if (res.has_header("Content-Length")) {
13922
0
      if (!req.content_receiver) {
13923
0
        auto len = res.get_header_value_u64("Content-Length");
13924
0
        if (len > res.body.max_size()) {
13925
0
          error = Error::Read;
13926
0
          output_error_log(error, &req);
13927
0
          return false;
13928
0
        }
13929
0
        // Cap the reservation by payload_max_length_ to avoid OOM when a
13930
0
        // hostile or malformed server sends an enormous Content-Length.
13931
0
        // The actual body read below is bounded by payload_max_length_,
13932
0
        // so reserving more than that is never useful.
13933
0
        auto reserve_len = static_cast<size_t>(len);
13934
0
        if (payload_max_length_ > 0 && reserve_len > payload_max_length_) {
13935
0
          reserve_len = payload_max_length_;
13936
0
        }
13937
0
        res.body.reserve(reserve_len);
13938
0
      }
13939
0
    }
13940
0
13941
0
    if (res.status != StatusCode::NotModified_304) {
13942
0
      int dummy_status;
13943
0
      auto max_length = (!has_payload_max_length_ && req.content_receiver)
13944
0
                            ? (std::numeric_limits<size_t>::max)()
13945
0
                            : payload_max_length_;
13946
0
      if (!detail::read_content(strm, res, max_length, dummy_status,
13947
0
                                std::move(progress), std::move(out),
13948
0
                                decompress_)) {
13949
0
        if (error != Error::Canceled) { error = Error::Read; }
13950
0
        output_error_log(error, &req);
13951
0
        return false;
13952
0
      }
13953
0
    }
13954
0
  }
13955
0
13956
0
  // Log
13957
0
  output_log(req, res);
13958
0
13959
0
  return true;
13960
0
}
13961
13962
inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
13963
    const std::string &boundary, const UploadFormDataItems &items,
13964
0
    const FormDataProviderItems &provider_items) const {
13965
0
  size_t cur_item = 0;
13966
0
  size_t cur_start = 0;
13967
0
  // cur_item and cur_start are copied to within the std::function and
13968
0
  // maintain state between successive calls
13969
0
  return [&, cur_item, cur_start](size_t offset,
13970
0
                                  DataSink &sink) mutable -> bool {
13971
0
    if (!offset && !items.empty()) {
13972
0
      sink.os << detail::serialize_multipart_formdata(items, boundary, false);
13973
0
      return true;
13974
0
    } else if (cur_item < provider_items.size()) {
13975
0
      if (!cur_start) {
13976
0
        const auto &begin = detail::serialize_multipart_formdata_item_begin(
13977
0
            provider_items[cur_item], boundary);
13978
0
        offset += begin.size();
13979
0
        cur_start = offset;
13980
0
        sink.os << begin;
13981
0
      }
13982
0
13983
0
      DataSink cur_sink;
13984
0
      auto has_data = true;
13985
0
      cur_sink.write = sink.write;
13986
0
      cur_sink.done = [&]() { has_data = false; };
13987
0
13988
0
      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
13989
0
        return false;
13990
0
      }
13991
0
13992
0
      if (!has_data) {
13993
0
        sink.os << detail::serialize_multipart_formdata_item_end();
13994
0
        cur_item++;
13995
0
        cur_start = 0;
13996
0
      }
13997
0
      return true;
13998
0
    } else {
13999
0
      sink.os << detail::serialize_multipart_formdata_finish(boundary);
14000
0
      sink.done();
14001
0
      return true;
14002
0
    }
14003
0
  };
14004
0
}
14005
14006
inline bool ClientImpl::process_socket(
14007
    const Socket &socket,
14008
    std::chrono::time_point<std::chrono::steady_clock> start_time,
14009
0
    std::function<bool(Stream &strm)> callback) {
14010
0
  return detail::process_client_socket(
14011
0
      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
14012
0
      write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
14013
0
}
14014
14015
0
inline bool ClientImpl::is_ssl() const { return false; }
14016
14017
inline Result ClientImpl::Get(const std::string &path,
14018
0
                              DownloadProgress progress) {
14019
0
  return Get(path, Headers(), std::move(progress));
14020
0
}
14021
14022
inline Result ClientImpl::Get(const std::string &path, const Params &params,
14023
                              const Headers &headers,
14024
0
                              DownloadProgress progress) {
14025
0
  if (params.empty()) { return Get(path, headers); }
14026
0
14027
0
  std::string path_with_query = append_query_params(path, params);
14028
0
  return Get(path_with_query, headers, std::move(progress));
14029
0
}
14030
14031
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
14032
0
                              DownloadProgress progress) {
14033
0
  Request req;
14034
0
  req.method = "GET";
14035
0
  req.path = path;
14036
0
  req.headers = headers;
14037
0
  req.download_progress = std::move(progress);
14038
0
  if (max_timeout_msec_ > 0) {
14039
0
    req.start_time_ = std::chrono::steady_clock::now();
14040
0
  }
14041
0
14042
0
  return send_(std::move(req));
14043
0
}
14044
14045
inline Result ClientImpl::Get(const std::string &path,
14046
                              ContentReceiver content_receiver,
14047
0
                              DownloadProgress progress) {
14048
0
  return Get(path, Headers(), nullptr, std::move(content_receiver),
14049
0
             std::move(progress));
14050
0
}
14051
14052
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
14053
                              ContentReceiver content_receiver,
14054
0
                              DownloadProgress progress) {
14055
0
  return Get(path, headers, nullptr, std::move(content_receiver),
14056
0
             std::move(progress));
14057
0
}
14058
14059
inline Result ClientImpl::Get(const std::string &path,
14060
                              ResponseHandler response_handler,
14061
                              ContentReceiver content_receiver,
14062
0
                              DownloadProgress progress) {
14063
0
  return Get(path, Headers(), std::move(response_handler),
14064
0
             std::move(content_receiver), std::move(progress));
14065
0
}
14066
14067
inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
14068
                              ResponseHandler response_handler,
14069
                              ContentReceiver content_receiver,
14070
0
                              DownloadProgress progress) {
14071
0
  Request req;
14072
0
  req.method = "GET";
14073
0
  req.path = path;
14074
0
  req.headers = headers;
14075
0
  req.response_handler = std::move(response_handler);
14076
0
  req.content_receiver =
14077
0
      [content_receiver](const char *data, size_t data_length,
14078
0
                         size_t /*offset*/, size_t /*total_length*/) {
14079
0
        return content_receiver(data, data_length);
14080
0
      };
14081
0
  req.download_progress = std::move(progress);
14082
0
  if (max_timeout_msec_ > 0) {
14083
0
    req.start_time_ = std::chrono::steady_clock::now();
14084
0
  }
14085
0
14086
0
  return send_(std::move(req));
14087
0
}
14088
14089
inline Result ClientImpl::Get(const std::string &path, const Params &params,
14090
                              const Headers &headers,
14091
                              ContentReceiver content_receiver,
14092
0
                              DownloadProgress progress) {
14093
0
  return Get(path, params, headers, nullptr, std::move(content_receiver),
14094
0
             std::move(progress));
14095
0
}
14096
14097
inline Result ClientImpl::Get(const std::string &path, const Params &params,
14098
                              const Headers &headers,
14099
                              ResponseHandler response_handler,
14100
                              ContentReceiver content_receiver,
14101
0
                              DownloadProgress progress) {
14102
0
  if (params.empty()) {
14103
0
    return Get(path, headers, std::move(response_handler),
14104
0
               std::move(content_receiver), std::move(progress));
14105
0
  }
14106
0
14107
0
  std::string path_with_query = append_query_params(path, params);
14108
0
  return Get(path_with_query, headers, std::move(response_handler),
14109
0
             std::move(content_receiver), std::move(progress));
14110
0
}
14111
14112
0
inline Result ClientImpl::Head(const std::string &path) {
14113
0
  return Head(path, Headers());
14114
0
}
14115
14116
inline Result ClientImpl::Head(const std::string &path,
14117
0
                               const Headers &headers) {
14118
0
  Request req;
14119
0
  req.method = "HEAD";
14120
0
  req.headers = headers;
14121
0
  req.path = path;
14122
0
  if (max_timeout_msec_ > 0) {
14123
0
    req.start_time_ = std::chrono::steady_clock::now();
14124
0
  }
14125
0
14126
0
  return send_(std::move(req));
14127
0
}
14128
14129
0
inline Result ClientImpl::Post(const std::string &path) {
14130
0
  return Post(path, std::string(), std::string());
14131
0
}
14132
14133
inline Result ClientImpl::Post(const std::string &path,
14134
0
                               const Headers &headers) {
14135
0
  return Post(path, headers, nullptr, 0, std::string());
14136
0
}
14137
14138
inline Result ClientImpl::Post(const std::string &path, const char *body,
14139
                               size_t content_length,
14140
                               const std::string &content_type,
14141
0
                               UploadProgress progress) {
14142
0
  return Post(path, Headers(), body, content_length, content_type, progress);
14143
0
}
14144
14145
inline Result ClientImpl::Post(const std::string &path, const std::string &body,
14146
                               const std::string &content_type,
14147
0
                               UploadProgress progress) {
14148
0
  return Post(path, Headers(), body, content_type, progress);
14149
0
}
14150
14151
0
inline Result ClientImpl::Post(const std::string &path, const Params &params) {
14152
0
  return Post(path, Headers(), params);
14153
0
}
14154
14155
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
14156
                               ContentProvider content_provider,
14157
                               const std::string &content_type,
14158
0
                               UploadProgress progress) {
14159
0
  return Post(path, Headers(), content_length, std::move(content_provider),
14160
0
              content_type, progress);
14161
0
}
14162
14163
inline Result ClientImpl::Post(const std::string &path, size_t content_length,
14164
                               ContentProvider content_provider,
14165
                               const std::string &content_type,
14166
                               ContentReceiver content_receiver,
14167
0
                               UploadProgress progress) {
14168
0
  return Post(path, Headers(), content_length, std::move(content_provider),
14169
0
              content_type, std::move(content_receiver), progress);
14170
0
}
14171
14172
inline Result ClientImpl::Post(const std::string &path,
14173
                               ContentProviderWithoutLength content_provider,
14174
                               const std::string &content_type,
14175
0
                               UploadProgress progress) {
14176
0
  return Post(path, Headers(), std::move(content_provider), content_type,
14177
0
              progress);
14178
0
}
14179
14180
inline Result ClientImpl::Post(const std::string &path,
14181
                               ContentProviderWithoutLength content_provider,
14182
                               const std::string &content_type,
14183
                               ContentReceiver content_receiver,
14184
0
                               UploadProgress progress) {
14185
0
  return Post(path, Headers(), std::move(content_provider), content_type,
14186
0
              std::move(content_receiver), progress);
14187
0
}
14188
14189
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14190
0
                               const Params &params) {
14191
0
  auto query = detail::params_to_query_str(params);
14192
0
  return Post(path, headers, query, "application/x-www-form-urlencoded");
14193
0
}
14194
14195
inline Result ClientImpl::Post(const std::string &path,
14196
                               const UploadFormDataItems &items,
14197
0
                               UploadProgress progress) {
14198
0
  return Post(path, Headers(), items, progress);
14199
0
}
14200
14201
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14202
                               const UploadFormDataItems &items,
14203
0
                               UploadProgress progress) {
14204
0
  const auto &boundary = detail::make_multipart_data_boundary();
14205
0
  const auto &content_type =
14206
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14207
0
  auto content_length = detail::get_multipart_content_length(items, boundary);
14208
0
  return Post(path, headers, content_length,
14209
0
              detail::make_multipart_content_provider(items, boundary),
14210
0
              content_type, progress);
14211
0
}
14212
14213
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14214
                               const UploadFormDataItems &items,
14215
                               const std::string &boundary,
14216
0
                               UploadProgress progress) {
14217
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
14218
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
14219
0
  }
14220
0
14221
0
  const auto &content_type =
14222
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14223
0
  auto content_length = detail::get_multipart_content_length(items, boundary);
14224
0
  return Post(path, headers, content_length,
14225
0
              detail::make_multipart_content_provider(items, boundary),
14226
0
              content_type, progress);
14227
0
}
14228
14229
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14230
                               const char *body, size_t content_length,
14231
                               const std::string &content_type,
14232
0
                               UploadProgress progress) {
14233
0
  return send_with_content_provider_and_receiver(
14234
0
      "POST", path, headers, body, content_length, nullptr, nullptr,
14235
0
      content_type, nullptr, progress);
14236
0
}
14237
14238
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14239
                               const std::string &body,
14240
                               const std::string &content_type,
14241
0
                               UploadProgress progress) {
14242
0
  return send_with_content_provider_and_receiver(
14243
0
      "POST", path, headers, body.data(), body.size(), nullptr, nullptr,
14244
0
      content_type, nullptr, progress);
14245
0
}
14246
14247
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14248
                               size_t content_length,
14249
                               ContentProvider content_provider,
14250
                               const std::string &content_type,
14251
0
                               UploadProgress progress) {
14252
0
  return send_with_content_provider_and_receiver(
14253
0
      "POST", path, headers, nullptr, content_length,
14254
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
14255
0
}
14256
14257
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14258
                               size_t content_length,
14259
                               ContentProvider content_provider,
14260
                               const std::string &content_type,
14261
                               ContentReceiver content_receiver,
14262
0
                               DownloadProgress progress) {
14263
0
  return send_with_content_provider_and_receiver(
14264
0
      "POST", path, headers, nullptr, content_length,
14265
0
      std::move(content_provider), nullptr, content_type,
14266
0
      std::move(content_receiver), std::move(progress));
14267
0
}
14268
14269
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14270
                               ContentProviderWithoutLength content_provider,
14271
                               const std::string &content_type,
14272
0
                               UploadProgress progress) {
14273
0
  return send_with_content_provider_and_receiver(
14274
0
      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14275
0
      content_type, nullptr, progress);
14276
0
}
14277
14278
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14279
                               ContentProviderWithoutLength content_provider,
14280
                               const std::string &content_type,
14281
                               ContentReceiver content_receiver,
14282
0
                               DownloadProgress progress) {
14283
0
  return send_with_content_provider_and_receiver(
14284
0
      "POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14285
0
      content_type, std::move(content_receiver), std::move(progress));
14286
0
}
14287
14288
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14289
                               const UploadFormDataItems &items,
14290
                               const FormDataProviderItems &provider_items,
14291
0
                               UploadProgress progress) {
14292
0
  const auto &boundary = detail::make_multipart_data_boundary();
14293
0
  const auto &content_type =
14294
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14295
0
  return send_with_content_provider_and_receiver(
14296
0
      "POST", path, headers, nullptr, 0, nullptr,
14297
0
      get_multipart_content_provider(boundary, items, provider_items),
14298
0
      content_type, nullptr, progress);
14299
0
}
14300
14301
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
14302
                               const std::string &body,
14303
                               const std::string &content_type,
14304
                               ContentReceiver content_receiver,
14305
0
                               DownloadProgress progress) {
14306
0
  Request req;
14307
0
  req.method = "POST";
14308
0
  req.path = path;
14309
0
  req.headers = headers;
14310
0
  req.body = body;
14311
0
  req.content_receiver =
14312
0
      [content_receiver](const char *data, size_t data_length,
14313
0
                         size_t /*offset*/, size_t /*total_length*/) {
14314
0
        return content_receiver(data, data_length);
14315
0
      };
14316
0
  req.download_progress = std::move(progress);
14317
0
14318
0
  if (max_timeout_msec_ > 0) {
14319
0
    req.start_time_ = std::chrono::steady_clock::now();
14320
0
  }
14321
0
14322
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
14323
0
14324
0
  return send_(std::move(req));
14325
0
}
14326
14327
0
inline Result ClientImpl::Put(const std::string &path) {
14328
0
  return Put(path, std::string(), std::string());
14329
0
}
14330
14331
0
inline Result ClientImpl::Put(const std::string &path, const Headers &headers) {
14332
0
  return Put(path, headers, nullptr, 0, std::string());
14333
0
}
14334
14335
inline Result ClientImpl::Put(const std::string &path, const char *body,
14336
                              size_t content_length,
14337
                              const std::string &content_type,
14338
0
                              UploadProgress progress) {
14339
0
  return Put(path, Headers(), body, content_length, content_type, progress);
14340
0
}
14341
14342
inline Result ClientImpl::Put(const std::string &path, const std::string &body,
14343
                              const std::string &content_type,
14344
0
                              UploadProgress progress) {
14345
0
  return Put(path, Headers(), body, content_type, progress);
14346
0
}
14347
14348
0
inline Result ClientImpl::Put(const std::string &path, const Params &params) {
14349
0
  return Put(path, Headers(), params);
14350
0
}
14351
14352
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
14353
                              ContentProvider content_provider,
14354
                              const std::string &content_type,
14355
0
                              UploadProgress progress) {
14356
0
  return Put(path, Headers(), content_length, std::move(content_provider),
14357
0
             content_type, progress);
14358
0
}
14359
14360
inline Result ClientImpl::Put(const std::string &path, size_t content_length,
14361
                              ContentProvider content_provider,
14362
                              const std::string &content_type,
14363
                              ContentReceiver content_receiver,
14364
0
                              UploadProgress progress) {
14365
0
  return Put(path, Headers(), content_length, std::move(content_provider),
14366
0
             content_type, std::move(content_receiver), progress);
14367
0
}
14368
14369
inline Result ClientImpl::Put(const std::string &path,
14370
                              ContentProviderWithoutLength content_provider,
14371
                              const std::string &content_type,
14372
0
                              UploadProgress progress) {
14373
0
  return Put(path, Headers(), std::move(content_provider), content_type,
14374
0
             progress);
14375
0
}
14376
14377
inline Result ClientImpl::Put(const std::string &path,
14378
                              ContentProviderWithoutLength content_provider,
14379
                              const std::string &content_type,
14380
                              ContentReceiver content_receiver,
14381
0
                              UploadProgress progress) {
14382
0
  return Put(path, Headers(), std::move(content_provider), content_type,
14383
0
             std::move(content_receiver), progress);
14384
0
}
14385
14386
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14387
0
                              const Params &params) {
14388
0
  auto query = detail::params_to_query_str(params);
14389
0
  return Put(path, headers, query, "application/x-www-form-urlencoded");
14390
0
}
14391
14392
inline Result ClientImpl::Put(const std::string &path,
14393
                              const UploadFormDataItems &items,
14394
0
                              UploadProgress progress) {
14395
0
  return Put(path, Headers(), items, progress);
14396
0
}
14397
14398
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14399
                              const UploadFormDataItems &items,
14400
0
                              UploadProgress progress) {
14401
0
  const auto &boundary = detail::make_multipart_data_boundary();
14402
0
  const auto &content_type =
14403
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14404
0
  auto content_length = detail::get_multipart_content_length(items, boundary);
14405
0
  return Put(path, headers, content_length,
14406
0
             detail::make_multipart_content_provider(items, boundary),
14407
0
             content_type, progress);
14408
0
}
14409
14410
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14411
                              const UploadFormDataItems &items,
14412
                              const std::string &boundary,
14413
0
                              UploadProgress progress) {
14414
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
14415
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
14416
0
  }
14417
0
14418
0
  const auto &content_type =
14419
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14420
0
  auto content_length = detail::get_multipart_content_length(items, boundary);
14421
0
  return Put(path, headers, content_length,
14422
0
             detail::make_multipart_content_provider(items, boundary),
14423
0
             content_type, progress);
14424
0
}
14425
14426
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14427
                              const char *body, size_t content_length,
14428
                              const std::string &content_type,
14429
0
                              UploadProgress progress) {
14430
0
  return send_with_content_provider_and_receiver(
14431
0
      "PUT", path, headers, body, content_length, nullptr, nullptr,
14432
0
      content_type, nullptr, progress);
14433
0
}
14434
14435
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14436
                              const std::string &body,
14437
                              const std::string &content_type,
14438
0
                              UploadProgress progress) {
14439
0
  return send_with_content_provider_and_receiver(
14440
0
      "PUT", path, headers, body.data(), body.size(), nullptr, nullptr,
14441
0
      content_type, nullptr, progress);
14442
0
}
14443
14444
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14445
                              size_t content_length,
14446
                              ContentProvider content_provider,
14447
                              const std::string &content_type,
14448
0
                              UploadProgress progress) {
14449
0
  return send_with_content_provider_and_receiver(
14450
0
      "PUT", path, headers, nullptr, content_length,
14451
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
14452
0
}
14453
14454
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14455
                              size_t content_length,
14456
                              ContentProvider content_provider,
14457
                              const std::string &content_type,
14458
                              ContentReceiver content_receiver,
14459
0
                              UploadProgress progress) {
14460
0
  return send_with_content_provider_and_receiver(
14461
0
      "PUT", path, headers, nullptr, content_length,
14462
0
      std::move(content_provider), nullptr, content_type,
14463
0
      std::move(content_receiver), progress);
14464
0
}
14465
14466
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14467
                              ContentProviderWithoutLength content_provider,
14468
                              const std::string &content_type,
14469
0
                              UploadProgress progress) {
14470
0
  return send_with_content_provider_and_receiver(
14471
0
      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14472
0
      content_type, nullptr, progress);
14473
0
}
14474
14475
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14476
                              ContentProviderWithoutLength content_provider,
14477
                              const std::string &content_type,
14478
                              ContentReceiver content_receiver,
14479
0
                              UploadProgress progress) {
14480
0
  return send_with_content_provider_and_receiver(
14481
0
      "PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14482
0
      content_type, std::move(content_receiver), progress);
14483
0
}
14484
14485
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14486
                              const UploadFormDataItems &items,
14487
                              const FormDataProviderItems &provider_items,
14488
0
                              UploadProgress progress) {
14489
0
  const auto &boundary = detail::make_multipart_data_boundary();
14490
0
  const auto &content_type =
14491
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14492
0
  return send_with_content_provider_and_receiver(
14493
0
      "PUT", path, headers, nullptr, 0, nullptr,
14494
0
      get_multipart_content_provider(boundary, items, provider_items),
14495
0
      content_type, nullptr, progress);
14496
0
}
14497
14498
inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
14499
                              const std::string &body,
14500
                              const std::string &content_type,
14501
                              ContentReceiver content_receiver,
14502
0
                              DownloadProgress progress) {
14503
0
  Request req;
14504
0
  req.method = "PUT";
14505
0
  req.path = path;
14506
0
  req.headers = headers;
14507
0
  req.body = body;
14508
0
  req.content_receiver =
14509
0
      [content_receiver](const char *data, size_t data_length,
14510
0
                         size_t /*offset*/, size_t /*total_length*/) {
14511
0
        return content_receiver(data, data_length);
14512
0
      };
14513
0
  req.download_progress = std::move(progress);
14514
0
14515
0
  if (max_timeout_msec_ > 0) {
14516
0
    req.start_time_ = std::chrono::steady_clock::now();
14517
0
  }
14518
0
14519
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
14520
0
14521
0
  return send_(std::move(req));
14522
0
}
14523
14524
0
inline Result ClientImpl::Patch(const std::string &path) {
14525
0
  return Patch(path, std::string(), std::string());
14526
0
}
14527
14528
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14529
0
                                UploadProgress progress) {
14530
0
  return Patch(path, headers, nullptr, 0, std::string(), progress);
14531
0
}
14532
14533
inline Result ClientImpl::Patch(const std::string &path, const char *body,
14534
                                size_t content_length,
14535
                                const std::string &content_type,
14536
0
                                UploadProgress progress) {
14537
0
  return Patch(path, Headers(), body, content_length, content_type, progress);
14538
0
}
14539
14540
inline Result ClientImpl::Patch(const std::string &path,
14541
                                const std::string &body,
14542
                                const std::string &content_type,
14543
0
                                UploadProgress progress) {
14544
0
  return Patch(path, Headers(), body, content_type, progress);
14545
0
}
14546
14547
0
inline Result ClientImpl::Patch(const std::string &path, const Params &params) {
14548
0
  return Patch(path, Headers(), params);
14549
0
}
14550
14551
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
14552
                                ContentProvider content_provider,
14553
                                const std::string &content_type,
14554
0
                                UploadProgress progress) {
14555
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
14556
0
               content_type, progress);
14557
0
}
14558
14559
inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
14560
                                ContentProvider content_provider,
14561
                                const std::string &content_type,
14562
                                ContentReceiver content_receiver,
14563
0
                                UploadProgress progress) {
14564
0
  return Patch(path, Headers(), content_length, std::move(content_provider),
14565
0
               content_type, std::move(content_receiver), progress);
14566
0
}
14567
14568
inline Result ClientImpl::Patch(const std::string &path,
14569
                                ContentProviderWithoutLength content_provider,
14570
                                const std::string &content_type,
14571
0
                                UploadProgress progress) {
14572
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
14573
0
               progress);
14574
0
}
14575
14576
inline Result ClientImpl::Patch(const std::string &path,
14577
                                ContentProviderWithoutLength content_provider,
14578
                                const std::string &content_type,
14579
                                ContentReceiver content_receiver,
14580
0
                                UploadProgress progress) {
14581
0
  return Patch(path, Headers(), std::move(content_provider), content_type,
14582
0
               std::move(content_receiver), progress);
14583
0
}
14584
14585
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14586
0
                                const Params &params) {
14587
0
  auto query = detail::params_to_query_str(params);
14588
0
  return Patch(path, headers, query, "application/x-www-form-urlencoded");
14589
0
}
14590
14591
inline Result ClientImpl::Patch(const std::string &path,
14592
                                const UploadFormDataItems &items,
14593
0
                                UploadProgress progress) {
14594
0
  return Patch(path, Headers(), items, progress);
14595
0
}
14596
14597
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14598
                                const UploadFormDataItems &items,
14599
0
                                UploadProgress progress) {
14600
0
  const auto &boundary = detail::make_multipart_data_boundary();
14601
0
  const auto &content_type =
14602
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14603
0
  auto content_length = detail::get_multipart_content_length(items, boundary);
14604
0
  return Patch(path, headers, content_length,
14605
0
               detail::make_multipart_content_provider(items, boundary),
14606
0
               content_type, progress);
14607
0
}
14608
14609
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14610
                                const UploadFormDataItems &items,
14611
                                const std::string &boundary,
14612
0
                                UploadProgress progress) {
14613
0
  if (!detail::is_multipart_boundary_chars_valid(boundary)) {
14614
0
    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
14615
0
  }
14616
0
14617
0
  const auto &content_type =
14618
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14619
0
  auto content_length = detail::get_multipart_content_length(items, boundary);
14620
0
  return Patch(path, headers, content_length,
14621
0
               detail::make_multipart_content_provider(items, boundary),
14622
0
               content_type, progress);
14623
0
}
14624
14625
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14626
                                const char *body, size_t content_length,
14627
                                const std::string &content_type,
14628
0
                                UploadProgress progress) {
14629
0
  return send_with_content_provider_and_receiver(
14630
0
      "PATCH", path, headers, body, content_length, nullptr, nullptr,
14631
0
      content_type, nullptr, progress);
14632
0
}
14633
14634
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14635
                                const std::string &body,
14636
                                const std::string &content_type,
14637
0
                                UploadProgress progress) {
14638
0
  return send_with_content_provider_and_receiver(
14639
0
      "PATCH", path, headers, body.data(), body.size(), nullptr, nullptr,
14640
0
      content_type, nullptr, progress);
14641
0
}
14642
14643
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14644
                                size_t content_length,
14645
                                ContentProvider content_provider,
14646
                                const std::string &content_type,
14647
0
                                UploadProgress progress) {
14648
0
  return send_with_content_provider_and_receiver(
14649
0
      "PATCH", path, headers, nullptr, content_length,
14650
0
      std::move(content_provider), nullptr, content_type, nullptr, progress);
14651
0
}
14652
14653
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14654
                                size_t content_length,
14655
                                ContentProvider content_provider,
14656
                                const std::string &content_type,
14657
                                ContentReceiver content_receiver,
14658
0
                                UploadProgress progress) {
14659
0
  return send_with_content_provider_and_receiver(
14660
0
      "PATCH", path, headers, nullptr, content_length,
14661
0
      std::move(content_provider), nullptr, content_type,
14662
0
      std::move(content_receiver), progress);
14663
0
}
14664
14665
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14666
                                ContentProviderWithoutLength content_provider,
14667
                                const std::string &content_type,
14668
0
                                UploadProgress progress) {
14669
0
  return send_with_content_provider_and_receiver(
14670
0
      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14671
0
      content_type, nullptr, progress);
14672
0
}
14673
14674
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14675
                                ContentProviderWithoutLength content_provider,
14676
                                const std::string &content_type,
14677
                                ContentReceiver content_receiver,
14678
0
                                UploadProgress progress) {
14679
0
  return send_with_content_provider_and_receiver(
14680
0
      "PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
14681
0
      content_type, std::move(content_receiver), progress);
14682
0
}
14683
14684
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14685
                                const UploadFormDataItems &items,
14686
                                const FormDataProviderItems &provider_items,
14687
0
                                UploadProgress progress) {
14688
0
  const auto &boundary = detail::make_multipart_data_boundary();
14689
0
  const auto &content_type =
14690
0
      detail::serialize_multipart_formdata_get_content_type(boundary);
14691
0
  return send_with_content_provider_and_receiver(
14692
0
      "PATCH", path, headers, nullptr, 0, nullptr,
14693
0
      get_multipart_content_provider(boundary, items, provider_items),
14694
0
      content_type, nullptr, progress);
14695
0
}
14696
14697
inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
14698
                                const std::string &body,
14699
                                const std::string &content_type,
14700
                                ContentReceiver content_receiver,
14701
0
                                DownloadProgress progress) {
14702
0
  Request req;
14703
0
  req.method = "PATCH";
14704
0
  req.path = path;
14705
0
  req.headers = headers;
14706
0
  req.body = body;
14707
0
  req.content_receiver =
14708
0
      [content_receiver](const char *data, size_t data_length,
14709
0
                         size_t /*offset*/, size_t /*total_length*/) {
14710
0
        return content_receiver(data, data_length);
14711
0
      };
14712
0
  req.download_progress = std::move(progress);
14713
0
14714
0
  if (max_timeout_msec_ > 0) {
14715
0
    req.start_time_ = std::chrono::steady_clock::now();
14716
0
  }
14717
0
14718
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
14719
0
14720
0
  return send_(std::move(req));
14721
0
}
14722
14723
inline Result ClientImpl::Delete(const std::string &path,
14724
0
                                 DownloadProgress progress) {
14725
0
  return Delete(path, Headers(), std::string(), std::string(), progress);
14726
0
}
14727
14728
inline Result ClientImpl::Delete(const std::string &path,
14729
                                 const Headers &headers,
14730
0
                                 DownloadProgress progress) {
14731
0
  return Delete(path, headers, std::string(), std::string(), progress);
14732
0
}
14733
14734
inline Result ClientImpl::Delete(const std::string &path, const char *body,
14735
                                 size_t content_length,
14736
                                 const std::string &content_type,
14737
0
                                 DownloadProgress progress) {
14738
0
  return Delete(path, Headers(), body, content_length, content_type, progress);
14739
0
}
14740
14741
inline Result ClientImpl::Delete(const std::string &path,
14742
                                 const std::string &body,
14743
                                 const std::string &content_type,
14744
0
                                 DownloadProgress progress) {
14745
0
  return Delete(path, Headers(), body.data(), body.size(), content_type,
14746
0
                progress);
14747
0
}
14748
14749
inline Result ClientImpl::Delete(const std::string &path,
14750
                                 const Headers &headers,
14751
                                 const std::string &body,
14752
                                 const std::string &content_type,
14753
0
                                 DownloadProgress progress) {
14754
0
  return Delete(path, headers, body.data(), body.size(), content_type,
14755
0
                progress);
14756
0
}
14757
14758
inline Result ClientImpl::Delete(const std::string &path, const Params &params,
14759
0
                                 DownloadProgress progress) {
14760
0
  return Delete(path, Headers(), params, progress);
14761
0
}
14762
14763
inline Result ClientImpl::Delete(const std::string &path,
14764
                                 const Headers &headers, const Params &params,
14765
0
                                 DownloadProgress progress) {
14766
0
  auto query = detail::params_to_query_str(params);
14767
0
  return Delete(path, headers, query, "application/x-www-form-urlencoded",
14768
0
                progress);
14769
0
}
14770
14771
inline Result ClientImpl::Delete(const std::string &path,
14772
                                 const Headers &headers, const char *body,
14773
                                 size_t content_length,
14774
                                 const std::string &content_type,
14775
0
                                 DownloadProgress progress) {
14776
0
  Request req;
14777
0
  req.method = "DELETE";
14778
0
  req.headers = headers;
14779
0
  req.path = path;
14780
0
  req.download_progress = std::move(progress);
14781
0
  if (max_timeout_msec_ > 0) {
14782
0
    req.start_time_ = std::chrono::steady_clock::now();
14783
0
  }
14784
0
14785
0
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
14786
0
  req.body.assign(body, content_length);
14787
0
14788
0
  return send_(std::move(req));
14789
0
}
14790
14791
0
inline Result ClientImpl::Options(const std::string &path) {
14792
0
  return Options(path, Headers());
14793
0
}
14794
14795
inline Result ClientImpl::Options(const std::string &path,
14796
0
                                  const Headers &headers) {
14797
0
  Request req;
14798
0
  req.method = "OPTIONS";
14799
0
  req.headers = headers;
14800
0
  req.path = path;
14801
0
  if (max_timeout_msec_ > 0) {
14802
0
    req.start_time_ = std::chrono::steady_clock::now();
14803
0
  }
14804
0
14805
0
  return send_(std::move(req));
14806
0
}
14807
14808
0
inline void ClientImpl::stop() {
14809
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
14810
0
14811
0
  // If there is anything ongoing right now, the ONLY thread-safe thing we can
14812
0
  // do is to shutdown_socket, so that threads using this socket suddenly
14813
0
  // discover they can't read/write any more and error out. Everything else
14814
0
  // (closing the socket, shutting ssl down) is unsafe because these actions
14815
0
  // are not thread-safe.
14816
0
  if (socket_requests_in_flight_ > 0) {
14817
0
    shutdown_socket(socket_);
14818
0
14819
0
    // Aside from that, we set a flag for the socket to be closed when we're
14820
0
    // done.
14821
0
    socket_should_be_closed_when_request_is_done_ = true;
14822
0
    return;
14823
0
  }
14824
0
14825
0
  disconnect(/*gracefully=*/true);
14826
0
}
14827
14828
0
inline std::string ClientImpl::host() const { return host_; }
14829
14830
0
inline int ClientImpl::port() const { return port_; }
14831
14832
0
inline size_t ClientImpl::is_socket_open() const {
14833
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
14834
0
  return socket_.is_open();
14835
0
}
14836
14837
0
inline socket_t ClientImpl::socket() const { return socket_.sock; }
14838
14839
0
inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
14840
0
  connection_timeout_sec_ = sec;
14841
0
  connection_timeout_usec_ = usec;
14842
0
}
14843
14844
0
inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
14845
0
  read_timeout_sec_ = sec;
14846
0
  read_timeout_usec_ = usec;
14847
0
}
14848
14849
0
inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
14850
0
  write_timeout_sec_ = sec;
14851
0
  write_timeout_usec_ = usec;
14852
0
}
14853
14854
0
inline void ClientImpl::set_max_timeout(time_t msec) {
14855
0
  max_timeout_msec_ = msec;
14856
0
}
14857
14858
inline void ClientImpl::set_basic_auth(const std::string &username,
14859
0
                                       const std::string &password) {
14860
0
  basic_auth_username_ = username;
14861
0
  basic_auth_password_ = password;
14862
0
}
14863
14864
0
inline void ClientImpl::set_bearer_token_auth(const std::string &token) {
14865
0
  bearer_token_auth_token_ = token;
14866
0
}
14867
14868
0
inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
14869
14870
0
inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
14871
14872
0
inline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
14873
14874
inline void
14875
0
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
14876
0
  addr_map_ = std::move(addr_map);
14877
0
}
14878
14879
0
inline void ClientImpl::set_default_headers(Headers headers) {
14880
0
  default_headers_ = std::move(headers);
14881
0
}
14882
14883
inline void ClientImpl::set_header_writer(
14884
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
14885
0
  header_writer_ = writer;
14886
0
}
14887
14888
0
inline void ClientImpl::set_address_family(int family) {
14889
0
  address_family_ = family;
14890
0
}
14891
14892
0
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
14893
14894
0
inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
14895
14896
0
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
14897
0
  socket_options_ = std::move(socket_options);
14898
0
}
14899
14900
0
inline void ClientImpl::set_compress(bool on) { compress_ = on; }
14901
14902
0
inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
14903
14904
0
inline void ClientImpl::set_payload_max_length(size_t length) {
14905
0
  payload_max_length_ = length;
14906
0
  has_payload_max_length_ = true;
14907
0
}
14908
14909
0
inline void ClientImpl::set_interface(const std::string &intf) {
14910
0
  interface_ = intf;
14911
0
}
14912
14913
0
inline void ClientImpl::set_proxy(const std::string &host, int port) {
14914
0
  proxy_host_ = host;
14915
0
  proxy_port_ = port;
14916
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
14917
0
  disconnect(/*gracefully=*/true);
14918
0
}
14919
14920
inline void ClientImpl::set_proxy_basic_auth(const std::string &username,
14921
0
                                             const std::string &password) {
14922
0
  proxy_basic_auth_username_ = username;
14923
0
  proxy_basic_auth_password_ = password;
14924
0
}
14925
14926
0
inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
14927
0
  proxy_bearer_token_auth_token_ = token;
14928
0
}
14929
14930
0
inline void ClientImpl::set_no_proxy(const std::vector<std::string> &patterns) {
14931
0
  std::vector<detail::NoProxyEntry> parsed;
14932
0
  parsed.reserve(patterns.size());
14933
0
  for (const auto &p : patterns) {
14934
0
    auto trimmed = detail::trim_copy(p);
14935
0
    if (trimmed.empty()) { continue; }
14936
0
    detail::NoProxyEntry entry;
14937
0
    if (detail::parse_no_proxy_entry(trimmed, entry)) {
14938
0
      parsed.push_back(std::move(entry));
14939
0
    }
14940
0
  }
14941
0
  no_proxy_entries_ = std::move(parsed);
14942
0
  std::lock_guard<std::mutex> guard(socket_mutex_);
14943
0
  disconnect(/*gracefully=*/true);
14944
0
}
14945
14946
#ifdef CPPHTTPLIB_SSL_ENABLED
14947
inline void ClientImpl::set_digest_auth(const std::string &username,
14948
                                        const std::string &password) {
14949
  digest_auth_username_ = username;
14950
  digest_auth_password_ = password;
14951
}
14952
14953
inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
14954
                                         const std::string &ca_cert_dir_path) {
14955
  ca_cert_file_path_ = ca_cert_file_path;
14956
  ca_cert_dir_path_ = ca_cert_dir_path;
14957
}
14958
14959
inline void ClientImpl::set_proxy_digest_auth(const std::string &username,
14960
                                              const std::string &password) {
14961
  proxy_digest_auth_username_ = username;
14962
  proxy_digest_auth_password_ = password;
14963
}
14964
14965
inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
14966
  server_certificate_verification_ = enabled;
14967
}
14968
14969
inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
14970
  server_hostname_verification_ = enabled;
14971
}
14972
#endif
14973
14974
0
inline void ClientImpl::set_logger(Logger logger) {
14975
0
  logger_ = std::move(logger);
14976
0
}
14977
14978
0
inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
14979
0
  error_logger_ = std::move(error_logger);
14980
0
}
14981
14982
/*
14983
 * SSL/TLS Common Implementation
14984
 */
14985
14986
inline ClientConnection::~ClientConnection() {
14987
#ifdef CPPHTTPLIB_SSL_ENABLED
14988
  if (session) {
14989
    tls::shutdown(session, true);
14990
    tls::free_session(session);
14991
    session = nullptr;
14992
  }
14993
#endif
14994
14995
  if (sock != INVALID_SOCKET) {
14996
    detail::close_socket(sock);
14997
    sock = INVALID_SOCKET;
14998
  }
14999
}
15000
15001
// Universal client implementation
15002
inline Client::Client(const std::string &scheme_host_port)
15003
    : Client(scheme_host_port, std::string(), std::string()) {}
15004
15005
inline Client::Client(const std::string &scheme_host_port,
15006
                      const std::string &client_cert_path,
15007
                      const std::string &client_key_path) {
15008
  detail::UrlComponents uc;
15009
  if (detail::parse_url(scheme_host_port, uc) && !uc.host.empty()) {
15010
    auto &scheme = uc.scheme;
15011
15012
#ifdef CPPHTTPLIB_SSL_ENABLED
15013
    if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
15014
#else
15015
    if (!scheme.empty() && scheme != "http") {
15016
#endif
15017
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
15018
      std::string msg = "'" + scheme + "' scheme is not supported.";
15019
      throw std::invalid_argument(msg);
15020
#endif
15021
      return;
15022
    }
15023
15024
    auto is_ssl = scheme == "https";
15025
15026
    auto host = std::move(uc.host);
15027
15028
    auto port = is_ssl ? 443 : 80;
15029
    if (!uc.port.empty() && !detail::parse_port(uc.port, port)) { return; }
15030
15031
    if (is_ssl) {
15032
#ifdef CPPHTTPLIB_SSL_ENABLED
15033
      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
15034
                                            client_key_path);
15035
      is_ssl_ = is_ssl;
15036
#endif
15037
    } else {
15038
      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
15039
                                             client_key_path);
15040
    }
15041
  } else {
15042
    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
15043
    // if port param below changes.
15044
    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
15045
                                           client_cert_path, client_key_path);
15046
  }
15047
}
15048
15049
inline Client::Client(const std::string &host, int port)
15050
    : Client(host, port, std::string(), std::string()) {}
15051
15052
inline Client::Client(const std::string &host, int port,
15053
                      const std::string &client_cert_path,
15054
                      const std::string &client_key_path)
15055
    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
15056
                                           client_key_path)) {}
15057
15058
inline Client::~Client() = default;
15059
15060
0
inline bool Client::is_valid() const {
15061
0
  return cli_ != nullptr && cli_->is_valid();
15062
0
}
15063
15064
0
inline Result Client::Get(const std::string &path, DownloadProgress progress) {
15065
0
  return cli_->Get(path, std::move(progress));
15066
0
}
15067
inline Result Client::Get(const std::string &path, const Headers &headers,
15068
0
                          DownloadProgress progress) {
15069
0
  return cli_->Get(path, headers, std::move(progress));
15070
0
}
15071
inline Result Client::Get(const std::string &path,
15072
                          ContentReceiver content_receiver,
15073
0
                          DownloadProgress progress) {
15074
0
  return cli_->Get(path, std::move(content_receiver), std::move(progress));
15075
0
}
15076
inline Result Client::Get(const std::string &path, const Headers &headers,
15077
                          ContentReceiver content_receiver,
15078
0
                          DownloadProgress progress) {
15079
0
  return cli_->Get(path, headers, std::move(content_receiver),
15080
0
                   std::move(progress));
15081
0
}
15082
inline Result Client::Get(const std::string &path,
15083
                          ResponseHandler response_handler,
15084
                          ContentReceiver content_receiver,
15085
0
                          DownloadProgress progress) {
15086
0
  return cli_->Get(path, std::move(response_handler),
15087
0
                   std::move(content_receiver), std::move(progress));
15088
0
}
15089
inline Result Client::Get(const std::string &path, const Headers &headers,
15090
                          ResponseHandler response_handler,
15091
                          ContentReceiver content_receiver,
15092
0
                          DownloadProgress progress) {
15093
0
  return cli_->Get(path, headers, std::move(response_handler),
15094
0
                   std::move(content_receiver), std::move(progress));
15095
0
}
15096
inline Result Client::Get(const std::string &path, const Params &params,
15097
0
                          const Headers &headers, DownloadProgress progress) {
15098
0
  return cli_->Get(path, params, headers, std::move(progress));
15099
0
}
15100
inline Result Client::Get(const std::string &path, const Params &params,
15101
                          const Headers &headers,
15102
                          ContentReceiver content_receiver,
15103
0
                          DownloadProgress progress) {
15104
0
  return cli_->Get(path, params, headers, std::move(content_receiver),
15105
0
                   std::move(progress));
15106
0
}
15107
inline Result Client::Get(const std::string &path, const Params &params,
15108
                          const Headers &headers,
15109
                          ResponseHandler response_handler,
15110
                          ContentReceiver content_receiver,
15111
0
                          DownloadProgress progress) {
15112
0
  return cli_->Get(path, params, headers, std::move(response_handler),
15113
0
                   std::move(content_receiver), std::move(progress));
15114
0
}
15115
15116
0
inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
15117
0
inline Result Client::Head(const std::string &path, const Headers &headers) {
15118
0
  return cli_->Head(path, headers);
15119
0
}
15120
15121
0
inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
15122
0
inline Result Client::Post(const std::string &path, const Headers &headers) {
15123
0
  return cli_->Post(path, headers);
15124
0
}
15125
inline Result Client::Post(const std::string &path, const char *body,
15126
                           size_t content_length,
15127
                           const std::string &content_type,
15128
0
                           UploadProgress progress) {
15129
0
  return cli_->Post(path, body, content_length, content_type, progress);
15130
0
}
15131
inline Result Client::Post(const std::string &path, const Headers &headers,
15132
                           const char *body, size_t content_length,
15133
                           const std::string &content_type,
15134
0
                           UploadProgress progress) {
15135
0
  return cli_->Post(path, headers, body, content_length, content_type,
15136
0
                    progress);
15137
0
}
15138
inline Result Client::Post(const std::string &path, const std::string &body,
15139
                           const std::string &content_type,
15140
0
                           UploadProgress progress) {
15141
0
  return cli_->Post(path, body, content_type, progress);
15142
0
}
15143
inline Result Client::Post(const std::string &path, const Headers &headers,
15144
                           const std::string &body,
15145
                           const std::string &content_type,
15146
0
                           UploadProgress progress) {
15147
0
  return cli_->Post(path, headers, body, content_type, progress);
15148
0
}
15149
inline Result Client::Post(const std::string &path, size_t content_length,
15150
                           ContentProvider content_provider,
15151
                           const std::string &content_type,
15152
0
                           UploadProgress progress) {
15153
0
  return cli_->Post(path, content_length, std::move(content_provider),
15154
0
                    content_type, progress);
15155
0
}
15156
inline Result Client::Post(const std::string &path, size_t content_length,
15157
                           ContentProvider content_provider,
15158
                           const std::string &content_type,
15159
                           ContentReceiver content_receiver,
15160
0
                           UploadProgress progress) {
15161
0
  return cli_->Post(path, content_length, std::move(content_provider),
15162
0
                    content_type, std::move(content_receiver), progress);
15163
0
}
15164
inline Result Client::Post(const std::string &path,
15165
                           ContentProviderWithoutLength content_provider,
15166
                           const std::string &content_type,
15167
0
                           UploadProgress progress) {
15168
0
  return cli_->Post(path, std::move(content_provider), content_type, progress);
15169
0
}
15170
inline Result Client::Post(const std::string &path,
15171
                           ContentProviderWithoutLength content_provider,
15172
                           const std::string &content_type,
15173
                           ContentReceiver content_receiver,
15174
0
                           UploadProgress progress) {
15175
0
  return cli_->Post(path, std::move(content_provider), content_type,
15176
0
                    std::move(content_receiver), progress);
15177
0
}
15178
inline Result Client::Post(const std::string &path, const Headers &headers,
15179
                           size_t content_length,
15180
                           ContentProvider content_provider,
15181
                           const std::string &content_type,
15182
0
                           UploadProgress progress) {
15183
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
15184
0
                    content_type, progress);
15185
0
}
15186
inline Result Client::Post(const std::string &path, const Headers &headers,
15187
                           size_t content_length,
15188
                           ContentProvider content_provider,
15189
                           const std::string &content_type,
15190
                           ContentReceiver content_receiver,
15191
0
                           DownloadProgress progress) {
15192
0
  return cli_->Post(path, headers, content_length, std::move(content_provider),
15193
0
                    content_type, std::move(content_receiver), progress);
15194
0
}
15195
inline Result Client::Post(const std::string &path, const Headers &headers,
15196
                           ContentProviderWithoutLength content_provider,
15197
                           const std::string &content_type,
15198
0
                           UploadProgress progress) {
15199
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
15200
0
                    progress);
15201
0
}
15202
inline Result Client::Post(const std::string &path, const Headers &headers,
15203
                           ContentProviderWithoutLength content_provider,
15204
                           const std::string &content_type,
15205
                           ContentReceiver content_receiver,
15206
0
                           DownloadProgress progress) {
15207
0
  return cli_->Post(path, headers, std::move(content_provider), content_type,
15208
0
                    std::move(content_receiver), progress);
15209
0
}
15210
0
inline Result Client::Post(const std::string &path, const Params &params) {
15211
0
  return cli_->Post(path, params);
15212
0
}
15213
inline Result Client::Post(const std::string &path, const Headers &headers,
15214
0
                           const Params &params) {
15215
0
  return cli_->Post(path, headers, params);
15216
0
}
15217
inline Result Client::Post(const std::string &path,
15218
                           const UploadFormDataItems &items,
15219
0
                           UploadProgress progress) {
15220
0
  return cli_->Post(path, items, progress);
15221
0
}
15222
inline Result Client::Post(const std::string &path, const Headers &headers,
15223
                           const UploadFormDataItems &items,
15224
0
                           UploadProgress progress) {
15225
0
  return cli_->Post(path, headers, items, progress);
15226
0
}
15227
inline Result Client::Post(const std::string &path, const Headers &headers,
15228
                           const UploadFormDataItems &items,
15229
                           const std::string &boundary,
15230
0
                           UploadProgress progress) {
15231
0
  return cli_->Post(path, headers, items, boundary, progress);
15232
0
}
15233
inline Result Client::Post(const std::string &path, const Headers &headers,
15234
                           const UploadFormDataItems &items,
15235
                           const FormDataProviderItems &provider_items,
15236
0
                           UploadProgress progress) {
15237
0
  return cli_->Post(path, headers, items, provider_items, progress);
15238
0
}
15239
inline Result Client::Post(const std::string &path, const Headers &headers,
15240
                           const std::string &body,
15241
                           const std::string &content_type,
15242
                           ContentReceiver content_receiver,
15243
0
                           DownloadProgress progress) {
15244
0
  return cli_->Post(path, headers, body, content_type,
15245
0
                    std::move(content_receiver), progress);
15246
0
}
15247
15248
0
inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
15249
0
inline Result Client::Put(const std::string &path, const Headers &headers) {
15250
0
  return cli_->Put(path, headers);
15251
0
}
15252
inline Result Client::Put(const std::string &path, const char *body,
15253
                          size_t content_length,
15254
                          const std::string &content_type,
15255
0
                          UploadProgress progress) {
15256
0
  return cli_->Put(path, body, content_length, content_type, progress);
15257
0
}
15258
inline Result Client::Put(const std::string &path, const Headers &headers,
15259
                          const char *body, size_t content_length,
15260
                          const std::string &content_type,
15261
0
                          UploadProgress progress) {
15262
0
  return cli_->Put(path, headers, body, content_length, content_type, progress);
15263
0
}
15264
inline Result Client::Put(const std::string &path, const std::string &body,
15265
                          const std::string &content_type,
15266
0
                          UploadProgress progress) {
15267
0
  return cli_->Put(path, body, content_type, progress);
15268
0
}
15269
inline Result Client::Put(const std::string &path, const Headers &headers,
15270
                          const std::string &body,
15271
                          const std::string &content_type,
15272
0
                          UploadProgress progress) {
15273
0
  return cli_->Put(path, headers, body, content_type, progress);
15274
0
}
15275
inline Result Client::Put(const std::string &path, size_t content_length,
15276
                          ContentProvider content_provider,
15277
                          const std::string &content_type,
15278
0
                          UploadProgress progress) {
15279
0
  return cli_->Put(path, content_length, std::move(content_provider),
15280
0
                   content_type, progress);
15281
0
}
15282
inline Result Client::Put(const std::string &path, size_t content_length,
15283
                          ContentProvider content_provider,
15284
                          const std::string &content_type,
15285
                          ContentReceiver content_receiver,
15286
0
                          UploadProgress progress) {
15287
0
  return cli_->Put(path, content_length, std::move(content_provider),
15288
0
                   content_type, std::move(content_receiver), progress);
15289
0
}
15290
inline Result Client::Put(const std::string &path,
15291
                          ContentProviderWithoutLength content_provider,
15292
                          const std::string &content_type,
15293
0
                          UploadProgress progress) {
15294
0
  return cli_->Put(path, std::move(content_provider), content_type, progress);
15295
0
}
15296
inline Result Client::Put(const std::string &path,
15297
                          ContentProviderWithoutLength content_provider,
15298
                          const std::string &content_type,
15299
                          ContentReceiver content_receiver,
15300
0
                          UploadProgress progress) {
15301
0
  return cli_->Put(path, std::move(content_provider), content_type,
15302
0
                   std::move(content_receiver), progress);
15303
0
}
15304
inline Result Client::Put(const std::string &path, const Headers &headers,
15305
                          size_t content_length,
15306
                          ContentProvider content_provider,
15307
                          const std::string &content_type,
15308
0
                          UploadProgress progress) {
15309
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
15310
0
                   content_type, progress);
15311
0
}
15312
inline Result Client::Put(const std::string &path, const Headers &headers,
15313
                          size_t content_length,
15314
                          ContentProvider content_provider,
15315
                          const std::string &content_type,
15316
                          ContentReceiver content_receiver,
15317
0
                          UploadProgress progress) {
15318
0
  return cli_->Put(path, headers, content_length, std::move(content_provider),
15319
0
                   content_type, std::move(content_receiver), progress);
15320
0
}
15321
inline Result Client::Put(const std::string &path, const Headers &headers,
15322
                          ContentProviderWithoutLength content_provider,
15323
                          const std::string &content_type,
15324
0
                          UploadProgress progress) {
15325
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
15326
0
                   progress);
15327
0
}
15328
inline Result Client::Put(const std::string &path, const Headers &headers,
15329
                          ContentProviderWithoutLength content_provider,
15330
                          const std::string &content_type,
15331
                          ContentReceiver content_receiver,
15332
0
                          UploadProgress progress) {
15333
0
  return cli_->Put(path, headers, std::move(content_provider), content_type,
15334
0
                   std::move(content_receiver), progress);
15335
0
}
15336
0
inline Result Client::Put(const std::string &path, const Params &params) {
15337
0
  return cli_->Put(path, params);
15338
0
}
15339
inline Result Client::Put(const std::string &path, const Headers &headers,
15340
0
                          const Params &params) {
15341
0
  return cli_->Put(path, headers, params);
15342
0
}
15343
inline Result Client::Put(const std::string &path,
15344
                          const UploadFormDataItems &items,
15345
0
                          UploadProgress progress) {
15346
0
  return cli_->Put(path, items, progress);
15347
0
}
15348
inline Result Client::Put(const std::string &path, const Headers &headers,
15349
                          const UploadFormDataItems &items,
15350
0
                          UploadProgress progress) {
15351
0
  return cli_->Put(path, headers, items, progress);
15352
0
}
15353
inline Result Client::Put(const std::string &path, const Headers &headers,
15354
                          const UploadFormDataItems &items,
15355
                          const std::string &boundary,
15356
0
                          UploadProgress progress) {
15357
0
  return cli_->Put(path, headers, items, boundary, progress);
15358
0
}
15359
inline Result Client::Put(const std::string &path, const Headers &headers,
15360
                          const UploadFormDataItems &items,
15361
                          const FormDataProviderItems &provider_items,
15362
0
                          UploadProgress progress) {
15363
0
  return cli_->Put(path, headers, items, provider_items, progress);
15364
0
}
15365
inline Result Client::Put(const std::string &path, const Headers &headers,
15366
                          const std::string &body,
15367
                          const std::string &content_type,
15368
                          ContentReceiver content_receiver,
15369
0
                          DownloadProgress progress) {
15370
0
  return cli_->Put(path, headers, body, content_type, content_receiver,
15371
0
                   progress);
15372
0
}
15373
15374
0
inline Result Client::Patch(const std::string &path) {
15375
0
  return cli_->Patch(path);
15376
0
}
15377
0
inline Result Client::Patch(const std::string &path, const Headers &headers) {
15378
0
  return cli_->Patch(path, headers);
15379
0
}
15380
inline Result Client::Patch(const std::string &path, const char *body,
15381
                            size_t content_length,
15382
                            const std::string &content_type,
15383
0
                            UploadProgress progress) {
15384
0
  return cli_->Patch(path, body, content_length, content_type, progress);
15385
0
}
15386
inline Result Client::Patch(const std::string &path, const Headers &headers,
15387
                            const char *body, size_t content_length,
15388
                            const std::string &content_type,
15389
0
                            UploadProgress progress) {
15390
0
  return cli_->Patch(path, headers, body, content_length, content_type,
15391
0
                     progress);
15392
0
}
15393
inline Result Client::Patch(const std::string &path, const std::string &body,
15394
                            const std::string &content_type,
15395
0
                            UploadProgress progress) {
15396
0
  return cli_->Patch(path, body, content_type, progress);
15397
0
}
15398
inline Result Client::Patch(const std::string &path, const Headers &headers,
15399
                            const std::string &body,
15400
                            const std::string &content_type,
15401
0
                            UploadProgress progress) {
15402
0
  return cli_->Patch(path, headers, body, content_type, progress);
15403
0
}
15404
inline Result Client::Patch(const std::string &path, size_t content_length,
15405
                            ContentProvider content_provider,
15406
                            const std::string &content_type,
15407
0
                            UploadProgress progress) {
15408
0
  return cli_->Patch(path, content_length, std::move(content_provider),
15409
0
                     content_type, progress);
15410
0
}
15411
inline Result Client::Patch(const std::string &path, size_t content_length,
15412
                            ContentProvider content_provider,
15413
                            const std::string &content_type,
15414
                            ContentReceiver content_receiver,
15415
0
                            UploadProgress progress) {
15416
0
  return cli_->Patch(path, content_length, std::move(content_provider),
15417
0
                     content_type, std::move(content_receiver), progress);
15418
0
}
15419
inline Result Client::Patch(const std::string &path,
15420
                            ContentProviderWithoutLength content_provider,
15421
                            const std::string &content_type,
15422
0
                            UploadProgress progress) {
15423
0
  return cli_->Patch(path, std::move(content_provider), content_type, progress);
15424
0
}
15425
inline Result Client::Patch(const std::string &path,
15426
                            ContentProviderWithoutLength content_provider,
15427
                            const std::string &content_type,
15428
                            ContentReceiver content_receiver,
15429
0
                            UploadProgress progress) {
15430
0
  return cli_->Patch(path, std::move(content_provider), content_type,
15431
0
                     std::move(content_receiver), progress);
15432
0
}
15433
inline Result Client::Patch(const std::string &path, const Headers &headers,
15434
                            size_t content_length,
15435
                            ContentProvider content_provider,
15436
                            const std::string &content_type,
15437
0
                            UploadProgress progress) {
15438
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
15439
0
                     content_type, progress);
15440
0
}
15441
inline Result Client::Patch(const std::string &path, const Headers &headers,
15442
                            size_t content_length,
15443
                            ContentProvider content_provider,
15444
                            const std::string &content_type,
15445
                            ContentReceiver content_receiver,
15446
0
                            UploadProgress progress) {
15447
0
  return cli_->Patch(path, headers, content_length, std::move(content_provider),
15448
0
                     content_type, std::move(content_receiver), progress);
15449
0
}
15450
inline Result Client::Patch(const std::string &path, const Headers &headers,
15451
                            ContentProviderWithoutLength content_provider,
15452
                            const std::string &content_type,
15453
0
                            UploadProgress progress) {
15454
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
15455
0
                     progress);
15456
0
}
15457
inline Result Client::Patch(const std::string &path, const Headers &headers,
15458
                            ContentProviderWithoutLength content_provider,
15459
                            const std::string &content_type,
15460
                            ContentReceiver content_receiver,
15461
0
                            UploadProgress progress) {
15462
0
  return cli_->Patch(path, headers, std::move(content_provider), content_type,
15463
0
                     std::move(content_receiver), progress);
15464
0
}
15465
0
inline Result Client::Patch(const std::string &path, const Params &params) {
15466
0
  return cli_->Patch(path, params);
15467
0
}
15468
inline Result Client::Patch(const std::string &path, const Headers &headers,
15469
0
                            const Params &params) {
15470
0
  return cli_->Patch(path, headers, params);
15471
0
}
15472
inline Result Client::Patch(const std::string &path,
15473
                            const UploadFormDataItems &items,
15474
0
                            UploadProgress progress) {
15475
0
  return cli_->Patch(path, items, progress);
15476
0
}
15477
inline Result Client::Patch(const std::string &path, const Headers &headers,
15478
                            const UploadFormDataItems &items,
15479
0
                            UploadProgress progress) {
15480
0
  return cli_->Patch(path, headers, items, progress);
15481
0
}
15482
inline Result Client::Patch(const std::string &path, const Headers &headers,
15483
                            const UploadFormDataItems &items,
15484
                            const std::string &boundary,
15485
0
                            UploadProgress progress) {
15486
0
  return cli_->Patch(path, headers, items, boundary, progress);
15487
0
}
15488
inline Result Client::Patch(const std::string &path, const Headers &headers,
15489
                            const UploadFormDataItems &items,
15490
                            const FormDataProviderItems &provider_items,
15491
0
                            UploadProgress progress) {
15492
0
  return cli_->Patch(path, headers, items, provider_items, progress);
15493
0
}
15494
inline Result Client::Patch(const std::string &path, const Headers &headers,
15495
                            const std::string &body,
15496
                            const std::string &content_type,
15497
                            ContentReceiver content_receiver,
15498
0
                            DownloadProgress progress) {
15499
0
  return cli_->Patch(path, headers, body, content_type, content_receiver,
15500
0
                     progress);
15501
0
}
15502
15503
inline Result Client::Delete(const std::string &path,
15504
0
                             DownloadProgress progress) {
15505
0
  return cli_->Delete(path, progress);
15506
0
}
15507
inline Result Client::Delete(const std::string &path, const Headers &headers,
15508
0
                             DownloadProgress progress) {
15509
0
  return cli_->Delete(path, headers, progress);
15510
0
}
15511
inline Result Client::Delete(const std::string &path, const char *body,
15512
                             size_t content_length,
15513
                             const std::string &content_type,
15514
0
                             DownloadProgress progress) {
15515
0
  return cli_->Delete(path, body, content_length, content_type, progress);
15516
0
}
15517
inline Result Client::Delete(const std::string &path, const Headers &headers,
15518
                             const char *body, size_t content_length,
15519
                             const std::string &content_type,
15520
0
                             DownloadProgress progress) {
15521
0
  return cli_->Delete(path, headers, body, content_length, content_type,
15522
0
                      progress);
15523
0
}
15524
inline Result Client::Delete(const std::string &path, const std::string &body,
15525
                             const std::string &content_type,
15526
0
                             DownloadProgress progress) {
15527
0
  return cli_->Delete(path, body, content_type, progress);
15528
0
}
15529
inline Result Client::Delete(const std::string &path, const Headers &headers,
15530
                             const std::string &body,
15531
                             const std::string &content_type,
15532
0
                             DownloadProgress progress) {
15533
0
  return cli_->Delete(path, headers, body, content_type, progress);
15534
0
}
15535
inline Result Client::Delete(const std::string &path, const Params &params,
15536
0
                             DownloadProgress progress) {
15537
0
  return cli_->Delete(path, params, progress);
15538
0
}
15539
inline Result Client::Delete(const std::string &path, const Headers &headers,
15540
0
                             const Params &params, DownloadProgress progress) {
15541
0
  return cli_->Delete(path, headers, params, progress);
15542
0
}
15543
15544
0
inline Result Client::Options(const std::string &path) {
15545
0
  return cli_->Options(path);
15546
0
}
15547
0
inline Result Client::Options(const std::string &path, const Headers &headers) {
15548
0
  return cli_->Options(path, headers);
15549
0
}
15550
15551
inline ClientImpl::StreamHandle
15552
Client::open_stream(const std::string &method, const std::string &path,
15553
                    const Params &params, const Headers &headers,
15554
0
                    const std::string &body, const std::string &content_type) {
15555
0
  return cli_->open_stream(method, path, params, headers, body, content_type);
15556
0
}
15557
15558
0
inline bool Client::send(Request &req, Response &res, Error &error) {
15559
0
  return cli_->send(req, res, error);
15560
0
}
15561
15562
0
inline Result Client::send(const Request &req) { return cli_->send(req); }
15563
15564
0
inline void Client::stop() { cli_->stop(); }
15565
15566
0
inline std::string Client::host() const { return cli_->host(); }
15567
15568
0
inline int Client::port() const { return cli_->port(); }
15569
15570
0
inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
15571
15572
0
inline socket_t Client::socket() const { return cli_->socket(); }
15573
15574
inline void
15575
0
Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
15576
0
  cli_->set_hostname_addr_map(std::move(addr_map));
15577
0
}
15578
15579
0
inline void Client::set_default_headers(Headers headers) {
15580
0
  cli_->set_default_headers(std::move(headers));
15581
0
}
15582
15583
inline void Client::set_header_writer(
15584
0
    std::function<ssize_t(Stream &, Headers &)> const &writer) {
15585
0
  cli_->set_header_writer(writer);
15586
0
}
15587
15588
0
inline void Client::set_address_family(int family) {
15589
0
  cli_->set_address_family(family);
15590
0
}
15591
15592
0
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
15593
15594
0
inline void Client::set_socket_options(SocketOptions socket_options) {
15595
0
  cli_->set_socket_options(std::move(socket_options));
15596
0
}
15597
15598
0
inline void Client::set_connection_timeout(time_t sec, time_t usec) {
15599
0
  cli_->set_connection_timeout(sec, usec);
15600
0
}
15601
15602
0
inline void Client::set_read_timeout(time_t sec, time_t usec) {
15603
0
  cli_->set_read_timeout(sec, usec);
15604
0
}
15605
15606
0
inline void Client::set_write_timeout(time_t sec, time_t usec) {
15607
0
  cli_->set_write_timeout(sec, usec);
15608
0
}
15609
15610
inline void Client::set_basic_auth(const std::string &username,
15611
0
                                   const std::string &password) {
15612
0
  cli_->set_basic_auth(username, password);
15613
0
}
15614
0
inline void Client::set_bearer_token_auth(const std::string &token) {
15615
0
  cli_->set_bearer_token_auth(token);
15616
0
}
15617
15618
0
inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
15619
0
inline void Client::set_follow_location(bool on) {
15620
0
  cli_->set_follow_location(on);
15621
0
}
15622
15623
0
inline void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }
15624
15625
0
inline void Client::set_compress(bool on) { cli_->set_compress(on); }
15626
15627
0
inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
15628
15629
0
inline void Client::set_payload_max_length(size_t length) {
15630
0
  cli_->set_payload_max_length(length);
15631
0
}
15632
15633
0
inline void Client::set_interface(const std::string &intf) {
15634
0
  cli_->set_interface(intf);
15635
0
}
15636
15637
0
inline void Client::set_proxy(const std::string &host, int port) {
15638
0
  cli_->set_proxy(host, port);
15639
0
}
15640
inline void Client::set_proxy_basic_auth(const std::string &username,
15641
0
                                         const std::string &password) {
15642
0
  cli_->set_proxy_basic_auth(username, password);
15643
0
}
15644
0
inline void Client::set_proxy_bearer_token_auth(const std::string &token) {
15645
0
  cli_->set_proxy_bearer_token_auth(token);
15646
0
}
15647
0
inline void Client::set_no_proxy(const std::vector<std::string> &patterns) {
15648
0
  cli_->set_no_proxy(patterns);
15649
0
}
15650
15651
0
inline void Client::set_logger(Logger logger) {
15652
0
  cli_->set_logger(std::move(logger));
15653
0
}
15654
15655
0
inline void Client::set_error_logger(ErrorLogger error_logger) {
15656
0
  cli_->set_error_logger(std::move(error_logger));
15657
0
}
15658
15659
/*
15660
 * Group 6: SSL Server and Client implementation
15661
 */
15662
15663
#ifdef CPPHTTPLIB_SSL_ENABLED
15664
15665
// SSL HTTP server implementation
15666
inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
15667
                            const char *client_ca_cert_file_path,
15668
                            const char *client_ca_cert_dir_path,
15669
                            const char *private_key_password) {
15670
  using namespace tls;
15671
15672
  ctx_ = create_server_context();
15673
  if (!ctx_) { return; }
15674
15675
  // Load server certificate and private key
15676
  if (!set_server_cert_file(ctx_, cert_path, private_key_path,
15677
                            private_key_password)) {
15678
    last_ssl_error_ = static_cast<int>(get_error());
15679
    free_context(ctx_);
15680
    ctx_ = nullptr;
15681
    return;
15682
  }
15683
15684
  // Load client CA certificates for client authentication
15685
  if (client_ca_cert_file_path || client_ca_cert_dir_path) {
15686
    if (!set_client_ca_file(ctx_, client_ca_cert_file_path,
15687
                            client_ca_cert_dir_path)) {
15688
      last_ssl_error_ = static_cast<int>(get_error());
15689
      free_context(ctx_);
15690
      ctx_ = nullptr;
15691
      return;
15692
    }
15693
    // Enable client certificate verification
15694
    set_verify_client(ctx_, true);
15695
  }
15696
}
15697
15698
inline SSLServer::SSLServer(const PemMemory &pem) {
15699
  using namespace tls;
15700
  ctx_ = create_server_context();
15701
  if (ctx_) {
15702
    if (!set_server_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
15703
                             pem.private_key_password)) {
15704
      last_ssl_error_ = static_cast<int>(get_error());
15705
      free_context(ctx_);
15706
      ctx_ = nullptr;
15707
    } else if (pem.client_ca_pem && pem.client_ca_pem_len > 0) {
15708
      if (!load_ca_pem(ctx_, pem.client_ca_pem, pem.client_ca_pem_len)) {
15709
        last_ssl_error_ = static_cast<int>(get_error());
15710
        free_context(ctx_);
15711
        ctx_ = nullptr;
15712
      } else {
15713
        set_verify_client(ctx_, true);
15714
      }
15715
    }
15716
  }
15717
}
15718
15719
inline SSLServer::SSLServer(const tls::ContextSetupCallback &setup_callback) {
15720
  using namespace tls;
15721
  ctx_ = create_server_context();
15722
  if (ctx_) {
15723
    if (!setup_callback(ctx_)) {
15724
      free_context(ctx_);
15725
      ctx_ = nullptr;
15726
    }
15727
  }
15728
}
15729
15730
inline SSLServer::~SSLServer() {
15731
  if (ctx_) { tls::free_context(ctx_); }
15732
}
15733
15734
inline bool SSLServer::is_valid() const { return ctx_ != nullptr; }
15735
15736
inline bool SSLServer::process_and_close_socket(socket_t sock) {
15737
  using namespace tls;
15738
15739
  // Create TLS session with mutex protection
15740
  session_t session = nullptr;
15741
  {
15742
    std::lock_guard<std::mutex> guard(ctx_mutex_);
15743
    session = create_session(static_cast<ctx_t>(ctx_), sock);
15744
  }
15745
15746
  if (!session) {
15747
    last_ssl_error_ = static_cast<int>(get_error());
15748
    detail::shutdown_socket(sock);
15749
    detail::close_socket(sock);
15750
    return false;
15751
  }
15752
15753
  // Use scope_exit to ensure cleanup on all paths (including exceptions)
15754
  bool handshake_done = false;
15755
  bool ret = false;
15756
  bool websocket_upgraded = false;
15757
  auto cleanup = detail::scope_exit([&] {
15758
    if (handshake_done) { shutdown(session, !websocket_upgraded && ret); }
15759
    free_session(session);
15760
    detail::shutdown_socket(sock);
15761
    detail::close_socket(sock);
15762
  });
15763
15764
  // Perform TLS accept handshake with timeout
15765
  TlsError tls_err;
15766
  if (!accept_nonblocking(session, sock, read_timeout_sec_, read_timeout_usec_,
15767
                          &tls_err)) {
15768
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
15769
    // Map TlsError to legacy ssl_error for backward compatibility
15770
    if (tls_err.code == ErrorCode::WantRead) {
15771
      last_ssl_error_ = SSL_ERROR_WANT_READ;
15772
    } else if (tls_err.code == ErrorCode::WantWrite) {
15773
      last_ssl_error_ = SSL_ERROR_WANT_WRITE;
15774
    } else {
15775
      last_ssl_error_ = SSL_ERROR_SSL;
15776
    }
15777
#else
15778
    last_ssl_error_ = static_cast<int>(get_error());
15779
#endif
15780
    return false;
15781
  }
15782
15783
  handshake_done = true;
15784
15785
  std::string remote_addr;
15786
  int remote_port = 0;
15787
  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
15788
15789
  std::string local_addr;
15790
  int local_port = 0;
15791
  detail::get_local_ip_and_port(sock, local_addr, local_port);
15792
15793
  ret = detail::process_server_socket_ssl(
15794
      svr_sock_, session, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
15795
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
15796
      write_timeout_usec_,
15797
      [&](Stream &strm, bool close_connection, bool &connection_closed) {
15798
        return process_request(
15799
            strm, remote_addr, remote_port, local_addr, local_port,
15800
            close_connection, connection_closed,
15801
            [&](Request &req) { req.ssl = session; }, &websocket_upgraded);
15802
      });
15803
15804
  return ret;
15805
}
15806
15807
inline bool SSLServer::update_certs_pem(const char *cert_pem,
15808
                                        const char *key_pem,
15809
                                        const char *client_ca_pem,
15810
                                        const char *password) {
15811
  if (!ctx_) { return false; }
15812
  std::lock_guard<std::mutex> guard(ctx_mutex_);
15813
  if (!tls::update_server_cert(ctx_, cert_pem, key_pem, password)) {
15814
    return false;
15815
  }
15816
  if (client_ca_pem) {
15817
    return tls::update_server_client_ca(ctx_, client_ca_pem);
15818
  }
15819
  return true;
15820
}
15821
15822
// SSL HTTP client implementation
15823
inline SSLClient::~SSLClient() {
15824
  if (ctx_) { tls::free_context(ctx_); }
15825
  // Make sure to shut down SSL since shutdown_ssl will resolve to the
15826
  // base function rather than the derived function once we get to the
15827
  // base class destructor, and won't free the SSL (causing a leak).
15828
  shutdown_ssl_impl(socket_, true);
15829
}
15830
15831
inline bool SSLClient::is_valid() const { return ctx_ != nullptr; }
15832
15833
inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
15834
  shutdown_ssl_impl(socket, shutdown_gracefully);
15835
}
15836
15837
inline void SSLClient::shutdown_ssl_impl(Socket &socket,
15838
                                         bool shutdown_gracefully) {
15839
  if (socket.sock == INVALID_SOCKET) {
15840
    assert(socket.ssl == nullptr);
15841
    return;
15842
  }
15843
  if (socket.ssl) {
15844
    tls::shutdown(socket.ssl, shutdown_gracefully);
15845
    {
15846
      std::lock_guard<std::mutex> guard(ctx_mutex_);
15847
      tls::free_session(socket.ssl);
15848
    }
15849
    socket.ssl = nullptr;
15850
  }
15851
  assert(socket.ssl == nullptr);
15852
}
15853
15854
inline bool SSLClient::process_socket(
15855
    const Socket &socket,
15856
    std::chrono::time_point<std::chrono::steady_clock> start_time,
15857
    std::function<bool(Stream &strm)> callback) {
15858
  assert(socket.ssl);
15859
  return detail::process_client_socket_ssl(
15860
      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
15861
      write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
15862
      std::move(callback));
15863
}
15864
15865
inline bool SSLClient::is_ssl() const { return true; }
15866
15867
inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
15868
  if (!is_valid()) {
15869
    error = Error::SSLConnection;
15870
    return false;
15871
  }
15872
  return ClientImpl::create_and_connect_socket(socket, error);
15873
}
15874
15875
inline bool SSLClient::setup_proxy_connection(
15876
    Socket &socket,
15877
    std::chrono::time_point<std::chrono::steady_clock> start_time,
15878
    Response &res, bool &success, Error &error) {
15879
  if (!is_proxy_enabled_for_host(host_)) { return true; }
15880
15881
  if (!connect_with_proxy(socket, start_time, res, success, error)) {
15882
    return false;
15883
  }
15884
15885
  if (!initialize_ssl(socket, error)) {
15886
    success = false;
15887
    return false;
15888
  }
15889
15890
  return true;
15891
}
15892
15893
// Assumes that socket_mutex_ is locked and that there are no requests in
15894
// flight
15895
inline bool SSLClient::connect_with_proxy(
15896
    Socket &socket,
15897
    std::chrono::time_point<std::chrono::steady_clock> start_time,
15898
    Response &res, bool &success, Error &error) {
15899
  success = true;
15900
  Response proxy_res;
15901
  if (!detail::process_client_socket(
15902
          socket.sock, read_timeout_sec_, read_timeout_usec_,
15903
          write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
15904
          start_time, [&](Stream &strm) {
15905
            Request req2;
15906
            req2.method = "CONNECT";
15907
            req2.path =
15908
                detail::make_host_and_port_string_always_port(host_, port_);
15909
            if (max_timeout_msec_ > 0) {
15910
              req2.start_time_ = std::chrono::steady_clock::now();
15911
            }
15912
            return process_request(strm, req2, proxy_res, false, error);
15913
          })) {
15914
    // Thread-safe to close everything because we are assuming there are no
15915
    // requests in flight
15916
    shutdown_ssl(socket, true);
15917
    shutdown_socket(socket);
15918
    close_socket(socket);
15919
    success = false;
15920
    return false;
15921
  }
15922
15923
  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
15924
    if (!proxy_digest_auth_username_.empty() &&
15925
        !proxy_digest_auth_password_.empty()) {
15926
      std::map<std::string, std::string> auth;
15927
      if (detail::parse_www_authenticate(proxy_res, auth, true)) {
15928
        // Close the current socket and create a new one for the authenticated
15929
        // request
15930
        shutdown_ssl(socket, true);
15931
        shutdown_socket(socket);
15932
        close_socket(socket);
15933
15934
        // Create a new socket for the authenticated CONNECT request
15935
        if (!ensure_socket_connection(socket, error)) {
15936
          success = false;
15937
          output_error_log(error, nullptr);
15938
          return false;
15939
        }
15940
15941
        proxy_res = Response();
15942
        if (!detail::process_client_socket(
15943
                socket.sock, read_timeout_sec_, read_timeout_usec_,
15944
                write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
15945
                start_time, [&](Stream &strm) {
15946
                  Request req3;
15947
                  req3.method = "CONNECT";
15948
                  req3.path = detail::make_host_and_port_string_always_port(
15949
                      host_, port_);
15950
                  req3.headers.insert(detail::make_digest_authentication_header(
15951
                      req3, auth, 1, detail::random_string(10),
15952
                      proxy_digest_auth_username_, proxy_digest_auth_password_,
15953
                      true));
15954
                  if (max_timeout_msec_ > 0) {
15955
                    req3.start_time_ = std::chrono::steady_clock::now();
15956
                  }
15957
                  return process_request(strm, req3, proxy_res, false, error);
15958
                })) {
15959
          // Thread-safe to close everything because we are assuming there are
15960
          // no requests in flight
15961
          shutdown_ssl(socket, true);
15962
          shutdown_socket(socket);
15963
          close_socket(socket);
15964
          success = false;
15965
          return false;
15966
        }
15967
      }
15968
    }
15969
  }
15970
15971
  // If status code is not 200, proxy request is failed.
15972
  // Set error to ProxyConnection and return proxy response
15973
  // as the response of the request
15974
  if (proxy_res.status != StatusCode::OK_200) {
15975
    error = Error::ProxyConnection;
15976
    output_error_log(error, nullptr);
15977
    res = std::move(proxy_res);
15978
    // Thread-safe to close everything because we are assuming there are
15979
    // no requests in flight
15980
    shutdown_ssl(socket, true);
15981
    shutdown_socket(socket);
15982
    close_socket(socket);
15983
    return false;
15984
  }
15985
15986
  return true;
15987
}
15988
15989
inline bool SSLClient::ensure_socket_connection(Socket &socket, Error &error) {
15990
  if (!ClientImpl::ensure_socket_connection(socket, error)) { return false; }
15991
15992
  if (is_proxy_enabled_for_host(host_)) { return true; }
15993
15994
  if (!initialize_ssl(socket, error)) {
15995
    shutdown_socket(socket);
15996
    close_socket(socket);
15997
    return false;
15998
  }
15999
16000
  return true;
16001
}
16002
16003
// SSL HTTP client implementation
16004
inline SSLClient::SSLClient(const std::string &host)
16005
    : SSLClient(host, 443, std::string(), std::string()) {}
16006
16007
inline SSLClient::SSLClient(const std::string &host, int port)
16008
    : SSLClient(host, port, std::string(), std::string()) {}
16009
16010
inline void SSLClient::init_ctx() {
16011
  ctx_ = tls::create_client_context();
16012
  if (ctx_) { tls::set_min_version(ctx_, tls::Version::TLS1_2); }
16013
}
16014
16015
inline void SSLClient::reset_ctx_on_error() {
16016
  last_backend_error_ = tls::get_error();
16017
  tls::free_context(ctx_);
16018
  ctx_ = nullptr;
16019
}
16020
16021
inline SSLClient::SSLClient(const std::string &host, int port,
16022
                            const std::string &client_cert_path,
16023
                            const std::string &client_key_path,
16024
                            const std::string &private_key_password)
16025
    : ClientImpl(host, port, client_cert_path, client_key_path) {
16026
  init_ctx();
16027
  if (!ctx_) { return; }
16028
16029
  if (!client_cert_path.empty() && !client_key_path.empty()) {
16030
    const char *password =
16031
        private_key_password.empty() ? nullptr : private_key_password.c_str();
16032
    if (!tls::set_client_cert_file(ctx_, client_cert_path.c_str(),
16033
                                   client_key_path.c_str(), password)) {
16034
      reset_ctx_on_error();
16035
    }
16036
  }
16037
}
16038
16039
inline SSLClient::SSLClient(const std::string &host, int port,
16040
                            const PemMemory &pem)
16041
    : ClientImpl(host, port) {
16042
  init_ctx();
16043
  if (!ctx_) { return; }
16044
16045
  if (pem.cert_pem && pem.key_pem) {
16046
    if (!tls::set_client_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
16047
                                  pem.private_key_password)) {
16048
      reset_ctx_on_error();
16049
    }
16050
  }
16051
}
16052
16053
inline void SSLClient::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
16054
  if (ca_cert_store && ctx_) {
16055
    // set_ca_store takes ownership of ca_cert_store
16056
    tls::set_ca_store(ctx_, ca_cert_store);
16057
  } else if (ca_cert_store) {
16058
    tls::free_ca_store(ca_cert_store);
16059
  }
16060
}
16061
16062
inline void
16063
SSLClient::set_server_certificate_verifier(tls::VerifyCallback verifier) {
16064
  if (!ctx_) { return; }
16065
  tls::set_verify_callback(ctx_, verifier);
16066
}
16067
16068
inline void SSLClient::set_session_verifier(
16069
    std::function<SSLVerifierResponse(tls::session_t)> verifier) {
16070
  session_verifier_ = std::move(verifier);
16071
}
16072
16073
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16074
inline void SSLClient::enable_windows_certificate_verification(bool enabled) {
16075
  enable_windows_cert_verification_ = enabled;
16076
}
16077
#endif
16078
16079
inline void SSLClient::load_ca_cert_store(const char *ca_cert,
16080
                                          std::size_t size) {
16081
  if (ctx_ && ca_cert && size > 0) {
16082
    ca_cert_pem_.assign(ca_cert, size); // Store for redirect transfer
16083
    tls::load_ca_pem(ctx_, ca_cert, size);
16084
  }
16085
}
16086
16087
inline bool SSLClient::load_certs() {
16088
  auto ret = true;
16089
16090
  std::call_once(initialize_cert_, [&]() {
16091
    std::lock_guard<std::mutex> guard(ctx_mutex_);
16092
16093
    if (!ca_cert_file_path_.empty()) {
16094
      if (!tls::load_ca_file(ctx_, ca_cert_file_path_.c_str())) {
16095
        last_backend_error_ = tls::get_error();
16096
        ret = false;
16097
      }
16098
    } else if (!ca_cert_dir_path_.empty()) {
16099
      if (!tls::load_ca_dir(ctx_, ca_cert_dir_path_.c_str())) {
16100
        last_backend_error_ = tls::get_error();
16101
        ret = false;
16102
      }
16103
    } else if (ca_cert_pem_.empty()) {
16104
      if (!tls::load_system_certs(ctx_)) {
16105
        last_backend_error_ = tls::get_error();
16106
      }
16107
    }
16108
  });
16109
16110
  return ret;
16111
}
16112
16113
inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
16114
  using namespace tls;
16115
16116
  // Load CA certificates if server verification is enabled
16117
  if (server_certificate_verification_) {
16118
    if (!load_certs()) {
16119
      error = Error::SSLLoadingCerts;
16120
      output_error_log(error, nullptr);
16121
      return false;
16122
    }
16123
  }
16124
16125
  bool is_ip = detail::is_ip_address(host_);
16126
16127
#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
16128
  // MbedTLS/wolfSSL need explicit verification mode (OpenSSL uses
16129
  // SSL_VERIFY_NONE by default and performs all verification post-handshake).
16130
  // For IP addresses with verification enabled, use OPTIONAL mode since
16131
  // these backends require hostname for strict verification.
16132
  if (is_ip && server_certificate_verification_) {
16133
    set_verify_client(ctx_, false);
16134
  } else {
16135
    set_verify_client(ctx_, server_certificate_verification_);
16136
  }
16137
#endif
16138
16139
  // Create TLS session
16140
  session_t session = nullptr;
16141
  {
16142
    std::lock_guard<std::mutex> guard(ctx_mutex_);
16143
    session = create_session(ctx_, socket.sock);
16144
  }
16145
16146
  if (!session) {
16147
    error = Error::SSLConnection;
16148
    last_backend_error_ = get_error();
16149
    return false;
16150
  }
16151
16152
  // Use scope_exit to ensure session is freed on error paths
16153
  bool success = false;
16154
  auto session_guard = detail::scope_exit([&] {
16155
    if (!success) { free_session(session); }
16156
  });
16157
16158
  // Set SNI extension (skip for IP addresses per RFC 6066).
16159
  // On MbedTLS, set_sni also enables hostname verification internally.
16160
  // On OpenSSL, set_sni only sets SNI; verification is done post-handshake.
16161
  if (!is_ip) {
16162
    if (!set_sni(session, host_.c_str())) {
16163
      error = Error::SSLConnection;
16164
      last_backend_error_ = get_error();
16165
      return false;
16166
    }
16167
  }
16168
16169
  // Perform non-blocking TLS handshake with timeout
16170
  TlsError tls_err;
16171
  if (!connect_nonblocking(session, socket.sock, connection_timeout_sec_,
16172
                           connection_timeout_usec_, &tls_err)) {
16173
    last_ssl_error_ = static_cast<int>(tls_err.code);
16174
    last_backend_error_ = tls_err.backend_code;
16175
    if (tls_err.code == ErrorCode::CertVerifyFailed) {
16176
      error = Error::SSLServerVerification;
16177
    } else if (tls_err.code == ErrorCode::HostnameMismatch) {
16178
      error = Error::SSLServerHostnameVerification;
16179
    } else {
16180
      error = Error::SSLConnection;
16181
    }
16182
    output_error_log(error, nullptr);
16183
    return false;
16184
  }
16185
16186
  // Post-handshake session verifier callback
16187
  auto verification_status = SSLVerifierResponse::NoDecisionMade;
16188
  if (session_verifier_) { verification_status = session_verifier_(session); }
16189
16190
  if (verification_status == SSLVerifierResponse::CertificateRejected) {
16191
    last_backend_error_ = get_error();
16192
    error = Error::SSLServerVerification;
16193
    output_error_log(error, nullptr);
16194
    return false;
16195
  }
16196
16197
  // Default server certificate verification
16198
  if (verification_status == SSLVerifierResponse::NoDecisionMade &&
16199
      server_certificate_verification_) {
16200
    verify_result_ = tls::get_verify_result(session);
16201
    if (verify_result_ != 0) {
16202
      last_backend_error_ = static_cast<uint64_t>(verify_result_);
16203
      error = Error::SSLServerVerification;
16204
      output_error_log(error, nullptr);
16205
      return false;
16206
    }
16207
16208
    auto server_cert = get_peer_cert(session);
16209
    if (!server_cert) {
16210
      last_backend_error_ = get_error();
16211
      error = Error::SSLServerVerification;
16212
      output_error_log(error, nullptr);
16213
      return false;
16214
    }
16215
    auto cert_guard = detail::scope_exit([&] { free_cert(server_cert); });
16216
16217
    // Hostname verification (post-handshake for all cases).
16218
    // On OpenSSL, verification is always post-handshake (SSL_VERIFY_NONE).
16219
    // On MbedTLS, set_sni already enabled hostname verification during
16220
    // handshake for non-IP hosts, but this check is still needed for IP
16221
    // addresses where SNI is not set.
16222
    if (server_hostname_verification_) {
16223
      if (!verify_hostname(server_cert, host_.c_str())) {
16224
        last_backend_error_ = hostname_mismatch_code();
16225
        error = Error::SSLServerHostnameVerification;
16226
        output_error_log(error, nullptr);
16227
        return false;
16228
      }
16229
    }
16230
16231
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16232
    // Additional Windows Schannel verification.
16233
    // This provides real-time certificate validation with Windows Update
16234
    // integration, working with both OpenSSL and MbedTLS backends.
16235
    // Skip when a custom CA cert is specified, as the Windows certificate
16236
    // store would not know about user-provided CA certificates.
16237
    if (enable_windows_cert_verification_ && ca_cert_file_path_.empty() &&
16238
        ca_cert_dir_path_.empty() && ca_cert_pem_.empty()) {
16239
      std::vector<unsigned char> der;
16240
      if (get_cert_der(server_cert, der)) {
16241
        uint64_t wincrypt_error = 0;
16242
        if (!detail::verify_cert_with_windows_schannel(
16243
                der, host_, server_hostname_verification_, wincrypt_error)) {
16244
          last_backend_error_ = wincrypt_error;
16245
          error = Error::SSLServerVerification;
16246
          output_error_log(error, nullptr);
16247
          return false;
16248
        }
16249
      }
16250
    }
16251
#endif
16252
  }
16253
16254
  success = true;
16255
  socket.ssl = session;
16256
  return true;
16257
}
16258
16259
inline void Client::set_digest_auth(const std::string &username,
16260
                                    const std::string &password) {
16261
  cli_->set_digest_auth(username, password);
16262
}
16263
16264
inline void Client::set_proxy_digest_auth(const std::string &username,
16265
                                          const std::string &password) {
16266
  cli_->set_proxy_digest_auth(username, password);
16267
}
16268
16269
inline void Client::enable_server_certificate_verification(bool enabled) {
16270
  cli_->enable_server_certificate_verification(enabled);
16271
}
16272
16273
inline void Client::enable_server_hostname_verification(bool enabled) {
16274
  cli_->enable_server_hostname_verification(enabled);
16275
}
16276
16277
#ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
16278
inline void Client::enable_windows_certificate_verification(bool enabled) {
16279
  if (is_ssl_) {
16280
    static_cast<SSLClient &>(*cli_).enable_windows_certificate_verification(
16281
        enabled);
16282
  }
16283
}
16284
#endif
16285
16286
inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
16287
                                     const std::string &ca_cert_dir_path) {
16288
  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
16289
}
16290
16291
inline void Client::set_ca_cert_store(tls::ca_store_t ca_cert_store) {
16292
  if (is_ssl_) {
16293
    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
16294
  } else if (ca_cert_store) {
16295
    tls::free_ca_store(ca_cert_store);
16296
  }
16297
}
16298
16299
inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
16300
  set_ca_cert_store(tls::create_ca_store(ca_cert, size));
16301
}
16302
16303
inline void
16304
Client::set_server_certificate_verifier(tls::VerifyCallback verifier) {
16305
  if (is_ssl_) {
16306
    static_cast<SSLClient &>(*cli_).set_server_certificate_verifier(
16307
        std::move(verifier));
16308
  }
16309
}
16310
16311
inline void Client::set_session_verifier(
16312
    std::function<SSLVerifierResponse(tls::session_t)> verifier) {
16313
  if (is_ssl_) {
16314
    static_cast<SSLClient &>(*cli_).set_session_verifier(std::move(verifier));
16315
  }
16316
}
16317
16318
inline tls::ctx_t Client::tls_context() const {
16319
  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).tls_context(); }
16320
  return nullptr;
16321
}
16322
16323
#endif // CPPHTTPLIB_SSL_ENABLED
16324
16325
/*
16326
 * Group 7: TLS abstraction layer - Common API
16327
 */
16328
16329
#ifdef CPPHTTPLIB_SSL_ENABLED
16330
16331
namespace tls {
16332
16333
// Helper for PeerCert construction
16334
inline PeerCert get_peer_cert_from_session(const_session_t session) {
16335
  return PeerCert(get_peer_cert(session));
16336
}
16337
16338
namespace impl {
16339
16340
inline VerifyCallback &get_verify_callback() {
16341
  static thread_local VerifyCallback callback;
16342
  return callback;
16343
}
16344
16345
inline VerifyCallback &get_mbedtls_verify_callback() {
16346
  static thread_local VerifyCallback callback;
16347
  return callback;
16348
}
16349
16350
// Check if a string is an IPv4 address
16351
inline bool is_ipv4_address(const std::string &str) {
16352
  int dots = 0;
16353
  for (char c : str) {
16354
    if (c == '.') {
16355
      dots++;
16356
    } else if (!isdigit(static_cast<unsigned char>(c))) {
16357
      return false;
16358
    }
16359
  }
16360
  return dots == 3;
16361
}
16362
16363
// Parse IPv4 address string to bytes
16364
inline bool parse_ipv4(const std::string &str, unsigned char *out) {
16365
  const char *p = str.c_str();
16366
  for (int i = 0; i < 4; i++) {
16367
    if (i > 0) {
16368
      if (*p != '.') { return false; }
16369
      p++;
16370
    }
16371
    int val = 0;
16372
    int digits = 0;
16373
    while (*p >= '0' && *p <= '9') {
16374
      val = val * 10 + (*p - '0');
16375
      if (val > 255) { return false; }
16376
      p++;
16377
      digits++;
16378
    }
16379
    if (digits == 0) { return false; }
16380
    // Reject leading zeros (e.g., "01.002.03.04") to prevent ambiguity
16381
    if (digits > 1 && *(p - digits) == '0') { return false; }
16382
    out[i] = static_cast<unsigned char>(val);
16383
  }
16384
  return *p == '\0';
16385
}
16386
16387
#ifdef _WIN32
16388
// Enumerate Windows system certificates and call callback with DER data
16389
template <typename Callback>
16390
inline bool enumerate_windows_system_certs(Callback cb) {
16391
  bool loaded = false;
16392
  static const wchar_t *store_names[] = {L"ROOT", L"CA"};
16393
  for (auto store_name : store_names) {
16394
    HCERTSTORE hStore = CertOpenSystemStoreW(0, store_name);
16395
    if (hStore) {
16396
      PCCERT_CONTEXT pContext = nullptr;
16397
      while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
16398
             nullptr) {
16399
        if (cb(pContext->pbCertEncoded, pContext->cbCertEncoded)) {
16400
          loaded = true;
16401
        }
16402
      }
16403
      CertCloseStore(hStore, 0);
16404
    }
16405
  }
16406
  return loaded;
16407
}
16408
#endif
16409
16410
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
16411
// Enumerate macOS Keychain certificates and call callback with DER data
16412
template <typename Callback>
16413
inline bool enumerate_macos_keychain_certs(Callback cb) {
16414
  bool loaded = false;
16415
  const SecTrustSettingsDomain domains[] = {
16416
      kSecTrustSettingsDomainSystem,
16417
      kSecTrustSettingsDomainAdmin,
16418
      kSecTrustSettingsDomainUser,
16419
  };
16420
  for (auto domain : domains) {
16421
    CFArrayRef certs = nullptr;
16422
    OSStatus status = SecTrustSettingsCopyCertificates(domain, &certs);
16423
    if (status != errSecSuccess || !certs) {
16424
      if (certs) CFRelease(certs);
16425
      continue;
16426
    }
16427
    CFIndex count = CFArrayGetCount(certs);
16428
    for (CFIndex i = 0; i < count; i++) {
16429
      SecCertificateRef cert =
16430
          (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
16431
      CFDataRef data = SecCertificateCopyData(cert);
16432
      if (data) {
16433
        if (cb(CFDataGetBytePtr(data),
16434
               static_cast<size_t>(CFDataGetLength(data)))) {
16435
          loaded = true;
16436
        }
16437
        CFRelease(data);
16438
      }
16439
    }
16440
    CFRelease(certs);
16441
  }
16442
  return loaded;
16443
}
16444
#endif
16445
16446
#if !defined(_WIN32) && !(defined(__APPLE__) &&                                \
16447
                          defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN))
16448
// Common CA certificate file paths on Linux/Unix
16449
inline const char **system_ca_paths() {
16450
  static const char *paths[] = {
16451
      "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu
16452
      "/etc/pki/tls/certs/ca-bundle.crt",   // RHEL/CentOS
16453
      "/etc/ssl/ca-bundle.pem",             // OpenSUSE
16454
      "/etc/pki/tls/cacert.pem",            // OpenELEC
16455
      "/etc/ssl/cert.pem",                  // Alpine, FreeBSD
16456
      nullptr};
16457
  return paths;
16458
}
16459
16460
// Common CA certificate directory paths on Linux/Unix
16461
inline const char **system_ca_dirs() {
16462
  static const char *dirs[] = {"/etc/ssl/certs",             // Debian/Ubuntu
16463
                               "/etc/pki/tls/certs",         // RHEL/CentOS
16464
                               "/usr/share/ca-certificates", // Other
16465
                               nullptr};
16466
  return dirs;
16467
}
16468
#endif
16469
16470
} // namespace impl
16471
16472
inline bool set_client_ca_file(ctx_t ctx, const char *ca_file,
16473
                               const char *ca_dir) {
16474
  if (!ctx) { return false; }
16475
16476
  bool success = true;
16477
  if (ca_file && *ca_file) {
16478
    if (!load_ca_file(ctx, ca_file)) { success = false; }
16479
  }
16480
  if (ca_dir && *ca_dir) {
16481
    if (!load_ca_dir(ctx, ca_dir)) { success = false; }
16482
  }
16483
16484
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16485
  // Set CA list for client certificate request (CertificateRequest message)
16486
  if (ca_file && *ca_file) {
16487
    auto list = SSL_load_client_CA_file(ca_file);
16488
    if (list) { SSL_CTX_set_client_CA_list(static_cast<SSL_CTX *>(ctx), list); }
16489
  }
16490
#endif
16491
16492
  return success;
16493
}
16494
16495
inline bool set_server_cert_pem(ctx_t ctx, const char *cert, const char *key,
16496
                                const char *password) {
16497
  return set_client_cert_pem(ctx, cert, key, password);
16498
}
16499
16500
inline bool set_server_cert_file(ctx_t ctx, const char *cert_path,
16501
                                 const char *key_path, const char *password) {
16502
  return set_client_cert_file(ctx, cert_path, key_path, password);
16503
}
16504
16505
// PeerCert implementation
16506
inline PeerCert::PeerCert() = default;
16507
16508
inline PeerCert::PeerCert(cert_t cert) : cert_(cert) {}
16509
16510
inline PeerCert::PeerCert(PeerCert &&other) noexcept : cert_(other.cert_) {
16511
  other.cert_ = nullptr;
16512
}
16513
16514
inline PeerCert &PeerCert::operator=(PeerCert &&other) noexcept {
16515
  if (this != &other) {
16516
    if (cert_) { free_cert(cert_); }
16517
    cert_ = other.cert_;
16518
    other.cert_ = nullptr;
16519
  }
16520
  return *this;
16521
}
16522
16523
inline PeerCert::~PeerCert() {
16524
  if (cert_) { free_cert(cert_); }
16525
}
16526
16527
inline PeerCert::operator bool() const { return cert_ != nullptr; }
16528
16529
inline std::string PeerCert::subject_cn() const {
16530
  return cert_ ? get_cert_subject_cn(cert_) : std::string();
16531
}
16532
16533
inline std::string PeerCert::issuer_name() const {
16534
  return cert_ ? get_cert_issuer_name(cert_) : std::string();
16535
}
16536
16537
inline bool PeerCert::check_hostname(const char *hostname) const {
16538
  return cert_ ? verify_hostname(cert_, hostname) : false;
16539
}
16540
16541
inline std::vector<SanEntry> PeerCert::sans() const {
16542
  std::vector<SanEntry> result;
16543
  if (cert_) { get_cert_sans(cert_, result); }
16544
  return result;
16545
}
16546
16547
inline bool PeerCert::validity(time_t &not_before, time_t &not_after) const {
16548
  return cert_ ? get_cert_validity(cert_, not_before, not_after) : false;
16549
}
16550
16551
inline std::string PeerCert::serial() const {
16552
  return cert_ ? get_cert_serial(cert_) : std::string();
16553
}
16554
16555
// VerifyContext method implementations
16556
inline std::string VerifyContext::subject_cn() const {
16557
  return cert ? get_cert_subject_cn(cert) : std::string();
16558
}
16559
16560
inline std::string VerifyContext::issuer_name() const {
16561
  return cert ? get_cert_issuer_name(cert) : std::string();
16562
}
16563
16564
inline bool VerifyContext::check_hostname(const char *hostname) const {
16565
  return cert ? verify_hostname(cert, hostname) : false;
16566
}
16567
16568
inline std::vector<SanEntry> VerifyContext::sans() const {
16569
  std::vector<SanEntry> result;
16570
  if (cert) { get_cert_sans(cert, result); }
16571
  return result;
16572
}
16573
16574
inline bool VerifyContext::validity(time_t &not_before,
16575
                                    time_t &not_after) const {
16576
  return cert ? get_cert_validity(cert, not_before, not_after) : false;
16577
}
16578
16579
inline std::string VerifyContext::serial() const {
16580
  return cert ? get_cert_serial(cert) : std::string();
16581
}
16582
16583
// TlsError static method implementation
16584
inline std::string TlsError::verify_error_to_string(long error_code) {
16585
  return verify_error_string(error_code);
16586
}
16587
16588
} // namespace tls
16589
16590
// Request::peer_cert() implementation
16591
inline tls::PeerCert Request::peer_cert() const {
16592
  return tls::get_peer_cert_from_session(ssl);
16593
}
16594
16595
// Request::sni() implementation
16596
inline std::string Request::sni() const {
16597
  if (!ssl) { return std::string(); }
16598
  const char *s = tls::get_sni(ssl);
16599
  return s ? std::string(s) : std::string();
16600
}
16601
16602
#endif // CPPHTTPLIB_SSL_ENABLED
16603
16604
/*
16605
 * Group 8: TLS abstraction layer - OpenSSL backend
16606
 */
16607
16608
/*
16609
 * OpenSSL Backend Implementation
16610
 */
16611
16612
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
16613
namespace tls {
16614
16615
namespace impl {
16616
16617
// Helper to map OpenSSL SSL_get_error to ErrorCode
16618
inline ErrorCode map_ssl_error(int ssl_error, int &out_errno) {
16619
  switch (ssl_error) {
16620
  case SSL_ERROR_NONE: return ErrorCode::Success;
16621
  case SSL_ERROR_WANT_READ: return ErrorCode::WantRead;
16622
  case SSL_ERROR_WANT_WRITE: return ErrorCode::WantWrite;
16623
  case SSL_ERROR_ZERO_RETURN: return ErrorCode::PeerClosed;
16624
  case SSL_ERROR_SYSCALL: out_errno = errno; return ErrorCode::SyscallError;
16625
  case SSL_ERROR_SSL:
16626
  default: return ErrorCode::Fatal;
16627
  }
16628
}
16629
16630
// Helper: Create client CA list from PEM string
16631
// Returns a new STACK_OF(X509_NAME)* or nullptr on failure
16632
// Caller takes ownership of returned list
16633
inline STACK_OF(X509_NAME) *
16634
    create_client_ca_list_from_pem(const char *ca_pem) {
16635
  if (!ca_pem) { return nullptr; }
16636
16637
  auto ca_list = sk_X509_NAME_new_null();
16638
  if (!ca_list) { return nullptr; }
16639
16640
  BIO *bio = BIO_new_mem_buf(ca_pem, -1);
16641
  if (!bio) {
16642
    sk_X509_NAME_pop_free(ca_list, X509_NAME_free);
16643
    return nullptr;
16644
  }
16645
16646
  X509 *cert = nullptr;
16647
  while ((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) !=
16648
         nullptr) {
16649
    const X509_NAME *name = X509_get_subject_name(cert);
16650
    if (name) {
16651
      sk_X509_NAME_push(ca_list, X509_NAME_dup(const_cast<X509_NAME *>(name)));
16652
    }
16653
    X509_free(cert);
16654
  }
16655
  BIO_free(bio);
16656
16657
  return ca_list;
16658
}
16659
16660
// OpenSSL verify callback wrapper
16661
inline int openssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
16662
  auto &callback = get_verify_callback();
16663
  if (!callback) { return preverify_ok; }
16664
16665
  // Get SSL object from X509_STORE_CTX
16666
  auto ssl = static_cast<SSL *>(
16667
      X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
16668
  if (!ssl) { return preverify_ok; }
16669
16670
  // Get current certificate and depth
16671
  auto cert = X509_STORE_CTX_get_current_cert(ctx);
16672
  int depth = X509_STORE_CTX_get_error_depth(ctx);
16673
  int error = X509_STORE_CTX_get_error(ctx);
16674
16675
  // Build context
16676
  VerifyContext verify_ctx;
16677
  verify_ctx.session = static_cast<session_t>(ssl);
16678
  verify_ctx.cert = static_cast<cert_t>(cert);
16679
  verify_ctx.depth = depth;
16680
  verify_ctx.preverify_ok = (preverify_ok != 0);
16681
  verify_ctx.error_code = error;
16682
  verify_ctx.error_string =
16683
      (error != X509_V_OK) ? X509_verify_cert_error_string(error) : nullptr;
16684
16685
  return callback(verify_ctx) ? 1 : 0;
16686
}
16687
16688
} // namespace impl
16689
16690
inline ctx_t create_client_context() {
16691
  SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
16692
  if (ctx) {
16693
    // Disable auto-retry to properly handle non-blocking I/O
16694
    SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
16695
    // Set minimum TLS version
16696
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
16697
  }
16698
  return static_cast<ctx_t>(ctx);
16699
}
16700
16701
inline void free_context(ctx_t ctx) {
16702
  if (ctx) { SSL_CTX_free(static_cast<SSL_CTX *>(ctx)); }
16703
}
16704
16705
inline bool set_min_version(ctx_t ctx, Version version) {
16706
  if (!ctx) return false;
16707
  return SSL_CTX_set_min_proto_version(static_cast<SSL_CTX *>(ctx),
16708
                                       static_cast<int>(version)) == 1;
16709
}
16710
16711
inline bool load_ca_pem(ctx_t ctx, const char *pem, size_t len) {
16712
  if (!ctx || !pem || len == 0) return false;
16713
16714
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
16715
  auto store = SSL_CTX_get_cert_store(ssl_ctx);
16716
  if (!store) return false;
16717
16718
  auto bio = BIO_new_mem_buf(pem, static_cast<int>(len));
16719
  if (!bio) return false;
16720
16721
  bool ok = true;
16722
  X509 *cert = nullptr;
16723
  while ((cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) !=
16724
         nullptr) {
16725
    if (X509_STORE_add_cert(store, cert) != 1) {
16726
      // Ignore duplicate errors
16727
      auto err = ERR_peek_last_error();
16728
      if (ERR_GET_REASON(err) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
16729
        ok = false;
16730
      }
16731
    }
16732
    X509_free(cert);
16733
    if (!ok) break;
16734
  }
16735
  BIO_free(bio);
16736
16737
  // Clear any "no more certificates" errors
16738
  ERR_clear_error();
16739
  return ok;
16740
}
16741
16742
inline bool load_ca_file(ctx_t ctx, const char *file_path) {
16743
  if (!ctx || !file_path) return false;
16744
  return SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(ctx), file_path,
16745
                                       nullptr) == 1;
16746
}
16747
16748
inline bool load_ca_dir(ctx_t ctx, const char *dir_path) {
16749
  if (!ctx || !dir_path) return false;
16750
  return SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(ctx), nullptr,
16751
                                       dir_path) == 1;
16752
}
16753
16754
inline bool load_system_certs(ctx_t ctx) {
16755
  if (!ctx) return false;
16756
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
16757
16758
#ifdef _WIN32
16759
  // Windows: Load from system certificate store (ROOT and CA)
16760
  auto store = SSL_CTX_get_cert_store(ssl_ctx);
16761
  if (!store) return false;
16762
16763
  bool loaded_any = false;
16764
  static const wchar_t *store_names[] = {L"ROOT", L"CA"};
16765
  for (auto store_name : store_names) {
16766
    auto hStore = CertOpenSystemStoreW(NULL, store_name);
16767
    if (!hStore) continue;
16768
16769
    PCCERT_CONTEXT pContext = nullptr;
16770
    while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
16771
           nullptr) {
16772
      const unsigned char *data = pContext->pbCertEncoded;
16773
      auto x509 = d2i_X509(nullptr, &data, pContext->cbCertEncoded);
16774
      if (x509) {
16775
        if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
16776
        X509_free(x509);
16777
      }
16778
    }
16779
    CertCloseStore(hStore, 0);
16780
  }
16781
  return loaded_any;
16782
16783
#elif defined(__APPLE__)
16784
#ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
16785
  // macOS: Load from Keychain
16786
  auto store = SSL_CTX_get_cert_store(ssl_ctx);
16787
  if (!store) return false;
16788
16789
  bool loaded_any = false;
16790
  const SecTrustSettingsDomain domains[] = {
16791
      kSecTrustSettingsDomainSystem,
16792
      kSecTrustSettingsDomainAdmin,
16793
      kSecTrustSettingsDomainUser,
16794
  };
16795
  for (auto domain : domains) {
16796
    CFArrayRef certs = nullptr;
16797
    if (SecTrustSettingsCopyCertificates(domain, &certs) != errSecSuccess ||
16798
        !certs) {
16799
      if (certs) CFRelease(certs);
16800
      continue;
16801
    }
16802
    auto count = CFArrayGetCount(certs);
16803
    for (CFIndex i = 0; i < count; i++) {
16804
      auto cert = reinterpret_cast<SecCertificateRef>(
16805
          const_cast<void *>(CFArrayGetValueAtIndex(certs, i)));
16806
      CFDataRef der = SecCertificateCopyData(cert);
16807
      if (der) {
16808
        const unsigned char *data = CFDataGetBytePtr(der);
16809
        auto x509 = d2i_X509(nullptr, &data, CFDataGetLength(der));
16810
        if (x509) {
16811
          if (X509_STORE_add_cert(store, x509) == 1) { loaded_any = true; }
16812
          X509_free(x509);
16813
        }
16814
        CFRelease(der);
16815
      }
16816
    }
16817
    CFRelease(certs);
16818
  }
16819
  return loaded_any || SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16820
#else
16821
  return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16822
#endif
16823
16824
#else
16825
  // Other Unix: use default verify paths
16826
  return SSL_CTX_set_default_verify_paths(ssl_ctx) == 1;
16827
#endif
16828
}
16829
16830
inline bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key,
16831
                                const char *password) {
16832
  if (!ctx || !cert || !key) return false;
16833
16834
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
16835
16836
  // Load certificate
16837
  auto cert_bio = BIO_new_mem_buf(cert, -1);
16838
  if (!cert_bio) return false;
16839
16840
  auto x509 = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
16841
  BIO_free(cert_bio);
16842
  if (!x509) return false;
16843
16844
  auto cert_ok = SSL_CTX_use_certificate(ssl_ctx, x509) == 1;
16845
  X509_free(x509);
16846
  if (!cert_ok) return false;
16847
16848
  // Load private key
16849
  auto key_bio = BIO_new_mem_buf(key, -1);
16850
  if (!key_bio) return false;
16851
16852
  auto pkey = PEM_read_bio_PrivateKey(key_bio, nullptr, nullptr,
16853
                                      password ? const_cast<char *>(password)
16854
                                               : nullptr);
16855
  BIO_free(key_bio);
16856
  if (!pkey) return false;
16857
16858
  auto key_ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey) == 1;
16859
  EVP_PKEY_free(pkey);
16860
16861
  return key_ok && SSL_CTX_check_private_key(ssl_ctx) == 1;
16862
}
16863
16864
inline bool set_client_cert_file(ctx_t ctx, const char *cert_path,
16865
                                 const char *key_path, const char *password) {
16866
  if (!ctx || !cert_path || !key_path) return false;
16867
16868
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
16869
16870
  if (password && password[0] != '\0') {
16871
    SSL_CTX_set_default_passwd_cb_userdata(
16872
        ssl_ctx, reinterpret_cast<void *>(const_cast<char *>(password)));
16873
  }
16874
16875
  return SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_path) == 1 &&
16876
         SSL_CTX_use_PrivateKey_file(ssl_ctx, key_path, SSL_FILETYPE_PEM) == 1;
16877
}
16878
16879
inline ctx_t create_server_context() {
16880
  SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
16881
  if (ctx) {
16882
    SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION |
16883
                                 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
16884
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
16885
  }
16886
  return static_cast<ctx_t>(ctx);
16887
}
16888
16889
inline void set_verify_client(ctx_t ctx, bool require) {
16890
  if (!ctx) return;
16891
  SSL_CTX_set_verify(static_cast<SSL_CTX *>(ctx),
16892
                     require
16893
                         ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
16894
                         : SSL_VERIFY_NONE,
16895
                     nullptr);
16896
}
16897
16898
inline session_t create_session(ctx_t ctx, socket_t sock) {
16899
  if (!ctx || sock == INVALID_SOCKET) return nullptr;
16900
16901
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
16902
  SSL *ssl = SSL_new(ssl_ctx);
16903
  if (!ssl) return nullptr;
16904
16905
  // Disable auto-retry for proper non-blocking I/O handling
16906
  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
16907
16908
  auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
16909
  if (!bio) {
16910
    SSL_free(ssl);
16911
    return nullptr;
16912
  }
16913
16914
  SSL_set_bio(ssl, bio, bio);
16915
  return static_cast<session_t>(ssl);
16916
}
16917
16918
inline void free_session(session_t session) {
16919
  if (session) { SSL_free(static_cast<SSL *>(session)); }
16920
}
16921
16922
inline bool set_sni(session_t session, const char *hostname) {
16923
  if (!session || !hostname) return false;
16924
16925
  auto ssl = static_cast<SSL *>(session);
16926
16927
  // Set SNI (Server Name Indication) only - does not enable verification
16928
#if defined(OPENSSL_IS_BORINGSSL)
16929
  return SSL_set_tlsext_host_name(ssl, hostname) == 1;
16930
#else
16931
  // Direct call instead of macro to suppress -Wold-style-cast warning
16932
  return SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
16933
                  static_cast<void *>(const_cast<char *>(hostname))) == 1;
16934
#endif
16935
}
16936
16937
inline bool set_hostname(session_t session, const char *hostname) {
16938
  if (!session || !hostname) return false;
16939
16940
  auto ssl = static_cast<SSL *>(session);
16941
16942
  // Set SNI (Server Name Indication)
16943
  if (!set_sni(session, hostname)) { return false; }
16944
16945
  // Enable hostname verification
16946
  auto param = SSL_get0_param(ssl);
16947
  if (!param) return false;
16948
16949
  X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
16950
  if (X509_VERIFY_PARAM_set1_host(param, hostname, 0) != 1) { return false; }
16951
16952
  SSL_set_verify(ssl, SSL_VERIFY_PEER, nullptr);
16953
  return true;
16954
}
16955
16956
inline TlsError connect(session_t session) {
16957
  if (!session) { return TlsError(); }
16958
16959
  auto ssl = static_cast<SSL *>(session);
16960
  auto ret = SSL_connect(ssl);
16961
16962
  TlsError err;
16963
  if (ret == 1) {
16964
    err.code = ErrorCode::Success;
16965
  } else {
16966
    auto ssl_err = SSL_get_error(ssl, ret);
16967
    err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
16968
    err.backend_code = ERR_get_error();
16969
  }
16970
  return err;
16971
}
16972
16973
inline TlsError accept(session_t session) {
16974
  if (!session) { return TlsError(); }
16975
16976
  auto ssl = static_cast<SSL *>(session);
16977
  auto ret = SSL_accept(ssl);
16978
16979
  TlsError err;
16980
  if (ret == 1) {
16981
    err.code = ErrorCode::Success;
16982
  } else {
16983
    auto ssl_err = SSL_get_error(ssl, ret);
16984
    err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
16985
    err.backend_code = ERR_get_error();
16986
  }
16987
  return err;
16988
}
16989
16990
inline bool connect_nonblocking(session_t session, socket_t sock,
16991
                                time_t timeout_sec, time_t timeout_usec,
16992
                                TlsError *err) {
16993
  if (!session) {
16994
    if (err) { err->code = ErrorCode::Fatal; }
16995
    return false;
16996
  }
16997
16998
  auto ssl = static_cast<SSL *>(session);
16999
  auto bio = SSL_get_rbio(ssl);
17000
17001
  // Set non-blocking mode for handshake
17002
  detail::set_nonblocking(sock, true);
17003
  if (bio) { BIO_set_nbio(bio, 1); }
17004
17005
  auto cleanup = detail::scope_exit([&]() {
17006
    // Restore blocking mode after handshake
17007
    if (bio) { BIO_set_nbio(bio, 0); }
17008
    detail::set_nonblocking(sock, false);
17009
  });
17010
17011
  auto res = 0;
17012
  while ((res = SSL_connect(ssl)) != 1) {
17013
    auto ssl_err = SSL_get_error(ssl, res);
17014
    switch (ssl_err) {
17015
    case SSL_ERROR_WANT_READ:
17016
      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
17017
        continue;
17018
      }
17019
      break;
17020
    case SSL_ERROR_WANT_WRITE:
17021
      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
17022
        continue;
17023
      }
17024
      break;
17025
    default: break;
17026
    }
17027
    if (err) {
17028
      err->code = impl::map_ssl_error(ssl_err, err->sys_errno);
17029
      err->backend_code = ERR_get_error();
17030
    }
17031
    return false;
17032
  }
17033
  if (err) { err->code = ErrorCode::Success; }
17034
  return true;
17035
}
17036
17037
inline bool accept_nonblocking(session_t session, socket_t sock,
17038
                               time_t timeout_sec, time_t timeout_usec,
17039
                               TlsError *err) {
17040
  if (!session) {
17041
    if (err) { err->code = ErrorCode::Fatal; }
17042
    return false;
17043
  }
17044
17045
  auto ssl = static_cast<SSL *>(session);
17046
  auto bio = SSL_get_rbio(ssl);
17047
17048
  // Set non-blocking mode for handshake
17049
  detail::set_nonblocking(sock, true);
17050
  if (bio) { BIO_set_nbio(bio, 1); }
17051
17052
  auto cleanup = detail::scope_exit([&]() {
17053
    // Restore blocking mode after handshake
17054
    if (bio) { BIO_set_nbio(bio, 0); }
17055
    detail::set_nonblocking(sock, false);
17056
  });
17057
17058
  auto res = 0;
17059
  while ((res = SSL_accept(ssl)) != 1) {
17060
    auto ssl_err = SSL_get_error(ssl, res);
17061
    switch (ssl_err) {
17062
    case SSL_ERROR_WANT_READ:
17063
      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
17064
        continue;
17065
      }
17066
      break;
17067
    case SSL_ERROR_WANT_WRITE:
17068
      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
17069
        continue;
17070
      }
17071
      break;
17072
    default: break;
17073
    }
17074
    if (err) {
17075
      err->code = impl::map_ssl_error(ssl_err, err->sys_errno);
17076
      err->backend_code = ERR_get_error();
17077
    }
17078
    return false;
17079
  }
17080
  if (err) { err->code = ErrorCode::Success; }
17081
  return true;
17082
}
17083
17084
inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) {
17085
  if (!session || !buf) {
17086
    err.code = ErrorCode::Fatal;
17087
    return -1;
17088
  }
17089
17090
  auto ssl = static_cast<SSL *>(session);
17091
  constexpr auto max_len =
17092
      static_cast<size_t>((std::numeric_limits<int>::max)());
17093
  if (len > max_len) { len = max_len; }
17094
  auto ret = SSL_read(ssl, buf, static_cast<int>(len));
17095
17096
  if (ret > 0) {
17097
    err.code = ErrorCode::Success;
17098
    return ret;
17099
  }
17100
17101
  auto ssl_err = SSL_get_error(ssl, ret);
17102
  err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17103
  if (err.code == ErrorCode::PeerClosed) {
17104
    return 0;
17105
  } // Gracefully handle the peer closed state.
17106
  if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
17107
  return -1;
17108
}
17109
17110
inline ssize_t write(session_t session, const void *buf, size_t len,
17111
                     TlsError &err) {
17112
  if (!session || !buf) {
17113
    err.code = ErrorCode::Fatal;
17114
    return -1;
17115
  }
17116
17117
  auto ssl = static_cast<SSL *>(session);
17118
  auto ret = SSL_write(ssl, buf, static_cast<int>(len));
17119
17120
  if (ret > 0) {
17121
    err.code = ErrorCode::Success;
17122
    return ret;
17123
  }
17124
17125
  auto ssl_err = SSL_get_error(ssl, ret);
17126
  err.code = impl::map_ssl_error(ssl_err, err.sys_errno);
17127
  if (err.code == ErrorCode::Fatal) { err.backend_code = ERR_get_error(); }
17128
  return -1;
17129
}
17130
17131
inline int pending(const_session_t session) {
17132
  if (!session) return 0;
17133
  return SSL_pending(static_cast<SSL *>(const_cast<void *>(session)));
17134
}
17135
17136
inline void shutdown(session_t session, bool graceful) {
17137
  if (!session) return;
17138
17139
  auto ssl = static_cast<SSL *>(session);
17140
  if (graceful) {
17141
    // First call sends close_notify
17142
    if (SSL_shutdown(ssl) == 0) {
17143
      // Second call waits for peer's close_notify
17144
      SSL_shutdown(ssl);
17145
    }
17146
  }
17147
}
17148
17149
inline bool is_peer_closed(session_t session, socket_t sock) {
17150
  if (!session) return true;
17151
17152
  // Temporarily set socket to non-blocking to avoid blocking on SSL_peek
17153
  detail::set_nonblocking(sock, true);
17154
  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
17155
17156
  auto ssl = static_cast<SSL *>(session);
17157
  char buf;
17158
  auto ret = SSL_peek(ssl, &buf, 1);
17159
  if (ret > 0) return false;
17160
17161
  auto err = SSL_get_error(ssl, ret);
17162
  return err == SSL_ERROR_ZERO_RETURN;
17163
}
17164
17165
inline cert_t get_peer_cert(const_session_t session) {
17166
  if (!session) return nullptr;
17167
  return static_cast<cert_t>(SSL_get1_peer_certificate(
17168
      static_cast<SSL *>(const_cast<void *>(session))));
17169
}
17170
17171
inline void free_cert(cert_t cert) {
17172
  if (cert) { X509_free(static_cast<X509 *>(cert)); }
17173
}
17174
17175
inline bool verify_hostname(cert_t cert, const char *hostname) {
17176
  if (!cert || !hostname) return false;
17177
17178
  auto x509 = static_cast<X509 *>(cert);
17179
17180
  // Use X509_check_ip_asc for IP addresses, X509_check_host for DNS names
17181
  if (detail::is_ip_address(hostname)) {
17182
    return X509_check_ip_asc(x509, hostname, 0) == 1;
17183
  }
17184
  return X509_check_host(x509, hostname, strlen(hostname), 0, nullptr) == 1;
17185
}
17186
17187
inline uint64_t hostname_mismatch_code() {
17188
  return static_cast<uint64_t>(X509_V_ERR_HOSTNAME_MISMATCH);
17189
}
17190
17191
inline long get_verify_result(const_session_t session) {
17192
  if (!session) return X509_V_ERR_UNSPECIFIED;
17193
  return SSL_get_verify_result(static_cast<SSL *>(const_cast<void *>(session)));
17194
}
17195
17196
inline std::string get_cert_subject_cn(cert_t cert) {
17197
  if (!cert) return "";
17198
  auto x509 = static_cast<X509 *>(cert);
17199
  auto subject_name = X509_get_subject_name(x509);
17200
  if (!subject_name) return "";
17201
17202
  char buf[256];
17203
  auto len =
17204
      X509_NAME_get_text_by_NID(subject_name, NID_commonName, buf, sizeof(buf));
17205
  if (len < 0) return "";
17206
  return std::string(buf, static_cast<size_t>(len));
17207
}
17208
17209
inline std::string get_cert_issuer_name(cert_t cert) {
17210
  if (!cert) return "";
17211
  auto x509 = static_cast<X509 *>(cert);
17212
  auto issuer_name = X509_get_issuer_name(x509);
17213
  if (!issuer_name) return "";
17214
17215
  char buf[256];
17216
  X509_NAME_oneline(issuer_name, buf, sizeof(buf));
17217
  return std::string(buf);
17218
}
17219
17220
inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
17221
  sans.clear();
17222
  if (!cert) return false;
17223
  auto x509 = static_cast<X509 *>(cert);
17224
17225
  auto names = static_cast<GENERAL_NAMES *>(
17226
      X509_get_ext_d2i(x509, NID_subject_alt_name, nullptr, nullptr));
17227
  if (!names) return true; // No SANs is valid
17228
17229
  auto count = sk_GENERAL_NAME_num(names);
17230
  for (decltype(count) i = 0; i < count; i++) {
17231
    auto gen = sk_GENERAL_NAME_value(names, i);
17232
    if (!gen) continue;
17233
17234
    SanEntry entry;
17235
    switch (gen->type) {
17236
    case GEN_DNS:
17237
      entry.type = SanType::DNS;
17238
      if (gen->d.dNSName) {
17239
        entry.value = std::string(
17240
            reinterpret_cast<const char *>(
17241
                ASN1_STRING_get0_data(gen->d.dNSName)),
17242
            static_cast<size_t>(ASN1_STRING_length(gen->d.dNSName)));
17243
      }
17244
      break;
17245
    case GEN_IPADD:
17246
      entry.type = SanType::IP;
17247
      if (gen->d.iPAddress) {
17248
        auto data = ASN1_STRING_get0_data(gen->d.iPAddress);
17249
        auto len = ASN1_STRING_length(gen->d.iPAddress);
17250
        if (len == 4) {
17251
          // IPv4
17252
          char buf[INET_ADDRSTRLEN];
17253
          inet_ntop(AF_INET, data, buf, sizeof(buf));
17254
          entry.value = buf;
17255
        } else if (len == 16) {
17256
          // IPv6
17257
          char buf[INET6_ADDRSTRLEN];
17258
          inet_ntop(AF_INET6, data, buf, sizeof(buf));
17259
          entry.value = buf;
17260
        }
17261
      }
17262
      break;
17263
    case GEN_EMAIL:
17264
      entry.type = SanType::EMAIL;
17265
      if (gen->d.rfc822Name) {
17266
        entry.value = std::string(
17267
            reinterpret_cast<const char *>(
17268
                ASN1_STRING_get0_data(gen->d.rfc822Name)),
17269
            static_cast<size_t>(ASN1_STRING_length(gen->d.rfc822Name)));
17270
      }
17271
      break;
17272
    case GEN_URI:
17273
      entry.type = SanType::URI;
17274
      if (gen->d.uniformResourceIdentifier) {
17275
        entry.value = std::string(
17276
            reinterpret_cast<const char *>(
17277
                ASN1_STRING_get0_data(gen->d.uniformResourceIdentifier)),
17278
            static_cast<size_t>(
17279
                ASN1_STRING_length(gen->d.uniformResourceIdentifier)));
17280
      }
17281
      break;
17282
    default: entry.type = SanType::OTHER; break;
17283
    }
17284
17285
    if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
17286
  }
17287
17288
  GENERAL_NAMES_free(names);
17289
  return true;
17290
}
17291
17292
inline bool get_cert_validity(cert_t cert, time_t &not_before,
17293
                              time_t &not_after) {
17294
  if (!cert) return false;
17295
  auto x509 = static_cast<X509 *>(cert);
17296
17297
  auto nb = X509_get0_notBefore(x509);
17298
  auto na = X509_get0_notAfter(x509);
17299
  if (!nb || !na) return false;
17300
17301
  ASN1_TIME *epoch = ASN1_TIME_new();
17302
  if (!epoch) return false;
17303
  auto se = detail::scope_exit([&] { ASN1_TIME_free(epoch); });
17304
17305
  if (!ASN1_TIME_set(epoch, 0)) return false;
17306
17307
  int pday, psec;
17308
17309
  if (!ASN1_TIME_diff(&pday, &psec, epoch, nb)) return false;
17310
  not_before = 86400 * (time_t)pday + psec;
17311
17312
  if (!ASN1_TIME_diff(&pday, &psec, epoch, na)) return false;
17313
  not_after = 86400 * (time_t)pday + psec;
17314
17315
  return true;
17316
}
17317
17318
inline std::string get_cert_serial(cert_t cert) {
17319
  if (!cert) return "";
17320
  auto x509 = static_cast<X509 *>(cert);
17321
17322
  auto serial = X509_get_serialNumber(x509);
17323
  if (!serial) return "";
17324
17325
  auto bn = ASN1_INTEGER_to_BN(serial, nullptr);
17326
  if (!bn) return "";
17327
17328
  auto hex = BN_bn2hex(bn);
17329
  BN_free(bn);
17330
  if (!hex) return "";
17331
17332
  std::string result(hex);
17333
  OPENSSL_free(hex);
17334
  return result;
17335
}
17336
17337
inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
17338
  if (!cert) return false;
17339
  auto x509 = static_cast<X509 *>(cert);
17340
  auto len = i2d_X509(x509, nullptr);
17341
  if (len < 0) return false;
17342
  der.resize(static_cast<size_t>(len));
17343
  auto p = der.data();
17344
  i2d_X509(x509, &p);
17345
  return true;
17346
}
17347
17348
inline const char *get_sni(const_session_t session) {
17349
  if (!session) return nullptr;
17350
  auto ssl = static_cast<SSL *>(const_cast<void *>(session));
17351
  return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
17352
}
17353
17354
inline uint64_t peek_error() { return ERR_peek_last_error(); }
17355
17356
inline uint64_t get_error() { return ERR_get_error(); }
17357
17358
inline std::string error_string(uint64_t code) {
17359
  char buf[256];
17360
  ERR_error_string_n(static_cast<unsigned long>(code), buf, sizeof(buf));
17361
  return std::string(buf);
17362
}
17363
17364
inline ca_store_t create_ca_store(const char *pem, size_t len) {
17365
  auto mem = BIO_new_mem_buf(pem, static_cast<int>(len));
17366
  if (!mem) { return nullptr; }
17367
  auto mem_guard = detail::scope_exit([&] { BIO_free_all(mem); });
17368
17369
  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
17370
  if (!inf) { return nullptr; }
17371
17372
  auto store = X509_STORE_new();
17373
  if (store) {
17374
    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
17375
      auto itmp = sk_X509_INFO_value(inf, i);
17376
      if (!itmp) { continue; }
17377
      if (itmp->x509) { X509_STORE_add_cert(store, itmp->x509); }
17378
      if (itmp->crl) { X509_STORE_add_crl(store, itmp->crl); }
17379
    }
17380
  }
17381
17382
  sk_X509_INFO_pop_free(inf, X509_INFO_free);
17383
  return static_cast<ca_store_t>(store);
17384
}
17385
17386
inline void free_ca_store(ca_store_t store) {
17387
  if (store) { X509_STORE_free(static_cast<X509_STORE *>(store)); }
17388
}
17389
17390
inline bool set_ca_store(ctx_t ctx, ca_store_t store) {
17391
  if (!ctx || !store) { return false; }
17392
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
17393
  auto x509_store = static_cast<X509_STORE *>(store);
17394
17395
  // Check if same store is already set
17396
  if (SSL_CTX_get_cert_store(ssl_ctx) == x509_store) { return true; }
17397
17398
  // SSL_CTX_set_cert_store takes ownership and frees the old store
17399
  SSL_CTX_set_cert_store(ssl_ctx, x509_store);
17400
  return true;
17401
}
17402
17403
inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
17404
  certs.clear();
17405
  if (!ctx) { return 0; }
17406
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
17407
17408
  auto store = SSL_CTX_get_cert_store(ssl_ctx);
17409
  if (!store) { return 0; }
17410
17411
  auto objs = X509_STORE_get0_objects(store);
17412
  if (!objs) { return 0; }
17413
17414
  auto count = sk_X509_OBJECT_num(objs);
17415
  for (decltype(count) i = 0; i < count; i++) {
17416
    auto obj = sk_X509_OBJECT_value(objs, i);
17417
    if (!obj) { continue; }
17418
    if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
17419
      auto x509 = X509_OBJECT_get0_X509(obj);
17420
      if (x509) {
17421
        // Increment reference count so caller can free it
17422
        X509_up_ref(x509);
17423
        certs.push_back(static_cast<cert_t>(x509));
17424
      }
17425
    }
17426
  }
17427
  return certs.size();
17428
}
17429
17430
inline std::vector<std::string> get_ca_names(ctx_t ctx) {
17431
  std::vector<std::string> names;
17432
  if (!ctx) { return names; }
17433
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
17434
17435
  auto store = SSL_CTX_get_cert_store(ssl_ctx);
17436
  if (!store) { return names; }
17437
17438
  auto objs = X509_STORE_get0_objects(store);
17439
  if (!objs) { return names; }
17440
17441
  auto count = sk_X509_OBJECT_num(objs);
17442
  for (decltype(count) i = 0; i < count; i++) {
17443
    auto obj = sk_X509_OBJECT_value(objs, i);
17444
    if (!obj) { continue; }
17445
    if (X509_OBJECT_get_type(obj) == X509_LU_X509) {
17446
      auto x509 = X509_OBJECT_get0_X509(obj);
17447
      if (x509) {
17448
        auto subject = X509_get_subject_name(x509);
17449
        if (subject) {
17450
          char buf[512];
17451
          X509_NAME_oneline(subject, buf, sizeof(buf));
17452
          names.push_back(buf);
17453
        }
17454
      }
17455
    }
17456
  }
17457
  return names;
17458
}
17459
17460
inline bool update_server_cert(ctx_t ctx, const char *cert_pem,
17461
                               const char *key_pem, const char *password) {
17462
  if (!ctx || !cert_pem || !key_pem) { return false; }
17463
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
17464
17465
  // Load certificate from PEM
17466
  auto cert_bio = BIO_new_mem_buf(cert_pem, -1);
17467
  if (!cert_bio) { return false; }
17468
  auto cert = PEM_read_bio_X509(cert_bio, nullptr, nullptr, nullptr);
17469
  BIO_free(cert_bio);
17470
  if (!cert) { return false; }
17471
17472
  // Load private key from PEM
17473
  auto key_bio = BIO_new_mem_buf(key_pem, -1);
17474
  if (!key_bio) {
17475
    X509_free(cert);
17476
    return false;
17477
  }
17478
  auto key = PEM_read_bio_PrivateKey(key_bio, nullptr, nullptr,
17479
                                     password ? const_cast<char *>(password)
17480
                                              : nullptr);
17481
  BIO_free(key_bio);
17482
  if (!key) {
17483
    X509_free(cert);
17484
    return false;
17485
  }
17486
17487
  // Update certificate and key
17488
  auto ret = SSL_CTX_use_certificate(ssl_ctx, cert) == 1 &&
17489
             SSL_CTX_use_PrivateKey(ssl_ctx, key) == 1;
17490
17491
  X509_free(cert);
17492
  EVP_PKEY_free(key);
17493
  return ret;
17494
}
17495
17496
inline bool update_server_client_ca(ctx_t ctx, const char *ca_pem) {
17497
  if (!ctx || !ca_pem) { return false; }
17498
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
17499
17500
  // Create new X509_STORE from PEM
17501
  auto store = create_ca_store(ca_pem, strlen(ca_pem));
17502
  if (!store) { return false; }
17503
17504
  // SSL_CTX_set_cert_store takes ownership
17505
  SSL_CTX_set_cert_store(ssl_ctx, static_cast<X509_STORE *>(store));
17506
17507
  // Set client CA list for client certificate request
17508
  auto ca_list = impl::create_client_ca_list_from_pem(ca_pem);
17509
  if (ca_list) {
17510
    // SSL_CTX_set_client_CA_list takes ownership of ca_list
17511
    SSL_CTX_set_client_CA_list(ssl_ctx, ca_list);
17512
  }
17513
17514
  return true;
17515
}
17516
17517
inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
17518
  if (!ctx) { return false; }
17519
  auto ssl_ctx = static_cast<SSL_CTX *>(ctx);
17520
17521
  impl::get_verify_callback() = std::move(callback);
17522
17523
  if (impl::get_verify_callback()) {
17524
    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, impl::openssl_verify_callback);
17525
  } else {
17526
    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr);
17527
  }
17528
  return true;
17529
}
17530
17531
inline long get_verify_error(const_session_t session) {
17532
  if (!session) { return -1; }
17533
  auto ssl = static_cast<SSL *>(const_cast<void *>(session));
17534
  return SSL_get_verify_result(ssl);
17535
}
17536
17537
inline std::string verify_error_string(long error_code) {
17538
  if (error_code == X509_V_OK) { return ""; }
17539
  const char *str = X509_verify_cert_error_string(static_cast<int>(error_code));
17540
  return str ? str : "unknown error";
17541
}
17542
17543
} // namespace tls
17544
17545
inline bool SSLClient::verify_host(X509 *server_cert) const {
17546
  /* Quote from RFC2818 section 3.1 "Server Identity"
17547
17548
     If a subjectAltName extension of type dNSName is present, that MUST
17549
     be used as the identity. Otherwise, the (most specific) Common Name
17550
     field in the Subject field of the certificate MUST be used. Although
17551
     the use of the Common Name is existing practice, it is deprecated and
17552
     Certification Authorities are encouraged to use the dNSName instead.
17553
17554
     Matching is performed using the matching rules specified by
17555
     [RFC2459].  If more than one identity of a given type is present in
17556
     the certificate (e.g., more than one dNSName name, a match in any one
17557
     of the set is considered acceptable.) Names may contain the wildcard
17558
     character * which is considered to match any single domain name
17559
     component or component fragment. E.g., *.a.com matches foo.a.com but
17560
     not bar.foo.a.com. f*.com matches foo.com but not bar.com.
17561
17562
     In some cases, the URI is specified as an IP address rather than a
17563
     hostname. In this case, the iPAddress subjectAltName must be present
17564
     in the certificate and must exactly match the IP in the URI.
17565
17566
  */
17567
  return verify_host_with_subject_alt_name(server_cert) ||
17568
         verify_host_with_common_name(server_cert);
17569
}
17570
17571
inline bool
17572
SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
17573
  auto ret = false;
17574
17575
  auto type = GEN_DNS;
17576
17577
  struct in6_addr addr6 = {};
17578
  struct in_addr addr = {};
17579
  size_t addr_len = 0;
17580
17581
#ifndef __MINGW32__
17582
  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
17583
    type = GEN_IPADD;
17584
    addr_len = sizeof(struct in6_addr);
17585
  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
17586
    type = GEN_IPADD;
17587
    addr_len = sizeof(struct in_addr);
17588
  }
17589
#endif
17590
17591
  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
17592
      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
17593
17594
  if (alt_names) {
17595
    auto dsn_matched = false;
17596
    auto ip_matched = false;
17597
17598
    auto count = sk_GENERAL_NAME_num(alt_names);
17599
17600
    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
17601
      auto val = sk_GENERAL_NAME_value(alt_names, i);
17602
      if (!val || val->type != type) { continue; }
17603
17604
      auto name =
17605
          reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
17606
      if (name == nullptr) { continue; }
17607
17608
      auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
17609
17610
      switch (type) {
17611
      case GEN_DNS:
17612
        dsn_matched =
17613
            detail::match_hostname(std::string(name, name_len), host_);
17614
        break;
17615
17616
      case GEN_IPADD:
17617
        if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
17618
          ip_matched = true;
17619
        }
17620
        break;
17621
      }
17622
    }
17623
17624
    if (dsn_matched || ip_matched) { ret = true; }
17625
  }
17626
17627
  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(
17628
      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
17629
  return ret;
17630
}
17631
17632
inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
17633
  const auto subject_name = X509_get_subject_name(server_cert);
17634
17635
  if (subject_name != nullptr) {
17636
    char name[BUFSIZ];
17637
    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
17638
                                              name, sizeof(name));
17639
17640
    if (name_len != -1) {
17641
      return detail::match_hostname(
17642
          std::string(name, static_cast<size_t>(name_len)), host_);
17643
    }
17644
  }
17645
17646
  return false;
17647
}
17648
17649
#endif // CPPHTTPLIB_OPENSSL_SUPPORT
17650
17651
/*
17652
 * Group 9: TLS abstraction layer - Mbed TLS backend
17653
 */
17654
17655
/*
17656
 * Mbed TLS Backend Implementation
17657
 */
17658
17659
#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
17660
namespace tls {
17661
17662
namespace impl {
17663
17664
// Mbed TLS session wrapper
17665
struct MbedTlsSession {
17666
  mbedtls_ssl_context ssl;
17667
  socket_t sock = INVALID_SOCKET;
17668
  std::string hostname;     // For client: set via set_sni
17669
  std::string sni_hostname; // For server: received from client via SNI callback
17670
17671
  MbedTlsSession() { mbedtls_ssl_init(&ssl); }
17672
17673
  ~MbedTlsSession() { mbedtls_ssl_free(&ssl); }
17674
17675
  MbedTlsSession(const MbedTlsSession &) = delete;
17676
  MbedTlsSession &operator=(const MbedTlsSession &) = delete;
17677
};
17678
17679
// Thread-local error code accessor for Mbed TLS (since it doesn't have an error
17680
// queue)
17681
inline int &mbedtls_last_error() {
17682
  static thread_local int err = 0;
17683
  return err;
17684
}
17685
17686
// Helper to map Mbed TLS error to ErrorCode
17687
inline ErrorCode map_mbedtls_error(int ret, int &out_errno) {
17688
  if (ret == 0) { return ErrorCode::Success; }
17689
  if (ret == MBEDTLS_ERR_SSL_WANT_READ) { return ErrorCode::WantRead; }
17690
  if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { return ErrorCode::WantWrite; }
17691
  if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
17692
    return ErrorCode::PeerClosed;
17693
  }
17694
  if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_NET_SEND_FAILED ||
17695
      ret == MBEDTLS_ERR_NET_RECV_FAILED) {
17696
    out_errno = errno;
17697
    return ErrorCode::SyscallError;
17698
  }
17699
  if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
17700
    return ErrorCode::CertVerifyFailed;
17701
  }
17702
  return ErrorCode::Fatal;
17703
}
17704
17705
// BIO-like send callback for Mbed TLS
17706
inline int mbedtls_net_send_cb(void *ctx, const unsigned char *buf,
17707
                               size_t len) {
17708
  auto sock = *static_cast<socket_t *>(ctx);
17709
#ifdef _WIN32
17710
  auto ret =
17711
      send(sock, reinterpret_cast<const char *>(buf), static_cast<int>(len), 0);
17712
  if (ret == SOCKET_ERROR) {
17713
    int err = WSAGetLastError();
17714
    if (err == WSAEWOULDBLOCK) { return MBEDTLS_ERR_SSL_WANT_WRITE; }
17715
    return MBEDTLS_ERR_NET_SEND_FAILED;
17716
  }
17717
#else
17718
  auto ret = send(sock, buf, len, 0);
17719
  if (ret < 0) {
17720
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
17721
      return MBEDTLS_ERR_SSL_WANT_WRITE;
17722
    }
17723
    return MBEDTLS_ERR_NET_SEND_FAILED;
17724
  }
17725
#endif
17726
  return static_cast<int>(ret);
17727
}
17728
17729
// BIO-like recv callback for Mbed TLS
17730
inline int mbedtls_net_recv_cb(void *ctx, unsigned char *buf, size_t len) {
17731
  auto sock = *static_cast<socket_t *>(ctx);
17732
#ifdef _WIN32
17733
  auto ret =
17734
      recv(sock, reinterpret_cast<char *>(buf), static_cast<int>(len), 0);
17735
  if (ret == SOCKET_ERROR) {
17736
    int err = WSAGetLastError();
17737
    if (err == WSAEWOULDBLOCK) { return MBEDTLS_ERR_SSL_WANT_READ; }
17738
    return MBEDTLS_ERR_NET_RECV_FAILED;
17739
  }
17740
#else
17741
  auto ret = recv(sock, buf, len, 0);
17742
  if (ret < 0) {
17743
    if (errno == EAGAIN || errno == EWOULDBLOCK) {
17744
      return MBEDTLS_ERR_SSL_WANT_READ;
17745
    }
17746
    return MBEDTLS_ERR_NET_RECV_FAILED;
17747
  }
17748
#endif
17749
  if (ret == 0) { return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY; }
17750
  return static_cast<int>(ret);
17751
}
17752
17753
// MbedTlsContext constructor/destructor implementations
17754
inline MbedTlsContext::MbedTlsContext() {
17755
  mbedtls_ssl_config_init(&conf);
17756
  mbedtls_entropy_init(&entropy);
17757
  mbedtls_ctr_drbg_init(&ctr_drbg);
17758
  mbedtls_x509_crt_init(&ca_chain);
17759
  mbedtls_x509_crt_init(&own_cert);
17760
  mbedtls_pk_init(&own_key);
17761
}
17762
17763
inline MbedTlsContext::~MbedTlsContext() {
17764
  mbedtls_pk_free(&own_key);
17765
  mbedtls_x509_crt_free(&own_cert);
17766
  mbedtls_x509_crt_free(&ca_chain);
17767
  mbedtls_ctr_drbg_free(&ctr_drbg);
17768
  mbedtls_entropy_free(&entropy);
17769
  mbedtls_ssl_config_free(&conf);
17770
}
17771
17772
// Thread-local storage for SNI captured during handshake
17773
// This is needed because the SNI callback doesn't have a way to pass
17774
// session-specific data before the session is fully set up
17775
inline std::string &mbedpending_sni() {
17776
  static thread_local std::string sni;
17777
  return sni;
17778
}
17779
17780
// SNI callback for Mbed TLS server to capture client's SNI hostname
17781
inline int mbedtls_sni_callback(void *p_ctx, mbedtls_ssl_context *ssl,
17782
                                const unsigned char *name, size_t name_len) {
17783
  (void)p_ctx;
17784
  (void)ssl;
17785
17786
  // Store SNI name in thread-local storage
17787
  // It will be retrieved and stored in the session after handshake
17788
  if (name && name_len > 0) {
17789
    mbedpending_sni().assign(reinterpret_cast<const char *>(name), name_len);
17790
  } else {
17791
    mbedpending_sni().clear();
17792
  }
17793
  return 0; // Accept any SNI
17794
}
17795
17796
inline int mbedtls_verify_callback(void *data, mbedtls_x509_crt *crt,
17797
                                   int cert_depth, uint32_t *flags);
17798
17799
// MbedTLS verify callback wrapper
17800
inline int mbedtls_verify_callback(void *data, mbedtls_x509_crt *crt,
17801
                                   int cert_depth, uint32_t *flags) {
17802
  auto &callback = get_verify_callback();
17803
  if (!callback) { return 0; } // Continue with default verification
17804
17805
  // data points to the MbedTlsSession
17806
  auto *session = static_cast<MbedTlsSession *>(data);
17807
17808
  // Build context
17809
  VerifyContext verify_ctx;
17810
  verify_ctx.session = static_cast<session_t>(session);
17811
  verify_ctx.cert = static_cast<cert_t>(crt);
17812
  verify_ctx.depth = cert_depth;
17813
  verify_ctx.preverify_ok = (*flags == 0);
17814
  verify_ctx.error_code = static_cast<long>(*flags);
17815
17816
  // Convert Mbed TLS flags to error string
17817
  static thread_local char error_buf[256];
17818
  if (*flags != 0) {
17819
    mbedtls_x509_crt_verify_info(error_buf, sizeof(error_buf), "", *flags);
17820
    verify_ctx.error_string = error_buf;
17821
  } else {
17822
    verify_ctx.error_string = nullptr;
17823
  }
17824
17825
  bool accepted = callback(verify_ctx);
17826
17827
  if (accepted) {
17828
    *flags = 0; // Clear all error flags
17829
    return 0;
17830
  }
17831
  return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
17832
}
17833
17834
} // namespace impl
17835
17836
inline ctx_t create_client_context() {
17837
  auto ctx = new (std::nothrow) impl::MbedTlsContext();
17838
  if (!ctx) { return nullptr; }
17839
17840
  ctx->is_server = false;
17841
17842
  // Seed the random number generator
17843
  const char *pers = "httplib_client";
17844
  int ret = mbedtls_ctr_drbg_seed(
17845
      &ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy,
17846
      reinterpret_cast<const unsigned char *>(pers), strlen(pers));
17847
  if (ret != 0) {
17848
    impl::mbedtls_last_error() = ret;
17849
    delete ctx;
17850
    return nullptr;
17851
  }
17852
17853
  // Set up SSL config for client
17854
  ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_CLIENT,
17855
                                    MBEDTLS_SSL_TRANSPORT_STREAM,
17856
                                    MBEDTLS_SSL_PRESET_DEFAULT);
17857
  if (ret != 0) {
17858
    impl::mbedtls_last_error() = ret;
17859
    delete ctx;
17860
    return nullptr;
17861
  }
17862
17863
  // Set random number generator
17864
  mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
17865
17866
  // Default: verify peer certificate
17867
  mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
17868
17869
  // Set minimum TLS version to 1.2
17870
#ifdef CPPHTTPLIB_MBEDTLS_V3
17871
  mbedtls_ssl_conf_min_tls_version(&ctx->conf, MBEDTLS_SSL_VERSION_TLS1_2);
17872
#else
17873
  mbedtls_ssl_conf_min_version(&ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
17874
                               MBEDTLS_SSL_MINOR_VERSION_3);
17875
#endif
17876
17877
  return static_cast<ctx_t>(ctx);
17878
}
17879
17880
inline ctx_t create_server_context() {
17881
  auto ctx = new (std::nothrow) impl::MbedTlsContext();
17882
  if (!ctx) { return nullptr; }
17883
17884
  ctx->is_server = true;
17885
17886
  // Seed the random number generator
17887
  const char *pers = "httplib_server";
17888
  int ret = mbedtls_ctr_drbg_seed(
17889
      &ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy,
17890
      reinterpret_cast<const unsigned char *>(pers), strlen(pers));
17891
  if (ret != 0) {
17892
    impl::mbedtls_last_error() = ret;
17893
    delete ctx;
17894
    return nullptr;
17895
  }
17896
17897
  // Set up SSL config for server
17898
  ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_SERVER,
17899
                                    MBEDTLS_SSL_TRANSPORT_STREAM,
17900
                                    MBEDTLS_SSL_PRESET_DEFAULT);
17901
  if (ret != 0) {
17902
    impl::mbedtls_last_error() = ret;
17903
    delete ctx;
17904
    return nullptr;
17905
  }
17906
17907
  // Set random number generator
17908
  mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
17909
17910
  // Default: don't verify client
17911
  mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
17912
17913
  // Set minimum TLS version to 1.2
17914
#ifdef CPPHTTPLIB_MBEDTLS_V3
17915
  mbedtls_ssl_conf_min_tls_version(&ctx->conf, MBEDTLS_SSL_VERSION_TLS1_2);
17916
#else
17917
  mbedtls_ssl_conf_min_version(&ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
17918
                               MBEDTLS_SSL_MINOR_VERSION_3);
17919
#endif
17920
17921
  // Set SNI callback to capture client's SNI hostname
17922
  mbedtls_ssl_conf_sni(&ctx->conf, impl::mbedtls_sni_callback, nullptr);
17923
17924
  return static_cast<ctx_t>(ctx);
17925
}
17926
17927
inline void free_context(ctx_t ctx) {
17928
  if (ctx) { delete static_cast<impl::MbedTlsContext *>(ctx); }
17929
}
17930
17931
inline bool set_min_version(ctx_t ctx, Version version) {
17932
  if (!ctx) { return false; }
17933
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
17934
17935
#ifdef CPPHTTPLIB_MBEDTLS_V3
17936
  // Mbed TLS 3.x uses mbedtls_ssl_protocol_version enum
17937
  mbedtls_ssl_protocol_version min_ver = MBEDTLS_SSL_VERSION_TLS1_2;
17938
  if (version >= Version::TLS1_3) {
17939
#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
17940
    min_ver = MBEDTLS_SSL_VERSION_TLS1_3;
17941
#endif
17942
  }
17943
  mbedtls_ssl_conf_min_tls_version(&mctx->conf, min_ver);
17944
#else
17945
  // Mbed TLS 2.x uses major/minor version numbers
17946
  int major = MBEDTLS_SSL_MAJOR_VERSION_3;
17947
  int minor = MBEDTLS_SSL_MINOR_VERSION_3; // TLS 1.2
17948
  if (version >= Version::TLS1_3) {
17949
#if defined(MBEDTLS_SSL_PROTO_TLS1_3)
17950
    minor = MBEDTLS_SSL_MINOR_VERSION_4; // TLS 1.3
17951
#else
17952
    minor = MBEDTLS_SSL_MINOR_VERSION_3; // Fall back to TLS 1.2
17953
#endif
17954
  }
17955
  mbedtls_ssl_conf_min_version(&mctx->conf, major, minor);
17956
#endif
17957
  return true;
17958
}
17959
17960
inline bool load_ca_pem(ctx_t ctx, const char *pem, size_t len) {
17961
  if (!ctx || !pem) { return false; }
17962
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
17963
17964
  // mbedtls_x509_crt_parse expects null-terminated string for PEM
17965
  // Add null terminator if not present
17966
  std::string pem_str(pem, len);
17967
  int ret = mbedtls_x509_crt_parse(
17968
      &mctx->ca_chain, reinterpret_cast<const unsigned char *>(pem_str.c_str()),
17969
      pem_str.size() + 1);
17970
  if (ret != 0) {
17971
    impl::mbedtls_last_error() = ret;
17972
    return false;
17973
  }
17974
17975
  mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain, nullptr);
17976
  return true;
17977
}
17978
17979
inline bool load_ca_file(ctx_t ctx, const char *file_path) {
17980
  if (!ctx || !file_path) { return false; }
17981
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
17982
17983
  int ret = mbedtls_x509_crt_parse_file(&mctx->ca_chain, file_path);
17984
  if (ret != 0) {
17985
    impl::mbedtls_last_error() = ret;
17986
    return false;
17987
  }
17988
17989
  mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain, nullptr);
17990
  return true;
17991
}
17992
17993
inline bool load_ca_dir(ctx_t ctx, const char *dir_path) {
17994
  if (!ctx || !dir_path) { return false; }
17995
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
17996
17997
  int ret = mbedtls_x509_crt_parse_path(&mctx->ca_chain, dir_path);
17998
  if (ret < 0) { // Returns number of certs on success, negative on error
17999
    impl::mbedtls_last_error() = ret;
18000
    return false;
18001
  }
18002
18003
  mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain, nullptr);
18004
  return true;
18005
}
18006
18007
inline bool load_system_certs(ctx_t ctx) {
18008
  if (!ctx) { return false; }
18009
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
18010
  bool loaded = false;
18011
18012
#ifdef _WIN32
18013
  loaded = impl::enumerate_windows_system_certs(
18014
      [&](const unsigned char *data, size_t len) {
18015
        return mbedtls_x509_crt_parse_der(&mctx->ca_chain, data, len) == 0;
18016
      });
18017
#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
18018
  loaded = impl::enumerate_macos_keychain_certs(
18019
      [&](const unsigned char *data, size_t len) {
18020
        return mbedtls_x509_crt_parse_der(&mctx->ca_chain, data, len) == 0;
18021
      });
18022
#else
18023
  for (auto path = impl::system_ca_paths(); *path; ++path) {
18024
    if (mbedtls_x509_crt_parse_file(&mctx->ca_chain, *path) >= 0) {
18025
      loaded = true;
18026
      break;
18027
    }
18028
  }
18029
18030
  if (!loaded) {
18031
    for (auto dir = impl::system_ca_dirs(); *dir; ++dir) {
18032
      if (mbedtls_x509_crt_parse_path(&mctx->ca_chain, *dir) >= 0) {
18033
        loaded = true;
18034
        break;
18035
      }
18036
    }
18037
  }
18038
#endif
18039
18040
  if (loaded) {
18041
    mbedtls_ssl_conf_ca_chain(&mctx->conf, &mctx->ca_chain, nullptr);
18042
  }
18043
  return loaded;
18044
}
18045
18046
inline bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key,
18047
                                const char *password) {
18048
  if (!ctx || !cert || !key) { return false; }
18049
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
18050
18051
  // Parse certificate
18052
  std::string cert_str(cert);
18053
  int ret = mbedtls_x509_crt_parse(
18054
      &mctx->own_cert,
18055
      reinterpret_cast<const unsigned char *>(cert_str.c_str()),
18056
      cert_str.size() + 1);
18057
  if (ret != 0) {
18058
    impl::mbedtls_last_error() = ret;
18059
    return false;
18060
  }
18061
18062
  // Parse private key
18063
  std::string key_str(key);
18064
  const unsigned char *pwd =
18065
      password ? reinterpret_cast<const unsigned char *>(password) : nullptr;
18066
  size_t pwd_len = password ? strlen(password) : 0;
18067
18068
#ifdef CPPHTTPLIB_MBEDTLS_V3
18069
  ret = mbedtls_pk_parse_key(
18070
      &mctx->own_key, reinterpret_cast<const unsigned char *>(key_str.c_str()),
18071
      key_str.size() + 1, pwd, pwd_len, mbedtls_ctr_drbg_random,
18072
      &mctx->ctr_drbg);
18073
#else
18074
  ret = mbedtls_pk_parse_key(
18075
      &mctx->own_key, reinterpret_cast<const unsigned char *>(key_str.c_str()),
18076
      key_str.size() + 1, pwd, pwd_len);
18077
#endif
18078
  if (ret != 0) {
18079
    impl::mbedtls_last_error() = ret;
18080
    return false;
18081
  }
18082
18083
  // Verify that the certificate and private key match
18084
#ifdef CPPHTTPLIB_MBEDTLS_V3
18085
  ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key,
18086
                              mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18087
#else
18088
  ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key);
18089
#endif
18090
  if (ret != 0) {
18091
    impl::mbedtls_last_error() = ret;
18092
    return false;
18093
  }
18094
18095
  ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key);
18096
  if (ret != 0) {
18097
    impl::mbedtls_last_error() = ret;
18098
    return false;
18099
  }
18100
18101
  return true;
18102
}
18103
18104
inline bool set_client_cert_file(ctx_t ctx, const char *cert_path,
18105
                                 const char *key_path, const char *password) {
18106
  if (!ctx || !cert_path || !key_path) { return false; }
18107
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
18108
18109
  // Parse certificate file
18110
  int ret = mbedtls_x509_crt_parse_file(&mctx->own_cert, cert_path);
18111
  if (ret != 0) {
18112
    impl::mbedtls_last_error() = ret;
18113
    return false;
18114
  }
18115
18116
  // Parse private key file
18117
#ifdef CPPHTTPLIB_MBEDTLS_V3
18118
  ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password,
18119
                                 mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18120
#else
18121
  ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password);
18122
#endif
18123
  if (ret != 0) {
18124
    impl::mbedtls_last_error() = ret;
18125
    return false;
18126
  }
18127
18128
  // Verify that the certificate and private key match
18129
#ifdef CPPHTTPLIB_MBEDTLS_V3
18130
  ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key,
18131
                              mbedtls_ctr_drbg_random, &mctx->ctr_drbg);
18132
#else
18133
  ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key);
18134
#endif
18135
  if (ret != 0) {
18136
    impl::mbedtls_last_error() = ret;
18137
    return false;
18138
  }
18139
18140
  ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key);
18141
  if (ret != 0) {
18142
    impl::mbedtls_last_error() = ret;
18143
    return false;
18144
  }
18145
18146
  return true;
18147
}
18148
18149
inline void set_verify_client(ctx_t ctx, bool require) {
18150
  if (!ctx) { return; }
18151
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
18152
  mctx->verify_client = require;
18153
  if (require) {
18154
    mbedtls_ssl_conf_authmode(&mctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
18155
  } else {
18156
    // If a verify callback is set, use OPTIONAL mode to ensure the callback
18157
    // is called (matching OpenSSL behavior). Otherwise use NONE.
18158
    mbedtls_ssl_conf_authmode(&mctx->conf, mctx->has_verify_callback
18159
                                               ? MBEDTLS_SSL_VERIFY_OPTIONAL
18160
                                               : MBEDTLS_SSL_VERIFY_NONE);
18161
  }
18162
}
18163
18164
inline session_t create_session(ctx_t ctx, socket_t sock) {
18165
  if (!ctx || sock == INVALID_SOCKET) { return nullptr; }
18166
  auto mctx = static_cast<impl::MbedTlsContext *>(ctx);
18167
18168
  auto session = new (std::nothrow) impl::MbedTlsSession();
18169
  if (!session) { return nullptr; }
18170
18171
  session->sock = sock;
18172
18173
  int ret = mbedtls_ssl_setup(&session->ssl, &mctx->conf);
18174
  if (ret != 0) {
18175
    impl::mbedtls_last_error() = ret;
18176
    delete session;
18177
    return nullptr;
18178
  }
18179
18180
  // Set BIO callbacks
18181
  mbedtls_ssl_set_bio(&session->ssl, &session->sock, impl::mbedtls_net_send_cb,
18182
                      impl::mbedtls_net_recv_cb, nullptr);
18183
18184
  // Set per-session verify callback with session pointer if callback is
18185
  // registered
18186
  if (mctx->has_verify_callback) {
18187
    mbedtls_ssl_set_verify(&session->ssl, impl::mbedtls_verify_callback,
18188
                           session);
18189
  }
18190
18191
  return static_cast<session_t>(session);
18192
}
18193
18194
inline void free_session(session_t session) {
18195
  if (session) { delete static_cast<impl::MbedTlsSession *>(session); }
18196
}
18197
18198
inline bool set_sni(session_t session, const char *hostname) {
18199
  if (!session || !hostname) { return false; }
18200
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18201
18202
  int ret = mbedtls_ssl_set_hostname(&msession->ssl, hostname);
18203
  if (ret != 0) {
18204
    impl::mbedtls_last_error() = ret;
18205
    return false;
18206
  }
18207
18208
  msession->hostname = hostname;
18209
  return true;
18210
}
18211
18212
inline bool set_hostname(session_t session, const char *hostname) {
18213
  // In Mbed TLS, set_hostname also sets up hostname verification
18214
  return set_sni(session, hostname);
18215
}
18216
18217
inline TlsError connect(session_t session) {
18218
  TlsError err;
18219
  if (!session) {
18220
    err.code = ErrorCode::Fatal;
18221
    return err;
18222
  }
18223
18224
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18225
  int ret = mbedtls_ssl_handshake(&msession->ssl);
18226
18227
  if (ret == 0) {
18228
    err.code = ErrorCode::Success;
18229
  } else {
18230
    err.code = impl::map_mbedtls_error(ret, err.sys_errno);
18231
    err.backend_code = static_cast<uint64_t>(-ret);
18232
    impl::mbedtls_last_error() = ret;
18233
  }
18234
18235
  return err;
18236
}
18237
18238
inline TlsError accept(session_t session) {
18239
  // Same as connect for Mbed TLS - handshake works for both client and server
18240
  auto result = connect(session);
18241
18242
  // After successful handshake, capture SNI from thread-local storage
18243
  if (result.code == ErrorCode::Success && session) {
18244
    auto msession = static_cast<impl::MbedTlsSession *>(session);
18245
    msession->sni_hostname = std::move(impl::mbedpending_sni());
18246
    impl::mbedpending_sni().clear();
18247
  }
18248
18249
  return result;
18250
}
18251
18252
inline bool connect_nonblocking(session_t session, socket_t sock,
18253
                                time_t timeout_sec, time_t timeout_usec,
18254
                                TlsError *err) {
18255
  if (!session) {
18256
    if (err) { err->code = ErrorCode::Fatal; }
18257
    return false;
18258
  }
18259
18260
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18261
18262
  // Set socket to non-blocking mode
18263
  detail::set_nonblocking(sock, true);
18264
  auto cleanup =
18265
      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
18266
18267
  int ret;
18268
  while ((ret = mbedtls_ssl_handshake(&msession->ssl)) != 0) {
18269
    if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
18270
      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
18271
        continue;
18272
      }
18273
    } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
18274
      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
18275
        continue;
18276
      }
18277
    }
18278
18279
    // TlsError or timeout
18280
    if (err) {
18281
      err->code = impl::map_mbedtls_error(ret, err->sys_errno);
18282
      err->backend_code = static_cast<uint64_t>(-ret);
18283
    }
18284
    impl::mbedtls_last_error() = ret;
18285
    return false;
18286
  }
18287
18288
  if (err) { err->code = ErrorCode::Success; }
18289
  return true;
18290
}
18291
18292
inline bool accept_nonblocking(session_t session, socket_t sock,
18293
                               time_t timeout_sec, time_t timeout_usec,
18294
                               TlsError *err) {
18295
  // Same implementation as connect for Mbed TLS
18296
  bool result =
18297
      connect_nonblocking(session, sock, timeout_sec, timeout_usec, err);
18298
18299
  // After successful handshake, capture SNI from thread-local storage
18300
  if (result && session) {
18301
    auto msession = static_cast<impl::MbedTlsSession *>(session);
18302
    msession->sni_hostname = std::move(impl::mbedpending_sni());
18303
    impl::mbedpending_sni().clear();
18304
  }
18305
18306
  return result;
18307
}
18308
18309
inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) {
18310
  if (!session || !buf) {
18311
    err.code = ErrorCode::Fatal;
18312
    return -1;
18313
  }
18314
18315
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18316
  int ret =
18317
      mbedtls_ssl_read(&msession->ssl, static_cast<unsigned char *>(buf), len);
18318
18319
  if (ret > 0) {
18320
    err.code = ErrorCode::Success;
18321
    return static_cast<ssize_t>(ret);
18322
  }
18323
18324
  if (ret == 0) {
18325
    err.code = ErrorCode::PeerClosed;
18326
    return 0;
18327
  }
18328
18329
  err.code = impl::map_mbedtls_error(ret, err.sys_errno);
18330
  err.backend_code = static_cast<uint64_t>(-ret);
18331
  impl::mbedtls_last_error() = ret;
18332
  // mbedTLS signals a clean close_notify via a negative error code rather
18333
  // than 0; surface it as a clean EOF the way OpenSSL/wolfSSL do.
18334
  if (err.code == ErrorCode::PeerClosed) { return 0; }
18335
  return -1;
18336
}
18337
18338
inline ssize_t write(session_t session, const void *buf, size_t len,
18339
                     TlsError &err) {
18340
  if (!session || !buf) {
18341
    err.code = ErrorCode::Fatal;
18342
    return -1;
18343
  }
18344
18345
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18346
  int ret = mbedtls_ssl_write(&msession->ssl,
18347
                              static_cast<const unsigned char *>(buf), len);
18348
18349
  if (ret > 0) {
18350
    err.code = ErrorCode::Success;
18351
    return static_cast<ssize_t>(ret);
18352
  }
18353
18354
  if (ret == 0) {
18355
    err.code = ErrorCode::PeerClosed;
18356
    return 0;
18357
  }
18358
18359
  err.code = impl::map_mbedtls_error(ret, err.sys_errno);
18360
  err.backend_code = static_cast<uint64_t>(-ret);
18361
  impl::mbedtls_last_error() = ret;
18362
  return -1;
18363
}
18364
18365
inline int pending(const_session_t session) {
18366
  if (!session) { return 0; }
18367
  auto msession =
18368
      static_cast<impl::MbedTlsSession *>(const_cast<void *>(session));
18369
  return static_cast<int>(mbedtls_ssl_get_bytes_avail(&msession->ssl));
18370
}
18371
18372
inline void shutdown(session_t session, bool graceful) {
18373
  if (!session) { return; }
18374
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18375
18376
  if (graceful) {
18377
    // Try to send close_notify, but don't block forever
18378
    int ret;
18379
    int attempts = 0;
18380
    while ((ret = mbedtls_ssl_close_notify(&msession->ssl)) != 0 &&
18381
           attempts < 3) {
18382
      if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
18383
          ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
18384
        break;
18385
      }
18386
      attempts++;
18387
    }
18388
  }
18389
}
18390
18391
inline bool is_peer_closed(session_t session, socket_t sock) {
18392
  if (!session || sock == INVALID_SOCKET) { return true; }
18393
  auto msession = static_cast<impl::MbedTlsSession *>(session);
18394
18395
  // Check if there's already decrypted data available in the TLS buffer
18396
  // If so, the connection is definitely alive
18397
  if (mbedtls_ssl_get_bytes_avail(&msession->ssl) > 0) { return false; }
18398
18399
  // Set socket to non-blocking to avoid blocking on read
18400
  detail::set_nonblocking(sock, true);
18401
  auto cleanup =
18402
      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
18403
18404
  // Try a 1-byte read to check connection status
18405
  // Note: This will consume the byte if data is available, but for the
18406
  // purpose of checking if peer is closed, this should be acceptable
18407
  // since we're only called when we expect the connection might be closing
18408
  unsigned char buf;
18409
  int ret = mbedtls_ssl_read(&msession->ssl, &buf, 1);
18410
18411
  // If we got data or WANT_READ (would block), connection is alive
18412
  if (ret > 0 || ret == MBEDTLS_ERR_SSL_WANT_READ) { return false; }
18413
18414
  // If we get a peer close notify or a connection reset, the peer is closed
18415
  return ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY ||
18416
         ret == MBEDTLS_ERR_NET_CONN_RESET || ret == 0;
18417
}
18418
18419
inline cert_t get_peer_cert(const_session_t session) {
18420
  if (!session) { return nullptr; }
18421
  auto msession =
18422
      static_cast<impl::MbedTlsSession *>(const_cast<void *>(session));
18423
18424
  // Mbed TLS returns a pointer to the internal peer cert chain.
18425
  // WARNING: This pointer is only valid while the session is active.
18426
  // Do not use the certificate after calling free_session().
18427
  const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&msession->ssl);
18428
  return const_cast<mbedtls_x509_crt *>(cert);
18429
}
18430
18431
inline void free_cert(cert_t cert) {
18432
  // Mbed TLS: peer certificate is owned by the SSL context.
18433
  // No-op here, but callers should still call this for cross-backend
18434
  // portability.
18435
  (void)cert;
18436
}
18437
18438
inline bool verify_hostname(cert_t cert, const char *hostname) {
18439
  if (!cert || !hostname) { return false; }
18440
  auto mcert = static_cast<const mbedtls_x509_crt *>(cert);
18441
  std::string host_str(hostname);
18442
18443
  // Check if hostname is an IP address
18444
  bool is_ip = impl::is_ipv4_address(host_str);
18445
  unsigned char ip_bytes[4];
18446
  if (is_ip) { impl::parse_ipv4(host_str, ip_bytes); }
18447
18448
  // Check Subject Alternative Names (SAN)
18449
  // In Mbed TLS 3.x, subject_alt_names contains raw values without ASN.1 tags
18450
  // - DNS names: raw string bytes
18451
  // - IP addresses: raw IP bytes (4 for IPv4, 16 for IPv6)
18452
  const mbedtls_x509_sequence *san = &mcert->subject_alt_names;
18453
  while (san != nullptr && san->buf.p != nullptr && san->buf.len > 0) {
18454
    const unsigned char *p = san->buf.p;
18455
    size_t len = san->buf.len;
18456
18457
    if (is_ip) {
18458
      // Check if this SAN is an IPv4 address (4 bytes)
18459
      if (len == 4 && memcmp(p, ip_bytes, 4) == 0) { return true; }
18460
      // Check if this SAN is an IPv6 address (16 bytes) - skip for now
18461
    } else {
18462
      // Check if this SAN is a DNS name (printable ASCII string)
18463
      bool is_dns = len > 0;
18464
      for (size_t i = 0; i < len && is_dns; i++) {
18465
        if (p[i] < 32 || p[i] > 126) { is_dns = false; }
18466
      }
18467
      if (is_dns) {
18468
        std::string san_name(reinterpret_cast<const char *>(p), len);
18469
        if (detail::match_hostname(san_name, host_str)) { return true; }
18470
      }
18471
    }
18472
    san = san->next;
18473
  }
18474
18475
  // Fallback: Check Common Name (CN) in subject
18476
  char cn[256];
18477
  int ret = mbedtls_x509_dn_gets(cn, sizeof(cn), &mcert->subject);
18478
  if (ret > 0) {
18479
    std::string cn_str(cn);
18480
18481
    // Look for "CN=" in the DN string
18482
    size_t cn_pos = cn_str.find("CN=");
18483
    if (cn_pos != std::string::npos) {
18484
      size_t start = cn_pos + 3;
18485
      size_t end = cn_str.find(',', start);
18486
      std::string cn_value =
18487
          cn_str.substr(start, end == std::string::npos ? end : end - start);
18488
18489
      if (detail::match_hostname(cn_value, host_str)) { return true; }
18490
    }
18491
  }
18492
18493
  return false;
18494
}
18495
18496
inline uint64_t hostname_mismatch_code() {
18497
  return static_cast<uint64_t>(MBEDTLS_X509_BADCERT_CN_MISMATCH);
18498
}
18499
18500
inline long get_verify_result(const_session_t session) {
18501
  if (!session) { return -1; }
18502
  auto msession =
18503
      static_cast<impl::MbedTlsSession *>(const_cast<void *>(session));
18504
  uint32_t flags = mbedtls_ssl_get_verify_result(&msession->ssl);
18505
  // Return 0 (X509_V_OK equivalent) if verification passed
18506
  return flags == 0 ? 0 : static_cast<long>(flags);
18507
}
18508
18509
inline std::string get_cert_subject_cn(cert_t cert) {
18510
  if (!cert) return "";
18511
  auto x509 = static_cast<mbedtls_x509_crt *>(cert);
18512
18513
  // Find the CN in the subject
18514
  const mbedtls_x509_name *name = &x509->subject;
18515
  while (name != nullptr) {
18516
    if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0) {
18517
      return std::string(reinterpret_cast<const char *>(name->val.p),
18518
                         name->val.len);
18519
    }
18520
    name = name->next;
18521
  }
18522
  return "";
18523
}
18524
18525
inline std::string get_cert_issuer_name(cert_t cert) {
18526
  if (!cert) return "";
18527
  auto x509 = static_cast<mbedtls_x509_crt *>(cert);
18528
18529
  // Build a human-readable issuer name string
18530
  char buf[512];
18531
  int ret = mbedtls_x509_dn_gets(buf, sizeof(buf), &x509->issuer);
18532
  if (ret < 0) return "";
18533
  return std::string(buf);
18534
}
18535
18536
inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
18537
  sans.clear();
18538
  if (!cert) return false;
18539
  auto x509 = static_cast<mbedtls_x509_crt *>(cert);
18540
18541
  // Parse the Subject Alternative Name extension
18542
  const mbedtls_x509_sequence *cur = &x509->subject_alt_names;
18543
  while (cur != nullptr) {
18544
    if (cur->buf.len > 0) {
18545
      // Mbed TLS stores SAN as ASN.1 sequences
18546
      // The tag byte indicates the type
18547
      const unsigned char *p = cur->buf.p;
18548
      size_t len = cur->buf.len;
18549
18550
      // First byte is the tag
18551
      unsigned char tag = *p;
18552
      p++;
18553
      len--;
18554
18555
      // Parse length (simple single-byte length assumed)
18556
      if (len > 0 && *p < 0x80) {
18557
        size_t value_len = *p;
18558
        p++;
18559
        len--;
18560
18561
        if (value_len <= len) {
18562
          SanEntry entry;
18563
          // ASN.1 context tags for GeneralName
18564
          switch (tag & 0x1F) {
18565
          case 2: // dNSName
18566
            entry.type = SanType::DNS;
18567
            entry.value =
18568
                std::string(reinterpret_cast<const char *>(p), value_len);
18569
            break;
18570
          case 7: // iPAddress
18571
            entry.type = SanType::IP;
18572
            if (value_len == 4) {
18573
              // IPv4
18574
              char buf[16];
18575
              snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
18576
              entry.value = buf;
18577
            } else if (value_len == 16) {
18578
              // IPv6
18579
              char buf[64];
18580
              snprintf(buf, sizeof(buf),
18581
                       "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
18582
                       "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
18583
                       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8],
18584
                       p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
18585
              entry.value = buf;
18586
            }
18587
            break;
18588
          case 1: // rfc822Name (email)
18589
            entry.type = SanType::EMAIL;
18590
            entry.value =
18591
                std::string(reinterpret_cast<const char *>(p), value_len);
18592
            break;
18593
          case 6: // uniformResourceIdentifier
18594
            entry.type = SanType::URI;
18595
            entry.value =
18596
                std::string(reinterpret_cast<const char *>(p), value_len);
18597
            break;
18598
          default: entry.type = SanType::OTHER; break;
18599
          }
18600
18601
          if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
18602
        }
18603
      }
18604
    }
18605
    cur = cur->next;
18606
  }
18607
  return true;
18608
}
18609
18610
inline bool get_cert_validity(cert_t cert, time_t &not_before,
18611
                              time_t &not_after) {
18612
  if (!cert) return false;
18613
  auto x509 = static_cast<mbedtls_x509_crt *>(cert);
18614
18615
  // Convert mbedtls_x509_time to time_t
18616
  auto to_time_t = [](const mbedtls_x509_time &t) -> time_t {
18617
    struct tm tm_time = {};
18618
    tm_time.tm_year = t.year - 1900;
18619
    tm_time.tm_mon = t.mon - 1;
18620
    tm_time.tm_mday = t.day;
18621
    tm_time.tm_hour = t.hour;
18622
    tm_time.tm_min = t.min;
18623
    tm_time.tm_sec = t.sec;
18624
#ifdef _WIN32
18625
    return _mkgmtime(&tm_time);
18626
#else
18627
    return timegm(&tm_time);
18628
#endif
18629
  };
18630
18631
  not_before = to_time_t(x509->valid_from);
18632
  not_after = to_time_t(x509->valid_to);
18633
  return true;
18634
}
18635
18636
inline std::string get_cert_serial(cert_t cert) {
18637
  if (!cert) return "";
18638
  auto x509 = static_cast<mbedtls_x509_crt *>(cert);
18639
18640
  // Convert serial number to hex string
18641
  std::string result;
18642
  result.reserve(x509->serial.len * 2);
18643
  for (size_t i = 0; i < x509->serial.len; i++) {
18644
    char hex[3];
18645
    snprintf(hex, sizeof(hex), "%02X", x509->serial.p[i]);
18646
    result += hex;
18647
  }
18648
  return result;
18649
}
18650
18651
inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
18652
  if (!cert) return false;
18653
  auto crt = static_cast<mbedtls_x509_crt *>(cert);
18654
  if (!crt->raw.p || crt->raw.len == 0) return false;
18655
  der.assign(crt->raw.p, crt->raw.p + crt->raw.len);
18656
  return true;
18657
}
18658
18659
inline const char *get_sni(const_session_t session) {
18660
  if (!session) return nullptr;
18661
  auto msession = static_cast<const impl::MbedTlsSession *>(session);
18662
18663
  // For server: return SNI received from client during handshake
18664
  if (!msession->sni_hostname.empty()) {
18665
    return msession->sni_hostname.c_str();
18666
  }
18667
18668
  // For client: return the hostname set via set_sni
18669
  if (!msession->hostname.empty()) { return msession->hostname.c_str(); }
18670
18671
  return nullptr;
18672
}
18673
18674
inline uint64_t peek_error() {
18675
  // Mbed TLS doesn't have an error queue, return the last error
18676
  return static_cast<uint64_t>(-impl::mbedtls_last_error());
18677
}
18678
18679
inline uint64_t get_error() {
18680
  // Mbed TLS doesn't have an error queue, return and clear the last error
18681
  uint64_t err = static_cast<uint64_t>(-impl::mbedtls_last_error());
18682
  impl::mbedtls_last_error() = 0;
18683
  return err;
18684
}
18685
18686
inline std::string error_string(uint64_t code) {
18687
  char buf[256];
18688
  mbedtls_strerror(-static_cast<int>(code), buf, sizeof(buf));
18689
  return std::string(buf);
18690
}
18691
18692
inline ca_store_t create_ca_store(const char *pem, size_t len) {
18693
  auto *ca_chain = new (std::nothrow) mbedtls_x509_crt;
18694
  if (!ca_chain) { return nullptr; }
18695
18696
  mbedtls_x509_crt_init(ca_chain);
18697
18698
  // mbedtls_x509_crt_parse expects null-terminated PEM
18699
  int ret = mbedtls_x509_crt_parse(ca_chain,
18700
                                   reinterpret_cast<const unsigned char *>(pem),
18701
                                   len + 1); // +1 for null terminator
18702
  if (ret != 0) {
18703
    // Try without +1 in case PEM is already null-terminated
18704
    ret = mbedtls_x509_crt_parse(
18705
        ca_chain, reinterpret_cast<const unsigned char *>(pem), len);
18706
    if (ret != 0) {
18707
      mbedtls_x509_crt_free(ca_chain);
18708
      delete ca_chain;
18709
      return nullptr;
18710
    }
18711
  }
18712
18713
  return static_cast<ca_store_t>(ca_chain);
18714
}
18715
18716
inline void free_ca_store(ca_store_t store) {
18717
  if (store) {
18718
    auto *ca_chain = static_cast<mbedtls_x509_crt *>(store);
18719
    mbedtls_x509_crt_free(ca_chain);
18720
    delete ca_chain;
18721
  }
18722
}
18723
18724
inline bool set_ca_store(ctx_t ctx, ca_store_t store) {
18725
  if (!ctx || !store) { return false; }
18726
  auto *mbed_ctx = static_cast<impl::MbedTlsContext *>(ctx);
18727
  auto *ca_chain = static_cast<mbedtls_x509_crt *>(store);
18728
18729
  // Free existing CA chain
18730
  mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
18731
  mbedtls_x509_crt_init(&mbed_ctx->ca_chain);
18732
18733
  // Copy the CA chain (deep copy)
18734
  // Parse from the raw data of the source cert
18735
  mbedtls_x509_crt *src = ca_chain;
18736
  while (src != nullptr) {
18737
    int ret = mbedtls_x509_crt_parse_der(&mbed_ctx->ca_chain, src->raw.p,
18738
                                         src->raw.len);
18739
    if (ret != 0) { return false; }
18740
    src = src->next;
18741
  }
18742
18743
  // Update the SSL config to use the new CA chain
18744
  mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->ca_chain, nullptr);
18745
  return true;
18746
}
18747
18748
inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
18749
  certs.clear();
18750
  if (!ctx) { return 0; }
18751
  auto *mbed_ctx = static_cast<impl::MbedTlsContext *>(ctx);
18752
18753
  // Iterate through the CA chain
18754
  mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
18755
  while (cert != nullptr && cert->raw.len > 0) {
18756
    // Create a copy of the certificate for the caller
18757
    auto *copy = new mbedtls_x509_crt;
18758
    mbedtls_x509_crt_init(copy);
18759
    int ret = mbedtls_x509_crt_parse_der(copy, cert->raw.p, cert->raw.len);
18760
    if (ret == 0) {
18761
      certs.push_back(static_cast<cert_t>(copy));
18762
    } else {
18763
      mbedtls_x509_crt_free(copy);
18764
      delete copy;
18765
    }
18766
    cert = cert->next;
18767
  }
18768
  return certs.size();
18769
}
18770
18771
inline std::vector<std::string> get_ca_names(ctx_t ctx) {
18772
  std::vector<std::string> names;
18773
  if (!ctx) { return names; }
18774
  auto *mbed_ctx = static_cast<impl::MbedTlsContext *>(ctx);
18775
18776
  // Iterate through the CA chain
18777
  mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
18778
  while (cert != nullptr && cert->raw.len > 0) {
18779
    char buf[512];
18780
    int ret = mbedtls_x509_dn_gets(buf, sizeof(buf), &cert->subject);
18781
    if (ret > 0) { names.push_back(buf); }
18782
    cert = cert->next;
18783
  }
18784
  return names;
18785
}
18786
18787
inline bool update_server_cert(ctx_t ctx, const char *cert_pem,
18788
                               const char *key_pem, const char *password) {
18789
  if (!ctx || !cert_pem || !key_pem) { return false; }
18790
  auto *mbed_ctx = static_cast<impl::MbedTlsContext *>(ctx);
18791
18792
  // Free existing certificate and key
18793
  mbedtls_x509_crt_free(&mbed_ctx->own_cert);
18794
  mbedtls_pk_free(&mbed_ctx->own_key);
18795
  mbedtls_x509_crt_init(&mbed_ctx->own_cert);
18796
  mbedtls_pk_init(&mbed_ctx->own_key);
18797
18798
  // Parse certificate PEM
18799
  int ret = mbedtls_x509_crt_parse(
18800
      &mbed_ctx->own_cert, reinterpret_cast<const unsigned char *>(cert_pem),
18801
      strlen(cert_pem) + 1);
18802
  if (ret != 0) {
18803
    impl::mbedtls_last_error() = ret;
18804
    return false;
18805
  }
18806
18807
  // Parse private key PEM
18808
#ifdef CPPHTTPLIB_MBEDTLS_V3
18809
  ret = mbedtls_pk_parse_key(
18810
      &mbed_ctx->own_key, reinterpret_cast<const unsigned char *>(key_pem),
18811
      strlen(key_pem) + 1,
18812
      password ? reinterpret_cast<const unsigned char *>(password) : nullptr,
18813
      password ? strlen(password) : 0, mbedtls_ctr_drbg_random,
18814
      &mbed_ctx->ctr_drbg);
18815
#else
18816
  ret = mbedtls_pk_parse_key(
18817
      &mbed_ctx->own_key, reinterpret_cast<const unsigned char *>(key_pem),
18818
      strlen(key_pem) + 1,
18819
      password ? reinterpret_cast<const unsigned char *>(password) : nullptr,
18820
      password ? strlen(password) : 0);
18821
#endif
18822
  if (ret != 0) {
18823
    impl::mbedtls_last_error() = ret;
18824
    return false;
18825
  }
18826
18827
  // Configure SSL to use the new certificate and key
18828
  ret = mbedtls_ssl_conf_own_cert(&mbed_ctx->conf, &mbed_ctx->own_cert,
18829
                                  &mbed_ctx->own_key);
18830
  if (ret != 0) {
18831
    impl::mbedtls_last_error() = ret;
18832
    return false;
18833
  }
18834
18835
  return true;
18836
}
18837
18838
inline bool update_server_client_ca(ctx_t ctx, const char *ca_pem) {
18839
  if (!ctx || !ca_pem) { return false; }
18840
  auto *mbed_ctx = static_cast<impl::MbedTlsContext *>(ctx);
18841
18842
  // Free existing CA chain
18843
  mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
18844
  mbedtls_x509_crt_init(&mbed_ctx->ca_chain);
18845
18846
  // Parse CA PEM
18847
  int ret = mbedtls_x509_crt_parse(
18848
      &mbed_ctx->ca_chain, reinterpret_cast<const unsigned char *>(ca_pem),
18849
      strlen(ca_pem) + 1);
18850
  if (ret != 0) {
18851
    impl::mbedtls_last_error() = ret;
18852
    return false;
18853
  }
18854
18855
  // Update SSL config to use new CA chain
18856
  mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->ca_chain, nullptr);
18857
  return true;
18858
}
18859
18860
inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
18861
  if (!ctx) { return false; }
18862
  auto *mbed_ctx = static_cast<impl::MbedTlsContext *>(ctx);
18863
18864
  impl::get_verify_callback() = std::move(callback);
18865
  mbed_ctx->has_verify_callback =
18866
      static_cast<bool>(impl::get_verify_callback());
18867
18868
  if (mbed_ctx->has_verify_callback) {
18869
    // Set OPTIONAL mode to ensure callback is called even when verification
18870
    // is disabled (matching OpenSSL behavior where SSL_VERIFY_PEER is set)
18871
    mbedtls_ssl_conf_authmode(&mbed_ctx->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
18872
    mbedtls_ssl_conf_verify(&mbed_ctx->conf, impl::mbedtls_verify_callback,
18873
                            nullptr);
18874
  } else {
18875
    mbedtls_ssl_conf_verify(&mbed_ctx->conf, nullptr, nullptr);
18876
  }
18877
  return true;
18878
}
18879
18880
inline long get_verify_error(const_session_t session) {
18881
  if (!session) { return -1; }
18882
  auto *msession =
18883
      static_cast<impl::MbedTlsSession *>(const_cast<void *>(session));
18884
  return static_cast<long>(mbedtls_ssl_get_verify_result(&msession->ssl));
18885
}
18886
18887
inline std::string verify_error_string(long error_code) {
18888
  if (error_code == 0) { return ""; }
18889
  char buf[256];
18890
  mbedtls_x509_crt_verify_info(buf, sizeof(buf), "",
18891
                               static_cast<uint32_t>(error_code));
18892
  // Remove trailing newline if present
18893
  std::string result(buf);
18894
  while (!result.empty() && (result.back() == '\n' || result.back() == ' ')) {
18895
    result.pop_back();
18896
  }
18897
  return result;
18898
}
18899
18900
} // namespace tls
18901
18902
#endif // CPPHTTPLIB_MBEDTLS_SUPPORT
18903
18904
/*
18905
 * Group 10: TLS abstraction layer - wolfSSL backend
18906
 */
18907
18908
/*
18909
 * wolfSSL Backend Implementation
18910
 */
18911
18912
#ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
18913
namespace tls {
18914
18915
namespace impl {
18916
18917
// wolfSSL session wrapper
18918
struct WolfSSLSession {
18919
  WOLFSSL *ssl = nullptr;
18920
  socket_t sock = INVALID_SOCKET;
18921
  std::string hostname;     // For client: set via set_sni
18922
  std::string sni_hostname; // For server: received from client via SNI callback
18923
18924
  WolfSSLSession() = default;
18925
18926
  ~WolfSSLSession() {
18927
    if (ssl) { wolfSSL_free(ssl); }
18928
  }
18929
18930
  WolfSSLSession(const WolfSSLSession &) = delete;
18931
  WolfSSLSession &operator=(const WolfSSLSession &) = delete;
18932
};
18933
18934
// Thread-local error code accessor for wolfSSL
18935
inline uint64_t &wolfssl_last_error() {
18936
  static thread_local uint64_t err = 0;
18937
  return err;
18938
}
18939
18940
// Helper to map wolfSSL error to ErrorCode.
18941
// ssl_error is the value from wolfSSL_get_error().
18942
// raw_ret is the raw return value from the wolfSSL call (for low-level error).
18943
inline ErrorCode map_wolfssl_error(WOLFSSL *ssl, int ssl_error,
18944
                                   int &out_errno) {
18945
  switch (ssl_error) {
18946
  case SSL_ERROR_NONE: return ErrorCode::Success;
18947
  case SSL_ERROR_WANT_READ: return ErrorCode::WantRead;
18948
  case SSL_ERROR_WANT_WRITE: return ErrorCode::WantWrite;
18949
  case SSL_ERROR_ZERO_RETURN: return ErrorCode::PeerClosed;
18950
  case SSL_ERROR_SYSCALL: out_errno = errno; return ErrorCode::SyscallError;
18951
  default:
18952
    if (ssl) {
18953
      // wolfSSL stores the low-level error code as a negative value.
18954
      // DOMAIN_NAME_MISMATCH (-322) indicates hostname verification failure.
18955
      int low_err = ssl_error; // wolfSSL_get_error returns the low-level code
18956
      if (low_err == DOMAIN_NAME_MISMATCH) {
18957
        return ErrorCode::HostnameMismatch;
18958
      }
18959
      // Check verify result to distinguish cert verification from generic SSL
18960
      // errors.
18961
      long vr = wolfSSL_get_verify_result(ssl);
18962
      if (vr != 0) { return ErrorCode::CertVerifyFailed; }
18963
    }
18964
    return ErrorCode::Fatal;
18965
  }
18966
}
18967
18968
// WolfSSLContext constructor/destructor implementations
18969
inline WolfSSLContext::WolfSSLContext() { wolfSSL_Init(); }
18970
18971
inline WolfSSLContext::~WolfSSLContext() {
18972
  if (ctx) { wolfSSL_CTX_free(ctx); }
18973
}
18974
18975
// Thread-local storage for SNI captured during handshake
18976
inline std::string &wolfssl_pending_sni() {
18977
  static thread_local std::string sni;
18978
  return sni;
18979
}
18980
18981
// SNI callback for wolfSSL server to capture client's SNI hostname
18982
inline int wolfssl_sni_callback(WOLFSSL *ssl, int *ret, void *exArg) {
18983
  (void)ret;
18984
  (void)exArg;
18985
18986
  void *name_data = nullptr;
18987
  unsigned short name_len =
18988
      wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, &name_data);
18989
18990
  if (name_data && name_len > 0) {
18991
    wolfssl_pending_sni().assign(static_cast<const char *>(name_data),
18992
                                 name_len);
18993
  } else {
18994
    wolfssl_pending_sni().clear();
18995
  }
18996
  return 0; // Continue regardless
18997
}
18998
18999
// wolfSSL verify callback wrapper
19000
inline int wolfssl_verify_callback(int preverify_ok,
19001
                                   WOLFSSL_X509_STORE_CTX *x509_ctx) {
19002
  auto &callback = get_verify_callback();
19003
  if (!callback) { return preverify_ok; }
19004
19005
  WOLFSSL_X509 *cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
19006
  int depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
19007
  int err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
19008
19009
  // Get the WOLFSSL object from the X509_STORE_CTX
19010
  WOLFSSL *ssl = static_cast<WOLFSSL *>(wolfSSL_X509_STORE_CTX_get_ex_data(
19011
      x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx()));
19012
19013
  VerifyContext verify_ctx;
19014
  verify_ctx.session = static_cast<session_t>(ssl);
19015
  verify_ctx.cert = static_cast<cert_t>(cert);
19016
  verify_ctx.depth = depth;
19017
  verify_ctx.preverify_ok = (preverify_ok != 0);
19018
  verify_ctx.error_code = static_cast<long>(err);
19019
19020
  if (err != 0) {
19021
    verify_ctx.error_string = wolfSSL_X509_verify_cert_error_string(err);
19022
  } else {
19023
    verify_ctx.error_string = nullptr;
19024
  }
19025
19026
  bool accepted = callback(verify_ctx);
19027
  return accepted ? 1 : 0;
19028
}
19029
19030
inline void set_wolfssl_password_cb(WOLFSSL_CTX *ctx, const char *password) {
19031
  wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, const_cast<char *>(password));
19032
  wolfSSL_CTX_set_default_passwd_cb(
19033
      ctx, [](char *buf, int size, int /*rwflag*/, void *userdata) -> int {
19034
        auto *pwd = static_cast<const char *>(userdata);
19035
        if (!pwd) return 0;
19036
        auto len = static_cast<int>(strlen(pwd));
19037
        if (len > size) len = size;
19038
        memcpy(buf, pwd, static_cast<size_t>(len));
19039
        return len;
19040
      });
19041
}
19042
19043
} // namespace impl
19044
19045
inline ctx_t create_client_context() {
19046
  auto ctx = new (std::nothrow) impl::WolfSSLContext();
19047
  if (!ctx) { return nullptr; }
19048
19049
  ctx->is_server = false;
19050
19051
  WOLFSSL_METHOD *method = wolfTLSv1_2_client_method();
19052
  if (!method) {
19053
    delete ctx;
19054
    return nullptr;
19055
  }
19056
19057
  ctx->ctx = wolfSSL_CTX_new(method);
19058
  if (!ctx->ctx) {
19059
    delete ctx;
19060
    return nullptr;
19061
  }
19062
19063
  // Default: verify peer certificate
19064
  wolfSSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_PEER, nullptr);
19065
19066
  return static_cast<ctx_t>(ctx);
19067
}
19068
19069
inline ctx_t create_server_context() {
19070
  auto ctx = new (std::nothrow) impl::WolfSSLContext();
19071
  if (!ctx) { return nullptr; }
19072
19073
  ctx->is_server = true;
19074
19075
  WOLFSSL_METHOD *method = wolfTLSv1_2_server_method();
19076
  if (!method) {
19077
    delete ctx;
19078
    return nullptr;
19079
  }
19080
19081
  ctx->ctx = wolfSSL_CTX_new(method);
19082
  if (!ctx->ctx) {
19083
    delete ctx;
19084
    return nullptr;
19085
  }
19086
19087
  // Default: don't verify client
19088
  wolfSSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_NONE, nullptr);
19089
19090
  // Enable SNI on server
19091
  wolfSSL_CTX_SNI_SetOptions(ctx->ctx, WOLFSSL_SNI_HOST_NAME,
19092
                             WOLFSSL_SNI_CONTINUE_ON_MISMATCH);
19093
  wolfSSL_CTX_set_servername_callback(ctx->ctx, impl::wolfssl_sni_callback);
19094
19095
  return static_cast<ctx_t>(ctx);
19096
}
19097
19098
inline void free_context(ctx_t ctx) {
19099
  if (ctx) { delete static_cast<impl::WolfSSLContext *>(ctx); }
19100
}
19101
19102
inline bool set_min_version(ctx_t ctx, Version version) {
19103
  if (!ctx) { return false; }
19104
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19105
19106
  int min_ver = WOLFSSL_TLSV1_2;
19107
  if (version >= Version::TLS1_3) { min_ver = WOLFSSL_TLSV1_3; }
19108
19109
  return wolfSSL_CTX_SetMinVersion(wctx->ctx, min_ver) == WOLFSSL_SUCCESS;
19110
}
19111
19112
inline bool load_ca_pem(ctx_t ctx, const char *pem, size_t len) {
19113
  if (!ctx || !pem) { return false; }
19114
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19115
19116
  int ret = wolfSSL_CTX_load_verify_buffer(
19117
      wctx->ctx, reinterpret_cast<const unsigned char *>(pem),
19118
      static_cast<long>(len), SSL_FILETYPE_PEM);
19119
  if (ret != SSL_SUCCESS) {
19120
    impl::wolfssl_last_error() =
19121
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19122
    return false;
19123
  }
19124
  wctx->ca_pem_data_.append(pem, len);
19125
  return true;
19126
}
19127
19128
inline bool load_ca_file(ctx_t ctx, const char *file_path) {
19129
  if (!ctx || !file_path) { return false; }
19130
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19131
19132
  int ret = wolfSSL_CTX_load_verify_locations(wctx->ctx, file_path, nullptr);
19133
  if (ret != SSL_SUCCESS) {
19134
    impl::wolfssl_last_error() =
19135
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19136
    return false;
19137
  }
19138
  return true;
19139
}
19140
19141
inline bool load_ca_dir(ctx_t ctx, const char *dir_path) {
19142
  if (!ctx || !dir_path) { return false; }
19143
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19144
19145
  int ret = wolfSSL_CTX_load_verify_locations(wctx->ctx, nullptr, dir_path);
19146
  // wolfSSL may fail if the directory doesn't contain properly hashed certs.
19147
  // Unlike OpenSSL which lazily loads certs from directories, wolfSSL scans
19148
  // immediately. Return true even on failure since the CA file may have
19149
  // already been loaded, matching OpenSSL's lenient behavior.
19150
  (void)ret;
19151
  return true;
19152
}
19153
19154
inline bool load_system_certs(ctx_t ctx) {
19155
  if (!ctx) { return false; }
19156
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19157
  bool loaded = false;
19158
19159
#ifdef _WIN32
19160
  loaded = impl::enumerate_windows_system_certs(
19161
      [&](const unsigned char *data, size_t len) {
19162
        return wolfSSL_CTX_load_verify_buffer(wctx->ctx, data,
19163
                                              static_cast<long>(len),
19164
                                              SSL_FILETYPE_ASN1) == SSL_SUCCESS;
19165
      });
19166
#elif defined(__APPLE__) && defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
19167
  loaded = impl::enumerate_macos_keychain_certs(
19168
      [&](const unsigned char *data, size_t len) {
19169
        return wolfSSL_CTX_load_verify_buffer(wctx->ctx, data,
19170
                                              static_cast<long>(len),
19171
                                              SSL_FILETYPE_ASN1) == SSL_SUCCESS;
19172
      });
19173
#else
19174
  for (auto path = impl::system_ca_paths(); *path; ++path) {
19175
    if (wolfSSL_CTX_load_verify_locations(wctx->ctx, *path, nullptr) ==
19176
        SSL_SUCCESS) {
19177
      loaded = true;
19178
      break;
19179
    }
19180
  }
19181
19182
  if (!loaded) {
19183
    for (auto dir = impl::system_ca_dirs(); *dir; ++dir) {
19184
      if (wolfSSL_CTX_load_verify_locations(wctx->ctx, nullptr, *dir) ==
19185
          SSL_SUCCESS) {
19186
        loaded = true;
19187
        break;
19188
      }
19189
    }
19190
  }
19191
#endif
19192
19193
  return loaded;
19194
}
19195
19196
inline bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key,
19197
                                const char *password) {
19198
  if (!ctx || !cert || !key) { return false; }
19199
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19200
19201
  // Load certificate
19202
  int ret = wolfSSL_CTX_use_certificate_buffer(
19203
      wctx->ctx, reinterpret_cast<const unsigned char *>(cert),
19204
      static_cast<long>(strlen(cert)), SSL_FILETYPE_PEM);
19205
  if (ret != SSL_SUCCESS) {
19206
    impl::wolfssl_last_error() =
19207
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19208
    return false;
19209
  }
19210
19211
  // Set password callback if password is provided
19212
  if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19213
19214
  // Load private key
19215
  ret = wolfSSL_CTX_use_PrivateKey_buffer(
19216
      wctx->ctx, reinterpret_cast<const unsigned char *>(key),
19217
      static_cast<long>(strlen(key)), SSL_FILETYPE_PEM);
19218
  if (ret != SSL_SUCCESS) {
19219
    impl::wolfssl_last_error() =
19220
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19221
    return false;
19222
  }
19223
19224
  // Verify that the certificate and private key match
19225
  return wolfSSL_CTX_check_private_key(wctx->ctx) == SSL_SUCCESS;
19226
}
19227
19228
inline bool set_client_cert_file(ctx_t ctx, const char *cert_path,
19229
                                 const char *key_path, const char *password) {
19230
  if (!ctx || !cert_path || !key_path) { return false; }
19231
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19232
19233
  // Load certificate file
19234
  int ret =
19235
      wolfSSL_CTX_use_certificate_file(wctx->ctx, cert_path, SSL_FILETYPE_PEM);
19236
  if (ret != SSL_SUCCESS) {
19237
    impl::wolfssl_last_error() =
19238
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19239
    return false;
19240
  }
19241
19242
  // Set password callback if password is provided
19243
  if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19244
19245
  // Load private key file
19246
  ret = wolfSSL_CTX_use_PrivateKey_file(wctx->ctx, key_path, SSL_FILETYPE_PEM);
19247
  if (ret != SSL_SUCCESS) {
19248
    impl::wolfssl_last_error() =
19249
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19250
    return false;
19251
  }
19252
19253
  // Verify that the certificate and private key match
19254
  return wolfSSL_CTX_check_private_key(wctx->ctx) == SSL_SUCCESS;
19255
}
19256
19257
inline void set_verify_client(ctx_t ctx, bool require) {
19258
  if (!ctx) { return; }
19259
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19260
  wctx->verify_client = require;
19261
  if (require) {
19262
    wolfSSL_CTX_set_verify(
19263
        wctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
19264
        wctx->has_verify_callback ? impl::wolfssl_verify_callback : nullptr);
19265
  } else {
19266
    if (wctx->has_verify_callback) {
19267
      wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_PEER,
19268
                             impl::wolfssl_verify_callback);
19269
    } else {
19270
      wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_NONE, nullptr);
19271
    }
19272
  }
19273
}
19274
19275
inline session_t create_session(ctx_t ctx, socket_t sock) {
19276
  if (!ctx || sock == INVALID_SOCKET) { return nullptr; }
19277
  auto wctx = static_cast<impl::WolfSSLContext *>(ctx);
19278
19279
  auto session = new (std::nothrow) impl::WolfSSLSession();
19280
  if (!session) { return nullptr; }
19281
19282
  session->sock = sock;
19283
  session->ssl = wolfSSL_new(wctx->ctx);
19284
  if (!session->ssl) {
19285
    impl::wolfssl_last_error() =
19286
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19287
    delete session;
19288
    return nullptr;
19289
  }
19290
19291
  wolfSSL_set_fd(session->ssl, static_cast<int>(sock));
19292
19293
  return static_cast<session_t>(session);
19294
}
19295
19296
inline void free_session(session_t session) {
19297
  if (session) { delete static_cast<impl::WolfSSLSession *>(session); }
19298
}
19299
19300
inline bool set_sni(session_t session, const char *hostname) {
19301
  if (!session || !hostname) { return false; }
19302
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19303
19304
  int ret = wolfSSL_UseSNI(wsession->ssl, WOLFSSL_SNI_HOST_NAME, hostname,
19305
                           static_cast<word16>(strlen(hostname)));
19306
  if (ret != WOLFSSL_SUCCESS) {
19307
    impl::wolfssl_last_error() =
19308
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19309
    return false;
19310
  }
19311
19312
  // Also set hostname for verification
19313
  wolfSSL_check_domain_name(wsession->ssl, hostname);
19314
19315
  wsession->hostname = hostname;
19316
  return true;
19317
}
19318
19319
inline bool set_hostname(session_t session, const char *hostname) {
19320
  // In wolfSSL, set_hostname also sets up hostname verification
19321
  return set_sni(session, hostname);
19322
}
19323
19324
inline TlsError connect(session_t session) {
19325
  TlsError err;
19326
  if (!session) {
19327
    err.code = ErrorCode::Fatal;
19328
    return err;
19329
  }
19330
19331
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19332
  int ret = wolfSSL_connect(wsession->ssl);
19333
19334
  if (ret == SSL_SUCCESS) {
19335
    err.code = ErrorCode::Success;
19336
  } else {
19337
    int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19338
    err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19339
    err.backend_code = static_cast<uint64_t>(ssl_error);
19340
    impl::wolfssl_last_error() = err.backend_code;
19341
  }
19342
19343
  return err;
19344
}
19345
19346
inline TlsError accept(session_t session) {
19347
  TlsError err;
19348
  if (!session) {
19349
    err.code = ErrorCode::Fatal;
19350
    return err;
19351
  }
19352
19353
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19354
  int ret = wolfSSL_accept(wsession->ssl);
19355
19356
  if (ret == SSL_SUCCESS) {
19357
    err.code = ErrorCode::Success;
19358
    // Capture SNI from thread-local storage after successful handshake
19359
    wsession->sni_hostname = std::move(impl::wolfssl_pending_sni());
19360
    impl::wolfssl_pending_sni().clear();
19361
  } else {
19362
    int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19363
    err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19364
    err.backend_code = static_cast<uint64_t>(ssl_error);
19365
    impl::wolfssl_last_error() = err.backend_code;
19366
  }
19367
19368
  return err;
19369
}
19370
19371
inline bool connect_nonblocking(session_t session, socket_t sock,
19372
                                time_t timeout_sec, time_t timeout_usec,
19373
                                TlsError *err) {
19374
  if (!session) {
19375
    if (err) { err->code = ErrorCode::Fatal; }
19376
    return false;
19377
  }
19378
19379
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19380
19381
  // Set socket to non-blocking mode
19382
  detail::set_nonblocking(sock, true);
19383
  auto cleanup =
19384
      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
19385
19386
  int ret;
19387
  while ((ret = wolfSSL_connect(wsession->ssl)) != SSL_SUCCESS) {
19388
    int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19389
    if (ssl_error == SSL_ERROR_WANT_READ) {
19390
      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
19391
        continue;
19392
      }
19393
    } else if (ssl_error == SSL_ERROR_WANT_WRITE) {
19394
      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
19395
        continue;
19396
      }
19397
    }
19398
19399
    // Error or timeout
19400
    if (err) {
19401
      err->code =
19402
          impl::map_wolfssl_error(wsession->ssl, ssl_error, err->sys_errno);
19403
      err->backend_code = static_cast<uint64_t>(ssl_error);
19404
    }
19405
    impl::wolfssl_last_error() = static_cast<uint64_t>(ssl_error);
19406
    return false;
19407
  }
19408
19409
  if (err) { err->code = ErrorCode::Success; }
19410
  return true;
19411
}
19412
19413
inline bool accept_nonblocking(session_t session, socket_t sock,
19414
                               time_t timeout_sec, time_t timeout_usec,
19415
                               TlsError *err) {
19416
  if (!session) {
19417
    if (err) { err->code = ErrorCode::Fatal; }
19418
    return false;
19419
  }
19420
19421
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19422
19423
  // Set socket to non-blocking mode
19424
  detail::set_nonblocking(sock, true);
19425
  auto cleanup =
19426
      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
19427
19428
  int ret;
19429
  while ((ret = wolfSSL_accept(wsession->ssl)) != SSL_SUCCESS) {
19430
    int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19431
    if (ssl_error == SSL_ERROR_WANT_READ) {
19432
      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
19433
        continue;
19434
      }
19435
    } else if (ssl_error == SSL_ERROR_WANT_WRITE) {
19436
      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
19437
        continue;
19438
      }
19439
    }
19440
19441
    // Error or timeout
19442
    if (err) {
19443
      err->code =
19444
          impl::map_wolfssl_error(wsession->ssl, ssl_error, err->sys_errno);
19445
      err->backend_code = static_cast<uint64_t>(ssl_error);
19446
    }
19447
    impl::wolfssl_last_error() = static_cast<uint64_t>(ssl_error);
19448
    return false;
19449
  }
19450
19451
  if (err) { err->code = ErrorCode::Success; }
19452
19453
  // Capture SNI from thread-local storage after successful handshake
19454
  wsession->sni_hostname = std::move(impl::wolfssl_pending_sni());
19455
  impl::wolfssl_pending_sni().clear();
19456
19457
  return true;
19458
}
19459
19460
inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) {
19461
  if (!session || !buf) {
19462
    err.code = ErrorCode::Fatal;
19463
    return -1;
19464
  }
19465
19466
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19467
  int ret = wolfSSL_read(wsession->ssl, buf, static_cast<int>(len));
19468
19469
  if (ret > 0) {
19470
    err.code = ErrorCode::Success;
19471
    return static_cast<ssize_t>(ret);
19472
  }
19473
19474
  if (ret == 0) {
19475
    err.code = ErrorCode::PeerClosed;
19476
    return 0;
19477
  }
19478
19479
  int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19480
  err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19481
  err.backend_code = static_cast<uint64_t>(ssl_error);
19482
  impl::wolfssl_last_error() = err.backend_code;
19483
  return -1;
19484
}
19485
19486
inline ssize_t write(session_t session, const void *buf, size_t len,
19487
                     TlsError &err) {
19488
  if (!session || !buf) {
19489
    err.code = ErrorCode::Fatal;
19490
    return -1;
19491
  }
19492
19493
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19494
  int ret = wolfSSL_write(wsession->ssl, buf, static_cast<int>(len));
19495
19496
  if (ret > 0) {
19497
    err.code = ErrorCode::Success;
19498
    return static_cast<ssize_t>(ret);
19499
  }
19500
19501
  // wolfSSL_write returns 0 when the peer has sent a close_notify.
19502
  // Treat this as an error (return -1) so callers don't spin in a
19503
  // write loop adding zero to the offset.
19504
  if (ret == 0) {
19505
    err.code = ErrorCode::PeerClosed;
19506
    return -1;
19507
  }
19508
19509
  int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19510
  err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
19511
  err.backend_code = static_cast<uint64_t>(ssl_error);
19512
  impl::wolfssl_last_error() = err.backend_code;
19513
  return -1;
19514
}
19515
19516
inline int pending(const_session_t session) {
19517
  if (!session) { return 0; }
19518
  auto wsession =
19519
      static_cast<impl::WolfSSLSession *>(const_cast<void *>(session));
19520
  return wolfSSL_pending(wsession->ssl);
19521
}
19522
19523
inline void shutdown(session_t session, bool graceful) {
19524
  if (!session) { return; }
19525
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19526
19527
  if (graceful) {
19528
    int ret;
19529
    int attempts = 0;
19530
    while ((ret = wolfSSL_shutdown(wsession->ssl)) != SSL_SUCCESS &&
19531
           attempts < 3) {
19532
      int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19533
      if (ssl_error != SSL_ERROR_WANT_READ &&
19534
          ssl_error != SSL_ERROR_WANT_WRITE) {
19535
        break;
19536
      }
19537
      attempts++;
19538
    }
19539
  } else {
19540
    wolfSSL_shutdown(wsession->ssl);
19541
  }
19542
}
19543
19544
inline bool is_peer_closed(session_t session, socket_t sock) {
19545
  if (!session || sock == INVALID_SOCKET) { return true; }
19546
  auto wsession = static_cast<impl::WolfSSLSession *>(session);
19547
19548
  // Check if there's already decrypted data available
19549
  if (wolfSSL_pending(wsession->ssl) > 0) { return false; }
19550
19551
  // Set socket to non-blocking to avoid blocking on read
19552
  detail::set_nonblocking(sock, true);
19553
  auto cleanup =
19554
      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
19555
19556
  // Peek 1 byte to check connection status without consuming data
19557
  unsigned char buf;
19558
  int ret = wolfSSL_peek(wsession->ssl, &buf, 1);
19559
19560
  // If we got data or WANT_READ (would block), connection is alive
19561
  if (ret > 0) { return false; }
19562
19563
  int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
19564
  if (ssl_error == SSL_ERROR_WANT_READ) { return false; }
19565
19566
  return ssl_error == SSL_ERROR_ZERO_RETURN || ssl_error == SSL_ERROR_SYSCALL ||
19567
         ret == 0;
19568
}
19569
19570
inline cert_t get_peer_cert(const_session_t session) {
19571
  if (!session) { return nullptr; }
19572
  auto wsession =
19573
      static_cast<impl::WolfSSLSession *>(const_cast<void *>(session));
19574
19575
  WOLFSSL_X509 *cert = wolfSSL_get_peer_certificate(wsession->ssl);
19576
  return static_cast<cert_t>(cert);
19577
}
19578
19579
inline void free_cert(cert_t cert) {
19580
  if (cert) { wolfSSL_X509_free(static_cast<WOLFSSL_X509 *>(cert)); }
19581
}
19582
19583
inline bool verify_hostname(cert_t cert, const char *hostname) {
19584
  if (!cert || !hostname) { return false; }
19585
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19586
  std::string host_str(hostname);
19587
19588
  // Check if hostname is an IP address
19589
  bool is_ip = impl::is_ipv4_address(host_str);
19590
  unsigned char ip_bytes[4];
19591
  if (is_ip) { impl::parse_ipv4(host_str, ip_bytes); }
19592
19593
  // Check Subject Alternative Names
19594
  auto *san_names = static_cast<WOLF_STACK_OF(WOLFSSL_GENERAL_NAME) *>(
19595
      wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name, nullptr, nullptr));
19596
19597
  if (san_names) {
19598
    int san_count = wolfSSL_sk_num(san_names);
19599
    for (int i = 0; i < san_count; i++) {
19600
      auto *names =
19601
          static_cast<WOLFSSL_GENERAL_NAME *>(wolfSSL_sk_value(san_names, i));
19602
      if (!names) continue;
19603
19604
      if (!is_ip && names->type == WOLFSSL_GEN_DNS) {
19605
        // DNS name
19606
        unsigned char *dns_name = nullptr;
19607
        int dns_len = wolfSSL_ASN1_STRING_to_UTF8(&dns_name, names->d.dNSName);
19608
        if (dns_name && dns_len > 0) {
19609
          std::string san_name(reinterpret_cast<char *>(dns_name),
19610
                               static_cast<size_t>(dns_len));
19611
          XFREE(dns_name, nullptr, DYNAMIC_TYPE_OPENSSL);
19612
          if (detail::match_hostname(san_name, host_str)) {
19613
            wolfSSL_sk_free(san_names);
19614
            return true;
19615
          }
19616
        }
19617
      } else if (is_ip && names->type == WOLFSSL_GEN_IPADD) {
19618
        // IP address
19619
        unsigned char *ip_data = wolfSSL_ASN1_STRING_data(names->d.iPAddress);
19620
        int ip_len = wolfSSL_ASN1_STRING_length(names->d.iPAddress);
19621
        if (ip_data && ip_len == 4 && memcmp(ip_data, ip_bytes, 4) == 0) {
19622
          wolfSSL_sk_free(san_names);
19623
          return true;
19624
        }
19625
      }
19626
    }
19627
    wolfSSL_sk_free(san_names);
19628
  }
19629
19630
  // Fallback: Check Common Name (CN) in subject
19631
  WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19632
  if (subject) {
19633
    char cn[256] = {};
19634
    int cn_len = wolfSSL_X509_NAME_get_text_by_NID(subject, NID_commonName, cn,
19635
                                                   sizeof(cn));
19636
    if (cn_len > 0) {
19637
      std::string cn_str(cn, static_cast<size_t>(cn_len));
19638
      if (detail::match_hostname(cn_str, host_str)) { return true; }
19639
    }
19640
  }
19641
19642
  return false;
19643
}
19644
19645
inline uint64_t hostname_mismatch_code() {
19646
  return static_cast<uint64_t>(DOMAIN_NAME_MISMATCH);
19647
}
19648
19649
inline long get_verify_result(const_session_t session) {
19650
  if (!session) { return -1; }
19651
  auto wsession =
19652
      static_cast<impl::WolfSSLSession *>(const_cast<void *>(session));
19653
  long result = wolfSSL_get_verify_result(wsession->ssl);
19654
  return result;
19655
}
19656
19657
inline std::string get_cert_subject_cn(cert_t cert) {
19658
  if (!cert) return "";
19659
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19660
19661
  WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19662
  if (!subject) return "";
19663
19664
  char cn[256] = {};
19665
  int cn_len = wolfSSL_X509_NAME_get_text_by_NID(subject, NID_commonName, cn,
19666
                                                 sizeof(cn));
19667
  if (cn_len <= 0) return "";
19668
  return std::string(cn, static_cast<size_t>(cn_len));
19669
}
19670
19671
inline std::string get_cert_issuer_name(cert_t cert) {
19672
  if (!cert) return "";
19673
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19674
19675
  WOLFSSL_X509_NAME *issuer = wolfSSL_X509_get_issuer_name(x509);
19676
  if (!issuer) return "";
19677
19678
  char *name_str = wolfSSL_X509_NAME_oneline(issuer, nullptr, 0);
19679
  if (!name_str) return "";
19680
19681
  std::string result(name_str);
19682
  XFREE(name_str, nullptr, DYNAMIC_TYPE_OPENSSL);
19683
  return result;
19684
}
19685
19686
inline bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans) {
19687
  sans.clear();
19688
  if (!cert) return false;
19689
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19690
19691
  auto *san_names = static_cast<WOLF_STACK_OF(WOLFSSL_GENERAL_NAME) *>(
19692
      wolfSSL_X509_get_ext_d2i(x509, NID_subject_alt_name, nullptr, nullptr));
19693
  if (!san_names) return true; // No SANs is not an error
19694
19695
  int count = wolfSSL_sk_num(san_names);
19696
  for (int i = 0; i < count; i++) {
19697
    auto *name =
19698
        static_cast<WOLFSSL_GENERAL_NAME *>(wolfSSL_sk_value(san_names, i));
19699
    if (!name) continue;
19700
19701
    SanEntry entry;
19702
    switch (name->type) {
19703
    case WOLFSSL_GEN_DNS: {
19704
      entry.type = SanType::DNS;
19705
      unsigned char *dns_name = nullptr;
19706
      int dns_len = wolfSSL_ASN1_STRING_to_UTF8(&dns_name, name->d.dNSName);
19707
      if (dns_name && dns_len > 0) {
19708
        entry.value = std::string(reinterpret_cast<char *>(dns_name),
19709
                                  static_cast<size_t>(dns_len));
19710
        XFREE(dns_name, nullptr, DYNAMIC_TYPE_OPENSSL);
19711
      }
19712
      break;
19713
    }
19714
    case WOLFSSL_GEN_IPADD: {
19715
      entry.type = SanType::IP;
19716
      unsigned char *ip_data = wolfSSL_ASN1_STRING_data(name->d.iPAddress);
19717
      int ip_len = wolfSSL_ASN1_STRING_length(name->d.iPAddress);
19718
      if (ip_data && ip_len == 4) {
19719
        char buf[16];
19720
        snprintf(buf, sizeof(buf), "%d.%d.%d.%d", ip_data[0], ip_data[1],
19721
                 ip_data[2], ip_data[3]);
19722
        entry.value = buf;
19723
      } else if (ip_data && ip_len == 16) {
19724
        char buf[64];
19725
        snprintf(buf, sizeof(buf),
19726
                 "%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
19727
                 "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
19728
                 ip_data[0], ip_data[1], ip_data[2], ip_data[3], ip_data[4],
19729
                 ip_data[5], ip_data[6], ip_data[7], ip_data[8], ip_data[9],
19730
                 ip_data[10], ip_data[11], ip_data[12], ip_data[13],
19731
                 ip_data[14], ip_data[15]);
19732
        entry.value = buf;
19733
      }
19734
      break;
19735
    }
19736
    case WOLFSSL_GEN_EMAIL:
19737
      entry.type = SanType::EMAIL;
19738
      {
19739
        unsigned char *email = nullptr;
19740
        int email_len = wolfSSL_ASN1_STRING_to_UTF8(&email, name->d.rfc822Name);
19741
        if (email && email_len > 0) {
19742
          entry.value = std::string(reinterpret_cast<char *>(email),
19743
                                    static_cast<size_t>(email_len));
19744
          XFREE(email, nullptr, DYNAMIC_TYPE_OPENSSL);
19745
        }
19746
      }
19747
      break;
19748
    case WOLFSSL_GEN_URI:
19749
      entry.type = SanType::URI;
19750
      {
19751
        unsigned char *uri = nullptr;
19752
        int uri_len = wolfSSL_ASN1_STRING_to_UTF8(
19753
            &uri, name->d.uniformResourceIdentifier);
19754
        if (uri && uri_len > 0) {
19755
          entry.value = std::string(reinterpret_cast<char *>(uri),
19756
                                    static_cast<size_t>(uri_len));
19757
          XFREE(uri, nullptr, DYNAMIC_TYPE_OPENSSL);
19758
        }
19759
      }
19760
      break;
19761
    default: entry.type = SanType::OTHER; break;
19762
    }
19763
19764
    if (!entry.value.empty()) { sans.push_back(std::move(entry)); }
19765
  }
19766
  wolfSSL_sk_free(san_names);
19767
  return true;
19768
}
19769
19770
inline bool get_cert_validity(cert_t cert, time_t &not_before,
19771
                              time_t &not_after) {
19772
  if (!cert) return false;
19773
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19774
19775
  const WOLFSSL_ASN1_TIME *nb = wolfSSL_X509_get_notBefore(x509);
19776
  const WOLFSSL_ASN1_TIME *na = wolfSSL_X509_get_notAfter(x509);
19777
19778
  if (!nb || !na) return false;
19779
19780
  // wolfSSL_ASN1_TIME_to_tm is available
19781
  struct tm tm_nb = {}, tm_na = {};
19782
  if (wolfSSL_ASN1_TIME_to_tm(nb, &tm_nb) != WOLFSSL_SUCCESS) return false;
19783
  if (wolfSSL_ASN1_TIME_to_tm(na, &tm_na) != WOLFSSL_SUCCESS) return false;
19784
19785
#ifdef _WIN32
19786
  not_before = _mkgmtime(&tm_nb);
19787
  not_after = _mkgmtime(&tm_na);
19788
#else
19789
  not_before = timegm(&tm_nb);
19790
  not_after = timegm(&tm_na);
19791
#endif
19792
  return true;
19793
}
19794
19795
inline std::string get_cert_serial(cert_t cert) {
19796
  if (!cert) return "";
19797
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19798
19799
  WOLFSSL_ASN1_INTEGER *serial_asn1 = wolfSSL_X509_get_serialNumber(x509);
19800
  if (!serial_asn1) return "";
19801
19802
  // Get the serial number data
19803
  int len = serial_asn1->length;
19804
  unsigned char *data = serial_asn1->data;
19805
  if (!data || len <= 0) return "";
19806
19807
  std::string result;
19808
  result.reserve(static_cast<size_t>(len) * 2);
19809
  for (int i = 0; i < len; i++) {
19810
    char hex[3];
19811
    snprintf(hex, sizeof(hex), "%02X", data[i]);
19812
    result += hex;
19813
  }
19814
  return result;
19815
}
19816
19817
inline bool get_cert_der(cert_t cert, std::vector<unsigned char> &der) {
19818
  if (!cert) return false;
19819
  auto x509 = static_cast<WOLFSSL_X509 *>(cert);
19820
19821
  int der_len = 0;
19822
  const unsigned char *der_data = wolfSSL_X509_get_der(x509, &der_len);
19823
  if (!der_data || der_len <= 0) return false;
19824
19825
  der.assign(der_data, der_data + der_len);
19826
  return true;
19827
}
19828
19829
inline const char *get_sni(const_session_t session) {
19830
  if (!session) return nullptr;
19831
  auto wsession = static_cast<const impl::WolfSSLSession *>(session);
19832
19833
  // For server: return SNI received from client during handshake
19834
  if (!wsession->sni_hostname.empty()) {
19835
    return wsession->sni_hostname.c_str();
19836
  }
19837
19838
  // For client: return the hostname set via set_sni
19839
  if (!wsession->hostname.empty()) { return wsession->hostname.c_str(); }
19840
19841
  return nullptr;
19842
}
19843
19844
inline uint64_t peek_error() {
19845
  return static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19846
}
19847
19848
inline uint64_t get_error() {
19849
  uint64_t err = impl::wolfssl_last_error();
19850
  impl::wolfssl_last_error() = 0;
19851
  return err;
19852
}
19853
19854
inline std::string error_string(uint64_t code) {
19855
  char buf[256];
19856
  wolfSSL_ERR_error_string(static_cast<unsigned long>(code), buf);
19857
  return std::string(buf);
19858
}
19859
19860
inline ca_store_t create_ca_store(const char *pem, size_t len) {
19861
  if (!pem || len == 0) { return nullptr; }
19862
  // Validate by attempting to load into a temporary ctx
19863
  WOLFSSL_CTX *tmp_ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
19864
  if (!tmp_ctx) { return nullptr; }
19865
  int ret = wolfSSL_CTX_load_verify_buffer(
19866
      tmp_ctx, reinterpret_cast<const unsigned char *>(pem),
19867
      static_cast<long>(len), SSL_FILETYPE_PEM);
19868
  wolfSSL_CTX_free(tmp_ctx);
19869
  if (ret != SSL_SUCCESS) { return nullptr; }
19870
  return static_cast<ca_store_t>(
19871
      new impl::WolfSSLCAStore{std::string(pem, len)});
19872
}
19873
19874
inline void free_ca_store(ca_store_t store) {
19875
  delete static_cast<impl::WolfSSLCAStore *>(store);
19876
}
19877
19878
inline bool set_ca_store(ctx_t ctx, ca_store_t store) {
19879
  if (!ctx || !store) { return false; }
19880
  auto *wctx = static_cast<impl::WolfSSLContext *>(ctx);
19881
  auto *ca = static_cast<impl::WolfSSLCAStore *>(store);
19882
  int ret = wolfSSL_CTX_load_verify_buffer(
19883
      wctx->ctx, reinterpret_cast<const unsigned char *>(ca->pem_data.data()),
19884
      static_cast<long>(ca->pem_data.size()), SSL_FILETYPE_PEM);
19885
  if (ret == SSL_SUCCESS) { wctx->ca_pem_data_ += ca->pem_data; }
19886
  return ret == SSL_SUCCESS;
19887
}
19888
19889
inline size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs) {
19890
  certs.clear();
19891
  if (!ctx) { return 0; }
19892
  auto *wctx = static_cast<impl::WolfSSLContext *>(ctx);
19893
  if (wctx->ca_pem_data_.empty()) { return 0; }
19894
19895
  const std::string &pem = wctx->ca_pem_data_;
19896
  const std::string begin_marker = "-----BEGIN CERTIFICATE-----";
19897
  const std::string end_marker = "-----END CERTIFICATE-----";
19898
  size_t pos = 0;
19899
  while ((pos = pem.find(begin_marker, pos)) != std::string::npos) {
19900
    size_t end_pos = pem.find(end_marker, pos);
19901
    if (end_pos == std::string::npos) { break; }
19902
    end_pos += end_marker.size();
19903
    std::string cert_pem = pem.substr(pos, end_pos - pos);
19904
    WOLFSSL_X509 *x509 = wolfSSL_X509_load_certificate_buffer(
19905
        reinterpret_cast<const unsigned char *>(cert_pem.data()),
19906
        static_cast<int>(cert_pem.size()), WOLFSSL_FILETYPE_PEM);
19907
    if (x509) { certs.push_back(static_cast<cert_t>(x509)); }
19908
    pos = end_pos;
19909
  }
19910
  return certs.size();
19911
}
19912
19913
inline std::vector<std::string> get_ca_names(ctx_t ctx) {
19914
  std::vector<std::string> names;
19915
  if (!ctx) { return names; }
19916
  auto *wctx = static_cast<impl::WolfSSLContext *>(ctx);
19917
  if (wctx->ca_pem_data_.empty()) { return names; }
19918
19919
  const std::string &pem = wctx->ca_pem_data_;
19920
  const std::string begin_marker = "-----BEGIN CERTIFICATE-----";
19921
  const std::string end_marker = "-----END CERTIFICATE-----";
19922
  size_t pos = 0;
19923
  while ((pos = pem.find(begin_marker, pos)) != std::string::npos) {
19924
    size_t end_pos = pem.find(end_marker, pos);
19925
    if (end_pos == std::string::npos) { break; }
19926
    end_pos += end_marker.size();
19927
    std::string cert_pem = pem.substr(pos, end_pos - pos);
19928
    WOLFSSL_X509 *x509 = wolfSSL_X509_load_certificate_buffer(
19929
        reinterpret_cast<const unsigned char *>(cert_pem.data()),
19930
        static_cast<int>(cert_pem.size()), WOLFSSL_FILETYPE_PEM);
19931
    if (x509) {
19932
      WOLFSSL_X509_NAME *subject = wolfSSL_X509_get_subject_name(x509);
19933
      if (subject) {
19934
        char *name_str = wolfSSL_X509_NAME_oneline(subject, nullptr, 0);
19935
        if (name_str) {
19936
          names.push_back(name_str);
19937
          XFREE(name_str, nullptr, DYNAMIC_TYPE_OPENSSL);
19938
        }
19939
      }
19940
      wolfSSL_X509_free(x509);
19941
    }
19942
    pos = end_pos;
19943
  }
19944
  return names;
19945
}
19946
19947
inline bool update_server_cert(ctx_t ctx, const char *cert_pem,
19948
                               const char *key_pem, const char *password) {
19949
  if (!ctx || !cert_pem || !key_pem) { return false; }
19950
  auto *wctx = static_cast<impl::WolfSSLContext *>(ctx);
19951
19952
  // Load new certificate
19953
  int ret = wolfSSL_CTX_use_certificate_buffer(
19954
      wctx->ctx, reinterpret_cast<const unsigned char *>(cert_pem),
19955
      static_cast<long>(strlen(cert_pem)), SSL_FILETYPE_PEM);
19956
  if (ret != SSL_SUCCESS) {
19957
    impl::wolfssl_last_error() =
19958
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19959
    return false;
19960
  }
19961
19962
  // Set password if provided
19963
  if (password) { impl::set_wolfssl_password_cb(wctx->ctx, password); }
19964
19965
  // Load new private key
19966
  ret = wolfSSL_CTX_use_PrivateKey_buffer(
19967
      wctx->ctx, reinterpret_cast<const unsigned char *>(key_pem),
19968
      static_cast<long>(strlen(key_pem)), SSL_FILETYPE_PEM);
19969
  if (ret != SSL_SUCCESS) {
19970
    impl::wolfssl_last_error() =
19971
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19972
    return false;
19973
  }
19974
19975
  return true;
19976
}
19977
19978
inline bool update_server_client_ca(ctx_t ctx, const char *ca_pem) {
19979
  if (!ctx || !ca_pem) { return false; }
19980
  auto *wctx = static_cast<impl::WolfSSLContext *>(ctx);
19981
19982
  int ret = wolfSSL_CTX_load_verify_buffer(
19983
      wctx->ctx, reinterpret_cast<const unsigned char *>(ca_pem),
19984
      static_cast<long>(strlen(ca_pem)), SSL_FILETYPE_PEM);
19985
  if (ret != SSL_SUCCESS) {
19986
    impl::wolfssl_last_error() =
19987
        static_cast<uint64_t>(wolfSSL_ERR_peek_last_error());
19988
    return false;
19989
  }
19990
  return true;
19991
}
19992
19993
inline bool set_verify_callback(ctx_t ctx, VerifyCallback callback) {
19994
  if (!ctx) { return false; }
19995
  auto *wctx = static_cast<impl::WolfSSLContext *>(ctx);
19996
19997
  impl::get_verify_callback() = std::move(callback);
19998
  wctx->has_verify_callback = static_cast<bool>(impl::get_verify_callback());
19999
20000
  if (wctx->has_verify_callback) {
20001
    wolfSSL_CTX_set_verify(wctx->ctx, SSL_VERIFY_PEER,
20002
                           impl::wolfssl_verify_callback);
20003
  } else {
20004
    wolfSSL_CTX_set_verify(
20005
        wctx->ctx,
20006
        wctx->verify_client
20007
            ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
20008
            : SSL_VERIFY_NONE,
20009
        nullptr);
20010
  }
20011
  return true;
20012
}
20013
20014
inline long get_verify_error(const_session_t session) {
20015
  if (!session) { return -1; }
20016
  auto *wsession =
20017
      static_cast<impl::WolfSSLSession *>(const_cast<void *>(session));
20018
  return wolfSSL_get_verify_result(wsession->ssl);
20019
}
20020
20021
inline std::string verify_error_string(long error_code) {
20022
  if (error_code == 0) { return ""; }
20023
  const char *str =
20024
      wolfSSL_X509_verify_cert_error_string(static_cast<int>(error_code));
20025
  return str ? std::string(str) : std::string();
20026
}
20027
20028
} // namespace tls
20029
20030
#endif // CPPHTTPLIB_WOLFSSL_SUPPORT
20031
20032
// WebSocket implementation
20033
namespace ws {
20034
20035
inline bool WebSocket::send_frame(Opcode op, const char *data, size_t len,
20036
0
                                  bool fin) {
20037
0
  std::lock_guard<std::mutex> lock(write_mutex_);
20038
0
  if (closed_) { return false; }
20039
0
  return detail::write_websocket_frame(strm_, op, data, len, fin, !is_server_);
20040
0
}
20041
20042
0
inline ReadResult WebSocket::read(std::string &msg) {
20043
0
  while (!closed_) {
20044
0
    Opcode opcode;
20045
0
    std::string payload;
20046
0
    bool fin;
20047
0
20048
0
    if (!impl::read_websocket_frame(strm_, opcode, payload, fin, is_server_,
20049
0
                                    CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH)) {
20050
0
      closed_ = true;
20051
0
      return Fail;
20052
0
    }
20053
0
20054
0
    switch (opcode) {
20055
0
    case Opcode::Ping: {
20056
0
      std::lock_guard<std::mutex> lock(write_mutex_);
20057
0
      detail::write_websocket_frame(strm_, Opcode::Pong, payload.data(),
20058
0
                                    payload.size(), true, !is_server_);
20059
0
      continue;
20060
0
    }
20061
0
    case Opcode::Pong: {
20062
0
      std::lock_guard<std::mutex> lock(ping_mutex_);
20063
0
      unacked_pings_ = 0;
20064
0
      continue;
20065
0
    }
20066
0
    case Opcode::Close: {
20067
0
      if (!closed_.exchange(true)) {
20068
0
        // Echo close frame back
20069
0
        std::lock_guard<std::mutex> lock(write_mutex_);
20070
0
        detail::write_websocket_frame(strm_, Opcode::Close, payload.data(),
20071
0
                                      payload.size(), true, !is_server_);
20072
0
      }
20073
0
      return Fail;
20074
0
    }
20075
0
    case Opcode::Text:
20076
0
    case Opcode::Binary: {
20077
0
      auto result = opcode == Opcode::Text ? Text : Binary;
20078
0
      msg = std::move(payload);
20079
0
20080
0
      // Handle fragmentation
20081
0
      if (!fin) {
20082
0
        while (true) {
20083
0
          Opcode cont_opcode;
20084
0
          std::string cont_payload;
20085
0
          bool cont_fin;
20086
0
          if (!impl::read_websocket_frame(
20087
0
                  strm_, cont_opcode, cont_payload, cont_fin, is_server_,
20088
0
                  CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH)) {
20089
0
            closed_ = true;
20090
0
            return Fail;
20091
0
          }
20092
0
          if (cont_opcode == Opcode::Ping) {
20093
0
            std::lock_guard<std::mutex> lock(write_mutex_);
20094
0
            detail::write_websocket_frame(
20095
0
                strm_, Opcode::Pong, cont_payload.data(), cont_payload.size(),
20096
0
                true, !is_server_);
20097
0
            continue;
20098
0
          }
20099
0
          if (cont_opcode == Opcode::Pong) {
20100
0
            std::lock_guard<std::mutex> lock(ping_mutex_);
20101
0
            unacked_pings_ = 0;
20102
0
            continue;
20103
0
          }
20104
0
          if (cont_opcode == Opcode::Close) {
20105
0
            if (!closed_.exchange(true)) {
20106
0
              std::lock_guard<std::mutex> lock(write_mutex_);
20107
0
              detail::write_websocket_frame(
20108
0
                  strm_, Opcode::Close, cont_payload.data(),
20109
0
                  cont_payload.size(), true, !is_server_);
20110
0
            }
20111
0
            return Fail;
20112
0
          }
20113
0
          // RFC 6455: continuation frames must use opcode 0x0
20114
0
          if (cont_opcode != Opcode::Continuation) {
20115
0
            closed_ = true;
20116
0
            return Fail;
20117
0
          }
20118
0
          msg += cont_payload;
20119
0
          if (msg.size() > CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH) {
20120
0
            closed_ = true;
20121
0
            return Fail;
20122
0
          }
20123
0
          if (cont_fin) { break; }
20124
0
        }
20125
0
      }
20126
0
      // RFC 6455 Section 5.6: text frames must contain valid UTF-8
20127
0
      if (result == Text && !impl::is_valid_utf8(msg)) {
20128
0
        close(CloseStatus::InvalidPayload, "invalid UTF-8");
20129
0
        return Fail;
20130
0
      }
20131
0
      return result;
20132
0
    }
20133
0
    default: closed_ = true; return Fail;
20134
0
    }
20135
0
  }
20136
0
  return Fail;
20137
0
}
20138
20139
0
inline bool WebSocket::send(const std::string &data) {
20140
0
  return send_frame(Opcode::Text, data.data(), data.size());
20141
0
}
20142
20143
0
inline bool WebSocket::send(const char *data, size_t len) {
20144
0
  return send_frame(Opcode::Binary, data, len);
20145
0
}
20146
20147
0
inline void WebSocket::close(CloseStatus status, const std::string &reason) {
20148
0
  if (closed_.exchange(true)) { return; }
20149
0
  ping_cv_.notify_all();
20150
0
  std::string payload;
20151
0
  auto code = static_cast<uint16_t>(status);
20152
0
  payload.push_back(static_cast<char>((code >> 8) & 0xFF));
20153
0
  payload.push_back(static_cast<char>(code & 0xFF));
20154
  // RFC 6455 Section 5.5: control frame payload must not exceed 125 bytes
20155
  // Close frame has 2-byte status code, so reason is limited to 123 bytes
20156
0
  payload += reason.substr(0, 123);
20157
0
  {
20158
0
    std::lock_guard<std::mutex> lock(write_mutex_);
20159
0
    detail::write_websocket_frame(strm_, Opcode::Close, payload.data(),
20160
0
                                  payload.size(), true, !is_server_);
20161
0
  }
20162
20163
  // RFC 6455 Section 7.1.1: after sending a Close frame, wait for the peer's
20164
  // Close response before closing the TCP connection. Use a short timeout to
20165
  // avoid hanging if the peer doesn't respond.
20166
0
  strm_.set_read_timeout(CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND, 0);
20167
0
  Opcode op;
20168
0
  std::string resp;
20169
0
  bool fin;
20170
0
  while (impl::read_websocket_frame(strm_, op, resp, fin, is_server_, 125)) {
20171
0
    if (op == Opcode::Close) { break; }
20172
0
  }
20173
0
}
20174
20175
0
inline WebSocket::~WebSocket() {
20176
0
  {
20177
0
    std::lock_guard<std::mutex> lock(ping_mutex_);
20178
0
    closed_ = true;
20179
0
  }
20180
0
  ping_cv_.notify_all();
20181
0
  if (ping_thread_.joinable()) { ping_thread_.join(); }
20182
0
}
20183
20184
0
inline void WebSocket::start_heartbeat() {
20185
0
  if (ping_interval_sec_ == 0) { return; }
20186
0
  ping_thread_ = std::thread([this]() {
20187
0
    std::unique_lock<std::mutex> lock(ping_mutex_);
20188
0
    while (!closed_) {
20189
0
      ping_cv_.wait_for(lock, std::chrono::seconds(ping_interval_sec_));
20190
0
      if (closed_) { break; }
20191
      // If the peer has failed to respond to the previous pings, give up.
20192
      // RFC 6455 does not define a pong-timeout mechanism; this is an
20193
      // opt-in liveness check controlled by max_missed_pongs_.
20194
0
      if (max_missed_pongs_ > 0 && unacked_pings_ >= max_missed_pongs_) {
20195
0
        lock.unlock();
20196
0
        close(CloseStatus::GoingAway, "pong timeout");
20197
0
        return;
20198
0
      }
20199
0
      lock.unlock();
20200
0
      if (!send_frame(Opcode::Ping, nullptr, 0)) {
20201
0
        lock.lock();
20202
0
        closed_ = true;
20203
0
        break;
20204
0
      }
20205
0
      lock.lock();
20206
0
      unacked_pings_++;
20207
0
    }
20208
0
  });
20209
0
}
20210
20211
0
inline const Request &WebSocket::request() const { return req_; }
20212
20213
0
inline bool WebSocket::is_open() const { return !closed_; }
20214
20215
// WebSocketClient implementation
20216
inline WebSocketClient::WebSocketClient(
20217
    const std::string &scheme_host_port_path, const Headers &headers)
20218
    : headers_(headers) {
20219
  detail::UrlComponents uc;
20220
  if (detail::parse_url(scheme_host_port_path, uc) && !uc.scheme.empty() &&
20221
      !uc.host.empty() && !uc.path.empty()) {
20222
    auto &scheme = uc.scheme;
20223
20224
#ifdef CPPHTTPLIB_SSL_ENABLED
20225
    if (scheme != "ws" && scheme != "wss") {
20226
#else
20227
    if (scheme != "ws") {
20228
#endif
20229
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
20230
      std::string msg = "'" + scheme + "' scheme is not supported.";
20231
      throw std::invalid_argument(msg);
20232
#endif
20233
      return;
20234
    }
20235
20236
    auto is_ssl = scheme == "wss";
20237
20238
    host_ = std::move(uc.host);
20239
20240
    port_ = is_ssl ? 443 : 80;
20241
    if (!uc.port.empty() && !detail::parse_port(uc.port, port_)) { return; }
20242
20243
    path_ = std::move(uc.path);
20244
20245
#ifdef CPPHTTPLIB_SSL_ENABLED
20246
    is_ssl_ = is_ssl;
20247
#else
20248
    if (is_ssl) { return; }
20249
#endif
20250
20251
    is_valid_ = true;
20252
  }
20253
}
20254
20255
inline WebSocketClient::~WebSocketClient() { shutdown_and_close(); }
20256
20257
0
inline bool WebSocketClient::is_valid() const { return is_valid_; }
20258
20259
0
inline void WebSocketClient::shutdown_and_close() {
20260
0
#ifdef CPPHTTPLIB_SSL_ENABLED
20261
0
  if (is_ssl_) {
20262
0
    if (tls_session_) {
20263
0
      tls::shutdown(tls_session_, true);
20264
0
      tls::free_session(tls_session_);
20265
0
      tls_session_ = nullptr;
20266
0
    }
20267
0
    if (tls_ctx_) {
20268
0
      tls::free_context(tls_ctx_);
20269
0
      tls_ctx_ = nullptr;
20270
0
    }
20271
0
  }
20272
0
#endif
20273
0
  if (ws_ && ws_->is_open()) { ws_->close(); }
20274
0
  ws_.reset();
20275
0
  if (sock_ != INVALID_SOCKET) {
20276
0
    detail::shutdown_socket(sock_);
20277
0
    detail::close_socket(sock_);
20278
0
    sock_ = INVALID_SOCKET;
20279
0
  }
20280
0
}
20281
20282
0
inline bool WebSocketClient::create_stream(std::unique_ptr<Stream> &strm) {
20283
0
#ifdef CPPHTTPLIB_SSL_ENABLED
20284
0
  if (is_ssl_) {
20285
0
    if (!detail::setup_client_tls_session(
20286
0
            host_, tls_ctx_, tls_session_, sock_,
20287
0
            server_certificate_verification_, ca_cert_file_path_,
20288
0
            ca_cert_store_, read_timeout_sec_, read_timeout_usec_)) {
20289
0
      return false;
20290
0
    }
20291
0
20292
0
    strm = std::unique_ptr<Stream>(new detail::SSLSocketStream(
20293
0
        sock_, tls_session_, read_timeout_sec_, read_timeout_usec_,
20294
0
        write_timeout_sec_, write_timeout_usec_));
20295
0
    return true;
20296
0
  }
20297
0
#endif
20298
0
  strm = std::unique_ptr<Stream>(
20299
0
      new detail::SocketStream(sock_, read_timeout_sec_, read_timeout_usec_,
20300
0
                               write_timeout_sec_, write_timeout_usec_));
20301
0
  return true;
20302
0
}
20303
20304
0
inline bool WebSocketClient::connect() {
20305
0
  if (!is_valid_) { return false; }
20306
0
  shutdown_and_close();
20307
0
20308
0
  Error error;
20309
0
  sock_ = detail::create_client_socket(
20310
0
      host_, std::string(), port_, address_family_, tcp_nodelay_, ipv6_v6only_,
20311
0
      socket_options_, connection_timeout_sec_, connection_timeout_usec_,
20312
0
      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
20313
0
      write_timeout_usec_, interface_, error);
20314
0
20315
0
  if (sock_ == INVALID_SOCKET) { return false; }
20316
0
20317
0
  std::unique_ptr<Stream> strm;
20318
0
  if (!create_stream(strm)) {
20319
0
    shutdown_and_close();
20320
0
    return false;
20321
0
  }
20322
0
20323
0
  std::string selected_subprotocol;
20324
0
  if (!detail::perform_websocket_handshake(*strm, host_, port_, path_, headers_,
20325
0
                                           selected_subprotocol)) {
20326
0
    shutdown_and_close();
20327
0
    return false;
20328
0
  }
20329
0
  subprotocol_ = std::move(selected_subprotocol);
20330
0
20331
0
  Request req;
20332
0
  req.method = "GET";
20333
0
  req.path = path_;
20334
0
  ws_ = std::unique_ptr<WebSocket>(new WebSocket(std::move(strm), req, false,
20335
0
                                                 websocket_ping_interval_sec_,
20336
0
                                                 websocket_max_missed_pongs_));
20337
0
  return true;
20338
0
}
20339
20340
0
inline ReadResult WebSocketClient::read(std::string &msg) {
20341
0
  if (!ws_) { return Fail; }
20342
0
  return ws_->read(msg);
20343
0
}
20344
20345
0
inline bool WebSocketClient::send(const std::string &data) {
20346
0
  if (!ws_) { return false; }
20347
0
  return ws_->send(data);
20348
0
}
20349
20350
0
inline bool WebSocketClient::send(const char *data, size_t len) {
20351
0
  if (!ws_) { return false; }
20352
0
  return ws_->send(data, len);
20353
0
}
20354
20355
inline void WebSocketClient::close(CloseStatus status,
20356
0
                                   const std::string &reason) {
20357
0
  if (ws_) { ws_->close(status, reason); }
20358
0
}
20359
20360
0
inline bool WebSocketClient::is_open() const { return ws_ && ws_->is_open(); }
20361
20362
0
inline const std::string &WebSocketClient::subprotocol() const {
20363
0
  return subprotocol_;
20364
0
}
20365
20366
0
inline void WebSocketClient::set_read_timeout(time_t sec, time_t usec) {
20367
0
  read_timeout_sec_ = sec;
20368
0
  read_timeout_usec_ = usec;
20369
0
}
20370
20371
0
inline void WebSocketClient::set_write_timeout(time_t sec, time_t usec) {
20372
0
  write_timeout_sec_ = sec;
20373
0
  write_timeout_usec_ = usec;
20374
0
}
20375
20376
0
inline void WebSocketClient::set_websocket_ping_interval(time_t sec) {
20377
0
  websocket_ping_interval_sec_ = sec;
20378
0
}
20379
20380
0
inline void WebSocketClient::set_websocket_max_missed_pongs(int count) {
20381
0
  websocket_max_missed_pongs_ = count;
20382
0
}
20383
20384
0
inline void WebSocketClient::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
20385
20386
0
inline void WebSocketClient::set_address_family(int family) {
20387
0
  address_family_ = family;
20388
0
}
20389
20390
0
inline void WebSocketClient::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
20391
20392
0
inline void WebSocketClient::set_socket_options(SocketOptions socket_options) {
20393
0
  socket_options_ = std::move(socket_options);
20394
0
}
20395
20396
0
inline void WebSocketClient::set_connection_timeout(time_t sec, time_t usec) {
20397
0
  connection_timeout_sec_ = sec;
20398
0
  connection_timeout_usec_ = usec;
20399
0
}
20400
20401
0
inline void WebSocketClient::set_interface(const std::string &intf) {
20402
0
  interface_ = intf;
20403
0
}
20404
20405
#ifdef CPPHTTPLIB_SSL_ENABLED
20406
20407
inline void WebSocketClient::set_ca_cert_path(const std::string &path) {
20408
  ca_cert_file_path_ = path;
20409
}
20410
20411
inline void WebSocketClient::set_ca_cert_store(tls::ca_store_t store) {
20412
  ca_cert_store_ = store;
20413
}
20414
20415
inline void
20416
WebSocketClient::enable_server_certificate_verification(bool enabled) {
20417
  server_certificate_verification_ = enabled;
20418
}
20419
20420
#endif // CPPHTTPLIB_SSL_ENABLED
20421
20422
} // namespace ws
20423
20424
// ----------------------------------------------------------------------------
20425
20426
} // namespace httplib
20427
20428
#endif // CPPHTTPLIB_HTTPLIB_H