ESPHome 2025.5.0
Loading...
Searching...
No Matches
httplib.h
Go to the documentation of this file.
1#pragma once
2
11
12//
13// httplib.h
14//
15// Copyright (c) 2024 Yuji Hirose. All rights reserved.
16// MIT License
17//
18
19#ifdef USE_HOST
20#ifndef CPPHTTPLIB_HTTPLIB_H
21#define CPPHTTPLIB_HTTPLIB_H
22
23#define CPPHTTPLIB_VERSION "0.18.2"
24
25/*
26 * Configuration
27 */
28
29#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
30#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
31#endif
32
33#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
34#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
35#endif
36
37#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
38#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
39#endif
40
41#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
42#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
43#endif
44
45#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
46#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
47#endif
48
49#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
50#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
51#endif
52
53#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
54#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
55#endif
56
57#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
58#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
59#endif
60
61#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
62#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
63#endif
64
65#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
66#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
67#endif
68
69#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
70#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
71#endif
72
73#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
74#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
75#endif
76
77#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
78#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
79#endif
80
81#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
82#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
83#endif
84
85#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
86#ifdef _WIN32
87#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
88#else
89#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
90#endif
91#endif
92
93#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
94#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
95#endif
96
97#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
98#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
99#endif
100
101#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
102#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
103#endif
104
105#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
106#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
107#endif
108
109#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
110#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
111#endif
112
113#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
114#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
115#endif
116
117#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
118#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
119#endif
120
121#ifndef CPPHTTPLIB_TCP_NODELAY
122#define CPPHTTPLIB_TCP_NODELAY false
123#endif
124
125#ifndef CPPHTTPLIB_IPV6_V6ONLY
126#define CPPHTTPLIB_IPV6_V6ONLY false
127#endif
128
129#ifndef CPPHTTPLIB_RECV_BUFSIZ
130#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
131#endif
132
133#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
134#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
135#endif
136
137#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
138#define CPPHTTPLIB_THREAD_POOL_COUNT \
139 ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0))
140#endif
141
142#ifndef CPPHTTPLIB_RECV_FLAGS
143#define CPPHTTPLIB_RECV_FLAGS 0
144#endif
145
146#ifndef CPPHTTPLIB_SEND_FLAGS
147#define CPPHTTPLIB_SEND_FLAGS 0
148#endif
149
150#ifndef CPPHTTPLIB_LISTEN_BACKLOG
151#define CPPHTTPLIB_LISTEN_BACKLOG 5
152#endif
153
154/*
155 * Headers
156 */
157
158#ifdef _WIN32
159#ifndef _CRT_SECURE_NO_WARNINGS
160#define _CRT_SECURE_NO_WARNINGS
161#endif //_CRT_SECURE_NO_WARNINGS
162
163#ifndef _CRT_NONSTDC_NO_DEPRECATE
164#define _CRT_NONSTDC_NO_DEPRECATE
165#endif //_CRT_NONSTDC_NO_DEPRECATE
166
167#if defined(_MSC_VER)
168#if _MSC_VER < 1900
169#error Sorry, Visual Studio versions prior to 2015 are not supported
170#endif
171
172#pragma comment(lib, "ws2_32.lib")
173
174#ifdef _WIN64
175using ssize_t = __int64;
176#else
177using ssize_t = long;
178#endif
179#endif // _MSC_VER
180
181#ifndef S_ISREG
182#define S_ISREG(m) (((m) &S_IFREG) == S_IFREG)
183#endif // S_ISREG
184
185#ifndef S_ISDIR
186#define S_ISDIR(m) (((m) &S_IFDIR) == S_IFDIR)
187#endif // S_ISDIR
188
189#ifndef NOMINMAX
190#define NOMINMAX
191#endif // NOMINMAX
192
193#include <io.h>
194#include <winsock2.h>
195#include <ws2tcpip.h>
196
197#ifndef WSA_FLAG_NO_HANDLE_INHERIT
198#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
199#endif
200
201using socket_t = SOCKET;
202#ifdef CPPHTTPLIB_USE_POLL
203#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
204#endif
205
206#else // not _WIN32
207
208#include <arpa/inet.h>
209#if !defined(_AIX) && !defined(__MVS__)
210#include <ifaddrs.h>
211#endif
212#ifdef __MVS__
213#include <strings.h>
214#ifndef NI_MAXHOST
215#define NI_MAXHOST 1025
216#endif
217#endif
218#include <net/if.h>
219#include <netdb.h>
220#include <netinet/in.h>
221#ifdef __linux__
222#include <resolv.h>
223#endif
224#include <netinet/tcp.h>
225#ifdef CPPHTTPLIB_USE_POLL
226#include <poll.h>
227#endif
228#include <csignal>
229#include <pthread.h>
230#include <sys/mman.h>
231#include <sys/select.h>
232#include <sys/socket.h>
233#include <sys/un.h>
234#include <unistd.h>
235
236using socket_t = int;
237#ifndef INVALID_SOCKET
238#define INVALID_SOCKET (-1)
239#endif
240#endif //_WIN32
241
242#include <algorithm>
243#include <array>
244#include <atomic>
245#include <cassert>
246#include <cctype>
247#include <climits>
248#include <condition_variable>
249#include <cstring>
250#include <errno.h>
251#include <exception>
252#include <fcntl.h>
253#include <fstream>
254#include <functional>
255#include <iomanip>
256#include <iostream>
257#include <list>
258#include <map>
259#include <memory>
260#include <mutex>
261#include <random>
262#include <regex>
263#include <set>
264#include <sstream>
265#include <string>
266#include <sys/stat.h>
267#include <thread>
268#include <unordered_map>
269#include <unordered_set>
270#include <utility>
271
272#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
273#ifdef _WIN32
274#include <wincrypt.h>
275
276// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
277// used
278#undef X509_NAME
279#undef X509_CERT_PAIR
280#undef X509_EXTENSIONS
281#undef PKCS7_SIGNER_INFO
282
283#ifdef _MSC_VER
284#pragma comment(lib, "crypt32.lib")
285#endif
286#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
287#include <TargetConditionals.h>
288#if TARGET_OS_OSX
289#include <CoreFoundation/CoreFoundation.h>
290#include <Security/Security.h>
291#endif // TARGET_OS_OSX
292#endif // _WIN32
293
294#include <openssl/err.h>
295#include <openssl/evp.h>
296#include <openssl/ssl.h>
297#include <openssl/x509v3.h>
298
299#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
300#include <openssl/applink.c>
301#endif
302
303#include <iostream>
304#include <sstream>
305
306#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
307#if OPENSSL_VERSION_NUMBER < 0x1010107f
308#error Please use OpenSSL or a current version of BoringSSL
309#endif
310#define SSL_get1_peer_certificate SSL_get_peer_certificate
311#elif OPENSSL_VERSION_NUMBER < 0x30000000L
312#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
313#endif
314
315#endif
316
317#ifdef CPPHTTPLIB_ZLIB_SUPPORT
318#include <zlib.h>
319#endif
320
321#ifdef CPPHTTPLIB_BROTLI_SUPPORT
322#include <brotli/decode.h>
323#include <brotli/encode.h>
324#endif
325
326/*
327 * Declaration
328 */
329namespace httplib {
330
331namespace detail {
332
333/*
334 * Backport std::make_unique from C++14.
335 *
336 * NOTE: This code came up with the following stackoverflow post:
337 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
338 *
339 */
340
341template<class T, class... Args>
342typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(Args &&...args) {
343 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
344}
345
346template<class T>
347typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(std::size_t n) {
348 typedef typename std::remove_extent<T>::type RT;
349 return std::unique_ptr<T>(new RT[n]);
350}
351
352namespace case_ignore {
353
354inline unsigned char to_lower(int c) {
355 const static unsigned char table[256] = {
356 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
357 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
358 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97,
359 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
360 120, 121, 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
361 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
362 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
363 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
364 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226, 227, 228, 229,
365 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 215, 248, 249, 250, 251,
366 252, 253, 254, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
367 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
368 };
369 return table[(unsigned char) (char) c];
370}
371
372inline bool equal(const std::string &a, const std::string &b) {
373 return a.size() == b.size() &&
374 std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) { return to_lower(ca) == to_lower(cb); });
375}
376
377struct equal_to {
378 bool operator()(const std::string &a, const std::string &b) const { return equal(a, b); }
379};
380
381struct hash {
382 size_t operator()(const std::string &key) const { return hash_core(key.data(), key.size(), 0); }
383
384 size_t hash_core(const char *s, size_t l, size_t h) const {
385 return (l == 0) ? h
386 : hash_core(s + 1, l - 1,
387 // Unsets the 6 high bits of h, therefore no
388 // overflow happens
389 (((std::numeric_limits<size_t>::max)() >> 6) & h * 33) ^
390 static_cast<unsigned char>(to_lower(*s)));
391 }
392};
393
394} // namespace case_ignore
395
396// This is based on
397// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
398
399struct scope_exit {
400 explicit scope_exit(std::function<void(void)> &&f) : exit_function(std::move(f)), execute_on_destruction{true} {}
401
402 scope_exit(scope_exit &&rhs) noexcept
403 : exit_function(std::move(rhs.exit_function)), execute_on_destruction{rhs.execute_on_destruction} {
404 rhs.release();
405 }
406
407 ~scope_exit() {
408 if (execute_on_destruction) {
409 this->exit_function();
410 }
411 }
412
413 void release() { this->execute_on_destruction = false; }
414
415 private:
416 scope_exit(const scope_exit &) = delete;
417 void operator=(const scope_exit &) = delete;
418 scope_exit &operator=(scope_exit &&) = delete;
419
420 std::function<void(void)> exit_function;
421 bool execute_on_destruction;
422};
423
424} // namespace detail
425
426enum StatusCode {
427 // Information responses
428 Continue_100 = 100,
429 SwitchingProtocol_101 = 101,
430 Processing_102 = 102,
431 EarlyHints_103 = 103,
432
433 // Successful responses
434 OK_200 = 200,
435 Created_201 = 201,
436 Accepted_202 = 202,
437 NonAuthoritativeInformation_203 = 203,
438 NoContent_204 = 204,
439 ResetContent_205 = 205,
440 PartialContent_206 = 206,
441 MultiStatus_207 = 207,
442 AlreadyReported_208 = 208,
443 IMUsed_226 = 226,
444
445 // Redirection messages
446 MultipleChoices_300 = 300,
447 MovedPermanently_301 = 301,
448 Found_302 = 302,
449 SeeOther_303 = 303,
450 NotModified_304 = 304,
451 UseProxy_305 = 305,
452 unused_306 = 306,
453 TemporaryRedirect_307 = 307,
454 PermanentRedirect_308 = 308,
455
456 // Client error responses
457 BadRequest_400 = 400,
458 Unauthorized_401 = 401,
459 PaymentRequired_402 = 402,
460 Forbidden_403 = 403,
461 NotFound_404 = 404,
462 MethodNotAllowed_405 = 405,
463 NotAcceptable_406 = 406,
464 ProxyAuthenticationRequired_407 = 407,
465 RequestTimeout_408 = 408,
466 Conflict_409 = 409,
467 Gone_410 = 410,
468 LengthRequired_411 = 411,
469 PreconditionFailed_412 = 412,
470 PayloadTooLarge_413 = 413,
471 UriTooLong_414 = 414,
472 UnsupportedMediaType_415 = 415,
473 RangeNotSatisfiable_416 = 416,
474 ExpectationFailed_417 = 417,
475 ImATeapot_418 = 418,
476 MisdirectedRequest_421 = 421,
477 UnprocessableContent_422 = 422,
478 Locked_423 = 423,
479 FailedDependency_424 = 424,
480 TooEarly_425 = 425,
481 UpgradeRequired_426 = 426,
482 PreconditionRequired_428 = 428,
483 TooManyRequests_429 = 429,
484 RequestHeaderFieldsTooLarge_431 = 431,
485 UnavailableForLegalReasons_451 = 451,
486
487 // Server error responses
488 InternalServerError_500 = 500,
489 NotImplemented_501 = 501,
490 BadGateway_502 = 502,
491 ServiceUnavailable_503 = 503,
492 GatewayTimeout_504 = 504,
493 HttpVersionNotSupported_505 = 505,
494 VariantAlsoNegotiates_506 = 506,
495 InsufficientStorage_507 = 507,
496 LoopDetected_508 = 508,
497 NotExtended_510 = 510,
498 NetworkAuthenticationRequired_511 = 511,
499};
500
501using Headers =
502 std::unordered_multimap<std::string, std::string, detail::case_ignore::hash, detail::case_ignore::equal_to>;
503
504using Params = std::multimap<std::string, std::string>;
505using Match = std::smatch;
506
507using Progress = std::function<bool(uint64_t current, uint64_t total)>;
508
509struct Response;
510using ResponseHandler = std::function<bool(const Response &response)>;
511
512struct MultipartFormData {
513 std::string name;
514 std::string content;
515 std::string filename;
516 std::string content_type;
517};
518using MultipartFormDataItems = std::vector<MultipartFormData>;
519using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
520
521class DataSink {
522 public:
523 DataSink() : os(&sb_), sb_(*this) {}
524
525 DataSink(const DataSink &) = delete;
526 DataSink &operator=(const DataSink &) = delete;
527 DataSink(DataSink &&) = delete;
528 DataSink &operator=(DataSink &&) = delete;
529
530 std::function<bool(const char *data, size_t data_len)> write;
531 std::function<bool()> is_writable;
532 std::function<void()> done;
533 std::function<void(const Headers &trailer)> done_with_trailer;
534 std::ostream os;
535
536 private:
537 class data_sink_streambuf final : public std::streambuf {
538 public:
539 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
540
541 protected:
542 std::streamsize xsputn(const char *s, std::streamsize n) override {
543 sink_.write(s, static_cast<size_t>(n));
544 return n;
545 }
546
547 private:
548 DataSink &sink_;
549 };
550
551 data_sink_streambuf sb_;
552};
553
554using ContentProvider = std::function<bool(size_t offset, size_t length, DataSink &sink)>;
555
556using ContentProviderWithoutLength = std::function<bool(size_t offset, DataSink &sink)>;
557
558using ContentProviderResourceReleaser = std::function<void(bool success)>;
559
560struct MultipartFormDataProvider {
561 std::string name;
562 ContentProviderWithoutLength provider;
563 std::string filename;
564 std::string content_type;
565};
566using MultipartFormDataProviderItems = std::vector<MultipartFormDataProvider>;
567
568using ContentReceiverWithProgress =
569 std::function<bool(const char *data, size_t data_length, uint64_t offset, uint64_t total_length)>;
570
571using ContentReceiver = std::function<bool(const char *data, size_t data_length)>;
572
573using MultipartContentHeader = std::function<bool(const MultipartFormData &file)>;
574
575class ContentReader {
576 public:
577 using Reader = std::function<bool(ContentReceiver receiver)>;
578 using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;
579
580 ContentReader(Reader reader, MultipartReader multipart_reader)
581 : reader_(std::move(reader)), multipart_reader_(std::move(multipart_reader)) {}
582
583 bool operator()(MultipartContentHeader header, ContentReceiver receiver) const {
584 return multipart_reader_(std::move(header), std::move(receiver));
585 }
586
587 bool operator()(ContentReceiver receiver) const { return reader_(std::move(receiver)); }
588
589 Reader reader_;
590 MultipartReader multipart_reader_;
591};
592
593using Range = std::pair<ssize_t, ssize_t>;
594using Ranges = std::vector<Range>;
595
596struct Request {
597 std::string method;
598 std::string path;
599 Params params;
600 Headers headers;
601 std::string body;
602
603 std::string remote_addr;
604 int remote_port = -1;
605 std::string local_addr;
606 int local_port = -1;
607
608 // for server
609 std::string version;
610 std::string target;
611 MultipartFormDataMap files;
612 Ranges ranges;
613 Match matches;
614 std::unordered_map<std::string, std::string> path_params;
615
616 // for client
617 ResponseHandler response_handler;
618 ContentReceiverWithProgress content_receiver;
619 Progress progress;
620#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
621 const SSL *ssl = nullptr;
622#endif
623
624 bool has_header(const std::string &key) const;
625 std::string get_header_value(const std::string &key, const char *def = "", size_t id = 0) const;
626 uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0, size_t id = 0) const;
627 size_t get_header_value_count(const std::string &key) const;
628 void set_header(const std::string &key, const std::string &val);
629
630 bool has_param(const std::string &key) const;
631 std::string get_param_value(const std::string &key, size_t id = 0) const;
632 size_t get_param_value_count(const std::string &key) const;
633
634 bool is_multipart_form_data() const;
635
636 bool has_file(const std::string &key) const;
637 MultipartFormData get_file_value(const std::string &key) const;
638 std::vector<MultipartFormData> get_file_values(const std::string &key) const;
639
640 // private members...
641 size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
642 size_t content_length_ = 0;
643 ContentProvider content_provider_;
644 bool is_chunked_content_provider_ = false;
645 size_t authorization_count_ = 0;
646};
647
648struct Response {
649 std::string version;
650 int status = -1;
651 std::string reason;
652 Headers headers;
653 std::string body;
654 std::string location; // Redirect location
655
656 bool has_header(const std::string &key) const;
657 std::string get_header_value(const std::string &key, const char *def = "", size_t id = 0) const;
658 uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0, size_t id = 0) const;
659 size_t get_header_value_count(const std::string &key) const;
660 void set_header(const std::string &key, const std::string &val);
661
662 void set_redirect(const std::string &url, int status = StatusCode::Found_302);
663 void set_content(const char *s, size_t n, const std::string &content_type);
664 void set_content(const std::string &s, const std::string &content_type);
665 void set_content(std::string &&s, const std::string &content_type);
666
667 void set_content_provider(size_t length, const std::string &content_type, ContentProvider provider,
668 ContentProviderResourceReleaser resource_releaser = nullptr);
669
670 void set_content_provider(const std::string &content_type, ContentProviderWithoutLength provider,
671 ContentProviderResourceReleaser resource_releaser = nullptr);
672
673 void set_chunked_content_provider(const std::string &content_type, ContentProviderWithoutLength provider,
674 ContentProviderResourceReleaser resource_releaser = nullptr);
675
676 void set_file_content(const std::string &path, const std::string &content_type);
677 void set_file_content(const std::string &path);
678
679 Response() = default;
680 Response(const Response &) = default;
681 Response &operator=(const Response &) = default;
682 Response(Response &&) = default;
683 Response &operator=(Response &&) = default;
684 ~Response() {
685 if (content_provider_resource_releaser_) {
686 content_provider_resource_releaser_(content_provider_success_);
687 }
688 }
689
690 // private members...
691 size_t content_length_ = 0;
692 ContentProvider content_provider_;
693 ContentProviderResourceReleaser content_provider_resource_releaser_;
694 bool is_chunked_content_provider_ = false;
695 bool content_provider_success_ = false;
696 std::string file_content_path_;
697 std::string file_content_content_type_;
698};
699
700class Stream {
701 public:
702 virtual ~Stream() = default;
703
704 virtual bool is_readable() const = 0;
705 virtual bool is_writable() const = 0;
706
707 virtual ssize_t read(char *ptr, size_t size) = 0;
708 virtual ssize_t write(const char *ptr, size_t size) = 0;
709 virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
710 virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
711 virtual socket_t socket() const = 0;
712
713 ssize_t write(const char *ptr);
714 ssize_t write(const std::string &s);
715};
716
717class TaskQueue {
718 public:
719 TaskQueue() = default;
720 virtual ~TaskQueue() = default;
721
722 virtual bool enqueue(std::function<void()> fn) = 0;
723 virtual void shutdown() = 0;
724
725 virtual void on_idle() {}
726};
727
728class ThreadPool final : public TaskQueue {
729 public:
730 explicit ThreadPool(size_t n, size_t mqr = 0) : shutdown_(false), max_queued_requests_(mqr) {
731 while (n) {
732 threads_.emplace_back(worker(*this));
733 n--;
734 }
735 }
736
737 ThreadPool(const ThreadPool &) = delete;
738 ~ThreadPool() override = default;
739
740 bool enqueue(std::function<void()> fn) override {
741 {
742 std::unique_lock<std::mutex> lock(mutex_);
743 if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {
744 return false;
745 }
746 jobs_.push_back(std::move(fn));
747 }
748
749 cond_.notify_one();
750 return true;
751 }
752
753 void shutdown() override {
754 // Stop all worker threads...
755 {
756 std::unique_lock<std::mutex> lock(mutex_);
757 shutdown_ = true;
758 }
759
760 cond_.notify_all();
761
762 // Join...
763 for (auto &t : threads_) {
764 t.join();
765 }
766 }
767
768 private:
769 struct worker {
770 explicit worker(ThreadPool &pool) : pool_(pool) {}
771
772 void operator()() {
773 for (;;) {
774 std::function<void()> fn;
775 {
776 std::unique_lock<std::mutex> lock(pool_.mutex_);
777
778 pool_.cond_.wait(lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
779
780 if (pool_.shutdown_ && pool_.jobs_.empty()) {
781 break;
782 }
783
784 fn = pool_.jobs_.front();
785 pool_.jobs_.pop_front();
786 }
787
788 assert(true == static_cast<bool>(fn));
789 fn();
790 }
791
792#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER)
793 OPENSSL_thread_stop();
794#endif
795 }
796
797 ThreadPool &pool_;
798 };
799 friend struct worker;
800
801 std::vector<std::thread> threads_;
802 std::list<std::function<void()>> jobs_;
803
804 bool shutdown_;
805 size_t max_queued_requests_ = 0;
806
807 std::condition_variable cond_;
808 std::mutex mutex_;
809};
810
811using Logger = std::function<void(const Request &, const Response &)>;
812
813using SocketOptions = std::function<void(socket_t sock)>;
814
815void default_socket_options(socket_t sock);
816
817const char *status_message(int status);
818
819std::string get_bearer_token_auth(const Request &req);
820
821namespace detail {
822
823class MatcherBase {
824 public:
825 virtual ~MatcherBase() = default;
826
827 // Match request path and populate its matches and
828 virtual bool match(Request &request) const = 0;
829};
830
849class PathParamsMatcher final : public MatcherBase {
850 public:
851 PathParamsMatcher(const std::string &pattern);
852
853 bool match(Request &request) const override;
854
855 private:
856 // Treat segment separators as the end of path parameter capture
857 // Does not need to handle query parameters as they are parsed before path
858 // matching
859 static constexpr char separator = '/';
860
861 // Contains static path fragments to match against, excluding the '/' after
862 // path params
863 // Fragments are separated by path params
864 std::vector<std::string> static_fragments_;
865 // Stores the names of the path parameters to be used as keys in the
866 // Request::path_params map
867 std::vector<std::string> param_names_;
868};
869
878class RegexMatcher final : public MatcherBase {
879 public:
880 RegexMatcher(const std::string &pattern) : regex_(pattern) {}
881
882 bool match(Request &request) const override;
883
884 private:
885 std::regex regex_;
886};
887
888ssize_t write_headers(Stream &strm, const Headers &headers);
889
890} // namespace detail
891
892class Server {
893 public:
894 using Handler = std::function<void(const Request &, Response &)>;
895
896 using ExceptionHandler = std::function<void(const Request &, Response &, std::exception_ptr ep)>;
897
898 enum class HandlerResponse {
899 Handled,
900 Unhandled,
901 };
902 using HandlerWithResponse = std::function<HandlerResponse(const Request &, Response &)>;
903
904 using HandlerWithContentReader =
905 std::function<void(const Request &, Response &, const ContentReader &content_reader)>;
906
907 using Expect100ContinueHandler = std::function<int(const Request &, Response &)>;
908
909 Server();
910
911 virtual ~Server();
912
913 virtual bool is_valid() const;
914
915 Server &Get(const std::string &pattern, Handler handler);
916 Server &Post(const std::string &pattern, Handler handler);
917 Server &Post(const std::string &pattern, HandlerWithContentReader handler);
918 Server &Put(const std::string &pattern, Handler handler);
919 Server &Put(const std::string &pattern, HandlerWithContentReader handler);
920 Server &Patch(const std::string &pattern, Handler handler);
921 Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
922 Server &Delete(const std::string &pattern, Handler handler);
923 Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
924 Server &Options(const std::string &pattern, Handler handler);
925
926 bool set_base_dir(const std::string &dir, const std::string &mount_point = std::string());
927 bool set_mount_point(const std::string &mount_point, const std::string &dir, Headers headers = Headers());
928 bool remove_mount_point(const std::string &mount_point);
929 Server &set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime);
930 Server &set_default_file_mimetype(const std::string &mime);
931 Server &set_file_request_handler(Handler handler);
932
933 template<class ErrorHandlerFunc> Server &set_error_handler(ErrorHandlerFunc &&handler) {
934 return set_error_handler_core(std::forward<ErrorHandlerFunc>(handler),
935 std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
936 }
937
938 Server &set_exception_handler(ExceptionHandler handler);
939 Server &set_pre_routing_handler(HandlerWithResponse handler);
940 Server &set_post_routing_handler(Handler handler);
941
942 Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
943 Server &set_logger(Logger logger);
944
945 Server &set_address_family(int family);
946 Server &set_tcp_nodelay(bool on);
947 Server &set_ipv6_v6only(bool on);
948 Server &set_socket_options(SocketOptions socket_options);
949
950 Server &set_default_headers(Headers headers);
951 Server &set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
952
953 Server &set_keep_alive_max_count(size_t count);
954 Server &set_keep_alive_timeout(time_t sec);
955
956 Server &set_read_timeout(time_t sec, time_t usec = 0);
957 template<class Rep, class Period> Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
958
959 Server &set_write_timeout(time_t sec, time_t usec = 0);
960 template<class Rep, class Period> Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
961
962 Server &set_idle_interval(time_t sec, time_t usec = 0);
963 template<class Rep, class Period> Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
964
965 Server &set_payload_max_length(size_t length);
966
967 bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
968 int bind_to_any_port(const std::string &host, int socket_flags = 0);
969 bool listen_after_bind();
970
971 bool listen(const std::string &host, int port, int socket_flags = 0);
972
973 bool is_running() const;
974 void wait_until_ready() const;
975 void stop();
976 void decommission();
977
978 std::function<TaskQueue *(void)> new_task_queue;
979
980 protected:
981 bool process_request(Stream &strm, const std::string &remote_addr, int remote_port, const std::string &local_addr,
982 int local_port, bool close_connection, bool &connection_closed,
983 const std::function<void(Request &)> &setup_request);
984
985 std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
986 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
987 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
988 time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
989 time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
990 time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
991 time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
992 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
993 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
994 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
995
996 private:
997 using Handlers = std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
998 using HandlersForContentReader =
999 std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, HandlerWithContentReader>>;
1000
1001 static std::unique_ptr<detail::MatcherBase> make_matcher(const std::string &pattern);
1002
1003 Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1004 Server &set_error_handler_core(Handler handler, std::false_type);
1005
1006 socket_t create_server_socket(const std::string &host, int port, int socket_flags,
1007 SocketOptions socket_options) const;
1008 int bind_internal(const std::string &host, int port, int socket_flags);
1009 bool listen_internal();
1010
1011 bool routing(Request &req, Response &res, Stream &strm);
1012 bool handle_file_request(const Request &req, Response &res, bool head = false);
1013 bool dispatch_request(Request &req, Response &res, const Handlers &handlers) const;
1014 bool dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
1015 const HandlersForContentReader &handlers) const;
1016
1017 bool parse_request_line(const char *s, Request &req) const;
1018 void apply_ranges(const Request &req, Response &res, std::string &content_type, std::string &boundary) const;
1019 bool write_response(Stream &strm, bool close_connection, Request &req, Response &res);
1020 bool write_response_with_content(Stream &strm, bool close_connection, const Request &req, Response &res);
1021 bool write_response_core(Stream &strm, bool close_connection, const Request &req, Response &res,
1022 bool need_apply_ranges);
1023 bool write_content_with_provider(Stream &strm, const Request &req, Response &res, const std::string &boundary,
1024 const std::string &content_type);
1025 bool read_content(Stream &strm, Request &req, Response &res);
1026 bool read_content_with_content_receiver(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
1027 MultipartContentHeader multipart_header, ContentReceiver multipart_receiver);
1028 bool read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
1029 MultipartContentHeader multipart_header, ContentReceiver multipart_receiver) const;
1030
1031 virtual bool process_and_close_socket(socket_t sock);
1032
1033 std::atomic<bool> is_running_{false};
1034 std::atomic<bool> is_decommisioned{false};
1035
1036 struct MountPointEntry {
1037 std::string mount_point;
1038 std::string base_dir;
1039 Headers headers;
1040 };
1041 std::vector<MountPointEntry> base_dirs_;
1042 std::map<std::string, std::string> file_extension_and_mimetype_map_;
1043 std::string default_file_mimetype_ = "application/octet-stream";
1044 Handler file_request_handler_;
1045
1046 Handlers get_handlers_;
1047 Handlers post_handlers_;
1048 HandlersForContentReader post_handlers_for_content_reader_;
1049 Handlers put_handlers_;
1050 HandlersForContentReader put_handlers_for_content_reader_;
1051 Handlers patch_handlers_;
1052 HandlersForContentReader patch_handlers_for_content_reader_;
1053 Handlers delete_handlers_;
1054 HandlersForContentReader delete_handlers_for_content_reader_;
1055 Handlers options_handlers_;
1056
1057 HandlerWithResponse error_handler_;
1058 ExceptionHandler exception_handler_;
1059 HandlerWithResponse pre_routing_handler_;
1060 Handler post_routing_handler_;
1061 Expect100ContinueHandler expect_100_continue_handler_;
1062
1063 Logger logger_;
1064
1065 int address_family_ = AF_UNSPEC;
1066 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1067 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1068 SocketOptions socket_options_ = default_socket_options;
1069
1070 Headers default_headers_;
1071 std::function<ssize_t(Stream &, Headers &)> header_writer_ = detail::write_headers;
1072};
1073
1074enum class Error {
1075 Success = 0,
1076 Unknown,
1077 Connection,
1078 BindIPAddress,
1079 Read,
1080 Write,
1081 ExceedRedirectCount,
1082 Canceled,
1083 SSLConnection,
1084 SSLLoadingCerts,
1085 SSLServerVerification,
1086 SSLServerHostnameVerification,
1087 UnsupportedMultipartBoundaryChars,
1088 Compression,
1089 ConnectionTimeout,
1090 ProxyConnection,
1091
1092 // For internal use only
1093 SSLPeerCouldBeClosed_,
1094};
1095
1096std::string to_string(Error error);
1097
1098std::ostream &operator<<(std::ostream &os, const Error &obj);
1099
1100class Result {
1101 public:
1102 Result() = default;
1103 Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers = Headers{})
1104 : res_(std::move(res)), err_(err), request_headers_(std::move(request_headers)) {}
1105 // Response
1106 operator bool() const { return res_ != nullptr; }
1107 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
1108 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
1109 const Response &value() const { return *res_; }
1110 Response &value() { return *res_; }
1111 const Response &operator*() const { return *res_; }
1112 Response &operator*() { return *res_; }
1113 const Response *operator->() const { return res_.get(); }
1114 Response *operator->() { return res_.get(); }
1115
1116 // Error
1117 Error error() const { return err_; }
1118
1119 // Request Headers
1120 bool has_request_header(const std::string &key) const;
1121 std::string get_request_header_value(const std::string &key, const char *def = "", size_t id = 0) const;
1122 uint64_t get_request_header_value_u64(const std::string &key, uint64_t def = 0, size_t id = 0) const;
1123 size_t get_request_header_value_count(const std::string &key) const;
1124
1125 private:
1126 std::unique_ptr<Response> res_;
1127 Error err_ = Error::Unknown;
1128 Headers request_headers_;
1129};
1130
1131class ClientImpl {
1132 public:
1133 explicit ClientImpl(const std::string &host);
1134
1135 explicit ClientImpl(const std::string &host, int port);
1136
1137 explicit ClientImpl(const std::string &host, int port, const std::string &client_cert_path,
1138 const std::string &client_key_path);
1139
1140 virtual ~ClientImpl();
1141
1142 virtual bool is_valid() const;
1143
1144 Result Get(const std::string &path);
1145 Result Get(const std::string &path, const Headers &headers);
1146 Result Get(const std::string &path, Progress progress);
1147 Result Get(const std::string &path, const Headers &headers, Progress progress);
1148 Result Get(const std::string &path, ContentReceiver content_receiver);
1149 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver);
1150 Result Get(const std::string &path, ContentReceiver content_receiver, Progress progress);
1151 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, Progress progress);
1152 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver);
1153 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
1154 ContentReceiver content_receiver);
1155 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1156 Progress progress);
1157 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
1158 ContentReceiver content_receiver, Progress progress);
1159
1160 Result Get(const std::string &path, const Params &params, const Headers &headers, Progress progress = nullptr);
1161 Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver,
1162 Progress progress = nullptr);
1163 Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler,
1164 ContentReceiver content_receiver, Progress progress = nullptr);
1165
1166 Result Head(const std::string &path);
1167 Result Head(const std::string &path, const Headers &headers);
1168
1169 Result Post(const std::string &path);
1170 Result Post(const std::string &path, const Headers &headers);
1171 Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1172 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1173 const std::string &content_type);
1174 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1175 const std::string &content_type, Progress progress);
1176 Result Post(const std::string &path, const std::string &body, const std::string &content_type);
1177 Result Post(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1178 Result Post(const std::string &path, const Headers &headers, const std::string &body,
1179 const std::string &content_type);
1180 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1181 Progress progress);
1182 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider,
1183 const std::string &content_type);
1184 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type);
1185 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1186 const std::string &content_type);
1187 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1188 const std::string &content_type);
1189 Result Post(const std::string &path, const Params &params);
1190 Result Post(const std::string &path, const Headers &headers, const Params &params);
1191 Result Post(const std::string &path, const Headers &headers, const Params &params, Progress progress);
1192 Result Post(const std::string &path, const MultipartFormDataItems &items);
1193 Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items);
1194 Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1195 const std::string &boundary);
1196 Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1197 const MultipartFormDataProviderItems &provider_items);
1198
1199 Result Put(const std::string &path);
1200 Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1201 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1202 const std::string &content_type);
1203 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1204 const std::string &content_type, Progress progress);
1205 Result Put(const std::string &path, const std::string &body, const std::string &content_type);
1206 Result Put(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1207 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type);
1208 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1209 Progress progress);
1210 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider,
1211 const std::string &content_type);
1212 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type);
1213 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1214 const std::string &content_type);
1215 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1216 const std::string &content_type);
1217 Result Put(const std::string &path, const Params &params);
1218 Result Put(const std::string &path, const Headers &headers, const Params &params);
1219 Result Put(const std::string &path, const Headers &headers, const Params &params, Progress progress);
1220 Result Put(const std::string &path, const MultipartFormDataItems &items);
1221 Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items);
1222 Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1223 const std::string &boundary);
1224 Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1225 const MultipartFormDataProviderItems &provider_items);
1226
1227 Result Patch(const std::string &path);
1228 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1229 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1230 Progress progress);
1231 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1232 const std::string &content_type);
1233 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1234 const std::string &content_type, Progress progress);
1235 Result Patch(const std::string &path, const std::string &body, const std::string &content_type);
1236 Result Patch(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1237 Result Patch(const std::string &path, const Headers &headers, const std::string &body,
1238 const std::string &content_type);
1239 Result Patch(const std::string &path, const Headers &headers, const std::string &body,
1240 const std::string &content_type, Progress progress);
1241 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
1242 const std::string &content_type);
1243 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type);
1244 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1245 const std::string &content_type);
1246 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1247 const std::string &content_type);
1248
1249 Result Delete(const std::string &path);
1250 Result Delete(const std::string &path, const Headers &headers);
1251 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1252 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1253 Progress progress);
1254 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1255 const std::string &content_type);
1256 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1257 const std::string &content_type, Progress progress);
1258 Result Delete(const std::string &path, const std::string &body, const std::string &content_type);
1259 Result Delete(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1260 Result Delete(const std::string &path, const Headers &headers, const std::string &body,
1261 const std::string &content_type);
1262 Result Delete(const std::string &path, const Headers &headers, const std::string &body,
1263 const std::string &content_type, Progress progress);
1264
1265 Result Options(const std::string &path);
1266 Result Options(const std::string &path, const Headers &headers);
1267
1268 bool send(Request &req, Response &res, Error &error);
1269 Result send(const Request &req);
1270
1271 void stop();
1272
1273 std::string host() const;
1274 int port() const;
1275
1276 size_t is_socket_open() const;
1277 socket_t socket() const;
1278
1279 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1280
1281 void set_default_headers(Headers headers);
1282
1283 void set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1284
1285 void set_address_family(int family);
1286 void set_tcp_nodelay(bool on);
1287 void set_ipv6_v6only(bool on);
1288 void set_socket_options(SocketOptions socket_options);
1289
1290 void set_connection_timeout(time_t sec, time_t usec = 0);
1291 template<class Rep, class Period> void set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1292
1293 void set_read_timeout(time_t sec, time_t usec = 0);
1294 template<class Rep, class Period> void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1295
1296 void set_write_timeout(time_t sec, time_t usec = 0);
1297 template<class Rep, class Period> void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1298
1299 void set_basic_auth(const std::string &username, const std::string &password);
1300 void set_bearer_token_auth(const std::string &token);
1301#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1302 void set_digest_auth(const std::string &username, const std::string &password);
1303#endif
1304
1305 void set_keep_alive(bool on);
1306 void set_follow_location(bool on);
1307
1308 void set_url_encode(bool on);
1309
1310 void set_compress(bool on);
1311
1312 void set_decompress(bool on);
1313
1314 void set_interface(const std::string &intf);
1315
1316 void set_proxy(const std::string &host, int port);
1317 void set_proxy_basic_auth(const std::string &username, const std::string &password);
1318 void set_proxy_bearer_token_auth(const std::string &token);
1319#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1320 void set_proxy_digest_auth(const std::string &username, const std::string &password);
1321#endif
1322
1323#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1324 void set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path = std::string());
1325 void set_ca_cert_store(X509_STORE *ca_cert_store);
1326 X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
1327#endif
1328
1329#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1330 void enable_server_certificate_verification(bool enabled);
1331 void enable_server_hostname_verification(bool enabled);
1332 void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);
1333#endif
1334
1335 void set_logger(Logger logger);
1336
1337 protected:
1338 struct Socket {
1339 socket_t sock = INVALID_SOCKET;
1340#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1341 SSL *ssl = nullptr;
1342#endif
1343
1344 bool is_open() const { return sock != INVALID_SOCKET; }
1345 };
1346
1347 virtual bool create_and_connect_socket(Socket &socket, Error &error);
1348
1349 // All of:
1350 // shutdown_ssl
1351 // shutdown_socket
1352 // close_socket
1353 // should ONLY be called when socket_mutex_ is locked.
1354 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
1355 // with a DIFFERENT thread sending requests using that socket.
1356 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
1357 void shutdown_socket(Socket &socket) const;
1358 void close_socket(Socket &socket);
1359
1360 bool process_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error);
1361
1362 bool write_content_with_provider(Stream &strm, const Request &req, Error &error) const;
1363
1364 void copy_settings(const ClientImpl &rhs);
1365
1366 // Socket endpoint information
1367 const std::string host_;
1368 const int port_;
1369 const std::string host_and_port_;
1370
1371 // Current open socket
1372 Socket socket_;
1373 mutable std::mutex socket_mutex_;
1374 std::recursive_mutex request_mutex_;
1375
1376 // These are all protected under socket_mutex
1377 size_t socket_requests_in_flight_ = 0;
1378 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
1379 bool socket_should_be_closed_when_request_is_done_ = false;
1380
1381 // Hostname-IP map
1382 std::map<std::string, std::string> addr_map_;
1383
1384 // Default headers
1385 Headers default_headers_;
1386
1387 // Header writer
1388 std::function<ssize_t(Stream &, Headers &)> header_writer_ = detail::write_headers;
1389
1390 // Settings
1391 std::string client_cert_path_;
1392 std::string client_key_path_;
1393
1394 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1395 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1396 time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1397 time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1398 time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1399 time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1400
1401 std::string basic_auth_username_;
1402 std::string basic_auth_password_;
1403 std::string bearer_token_auth_token_;
1404#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1405 std::string digest_auth_username_;
1406 std::string digest_auth_password_;
1407#endif
1408
1409 bool keep_alive_ = false;
1410 bool follow_location_ = false;
1411
1412 bool url_encode_ = true;
1413
1414 int address_family_ = AF_UNSPEC;
1415 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1416 bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1417 SocketOptions socket_options_ = nullptr;
1418
1419 bool compress_ = false;
1420 bool decompress_ = true;
1421
1422 std::string interface_;
1423
1424 std::string proxy_host_;
1425 int proxy_port_ = -1;
1426
1427 std::string proxy_basic_auth_username_;
1428 std::string proxy_basic_auth_password_;
1429 std::string proxy_bearer_token_auth_token_;
1430#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1431 std::string proxy_digest_auth_username_;
1432 std::string proxy_digest_auth_password_;
1433#endif
1434
1435#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1436 std::string ca_cert_file_path_;
1437 std::string ca_cert_dir_path_;
1438
1439 X509_STORE *ca_cert_store_ = nullptr;
1440#endif
1441
1442#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1443 bool server_certificate_verification_ = true;
1444 bool server_hostname_verification_ = true;
1445 std::function<bool(SSL *ssl)> server_certificate_verifier_;
1446#endif
1447
1448 Logger logger_;
1449
1450 private:
1451 bool send_(Request &req, Response &res, Error &error);
1452 Result send_(Request &&req);
1453
1454#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1455 bool is_ssl_peer_could_be_closed(SSL *ssl) const;
1456#endif
1457 socket_t create_client_socket(Error &error) const;
1458 bool read_response_line(Stream &strm, const Request &req, Response &res) const;
1459 bool write_request(Stream &strm, Request &req, bool close_connection, Error &error);
1460 bool redirect(Request &req, Response &res, Error &error);
1461 bool handle_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error);
1462 std::unique_ptr<Response> send_with_content_provider(Request &req, const char *body, size_t content_length,
1463 ContentProvider content_provider,
1464 ContentProviderWithoutLength content_provider_without_length,
1465 const std::string &content_type, Error &error);
1466 Result send_with_content_provider(const std::string &method, const std::string &path, const Headers &headers,
1467 const char *body, size_t content_length, ContentProvider content_provider,
1468 ContentProviderWithoutLength content_provider_without_length,
1469 const std::string &content_type, Progress progress);
1470 ContentProviderWithoutLength get_multipart_content_provider(
1471 const std::string &boundary, const MultipartFormDataItems &items,
1472 const MultipartFormDataProviderItems &provider_items) const;
1473
1474 std::string adjust_host_string(const std::string &host) const;
1475
1476 virtual bool process_socket(const Socket &socket, std::function<bool(Stream &strm)> callback);
1477 virtual bool is_ssl() const;
1478};
1479
1480class Client {
1481 public:
1482 // Universal interface
1483 explicit Client(const std::string &scheme_host_port);
1484
1485 explicit Client(const std::string &scheme_host_port, const std::string &client_cert_path,
1486 const std::string &client_key_path);
1487
1488 // HTTP only interface
1489 explicit Client(const std::string &host, int port);
1490
1491 explicit Client(const std::string &host, int port, const std::string &client_cert_path,
1492 const std::string &client_key_path);
1493
1494 Client(Client &&) = default;
1495 Client &operator=(Client &&) = default;
1496
1497 ~Client();
1498
1499 bool is_valid() const;
1500
1501 Result Get(const std::string &path);
1502 Result Get(const std::string &path, const Headers &headers);
1503 Result Get(const std::string &path, Progress progress);
1504 Result Get(const std::string &path, const Headers &headers, Progress progress);
1505 Result Get(const std::string &path, ContentReceiver content_receiver);
1506 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver);
1507 Result Get(const std::string &path, ContentReceiver content_receiver, Progress progress);
1508 Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, Progress progress);
1509 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver);
1510 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
1511 ContentReceiver content_receiver);
1512 Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
1513 ContentReceiver content_receiver, Progress progress);
1514 Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
1515 Progress progress);
1516
1517 Result Get(const std::string &path, const Params &params, const Headers &headers, Progress progress = nullptr);
1518 Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver,
1519 Progress progress = nullptr);
1520 Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler,
1521 ContentReceiver content_receiver, Progress progress = nullptr);
1522
1523 Result Head(const std::string &path);
1524 Result Head(const std::string &path, const Headers &headers);
1525
1526 Result Post(const std::string &path);
1527 Result Post(const std::string &path, const Headers &headers);
1528 Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1529 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1530 const std::string &content_type);
1531 Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1532 const std::string &content_type, Progress progress);
1533 Result Post(const std::string &path, const std::string &body, const std::string &content_type);
1534 Result Post(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1535 Result Post(const std::string &path, const Headers &headers, const std::string &body,
1536 const std::string &content_type);
1537 Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1538 Progress progress);
1539 Result Post(const std::string &path, size_t content_length, ContentProvider content_provider,
1540 const std::string &content_type);
1541 Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type);
1542 Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1543 const std::string &content_type);
1544 Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1545 const std::string &content_type);
1546 Result Post(const std::string &path, const Params &params);
1547 Result Post(const std::string &path, const Headers &headers, const Params &params);
1548 Result Post(const std::string &path, const Headers &headers, const Params &params, Progress progress);
1549 Result Post(const std::string &path, const MultipartFormDataItems &items);
1550 Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items);
1551 Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1552 const std::string &boundary);
1553 Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1554 const MultipartFormDataProviderItems &provider_items);
1555
1556 Result Put(const std::string &path);
1557 Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1558 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1559 const std::string &content_type);
1560 Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1561 const std::string &content_type, Progress progress);
1562 Result Put(const std::string &path, const std::string &body, const std::string &content_type);
1563 Result Put(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1564 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type);
1565 Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type,
1566 Progress progress);
1567 Result Put(const std::string &path, size_t content_length, ContentProvider content_provider,
1568 const std::string &content_type);
1569 Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type);
1570 Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1571 const std::string &content_type);
1572 Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1573 const std::string &content_type);
1574 Result Put(const std::string &path, const Params &params);
1575 Result Put(const std::string &path, const Headers &headers, const Params &params);
1576 Result Put(const std::string &path, const Headers &headers, const Params &params, Progress progress);
1577 Result Put(const std::string &path, const MultipartFormDataItems &items);
1578 Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items);
1579 Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1580 const std::string &boundary);
1581 Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
1582 const MultipartFormDataProviderItems &provider_items);
1583
1584 Result Patch(const std::string &path);
1585 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1586 Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1587 Progress progress);
1588 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1589 const std::string &content_type);
1590 Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1591 const std::string &content_type, Progress progress);
1592 Result Patch(const std::string &path, const std::string &body, const std::string &content_type);
1593 Result Patch(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1594 Result Patch(const std::string &path, const Headers &headers, const std::string &body,
1595 const std::string &content_type);
1596 Result Patch(const std::string &path, const Headers &headers, const std::string &body,
1597 const std::string &content_type, Progress progress);
1598 Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
1599 const std::string &content_type);
1600 Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type);
1601 Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider,
1602 const std::string &content_type);
1603 Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider,
1604 const std::string &content_type);
1605
1606 Result Delete(const std::string &path);
1607 Result Delete(const std::string &path, const Headers &headers);
1608 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type);
1609 Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type,
1610 Progress progress);
1611 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1612 const std::string &content_type);
1613 Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
1614 const std::string &content_type, Progress progress);
1615 Result Delete(const std::string &path, const std::string &body, const std::string &content_type);
1616 Result Delete(const std::string &path, const std::string &body, const std::string &content_type, Progress progress);
1617 Result Delete(const std::string &path, const Headers &headers, const std::string &body,
1618 const std::string &content_type);
1619 Result Delete(const std::string &path, const Headers &headers, const std::string &body,
1620 const std::string &content_type, Progress progress);
1621
1622 Result Options(const std::string &path);
1623 Result Options(const std::string &path, const Headers &headers);
1624
1625 bool send(Request &req, Response &res, Error &error);
1626 Result send(const Request &req);
1627
1628 void stop();
1629
1630 std::string host() const;
1631 int port() const;
1632
1633 size_t is_socket_open() const;
1634 socket_t socket() const;
1635
1636 void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
1637
1638 void set_default_headers(Headers headers);
1639
1640 void set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
1641
1642 void set_address_family(int family);
1643 void set_tcp_nodelay(bool on);
1644 void set_socket_options(SocketOptions socket_options);
1645
1646 void set_connection_timeout(time_t sec, time_t usec = 0);
1647 template<class Rep, class Period> void set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
1648
1649 void set_read_timeout(time_t sec, time_t usec = 0);
1650 template<class Rep, class Period> void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
1651
1652 void set_write_timeout(time_t sec, time_t usec = 0);
1653 template<class Rep, class Period> void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1654
1655 void set_basic_auth(const std::string &username, const std::string &password);
1656 void set_bearer_token_auth(const std::string &token);
1657#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1658 void set_digest_auth(const std::string &username, const std::string &password);
1659#endif
1660
1661 void set_keep_alive(bool on);
1662 void set_follow_location(bool on);
1663
1664 void set_url_encode(bool on);
1665
1666 void set_compress(bool on);
1667
1668 void set_decompress(bool on);
1669
1670 void set_interface(const std::string &intf);
1671
1672 void set_proxy(const std::string &host, int port);
1673 void set_proxy_basic_auth(const std::string &username, const std::string &password);
1674 void set_proxy_bearer_token_auth(const std::string &token);
1675#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1676 void set_proxy_digest_auth(const std::string &username, const std::string &password);
1677#endif
1678
1679#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1680 void enable_server_certificate_verification(bool enabled);
1681 void enable_server_hostname_verification(bool enabled);
1682 void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);
1683#endif
1684
1685 void set_logger(Logger logger);
1686
1687 // SSL
1688#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1689 void set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path = std::string());
1690
1691 void set_ca_cert_store(X509_STORE *ca_cert_store);
1692 void load_ca_cert_store(const char *ca_cert, std::size_t size);
1693
1694 long get_openssl_verify_result() const;
1695
1696 SSL_CTX *ssl_context() const;
1697#endif
1698
1699 private:
1700 std::unique_ptr<ClientImpl> cli_;
1701
1702#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1703 bool is_ssl_ = false;
1704#endif
1705};
1706
1707#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1708class SSLServer : public Server {
1709 public:
1710 SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path = nullptr,
1711 const char *client_ca_cert_dir_path = nullptr, const char *private_key_password = nullptr);
1712
1713 SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store = nullptr);
1714
1715 SSLServer(const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
1716
1717 ~SSLServer() override;
1718
1719 bool is_valid() const override;
1720
1721 SSL_CTX *ssl_context() const;
1722
1723 void update_certs(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store = nullptr);
1724
1725 private:
1726 bool process_and_close_socket(socket_t sock) override;
1727
1728 SSL_CTX *ctx_;
1729 std::mutex ctx_mutex_;
1730};
1731
1732class SSLClient final : public ClientImpl {
1733 public:
1734 explicit SSLClient(const std::string &host);
1735
1736 explicit SSLClient(const std::string &host, int port);
1737
1738 explicit SSLClient(const std::string &host, int port, const std::string &client_cert_path,
1739 const std::string &client_key_path, const std::string &private_key_password = std::string());
1740
1741 explicit SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key,
1742 const std::string &private_key_password = std::string());
1743
1744 ~SSLClient() override;
1745
1746 bool is_valid() const override;
1747
1748 void set_ca_cert_store(X509_STORE *ca_cert_store);
1749 void load_ca_cert_store(const char *ca_cert, std::size_t size);
1750
1751 long get_openssl_verify_result() const;
1752
1753 SSL_CTX *ssl_context() const;
1754
1755 private:
1756 bool create_and_connect_socket(Socket &socket, Error &error) override;
1757 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1758 void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
1759
1760 bool process_socket(const Socket &socket, std::function<bool(Stream &strm)> callback) override;
1761 bool is_ssl() const override;
1762
1763 bool connect_with_proxy(Socket &sock, Response &res, bool &success, Error &error);
1764 bool initialize_ssl(Socket &socket, Error &error);
1765
1766 bool load_certs();
1767
1768 bool verify_host(X509 *server_cert) const;
1769 bool verify_host_with_subject_alt_name(X509 *server_cert) const;
1770 bool verify_host_with_common_name(X509 *server_cert) const;
1771 bool check_host_name(const char *pattern, size_t pattern_len) const;
1772
1773 SSL_CTX *ctx_;
1774 std::mutex ctx_mutex_;
1775 std::once_flag initialize_cert_;
1776
1777 std::vector<std::string> host_components_;
1778
1779 long verify_result_ = 0;
1780
1781 friend class ClientImpl;
1782};
1783#endif
1784
1785/*
1786 * Implementation of template methods.
1787 */
1788
1789namespace detail {
1790
1791template<typename T, typename U> inline void duration_to_sec_and_usec(const T &duration, U callback) {
1792 auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
1793 auto usec = std::chrono::duration_cast<std::chrono::microseconds>(duration - std::chrono::seconds(sec)).count();
1794 callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
1795}
1796
1797inline uint64_t get_header_value_u64(const Headers &headers, const std::string &key, uint64_t def, size_t id) {
1798 auto rng = headers.equal_range(key);
1799 auto it = rng.first;
1800 std::advance(it, static_cast<ssize_t>(id));
1801 if (it != rng.second) {
1802 return std::strtoull(it->second.data(), nullptr, 10);
1803 }
1804 return def;
1805}
1806
1807} // namespace detail
1808
1809inline uint64_t Request::get_header_value_u64(const std::string &key, uint64_t def, size_t id) const {
1810 return detail::get_header_value_u64(headers, key, def, id);
1811}
1812
1813inline uint64_t Response::get_header_value_u64(const std::string &key, uint64_t def, size_t id) const {
1814 return detail::get_header_value_u64(headers, key, def, id);
1815}
1816
1817inline void default_socket_options(socket_t sock) {
1818 int opt = 1;
1819#ifdef _WIN32
1820 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&opt), sizeof(opt));
1821 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char *>(&opt), sizeof(opt));
1822#else
1823#ifdef SO_REUSEPORT
1824 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<const void *>(&opt), sizeof(opt));
1825#else
1826 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const void *>(&opt), sizeof(opt));
1827#endif
1828#endif
1829}
1830
1831inline const char *status_message(int status) {
1832 switch (status) {
1833 case StatusCode::Continue_100:
1834 return "Continue";
1835 case StatusCode::SwitchingProtocol_101:
1836 return "Switching Protocol";
1837 case StatusCode::Processing_102:
1838 return "Processing";
1839 case StatusCode::EarlyHints_103:
1840 return "Early Hints";
1841 case StatusCode::OK_200:
1842 return "OK";
1843 case StatusCode::Created_201:
1844 return "Created";
1845 case StatusCode::Accepted_202:
1846 return "Accepted";
1847 case StatusCode::NonAuthoritativeInformation_203:
1848 return "Non-Authoritative Information";
1849 case StatusCode::NoContent_204:
1850 return "No Content";
1851 case StatusCode::ResetContent_205:
1852 return "Reset Content";
1853 case StatusCode::PartialContent_206:
1854 return "Partial Content";
1855 case StatusCode::MultiStatus_207:
1856 return "Multi-Status";
1857 case StatusCode::AlreadyReported_208:
1858 return "Already Reported";
1859 case StatusCode::IMUsed_226:
1860 return "IM Used";
1861 case StatusCode::MultipleChoices_300:
1862 return "Multiple Choices";
1863 case StatusCode::MovedPermanently_301:
1864 return "Moved Permanently";
1865 case StatusCode::Found_302:
1866 return "Found";
1867 case StatusCode::SeeOther_303:
1868 return "See Other";
1869 case StatusCode::NotModified_304:
1870 return "Not Modified";
1871 case StatusCode::UseProxy_305:
1872 return "Use Proxy";
1873 case StatusCode::unused_306:
1874 return "unused";
1875 case StatusCode::TemporaryRedirect_307:
1876 return "Temporary Redirect";
1877 case StatusCode::PermanentRedirect_308:
1878 return "Permanent Redirect";
1879 case StatusCode::BadRequest_400:
1880 return "Bad Request";
1881 case StatusCode::Unauthorized_401:
1882 return "Unauthorized";
1883 case StatusCode::PaymentRequired_402:
1884 return "Payment Required";
1885 case StatusCode::Forbidden_403:
1886 return "Forbidden";
1887 case StatusCode::NotFound_404:
1888 return "Not Found";
1889 case StatusCode::MethodNotAllowed_405:
1890 return "Method Not Allowed";
1891 case StatusCode::NotAcceptable_406:
1892 return "Not Acceptable";
1893 case StatusCode::ProxyAuthenticationRequired_407:
1894 return "Proxy Authentication Required";
1895 case StatusCode::RequestTimeout_408:
1896 return "Request Timeout";
1897 case StatusCode::Conflict_409:
1898 return "Conflict";
1899 case StatusCode::Gone_410:
1900 return "Gone";
1901 case StatusCode::LengthRequired_411:
1902 return "Length Required";
1903 case StatusCode::PreconditionFailed_412:
1904 return "Precondition Failed";
1905 case StatusCode::PayloadTooLarge_413:
1906 return "Payload Too Large";
1907 case StatusCode::UriTooLong_414:
1908 return "URI Too Long";
1909 case StatusCode::UnsupportedMediaType_415:
1910 return "Unsupported Media Type";
1911 case StatusCode::RangeNotSatisfiable_416:
1912 return "Range Not Satisfiable";
1913 case StatusCode::ExpectationFailed_417:
1914 return "Expectation Failed";
1915 case StatusCode::ImATeapot_418:
1916 return "I'm a teapot";
1917 case StatusCode::MisdirectedRequest_421:
1918 return "Misdirected Request";
1919 case StatusCode::UnprocessableContent_422:
1920 return "Unprocessable Content";
1921 case StatusCode::Locked_423:
1922 return "Locked";
1923 case StatusCode::FailedDependency_424:
1924 return "Failed Dependency";
1925 case StatusCode::TooEarly_425:
1926 return "Too Early";
1927 case StatusCode::UpgradeRequired_426:
1928 return "Upgrade Required";
1929 case StatusCode::PreconditionRequired_428:
1930 return "Precondition Required";
1931 case StatusCode::TooManyRequests_429:
1932 return "Too Many Requests";
1933 case StatusCode::RequestHeaderFieldsTooLarge_431:
1934 return "Request Header Fields Too Large";
1935 case StatusCode::UnavailableForLegalReasons_451:
1936 return "Unavailable For Legal Reasons";
1937 case StatusCode::NotImplemented_501:
1938 return "Not Implemented";
1939 case StatusCode::BadGateway_502:
1940 return "Bad Gateway";
1941 case StatusCode::ServiceUnavailable_503:
1942 return "Service Unavailable";
1943 case StatusCode::GatewayTimeout_504:
1944 return "Gateway Timeout";
1945 case StatusCode::HttpVersionNotSupported_505:
1946 return "HTTP Version Not Supported";
1947 case StatusCode::VariantAlsoNegotiates_506:
1948 return "Variant Also Negotiates";
1949 case StatusCode::InsufficientStorage_507:
1950 return "Insufficient Storage";
1951 case StatusCode::LoopDetected_508:
1952 return "Loop Detected";
1953 case StatusCode::NotExtended_510:
1954 return "Not Extended";
1955 case StatusCode::NetworkAuthenticationRequired_511:
1956 return "Network Authentication Required";
1957
1958 default:
1959 case StatusCode::InternalServerError_500:
1960 return "Internal Server Error";
1961 }
1962}
1963
1964inline std::string get_bearer_token_auth(const Request &req) {
1965 if (req.has_header("Authorization")) {
1966 static std::string BearerHeaderPrefix = "Bearer ";
1967 return req.get_header_value("Authorization").substr(BearerHeaderPrefix.length());
1968 }
1969 return "";
1970}
1971
1972template<class Rep, class Period>
1973inline Server &Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
1974 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
1975 return *this;
1976}
1977
1978template<class Rep, class Period>
1979inline Server &Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
1980 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
1981 return *this;
1982}
1983
1984template<class Rep, class Period>
1985inline Server &Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
1986 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
1987 return *this;
1988}
1989
1990inline std::string to_string(const Error error) {
1991 switch (error) {
1992 case Error::Success:
1993 return "Success (no error)";
1994 case Error::Connection:
1995 return "Could not establish connection";
1996 case Error::BindIPAddress:
1997 return "Failed to bind IP address";
1998 case Error::Read:
1999 return "Failed to read connection";
2000 case Error::Write:
2001 return "Failed to write connection";
2002 case Error::ExceedRedirectCount:
2003 return "Maximum redirect count exceeded";
2004 case Error::Canceled:
2005 return "Connection handling canceled";
2006 case Error::SSLConnection:
2007 return "SSL connection failed";
2008 case Error::SSLLoadingCerts:
2009 return "SSL certificate loading failed";
2010 case Error::SSLServerVerification:
2011 return "SSL server verification failed";
2012 case Error::SSLServerHostnameVerification:
2013 return "SSL server hostname verification failed";
2014 case Error::UnsupportedMultipartBoundaryChars:
2015 return "Unsupported HTTP multipart boundary characters";
2016 case Error::Compression:
2017 return "Compression failed";
2018 case Error::ConnectionTimeout:
2019 return "Connection timed out";
2020 case Error::ProxyConnection:
2021 return "Proxy connection failed";
2022 case Error::Unknown:
2023 return "Unknown";
2024 default:
2025 break;
2026 }
2027
2028 return "Invalid";
2029}
2030
2031inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
2032 os << to_string(obj);
2033 os << " (" << static_cast<std::underlying_type<Error>::type>(obj) << ')';
2034 return os;
2035}
2036
2037inline uint64_t Result::get_request_header_value_u64(const std::string &key, uint64_t def, size_t id) const {
2038 return detail::get_header_value_u64(request_headers_, key, def, id);
2039}
2040
2041template<class Rep, class Period>
2042inline void ClientImpl::set_connection_timeout(const std::chrono::duration<Rep, Period> &duration) {
2043 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { set_connection_timeout(sec, usec); });
2044}
2045
2046template<class Rep, class Period>
2047inline void ClientImpl::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2048 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
2049}
2050
2051template<class Rep, class Period>
2052inline void ClientImpl::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2053 detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2054}
2055
2056template<class Rep, class Period>
2057inline void Client::set_connection_timeout(const std::chrono::duration<Rep, Period> &duration) {
2058 cli_->set_connection_timeout(duration);
2059}
2060
2061template<class Rep, class Period>
2062inline void Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
2063 cli_->set_read_timeout(duration);
2064}
2065
2066template<class Rep, class Period>
2067inline void Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2068 cli_->set_write_timeout(duration);
2069}
2070
2071/*
2072 * Forward declarations and types that will be part of the .h file if split into
2073 * .h + .cc.
2074 */
2075
2076std::string hosted_at(const std::string &hostname);
2077
2078void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
2079
2080std::string append_query_params(const std::string &path, const Params &params);
2081
2082std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
2083
2084std::pair<std::string, std::string> make_basic_authentication_header(const std::string &username,
2085 const std::string &password,
2086 bool is_proxy = false);
2087
2088namespace detail {
2089
2090#if defined(_WIN32)
2091inline std::wstring u8string_to_wstring(const char *s) {
2092 std::wstring ws;
2093 auto len = static_cast<int>(strlen(s));
2094 auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2095 if (wlen > 0) {
2096 ws.resize(wlen);
2097 wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2098 if (wlen != static_cast<int>(ws.size())) {
2099 ws.clear();
2100 }
2101 }
2102 return ws;
2103}
2104#endif
2105
2106struct FileStat {
2107 FileStat(const std::string &path);
2108 bool is_file() const;
2109 bool is_dir() const;
2110
2111 private:
2112#if defined(_WIN32)
2113 struct _stat st_;
2114#else
2115 struct stat st_;
2116#endif
2117 int ret_ = -1;
2118};
2119
2120std::string encode_query_param(const std::string &value);
2121
2122std::string decode_url(const std::string &s, bool convert_plus_to_space);
2123
2124void read_file(const std::string &path, std::string &out);
2125
2126std::string trim_copy(const std::string &s);
2127
2128void divide(const char *data, std::size_t size, char d,
2129 std::function<void(const char *, std::size_t, const char *, std::size_t)> fn);
2130
2131void divide(const std::string &str, char d,
2132 std::function<void(const char *, std::size_t, const char *, std::size_t)> fn);
2133
2134void split(const char *b, const char *e, char d, std::function<void(const char *, const char *)> fn);
2135
2136void split(const char *b, const char *e, char d, size_t m, std::function<void(const char *, const char *)> fn);
2137
2138bool process_client_socket(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
2139 time_t write_timeout_usec, std::function<bool(Stream &)> callback);
2140
2141socket_t create_client_socket(const std::string &host, const std::string &ip, int port, int address_family,
2142 bool tcp_nodelay, bool ipv6_v6only, SocketOptions socket_options,
2143 time_t connection_timeout_sec, time_t connection_timeout_usec, time_t read_timeout_sec,
2144 time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec,
2145 const std::string &intf, Error &error);
2146
2147const char *get_header_value(const Headers &headers, const std::string &key, const char *def, size_t id);
2148
2149std::string params_to_query_str(const Params &params);
2150
2151void parse_query_text(const char *data, std::size_t size, Params &params);
2152
2153void parse_query_text(const std::string &s, Params &params);
2154
2155bool parse_multipart_boundary(const std::string &content_type, std::string &boundary);
2156
2157bool parse_range_header(const std::string &s, Ranges &ranges);
2158
2159int close_socket(socket_t sock);
2160
2161ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2162
2163ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2164
2165enum class EncodingType { None = 0, Gzip, Brotli };
2166
2167EncodingType encoding_type(const Request &req, const Response &res);
2168
2169class BufferStream final : public Stream {
2170 public:
2171 BufferStream() = default;
2172 ~BufferStream() override = default;
2173
2174 bool is_readable() const override;
2175 bool is_writable() const override;
2176 ssize_t read(char *ptr, size_t size) override;
2177 ssize_t write(const char *ptr, size_t size) override;
2178 void get_remote_ip_and_port(std::string &ip, int &port) const override;
2179 void get_local_ip_and_port(std::string &ip, int &port) const override;
2180 socket_t socket() const override;
2181
2182 const std::string &get_buffer() const;
2183
2184 private:
2185 std::string buffer;
2186 size_t position = 0;
2187};
2188
2189class compressor {
2190 public:
2191 virtual ~compressor() = default;
2192
2193 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2194 virtual bool compress(const char *data, size_t data_length, bool last, Callback callback) = 0;
2195};
2196
2197class decompressor {
2198 public:
2199 virtual ~decompressor() = default;
2200
2201 virtual bool is_valid() const = 0;
2202
2203 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2204 virtual bool decompress(const char *data, size_t data_length, Callback callback) = 0;
2205};
2206
2207class nocompressor final : public compressor {
2208 public:
2209 ~nocompressor() override = default;
2210
2211 bool compress(const char *data, size_t data_length, bool /*last*/, Callback callback) override;
2212};
2213
2214#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2215class gzip_compressor final : public compressor {
2216 public:
2217 gzip_compressor();
2218 ~gzip_compressor() override;
2219
2220 bool compress(const char *data, size_t data_length, bool last, Callback callback) override;
2221
2222 private:
2223 bool is_valid_ = false;
2224 z_stream strm_;
2225};
2226
2227class gzip_decompressor final : public decompressor {
2228 public:
2229 gzip_decompressor();
2230 ~gzip_decompressor() override;
2231
2232 bool is_valid() const override;
2233
2234 bool decompress(const char *data, size_t data_length, Callback callback) override;
2235
2236 private:
2237 bool is_valid_ = false;
2238 z_stream strm_;
2239};
2240#endif
2241
2242#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2243class brotli_compressor final : public compressor {
2244 public:
2245 brotli_compressor();
2246 ~brotli_compressor();
2247
2248 bool compress(const char *data, size_t data_length, bool last, Callback callback) override;
2249
2250 private:
2251 BrotliEncoderState *state_ = nullptr;
2252};
2253
2254class brotli_decompressor final : public decompressor {
2255 public:
2256 brotli_decompressor();
2257 ~brotli_decompressor();
2258
2259 bool is_valid() const override;
2260
2261 bool decompress(const char *data, size_t data_length, Callback callback) override;
2262
2263 private:
2264 BrotliDecoderResult decoder_r;
2265 BrotliDecoderState *decoder_s = nullptr;
2266};
2267#endif
2268
2269// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2270// to store data. The call can set memory on stack for performance.
2271class stream_line_reader {
2272 public:
2273 stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size);
2274 const char *ptr() const;
2275 size_t size() const;
2276 bool end_with_crlf() const;
2277 bool getline();
2278
2279 private:
2280 void append(char c);
2281
2282 Stream &strm_;
2283 char *fixed_buffer_;
2284 const size_t fixed_buffer_size_;
2285 size_t fixed_buffer_used_size_ = 0;
2286 std::string glowable_buffer_;
2287};
2288
2289class mmap {
2290 public:
2291 mmap(const char *path);
2292 ~mmap();
2293
2294 bool open(const char *path);
2295 void close();
2296
2297 bool is_open() const;
2298 size_t size() const;
2299 const char *data() const;
2300
2301 private:
2302#if defined(_WIN32)
2303 HANDLE hFile_ = NULL;
2304 HANDLE hMapping_ = NULL;
2305#else
2306 int fd_ = -1;
2307#endif
2308 size_t size_ = 0;
2309 void *addr_ = nullptr;
2310 bool is_open_empty_file = false;
2311};
2312
2313} // namespace detail
2314
2315// ----------------------------------------------------------------------------
2316
2317/*
2318 * Implementation that will be part of the .cc file if split into .h + .cc.
2319 */
2320
2321namespace detail {
2322
2323inline bool is_hex(char c, int &v) {
2324 if (0x20 <= c && isdigit(c)) {
2325 v = c - '0';
2326 return true;
2327 } else if ('A' <= c && c <= 'F') {
2328 v = c - 'A' + 10;
2329 return true;
2330 } else if ('a' <= c && c <= 'f') {
2331 v = c - 'a' + 10;
2332 return true;
2333 }
2334 return false;
2335}
2336
2337inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) {
2338 if (i >= s.size()) {
2339 return false;
2340 }
2341
2342 val = 0;
2343 for (; cnt; i++, cnt--) {
2344 if (!s[i]) {
2345 return false;
2346 }
2347 auto v = 0;
2348 if (is_hex(s[i], v)) {
2349 val = val * 16 + v;
2350 } else {
2351 return false;
2352 }
2353 }
2354 return true;
2355}
2356
2357inline std::string from_i_to_hex(size_t n) {
2358 static const auto charset = "0123456789abcdef";
2359 std::string ret;
2360 do {
2361 ret = charset[n & 15] + ret;
2362 n >>= 4;
2363 } while (n > 0);
2364 return ret;
2365}
2366
2367inline size_t to_utf8(int code, char *buff) {
2368 if (code < 0x0080) {
2369 buff[0] = static_cast<char>(code & 0x7F);
2370 return 1;
2371 } else if (code < 0x0800) {
2372 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
2373 buff[1] = static_cast<char>(0x80 | (code & 0x3F));
2374 return 2;
2375 } else if (code < 0xD800) {
2376 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2377 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2378 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
2379 return 3;
2380 } else if (code < 0xE000) { // D800 - DFFF is invalid...
2381 return 0;
2382 } else if (code < 0x10000) {
2383 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
2384 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2385 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
2386 return 3;
2387 } else if (code < 0x110000) {
2388 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
2389 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
2390 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
2391 buff[3] = static_cast<char>(0x80 | (code & 0x3F));
2392 return 4;
2393 }
2394
2395 // NOTREACHED
2396 return 0;
2397}
2398
2399// NOTE: This code came up with the following stackoverflow post:
2400// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
2401inline std::string base64_encode(const std::string &in) {
2402 static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2403
2404 std::string out;
2405 out.reserve(in.size());
2406
2407 auto val = 0;
2408 auto valb = -6;
2409
2410 for (auto c : in) {
2412 valb += 8;
2413 while (valb >= 0) {
2414 out.push_back(lookup[(val >> valb) & 0x3F]);
2415 valb -= 6;
2416 }
2417 }
2418
2419 if (valb > -6) {
2420 out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]);
2421 }
2422
2423 while (out.size() % 4) {
2424 out.push_back('=');
2425 }
2426
2427 return out;
2428}
2429
2430inline bool is_valid_path(const std::string &path) {
2431 size_t level = 0;
2432 size_t i = 0;
2433
2434 // Skip slash
2435 while (i < path.size() && path[i] == '/') {
2436 i++;
2437 }
2438
2439 while (i < path.size()) {
2440 // Read component
2441 auto beg = i;
2442 while (i < path.size() && path[i] != '/') {
2443 if (path[i] == '\0') {
2444 return false;
2445 } else if (path[i] == '\\') {
2446 return false;
2447 }
2448 i++;
2449 }
2450
2451 auto len = i - beg;
2452 assert(len > 0);
2453
2454 if (!path.compare(beg, len, ".")) {
2455 ;
2456 } else if (!path.compare(beg, len, "..")) {
2457 if (level == 0) {
2458 return false;
2459 }
2460 level--;
2461 } else {
2462 level++;
2463 }
2464
2465 // Skip slash
2466 while (i < path.size() && path[i] == '/') {
2467 i++;
2468 }
2469 }
2470
2471 return true;
2472}
2473
2474inline FileStat::FileStat(const std::string &path) {
2475#if defined(_WIN32)
2476 auto wpath = u8string_to_wstring(path.c_str());
2477 ret_ = _wstat(wpath.c_str(), &st_);
2478#else
2479 ret_ = stat(path.c_str(), &st_);
2480#endif
2481}
2482inline bool FileStat::is_file() const { return ret_ >= 0 && S_ISREG(st_.st_mode); }
2483inline bool FileStat::is_dir() const { return ret_ >= 0 && S_ISDIR(st_.st_mode); }
2484
2485inline std::string encode_query_param(const std::string &value) {
2486 std::ostringstream escaped;
2487 escaped.fill('0');
2488 escaped << std::hex;
2489
2490 for (auto c : value) {
2491 if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' ||
2492 c == '\'' || c == '(' || c == ')') {
2493 escaped << c;
2494 } else {
2495 escaped << std::uppercase;
2496 escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
2497 escaped << std::nouppercase;
2498 }
2499 }
2500
2501 return escaped.str();
2502}
2503
2504inline std::string encode_url(const std::string &s) {
2505 std::string result;
2506 result.reserve(s.size());
2507
2508 for (size_t i = 0; s[i]; i++) {
2509 switch (s[i]) {
2510 case ' ':
2511 result += "%20";
2512 break;
2513 case '+':
2514 result += "%2B";
2515 break;
2516 case '\r':
2517 result += "%0D";
2518 break;
2519 case '\n':
2520 result += "%0A";
2521 break;
2522 case '\'':
2523 result += "%27";
2524 break;
2525 case ',':
2526 result += "%2C";
2527 break;
2528 // case ':': result += "%3A"; break; // ok? probably...
2529 case ';':
2530 result += "%3B";
2531 break;
2532 default:
2533 auto c = static_cast<uint8_t>(s[i]);
2534 if (c >= 0x80) {
2535 result += '%';
2536 char hex[4];
2537 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
2538 assert(len == 2);
2539 result.append(hex, static_cast<size_t>(len));
2540 } else {
2541 result += s[i];
2542 }
2543 break;
2544 }
2545 }
2546
2547 return result;
2548}
2549
2550inline std::string decode_url(const std::string &s, bool convert_plus_to_space) {
2551 std::string result;
2552
2553 for (size_t i = 0; i < s.size(); i++) {
2554 if (s[i] == '%' && i + 1 < s.size()) {
2555 if (s[i + 1] == 'u') {
2556 auto val = 0;
2557 if (from_hex_to_i(s, i + 2, 4, val)) {
2558 // 4 digits Unicode codes
2559 char buff[4];
2560 size_t len = to_utf8(val, buff);
2561 if (len > 0) {
2562 result.append(buff, len);
2563 }
2564 i += 5; // 'u0000'
2565 } else {
2566 result += s[i];
2567 }
2568 } else {
2569 auto val = 0;
2570 if (from_hex_to_i(s, i + 1, 2, val)) {
2571 // 2 digits hex codes
2572 result += static_cast<char>(val);
2573 i += 2; // '00'
2574 } else {
2575 result += s[i];
2576 }
2577 }
2578 } else if (convert_plus_to_space && s[i] == '+') {
2579 result += ' ';
2580 } else {
2581 result += s[i];
2582 }
2583 }
2584
2585 return result;
2586}
2587
2588inline void read_file(const std::string &path, std::string &out) {
2589 std::ifstream fs(path, std::ios_base::binary);
2590 fs.seekg(0, std::ios_base::end);
2591 auto size = fs.tellg();
2592 fs.seekg(0);
2593 out.resize(static_cast<size_t>(size));
2594 fs.read(&out[0], static_cast<std::streamsize>(size));
2595}
2596
2597inline std::string file_extension(const std::string &path) {
2598 std::smatch m;
2599 static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
2600 if (std::regex_search(path, m, re)) {
2601 return m[1].str();
2602 }
2603 return std::string();
2604}
2605
2606inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
2607
2608inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left, size_t right) {
2609 while (b + left < e && is_space_or_tab(b[left])) {
2610 left++;
2611 }
2612 while (right > 0 && is_space_or_tab(b[right - 1])) {
2613 right--;
2614 }
2615 return std::make_pair(left, right);
2616}
2617
2618inline std::string trim_copy(const std::string &s) {
2619 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
2620 return s.substr(r.first, r.second - r.first);
2621}
2622
2623inline std::string trim_double_quotes_copy(const std::string &s) {
2624 if (s.length() >= 2 && s.front() == '"' && s.back() == '"') {
2625 return s.substr(1, s.size() - 2);
2626 }
2627 return s;
2628}
2629
2630inline void divide(const char *data, std::size_t size, char d,
2631 std::function<void(const char *, std::size_t, const char *, std::size_t)> fn) {
2632 const auto it = std::find(data, data + size, d);
2633 const auto found = static_cast<std::size_t>(it != data + size);
2634 const auto lhs_data = data;
2635 const auto lhs_size = static_cast<std::size_t>(it - data);
2636 const auto rhs_data = it + found;
2637 const auto rhs_size = size - lhs_size - found;
2638
2639 fn(lhs_data, lhs_size, rhs_data, rhs_size);
2640}
2641
2642inline void divide(const std::string &str, char d,
2643 std::function<void(const char *, std::size_t, const char *, std::size_t)> fn) {
2644 divide(str.data(), str.size(), d, std::move(fn));
2645}
2646
2647inline void split(const char *b, const char *e, char d, std::function<void(const char *, const char *)> fn) {
2648 return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
2649}
2650
2651inline void split(const char *b, const char *e, char d, size_t m, std::function<void(const char *, const char *)> fn) {
2652 size_t i = 0;
2653 size_t beg = 0;
2654 size_t count = 1;
2655
2656 while (e ? (b + i < e) : (b[i] != '\0')) {
2657 if (b[i] == d && count < m) {
2658 auto r = trim(b, e, beg, i);
2659 if (r.first < r.second) {
2660 fn(&b[r.first], &b[r.second]);
2661 }
2662 beg = i + 1;
2663 count++;
2664 }
2665 i++;
2666 }
2667
2668 if (i) {
2669 auto r = trim(b, e, beg, i);
2670 if (r.first < r.second) {
2671 fn(&b[r.first], &b[r.second]);
2672 }
2673 }
2674}
2675
2676inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size)
2677 : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {}
2678
2679inline const char *stream_line_reader::ptr() const {
2680 if (glowable_buffer_.empty()) {
2681 return fixed_buffer_;
2682 } else {
2683 return glowable_buffer_.data();
2684 }
2685}
2686
2687inline size_t stream_line_reader::size() const {
2688 if (glowable_buffer_.empty()) {
2689 return fixed_buffer_used_size_;
2690 } else {
2691 return glowable_buffer_.size();
2692 }
2693}
2694
2695inline bool stream_line_reader::end_with_crlf() const {
2696 auto end = ptr() + size();
2697 return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
2698}
2699
2700inline bool stream_line_reader::getline() {
2701 fixed_buffer_used_size_ = 0;
2702 glowable_buffer_.clear();
2703
2704#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2705 char prev_byte = 0;
2706#endif
2707
2708 for (size_t i = 0;; i++) {
2709 char byte;
2710 auto n = strm_.read(&byte, 1);
2711
2712 if (n < 0) {
2713 return false;
2714 } else if (n == 0) {
2715 if (i == 0) {
2716 return false;
2717 } else {
2718 break;
2719 }
2720 }
2721
2722 append(byte);
2723
2724#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2725 if (byte == '\n') {
2726 break;
2727 }
2728#else
2729 if (prev_byte == '\r' && byte == '\n') {
2730 break;
2731 }
2732 prev_byte = byte;
2733#endif
2734 }
2735
2736 return true;
2737}
2738
2739inline void stream_line_reader::append(char c) {
2740 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
2741 fixed_buffer_[fixed_buffer_used_size_++] = c;
2742 fixed_buffer_[fixed_buffer_used_size_] = '\0';
2743 } else {
2744 if (glowable_buffer_.empty()) {
2745 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
2746 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
2747 }
2748 glowable_buffer_ += c;
2749 }
2750}
2751
2752inline mmap::mmap(const char *path) { open(path); }
2753
2754inline mmap::~mmap() { close(); }
2755
2756inline bool mmap::open(const char *path) {
2757 close();
2758
2759#if defined(_WIN32)
2760 auto wpath = u8string_to_wstring(path);
2761 if (wpath.empty()) {
2762 return false;
2763 }
2764
2765#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2766 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, NULL);
2767#else
2768 hFile_ =
2769 ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2770#endif
2771
2772 if (hFile_ == INVALID_HANDLE_VALUE) {
2773 return false;
2774 }
2775
2776 LARGE_INTEGER size{};
2777 if (!::GetFileSizeEx(hFile_, &size)) {
2778 return false;
2779 }
2780 // If the following line doesn't compile due to QuadPart, update Windows SDK.
2781 // See:
2782 // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
2783 if (static_cast<ULONGLONG>(size.QuadPart) > (std::numeric_limits<decltype(size_)>::max)()) {
2784 // `size_t` might be 32-bits, on 32-bits Windows.
2785 return false;
2786 }
2787 size_ = static_cast<size_t>(size.QuadPart);
2788
2789#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2790 hMapping_ = ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
2791#else
2792 hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
2793#endif
2794
2795 // Special treatment for an empty file...
2796 if (hMapping_ == NULL && size_ == 0) {
2797 close();
2798 is_open_empty_file = true;
2799 return true;
2800 }
2801
2802 if (hMapping_ == NULL) {
2803 close();
2804 return false;
2805 }
2806
2807#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2808 addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
2809#else
2810 addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
2811#endif
2812
2813 if (addr_ == nullptr) {
2814 close();
2815 return false;
2816 }
2817#else
2818 fd_ = ::open(path, O_RDONLY);
2819 if (fd_ == -1) {
2820 return false;
2821 }
2822
2823 struct stat sb;
2824 if (fstat(fd_, &sb) == -1) {
2825 close();
2826 return false;
2827 }
2828 size_ = static_cast<size_t>(sb.st_size);
2829
2830 addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
2831
2832 // Special treatment for an empty file...
2833 if (addr_ == MAP_FAILED && size_ == 0) {
2834 close();
2835 is_open_empty_file = true;
2836 return false;
2837 }
2838#endif
2839
2840 return true;
2841}
2842
2843inline bool mmap::is_open() const { return is_open_empty_file ? true : addr_ != nullptr; }
2844
2845inline size_t mmap::size() const { return size_; }
2846
2847inline const char *mmap::data() const { return is_open_empty_file ? "" : static_cast<const char *>(addr_); }
2848
2849inline void mmap::close() {
2850#if defined(_WIN32)
2851 if (addr_) {
2852 ::UnmapViewOfFile(addr_);
2853 addr_ = nullptr;
2854 }
2855
2856 if (hMapping_) {
2857 ::CloseHandle(hMapping_);
2858 hMapping_ = NULL;
2859 }
2860
2861 if (hFile_ != INVALID_HANDLE_VALUE) {
2862 ::CloseHandle(hFile_);
2863 hFile_ = INVALID_HANDLE_VALUE;
2864 }
2865
2866 is_open_empty_file = false;
2867#else
2868 if (addr_ != nullptr) {
2869 munmap(addr_, size_);
2870 addr_ = nullptr;
2871 }
2872
2873 if (fd_ != -1) {
2874 ::close(fd_);
2875 fd_ = -1;
2876 }
2877#endif
2878 size_ = 0;
2879}
2880inline int close_socket(socket_t sock) {
2881#ifdef _WIN32
2882 return closesocket(sock);
2883#else
2884 return close(sock);
2885#endif
2886}
2887
2888template<typename T> inline ssize_t handle_EINTR(T fn) {
2889 ssize_t res = 0;
2890 while (true) {
2891 res = fn();
2892 if (res < 0 && errno == EINTR) {
2893 std::this_thread::sleep_for(std::chrono::microseconds{1});
2894 continue;
2895 }
2896 break;
2897 }
2898 return res;
2899}
2900
2901inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
2902 return handle_EINTR([&]() {
2903 return recv(sock,
2904#ifdef _WIN32
2905 static_cast<char *>(ptr), static_cast<int>(size),
2906#else
2907 ptr, size,
2908#endif
2909 flags);
2910 });
2911}
2912
2913inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags) {
2914 return handle_EINTR([&]() {
2915 return send(sock,
2916#ifdef _WIN32
2917 static_cast<const char *>(ptr), static_cast<int>(size),
2918#else
2919 ptr, size,
2920#endif
2921 flags);
2922 });
2923}
2924
2925inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
2926#ifdef CPPHTTPLIB_USE_POLL
2927 struct pollfd pfd_read;
2928 pfd_read.fd = sock;
2929 pfd_read.events = POLLIN;
2930
2931 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
2932
2933 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
2934#else
2935#ifndef _WIN32
2936 if (sock >= FD_SETSIZE) {
2937 return -1;
2938 }
2939#endif
2940
2941 fd_set fds;
2942 FD_ZERO(&fds);
2943 FD_SET(sock, &fds);
2944
2945 timeval tv;
2946 tv.tv_sec = static_cast<long>(sec);
2947 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
2948
2949 return handle_EINTR([&]() { return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv); });
2950#endif
2951}
2952
2953inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
2954#ifdef CPPHTTPLIB_USE_POLL
2955 struct pollfd pfd_read;
2956 pfd_read.fd = sock;
2957 pfd_read.events = POLLOUT;
2958
2959 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
2960
2961 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
2962#else
2963#ifndef _WIN32
2964 if (sock >= FD_SETSIZE) {
2965 return -1;
2966 }
2967#endif
2968
2969 fd_set fds;
2970 FD_ZERO(&fds);
2971 FD_SET(sock, &fds);
2972
2973 timeval tv;
2974 tv.tv_sec = static_cast<long>(sec);
2975 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
2976
2977 return handle_EINTR([&]() { return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv); });
2978#endif
2979}
2980
2981inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
2982#ifdef CPPHTTPLIB_USE_POLL
2983 struct pollfd pfd_read;
2984 pfd_read.fd = sock;
2985 pfd_read.events = POLLIN | POLLOUT;
2986
2987 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
2988
2989 auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
2990
2991 if (poll_res == 0) {
2992 return Error::ConnectionTimeout;
2993 }
2994
2995 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
2996 auto error = 0;
2997 socklen_t len = sizeof(error);
2998 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &len);
2999 auto successful = res >= 0 && !error;
3000 return successful ? Error::Success : Error::Connection;
3001 }
3002
3003 return Error::Connection;
3004#else
3005#ifndef _WIN32
3006 if (sock >= FD_SETSIZE) {
3007 return Error::Connection;
3008 }
3009#endif
3010
3011 fd_set fdsr;
3012 FD_ZERO(&fdsr);
3013 FD_SET(sock, &fdsr);
3014
3015 auto fdsw = fdsr;
3016 auto fdse = fdsr;
3017
3018 timeval tv;
3019 tv.tv_sec = static_cast<long>(sec);
3020 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3021
3022 auto ret = handle_EINTR([&]() { return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv); });
3023
3024 if (ret == 0) {
3025 return Error::ConnectionTimeout;
3026 }
3027
3028 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3029 auto error = 0;
3030 socklen_t len = sizeof(error);
3031 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &len);
3032 auto successful = res >= 0 && !error;
3033 return successful ? Error::Success : Error::Connection;
3034 }
3035 return Error::Connection;
3036#endif
3037}
3038
3039inline bool is_socket_alive(socket_t sock) {
3040 const auto val = detail::select_read(sock, 0, 0);
3041 if (val == 0) {
3042 return true;
3043 } else if (val < 0 && errno == EBADF) {
3044 return false;
3045 }
3046 char buf[1];
3047 return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
3048}
3049
3050class SocketStream final : public Stream {
3051 public:
3052 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3053 time_t write_timeout_usec);
3054 ~SocketStream() override;
3055
3056 bool is_readable() const override;
3057 bool is_writable() const override;
3058 ssize_t read(char *ptr, size_t size) override;
3059 ssize_t write(const char *ptr, size_t size) override;
3060 void get_remote_ip_and_port(std::string &ip, int &port) const override;
3061 void get_local_ip_and_port(std::string &ip, int &port) const override;
3062 socket_t socket() const override;
3063
3064 private:
3065 socket_t sock_;
3066 time_t read_timeout_sec_;
3067 time_t read_timeout_usec_;
3068 time_t write_timeout_sec_;
3069 time_t write_timeout_usec_;
3070
3071 std::vector<char> read_buff_;
3072 size_t read_buff_off_ = 0;
3073 size_t read_buff_content_size_ = 0;
3074
3075 static const size_t read_buff_size_ = 1024l * 4;
3076};
3077
3078#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3079class SSLSocketStream final : public Stream {
3080 public:
3081 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3082 time_t write_timeout_usec);
3083 ~SSLSocketStream() override;
3084
3085 bool is_readable() const override;
3086 bool is_writable() const override;
3087 ssize_t read(char *ptr, size_t size) override;
3088 ssize_t write(const char *ptr, size_t size) override;
3089 void get_remote_ip_and_port(std::string &ip, int &port) const override;
3090 void get_local_ip_and_port(std::string &ip, int &port) const override;
3091 socket_t socket() const override;
3092
3093 private:
3094 socket_t sock_;
3095 SSL *ssl_;
3096 time_t read_timeout_sec_;
3097 time_t read_timeout_usec_;
3098 time_t write_timeout_sec_;
3099 time_t write_timeout_usec_;
3100};
3101#endif
3102
3103inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock, time_t keep_alive_timeout_sec) {
3104 using namespace std::chrono;
3105
3106 const auto interval_usec = CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
3107
3108 // Avoid expensive `steady_clock::now()` call for the first time
3109 if (select_read(sock, 0, interval_usec) > 0) {
3110 return true;
3111 }
3112
3113 const auto start = steady_clock::now() - microseconds{interval_usec};
3114 const auto timeout = seconds{keep_alive_timeout_sec};
3115
3116 while (true) {
3117 if (svr_sock == INVALID_SOCKET) {
3118 break; // Server socket is closed
3119 }
3120
3121 auto val = select_read(sock, 0, interval_usec);
3122 if (val < 0) {
3123 break; // Ssocket error
3124 } else if (val == 0) {
3125 if (steady_clock::now() - start > timeout) {
3126 break; // Timeout
3127 }
3128 } else {
3129 return true; // Ready for read
3130 }
3131 }
3132
3133 return false;
3134}
3135
3136template<typename T>
3137inline bool process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
3138 size_t keep_alive_max_count, time_t keep_alive_timeout_sec, T callback) {
3139 assert(keep_alive_max_count > 0);
3140 auto ret = false;
3141 auto count = keep_alive_max_count;
3142 while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3143 auto close_connection = count == 1;
3144 auto connection_closed = false;
3145 ret = callback(close_connection, connection_closed);
3146 if (!ret || connection_closed) {
3147 break;
3148 }
3149 count--;
3150 }
3151 return ret;
3152}
3153
3154template<typename T>
3155inline bool process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock, size_t keep_alive_max_count,
3156 time_t keep_alive_timeout_sec, time_t read_timeout_sec, time_t read_timeout_usec,
3157 time_t write_timeout_sec, time_t write_timeout_usec, T callback) {
3158 return process_server_socket_core(svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
3159 [&](bool close_connection, bool &connection_closed) {
3160 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec,
3161 write_timeout_usec);
3162 return callback(strm, close_connection, connection_closed);
3163 });
3164}
3165
3166inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3167 time_t write_timeout_sec, time_t write_timeout_usec,
3168 std::function<bool(Stream &)> callback) {
3169 SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);
3170 return callback(strm);
3171}
3172
3173inline int shutdown_socket(socket_t sock) {
3174#ifdef _WIN32
3175 return shutdown(sock, SD_BOTH);
3176#else
3177 return shutdown(sock, SHUT_RDWR);
3178#endif
3179}
3180
3181inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
3182 if (s.size() > 1 && s[0] == '\0') {
3183 auto ret = s;
3184 ret[0] = '@';
3185 return ret;
3186 }
3187 return s;
3188}
3189
3190inline std::string unescape_abstract_namespace_unix_domain(const std::string &s) {
3191 if (s.size() > 1 && s[0] == '@') {
3192 auto ret = s;
3193 ret[0] = '\0';
3194 return ret;
3195 }
3196 return s;
3197}
3198
3199template<typename BindOrConnect>
3200socket_t create_socket(const std::string &host, const std::string &ip, int port, int address_family, int socket_flags,
3201 bool tcp_nodelay, bool ipv6_v6only, SocketOptions socket_options,
3202 BindOrConnect bind_or_connect) {
3203 // Get address info
3204 const char *node = nullptr;
3205 struct addrinfo hints;
3206 struct addrinfo *result;
3207
3208 memset(&hints, 0, sizeof(struct addrinfo));
3209 hints.ai_socktype = SOCK_STREAM;
3210 hints.ai_protocol = IPPROTO_IP;
3211
3212 if (!ip.empty()) {
3213 node = ip.c_str();
3214 // Ask getaddrinfo to convert IP in c-string to address
3215 hints.ai_family = AF_UNSPEC;
3216 hints.ai_flags = AI_NUMERICHOST;
3217 } else {
3218 if (!host.empty()) {
3219 node = host.c_str();
3220 }
3221 hints.ai_family = address_family;
3222 hints.ai_flags = socket_flags;
3223 }
3224
3225#ifndef _WIN32
3226 if (hints.ai_family == AF_UNIX) {
3227 const auto addrlen = host.length();
3228 if (addrlen > sizeof(sockaddr_un::sun_path)) {
3229 return INVALID_SOCKET;
3230 }
3231
3232#ifdef SOCK_CLOEXEC
3233 auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC, hints.ai_protocol);
3234#else
3235 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3236#endif
3237
3238 if (sock != INVALID_SOCKET) {
3239 sockaddr_un addr{};
3240 addr.sun_family = AF_UNIX;
3241
3242 auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
3243 std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3244
3245 hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
3246 hints.ai_addrlen = static_cast<socklen_t>(sizeof(addr) - sizeof(addr.sun_path) + addrlen);
3247
3248#ifndef SOCK_CLOEXEC
3249 fcntl(sock, F_SETFD, FD_CLOEXEC);
3250#endif
3251
3252 if (socket_options) {
3253 socket_options(sock);
3254 }
3255
3256 bool dummy;
3257 if (!bind_or_connect(sock, hints, dummy)) {
3258 close_socket(sock);
3259 sock = INVALID_SOCKET;
3260 }
3261 }
3262 return sock;
3263 }
3264#endif
3265
3266 auto service = std::to_string(port);
3267
3268 if (getaddrinfo(node, service.c_str(), &hints, &result)) {
3269#if defined __linux__ && !defined __ANDROID__
3270 res_init();
3271#endif
3272 return INVALID_SOCKET;
3273 }
3274 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3275
3276 for (auto rp = result; rp; rp = rp->ai_next) {
3277 // Create a socket
3278#ifdef _WIN32
3279 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
3280 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
3295 if (sock == INVALID_SOCKET) {
3296 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3297 }
3298#else
3299
3300#ifdef SOCK_CLOEXEC
3301 auto sock = socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3302#else
3303 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3304#endif
3305
3306#endif
3307 if (sock == INVALID_SOCKET) {
3308 continue;
3309 }
3310
3311#if !defined _WIN32 && !defined SOCK_CLOEXEC
3312 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3313 close_socket(sock);
3314 continue;
3315 }
3316#endif
3317
3318 if (tcp_nodelay) {
3319 auto opt = 1;
3320#ifdef _WIN32
3321 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&opt), sizeof(opt));
3322#else
3323 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const void *>(&opt), sizeof(opt));
3324#endif
3325 }
3326
3327 if (rp->ai_family == AF_INET6) {
3328 auto opt = ipv6_v6only ? 1 : 0;
3329#ifdef _WIN32
3330 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&opt), sizeof(opt));
3331#else
3332 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const void *>(&opt), sizeof(opt));
3333#endif
3334 }
3335
3336 if (socket_options) {
3337 socket_options(sock);
3338 }
3339
3340 // bind or connect
3341 auto quit = false;
3342 if (bind_or_connect(sock, *rp, quit)) {
3343 return sock;
3344 }
3345
3346 close_socket(sock);
3347
3348 if (quit) {
3349 break;
3350 }
3351 }
3352
3353 return INVALID_SOCKET;
3354}
3355
3356inline void set_nonblocking(socket_t sock, bool nonblocking) {
3357#ifdef _WIN32
3358 auto flags = nonblocking ? 1UL : 0UL;
3359 ioctlsocket(sock, FIONBIO, &flags);
3360#else
3361 auto flags = fcntl(sock, F_GETFL, 0);
3362 fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
3363#endif
3364}
3365
3366inline bool is_connection_error() {
3367#ifdef _WIN32
3368 return WSAGetLastError() != WSAEWOULDBLOCK;
3369#else
3370 return errno != EINPROGRESS;
3371#endif
3372}
3373
3374inline bool bind_ip_address(socket_t sock, const std::string &host) {
3375 struct addrinfo hints;
3376 struct addrinfo *result;
3377
3378 memset(&hints, 0, sizeof(struct addrinfo));
3379 hints.ai_family = AF_UNSPEC;
3380 hints.ai_socktype = SOCK_STREAM;
3381 hints.ai_protocol = 0;
3382
3383 if (getaddrinfo(host.c_str(), "0", &hints, &result)) {
3384 return false;
3385 }
3386 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3387
3388 auto ret = false;
3389 for (auto rp = result; rp; rp = rp->ai_next) {
3390 const auto &ai = *rp;
3391 if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
3392 ret = true;
3393 break;
3394 }
3395 }
3396
3397 return ret;
3398}
3399
3400#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
3401#define USE_IF2IP
3402#endif
3403
3404#ifdef USE_IF2IP
3405inline std::string if2ip(int address_family, const std::string &ifn) {
3406 struct ifaddrs *ifap;
3407 getifaddrs(&ifap);
3408 auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
3409
3410 std::string addr_candidate;
3411 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3412 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
3413 (AF_UNSPEC == address_family || ifa->ifa_addr->sa_family == address_family)) {
3414 if (ifa->ifa_addr->sa_family == AF_INET) {
3415 auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
3416 char buf[INET_ADDRSTRLEN];
3417 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3418 return std::string(buf, INET_ADDRSTRLEN);
3419 }
3420 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
3421 auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
3422 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
3423 char buf[INET6_ADDRSTRLEN] = {};
3424 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
3425 // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
3426 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
3427 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3428 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3429 } else {
3430 return std::string(buf, INET6_ADDRSTRLEN);
3431 }
3432 }
3433 }
3434 }
3435 }
3436 }
3437 return addr_candidate;
3438}
3439#endif
3440
3441inline socket_t create_client_socket(const std::string &host, const std::string &ip, int port, int address_family,
3442 bool tcp_nodelay, bool ipv6_v6only, SocketOptions socket_options,
3443 time_t connection_timeout_sec, time_t connection_timeout_usec,
3444 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3445 time_t write_timeout_usec, const std::string &intf, Error &error) {
3446 auto sock = create_socket(
3447 host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only, std::move(socket_options),
3448 [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
3449 if (!intf.empty()) {
3450#ifdef USE_IF2IP
3451 auto ip_from_if = if2ip(address_family, intf);
3452 if (ip_from_if.empty()) {
3453 ip_from_if = intf;
3454 }
3455 if (!bind_ip_address(sock2, ip_from_if)) {
3456 error = Error::BindIPAddress;
3457 return false;
3458 }
3459#endif
3460 }
3461
3462 set_nonblocking(sock2, true);
3463
3464 auto ret = ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
3465
3466 if (ret < 0) {
3467 if (is_connection_error()) {
3468 error = Error::Connection;
3469 return false;
3470 }
3471 error = wait_until_socket_is_ready(sock2, connection_timeout_sec, connection_timeout_usec);
3472 if (error != Error::Success) {
3473 if (error == Error::ConnectionTimeout) {
3474 quit = true;
3475 }
3476 return false;
3477 }
3478 }
3479
3480 set_nonblocking(sock2, false);
3481
3482 {
3483#ifdef _WIN32
3484 auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 + read_timeout_usec / 1000);
3485 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char *>(&timeout), sizeof(timeout));
3486#else
3487 timeval tv;
3488 tv.tv_sec = static_cast<long>(read_timeout_sec);
3489 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
3490 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const void *>(&tv), sizeof(tv));
3491#endif
3492 }
3493 {
3494
3495#ifdef _WIN32
3496 auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 + write_timeout_usec / 1000);
3497 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char *>(&timeout), sizeof(timeout));
3498#else
3499 timeval tv;
3500 tv.tv_sec = static_cast<long>(write_timeout_sec);
3501 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
3502 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const void *>(&tv), sizeof(tv));
3503#endif
3504 }
3505
3506 error = Error::Success;
3507 return true;
3508 });
3509
3510 if (sock != INVALID_SOCKET) {
3511 error = Error::Success;
3512 } else {
3513 if (error == Error::Success) {
3514 error = Error::Connection;
3515 }
3516 }
3517
3518 return sock;
3519}
3520
3521inline bool get_ip_and_port(const struct sockaddr_storage &addr, socklen_t addr_len, std::string &ip, int &port) {
3522 if (addr.ss_family == AF_INET) {
3523 port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
3524 } else if (addr.ss_family == AF_INET6) {
3525 port = ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
3526 } else {
3527 return false;
3528 }
3529
3530 std::array<char, NI_MAXHOST> ipstr{};
3531 if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len, ipstr.data(),
3532 static_cast<socklen_t>(ipstr.size()), nullptr, 0, NI_NUMERICHOST)) {
3533 return false;
3534 }
3535
3536 ip = ipstr.data();
3537 return true;
3538}
3539
3540inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
3541 struct sockaddr_storage addr;
3542 socklen_t addr_len = sizeof(addr);
3543 if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr), &addr_len)) {
3544 get_ip_and_port(addr, addr_len, ip, port);
3545 }
3546}
3547
3548inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
3549 struct sockaddr_storage addr;
3550 socklen_t addr_len = sizeof(addr);
3551
3552 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &addr_len)) {
3553#ifndef _WIN32
3554 if (addr.ss_family == AF_UNIX) {
3555#if defined(__linux__)
3556 struct ucred ucred;
3557 socklen_t len = sizeof(ucred);
3558 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
3559 port = ucred.pid;
3560 }
3561#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
3562 pid_t pid;
3563 socklen_t len = sizeof(pid);
3564 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
3565 port = pid;
3566 }
3567#endif
3568 return;
3569 }
3570#endif
3571 get_ip_and_port(addr, addr_len, ip, port);
3572 }
3573}
3574
3575inline constexpr unsigned int str2tag_core(const char *s, size_t l, unsigned int h) {
3576 return (l == 0) ? h
3577 : str2tag_core(
3578 s + 1, l - 1,
3579 // Unsets the 6 high bits of h, therefore no overflow happens
3580 (((std::numeric_limits<unsigned int>::max)() >> 6) & h * 33) ^ static_cast<unsigned char>(*s));
3581}
3582
3583inline unsigned int str2tag(const std::string &s) { return str2tag_core(s.data(), s.size(), 0); }
3584
3585namespace udl {
3586
3587inline constexpr unsigned int operator""_t(const char *s, size_t l) { return str2tag_core(s, l, 0); }
3588
3589} // namespace udl
3590
3591inline std::string find_content_type(const std::string &path, const std::map<std::string, std::string> &user_data,
3592 const std::string &default_content_type) {
3593 auto ext = file_extension(path);
3594
3595 auto it = user_data.find(ext);
3596 if (it != user_data.end()) {
3597 return it->second;
3598 }
3599
3600 using udl::operator""_t;
3601
3602 switch (str2tag(ext)) {
3603 default:
3604 return default_content_type;
3605
3606 case "css"_t:
3607 return "text/css";
3608 case "csv"_t:
3609 return "text/csv";
3610 case "htm"_t:
3611 case "html"_t:
3612 return "text/html";
3613 case "js"_t:
3614 case "mjs"_t:
3615 return "text/javascript";
3616 case "txt"_t:
3617 return "text/plain";
3618 case "vtt"_t:
3619 return "text/vtt";
3620
3621 case "apng"_t:
3622 return "image/apng";
3623 case "avif"_t:
3624 return "image/avif";
3625 case "bmp"_t:
3626 return "image/bmp";
3627 case "gif"_t:
3628 return "image/gif";
3629 case "png"_t:
3630 return "image/png";
3631 case "svg"_t:
3632 return "image/svg+xml";
3633 case "webp"_t:
3634 return "image/webp";
3635 case "ico"_t:
3636 return "image/x-icon";
3637 case "tif"_t:
3638 return "image/tiff";
3639 case "tiff"_t:
3640 return "image/tiff";
3641 case "jpg"_t:
3642 case "jpeg"_t:
3643 return "image/jpeg";
3644
3645 case "mp4"_t:
3646 return "video/mp4";
3647 case "mpeg"_t:
3648 return "video/mpeg";
3649 case "webm"_t:
3650 return "video/webm";
3651
3652 case "mp3"_t:
3653 return "audio/mp3";
3654 case "mpga"_t:
3655 return "audio/mpeg";
3656 case "weba"_t:
3657 return "audio/webm";
3658 case "wav"_t:
3659 return "audio/wave";
3660
3661 case "otf"_t:
3662 return "font/otf";
3663 case "ttf"_t:
3664 return "font/ttf";
3665 case "woff"_t:
3666 return "font/woff";
3667 case "woff2"_t:
3668 return "font/woff2";
3669
3670 case "7z"_t:
3671 return "application/x-7z-compressed";
3672 case "atom"_t:
3673 return "application/atom+xml";
3674 case "pdf"_t:
3675 return "application/pdf";
3676 case "json"_t:
3677 return "application/json";
3678 case "rss"_t:
3679 return "application/rss+xml";
3680 case "tar"_t:
3681 return "application/x-tar";
3682 case "xht"_t:
3683 case "xhtml"_t:
3684 return "application/xhtml+xml";
3685 case "xslt"_t:
3686 return "application/xslt+xml";
3687 case "xml"_t:
3688 return "application/xml";
3689 case "gz"_t:
3690 return "application/gzip";
3691 case "zip"_t:
3692 return "application/zip";
3693 case "wasm"_t:
3694 return "application/wasm";
3695 }
3696}
3697
3698inline bool can_compress_content_type(const std::string &content_type) {
3699 using udl::operator""_t;
3700
3701 auto tag = str2tag(content_type);
3702
3703 switch (tag) {
3704 case "image/svg+xml"_t:
3705 case "application/javascript"_t:
3706 case "application/json"_t:
3707 case "application/xml"_t:
3708 case "application/protobuf"_t:
3709 case "application/xhtml+xml"_t:
3710 return true;
3711
3712 case "text/event-stream"_t:
3713 return false;
3714
3715 default:
3716 return !content_type.rfind("text/", 0);
3717 }
3718}
3719
3720inline EncodingType encoding_type(const Request &req, const Response &res) {
3721 auto ret = detail::can_compress_content_type(res.get_header_value("Content-Type"));
3722 if (!ret) {
3723 return EncodingType::None;
3724 }
3725
3726 const auto &s = req.get_header_value("Accept-Encoding");
3727 (void) (s);
3728
3729#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3730 // TODO: 'Accept-Encoding' has br, not br;q=0
3731 ret = s.find("br") != std::string::npos;
3732 if (ret) {
3733 return EncodingType::Brotli;
3734 }
3735#endif
3736
3737#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3738 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
3739 ret = s.find("gzip") != std::string::npos;
3740 if (ret) {
3741 return EncodingType::Gzip;
3742 }
3743#endif
3744
3745 return EncodingType::None;
3746}
3747
3748inline bool nocompressor::compress(const char *data, size_t data_length, bool /*last*/, Callback callback) {
3749 if (!data_length) {
3750 return true;
3751 }
3752 return callback(data, data_length);
3753}
3754
3755#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3756inline gzip_compressor::gzip_compressor() {
3757 std::memset(&strm_, 0, sizeof(strm_));
3758 strm_.zalloc = Z_NULL;
3759 strm_.zfree = Z_NULL;
3760 strm_.opaque = Z_NULL;
3761
3762 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK;
3763}
3764
3765inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
3766
3767inline bool gzip_compressor::compress(const char *data, size_t data_length, bool last, Callback callback) {
3768 assert(is_valid_);
3769
3770 do {
3771 constexpr size_t max_avail_in = (std::numeric_limits<decltype(strm_.avail_in)>::max)();
3772
3773 strm_.avail_in = static_cast<decltype(strm_.avail_in)>((std::min)(data_length, max_avail_in));
3774 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
3775
3776 data_length -= strm_.avail_in;
3777 data += strm_.avail_in;
3778
3779 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
3780 auto ret = Z_OK;
3781
3782 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3783 do {
3784 strm_.avail_out = static_cast<uInt>(buff.size());
3785 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
3786
3787 ret = deflate(&strm_, flush);
3788 if (ret == Z_STREAM_ERROR) {
3789 return false;
3790 }
3791
3792 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3793 return false;
3794 }
3795 } while (strm_.avail_out == 0);
3796
3797 assert((flush == Z_FINISH && ret == Z_STREAM_END) || (flush == Z_NO_FLUSH && ret == Z_OK));
3798 assert(strm_.avail_in == 0);
3799 } while (data_length > 0);
3800
3801 return true;
3802}
3803
3804inline gzip_decompressor::gzip_decompressor() {
3805 std::memset(&strm_, 0, sizeof(strm_));
3806 strm_.zalloc = Z_NULL;
3807 strm_.zfree = Z_NULL;
3808 strm_.opaque = Z_NULL;
3809
3810 // 15 is the value of wbits, which should be at the maximum possible value
3811 // to ensure that any gzip stream can be decoded. The offset of 32 specifies
3812 // that the stream type should be automatically detected either gzip or
3813 // deflate.
3814 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
3815}
3816
3817inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
3818
3819inline bool gzip_decompressor::is_valid() const { return is_valid_; }
3820
3821inline bool gzip_decompressor::decompress(const char *data, size_t data_length, Callback callback) {
3822 assert(is_valid_);
3823
3824 auto ret = Z_OK;
3825
3826 do {
3827 constexpr size_t max_avail_in = (std::numeric_limits<decltype(strm_.avail_in)>::max)();
3828
3829 strm_.avail_in = static_cast<decltype(strm_.avail_in)>((std::min)(data_length, max_avail_in));
3830 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
3831
3832 data_length -= strm_.avail_in;
3833 data += strm_.avail_in;
3834
3835 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3836 while (strm_.avail_in > 0 && ret == Z_OK) {
3837 strm_.avail_out = static_cast<uInt>(buff.size());
3838 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
3839
3840 ret = inflate(&strm_, Z_NO_FLUSH);
3841
3842 assert(ret != Z_STREAM_ERROR);
3843 switch (ret) {
3844 case Z_NEED_DICT:
3845 case Z_DATA_ERROR:
3846 case Z_MEM_ERROR:
3847 inflateEnd(&strm_);
3848 return false;
3849 }
3850
3851 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
3852 return false;
3853 }
3854 }
3855
3856 if (ret != Z_OK && ret != Z_STREAM_END) {
3857 return false;
3858 }
3859
3860 } while (data_length > 0);
3861
3862 return true;
3863}
3864#endif
3865
3866#ifdef CPPHTTPLIB_BROTLI_SUPPORT
3867inline brotli_compressor::brotli_compressor() { state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); }
3868
3869inline brotli_compressor::~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
3870
3871inline bool brotli_compressor::compress(const char *data, size_t data_length, bool last, Callback callback) {
3872 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3873
3874 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
3875 auto available_in = data_length;
3876 auto next_in = reinterpret_cast<const uint8_t *>(data);
3877
3878 for (;;) {
3879 if (last) {
3880 if (BrotliEncoderIsFinished(state_)) {
3881 break;
3882 }
3883 } else {
3884 if (!available_in) {
3885 break;
3886 }
3887 }
3888
3889 auto available_out = buff.size();
3890 auto next_out = buff.data();
3891
3892 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, &available_out, &next_out, nullptr)) {
3893 return false;
3894 }
3895
3896 auto output_bytes = buff.size() - available_out;
3897 if (output_bytes) {
3898 callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
3899 }
3900 }
3901
3902 return true;
3903}
3904
3905inline brotli_decompressor::brotli_decompressor() {
3906 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
3907 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT : BROTLI_DECODER_RESULT_ERROR;
3908}
3909
3910inline brotli_decompressor::~brotli_decompressor() {
3911 if (decoder_s) {
3912 BrotliDecoderDestroyInstance(decoder_s);
3913 }
3914}
3915
3916inline bool brotli_decompressor::is_valid() const { return decoder_s; }
3917
3918inline bool brotli_decompressor::decompress(const char *data, size_t data_length, Callback callback) {
3919 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3920 return 0;
3921 }
3922
3923 auto next_in = reinterpret_cast<const uint8_t *>(data);
3924 size_t avail_in = data_length;
3925 size_t total_out;
3926
3927 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
3928
3929 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
3930 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
3931 char *next_out = buff.data();
3932 size_t avail_out = buff.size();
3933
3934 decoder_r = BrotliDecoderDecompressStream(decoder_s, &avail_in, &next_in, &avail_out,
3935 reinterpret_cast<uint8_t **>(&next_out), &total_out);
3936
3937 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) {
3938 return false;
3939 }
3940
3941 if (!callback(buff.data(), buff.size() - avail_out)) {
3942 return false;
3943 }
3944 }
3945
3946 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
3947}
3948#endif
3949
3950inline bool has_header(const Headers &headers, const std::string &key) { return headers.find(key) != headers.end(); }
3951
3952inline const char *get_header_value(const Headers &headers, const std::string &key, const char *def, size_t id) {
3953 auto rng = headers.equal_range(key);
3954 auto it = rng.first;
3955 std::advance(it, static_cast<ssize_t>(id));
3956 if (it != rng.second) {
3957 return it->second.c_str();
3958 }
3959 return def;
3960}
3961
3962template<typename T> inline bool parse_header(const char *beg, const char *end, T fn) {
3963 // Skip trailing spaces and tabs.
3964 while (beg < end && is_space_or_tab(end[-1])) {
3965 end--;
3966 }
3967
3968 auto p = beg;
3969 while (p < end && *p != ':') {
3970 p++;
3971 }
3972
3973 if (p == end) {
3974 return false;
3975 }
3976
3977 auto key_end = p;
3978
3979 if (*p++ != ':') {
3980 return false;
3981 }
3982
3983 while (p < end && is_space_or_tab(*p)) {
3984 p++;
3985 }
3986
3987 if (p <= end) {
3988 auto key_len = key_end - beg;
3989 if (!key_len) {
3990 return false;
3991 }
3992
3993 auto key = std::string(beg, key_end);
3994 auto val = case_ignore::equal(key, "Location") ? std::string(p, end) : decode_url(std::string(p, end), false);
3995
3996 // NOTE: From RFC 9110:
3997 // Field values containing CR, LF, or NUL characters are
3998 // invalid and dangerous, due to the varying ways that
3999 // implementations might parse and interpret those
4000 // characters; a recipient of CR, LF, or NUL within a field
4001 // value MUST either reject the message or replace each of
4002 // those characters with SP before further processing or
4003 // forwarding of that message.
4004 static const std::string CR_LF_NUL("\r\n\0", 3);
4005 if (val.find_first_of(CR_LF_NUL) != std::string::npos) {
4006 return false;
4007 }
4008
4009 fn(key, val);
4010 return true;
4011 }
4012
4013 return false;
4014}
4015
4016inline bool read_headers(Stream &strm, Headers &headers) {
4017 const auto bufsiz = 2048;
4018 char buf[bufsiz];
4019 stream_line_reader line_reader(strm, buf, bufsiz);
4020
4021 for (;;) {
4022 if (!line_reader.getline()) {
4023 return false;
4024 }
4025
4026 // Check if the line ends with CRLF.
4027 auto line_terminator_len = 2;
4028 if (line_reader.end_with_crlf()) {
4029 // Blank line indicates end of headers.
4030 if (line_reader.size() == 2) {
4031 break;
4032 }
4033 } else {
4034#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4035 // Blank line indicates end of headers.
4036 if (line_reader.size() == 1) {
4037 break;
4038 }
4039 line_terminator_len = 1;
4040#else
4041 continue; // Skip invalid line.
4042#endif
4043 }
4044
4045 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4046 return false;
4047 }
4048
4049 // Exclude line terminator
4050 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4051
4052 if (!parse_header(line_reader.ptr(), end,
4053 [&](const std::string &key, std::string &val) { headers.emplace(key, val); })) {
4054 return false;
4055 }
4056 }
4057
4058 return true;
4059}
4060
4061inline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiverWithProgress out) {
4062 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4063
4064 uint64_t r = 0;
4065 while (r < len) {
4066 auto read_len = static_cast<size_t>(len - r);
4067 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4068 if (n <= 0) {
4069 return false;
4070 }
4071
4072 if (!out(buf, static_cast<size_t>(n), r, len)) {
4073 return false;
4074 }
4075 r += static_cast<uint64_t>(n);
4076
4077 if (progress) {
4078 if (!progress(r, len)) {
4079 return false;
4080 }
4081 }
4082 }
4083
4084 return true;
4085}
4086
4087inline void skip_content_with_length(Stream &strm, uint64_t len) {
4088 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4089 uint64_t r = 0;
4090 while (r < len) {
4091 auto read_len = static_cast<size_t>(len - r);
4092 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
4093 if (n <= 0) {
4094 return;
4095 }
4096 r += static_cast<uint64_t>(n);
4097 }
4098}
4099
4100inline bool read_content_without_length(Stream &strm, ContentReceiverWithProgress out) {
4101 char buf[CPPHTTPLIB_RECV_BUFSIZ];
4102 uint64_t r = 0;
4103 for (;;) {
4104 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4105 if (n <= 0) {
4106 return true;
4107 }
4108
4109 if (!out(buf, static_cast<size_t>(n), r, 0)) {
4110 return false;
4111 }
4112 r += static_cast<uint64_t>(n);
4113 }
4114
4115 return true;
4116}
4117
4118template<typename T> inline bool read_content_chunked(Stream &strm, T &x, ContentReceiverWithProgress out) {
4119 const auto bufsiz = 16;
4120 char buf[bufsiz];
4121
4122 stream_line_reader line_reader(strm, buf, bufsiz);
4123
4124 if (!line_reader.getline()) {
4125 return false;
4126 }
4127
4128 unsigned long chunk_len;
4129 while (true) {
4130 char *end_ptr;
4131
4132 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
4133
4134 if (end_ptr == line_reader.ptr()) {
4135 return false;
4136 }
4137 if (chunk_len == ULONG_MAX) {
4138 return false;
4139 }
4140
4141 if (chunk_len == 0) {
4142 break;
4143 }
4144
4145 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
4146 return false;
4147 }
4148
4149 if (!line_reader.getline()) {
4150 return false;
4151 }
4152
4153 if (strcmp(line_reader.ptr(), "\r\n") != 0) {
4154 return false;
4155 }
4156
4157 if (!line_reader.getline()) {
4158 return false;
4159 }
4160 }
4161
4162 assert(chunk_len == 0);
4163
4164 // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentiones "The chunked
4165 // transfer coding is complete when a chunk with a chunk-size of zero is
4166 // received, possibly followed by a trailer section, and finally terminated by
4167 // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
4168 //
4169 // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
4170 // does't care for the existence of the final CRLF. In other words, it seems
4171 // to be ok whether the final CRLF exists or not in the chunked data.
4172 // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4173 //
4174 // According to the reference code in RFC 9112, cpp-htpplib now allows
4175 // chuncked transfer coding data without the final CRLF.
4176 if (!line_reader.getline()) {
4177 return true;
4178 }
4179
4180 while (strcmp(line_reader.ptr(), "\r\n") != 0) {
4181 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4182 return false;
4183 }
4184
4185 // Exclude line terminator
4186 constexpr auto line_terminator_len = 2;
4187 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4188
4189 parse_header(line_reader.ptr(), end,
4190 [&](const std::string &key, const std::string &val) { x.headers.emplace(key, val); });
4191
4192 if (!line_reader.getline()) {
4193 return false;
4194 }
4195 }
4196
4197 return true;
4198}
4199
4200inline bool is_chunked_transfer_encoding(const Headers &headers) {
4201 return case_ignore::equal(get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
4202}
4203
4204template<typename T, typename U>
4205bool prepare_content_receiver(T &x, int &status, ContentReceiverWithProgress receiver, bool decompress, U callback) {
4206 if (decompress) {
4207 std::string encoding = x.get_header_value("Content-Encoding");
4208 std::unique_ptr<decompressor> decompressor;
4209
4210 if (encoding == "gzip" || encoding == "deflate") {
4211#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4212 decompressor = detail::make_unique<gzip_decompressor>();
4213#else
4214 status = StatusCode::UnsupportedMediaType_415;
4215 return false;
4216#endif
4217 } else if (encoding.find("br") != std::string::npos) {
4218#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4219 decompressor = detail::make_unique<brotli_decompressor>();
4220#else
4221 status = StatusCode::UnsupportedMediaType_415;
4222 return false;
4223#endif
4224 }
4225
4226 if (decompressor) {
4227 if (decompressor->is_valid()) {
4228 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
4229 return decompressor->decompress(buf, n,
4230 [&](const char *buf2, size_t n2) { return receiver(buf2, n2, off, len); });
4231 };
4232 return callback(std::move(out));
4233 } else {
4234 status = StatusCode::InternalServerError_500;
4235 return false;
4236 }
4237 }
4238 }
4239
4240 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
4241 return receiver(buf, n, off, len);
4242 };
4243 return callback(std::move(out));
4244}
4245
4246template<typename T>
4247bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, Progress progress,
4248 ContentReceiverWithProgress receiver, bool decompress) {
4249 return prepare_content_receiver(
4250 x, status, std::move(receiver), decompress, [&](const ContentReceiverWithProgress &out) {
4251 auto ret = true;
4252 auto exceed_payload_max_length = false;
4253
4254 if (is_chunked_transfer_encoding(x.headers)) {
4255 ret = read_content_chunked(strm, x, out);
4256 } else if (!has_header(x.headers, "Content-Length")) {
4257 ret = read_content_without_length(strm, out);
4258 } else {
4259 auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0);
4260 if (len > payload_max_length) {
4261 exceed_payload_max_length = true;
4262 skip_content_with_length(strm, len);
4263 ret = false;
4264 } else if (len > 0) {
4265 ret = read_content_with_length(strm, len, std::move(progress), out);
4266 }
4267 }
4268
4269 if (!ret) {
4270 status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 : StatusCode::BadRequest_400;
4271 }
4272 return ret;
4273 });
4274}
4275
4276inline ssize_t write_request_line(Stream &strm, const std::string &method, const std::string &path) {
4277 std::string s = method;
4278 s += " ";
4279 s += path;
4280 s += " HTTP/1.1\r\n";
4281 return strm.write(s.data(), s.size());
4282}
4283
4284inline ssize_t write_response_line(Stream &strm, int status) {
4285 std::string s = "HTTP/1.1 ";
4286 s += std::to_string(status);
4287 s += " ";
4288 s += httplib::status_message(status);
4289 s += "\r\n";
4290 return strm.write(s.data(), s.size());
4291}
4292
4293inline ssize_t write_headers(Stream &strm, const Headers &headers) {
4294 ssize_t write_len = 0;
4295 for (const auto &x : headers) {
4296 std::string s;
4297 s = x.first;
4298 s += ": ";
4299 s += x.second;
4300 s += "\r\n";
4301
4302 auto len = strm.write(s.data(), s.size());
4303 if (len < 0) {
4304 return len;
4305 }
4306 write_len += len;
4307 }
4308 auto len = strm.write("\r\n");
4309 if (len < 0) {
4310 return len;
4311 }
4312 write_len += len;
4313 return write_len;
4314}
4315
4316inline bool write_data(Stream &strm, const char *d, size_t l) {
4317 size_t offset = 0;
4318 while (offset < l) {
4319 auto length = strm.write(d + offset, l - offset);
4320 if (length < 0) {
4321 return false;
4322 }
4323 offset += static_cast<size_t>(length);
4324 }
4325 return true;
4326}
4327
4328template<typename T>
4329inline bool write_content(Stream &strm, const ContentProvider &content_provider, size_t offset, size_t length,
4330 T is_shutting_down, Error &error) {
4331 size_t end_offset = offset + length;
4332 auto ok = true;
4333 DataSink data_sink;
4334
4335 data_sink.write = [&](const char *d, size_t l) -> bool {
4336 if (ok) {
4337 if (strm.is_writable() && write_data(strm, d, l)) {
4338 offset += l;
4339 } else {
4340 ok = false;
4341 }
4342 }
4343 return ok;
4344 };
4345
4346 data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
4347
4348 while (offset < end_offset && !is_shutting_down()) {
4349 if (!strm.is_writable()) {
4350 error = Error::Write;
4351 return false;
4352 } else if (!content_provider(offset, end_offset - offset, data_sink)) {
4353 error = Error::Canceled;
4354 return false;
4355 } else if (!ok) {
4356 error = Error::Write;
4357 return false;
4358 }
4359 }
4360
4361 error = Error::Success;
4362 return true;
4363}
4364
4365template<typename T>
4366inline bool write_content(Stream &strm, const ContentProvider &content_provider, size_t offset, size_t length,
4367 const T &is_shutting_down) {
4368 auto error = Error::Success;
4369 return write_content(strm, content_provider, offset, length, is_shutting_down, error);
4370}
4371
4372template<typename T>
4373inline bool write_content_without_length(Stream &strm, const ContentProvider &content_provider,
4374 const T &is_shutting_down) {
4375 size_t offset = 0;
4376 auto data_available = true;
4377 auto ok = true;
4378 DataSink data_sink;
4379
4380 data_sink.write = [&](const char *d, size_t l) -> bool {
4381 if (ok) {
4382 offset += l;
4383 if (!strm.is_writable() || !write_data(strm, d, l)) {
4384 ok = false;
4385 }
4386 }
4387 return ok;
4388 };
4389
4390 data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
4391
4392 data_sink.done = [&](void) { data_available = false; };
4393
4394 while (data_available && !is_shutting_down()) {
4395 if (!strm.is_writable()) {
4396 return false;
4397 } else if (!content_provider(offset, 0, data_sink)) {
4398 return false;
4399 } else if (!ok) {
4400 return false;
4401 }
4402 }
4403 return true;
4404}
4405
4406template<typename T, typename U>
4407inline bool write_content_chunked(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down,
4408 U &compressor, Error &error) {
4409 size_t offset = 0;
4410 auto data_available = true;
4411 auto ok = true;
4412 DataSink data_sink;
4413
4414 data_sink.write = [&](const char *d, size_t l) -> bool {
4415 if (ok) {
4416 data_available = l > 0;
4417 offset += l;
4418
4419 std::string payload;
4420 if (compressor.compress(d, l, false, [&](const char *data, size_t data_len) {
4421 payload.append(data, data_len);
4422 return true;
4423 })) {
4424 if (!payload.empty()) {
4425 // Emit chunked response header and footer for each chunk
4426 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
4427 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size())) {
4428 ok = false;
4429 }
4430 }
4431 } else {
4432 ok = false;
4433 }
4434 }
4435 return ok;
4436 };
4437
4438 data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
4439
4440 auto done_with_trailer = [&](const Headers *trailer) {
4441 if (!ok) {
4442 return;
4443 }
4444
4445 data_available = false;
4446
4447 std::string payload;
4448 if (!compressor.compress(nullptr, 0, true, [&](const char *data, size_t data_len) {
4449 payload.append(data, data_len);
4450 return true;
4451 })) {
4452 ok = false;
4453 return;
4454 }
4455
4456 if (!payload.empty()) {
4457 // Emit chunked response header and footer for each chunk
4458 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
4459 if (!strm.is_writable() || !write_data(strm, chunk.data(), chunk.size())) {
4460 ok = false;
4461 return;
4462 }
4463 }
4464
4465 static const std::string done_marker("0\r\n");
4466 if (!write_data(strm, done_marker.data(), done_marker.size())) {
4467 ok = false;
4468 }
4469
4470 // Trailer
4471 if (trailer) {
4472 for (const auto &kv : *trailer) {
4473 std::string field_line = kv.first + ": " + kv.second + "\r\n";
4474 if (!write_data(strm, field_line.data(), field_line.size())) {
4475 ok = false;
4476 }
4477 }
4478 }
4479
4480 static const std::string crlf("\r\n");
4481 if (!write_data(strm, crlf.data(), crlf.size())) {
4482 ok = false;
4483 }
4484 };
4485
4486 data_sink.done = [&](void) { done_with_trailer(nullptr); };
4487
4488 data_sink.done_with_trailer = [&](const Headers &trailer) { done_with_trailer(&trailer); };
4489
4490 while (data_available && !is_shutting_down()) {
4491 if (!strm.is_writable()) {
4492 error = Error::Write;
4493 return false;
4494 } else if (!content_provider(offset, 0, data_sink)) {
4495 error = Error::Canceled;
4496 return false;
4497 } else if (!ok) {
4498 error = Error::Write;
4499 return false;
4500 }
4501 }
4502
4503 error = Error::Success;
4504 return true;
4505}
4506
4507template<typename T, typename U>
4508inline bool write_content_chunked(Stream &strm, const ContentProvider &content_provider, const T &is_shutting_down,
4509 U &compressor) {
4510 auto error = Error::Success;
4511 return write_content_chunked(strm, content_provider, is_shutting_down, compressor, error);
4512}
4513
4514template<typename T>
4515inline bool redirect(T &cli, Request &req, Response &res, const std::string &path, const std::string &location,
4516 Error &error) {
4517 Request new_req = req;
4518 new_req.path = path;
4519 new_req.redirect_count_ -= 1;
4520
4521 if (res.status == StatusCode::SeeOther_303 && (req.method != "GET" && req.method != "HEAD")) {
4522 new_req.method = "GET";
4523 new_req.body.clear();
4524 new_req.headers.clear();
4525 }
4526
4527 Response new_res;
4528
4529 auto ret = cli.send(new_req, new_res, error);
4530 if (ret) {
4531 req = new_req;
4532 res = new_res;
4533
4534 if (res.location.empty()) {
4535 res.location = location;
4536 }
4537 }
4538 return ret;
4539}
4540
4541inline std::string params_to_query_str(const Params &params) {
4542 std::string query;
4543
4544 for (auto it = params.begin(); it != params.end(); ++it) {
4545 if (it != params.begin()) {
4546 query += "&";
4547 }
4548 query += it->first;
4549 query += "=";
4550 query += encode_query_param(it->second);
4551 }
4552 return query;
4553}
4554
4555inline void parse_query_text(const char *data, std::size_t size, Params &params) {
4556 std::set<std::string> cache;
4557 split(data, data + size, '&', [&](const char *b, const char *e) {
4558 std::string kv(b, e);
4559 if (cache.find(kv) != cache.end()) {
4560 return;
4561 }
4562 cache.insert(std::move(kv));
4563
4564 std::string key;
4565 std::string val;
4566 divide(b, static_cast<std::size_t>(e - b), '=',
4567 [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data, std::size_t rhs_size) {
4568 key.assign(lhs_data, lhs_size);
4569 val.assign(rhs_data, rhs_size);
4570 });
4571
4572 if (!key.empty()) {
4573 params.emplace(decode_url(key, true), decode_url(val, true));
4574 }
4575 });
4576}
4577
4578inline void parse_query_text(const std::string &s, Params &params) { parse_query_text(s.data(), s.size(), params); }
4579
4580inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) {
4581 auto boundary_keyword = "boundary=";
4582 auto pos = content_type.find(boundary_keyword);
4583 if (pos == std::string::npos) {
4584 return false;
4585 }
4586 auto end = content_type.find(';', pos);
4587 auto beg = pos + strlen(boundary_keyword);
4588 boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
4589 return !boundary.empty();
4590}
4591
4592inline void parse_disposition_params(const std::string &s, Params &params) {
4593 std::set<std::string> cache;
4594 split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
4595 std::string kv(b, e);
4596 if (cache.find(kv) != cache.end()) {
4597 return;
4598 }
4599 cache.insert(kv);
4600
4601 std::string key;
4602 std::string val;
4603 split(b, e, '=', [&](const char *b2, const char *e2) {
4604 if (key.empty()) {
4605 key.assign(b2, e2);
4606 } else {
4607 val.assign(b2, e2);
4608 }
4609 });
4610
4611 if (!key.empty()) {
4612 params.emplace(trim_double_quotes_copy((key)), trim_double_quotes_copy((val)));
4613 }
4614 });
4615}
4616
4617#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4618inline bool parse_range_header(const std::string &s, Ranges &ranges) {
4619#else
4620inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
4621#endif
4622 auto is_valid = [](const std::string &str) {
4623 return std::all_of(str.cbegin(), str.cend(), [](unsigned char c) { return std::isdigit(c); });
4624 };
4625
4626 if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
4627 const auto pos = static_cast<size_t>(6);
4628 const auto len = static_cast<size_t>(s.size() - 6);
4629 auto all_valid_ranges = true;
4630 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
4631 if (!all_valid_ranges) {
4632 return;
4633 }
4634
4635 const auto it = std::find(b, e, '-');
4636 if (it == e) {
4637 all_valid_ranges = false;
4638 return;
4639 }
4640
4641 const auto lhs = std::string(b, it);
4642 const auto rhs = std::string(it + 1, e);
4643 if (!is_valid(lhs) || !is_valid(rhs)) {
4644 all_valid_ranges = false;
4645 return;
4646 }
4647
4648 const auto first = static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
4649 const auto last = static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
4650 if ((first == -1 && last == -1) || (first != -1 && last != -1 && first > last)) {
4651 all_valid_ranges = false;
4652 return;
4653 }
4654
4655 ranges.emplace_back(first, last);
4656 });
4657 return all_valid_ranges && !ranges.empty();
4658 }
4659 return false;
4660#ifdef CPPHTTPLIB_NO_EXCEPTIONS
4661}
4662#else
4663} catch (...) {
4664 return false;
4665}
4666#endif
4667
4668class MultipartFormDataParser {
4669 public:
4670 MultipartFormDataParser() = default;
4671
4672 void set_boundary(std::string &&boundary) {
4673 boundary_ = boundary;
4674 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
4675 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
4676 }
4677
4678 bool is_valid() const { return is_valid_; }
4679
4680 bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,
4681 const MultipartContentHeader &header_callback) {
4682 buf_append(buf, n);
4683
4684 while (buf_size() > 0) {
4685 switch (state_) {
4686 case 0: { // Initial boundary
4687 buf_erase(buf_find(dash_boundary_crlf_));
4688 if (dash_boundary_crlf_.size() > buf_size()) {
4689 return true;
4690 }
4691 if (!buf_start_with(dash_boundary_crlf_)) {
4692 return false;
4693 }
4694 buf_erase(dash_boundary_crlf_.size());
4695 state_ = 1;
4696 break;
4697 }
4698 case 1: { // New entry
4699 clear_file_info();
4700 state_ = 2;
4701 break;
4702 }
4703 case 2: { // Headers
4704 auto pos = buf_find(crlf_);
4705 if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) {
4706 return false;
4707 }
4708 while (pos < buf_size()) {
4709 // Empty line
4710 if (pos == 0) {
4711 if (!header_callback(file_)) {
4712 is_valid_ = false;
4713 return false;
4714 }
4715 buf_erase(crlf_.size());
4716 state_ = 3;
4717 break;
4718 }
4719
4720 const auto header = buf_head(pos);
4721
4722 if (!parse_header(header.data(), header.data() + header.size(),
4723 [&](const std::string &, const std::string &) {})) {
4724 is_valid_ = false;
4725 return false;
4726 }
4727
4728 static const std::string header_content_type = "Content-Type:";
4729
4730 if (start_with_case_ignore(header, header_content_type)) {
4731 file_.content_type = trim_copy(header.substr(header_content_type.size()));
4732 } else {
4733 static const std::regex re_content_disposition(R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4734 std::regex_constants::icase);
4735
4736 std::smatch m;
4737 if (std::regex_match(header, m, re_content_disposition)) {
4738 Params params;
4739 parse_disposition_params(m[1], params);
4740
4741 auto it = params.find("name");
4742 if (it != params.end()) {
4743 file_.name = it->second;
4744 } else {
4745 is_valid_ = false;
4746 return false;
4747 }
4748
4749 it = params.find("filename");
4750 if (it != params.end()) {
4751 file_.filename = it->second;
4752 }
4753
4754 it = params.find("filename*");
4755 if (it != params.end()) {
4756 // Only allow UTF-8 enconnding...
4757 static const std::regex re_rfc5987_encoding(R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4758
4759 std::smatch m2;
4760 if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {
4761 file_.filename = decode_url(m2[1], false); // override...
4762 } else {
4763 is_valid_ = false;
4764 return false;
4765 }
4766 }
4767 }
4768 }
4769 buf_erase(pos + crlf_.size());
4770 pos = buf_find(crlf_);
4771 }
4772 if (state_ != 3) {
4773 return true;
4774 }
4775 break;
4776 }
4777 case 3: { // Body
4778 if (crlf_dash_boundary_.size() > buf_size()) {
4779 return true;
4780 }
4781 auto pos = buf_find(crlf_dash_boundary_);
4782 if (pos < buf_size()) {
4783 if (!content_callback(buf_data(), pos)) {
4784 is_valid_ = false;
4785 return false;
4786 }
4787 buf_erase(pos + crlf_dash_boundary_.size());
4788 state_ = 4;
4789 } else {
4790 auto len = buf_size() - crlf_dash_boundary_.size();
4791 if (len > 0) {
4792 if (!content_callback(buf_data(), len)) {
4793 is_valid_ = false;
4794 return false;
4795 }
4796 buf_erase(len);
4797 }
4798 return true;
4799 }
4800 break;
4801 }
4802 case 4: { // Boundary
4803 if (crlf_.size() > buf_size()) {
4804 return true;
4805 }
4806 if (buf_start_with(crlf_)) {
4807 buf_erase(crlf_.size());
4808 state_ = 1;
4809 } else {
4810 if (dash_.size() > buf_size()) {
4811 return true;
4812 }
4813 if (buf_start_with(dash_)) {
4814 buf_erase(dash_.size());
4815 is_valid_ = true;
4816 buf_erase(buf_size()); // Remove epilogue
4817 } else {
4818 return true;
4819 }
4820 }
4821 break;
4822 }
4823 }
4824 }
4825
4826 return true;
4827 }
4828
4829 private:
4830 void clear_file_info() {
4831 file_.name.clear();
4832 file_.filename.clear();
4833 file_.content_type.clear();
4834 }
4835
4836 bool start_with_case_ignore(const std::string &a, const std::string &b) const {
4837 if (a.size() < b.size()) {
4838 return false;
4839 }
4840 for (size_t i = 0; i < b.size(); i++) {
4841 if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
4842 return false;
4843 }
4844 }
4845 return true;
4846 }
4847
4848 const std::string dash_ = "--";
4849 const std::string crlf_ = "\r\n";
4850 std::string boundary_;
4851 std::string dash_boundary_crlf_;
4852 std::string crlf_dash_boundary_;
4853
4854 size_t state_ = 0;
4855 bool is_valid_ = false;
4856 MultipartFormData file_;
4857
4858 // Buffer
4859 bool start_with(const std::string &a, size_t spos, size_t epos, const std::string &b) const {
4860 if (epos - spos < b.size()) {
4861 return false;
4862 }
4863 for (size_t i = 0; i < b.size(); i++) {
4864 if (a[i + spos] != b[i]) {
4865 return false;
4866 }
4867 }
4868 return true;
4869 }
4870
4871 size_t buf_size() const { return buf_epos_ - buf_spos_; }
4872
4873 const char *buf_data() const { return &buf_[buf_spos_]; }
4874
4875 std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
4876
4877 bool buf_start_with(const std::string &s) const { return start_with(buf_, buf_spos_, buf_epos_, s); }
4878
4879 size_t buf_find(const std::string &s) const {
4880 auto c = s.front();
4881
4882 size_t off = buf_spos_;
4883 while (off < buf_epos_) {
4884 auto pos = off;
4885 while (true) {
4886 if (pos == buf_epos_) {
4887 return buf_size();
4888 }
4889 if (buf_[pos] == c) {
4890 break;
4891 }
4892 pos++;
4893 }
4894
4895 auto remaining_size = buf_epos_ - pos;
4896 if (s.size() > remaining_size) {
4897 return buf_size();
4898 }
4899
4900 if (start_with(buf_, pos, buf_epos_, s)) {
4901 return pos - buf_spos_;
4902 }
4903
4904 off = pos + 1;
4905 }
4906
4907 return buf_size();
4908 }
4909
4910 void buf_append(const char *data, size_t n) {
4911 auto remaining_size = buf_size();
4912 if (remaining_size > 0 && buf_spos_ > 0) {
4913 for (size_t i = 0; i < remaining_size; i++) {
4914 buf_[i] = buf_[buf_spos_ + i];
4915 }
4916 }
4917 buf_spos_ = 0;
4918 buf_epos_ = remaining_size;
4919
4920 if (remaining_size + n > buf_.size()) {
4921 buf_.resize(remaining_size + n);
4922 }
4923
4924 for (size_t i = 0; i < n; i++) {
4925 buf_[buf_epos_ + i] = data[i];
4926 }
4927 buf_epos_ += n;
4928 }
4929
4930 void buf_erase(size_t size) { buf_spos_ += size; }
4931
4932 std::string buf_;
4933 size_t buf_spos_ = 0;
4934 size_t buf_epos_ = 0;
4935};
4936
4937inline std::string random_string(size_t length) {
4938 static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
4939
4940 // std::random_device might actually be deterministic on some
4941 // platforms, but due to lack of support in the c++ standard library,
4942 // doing better requires either some ugly hacks or breaking portability.
4943 static std::random_device seed_gen;
4944
4945 // Request 128 bits of entropy for initialization
4946 static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
4947
4948 static std::mt19937 engine(seed_sequence);
4949
4950 std::string result;
4951 for (size_t i = 0; i < length; i++) {
4952 result += data[engine() % (sizeof(data) - 1)];
4953 }
4954 return result;
4955}
4956
4957inline std::string make_multipart_data_boundary() {
4958 return "--cpp-httplib-multipart-data-" + detail::random_string(16);
4959}
4960
4961inline bool is_multipart_boundary_chars_valid(const std::string &boundary) {
4962 auto valid = true;
4963 for (size_t i = 0; i < boundary.size(); i++) {
4964 auto c = boundary[i];
4965 if (!std::isalnum(c) && c != '-' && c != '_') {
4966 valid = false;
4967 break;
4968 }
4969 }
4970 return valid;
4971}
4972
4973template<typename T>
4974inline std::string serialize_multipart_formdata_item_begin(const T &item, const std::string &boundary) {
4975 std::string body = "--" + boundary + "\r\n";
4976 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
4977 if (!item.filename.empty()) {
4978 body += "; filename=\"" + item.filename + "\"";
4979 }
4980 body += "\r\n";
4981 if (!item.content_type.empty()) {
4982 body += "Content-Type: " + item.content_type + "\r\n";
4983 }
4984 body += "\r\n";
4985
4986 return body;
4987}
4988
4989inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
4990
4991inline std::string serialize_multipart_formdata_finish(const std::string &boundary) {
4992 return "--" + boundary + "--\r\n";
4993}
4994
4995inline std::string serialize_multipart_formdata_get_content_type(const std::string &boundary) {
4996 return "multipart/form-data; boundary=" + boundary;
4997}
4998
4999inline std::string serialize_multipart_formdata(const MultipartFormDataItems &items, const std::string &boundary,
5000 bool finish = true) {
5001 std::string body;
5002
5003 for (const auto &item : items) {
5004 body += serialize_multipart_formdata_item_begin(item, boundary);
5005 body += item.content + serialize_multipart_formdata_item_end();
5006 }
5007
5008 if (finish) {
5009 body += serialize_multipart_formdata_finish(boundary);
5010 }
5011
5012 return body;
5013}
5014
5015inline bool range_error(Request &req, Response &res) {
5016 if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
5017 ssize_t contant_len = static_cast<ssize_t>(res.content_length_ ? res.content_length_ : res.body.size());
5018
5019 ssize_t prev_first_pos = -1;
5020 ssize_t prev_last_pos = -1;
5021 size_t overwrapping_count = 0;
5022
5023 // NOTE: The following Range check is based on '14.2. Range' in RFC 9110
5024 // 'HTTP Semantics' to avoid potential denial-of-service attacks.
5025 // https://www.rfc-editor.org/rfc/rfc9110#section-14.2
5026
5027 // Too many ranges
5028 if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) {
5029 return true;
5030 }
5031
5032 for (auto &r : req.ranges) {
5033 auto &first_pos = r.first;
5034 auto &last_pos = r.second;
5035
5036 if (first_pos == -1 && last_pos == -1) {
5037 first_pos = 0;
5038 last_pos = contant_len;
5039 }
5040
5041 if (first_pos == -1) {
5042 first_pos = contant_len - last_pos;
5043 last_pos = contant_len - 1;
5044 }
5045
5046 if (last_pos == -1) {
5047 last_pos = contant_len - 1;
5048 }
5049
5050 // Range must be within content length
5051 if (!(0 <= first_pos && first_pos <= last_pos && last_pos <= contant_len - 1)) {
5052 return true;
5053 }
5054
5055 // Ranges must be in ascending order
5056 if (first_pos <= prev_first_pos) {
5057 return true;
5058 }
5059
5060 // Request must not have more than two overlapping ranges
5061 if (first_pos <= prev_last_pos) {
5062 overwrapping_count++;
5063 if (overwrapping_count > 2) {
5064 return true;
5065 }
5066 }
5067
5068 prev_first_pos = (std::max)(prev_first_pos, first_pos);
5069 prev_last_pos = (std::max)(prev_last_pos, last_pos);
5070 }
5071 }
5072
5073 return false;
5074}
5075
5076inline std::pair<size_t, size_t> get_range_offset_and_length(Range r, size_t content_length) {
5077 assert(r.first != -1 && r.second != -1);
5078 assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
5079 assert(r.first <= r.second && r.second < static_cast<ssize_t>(content_length));
5080 (void) (content_length);
5081 return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
5082}
5083
5084inline std::string make_content_range_header_field(const std::pair<size_t, size_t> &offset_and_length,
5085 size_t content_length) {
5086 auto st = offset_and_length.first;
5087 auto ed = st + offset_and_length.second - 1;
5088
5089 std::string field = "bytes ";
5090 field += std::to_string(st);
5091 field += "-";
5092 field += std::to_string(ed);
5093 field += "/";
5094 field += std::to_string(content_length);
5095 return field;
5096}
5097
5098template<typename SToken, typename CToken, typename Content>
5099bool process_multipart_ranges_data(const Request &req, const std::string &boundary, const std::string &content_type,
5100 size_t content_length, SToken stoken, CToken ctoken, Content content) {
5101 for (size_t i = 0; i < req.ranges.size(); i++) {
5102 ctoken("--");
5103 stoken(boundary);
5104 ctoken("\r\n");
5105 if (!content_type.empty()) {
5106 ctoken("Content-Type: ");
5107 stoken(content_type);
5108 ctoken("\r\n");
5109 }
5110
5111 auto offset_and_length = get_range_offset_and_length(req.ranges[i], content_length);
5112
5113 ctoken("Content-Range: ");
5114 stoken(make_content_range_header_field(offset_and_length, content_length));
5115 ctoken("\r\n");
5116 ctoken("\r\n");
5117
5118 if (!content(offset_and_length.first, offset_and_length.second)) {
5119 return false;
5120 }
5121 ctoken("\r\n");
5122 }
5123
5124 ctoken("--");
5125 stoken(boundary);
5126 ctoken("--");
5127
5128 return true;
5129}
5130
5131inline void make_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary,
5132 const std::string &content_type, size_t content_length, std::string &data) {
5133 process_multipart_ranges_data(
5134 req, boundary, content_type, content_length, [&](const std::string &token) { data += token; },
5135 [&](const std::string &token) { data += token; },
5136 [&](size_t offset, size_t length) {
5137 assert(offset + length <= content_length);
5138 data += res.body.substr(offset, length);
5139 return true;
5140 });
5141}
5142
5143inline size_t get_multipart_ranges_data_length(const Request &req, const std::string &boundary,
5144 const std::string &content_type, size_t content_length) {
5145 size_t data_length = 0;
5146
5147 process_multipart_ranges_data(
5148 req, boundary, content_type, content_length, [&](const std::string &token) { data_length += token.size(); },
5149 [&](const std::string &token) { data_length += token.size(); },
5150 [&](size_t /*offset*/, size_t length) {
5151 data_length += length;
5152 return true;
5153 });
5154
5155 return data_length;
5156}
5157
5158template<typename T>
5159inline bool write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, const std::string &boundary,
5160 const std::string &content_type, size_t content_length,
5161 const T &is_shutting_down) {
5162 return process_multipart_ranges_data(
5163 req, boundary, content_type, content_length, [&](const std::string &token) { strm.write(token); },
5164 [&](const std::string &token) { strm.write(token); },
5165 [&](size_t offset, size_t length) {
5166 return write_content(strm, res.content_provider_, offset, length, is_shutting_down);
5167 });
5168}
5169
5170inline bool expect_content(const Request &req) {
5171 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI" ||
5172 req.method == "DELETE") {
5173 return true;
5174 }
5175 // TODO: check if Content-Length is set
5176 return false;
5177}
5178
5179inline bool has_crlf(const std::string &s) {
5180 auto p = s.c_str();
5181 while (*p) {
5182 if (*p == '\r' || *p == '\n') {
5183 return true;
5184 }
5185 p++;
5186 }
5187 return false;
5188}
5189
5190#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5191inline std::string message_digest(const std::string &s, const EVP_MD *algo) {
5192 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
5193
5194 unsigned int hash_length = 0;
5195 unsigned char hash[EVP_MAX_MD_SIZE];
5196
5197 EVP_DigestInit_ex(context.get(), algo, nullptr);
5198 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
5199 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
5200
5201 std::stringstream ss;
5202 for (auto i = 0u; i < hash_length; ++i) {
5203 ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(hash[i]);
5204 }
5205
5206 return ss.str();
5207}
5208
5209inline std::string MD5(const std::string &s) { return message_digest(s, EVP_md5()); }
5210
5211inline std::string SHA_256(const std::string &s) { return message_digest(s, EVP_sha256()); }
5212
5213inline std::string SHA_512(const std::string &s) { return message_digest(s, EVP_sha512()); }
5214#endif
5215
5216#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5217#ifdef _WIN32
5218// NOTE: This code came up with the following stackoverflow post:
5219// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
5220inline bool load_system_certs_on_windows(X509_STORE *store) {
5221 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY) NULL, L"ROOT");
5222 if (!hStore) {
5223 return false;
5224 }
5225
5226 auto result = false;
5227 PCCERT_CONTEXT pContext = NULL;
5228 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr) {
5229 auto encoded_cert = static_cast<const unsigned char *>(pContext->pbCertEncoded);
5230
5231 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
5232 if (x509) {
5233 X509_STORE_add_cert(store, x509);
5234 X509_free(x509);
5235 result = true;
5236 }
5237 }
5238
5239 CertFreeCertificateContext(pContext);
5240 CertCloseStore(hStore, 0);
5241
5242 return result;
5243}
5244#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
5245#if TARGET_OS_OSX
5246template<typename T> using CFObjectPtr = std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
5247
5248inline void cf_object_ptr_deleter(CFTypeRef obj) {
5249 if (obj) {
5250 CFRelease(obj);
5251 }
5252}
5253
5254inline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5255 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
5256 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, kCFBooleanTrue};
5257
5258 CFObjectPtr<CFDictionaryRef> query(
5259 CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values, sizeof(keys) / sizeof(keys[0]),
5260 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks),
5261 cf_object_ptr_deleter);
5262
5263 if (!query) {
5264 return false;
5265 }
5266
5267 CFTypeRef security_items = nullptr;
5268 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
5269 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
5270 return false;
5271 }
5272
5273 certs.reset(reinterpret_cast<CFArrayRef>(security_items));
5274 return true;
5275}
5276
5277inline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
5278 CFArrayRef root_security_items = nullptr;
5279 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
5280 return false;
5281 }
5282
5283 certs.reset(root_security_items);
5284 return true;
5285}
5286
5287inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
5288 auto result = false;
5289 for (auto i = 0; i < CFArrayGetCount(certs); ++i) {
5290 const auto cert = reinterpret_cast<const __SecCertificate *>(CFArrayGetValueAtIndex(certs, i));
5291
5292 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) {
5293 continue;
5294 }
5295
5296 CFDataRef cert_data = nullptr;
5297 if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) != errSecSuccess) {
5298 continue;
5299 }
5300
5301 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
5302
5303 auto encoded_cert = static_cast<const unsigned char *>(CFDataGetBytePtr(cert_data_ptr.get()));
5304
5305 auto x509 = d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
5306
5307 if (x509) {
5308 X509_STORE_add_cert(store, x509);
5309 X509_free(x509);
5310 result = true;
5311 }
5312 }
5313
5314 return result;
5315}
5316
5317inline bool load_system_certs_on_macos(X509_STORE *store) {
5318 auto result = false;
5319 CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
5320 if (retrieve_certs_from_keychain(certs) && certs) {
5321 result = add_certs_to_x509_store(certs.get(), store);
5322 }
5323
5324 if (retrieve_root_certs_from_keychain(certs) && certs) {
5325 result = add_certs_to_x509_store(certs.get(), store) || result;
5326 }
5327
5328 return result;
5329}
5330#endif // TARGET_OS_OSX
5331#endif // _WIN32
5332#endif // CPPHTTPLIB_OPENSSL_SUPPORT
5333
5334#ifdef _WIN32
5335class WSInit {
5336 public:
5337 WSInit() {
5338 WSADATA wsaData;
5339 if (WSAStartup(0x0002, &wsaData) == 0)
5340 is_valid_ = true;
5341 }
5342
5343 ~WSInit() {
5344 if (is_valid_)
5345 WSACleanup();
5346 }
5347
5348 bool is_valid_ = false;
5349};
5350
5351static WSInit wsinit_;
5352#endif
5353
5354#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5355inline std::pair<std::string, std::string> make_digest_authentication_header(
5356 const Request &req, const std::map<std::string, std::string> &auth, size_t cnonce_count, const std::string &cnonce,
5357 const std::string &username, const std::string &password, bool is_proxy = false) {
5358 std::string nc;
5359 {
5360 std::stringstream ss;
5361 ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
5362 nc = ss.str();
5363 }
5364
5365 std::string qop;
5366 if (auth.find("qop") != auth.end()) {
5367 qop = auth.at("qop");
5368 if (qop.find("auth-int") != std::string::npos) {
5369 qop = "auth-int";
5370 } else if (qop.find("auth") != std::string::npos) {
5371 qop = "auth";
5372 } else {
5373 qop.clear();
5374 }
5375 }
5376
5377 std::string algo = "MD5";
5378 if (auth.find("algorithm") != auth.end()) {
5379 algo = auth.at("algorithm");
5380 }
5381
5382 std::string response;
5383 {
5384 auto H = algo == "SHA-256" ? detail::SHA_256 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
5385
5386 auto A1 = username + ":" + auth.at("realm") + ":" + password;
5387
5388 auto A2 = req.method + ":" + req.path;
5389 if (qop == "auth-int") {
5390 A2 += ":" + H(req.body);
5391 }
5392
5393 if (qop.empty()) {
5394 response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
5395 } else {
5396 response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2));
5397 }
5398 }
5399
5400 auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
5401
5402 auto field =
5403 "Digest username=\"" + username + "\", realm=\"" + auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
5404 "\", uri=\"" + req.path + "\", algorithm=" + algo +
5405 (qop.empty() ? ", response=\"" : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + cnonce + "\", response=\"") +
5406 response + "\"" + (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
5407
5408 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5409 return std::make_pair(key, field);
5410}
5411#endif
5412
5413inline bool parse_www_authenticate(const Response &res, std::map<std::string, std::string> &auth, bool is_proxy) {
5414 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
5415 if (res.has_header(auth_key)) {
5416 static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5417 auto s = res.get_header_value(auth_key);
5418 auto pos = s.find(' ');
5419 if (pos != std::string::npos) {
5420 auto type = s.substr(0, pos);
5421 if (type == "Basic") {
5422 return false;
5423 } else if (type == "Digest") {
5424 s = s.substr(pos + 1);
5425 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
5426 for (auto i = beg; i != std::sregex_iterator(); ++i) {
5427 const auto &m = *i;
5428 auto key = s.substr(static_cast<size_t>(m.position(1)), static_cast<size_t>(m.length(1)));
5429 auto val = m.length(2) > 0 ? s.substr(static_cast<size_t>(m.position(2)), static_cast<size_t>(m.length(2)))
5430 : s.substr(static_cast<size_t>(m.position(3)), static_cast<size_t>(m.length(3)));
5431 auth[key] = val;
5432 }
5433 return true;
5434 }
5435 }
5436 }
5437 return false;
5438}
5439
5440class ContentProviderAdapter {
5441 public:
5442 explicit ContentProviderAdapter(ContentProviderWithoutLength &&content_provider)
5443 : content_provider_(content_provider) {}
5444
5445 bool operator()(size_t offset, size_t, DataSink &sink) { return content_provider_(offset, sink); }
5446
5447 private:
5448 ContentProviderWithoutLength content_provider_;
5449};
5450
5451} // namespace detail
5452
5453inline std::string hosted_at(const std::string &hostname) {
5454 std::vector<std::string> addrs;
5455 hosted_at(hostname, addrs);
5456 if (addrs.empty()) {
5457 return std::string();
5458 }
5459 return addrs[0];
5460}
5461
5462inline void hosted_at(const std::string &hostname, std::vector<std::string> &addrs) {
5463 struct addrinfo hints;
5464 struct addrinfo *result;
5465
5466 memset(&hints, 0, sizeof(struct addrinfo));
5467 hints.ai_family = AF_UNSPEC;
5468 hints.ai_socktype = SOCK_STREAM;
5469 hints.ai_protocol = 0;
5470
5471 if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {
5472#if defined __linux__ && !defined __ANDROID__
5473 res_init();
5474#endif
5475 return;
5476 }
5477 auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5478
5479 for (auto rp = result; rp; rp = rp->ai_next) {
5480 const auto &addr = *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
5481 std::string ip;
5482 auto dummy = -1;
5483 if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip, dummy)) {
5484 addrs.push_back(ip);
5485 }
5486 }
5487}
5488
5489inline std::string append_query_params(const std::string &path, const Params &params) {
5490 std::string path_with_query = path;
5491 const static std::regex re("[^?]+\\?.*");
5492 auto delm = std::regex_match(path, re) ? '&' : '?';
5493 path_with_query += delm + detail::params_to_query_str(params);
5494 return path_with_query;
5495}
5496
5497// Header utilities
5498inline std::pair<std::string, std::string> make_range_header(const Ranges &ranges) {
5499 std::string field = "bytes=";
5500 auto i = 0;
5501 for (const auto &r : ranges) {
5502 if (i != 0) {
5503 field += ", ";
5504 }
5505 if (r.first != -1) {
5506 field += std::to_string(r.first);
5507 }
5508 field += '-';
5509 if (r.second != -1) {
5510 field += std::to_string(r.second);
5511 }
5512 i++;
5513 }
5514 return std::make_pair("Range", std::move(field));
5515}
5516
5517inline std::pair<std::string, std::string> make_basic_authentication_header(const std::string &username,
5518 const std::string &password,
5519 bool is_proxy) {
5520 auto field = "Basic " + detail::base64_encode(username + ":" + password);
5521 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5522 return std::make_pair(key, std::move(field));
5523}
5524
5525inline std::pair<std::string, std::string> make_bearer_token_authentication_header(const std::string &token,
5526 bool is_proxy = false) {
5527 auto field = "Bearer " + token;
5528 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5529 return std::make_pair(key, std::move(field));
5530}
5531
5532// Request implementation
5533inline bool Request::has_header(const std::string &key) const { return detail::has_header(headers, key); }
5534
5535inline std::string Request::get_header_value(const std::string &key, const char *def, size_t id) const {
5536 return detail::get_header_value(headers, key, def, id);
5537}
5538
5539inline size_t Request::get_header_value_count(const std::string &key) const {
5540 auto r = headers.equal_range(key);
5541 return static_cast<size_t>(std::distance(r.first, r.second));
5542}
5543
5544inline void Request::set_header(const std::string &key, const std::string &val) {
5545 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
5546 headers.emplace(key, val);
5547 }
5548}
5549
5550inline bool Request::has_param(const std::string &key) const { return params.find(key) != params.end(); }
5551
5552inline std::string Request::get_param_value(const std::string &key, size_t id) const {
5553 auto rng = params.equal_range(key);
5554 auto it = rng.first;
5555 std::advance(it, static_cast<ssize_t>(id));
5556 if (it != rng.second) {
5557 return it->second;
5558 }
5559 return std::string();
5560}
5561
5562inline size_t Request::get_param_value_count(const std::string &key) const {
5563 auto r = params.equal_range(key);
5564 return static_cast<size_t>(std::distance(r.first, r.second));
5565}
5566
5567inline bool Request::is_multipart_form_data() const {
5568 const auto &content_type = get_header_value("Content-Type");
5569 return !content_type.rfind("multipart/form-data", 0);
5570}
5571
5572inline bool Request::has_file(const std::string &key) const { return files.find(key) != files.end(); }
5573
5574inline MultipartFormData Request::get_file_value(const std::string &key) const {
5575 auto it = files.find(key);
5576 if (it != files.end()) {
5577 return it->second;
5578 }
5579 return MultipartFormData();
5580}
5581
5582inline std::vector<MultipartFormData> Request::get_file_values(const std::string &key) const {
5583 std::vector<MultipartFormData> values;
5584 auto rng = files.equal_range(key);
5585 for (auto it = rng.first; it != rng.second; it++) {
5586 values.push_back(it->second);
5587 }
5588 return values;
5589}
5590
5591// Response implementation
5592inline bool Response::has_header(const std::string &key) const { return headers.find(key) != headers.end(); }
5593
5594inline std::string Response::get_header_value(const std::string &key, const char *def, size_t id) const {
5595 return detail::get_header_value(headers, key, def, id);
5596}
5597
5598inline size_t Response::get_header_value_count(const std::string &key) const {
5599 auto r = headers.equal_range(key);
5600 return static_cast<size_t>(std::distance(r.first, r.second));
5601}
5602
5603inline void Response::set_header(const std::string &key, const std::string &val) {
5604 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
5605 headers.emplace(key, val);
5606 }
5607}
5608
5609inline void Response::set_redirect(const std::string &url, int stat) {
5610 if (!detail::has_crlf(url)) {
5611 set_header("Location", url);
5612 if (300 <= stat && stat < 400) {
5613 this->status = stat;
5614 } else {
5615 this->status = StatusCode::Found_302;
5616 }
5617 }
5618}
5619
5620inline void Response::set_content(const char *s, size_t n, const std::string &content_type) {
5621 body.assign(s, n);
5622
5623 auto rng = headers.equal_range("Content-Type");
5624 headers.erase(rng.first, rng.second);
5625 set_header("Content-Type", content_type);
5626}
5627
5628inline void Response::set_content(const std::string &s, const std::string &content_type) {
5629 set_content(s.data(), s.size(), content_type);
5630}
5631
5632inline void Response::set_content(std::string &&s, const std::string &content_type) {
5633 body = std::move(s);
5634
5635 auto rng = headers.equal_range("Content-Type");
5636 headers.erase(rng.first, rng.second);
5637 set_header("Content-Type", content_type);
5638}
5639
5640inline void Response::set_content_provider(size_t in_length, const std::string &content_type, ContentProvider provider,
5641 ContentProviderResourceReleaser resource_releaser) {
5642 set_header("Content-Type", content_type);
5643 content_length_ = in_length;
5644 if (in_length > 0) {
5645 content_provider_ = std::move(provider);
5646 }
5647 content_provider_resource_releaser_ = std::move(resource_releaser);
5648 is_chunked_content_provider_ = false;
5649}
5650
5651inline void Response::set_content_provider(const std::string &content_type, ContentProviderWithoutLength provider,
5652 ContentProviderResourceReleaser resource_releaser) {
5653 set_header("Content-Type", content_type);
5654 content_length_ = 0;
5655 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5656 content_provider_resource_releaser_ = std::move(resource_releaser);
5657 is_chunked_content_provider_ = false;
5658}
5659
5660inline void Response::set_chunked_content_provider(const std::string &content_type,
5661 ContentProviderWithoutLength provider,
5662 ContentProviderResourceReleaser resource_releaser) {
5663 set_header("Content-Type", content_type);
5664 content_length_ = 0;
5665 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
5666 content_provider_resource_releaser_ = std::move(resource_releaser);
5667 is_chunked_content_provider_ = true;
5668}
5669
5670inline void Response::set_file_content(const std::string &path, const std::string &content_type) {
5671 file_content_path_ = path;
5672 file_content_content_type_ = content_type;
5673}
5674
5675inline void Response::set_file_content(const std::string &path) { file_content_path_ = path; }
5676
5677// Result implementation
5678inline bool Result::has_request_header(const std::string &key) const {
5679 return request_headers_.find(key) != request_headers_.end();
5680}
5681
5682inline std::string Result::get_request_header_value(const std::string &key, const char *def, size_t id) const {
5683 return detail::get_header_value(request_headers_, key, def, id);
5684}
5685
5686inline size_t Result::get_request_header_value_count(const std::string &key) const {
5687 auto r = request_headers_.equal_range(key);
5688 return static_cast<size_t>(std::distance(r.first, r.second));
5689}
5690
5691// Stream implementation
5692inline ssize_t Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); }
5693
5694inline ssize_t Stream::write(const std::string &s) { return write(s.data(), s.size()); }
5695
5696namespace detail {
5697
5698// Socket stream implementation
5699inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
5700 time_t write_timeout_sec, time_t write_timeout_usec)
5701 : sock_(sock),
5702 read_timeout_sec_(read_timeout_sec),
5703 read_timeout_usec_(read_timeout_usec),
5704 write_timeout_sec_(write_timeout_sec),
5705 write_timeout_usec_(write_timeout_usec),
5706 read_buff_(read_buff_size_, 0) {}
5707
5708inline SocketStream::~SocketStream() = default;
5709
5710inline bool SocketStream::is_readable() const { return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; }
5711
5712inline bool SocketStream::is_writable() const {
5713 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_);
5714}
5715
5716inline ssize_t SocketStream::read(char *ptr, size_t size) {
5717#ifdef _WIN32
5718 size = (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
5719#else
5720 size = (std::min)(size, static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
5721#endif
5722
5723 if (read_buff_off_ < read_buff_content_size_) {
5724 auto remaining_size = read_buff_content_size_ - read_buff_off_;
5725 if (size <= remaining_size) {
5726 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
5727 read_buff_off_ += size;
5728 return static_cast<ssize_t>(size);
5729 } else {
5730 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
5731 read_buff_off_ += remaining_size;
5732 return static_cast<ssize_t>(remaining_size);
5733 }
5734 }
5735
5736 if (!is_readable()) {
5737 return -1;
5738 }
5739
5740 read_buff_off_ = 0;
5741 read_buff_content_size_ = 0;
5742
5743 if (size < read_buff_size_) {
5744 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, CPPHTTPLIB_RECV_FLAGS);
5745 if (n <= 0) {
5746 return n;
5747 } else if (n <= static_cast<ssize_t>(size)) {
5748 memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
5749 return n;
5750 } else {
5751 memcpy(ptr, read_buff_.data(), size);
5752 read_buff_off_ = size;
5753 read_buff_content_size_ = static_cast<size_t>(n);
5754 return static_cast<ssize_t>(size);
5755 }
5756 } else {
5757 return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
5758 }
5759}
5760
5761inline ssize_t SocketStream::write(const char *ptr, size_t size) {
5762 if (!is_writable()) {
5763 return -1;
5764 }
5765
5766#if defined(_WIN32) && !defined(_WIN64)
5767 size = (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
5768#endif
5769
5770 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
5771}
5772
5773inline void SocketStream::get_remote_ip_and_port(std::string &ip, int &port) const {
5774 return detail::get_remote_ip_and_port(sock_, ip, port);
5775}
5776
5777inline void SocketStream::get_local_ip_and_port(std::string &ip, int &port) const {
5778 return detail::get_local_ip_and_port(sock_, ip, port);
5779}
5780
5781inline socket_t SocketStream::socket() const { return sock_; }
5782
5783// Buffer stream implementation
5784inline bool BufferStream::is_readable() const { return true; }
5785
5786inline bool BufferStream::is_writable() const { return true; }
5787
5788inline ssize_t BufferStream::read(char *ptr, size_t size) {
5789#if defined(_MSC_VER) && _MSC_VER < 1910
5790 auto len_read = buffer._Copy_s(ptr, size, size, position);
5791#else
5792 auto len_read = buffer.copy(ptr, size, position);
5793#endif
5794 position += static_cast<size_t>(len_read);
5795 return static_cast<ssize_t>(len_read);
5796}
5797
5798inline ssize_t BufferStream::write(const char *ptr, size_t size) {
5799 buffer.append(ptr, size);
5800 return static_cast<ssize_t>(size);
5801}
5802
5803inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, int & /*port*/) const {}
5804
5805inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/, int & /*port*/) const {}
5806
5807inline socket_t BufferStream::socket() const { return 0; }
5808
5809inline const std::string &BufferStream::get_buffer() const { return buffer; }
5810
5811inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
5812 static constexpr char marker[] = "/:";
5813
5814 // One past the last ending position of a path param substring
5815 std::size_t last_param_end = 0;
5816
5817#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5818 // Needed to ensure that parameter names are unique during matcher
5819 // construction
5820 // If exceptions are disabled, only last duplicate path
5821 // parameter will be set
5822 std::unordered_set<std::string> param_name_set;
5823#endif
5824
5825 while (true) {
5826 const auto marker_pos = pattern.find(marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
5827 if (marker_pos == std::string::npos) {
5828 break;
5829 }
5830
5831 static_fragments_.push_back(pattern.substr(last_param_end, marker_pos - last_param_end + 1));
5832
5833 const auto param_name_start = marker_pos + 2;
5834
5835 auto sep_pos = pattern.find(separator, param_name_start);
5836 if (sep_pos == std::string::npos) {
5837 sep_pos = pattern.length();
5838 }
5839
5840 auto param_name = pattern.substr(param_name_start, sep_pos - param_name_start);
5841
5842#ifndef CPPHTTPLIB_NO_EXCEPTIONS
5843 if (param_name_set.find(param_name) != param_name_set.cend()) {
5844 std::string msg =
5845 "Encountered path parameter '" + param_name + "' multiple times in route pattern '" + pattern + "'.";
5846 throw std::invalid_argument(msg);
5847 }
5848#endif
5849
5850 param_names_.push_back(std::move(param_name));
5851
5852 last_param_end = sep_pos + 1;
5853 }
5854
5855 if (last_param_end < pattern.length()) {
5856 static_fragments_.push_back(pattern.substr(last_param_end));
5857 }
5858}
5859
5860inline bool PathParamsMatcher::match(Request &request) const {
5861 request.matches = std::smatch();
5862 request.path_params.clear();
5863 request.path_params.reserve(param_names_.size());
5864
5865 // One past the position at which the path matched the pattern last time
5866 std::size_t starting_pos = 0;
5867 for (size_t i = 0; i < static_fragments_.size(); ++i) {
5868 const auto &fragment = static_fragments_[i];
5869
5870 if (starting_pos + fragment.length() > request.path.length()) {
5871 return false;
5872 }
5873
5874 // Avoid unnecessary allocation by using strncmp instead of substr +
5875 // comparison
5876 if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(), fragment.length()) != 0) {
5877 return false;
5878 }
5879
5880 starting_pos += fragment.length();
5881
5882 // Should only happen when we have a static fragment after a param
5883 // Example: '/users/:id/subscriptions'
5884 // The 'subscriptions' fragment here does not have a corresponding param
5885 if (i >= param_names_.size()) {
5886 continue;
5887 }
5888
5889 auto sep_pos = request.path.find(separator, starting_pos);
5890 if (sep_pos == std::string::npos) {
5891 sep_pos = request.path.length();
5892 }
5893
5894 const auto &param_name = param_names_[i];
5895
5896 request.path_params.emplace(param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
5897
5898 // Mark everything up to '/' as matched
5899 starting_pos = sep_pos + 1;
5900 }
5901 // Returns false if the path is longer than the pattern
5902 return starting_pos >= request.path.length();
5903}
5904
5905inline bool RegexMatcher::match(Request &request) const {
5906 request.path_params.clear();
5907 return std::regex_match(request.path, request.matches, regex_);
5908}
5909
5910} // namespace detail
5911
5912// HTTP server implementation
5913inline Server::Server() : new_task_queue([] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
5914#ifndef _WIN32
5915 signal(SIGPIPE, SIG_IGN);
5916#endif
5917}
5918
5919inline Server::~Server() = default;
5920
5921inline std::unique_ptr<detail::MatcherBase> Server::make_matcher(const std::string &pattern) {
5922 if (pattern.find("/:") != std::string::npos) {
5923 return detail::make_unique<detail::PathParamsMatcher>(pattern);
5924 } else {
5925 return detail::make_unique<detail::RegexMatcher>(pattern);
5926 }
5927}
5928
5929inline Server &Server::Get(const std::string &pattern, Handler handler) {
5930 get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5931 return *this;
5932}
5933
5934inline Server &Server::Post(const std::string &pattern, Handler handler) {
5935 post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5936 return *this;
5937}
5938
5939inline Server &Server::Post(const std::string &pattern, HandlerWithContentReader handler) {
5940 post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5941 return *this;
5942}
5943
5944inline Server &Server::Put(const std::string &pattern, Handler handler) {
5945 put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5946 return *this;
5947}
5948
5949inline Server &Server::Put(const std::string &pattern, HandlerWithContentReader handler) {
5950 put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5951 return *this;
5952}
5953
5954inline Server &Server::Patch(const std::string &pattern, Handler handler) {
5955 patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5956 return *this;
5957}
5958
5959inline Server &Server::Patch(const std::string &pattern, HandlerWithContentReader handler) {
5960 patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5961 return *this;
5962}
5963
5964inline Server &Server::Delete(const std::string &pattern, Handler handler) {
5965 delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5966 return *this;
5967}
5968
5969inline Server &Server::Delete(const std::string &pattern, HandlerWithContentReader handler) {
5970 delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), std::move(handler));
5971 return *this;
5972}
5973
5974inline Server &Server::Options(const std::string &pattern, Handler handler) {
5975 options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
5976 return *this;
5977}
5978
5979inline bool Server::set_base_dir(const std::string &dir, const std::string &mount_point) {
5980 return set_mount_point(mount_point, dir);
5981}
5982
5983inline bool Server::set_mount_point(const std::string &mount_point, const std::string &dir, Headers headers) {
5984 detail::FileStat stat(dir);
5985 if (stat.is_dir()) {
5986 std::string mnt = !mount_point.empty() ? mount_point : "/";
5987 if (!mnt.empty() && mnt[0] == '/') {
5988 base_dirs_.push_back({mnt, dir, std::move(headers)});
5989 return true;
5990 }
5991 }
5992 return false;
5993}
5994
5995inline bool Server::remove_mount_point(const std::string &mount_point) {
5996 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
5997 if (it->mount_point == mount_point) {
5998 base_dirs_.erase(it);
5999 return true;
6000 }
6001 }
6002 return false;
6003}
6004
6005inline Server &Server::set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime) {
6006 file_extension_and_mimetype_map_[ext] = mime;
6007 return *this;
6008}
6009
6010inline Server &Server::set_default_file_mimetype(const std::string &mime) {
6011 default_file_mimetype_ = mime;
6012 return *this;
6013}
6014
6015inline Server &Server::set_file_request_handler(Handler handler) {
6016 file_request_handler_ = std::move(handler);
6017 return *this;
6018}
6019
6020inline Server &Server::set_error_handler_core(HandlerWithResponse handler, std::true_type) {
6021 error_handler_ = std::move(handler);
6022 return *this;
6023}
6024
6025inline Server &Server::set_error_handler_core(Handler handler, std::false_type) {
6026 error_handler_ = [handler](const Request &req, Response &res) {
6027 handler(req, res);
6028 return HandlerResponse::Handled;
6029 };
6030 return *this;
6031}
6032
6033inline Server &Server::set_exception_handler(ExceptionHandler handler) {
6034 exception_handler_ = std::move(handler);
6035 return *this;
6036}
6037
6038inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
6039 pre_routing_handler_ = std::move(handler);
6040 return *this;
6041}
6042
6043inline Server &Server::set_post_routing_handler(Handler handler) {
6044 post_routing_handler_ = std::move(handler);
6045 return *this;
6046}
6047
6048inline Server &Server::set_logger(Logger logger) {
6049 logger_ = std::move(logger);
6050 return *this;
6051}
6052
6053inline Server &Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
6054 expect_100_continue_handler_ = std::move(handler);
6055 return *this;
6056}
6057
6058inline Server &Server::set_address_family(int family) {
6059 address_family_ = family;
6060 return *this;
6061}
6062
6063inline Server &Server::set_tcp_nodelay(bool on) {
6064 tcp_nodelay_ = on;
6065 return *this;
6066}
6067
6068inline Server &Server::set_ipv6_v6only(bool on) {
6069 ipv6_v6only_ = on;
6070 return *this;
6071}
6072
6073inline Server &Server::set_socket_options(SocketOptions socket_options) {
6074 socket_options_ = std::move(socket_options);
6075 return *this;
6076}
6077
6078inline Server &Server::set_default_headers(Headers headers) {
6079 default_headers_ = std::move(headers);
6080 return *this;
6081}
6082
6083inline Server &Server::set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer) {
6084 header_writer_ = writer;
6085 return *this;
6086}
6087
6088inline Server &Server::set_keep_alive_max_count(size_t count) {
6089 keep_alive_max_count_ = count;
6090 return *this;
6091}
6092
6093inline Server &Server::set_keep_alive_timeout(time_t sec) {
6094 keep_alive_timeout_sec_ = sec;
6095 return *this;
6096}
6097
6098inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
6099 read_timeout_sec_ = sec;
6100 read_timeout_usec_ = usec;
6101 return *this;
6102}
6103
6104inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
6105 write_timeout_sec_ = sec;
6106 write_timeout_usec_ = usec;
6107 return *this;
6108}
6109
6110inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
6111 idle_interval_sec_ = sec;
6112 idle_interval_usec_ = usec;
6113 return *this;
6114}
6115
6116inline Server &Server::set_payload_max_length(size_t length) {
6117 payload_max_length_ = length;
6118 return *this;
6119}
6120
6121inline bool Server::bind_to_port(const std::string &host, int port, int socket_flags) {
6122 auto ret = bind_internal(host, port, socket_flags);
6123 if (ret == -1) {
6124 is_decommisioned = true;
6125 }
6126 return ret >= 0;
6127}
6128inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
6129 auto ret = bind_internal(host, 0, socket_flags);
6130 if (ret == -1) {
6131 is_decommisioned = true;
6132 }
6133 return ret;
6134}
6135
6136inline bool Server::listen_after_bind() { return listen_internal(); }
6137
6138inline bool Server::listen(const std::string &host, int port, int socket_flags) {
6139 return bind_to_port(host, port, socket_flags) && listen_internal();
6140}
6141
6142inline bool Server::is_running() const { return is_running_; }
6143
6144inline void Server::wait_until_ready() const {
6145 while (!is_running_ && !is_decommisioned) {
6146 std::this_thread::sleep_for(std::chrono::milliseconds{1});
6147 }
6148}
6149
6150inline void Server::stop() {
6151 if (is_running_) {
6152 assert(svr_sock_ != INVALID_SOCKET);
6153 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
6154 detail::shutdown_socket(sock);
6155 detail::close_socket(sock);
6156 }
6157 is_decommisioned = false;
6158}
6159
6160inline void Server::decommission() { is_decommisioned = true; }
6161
6162inline bool Server::parse_request_line(const char *s, Request &req) const {
6163 auto len = strlen(s);
6164 if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') {
6165 return false;
6166 }
6167 len -= 2;
6168
6169 {
6170 size_t count = 0;
6171
6172 detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
6173 switch (count) {
6174 case 0:
6175 req.method = std::string(b, e);
6176 break;
6177 case 1:
6178 req.target = std::string(b, e);
6179 break;
6180 case 2:
6181 req.version = std::string(b, e);
6182 break;
6183 default:
6184 break;
6185 }
6186 count++;
6187 });
6188
6189 if (count != 3) {
6190 return false;
6191 }
6192 }
6193
6194 static const std::set<std::string> methods{"GET", "HEAD", "POST", "PUT", "DELETE",
6195 "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
6196
6197 if (methods.find(req.method) == methods.end()) {
6198 return false;
6199 }
6200
6201 if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") {
6202 return false;
6203 }
6204
6205 {
6206 // Skip URL fragment
6207 for (size_t i = 0; i < req.target.size(); i++) {
6208 if (req.target[i] == '#') {
6209 req.target.erase(i);
6210 break;
6211 }
6212 }
6213
6214 detail::divide(req.target, '?',
6215 [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data, std::size_t rhs_size) {
6216 req.path = detail::decode_url(std::string(lhs_data, lhs_size), false);
6217 detail::parse_query_text(rhs_data, rhs_size, req.params);
6218 });
6219 }
6220
6221 return true;
6222}
6223
6224inline bool Server::write_response(Stream &strm, bool close_connection, Request &req, Response &res) {
6225 // NOTE: `req.ranges` should be empty, otherwise it will be applied
6226 // incorrectly to the error content.
6227 req.ranges.clear();
6228 return write_response_core(strm, close_connection, req, res, false);
6229}
6230
6231inline bool Server::write_response_with_content(Stream &strm, bool close_connection, const Request &req,
6232 Response &res) {
6233 return write_response_core(strm, close_connection, req, res, true);
6234}
6235
6236inline bool Server::write_response_core(Stream &strm, bool close_connection, const Request &req, Response &res,
6237 bool need_apply_ranges) {
6238 assert(res.status != -1);
6239
6240 if (400 <= res.status && error_handler_ && error_handler_(req, res) == HandlerResponse::Handled) {
6241 need_apply_ranges = true;
6242 }
6243
6244 std::string content_type;
6245 std::string boundary;
6246 if (need_apply_ranges) {
6247 apply_ranges(req, res, content_type, boundary);
6248 }
6249
6250 // Prepare additional headers
6251 if (close_connection || req.get_header_value("Connection") == "close") {
6252 res.set_header("Connection", "close");
6253 } else {
6254 std::string s = "timeout=";
6255 s += std::to_string(keep_alive_timeout_sec_);
6256 s += ", max=";
6257 s += std::to_string(keep_alive_max_count_);
6258 res.set_header("Keep-Alive", s);
6259 }
6260
6261 if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) && !res.has_header("Content-Type")) {
6262 res.set_header("Content-Type", "text/plain");
6263 }
6264
6265 if (res.body.empty() && !res.content_length_ && !res.content_provider_ && !res.has_header("Content-Length")) {
6266 res.set_header("Content-Length", "0");
6267 }
6268
6269 if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
6270 res.set_header("Accept-Ranges", "bytes");
6271 }
6272
6273 if (post_routing_handler_) {
6274 post_routing_handler_(req, res);
6275 }
6276
6277 // Response line and headers
6278 {
6279 detail::BufferStream bstrm;
6280 if (!detail::write_response_line(bstrm, res.status)) {
6281 return false;
6282 }
6283 if (!header_writer_(bstrm, res.headers)) {
6284 return false;
6285 }
6286
6287 // Flush buffer
6288 auto &data = bstrm.get_buffer();
6289 detail::write_data(strm, data.data(), data.size());
6290 }
6291
6292 // Body
6293 auto ret = true;
6294 if (req.method != "HEAD") {
6295 if (!res.body.empty()) {
6296 if (!detail::write_data(strm, res.body.data(), res.body.size())) {
6297 ret = false;
6298 }
6299 } else if (res.content_provider_) {
6300 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
6301 res.content_provider_success_ = true;
6302 } else {
6303 ret = false;
6304 }
6305 }
6306 }
6307
6308 // Log
6309 if (logger_) {
6310 logger_(req, res);
6311 }
6312
6313 return ret;
6314}
6315
6316inline bool Server::write_content_with_provider(Stream &strm, const Request &req, Response &res,
6317 const std::string &boundary, const std::string &content_type) {
6318 auto is_shutting_down = [this]() { return this->svr_sock_ == INVALID_SOCKET; };
6319
6320 if (res.content_length_ > 0) {
6321 if (req.ranges.empty()) {
6322 return detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down);
6323 } else if (req.ranges.size() == 1) {
6324 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.content_length_);
6325
6326 return detail::write_content(strm, res.content_provider_, offset_and_length.first, offset_and_length.second,
6327 is_shutting_down);
6328 } else {
6329 return detail::write_multipart_ranges_data(strm, req, res, boundary, content_type, res.content_length_,
6330 is_shutting_down);
6331 }
6332 } else {
6333 if (res.is_chunked_content_provider_) {
6334 auto type = detail::encoding_type(req, res);
6335
6336 std::unique_ptr<detail::compressor> compressor;
6337 if (type == detail::EncodingType::Gzip) {
6338#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6339 compressor = detail::make_unique<detail::gzip_compressor>();
6340#endif
6341 } else if (type == detail::EncodingType::Brotli) {
6342#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6343 compressor = detail::make_unique<detail::brotli_compressor>();
6344#endif
6345 } else {
6346 compressor = detail::make_unique<detail::nocompressor>();
6347 }
6348 assert(compressor != nullptr);
6349
6350 return detail::write_content_chunked(strm, res.content_provider_, is_shutting_down, *compressor);
6351 } else {
6352 return detail::write_content_without_length(strm, res.content_provider_, is_shutting_down);
6353 }
6354 }
6355}
6356
6357inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
6358 MultipartFormDataMap::iterator cur;
6359 auto file_count = 0;
6360 if (read_content_core(
6361 strm, req, res,
6362 // Regular
6363 [&](const char *buf, size_t n) {
6364 if (req.body.size() + n > req.body.max_size()) {
6365 return false;
6366 }
6367 req.body.append(buf, n);
6368 return true;
6369 },
6370 // Multipart
6371 [&](const MultipartFormData &file) {
6372 if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
6373 return false;
6374 }
6375 cur = req.files.emplace(file.name, file);
6376 return true;
6377 },
6378 [&](const char *buf, size_t n) {
6379 auto &content = cur->second.content;
6380 if (content.size() + n > content.max_size()) {
6381 return false;
6382 }
6383 content.append(buf, n);
6384 return true;
6385 })) {
6386 const auto &content_type = req.get_header_value("Content-Type");
6387 if (!content_type.find("application/x-www-form-urlencoded")) {
6388 if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
6389 res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
6390 return false;
6391 }
6392 detail::parse_query_text(req.body, req.params);
6393 }
6394 return true;
6395 }
6396 return false;
6397}
6398
6399inline bool Server::read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
6400 ContentReceiver receiver,
6401 MultipartContentHeader multipart_header,
6402 ContentReceiver multipart_receiver) {
6403 return read_content_core(strm, req, res, std::move(receiver), std::move(multipart_header),
6404 std::move(multipart_receiver));
6405}
6406
6407inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver,
6408 MultipartContentHeader multipart_header,
6409 ContentReceiver multipart_receiver) const {
6410 detail::MultipartFormDataParser multipart_form_data_parser;
6411 ContentReceiverWithProgress out;
6412
6413 if (req.is_multipart_form_data()) {
6414 const auto &content_type = req.get_header_value("Content-Type");
6415 std::string boundary;
6416 if (!detail::parse_multipart_boundary(content_type, boundary)) {
6417 res.status = StatusCode::BadRequest_400;
6418 return false;
6419 }
6420
6421 multipart_form_data_parser.set_boundary(std::move(boundary));
6422 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
6423 /* For debug
6424 size_t pos = 0;
6425 while (pos < n) {
6426 auto read_size = (std::min)<size_t>(1, n - pos);
6427 auto ret = multipart_form_data_parser.parse(
6428 buf + pos, read_size, multipart_receiver, multipart_header);
6429 if (!ret) { return false; }
6430 pos += read_size;
6431 }
6432 return true;
6433 */
6434 return multipart_form_data_parser.parse(buf, n, multipart_receiver, multipart_header);
6435 };
6436 } else {
6437 out = [receiver](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) { return receiver(buf, n); };
6438 }
6439
6440 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
6441 return true;
6442 }
6443
6444 if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, out, true)) {
6445 return false;
6446 }
6447
6448 if (req.is_multipart_form_data()) {
6449 if (!multipart_form_data_parser.is_valid()) {
6450 res.status = StatusCode::BadRequest_400;
6451 return false;
6452 }
6453 }
6454
6455 return true;
6456}
6457
6458inline bool Server::handle_file_request(const Request &req, Response &res, bool head) {
6459 for (const auto &entry : base_dirs_) {
6460 // Prefix match
6461 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
6462 std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
6463 if (detail::is_valid_path(sub_path)) {
6464 auto path = entry.base_dir + sub_path;
6465 if (path.back() == '/') {
6466 path += "index.html";
6467 }
6468
6469 detail::FileStat stat(path);
6470
6471 if (stat.is_dir()) {
6472 res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
6473 return true;
6474 }
6475
6476 if (stat.is_file()) {
6477 for (const auto &kv : entry.headers) {
6478 res.set_header(kv.first, kv.second);
6479 }
6480
6481 auto mm = std::make_shared<detail::mmap>(path.c_str());
6482 if (!mm->is_open()) {
6483 return false;
6484 }
6485
6486 res.set_content_provider(
6487 mm->size(), detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_),
6488 [mm](size_t offset, size_t length, DataSink &sink) -> bool {
6489 sink.write(mm->data() + offset, length);
6490 return true;
6491 });
6492
6493 if (!head && file_request_handler_) {
6494 file_request_handler_(req, res);
6495 }
6496
6497 return true;
6498 }
6499 }
6500 }
6501 }
6502 return false;
6503}
6504
6505inline socket_t Server::create_server_socket(const std::string &host, int port, int socket_flags,
6506 SocketOptions socket_options) const {
6507 return detail::create_socket(host, std::string(), port, address_family_, socket_flags, tcp_nodelay_, ipv6_v6only_,
6508 std::move(socket_options),
6509 [](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
6510 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
6511 return false;
6512 }
6513 if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
6514 return false;
6515 }
6516 return true;
6517 });
6518}
6519
6520inline int Server::bind_internal(const std::string &host, int port, int socket_flags) {
6521 if (is_decommisioned) {
6522 return -1;
6523 }
6524
6525 if (!is_valid()) {
6526 return -1;
6527 }
6528
6529 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
6530 if (svr_sock_ == INVALID_SOCKET) {
6531 return -1;
6532 }
6533
6534 if (port == 0) {
6535 struct sockaddr_storage addr;
6536 socklen_t addr_len = sizeof(addr);
6537 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr), &addr_len) == -1) {
6538 return -1;
6539 }
6540 if (addr.ss_family == AF_INET) {
6541 return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
6542 } else if (addr.ss_family == AF_INET6) {
6543 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
6544 } else {
6545 return -1;
6546 }
6547 } else {
6548 return port;
6549 }
6550}
6551
6552inline bool Server::listen_internal() {
6553 if (is_decommisioned) {
6554 return false;
6555 }
6556
6557 auto ret = true;
6558 is_running_ = true;
6559 auto se = detail::scope_exit([&]() { is_running_ = false; });
6560
6561 {
6562 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
6563
6564 while (svr_sock_ != INVALID_SOCKET) {
6565#ifndef _WIN32
6566 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
6567#endif
6568 auto val = detail::select_read(svr_sock_, idle_interval_sec_, idle_interval_usec_);
6569 if (val == 0) { // Timeout
6570 task_queue->on_idle();
6571 continue;
6572 }
6573#ifndef _WIN32
6574 }
6575#endif
6576
6577#if defined _WIN32
6578 // sockets conneced via WASAccept inherit flags NO_HANDLE_INHERIT,
6579 // OVERLAPPED
6580 socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
6581#elif defined SOCK_CLOEXEC
6582 socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
6583#else
6584 socket_t sock = accept(svr_sock_, nullptr, nullptr);
6585#endif
6586
6587 if (sock == INVALID_SOCKET) {
6588 if (errno == EMFILE) {
6589 // The per-process limit of open file descriptors has been reached.
6590 // Try to accept new connections after a short sleep.
6591 std::this_thread::sleep_for(std::chrono::microseconds{1});
6592 continue;
6593 } else if (errno == EINTR || errno == EAGAIN) {
6594 continue;
6595 }
6596 if (svr_sock_ != INVALID_SOCKET) {
6597 detail::close_socket(svr_sock_);
6598 ret = false;
6599 } else {
6600 ; // The server socket was closed by user.
6601 }
6602 break;
6603 }
6604
6605 {
6606#ifdef _WIN32
6607 auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 + read_timeout_usec_ / 1000);
6608 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char *>(&timeout), sizeof(timeout));
6609#else
6610 timeval tv;
6611 tv.tv_sec = static_cast<long>(read_timeout_sec_);
6612 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);
6613 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const void *>(&tv), sizeof(tv));
6614#endif
6615 }
6616 {
6617#ifdef _WIN32
6618 auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 + write_timeout_usec_ / 1000);
6619 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char *>(&timeout), sizeof(timeout));
6620#else
6621 timeval tv;
6622 tv.tv_sec = static_cast<long>(write_timeout_sec_);
6623 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);
6624 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const void *>(&tv), sizeof(tv));
6625#endif
6626 }
6627
6628 if (!task_queue->enqueue([this, sock]() { process_and_close_socket(sock); })) {
6629 detail::shutdown_socket(sock);
6630 detail::close_socket(sock);
6631 }
6632 }
6633
6634 task_queue->shutdown();
6635 }
6636
6637 is_decommisioned = !ret;
6638 return ret;
6639}
6640
6641inline bool Server::routing(Request &req, Response &res, Stream &strm) {
6642 if (pre_routing_handler_ && pre_routing_handler_(req, res) == HandlerResponse::Handled) {
6643 return true;
6644 }
6645
6646 // File handler
6647 auto is_head_request = req.method == "HEAD";
6648 if ((req.method == "GET" || is_head_request) && handle_file_request(req, res, is_head_request)) {
6649 return true;
6650 }
6651
6652 if (detail::expect_content(req)) {
6653 // Content reader handler
6654 {
6655 ContentReader reader(
6656 [&](ContentReceiver receiver) {
6657 return read_content_with_content_receiver(strm, req, res, std::move(receiver), nullptr, nullptr);
6658 },
6659 [&](MultipartContentHeader header, ContentReceiver receiver) {
6660 return read_content_with_content_receiver(strm, req, res, nullptr, std::move(header), std::move(receiver));
6661 });
6662
6663 if (req.method == "POST") {
6664 if (dispatch_request_for_content_reader(req, res, std::move(reader), post_handlers_for_content_reader_)) {
6665 return true;
6666 }
6667 } else if (req.method == "PUT") {
6668 if (dispatch_request_for_content_reader(req, res, std::move(reader), put_handlers_for_content_reader_)) {
6669 return true;
6670 }
6671 } else if (req.method == "PATCH") {
6672 if (dispatch_request_for_content_reader(req, res, std::move(reader), patch_handlers_for_content_reader_)) {
6673 return true;
6674 }
6675 } else if (req.method == "DELETE") {
6676 if (dispatch_request_for_content_reader(req, res, std::move(reader), delete_handlers_for_content_reader_)) {
6677 return true;
6678 }
6679 }
6680 }
6681
6682 // Read content into `req.body`
6683 if (!read_content(strm, req, res)) {
6684 return false;
6685 }
6686 }
6687
6688 // Regular handler
6689 if (req.method == "GET" || req.method == "HEAD") {
6690 return dispatch_request(req, res, get_handlers_);
6691 } else if (req.method == "POST") {
6692 return dispatch_request(req, res, post_handlers_);
6693 } else if (req.method == "PUT") {
6694 return dispatch_request(req, res, put_handlers_);
6695 } else if (req.method == "DELETE") {
6696 return dispatch_request(req, res, delete_handlers_);
6697 } else if (req.method == "OPTIONS") {
6698 return dispatch_request(req, res, options_handlers_);
6699 } else if (req.method == "PATCH") {
6700 return dispatch_request(req, res, patch_handlers_);
6701 }
6702
6703 res.status = StatusCode::BadRequest_400;
6704 return false;
6705}
6706
6707inline bool Server::dispatch_request(Request &req, Response &res, const Handlers &handlers) const {
6708 for (const auto &x : handlers) {
6709 const auto &matcher = x.first;
6710 const auto &handler = x.second;
6711
6712 if (matcher->match(req)) {
6713 handler(req, res);
6714 return true;
6715 }
6716 }
6717 return false;
6718}
6719
6720inline void Server::apply_ranges(const Request &req, Response &res, std::string &content_type,
6721 std::string &boundary) const {
6722 if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
6723 auto it = res.headers.find("Content-Type");
6724 if (it != res.headers.end()) {
6725 content_type = it->second;
6726 res.headers.erase(it);
6727 }
6728
6729 boundary = detail::make_multipart_data_boundary();
6730
6731 res.set_header("Content-Type", "multipart/byteranges; boundary=" + boundary);
6732 }
6733
6734 auto type = detail::encoding_type(req, res);
6735
6736 if (res.body.empty()) {
6737 if (res.content_length_ > 0) {
6738 size_t length = 0;
6739 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6740 length = res.content_length_;
6741 } else if (req.ranges.size() == 1) {
6742 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.content_length_);
6743
6744 length = offset_and_length.second;
6745
6746 auto content_range = detail::make_content_range_header_field(offset_and_length, res.content_length_);
6747 res.set_header("Content-Range", content_range);
6748 } else {
6749 length = detail::get_multipart_ranges_data_length(req, boundary, content_type, res.content_length_);
6750 }
6751 res.set_header("Content-Length", std::to_string(length));
6752 } else {
6753 if (res.content_provider_) {
6754 if (res.is_chunked_content_provider_) {
6755 res.set_header("Transfer-Encoding", "chunked");
6756 if (type == detail::EncodingType::Gzip) {
6757 res.set_header("Content-Encoding", "gzip");
6758 } else if (type == detail::EncodingType::Brotli) {
6759 res.set_header("Content-Encoding", "br");
6760 }
6761 }
6762 }
6763 }
6764 } else {
6765 if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6766 ;
6767 } else if (req.ranges.size() == 1) {
6768 auto offset_and_length = detail::get_range_offset_and_length(req.ranges[0], res.body.size());
6769 auto offset = offset_and_length.first;
6770 auto length = offset_and_length.second;
6771
6772 auto content_range = detail::make_content_range_header_field(offset_and_length, res.body.size());
6773 res.set_header("Content-Range", content_range);
6774
6775 assert(offset + length <= res.body.size());
6776 res.body = res.body.substr(offset, length);
6777 } else {
6778 std::string data;
6779 detail::make_multipart_ranges_data(req, res, boundary, content_type, res.body.size(), data);
6780 res.body.swap(data);
6781 }
6782
6783 if (type != detail::EncodingType::None) {
6784 std::unique_ptr<detail::compressor> compressor;
6785 std::string content_encoding;
6786
6787 if (type == detail::EncodingType::Gzip) {
6788#ifdef CPPHTTPLIB_ZLIB_SUPPORT
6789 compressor = detail::make_unique<detail::gzip_compressor>();
6790 content_encoding = "gzip";
6791#endif
6792 } else if (type == detail::EncodingType::Brotli) {
6793#ifdef CPPHTTPLIB_BROTLI_SUPPORT
6794 compressor = detail::make_unique<detail::brotli_compressor>();
6795 content_encoding = "br";
6796#endif
6797 }
6798
6799 if (compressor) {
6800 std::string compressed;
6801 if (compressor->compress(res.body.data(), res.body.size(), true, [&](const char *data, size_t data_len) {
6802 compressed.append(data, data_len);
6803 return true;
6804 })) {
6805 res.body.swap(compressed);
6806 res.set_header("Content-Encoding", content_encoding);
6807 }
6808 }
6809 }
6810
6811 auto length = std::to_string(res.body.size());
6812 res.set_header("Content-Length", length);
6813 }
6814}
6815
6816inline bool Server::dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
6817 const HandlersForContentReader &handlers) const {
6818 for (const auto &x : handlers) {
6819 const auto &matcher = x.first;
6820 const auto &handler = x.second;
6821
6822 if (matcher->match(req)) {
6823 handler(req, res, content_reader);
6824 return true;
6825 }
6826 }
6827 return false;
6828}
6829
6830inline bool Server::process_request(Stream &strm, const std::string &remote_addr, int remote_port,
6831 const std::string &local_addr, int local_port, bool close_connection,
6832 bool &connection_closed, const std::function<void(Request &)> &setup_request) {
6833 std::array<char, 2048> buf{};
6834
6835 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
6836
6837 // Connection has been closed on client
6838 if (!line_reader.getline()) {
6839 return false;
6840 }
6841
6842 Request req;
6843
6844 Response res;
6845 res.version = "HTTP/1.1";
6846 res.headers = default_headers_;
6847
6848#ifdef _WIN32
6849 // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).
6850#else
6851#ifndef CPPHTTPLIB_USE_POLL
6852 // Socket file descriptor exceeded FD_SETSIZE...
6853 if (strm.socket() >= FD_SETSIZE) {
6854 Headers dummy;
6855 detail::read_headers(strm, dummy);
6856 res.status = StatusCode::InternalServerError_500;
6857 return write_response(strm, close_connection, req, res);
6858 }
6859#endif
6860#endif
6861
6862 // Check if the request URI doesn't exceed the limit
6863 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
6864 Headers dummy;
6865 detail::read_headers(strm, dummy);
6866 res.status = StatusCode::UriTooLong_414;
6867 return write_response(strm, close_connection, req, res);
6868 }
6869
6870 // Request line and headers
6871 if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) {
6872 res.status = StatusCode::BadRequest_400;
6873 return write_response(strm, close_connection, req, res);
6874 }
6875
6876 if (req.get_header_value("Connection") == "close") {
6877 connection_closed = true;
6878 }
6879
6880 if (req.version == "HTTP/1.0" && req.get_header_value("Connection") != "Keep-Alive") {
6881 connection_closed = true;
6882 }
6883
6884 req.remote_addr = remote_addr;
6885 req.remote_port = remote_port;
6886 req.set_header("REMOTE_ADDR", req.remote_addr);
6887 req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
6888
6889 req.local_addr = local_addr;
6890 req.local_port = local_port;
6891 req.set_header("LOCAL_ADDR", req.local_addr);
6892 req.set_header("LOCAL_PORT", std::to_string(req.local_port));
6893
6894 if (req.has_header("Range")) {
6895 const auto &range_header_value = req.get_header_value("Range");
6896 if (!detail::parse_range_header(range_header_value, req.ranges)) {
6897 res.status = StatusCode::RangeNotSatisfiable_416;
6898 return write_response(strm, close_connection, req, res);
6899 }
6900 }
6901
6902 if (setup_request) {
6903 setup_request(req);
6904 }
6905
6906 if (req.get_header_value("Expect") == "100-continue") {
6907 int status = StatusCode::Continue_100;
6908 if (expect_100_continue_handler_) {
6909 status = expect_100_continue_handler_(req, res);
6910 }
6911 switch (status) {
6912 case StatusCode::Continue_100:
6913 case StatusCode::ExpectationFailed_417:
6914 detail::write_response_line(strm, status);
6915 strm.write("\r\n");
6916 break;
6917 default:
6918 connection_closed = true;
6919 return write_response(strm, true, req, res);
6920 }
6921 }
6922
6923 // Routing
6924 auto routed = false;
6925#ifdef CPPHTTPLIB_NO_EXCEPTIONS
6926 routed = routing(req, res, strm);
6927#else
6928 try {
6929 routed = routing(req, res, strm);
6930 } catch (std::exception &e) {
6931 if (exception_handler_) {
6932 auto ep = std::current_exception();
6933 exception_handler_(req, res, ep);
6934 routed = true;
6935 } else {
6936 res.status = StatusCode::InternalServerError_500;
6937 std::string val;
6938 auto s = e.what();
6939 for (size_t i = 0; s[i]; i++) {
6940 switch (s[i]) {
6941 case '\r':
6942 val += "\\r";
6943 break;
6944 case '\n':
6945 val += "\\n";
6946 break;
6947 default:
6948 val += s[i];
6949 break;
6950 }
6951 }
6952 res.set_header("EXCEPTION_WHAT", val);
6953 }
6954 } catch (...) {
6955 if (exception_handler_) {
6956 auto ep = std::current_exception();
6957 exception_handler_(req, res, ep);
6958 routed = true;
6959 } else {
6960 res.status = StatusCode::InternalServerError_500;
6961 res.set_header("EXCEPTION_WHAT", "UNKNOWN");
6962 }
6963 }
6964#endif
6965 if (routed) {
6966 if (res.status == -1) {
6967 res.status = req.ranges.empty() ? StatusCode::OK_200 : StatusCode::PartialContent_206;
6968 }
6969
6970 if (detail::range_error(req, res)) {
6971 res.body.clear();
6972 res.content_length_ = 0;
6973 res.content_provider_ = nullptr;
6974 res.status = StatusCode::RangeNotSatisfiable_416;
6975 return write_response(strm, close_connection, req, res);
6976 }
6977
6978 // Serve file content by using a content provider
6979 if (!res.file_content_path_.empty()) {
6980 const auto &path = res.file_content_path_;
6981 auto mm = std::make_shared<detail::mmap>(path.c_str());
6982 if (!mm->is_open()) {
6983 res.body.clear();
6984 res.content_length_ = 0;
6985 res.content_provider_ = nullptr;
6986 res.status = StatusCode::NotFound_404;
6987 return write_response(strm, close_connection, req, res);
6988 }
6989
6990 auto content_type = res.file_content_content_type_;
6991 if (content_type.empty()) {
6992 content_type = detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_);
6993 }
6994
6995 res.set_content_provider(mm->size(), content_type, [mm](size_t offset, size_t length, DataSink &sink) -> bool {
6996 sink.write(mm->data() + offset, length);
6997 return true;
6998 });
6999 }
7000
7001 return write_response_with_content(strm, close_connection, req, res);
7002 } else {
7003 if (res.status == -1) {
7004 res.status = StatusCode::NotFound_404;
7005 }
7006
7007 return write_response(strm, close_connection, req, res);
7008 }
7009}
7010
7011inline bool Server::is_valid() const { return true; }
7012
7013inline bool Server::process_and_close_socket(socket_t sock) {
7014 std::string remote_addr;
7015 int remote_port = 0;
7016 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
7017
7018 std::string local_addr;
7019 int local_port = 0;
7020 detail::get_local_ip_and_port(sock, local_addr, local_port);
7021
7022 auto ret = detail::process_server_socket(
7023 svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_,
7024 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm, bool close_connection, bool &connection_closed) {
7025 return process_request(strm, remote_addr, remote_port, local_addr, local_port, close_connection,
7026 connection_closed, nullptr);
7027 });
7028
7029 detail::shutdown_socket(sock);
7030 detail::close_socket(sock);
7031 return ret;
7032}
7033
7034// HTTP client implementation
7035inline ClientImpl::ClientImpl(const std::string &host) : ClientImpl(host, 80, std::string(), std::string()) {}
7036
7037inline ClientImpl::ClientImpl(const std::string &host, int port)
7038 : ClientImpl(host, port, std::string(), std::string()) {}
7039
7040inline ClientImpl::ClientImpl(const std::string &host, int port, const std::string &client_cert_path,
7041 const std::string &client_key_path)
7042 : host_(detail::escape_abstract_namespace_unix_domain(host)),
7043 port_(port),
7044 host_and_port_(adjust_host_string(host_) + ":" + std::to_string(port)),
7045 client_cert_path_(client_cert_path),
7046 client_key_path_(client_key_path) {}
7047
7048inline ClientImpl::~ClientImpl() {
7049 std::lock_guard<std::mutex> guard(socket_mutex_);
7050 shutdown_socket(socket_);
7051 close_socket(socket_);
7052}
7053
7054inline bool ClientImpl::is_valid() const { return true; }
7055
7056inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
7057 client_cert_path_ = rhs.client_cert_path_;
7058 client_key_path_ = rhs.client_key_path_;
7059 connection_timeout_sec_ = rhs.connection_timeout_sec_;
7060 read_timeout_sec_ = rhs.read_timeout_sec_;
7061 read_timeout_usec_ = rhs.read_timeout_usec_;
7062 write_timeout_sec_ = rhs.write_timeout_sec_;
7063 write_timeout_usec_ = rhs.write_timeout_usec_;
7064 basic_auth_username_ = rhs.basic_auth_username_;
7065 basic_auth_password_ = rhs.basic_auth_password_;
7066 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
7067#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7068 digest_auth_username_ = rhs.digest_auth_username_;
7069 digest_auth_password_ = rhs.digest_auth_password_;
7070#endif
7071 keep_alive_ = rhs.keep_alive_;
7072 follow_location_ = rhs.follow_location_;
7073 url_encode_ = rhs.url_encode_;
7074 address_family_ = rhs.address_family_;
7075 tcp_nodelay_ = rhs.tcp_nodelay_;
7076 ipv6_v6only_ = rhs.ipv6_v6only_;
7077 socket_options_ = rhs.socket_options_;
7078 compress_ = rhs.compress_;
7079 decompress_ = rhs.decompress_;
7080 interface_ = rhs.interface_;
7081 proxy_host_ = rhs.proxy_host_;
7082 proxy_port_ = rhs.proxy_port_;
7083 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
7084 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
7085 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
7086#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7087 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
7088 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
7089#endif
7090#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7091 ca_cert_file_path_ = rhs.ca_cert_file_path_;
7092 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
7093 ca_cert_store_ = rhs.ca_cert_store_;
7094#endif
7095#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7096 server_certificate_verification_ = rhs.server_certificate_verification_;
7097 server_hostname_verification_ = rhs.server_hostname_verification_;
7098 server_certificate_verifier_ = rhs.server_certificate_verifier_;
7099#endif
7100 logger_ = rhs.logger_;
7101}
7102
7103inline socket_t ClientImpl::create_client_socket(Error &error) const {
7104 if (!proxy_host_.empty() && proxy_port_ != -1) {
7105 return detail::create_client_socket(proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
7106 ipv6_v6only_, socket_options_, connection_timeout_sec_,
7107 connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
7108 write_timeout_sec_, write_timeout_usec_, interface_, error);
7109 }
7110
7111 // Check is custom IP specified for host_
7112 std::string ip;
7113 auto it = addr_map_.find(host_);
7114 if (it != addr_map_.end()) {
7115 ip = it->second;
7116 }
7117
7118 return detail::create_client_socket(host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_, socket_options_,
7119 connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
7120 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, error);
7121}
7122
7123inline bool ClientImpl::create_and_connect_socket(Socket &socket, Error &error) {
7124 auto sock = create_client_socket(error);
7125 if (sock == INVALID_SOCKET) {
7126 return false;
7127 }
7128 socket.sock = sock;
7129 return true;
7130}
7131
7132inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, bool /*shutdown_gracefully*/) {
7133 // If there are any requests in flight from threads other than us, then it's
7134 // a thread-unsafe race because individual ssl* objects are not thread-safe.
7135 assert(socket_requests_in_flight_ == 0 || socket_requests_are_from_thread_ == std::this_thread::get_id());
7136}
7137
7138inline void ClientImpl::shutdown_socket(Socket &socket) const {
7139 if (socket.sock == INVALID_SOCKET) {
7140 return;
7141 }
7142 detail::shutdown_socket(socket.sock);
7143}
7144
7145inline void ClientImpl::close_socket(Socket &socket) {
7146 // If there are requests in flight in another thread, usually closing
7147 // the socket will be fine and they will simply receive an error when
7148 // using the closed socket, but it is still a bug since rarely the OS
7149 // may reassign the socket id to be used for a new socket, and then
7150 // suddenly they will be operating on a live socket that is different
7151 // than the one they intended!
7152 assert(socket_requests_in_flight_ == 0 || socket_requests_are_from_thread_ == std::this_thread::get_id());
7153
7154 // It is also a bug if this happens while SSL is still active
7155#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7156 assert(socket.ssl == nullptr);
7157#endif
7158 if (socket.sock == INVALID_SOCKET) {
7159 return;
7160 }
7161 detail::close_socket(socket.sock);
7162 socket.sock = INVALID_SOCKET;
7163}
7164
7165inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, Response &res) const {
7166 std::array<char, 2048> buf{};
7167
7168 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
7169
7170 if (!line_reader.getline()) {
7171 return false;
7172 }
7173
7174#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7175 const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7176#else
7177 const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7178#endif
7179
7180 std::cmatch m;
7181 if (!std::regex_match(line_reader.ptr(), m, re)) {
7182 return req.method == "CONNECT";
7183 }
7184 res.version = std::string(m[1]);
7185 res.status = std::stoi(std::string(m[2]));
7186 res.reason = std::string(m[3]);
7187
7188 // Ignore '100 Continue'
7189 while (res.status == StatusCode::Continue_100) {
7190 if (!line_reader.getline()) {
7191 return false;
7192 } // CRLF
7193 if (!line_reader.getline()) {
7194 return false;
7195 } // next response line
7196
7197 if (!std::regex_match(line_reader.ptr(), m, re)) {
7198 return false;
7199 }
7200 res.version = std::string(m[1]);
7201 res.status = std::stoi(std::string(m[2]));
7202 res.reason = std::string(m[3]);
7203 }
7204
7205 return true;
7206}
7207
7208inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
7209 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
7210 auto ret = send_(req, res, error);
7211 if (error == Error::SSLPeerCouldBeClosed_) {
7212 assert(!ret);
7213 ret = send_(req, res, error);
7214 }
7215 return ret;
7216}
7217
7218#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7219inline bool ClientImpl::is_ssl_peer_could_be_closed(SSL *ssl) const {
7220 char buf[1];
7221 return !SSL_peek(ssl, buf, 1) && SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7222}
7223#endif
7224
7225inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7226 {
7227 std::lock_guard<std::mutex> guard(socket_mutex_);
7228
7229 // Set this to false immediately - if it ever gets set to true by the end of
7230 // the request, we know another thread instructed us to close the socket.
7231 socket_should_be_closed_when_request_is_done_ = false;
7232
7233 auto is_alive = false;
7234 if (socket_.is_open()) {
7235 is_alive = detail::is_socket_alive(socket_.sock);
7236
7237#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7238 if (is_alive && is_ssl()) {
7239 if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7240 is_alive = false;
7241 }
7242 }
7243#endif
7244
7245 if (!is_alive) {
7246 // Attempt to avoid sigpipe by shutting down nongracefully if it seems
7247 // like the other side has already closed the connection Also, there
7248 // cannot be any requests in flight from other threads since we locked
7249 // request_mutex_, so safe to close everything immediately
7250 const bool shutdown_gracefully = false;
7251 shutdown_ssl(socket_, shutdown_gracefully);
7252 shutdown_socket(socket_);
7253 close_socket(socket_);
7254 }
7255 }
7256
7257 if (!is_alive) {
7258 if (!create_and_connect_socket(socket_, error)) {
7259 return false;
7260 }
7261
7262#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7263 // TODO: refactoring
7264 if (is_ssl()) {
7265 auto &scli = static_cast<SSLClient &>(*this);
7266 if (!proxy_host_.empty() && proxy_port_ != -1) {
7267 auto success = false;
7268 if (!scli.connect_with_proxy(socket_, res, success, error)) {
7269 return success;
7270 }
7271 }
7272
7273 if (!scli.initialize_ssl(socket_, error)) {
7274 return false;
7275 }
7276 }
7277#endif
7278 }
7279
7280 // Mark the current socket as being in use so that it cannot be closed by
7281 // anyone else while this request is ongoing, even though we will be
7282 // releasing the mutex.
7283 if (socket_requests_in_flight_ > 1) {
7284 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
7285 }
7286 socket_requests_in_flight_ += 1;
7287 socket_requests_are_from_thread_ = std::this_thread::get_id();
7288 }
7289
7290 for (const auto &header : default_headers_) {
7291 if (req.headers.find(header.first) == req.headers.end()) {
7292 req.headers.insert(header);
7293 }
7294 }
7295
7296 auto ret = false;
7297 auto close_connection = !keep_alive_;
7298
7299 auto se = detail::scope_exit([&]() {
7300 // Briefly lock mutex in order to mark that a request is no longer ongoing
7301 std::lock_guard<std::mutex> guard(socket_mutex_);
7302 socket_requests_in_flight_ -= 1;
7303 if (socket_requests_in_flight_ <= 0) {
7304 assert(socket_requests_in_flight_ == 0);
7305 socket_requests_are_from_thread_ = std::thread::id();
7306 }
7307
7308 if (socket_should_be_closed_when_request_is_done_ || close_connection || !ret) {
7309 shutdown_ssl(socket_, true);
7310 shutdown_socket(socket_);
7311 close_socket(socket_);
7312 }
7313 });
7314
7315 ret = process_socket(socket_, [&](Stream &strm) { return handle_request(strm, req, res, close_connection, error); });
7316
7317 if (!ret) {
7318 if (error == Error::Success) {
7319 error = Error::Unknown;
7320 }
7321 }
7322
7323 return ret;
7324}
7325
7326inline Result ClientImpl::send(const Request &req) {
7327 auto req2 = req;
7328 return send_(std::move(req2));
7329}
7330
7331inline Result ClientImpl::send_(Request &&req) {
7332 auto res = detail::make_unique<Response>();
7333 auto error = Error::Success;
7334 auto ret = send(req, *res, error);
7335 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
7336}
7337
7338inline bool ClientImpl::handle_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error) {
7339 if (req.path.empty()) {
7340 error = Error::Connection;
7341 return false;
7342 }
7343
7344 auto req_save = req;
7345
7346 bool ret;
7347
7348 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
7349 auto req2 = req;
7350 req2.path = "http://" + host_and_port_ + req.path;
7351 ret = process_request(strm, req2, res, close_connection, error);
7352 req = req2;
7353 req.path = req_save.path;
7354 } else {
7355 ret = process_request(strm, req, res, close_connection, error);
7356 }
7357
7358 if (!ret) {
7359 return false;
7360 }
7361
7362 if (res.get_header_value("Connection") == "close" ||
7363 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
7364 // TODO this requires a not-entirely-obvious chain of calls to be correct
7365 // for this to be safe.
7366
7367 // This is safe to call because handle_request is only called by send_
7368 // which locks the request mutex during the process. It would be a bug
7369 // to call it from a different thread since it's a thread-safety issue
7370 // to do these things to the socket if another thread is using the socket.
7371 std::lock_guard<std::mutex> guard(socket_mutex_);
7372 shutdown_ssl(socket_, true);
7373 shutdown_socket(socket_);
7374 close_socket(socket_);
7375 }
7376
7377 if (300 < res.status && res.status < 400 && follow_location_) {
7378 req = req_save;
7379 ret = redirect(req, res, error);
7380 }
7381
7382#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7383 if ((res.status == StatusCode::Unauthorized_401 || res.status == StatusCode::ProxyAuthenticationRequired_407) &&
7384 req.authorization_count_ < 5) {
7385 auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;
7386 const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
7387 const auto &password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
7388
7389 if (!username.empty() && !password.empty()) {
7390 std::map<std::string, std::string> auth;
7391 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
7392 Request new_req = req;
7393 new_req.authorization_count_ += 1;
7394 new_req.headers.erase(is_proxy ? "Proxy-Authorization" : "Authorization");
7395 new_req.headers.insert(detail::make_digest_authentication_header(
7396 req, auth, new_req.authorization_count_, detail::random_string(10), username, password, is_proxy));
7397
7398 Response new_res;
7399
7400 ret = send(new_req, new_res, error);
7401 if (ret) {
7402 res = new_res;
7403 }
7404 }
7405 }
7406 }
7407#endif
7408
7409 return ret;
7410}
7411
7412inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
7413 if (req.redirect_count_ == 0) {
7414 error = Error::ExceedRedirectCount;
7415 return false;
7416 }
7417
7418 auto location = res.get_header_value("location");
7419 if (location.empty()) {
7420 return false;
7421 }
7422
7423 const static std::regex re(
7424 R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7425
7426 std::smatch m;
7427 if (!std::regex_match(location, m, re)) {
7428 return false;
7429 }
7430
7431 auto scheme = is_ssl() ? "https" : "http";
7432
7433 auto next_scheme = m[1].str();
7434 auto next_host = m[2].str();
7435 if (next_host.empty()) {
7436 next_host = m[3].str();
7437 }
7438 auto port_str = m[4].str();
7439 auto next_path = m[5].str();
7440 auto next_query = m[6].str();
7441
7442 auto next_port = port_;
7443 if (!port_str.empty()) {
7444 next_port = std::stoi(port_str);
7445 } else if (!next_scheme.empty()) {
7446 next_port = next_scheme == "https" ? 443 : 80;
7447 }
7448
7449 if (next_scheme.empty()) {
7450 next_scheme = scheme;
7451 }
7452 if (next_host.empty()) {
7453 next_host = host_;
7454 }
7455 if (next_path.empty()) {
7456 next_path = "/";
7457 }
7458
7459 auto path = detail::decode_url(next_path, true) + next_query;
7460
7461 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
7462 return detail::redirect(*this, req, res, path, location, error);
7463 } else {
7464 if (next_scheme == "https") {
7465#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7466 SSLClient cli(next_host, next_port);
7467 cli.copy_settings(*this);
7468 if (ca_cert_store_) {
7469 cli.set_ca_cert_store(ca_cert_store_);
7470 }
7471 return detail::redirect(cli, req, res, path, location, error);
7472#else
7473 return false;
7474#endif
7475 } else {
7476 ClientImpl cli(next_host, next_port);
7477 cli.copy_settings(*this);
7478 return detail::redirect(cli, req, res, path, location, error);
7479 }
7480 }
7481}
7482
7483inline bool ClientImpl::write_content_with_provider(Stream &strm, const Request &req, Error &error) const {
7484 auto is_shutting_down = []() { return false; };
7485
7486 if (req.is_chunked_content_provider_) {
7487 // TODO: Brotli support
7488 std::unique_ptr<detail::compressor> compressor;
7489#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7490 if (compress_) {
7491 compressor = detail::make_unique<detail::gzip_compressor>();
7492 } else
7493#endif
7494 {
7495 compressor = detail::make_unique<detail::nocompressor>();
7496 }
7497
7498 return detail::write_content_chunked(strm, req.content_provider_, is_shutting_down, *compressor, error);
7499 } else {
7500 return detail::write_content(strm, req.content_provider_, 0, req.content_length_, is_shutting_down, error);
7501 }
7502}
7503
7504inline bool ClientImpl::write_request(Stream &strm, Request &req, bool close_connection, Error &error) {
7505 // Prepare additional headers
7506 if (close_connection) {
7507 if (!req.has_header("Connection")) {
7508 req.set_header("Connection", "close");
7509 }
7510 }
7511
7512 if (!req.has_header("Host")) {
7513 if (is_ssl()) {
7514 if (port_ == 443) {
7515 req.set_header("Host", host_);
7516 } else {
7517 req.set_header("Host", host_and_port_);
7518 }
7519 } else {
7520 if (port_ == 80) {
7521 req.set_header("Host", host_);
7522 } else {
7523 req.set_header("Host", host_and_port_);
7524 }
7525 }
7526 }
7527
7528 if (!req.has_header("Accept")) {
7529 req.set_header("Accept", "*/*");
7530 }
7531
7532 if (!req.content_receiver) {
7533 if (!req.has_header("Accept-Encoding")) {
7534 std::string accept_encoding;
7535#ifdef CPPHTTPLIB_BROTLI_SUPPORT
7536 accept_encoding = "br";
7537#endif
7538#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7539 if (!accept_encoding.empty()) {
7540 accept_encoding += ", ";
7541 }
7542 accept_encoding += "gzip, deflate";
7543#endif
7544 req.set_header("Accept-Encoding", accept_encoding);
7545 }
7546
7547#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
7548 if (!req.has_header("User-Agent")) {
7549 auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
7550 req.set_header("User-Agent", agent);
7551 }
7552#endif
7553 };
7554
7555 if (req.body.empty()) {
7556 if (req.content_provider_) {
7557 if (!req.is_chunked_content_provider_) {
7558 if (!req.has_header("Content-Length")) {
7559 auto length = std::to_string(req.content_length_);
7560 req.set_header("Content-Length", length);
7561 }
7562 }
7563 } else {
7564 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") {
7565 req.set_header("Content-Length", "0");
7566 }
7567 }
7568 } else {
7569 if (!req.has_header("Content-Type")) {
7570 req.set_header("Content-Type", "text/plain");
7571 }
7572
7573 if (!req.has_header("Content-Length")) {
7574 auto length = std::to_string(req.body.size());
7575 req.set_header("Content-Length", length);
7576 }
7577 }
7578
7579 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
7580 if (!req.has_header("Authorization")) {
7581 req.headers.insert(make_basic_authentication_header(basic_auth_username_, basic_auth_password_, false));
7582 }
7583 }
7584
7585 if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) {
7586 if (!req.has_header("Proxy-Authorization")) {
7587 req.headers.insert(
7588 make_basic_authentication_header(proxy_basic_auth_username_, proxy_basic_auth_password_, true));
7589 }
7590 }
7591
7592 if (!bearer_token_auth_token_.empty()) {
7593 if (!req.has_header("Authorization")) {
7594 req.headers.insert(make_bearer_token_authentication_header(bearer_token_auth_token_, false));
7595 }
7596 }
7597
7598 if (!proxy_bearer_token_auth_token_.empty()) {
7599 if (!req.has_header("Proxy-Authorization")) {
7600 req.headers.insert(make_bearer_token_authentication_header(proxy_bearer_token_auth_token_, true));
7601 }
7602 }
7603
7604 // Request line and headers
7605 {
7606 detail::BufferStream bstrm;
7607
7608 const auto &path_with_query = req.params.empty() ? req.path : append_query_params(req.path, req.params);
7609
7610 const auto &path = url_encode_ ? detail::encode_url(path_with_query) : path_with_query;
7611
7612 detail::write_request_line(bstrm, req.method, path);
7613
7614 header_writer_(bstrm, req.headers);
7615
7616 // Flush buffer
7617 auto &data = bstrm.get_buffer();
7618 if (!detail::write_data(strm, data.data(), data.size())) {
7619 error = Error::Write;
7620 return false;
7621 }
7622 }
7623
7624 // Body
7625 if (req.body.empty()) {
7626 return write_content_with_provider(strm, req, error);
7627 }
7628
7629 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
7630 error = Error::Write;
7631 return false;
7632 }
7633
7634 return true;
7635}
7636
7637inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
7638 Request &req, const char *body, size_t content_length, ContentProvider content_provider,
7639 ContentProviderWithoutLength content_provider_without_length, const std::string &content_type, Error &error) {
7640 if (!content_type.empty()) {
7641 req.set_header("Content-Type", content_type);
7642 }
7643
7644#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7645 if (compress_) {
7646 req.set_header("Content-Encoding", "gzip");
7647 }
7648#endif
7649
7650#ifdef CPPHTTPLIB_ZLIB_SUPPORT
7651 if (compress_ && !content_provider_without_length) {
7652 // TODO: Brotli support
7653 detail::gzip_compressor compressor;
7654
7655 if (content_provider) {
7656 auto ok = true;
7657 size_t offset = 0;
7658 DataSink data_sink;
7659
7660 data_sink.write = [&](const char *data, size_t data_len) -> bool {
7661 if (ok) {
7662 auto last = offset + data_len == content_length;
7663
7664 auto ret =
7665 compressor.compress(data, data_len, last, [&](const char *compressed_data, size_t compressed_data_len) {
7666 req.body.append(compressed_data, compressed_data_len);
7667 return true;
7668 });
7669
7670 if (ret) {
7671 offset += data_len;
7672 } else {
7673 ok = false;
7674 }
7675 }
7676 return ok;
7677 };
7678
7679 while (ok && offset < content_length) {
7680 if (!content_provider(offset, content_length - offset, data_sink)) {
7681 error = Error::Canceled;
7682 return nullptr;
7683 }
7684 }
7685 } else {
7686 if (!compressor.compress(body, content_length, true, [&](const char *data, size_t data_len) {
7687 req.body.append(data, data_len);
7688 return true;
7689 })) {
7690 error = Error::Compression;
7691 return nullptr;
7692 }
7693 }
7694 } else
7695#endif
7696 {
7697 if (content_provider) {
7698 req.content_length_ = content_length;
7699 req.content_provider_ = std::move(content_provider);
7700 req.is_chunked_content_provider_ = false;
7701 } else if (content_provider_without_length) {
7702 req.content_length_ = 0;
7703 req.content_provider_ = detail::ContentProviderAdapter(std::move(content_provider_without_length));
7704 req.is_chunked_content_provider_ = true;
7705 req.set_header("Transfer-Encoding", "chunked");
7706 } else {
7707 req.body.assign(body, content_length);
7708 }
7709 }
7710
7711 auto res = detail::make_unique<Response>();
7712 return send(req, *res, error) ? std::move(res) : nullptr;
7713}
7714
7715inline Result ClientImpl::send_with_content_provider(const std::string &method, const std::string &path,
7716 const Headers &headers, const char *body, size_t content_length,
7717 ContentProvider content_provider,
7718 ContentProviderWithoutLength content_provider_without_length,
7719 const std::string &content_type, Progress progress) {
7720 Request req;
7721 req.method = method;
7722 req.headers = headers;
7723 req.path = path;
7724 req.progress = progress;
7725
7726 auto error = Error::Success;
7727
7728 auto res = send_with_content_provider(req, body, content_length, std::move(content_provider),
7729 std::move(content_provider_without_length), content_type, error);
7730
7731 return Result{std::move(res), error, std::move(req.headers)};
7732}
7733
7734inline std::string ClientImpl::adjust_host_string(const std::string &host) const {
7735 if (host.find(':') != std::string::npos) {
7736 return "[" + host + "]";
7737 }
7738 return host;
7739}
7740
7741inline bool ClientImpl::process_request(Stream &strm, Request &req, Response &res, bool close_connection,
7742 Error &error) {
7743 // Send request
7744 if (!write_request(strm, req, close_connection, error)) {
7745 return false;
7746 }
7747
7748#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7749 if (is_ssl()) {
7750 auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
7751 if (!is_proxy_enabled) {
7752 if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7753 error = Error::SSLPeerCouldBeClosed_;
7754 return false;
7755 }
7756 }
7757 }
7758#endif
7759
7760 // Receive response and headers
7761 if (!read_response_line(strm, req, res) || !detail::read_headers(strm, res.headers)) {
7762 error = Error::Read;
7763 return false;
7764 }
7765
7766 // Body
7767 if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" && req.method != "CONNECT") {
7768 auto redirect = 300 < res.status && res.status < 400 && follow_location_;
7769
7770 if (req.response_handler && !redirect) {
7771 if (!req.response_handler(res)) {
7772 error = Error::Canceled;
7773 return false;
7774 }
7775 }
7776
7777 auto out =
7778 req.content_receiver
7779 ? static_cast<ContentReceiverWithProgress>([&](const char *buf, size_t n, uint64_t off, uint64_t len) {
7780 if (redirect) {
7781 return true;
7782 }
7783 auto ret = req.content_receiver(buf, n, off, len);
7784 if (!ret) {
7785 error = Error::Canceled;
7786 }
7787 return ret;
7788 })
7789 : static_cast<ContentReceiverWithProgress>(
7790 [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
7791 assert(res.body.size() + n <= res.body.max_size());
7792 res.body.append(buf, n);
7793 return true;
7794 });
7795
7796 auto progress = [&](uint64_t current, uint64_t total) {
7797 if (!req.progress || redirect) {
7798 return true;
7799 }
7800 auto ret = req.progress(current, total);
7801 if (!ret) {
7802 error = Error::Canceled;
7803 }
7804 return ret;
7805 };
7806
7807 if (res.has_header("Content-Length")) {
7808 if (!req.content_receiver) {
7809 auto len = res.get_header_value_u64("Content-Length");
7810 if (len > res.body.max_size()) {
7811 error = Error::Read;
7812 return false;
7813 }
7814 res.body.reserve(len);
7815 }
7816 }
7817
7818 int dummy_status;
7819 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(), dummy_status, std::move(progress),
7820 std::move(out), decompress_)) {
7821 if (error != Error::Canceled) {
7822 error = Error::Read;
7823 }
7824 return false;
7825 }
7826 }
7827
7828 // Log
7829 if (logger_) {
7830 logger_(req, res);
7831 }
7832
7833 return true;
7834}
7835
7836inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
7837 const std::string &boundary, const MultipartFormDataItems &items,
7838 const MultipartFormDataProviderItems &provider_items) const {
7839 size_t cur_item = 0;
7840 size_t cur_start = 0;
7841 // cur_item and cur_start are copied to within the std::function and maintain
7842 // state between successive calls
7843 return [&, cur_item, cur_start](size_t offset, DataSink &sink) mutable -> bool {
7844 if (!offset && !items.empty()) {
7845 sink.os << detail::serialize_multipart_formdata(items, boundary, false);
7846 return true;
7847 } else if (cur_item < provider_items.size()) {
7848 if (!cur_start) {
7849 const auto &begin = detail::serialize_multipart_formdata_item_begin(provider_items[cur_item], boundary);
7850 offset += begin.size();
7851 cur_start = offset;
7852 sink.os << begin;
7853 }
7854
7855 DataSink cur_sink;
7856 auto has_data = true;
7857 cur_sink.write = sink.write;
7858 cur_sink.done = [&]() { has_data = false; };
7859
7860 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
7861 return false;
7862 }
7863
7864 if (!has_data) {
7865 sink.os << detail::serialize_multipart_formdata_item_end();
7866 cur_item++;
7867 cur_start = 0;
7868 }
7869 return true;
7870 } else {
7871 sink.os << detail::serialize_multipart_formdata_finish(boundary);
7872 sink.done();
7873 return true;
7874 }
7875 };
7876}
7877
7878inline bool ClientImpl::process_socket(const Socket &socket, std::function<bool(Stream &strm)> callback) {
7879 return detail::process_client_socket(socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7880 write_timeout_usec_, std::move(callback));
7881}
7882
7883inline bool ClientImpl::is_ssl() const { return false; }
7884
7885inline Result ClientImpl::Get(const std::string &path) { return Get(path, Headers(), Progress()); }
7886
7887inline Result ClientImpl::Get(const std::string &path, Progress progress) {
7888 return Get(path, Headers(), std::move(progress));
7889}
7890
7891inline Result ClientImpl::Get(const std::string &path, const Headers &headers) {
7892 return Get(path, headers, Progress());
7893}
7894
7895inline Result ClientImpl::Get(const std::string &path, const Headers &headers, Progress progress) {
7896 Request req;
7897 req.method = "GET";
7898 req.path = path;
7899 req.headers = headers;
7900 req.progress = std::move(progress);
7901
7902 return send_(std::move(req));
7903}
7904
7905inline Result ClientImpl::Get(const std::string &path, ContentReceiver content_receiver) {
7906 return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
7907}
7908
7909inline Result ClientImpl::Get(const std::string &path, ContentReceiver content_receiver, Progress progress) {
7910 return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress));
7911}
7912
7913inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver) {
7914 return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
7915}
7916
7917inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver,
7918 Progress progress) {
7919 return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress));
7920}
7921
7922inline Result ClientImpl::Get(const std::string &path, ResponseHandler response_handler,
7923 ContentReceiver content_receiver) {
7924 return Get(path, Headers(), std::move(response_handler), std::move(content_receiver), nullptr);
7925}
7926
7927inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
7928 ContentReceiver content_receiver) {
7929 return Get(path, headers, std::move(response_handler), std::move(content_receiver), nullptr);
7930}
7931
7932inline Result ClientImpl::Get(const std::string &path, ResponseHandler response_handler,
7933 ContentReceiver content_receiver, Progress progress) {
7934 return Get(path, Headers(), std::move(response_handler), std::move(content_receiver), std::move(progress));
7935}
7936
7937inline Result ClientImpl::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
7938 ContentReceiver content_receiver, Progress progress) {
7939 Request req;
7940 req.method = "GET";
7941 req.path = path;
7942 req.headers = headers;
7943 req.response_handler = std::move(response_handler);
7944 req.content_receiver = [content_receiver](const char *data, size_t data_length, uint64_t /*offset*/,
7945 uint64_t /*total_length*/) { return content_receiver(data, data_length); };
7946 req.progress = std::move(progress);
7947
7948 return send_(std::move(req));
7949}
7950
7951inline Result ClientImpl::Get(const std::string &path, const Params &params, const Headers &headers,
7952 Progress progress) {
7953 if (params.empty()) {
7954 return Get(path, headers);
7955 }
7956
7957 std::string path_with_query = append_query_params(path, params);
7958 return Get(path_with_query, headers, std::move(progress));
7959}
7960
7961inline Result ClientImpl::Get(const std::string &path, const Params &params, const Headers &headers,
7962 ContentReceiver content_receiver, Progress progress) {
7963 return Get(path, params, headers, nullptr, std::move(content_receiver), std::move(progress));
7964}
7965
7966inline Result ClientImpl::Get(const std::string &path, const Params &params, const Headers &headers,
7967 ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) {
7968 if (params.empty()) {
7969 return Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
7970 }
7971
7972 std::string path_with_query = append_query_params(path, params);
7973 return Get(path_with_query, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
7974}
7975
7976inline Result ClientImpl::Head(const std::string &path) { return Head(path, Headers()); }
7977
7978inline Result ClientImpl::Head(const std::string &path, const Headers &headers) {
7979 Request req;
7980 req.method = "HEAD";
7981 req.headers = headers;
7982 req.path = path;
7983
7984 return send_(std::move(req));
7985}
7986
7987inline Result ClientImpl::Post(const std::string &path) { return Post(path, std::string(), std::string()); }
7988
7989inline Result ClientImpl::Post(const std::string &path, const Headers &headers) {
7990 return Post(path, headers, nullptr, 0, std::string());
7991}
7992
7993inline Result ClientImpl::Post(const std::string &path, const char *body, size_t content_length,
7994 const std::string &content_type) {
7995 return Post(path, Headers(), body, content_length, content_type, nullptr);
7996}
7997
7998inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
7999 const std::string &content_type) {
8000 return send_with_content_provider("POST", path, headers, body, content_length, nullptr, nullptr, content_type,
8001 nullptr);
8002}
8003
8004inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
8005 const std::string &content_type, Progress progress) {
8006 return send_with_content_provider("POST", path, headers, body, content_length, nullptr, nullptr, content_type,
8007 progress);
8008}
8009
8010inline Result ClientImpl::Post(const std::string &path, const std::string &body, const std::string &content_type) {
8011 return Post(path, Headers(), body, content_type);
8012}
8013
8014inline Result ClientImpl::Post(const std::string &path, const std::string &body, const std::string &content_type,
8015 Progress progress) {
8016 return Post(path, Headers(), body, content_type, progress);
8017}
8018
8019inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const std::string &body,
8020 const std::string &content_type) {
8021 return send_with_content_provider("POST", path, headers, body.data(), body.size(), nullptr, nullptr, content_type,
8022 nullptr);
8023}
8024
8025inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const std::string &body,
8026 const std::string &content_type, Progress progress) {
8027 return send_with_content_provider("POST", path, headers, body.data(), body.size(), nullptr, nullptr, content_type,
8028 progress);
8029}
8030
8031inline Result ClientImpl::Post(const std::string &path, const Params &params) { return Post(path, Headers(), params); }
8032
8033inline Result ClientImpl::Post(const std::string &path, size_t content_length, ContentProvider content_provider,
8034 const std::string &content_type) {
8035 return Post(path, Headers(), content_length, std::move(content_provider), content_type);
8036}
8037
8038inline Result ClientImpl::Post(const std::string &path, ContentProviderWithoutLength content_provider,
8039 const std::string &content_type) {
8040 return Post(path, Headers(), std::move(content_provider), content_type);
8041}
8042
8043inline Result ClientImpl::Post(const std::string &path, const Headers &headers, size_t content_length,
8044 ContentProvider content_provider, const std::string &content_type) {
8045 return send_with_content_provider("POST", path, headers, nullptr, content_length, std::move(content_provider),
8046 nullptr, content_type, nullptr);
8047}
8048
8049inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
8050 ContentProviderWithoutLength content_provider, const std::string &content_type) {
8051 return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, std::move(content_provider),
8052 content_type, nullptr);
8053}
8054
8055inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const Params &params) {
8056 auto query = detail::params_to_query_str(params);
8057 return Post(path, headers, query, "application/x-www-form-urlencoded");
8058}
8059
8060inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const Params &params,
8061 Progress progress) {
8062 auto query = detail::params_to_query_str(params);
8063 return Post(path, headers, query, "application/x-www-form-urlencoded", progress);
8064}
8065
8066inline Result ClientImpl::Post(const std::string &path, const MultipartFormDataItems &items) {
8067 return Post(path, Headers(), items);
8068}
8069
8070inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) {
8071 const auto &boundary = detail::make_multipart_data_boundary();
8072 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8073 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8074 return Post(path, headers, body, content_type);
8075}
8076
8077inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
8078 const std::string &boundary) {
8079 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
8080 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
8081 }
8082
8083 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8084 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8085 return Post(path, headers, body, content_type);
8086}
8087
8088inline Result ClientImpl::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
8089 const MultipartFormDataProviderItems &provider_items) {
8090 const auto &boundary = detail::make_multipart_data_boundary();
8091 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8092 return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
8093 get_multipart_content_provider(boundary, items, provider_items), content_type,
8094 nullptr);
8095}
8096
8097inline Result ClientImpl::Put(const std::string &path) { return Put(path, std::string(), std::string()); }
8098
8099inline Result ClientImpl::Put(const std::string &path, const char *body, size_t content_length,
8100 const std::string &content_type) {
8101 return Put(path, Headers(), body, content_length, content_type);
8102}
8103
8104inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
8105 const std::string &content_type) {
8106 return send_with_content_provider("PUT", path, headers, body, content_length, nullptr, nullptr, content_type,
8107 nullptr);
8108}
8109
8110inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
8111 const std::string &content_type, Progress progress) {
8112 return send_with_content_provider("PUT", path, headers, body, content_length, nullptr, nullptr, content_type,
8113 progress);
8114}
8115
8116inline Result ClientImpl::Put(const std::string &path, const std::string &body, const std::string &content_type) {
8117 return Put(path, Headers(), body, content_type);
8118}
8119
8120inline Result ClientImpl::Put(const std::string &path, const std::string &body, const std::string &content_type,
8121 Progress progress) {
8122 return Put(path, Headers(), body, content_type, progress);
8123}
8124
8125inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const std::string &body,
8126 const std::string &content_type) {
8127 return send_with_content_provider("PUT", path, headers, body.data(), body.size(), nullptr, nullptr, content_type,
8128 nullptr);
8129}
8130
8131inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const std::string &body,
8132 const std::string &content_type, Progress progress) {
8133 return send_with_content_provider("PUT", path, headers, body.data(), body.size(), nullptr, nullptr, content_type,
8134 progress);
8135}
8136
8137inline Result ClientImpl::Put(const std::string &path, size_t content_length, ContentProvider content_provider,
8138 const std::string &content_type) {
8139 return Put(path, Headers(), content_length, std::move(content_provider), content_type);
8140}
8141
8142inline Result ClientImpl::Put(const std::string &path, ContentProviderWithoutLength content_provider,
8143 const std::string &content_type) {
8144 return Put(path, Headers(), std::move(content_provider), content_type);
8145}
8146
8147inline Result ClientImpl::Put(const std::string &path, const Headers &headers, size_t content_length,
8148 ContentProvider content_provider, const std::string &content_type) {
8149 return send_with_content_provider("PUT", path, headers, nullptr, content_length, std::move(content_provider), nullptr,
8150 content_type, nullptr);
8151}
8152
8153inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
8154 ContentProviderWithoutLength content_provider, const std::string &content_type) {
8155 return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, std::move(content_provider),
8156 content_type, nullptr);
8157}
8158
8159inline Result ClientImpl::Put(const std::string &path, const Params &params) { return Put(path, Headers(), params); }
8160
8161inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const Params &params) {
8162 auto query = detail::params_to_query_str(params);
8163 return Put(path, headers, query, "application/x-www-form-urlencoded");
8164}
8165
8166inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const Params &params,
8167 Progress progress) {
8168 auto query = detail::params_to_query_str(params);
8169 return Put(path, headers, query, "application/x-www-form-urlencoded", progress);
8170}
8171
8172inline Result ClientImpl::Put(const std::string &path, const MultipartFormDataItems &items) {
8173 return Put(path, Headers(), items);
8174}
8175
8176inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) {
8177 const auto &boundary = detail::make_multipart_data_boundary();
8178 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8179 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8180 return Put(path, headers, body, content_type);
8181}
8182
8183inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
8184 const std::string &boundary) {
8185 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
8186 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
8187 }
8188
8189 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8190 const auto &body = detail::serialize_multipart_formdata(items, boundary);
8191 return Put(path, headers, body, content_type);
8192}
8193
8194inline Result ClientImpl::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
8195 const MultipartFormDataProviderItems &provider_items) {
8196 const auto &boundary = detail::make_multipart_data_boundary();
8197 const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary);
8198 return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
8199 get_multipart_content_provider(boundary, items, provider_items), content_type,
8200 nullptr);
8201}
8202inline Result ClientImpl::Patch(const std::string &path) { return Patch(path, std::string(), std::string()); }
8203
8204inline Result ClientImpl::Patch(const std::string &path, const char *body, size_t content_length,
8205 const std::string &content_type) {
8206 return Patch(path, Headers(), body, content_length, content_type);
8207}
8208
8209inline Result ClientImpl::Patch(const std::string &path, const char *body, size_t content_length,
8210 const std::string &content_type, Progress progress) {
8211 return Patch(path, Headers(), body, content_length, content_type, progress);
8212}
8213
8214inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, const char *body,
8215 size_t content_length, const std::string &content_type) {
8216 return Patch(path, headers, body, content_length, content_type, nullptr);
8217}
8218
8219inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, const char *body,
8220 size_t content_length, const std::string &content_type, Progress progress) {
8221 return send_with_content_provider("PATCH", path, headers, body, content_length, nullptr, nullptr, content_type,
8222 progress);
8223}
8224
8225inline Result ClientImpl::Patch(const std::string &path, const std::string &body, const std::string &content_type) {
8226 return Patch(path, Headers(), body, content_type);
8227}
8228
8229inline Result ClientImpl::Patch(const std::string &path, const std::string &body, const std::string &content_type,
8230 Progress progress) {
8231 return Patch(path, Headers(), body, content_type, progress);
8232}
8233
8234inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, const std::string &body,
8235 const std::string &content_type) {
8236 return Patch(path, headers, body, content_type, nullptr);
8237}
8238
8239inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, const std::string &body,
8240 const std::string &content_type, Progress progress) {
8241 return send_with_content_provider("PATCH", path, headers, body.data(), body.size(), nullptr, nullptr, content_type,
8242 progress);
8243}
8244
8245inline Result ClientImpl::Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
8246 const std::string &content_type) {
8247 return Patch(path, Headers(), content_length, std::move(content_provider), content_type);
8248}
8249
8250inline Result ClientImpl::Patch(const std::string &path, ContentProviderWithoutLength content_provider,
8251 const std::string &content_type) {
8252 return Patch(path, Headers(), std::move(content_provider), content_type);
8253}
8254
8255inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, size_t content_length,
8256 ContentProvider content_provider, const std::string &content_type) {
8257 return send_with_content_provider("PATCH", path, headers, nullptr, content_length, std::move(content_provider),
8258 nullptr, content_type, nullptr);
8259}
8260
8261inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
8262 ContentProviderWithoutLength content_provider, const std::string &content_type) {
8263 return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, std::move(content_provider),
8264 content_type, nullptr);
8265}
8266
8267inline Result ClientImpl::Delete(const std::string &path) {
8268 return Delete(path, Headers(), std::string(), std::string());
8269}
8270
8271inline Result ClientImpl::Delete(const std::string &path, const Headers &headers) {
8272 return Delete(path, headers, std::string(), std::string());
8273}
8274
8275inline Result ClientImpl::Delete(const std::string &path, const char *body, size_t content_length,
8276 const std::string &content_type) {
8277 return Delete(path, Headers(), body, content_length, content_type);
8278}
8279
8280inline Result ClientImpl::Delete(const std::string &path, const char *body, size_t content_length,
8281 const std::string &content_type, Progress progress) {
8282 return Delete(path, Headers(), body, content_length, content_type, progress);
8283}
8284
8285inline Result ClientImpl::Delete(const std::string &path, const Headers &headers, const char *body,
8286 size_t content_length, const std::string &content_type) {
8287 return Delete(path, headers, body, content_length, content_type, nullptr);
8288}
8289
8290inline Result ClientImpl::Delete(const std::string &path, const Headers &headers, const char *body,
8291 size_t content_length, const std::string &content_type, Progress progress) {
8292 Request req;
8293 req.method = "DELETE";
8294 req.headers = headers;
8295 req.path = path;
8296 req.progress = progress;
8297
8298 if (!content_type.empty()) {
8299 req.set_header("Content-Type", content_type);
8300 }
8301 req.body.assign(body, content_length);
8302
8303 return send_(std::move(req));
8304}
8305
8306inline Result ClientImpl::Delete(const std::string &path, const std::string &body, const std::string &content_type) {
8307 return Delete(path, Headers(), body.data(), body.size(), content_type);
8308}
8309
8310inline Result ClientImpl::Delete(const std::string &path, const std::string &body, const std::string &content_type,
8311 Progress progress) {
8312 return Delete(path, Headers(), body.data(), body.size(), content_type, progress);
8313}
8314
8315inline Result ClientImpl::Delete(const std::string &path, const Headers &headers, const std::string &body,
8316 const std::string &content_type) {
8317 return Delete(path, headers, body.data(), body.size(), content_type);
8318}
8319
8320inline Result ClientImpl::Delete(const std::string &path, const Headers &headers, const std::string &body,
8321 const std::string &content_type, Progress progress) {
8322 return Delete(path, headers, body.data(), body.size(), content_type, progress);
8323}
8324
8325inline Result ClientImpl::Options(const std::string &path) { return Options(path, Headers()); }
8326
8327inline Result ClientImpl::Options(const std::string &path, const Headers &headers) {
8328 Request req;
8329 req.method = "OPTIONS";
8330 req.headers = headers;
8331 req.path = path;
8332
8333 return send_(std::move(req));
8334}
8335
8336inline void ClientImpl::stop() {
8337 std::lock_guard<std::mutex> guard(socket_mutex_);
8338
8339 // If there is anything ongoing right now, the ONLY thread-safe thing we can
8340 // do is to shutdown_socket, so that threads using this socket suddenly
8341 // discover they can't read/write any more and error out. Everything else
8342 // (closing the socket, shutting ssl down) is unsafe because these actions are
8343 // not thread-safe.
8344 if (socket_requests_in_flight_ > 0) {
8345 shutdown_socket(socket_);
8346
8347 // Aside from that, we set a flag for the socket to be closed when we're
8348 // done.
8349 socket_should_be_closed_when_request_is_done_ = true;
8350 return;
8351 }
8352
8353 // Otherwise, still holding the mutex, we can shut everything down ourselves
8354 shutdown_ssl(socket_, true);
8355 shutdown_socket(socket_);
8356 close_socket(socket_);
8357}
8358
8359inline std::string ClientImpl::host() const { return host_; }
8360
8361inline int ClientImpl::port() const { return port_; }
8362
8363inline size_t ClientImpl::is_socket_open() const {
8364 std::lock_guard<std::mutex> guard(socket_mutex_);
8365 return socket_.is_open();
8366}
8367
8368inline socket_t ClientImpl::socket() const { return socket_.sock; }
8369
8370inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
8371 connection_timeout_sec_ = sec;
8372 connection_timeout_usec_ = usec;
8373}
8374
8375inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
8376 read_timeout_sec_ = sec;
8377 read_timeout_usec_ = usec;
8378}
8379
8380inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
8381 write_timeout_sec_ = sec;
8382 write_timeout_usec_ = usec;
8383}
8384
8385inline void ClientImpl::set_basic_auth(const std::string &username, const std::string &password) {
8386 basic_auth_username_ = username;
8387 basic_auth_password_ = password;
8388}
8389
8390inline void ClientImpl::set_bearer_token_auth(const std::string &token) { bearer_token_auth_token_ = token; }
8391
8392#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8393inline void ClientImpl::set_digest_auth(const std::string &username, const std::string &password) {
8394 digest_auth_username_ = username;
8395 digest_auth_password_ = password;
8396}
8397#endif
8398
8399inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
8400
8401inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
8402
8403inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
8404
8405inline void ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
8406 addr_map_ = std::move(addr_map);
8407}
8408
8409inline void ClientImpl::set_default_headers(Headers headers) { default_headers_ = std::move(headers); }
8410
8411inline void ClientImpl::set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer) {
8412 header_writer_ = writer;
8413}
8414
8415inline void ClientImpl::set_address_family(int family) { address_family_ = family; }
8416
8417inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
8418
8419inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
8420
8421inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
8422 socket_options_ = std::move(socket_options);
8423}
8424
8425inline void ClientImpl::set_compress(bool on) { compress_ = on; }
8426
8427inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
8428
8429inline void ClientImpl::set_interface(const std::string &intf) { interface_ = intf; }
8430
8431inline void ClientImpl::set_proxy(const std::string &host, int port) {
8432 proxy_host_ = host;
8433 proxy_port_ = port;
8434}
8435
8436inline void ClientImpl::set_proxy_basic_auth(const std::string &username, const std::string &password) {
8437 proxy_basic_auth_username_ = username;
8438 proxy_basic_auth_password_ = password;
8439}
8440
8441inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
8442 proxy_bearer_token_auth_token_ = token;
8443}
8444
8445#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8446inline void ClientImpl::set_proxy_digest_auth(const std::string &username, const std::string &password) {
8447 proxy_digest_auth_username_ = username;
8448 proxy_digest_auth_password_ = password;
8449}
8450
8451inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path) {
8452 ca_cert_file_path_ = ca_cert_file_path;
8453 ca_cert_dir_path_ = ca_cert_dir_path;
8454}
8455
8456inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
8457 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
8458 ca_cert_store_ = ca_cert_store;
8459 }
8460}
8461
8462inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, std::size_t size) const {
8463 auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
8464 auto se = detail::scope_exit([&] { BIO_free_all(mem); });
8465 if (!mem) {
8466 return nullptr;
8467 }
8468
8469 auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
8470 if (!inf) {
8471 return nullptr;
8472 }
8473
8474 auto cts = X509_STORE_new();
8475 if (cts) {
8476 for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
8477 auto itmp = sk_X509_INFO_value(inf, i);
8478 if (!itmp) {
8479 continue;
8480 }
8481
8482 if (itmp->x509) {
8483 X509_STORE_add_cert(cts, itmp->x509);
8484 }
8485 if (itmp->crl) {
8486 X509_STORE_add_crl(cts, itmp->crl);
8487 }
8488 }
8489 }
8490
8491 sk_X509_INFO_pop_free(inf, X509_INFO_free);
8492 return cts;
8493}
8494
8495inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
8496 server_certificate_verification_ = enabled;
8497}
8498
8499inline void ClientImpl::enable_server_hostname_verification(bool enabled) { server_hostname_verification_ = enabled; }
8500
8501inline void ClientImpl::set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier) {
8502 server_certificate_verifier_ = verifier;
8503}
8504#endif
8505
8506inline void ClientImpl::set_logger(Logger logger) { logger_ = std::move(logger); }
8507
8508/*
8509 * SSL Implementation
8510 */
8511#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
8512namespace detail {
8513
8514template<typename U, typename V>
8515inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup) {
8516 SSL *ssl = nullptr;
8517 {
8518 std::lock_guard<std::mutex> guard(ctx_mutex);
8519 ssl = SSL_new(ctx);
8520 }
8521
8522 if (ssl) {
8523 set_nonblocking(sock, true);
8524 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
8525 BIO_set_nbio(bio, 1);
8526 SSL_set_bio(ssl, bio, bio);
8527
8528 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
8529 SSL_shutdown(ssl);
8530 {
8531 std::lock_guard<std::mutex> guard(ctx_mutex);
8532 SSL_free(ssl);
8533 }
8534 set_nonblocking(sock, false);
8535 return nullptr;
8536 }
8537 BIO_set_nbio(bio, 0);
8538 set_nonblocking(sock, false);
8539 }
8540
8541 return ssl;
8542}
8543
8544inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock, bool shutdown_gracefully) {
8545 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
8546 // the remote has closed the network connection
8547 // Note that it is not always possible to avoid SIGPIPE, this is merely a
8548 // best-efforts.
8549 if (shutdown_gracefully) {
8550#ifdef _WIN32
8551 SSL_shutdown(ssl);
8552#else
8553 timeval tv;
8554 tv.tv_sec = 1;
8555 tv.tv_usec = 0;
8556 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const void *>(&tv), sizeof(tv));
8557
8558 auto ret = SSL_shutdown(ssl);
8559 while (ret == 0) {
8560 std::this_thread::sleep_for(std::chrono::milliseconds{100});
8561 ret = SSL_shutdown(ssl);
8562 }
8563#endif
8564 }
8565
8566 std::lock_guard<std::mutex> guard(ctx_mutex);
8567 SSL_free(ssl);
8568}
8569
8570template<typename U>
8571bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, U ssl_connect_or_accept, time_t timeout_sec,
8572 time_t timeout_usec) {
8573 auto res = 0;
8574 while ((res = ssl_connect_or_accept(ssl)) != 1) {
8575 auto err = SSL_get_error(ssl, res);
8576 switch (err) {
8577 case SSL_ERROR_WANT_READ:
8578 if (select_read(sock, timeout_sec, timeout_usec) > 0) {
8579 continue;
8580 }
8581 break;
8582 case SSL_ERROR_WANT_WRITE:
8583 if (select_write(sock, timeout_sec, timeout_usec) > 0) {
8584 continue;
8585 }
8586 break;
8587 default:
8588 break;
8589 }
8590 return false;
8591 }
8592 return true;
8593}
8594
8595template<typename T>
8596inline bool process_server_socket_ssl(const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
8597 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
8598 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
8599 time_t write_timeout_usec, T callback) {
8600 return process_server_socket_core(svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
8601 [&](bool close_connection, bool &connection_closed) {
8602 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
8603 write_timeout_sec, write_timeout_usec);
8604 return callback(strm, close_connection, connection_closed);
8605 });
8606}
8607
8608template<typename T>
8609inline bool process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
8610 time_t write_timeout_sec, time_t write_timeout_usec, T callback) {
8611 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);
8612 return callback(strm);
8613}
8614
8615class SSLInit {
8616 public:
8617 SSLInit() { OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); }
8618};
8619
8620// SSL socket stream implementation
8621inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
8622 time_t write_timeout_sec, time_t write_timeout_usec)
8623 : sock_(sock),
8624 ssl_(ssl),
8625 read_timeout_sec_(read_timeout_sec),
8626 read_timeout_usec_(read_timeout_usec),
8627 write_timeout_sec_(write_timeout_sec),
8628 write_timeout_usec_(write_timeout_usec) {
8629 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
8630}
8631
8632inline SSLSocketStream::~SSLSocketStream() = default;
8633
8634inline bool SSLSocketStream::is_readable() const {
8635 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
8636}
8637
8638inline bool SSLSocketStream::is_writable() const {
8639 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && is_socket_alive(sock_);
8640}
8641
8642inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
8643 if (SSL_pending(ssl_) > 0) {
8644 return SSL_read(ssl_, ptr, static_cast<int>(size));
8645 } else if (is_readable()) {
8646 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
8647 if (ret < 0) {
8648 auto err = SSL_get_error(ssl_, ret);
8649 auto n = 1000;
8650#ifdef _WIN32
8651 while (--n >= 0 &&
8652 (err == SSL_ERROR_WANT_READ || (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
8653#else
8654 while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
8655#endif
8656 if (SSL_pending(ssl_) > 0) {
8657 return SSL_read(ssl_, ptr, static_cast<int>(size));
8658 } else if (is_readable()) {
8659 std::this_thread::sleep_for(std::chrono::microseconds{10});
8660 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
8661 if (ret >= 0) {
8662 return ret;
8663 }
8664 err = SSL_get_error(ssl_, ret);
8665 } else {
8666 return -1;
8667 }
8668 }
8669 }
8670 return ret;
8671 }
8672 return -1;
8673}
8674
8675inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
8676 if (is_writable()) {
8677 auto handle_size = static_cast<int>(std::min<size_t>(size, (std::numeric_limits<int>::max)()));
8678
8679 auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
8680 if (ret < 0) {
8681 auto err = SSL_get_error(ssl_, ret);
8682 auto n = 1000;
8683#ifdef _WIN32
8684 while (--n >= 0 &&
8685 (err == SSL_ERROR_WANT_WRITE || (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
8686#else
8687 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
8688#endif
8689 if (is_writable()) {
8690 std::this_thread::sleep_for(std::chrono::microseconds{10});
8691 ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
8692 if (ret >= 0) {
8693 return ret;
8694 }
8695 err = SSL_get_error(ssl_, ret);
8696 } else {
8697 return -1;
8698 }
8699 }
8700 }
8701 return ret;
8702 }
8703 return -1;
8704}
8705
8706inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, int &port) const {
8707 detail::get_remote_ip_and_port(sock_, ip, port);
8708}
8709
8710inline void SSLSocketStream::get_local_ip_and_port(std::string &ip, int &port) const {
8711 detail::get_local_ip_and_port(sock_, ip, port);
8712}
8713
8714inline socket_t SSLSocketStream::socket() const { return sock_; }
8715
8716static SSLInit sslinit_;
8717
8718} // namespace detail
8719
8720// SSL HTTP server implementation
8721inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path,
8722 const char *client_ca_cert_dir_path, const char *private_key_password) {
8723 ctx_ = SSL_CTX_new(TLS_server_method());
8724
8725 if (ctx_) {
8726 SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8727
8728 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8729
8730 if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
8731 SSL_CTX_set_default_passwd_cb_userdata(ctx_, reinterpret_cast<void *>(const_cast<char *>(private_key_password)));
8732 }
8733
8734 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
8735 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1 ||
8736 SSL_CTX_check_private_key(ctx_) != 1) {
8737 SSL_CTX_free(ctx_);
8738 ctx_ = nullptr;
8739 } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
8740 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path);
8741
8742 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
8743 }
8744 }
8745}
8746
8747inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) {
8748 ctx_ = SSL_CTX_new(TLS_server_method());
8749
8750 if (ctx_) {
8751 SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8752
8753 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8754
8755 if (SSL_CTX_use_certificate(ctx_, cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
8756 SSL_CTX_free(ctx_);
8757 ctx_ = nullptr;
8758 } else if (client_ca_cert_store) {
8759 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
8760
8761 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
8762 }
8763 }
8764}
8765
8766inline SSLServer::SSLServer(const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
8767 ctx_ = SSL_CTX_new(TLS_method());
8768 if (ctx_) {
8769 if (!setup_ssl_ctx_callback(*ctx_)) {
8770 SSL_CTX_free(ctx_);
8771 ctx_ = nullptr;
8772 }
8773 }
8774}
8775
8776inline SSLServer::~SSLServer() {
8777 if (ctx_) {
8778 SSL_CTX_free(ctx_);
8779 }
8780}
8781
8782inline bool SSLServer::is_valid() const { return ctx_; }
8783
8784inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
8785
8786inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key, X509_STORE *client_ca_cert_store) {
8787 std::lock_guard<std::mutex> guard(ctx_mutex_);
8788
8789 SSL_CTX_use_certificate(ctx_, cert);
8790 SSL_CTX_use_PrivateKey(ctx_, private_key);
8791
8792 if (client_ca_cert_store != nullptr) {
8793 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
8794 }
8795}
8796
8797inline bool SSLServer::process_and_close_socket(socket_t sock) {
8798 auto ssl = detail::ssl_new(
8799 sock, ctx_, ctx_mutex_,
8800 [&](SSL *ssl2) {
8801 return detail::ssl_connect_or_accept_nonblocking(sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
8802 },
8803 [](SSL * /*ssl2*/) { return true; });
8804
8805 auto ret = false;
8806 if (ssl) {
8807 std::string remote_addr;
8808 int remote_port = 0;
8809 detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
8810
8811 std::string local_addr;
8812 int local_port = 0;
8813 detail::get_local_ip_and_port(sock, local_addr, local_port);
8814
8815 ret = detail::process_server_socket_ssl(
8816 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, read_timeout_usec_,
8817 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm, bool close_connection, bool &connection_closed) {
8818 return process_request(strm, remote_addr, remote_port, local_addr, local_port, close_connection,
8819 connection_closed, [&](Request &req) { req.ssl = ssl; });
8820 });
8821
8822 // Shutdown gracefully if the result seemed successful, non-gracefully if
8823 // the connection appeared to be closed.
8824 const bool shutdown_gracefully = ret;
8825 detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
8826 }
8827
8828 detail::shutdown_socket(sock);
8829 detail::close_socket(sock);
8830 return ret;
8831}
8832
8833// SSL HTTP client implementation
8834inline SSLClient::SSLClient(const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {}
8835
8836inline SSLClient::SSLClient(const std::string &host, int port) : SSLClient(host, port, std::string(), std::string()) {}
8837
8838inline SSLClient::SSLClient(const std::string &host, int port, const std::string &client_cert_path,
8839 const std::string &client_key_path, const std::string &private_key_password)
8840 : ClientImpl(host, port, client_cert_path, client_key_path) {
8841 ctx_ = SSL_CTX_new(TLS_client_method());
8842
8843 SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8844
8845 detail::split(&host_[0], &host_[host_.size()], '.',
8846 [&](const char *b, const char *e) { host_components_.emplace_back(b, e); });
8847
8848 if (!client_cert_path.empty() && !client_key_path.empty()) {
8849 if (!private_key_password.empty()) {
8850 SSL_CTX_set_default_passwd_cb_userdata(
8851 ctx_, reinterpret_cast<void *>(const_cast<char *>(private_key_password.c_str())));
8852 }
8853
8854 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 ||
8855 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1) {
8856 SSL_CTX_free(ctx_);
8857 ctx_ = nullptr;
8858 }
8859 }
8860}
8861
8862inline SSLClient::SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key,
8863 const std::string &private_key_password)
8864 : ClientImpl(host, port) {
8865 ctx_ = SSL_CTX_new(TLS_client_method());
8866
8867 detail::split(&host_[0], &host_[host_.size()], '.',
8868 [&](const char *b, const char *e) { host_components_.emplace_back(b, e); });
8869
8870 if (client_cert != nullptr && client_key != nullptr) {
8871 if (!private_key_password.empty()) {
8872 SSL_CTX_set_default_passwd_cb_userdata(
8873 ctx_, reinterpret_cast<void *>(const_cast<char *>(private_key_password.c_str())));
8874 }
8875
8876 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
8877 SSL_CTX_free(ctx_);
8878 ctx_ = nullptr;
8879 }
8880 }
8881}
8882
8883inline SSLClient::~SSLClient() {
8884 if (ctx_) {
8885 SSL_CTX_free(ctx_);
8886 }
8887 // Make sure to shut down SSL since shutdown_ssl will resolve to the
8888 // base function rather than the derived function once we get to the
8889 // base class destructor, and won't free the SSL (causing a leak).
8890 shutdown_ssl_impl(socket_, true);
8891}
8892
8893inline bool SSLClient::is_valid() const { return ctx_; }
8894
8895inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
8896 if (ca_cert_store) {
8897 if (ctx_) {
8898 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
8899 // Free memory allocated for old cert and use new store `ca_cert_store`
8900 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
8901 }
8902 } else {
8903 X509_STORE_free(ca_cert_store);
8904 }
8905 }
8906}
8907
8908inline void SSLClient::load_ca_cert_store(const char *ca_cert, std::size_t size) {
8909 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
8910}
8911
8912inline long SSLClient::get_openssl_verify_result() const { return verify_result_; }
8913
8914inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
8915
8916inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
8917 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
8918}
8919
8920// Assumes that socket_mutex_ is locked and that there are no requests in flight
8921inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, bool &success, Error &error) {
8922 success = true;
8923 Response proxy_res;
8924 if (!detail::process_client_socket(socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8925 write_timeout_usec_, [&](Stream &strm) {
8926 Request req2;
8927 req2.method = "CONNECT";
8928 req2.path = host_and_port_;
8929 return process_request(strm, req2, proxy_res, false, error);
8930 })) {
8931 // Thread-safe to close everything because we are assuming there are no
8932 // requests in flight
8933 shutdown_ssl(socket, true);
8934 shutdown_socket(socket);
8935 close_socket(socket);
8936 success = false;
8937 return false;
8938 }
8939
8940 if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {
8941 if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) {
8942 std::map<std::string, std::string> auth;
8943 if (detail::parse_www_authenticate(proxy_res, auth, true)) {
8944 proxy_res = Response();
8945 if (!detail::process_client_socket(socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8946 write_timeout_usec_, [&](Stream &strm) {
8947 Request req3;
8948 req3.method = "CONNECT";
8949 req3.path = host_and_port_;
8950 req3.headers.insert(detail::make_digest_authentication_header(
8951 req3, auth, 1, detail::random_string(10), proxy_digest_auth_username_,
8952 proxy_digest_auth_password_, true));
8953 return process_request(strm, req3, proxy_res, false, error);
8954 })) {
8955 // Thread-safe to close everything because we are assuming there are
8956 // no requests in flight
8957 shutdown_ssl(socket, true);
8958 shutdown_socket(socket);
8959 close_socket(socket);
8960 success = false;
8961 return false;
8962 }
8963 }
8964 }
8965 }
8966
8967 // If status code is not 200, proxy request is failed.
8968 // Set error to ProxyConnection and return proxy response
8969 // as the response of the request
8970 if (proxy_res.status != StatusCode::OK_200) {
8971 error = Error::ProxyConnection;
8972 res = std::move(proxy_res);
8973 // Thread-safe to close everything because we are assuming there are
8974 // no requests in flight
8975 shutdown_ssl(socket, true);
8976 shutdown_socket(socket);
8977 close_socket(socket);
8978 return false;
8979 }
8980
8981 return true;
8982}
8983
8984inline bool SSLClient::load_certs() {
8985 auto ret = true;
8986
8987 std::call_once(initialize_cert_, [&]() {
8988 std::lock_guard<std::mutex> guard(ctx_mutex_);
8989 if (!ca_cert_file_path_.empty()) {
8990 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), nullptr)) {
8991 ret = false;
8992 }
8993 } else if (!ca_cert_dir_path_.empty()) {
8994 if (!SSL_CTX_load_verify_locations(ctx_, nullptr, ca_cert_dir_path_.c_str())) {
8995 ret = false;
8996 }
8997 } else {
8998 auto loaded = false;
8999#ifdef _WIN32
9000 loaded = detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
9001#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
9002#if TARGET_OS_OSX
9003 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
9004#endif // TARGET_OS_OSX
9005#endif // _WIN32
9006 if (!loaded) {
9007 SSL_CTX_set_default_verify_paths(ctx_);
9008 }
9009 }
9010 });
9011
9012 return ret;
9013}
9014
9015inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
9016 auto ssl = detail::ssl_new(
9017 socket.sock, ctx_, ctx_mutex_,
9018 [&](SSL *ssl2) {
9019 if (server_certificate_verification_) {
9020 if (!load_certs()) {
9021 error = Error::SSLLoadingCerts;
9022 return false;
9023 }
9024 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
9025 }
9026
9027 if (!detail::ssl_connect_or_accept_nonblocking(socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
9028 connection_timeout_usec_)) {
9029 error = Error::SSLConnection;
9030 return false;
9031 }
9032
9033 if (server_certificate_verification_) {
9034 if (server_certificate_verifier_) {
9035 if (!server_certificate_verifier_(ssl2)) {
9036 error = Error::SSLServerVerification;
9037 return false;
9038 }
9039 } else {
9040 verify_result_ = SSL_get_verify_result(ssl2);
9041
9042 if (verify_result_ != X509_V_OK) {
9043 error = Error::SSLServerVerification;
9044 return false;
9045 }
9046
9047 auto server_cert = SSL_get1_peer_certificate(ssl2);
9048 auto se = detail::scope_exit([&] { X509_free(server_cert); });
9049
9050 if (server_cert == nullptr) {
9051 error = Error::SSLServerVerification;
9052 return false;
9053 }
9054
9055 if (server_hostname_verification_) {
9056 if (!verify_host(server_cert)) {
9057 error = Error::SSLServerHostnameVerification;
9058 return false;
9059 }
9060 }
9061 }
9062 }
9063
9064 return true;
9065 },
9066 [&](SSL *ssl2) {
9067#if defined(OPENSSL_IS_BORINGSSL)
9068 SSL_set_tlsext_host_name(ssl2, host_.c_str());
9069#else
9070 // NOTE: Direct call instead of using the OpenSSL macro to suppress
9071 // -Wold-style-cast warning
9072 SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
9073 static_cast<void *>(const_cast<char *>(host_.c_str())));
9074#endif
9075 return true;
9076 });
9077
9078 if (ssl) {
9079 socket.ssl = ssl;
9080 return true;
9081 }
9082
9083 shutdown_socket(socket);
9084 close_socket(socket);
9085 return false;
9086}
9087
9088inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
9089 shutdown_ssl_impl(socket, shutdown_gracefully);
9090}
9091
9092inline void SSLClient::shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully) {
9093 if (socket.sock == INVALID_SOCKET) {
9094 assert(socket.ssl == nullptr);
9095 return;
9096 }
9097 if (socket.ssl) {
9098 detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock, shutdown_gracefully);
9099 socket.ssl = nullptr;
9100 }
9101 assert(socket.ssl == nullptr);
9102}
9103
9104inline bool SSLClient::process_socket(const Socket &socket, std::function<bool(Stream &strm)> callback) {
9105 assert(socket.ssl);
9106 return detail::process_client_socket_ssl(socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
9107 write_timeout_sec_, write_timeout_usec_, std::move(callback));
9108}
9109
9110inline bool SSLClient::is_ssl() const { return true; }
9111
9112inline bool SSLClient::verify_host(X509 *server_cert) const {
9113 /* Quote from RFC2818 section 3.1 "Server Identity"
9114
9115 If a subjectAltName extension of type dNSName is present, that MUST
9116 be used as the identity. Otherwise, the (most specific) Common Name
9117 field in the Subject field of the certificate MUST be used. Although
9118 the use of the Common Name is existing practice, it is deprecated and
9119 Certification Authorities are encouraged to use the dNSName instead.
9120
9121 Matching is performed using the matching rules specified by
9122 [RFC2459]. If more than one identity of a given type is present in
9123 the certificate (e.g., more than one dNSName name, a match in any one
9124 of the set is considered acceptable.) Names may contain the wildcard
9125 character * which is considered to match any single domain name
9126 component or component fragment. E.g., *.a.com matches foo.a.com but
9127 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
9128
9129 In some cases, the URI is specified as an IP address rather than a
9130 hostname. In this case, the iPAddress subjectAltName must be present
9131 in the certificate and must exactly match the IP in the URI.
9132
9133 */
9134 return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert);
9135}
9136
9137inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
9138 auto ret = false;
9139
9140 auto type = GEN_DNS;
9141
9142 struct in6_addr addr6 {};
9143 struct in_addr addr {};
9144 size_t addr_len = 0;
9145
9146#ifndef __MINGW32__
9147 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
9148 type = GEN_IPADD;
9149 addr_len = sizeof(struct in6_addr);
9150 } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
9151 type = GEN_IPADD;
9152 addr_len = sizeof(struct in_addr);
9153 }
9154#endif
9155
9156 auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
9157 X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
9158
9159 if (alt_names) {
9160 auto dsn_matched = false;
9161 auto ip_matched = false;
9162
9163 auto count = sk_GENERAL_NAME_num(alt_names);
9164
9165 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
9166 auto val = sk_GENERAL_NAME_value(alt_names, i);
9167 if (val->type == type) {
9168 auto name = reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));
9169 auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));
9170
9171 switch (type) {
9172 case GEN_DNS:
9173 dsn_matched = check_host_name(name, name_len);
9174 break;
9175
9176 case GEN_IPADD:
9177 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
9178 ip_matched = true;
9179 }
9180 break;
9181 }
9182 }
9183 }
9184
9185 if (dsn_matched || ip_matched) {
9186 ret = true;
9187 }
9188 }
9189
9190 GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));
9191 return ret;
9192}
9193
9194inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
9195 const auto subject_name = X509_get_subject_name(server_cert);
9196
9197 if (subject_name != nullptr) {
9198 char name[BUFSIZ];
9199 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name));
9200
9201 if (name_len != -1) {
9202 return check_host_name(name, static_cast<size_t>(name_len));
9203 }
9204 }
9205
9206 return false;
9207}
9208
9209inline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const {
9210 if (host_.size() == pattern_len && host_ == pattern) {
9211 return true;
9212 }
9213
9214 // Wildcard match
9215 // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
9216 std::vector<std::string> pattern_components;
9217 detail::split(&pattern[0], &pattern[pattern_len], '.',
9218 [&](const char *b, const char *e) { pattern_components.emplace_back(b, e); });
9219
9220 if (host_components_.size() != pattern_components.size()) {
9221 return false;
9222 }
9223
9224 auto itr = pattern_components.begin();
9225 for (const auto &h : host_components_) {
9226 auto &p = *itr;
9227 if (p != h && p != "*") {
9228 auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h));
9229 if (!partial_match) {
9230 return false;
9231 }
9232 }
9233 ++itr;
9234 }
9235
9236 return true;
9237}
9238#endif
9239
9240// Universal client implementation
9241inline Client::Client(const std::string &scheme_host_port) : Client(scheme_host_port, std::string(), std::string()) {}
9242
9243inline Client::Client(const std::string &scheme_host_port, const std::string &client_cert_path,
9244 const std::string &client_key_path) {
9245 const static std::regex re(R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
9246
9247 std::smatch m;
9248 if (std::regex_match(scheme_host_port, m, re)) {
9249 auto scheme = m[1].str();
9250
9251#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9252 if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
9253#else
9254 if (!scheme.empty() && scheme != "http") {
9255#endif
9256#ifndef CPPHTTPLIB_NO_EXCEPTIONS
9257 std::string msg = "'" + scheme + "' scheme is not supported.";
9258 throw std::invalid_argument(msg);
9259#endif
9260 return;
9261 }
9262
9263 auto is_ssl = scheme == "https";
9264
9265 auto host = m[2].str();
9266 if (host.empty()) {
9267 host = m[3].str();
9268 }
9269
9270 auto port_str = m[4].str();
9271 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
9272
9273 if (is_ssl) {
9274#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9275 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path, client_key_path);
9276 is_ssl_ = is_ssl;
9277#endif
9278 } else {
9279 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path, client_key_path);
9280 }
9281 } else {
9282 // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
9283 // if port param below changes.
9284 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80, client_cert_path, client_key_path);
9285 }
9286} // namespace detail
9287
9288inline Client::Client(const std::string &host, int port) : cli_(detail::make_unique<ClientImpl>(host, port)) {}
9289
9290inline Client::Client(const std::string &host, int port, const std::string &client_cert_path,
9291 const std::string &client_key_path)
9292 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path, client_key_path)) {}
9293
9294inline Client::~Client() = default;
9295
9296inline bool Client::is_valid() const { return cli_ != nullptr && cli_->is_valid(); }
9297
9298inline Result Client::Get(const std::string &path) { return cli_->Get(path); }
9299inline Result Client::Get(const std::string &path, const Headers &headers) { return cli_->Get(path, headers); }
9300inline Result Client::Get(const std::string &path, Progress progress) { return cli_->Get(path, std::move(progress)); }
9301inline Result Client::Get(const std::string &path, const Headers &headers, Progress progress) {
9302 return cli_->Get(path, headers, std::move(progress));
9303}
9304inline Result Client::Get(const std::string &path, ContentReceiver content_receiver) {
9305 return cli_->Get(path, std::move(content_receiver));
9306}
9307inline Result Client::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver) {
9308 return cli_->Get(path, headers, std::move(content_receiver));
9309}
9310inline Result Client::Get(const std::string &path, ContentReceiver content_receiver, Progress progress) {
9311 return cli_->Get(path, std::move(content_receiver), std::move(progress));
9312}
9313inline Result Client::Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver,
9314 Progress progress) {
9315 return cli_->Get(path, headers, std::move(content_receiver), std::move(progress));
9316}
9317inline Result Client::Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver) {
9318 return cli_->Get(path, std::move(response_handler), std::move(content_receiver));
9319}
9320inline Result Client::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
9321 ContentReceiver content_receiver) {
9322 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver));
9323}
9324inline Result Client::Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver,
9325 Progress progress) {
9326 return cli_->Get(path, std::move(response_handler), std::move(content_receiver), std::move(progress));
9327}
9328inline Result Client::Get(const std::string &path, const Headers &headers, ResponseHandler response_handler,
9329 ContentReceiver content_receiver, Progress progress) {
9330 return cli_->Get(path, headers, std::move(response_handler), std::move(content_receiver), std::move(progress));
9331}
9332inline Result Client::Get(const std::string &path, const Params &params, const Headers &headers, Progress progress) {
9333 return cli_->Get(path, params, headers, std::move(progress));
9334}
9335inline Result Client::Get(const std::string &path, const Params &params, const Headers &headers,
9336 ContentReceiver content_receiver, Progress progress) {
9337 return cli_->Get(path, params, headers, std::move(content_receiver), std::move(progress));
9338}
9339inline Result Client::Get(const std::string &path, const Params &params, const Headers &headers,
9340 ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) {
9341 return cli_->Get(path, params, headers, std::move(response_handler), std::move(content_receiver),
9342 std::move(progress));
9343}
9344
9345inline Result Client::Head(const std::string &path) { return cli_->Head(path); }
9346inline Result Client::Head(const std::string &path, const Headers &headers) { return cli_->Head(path, headers); }
9347
9348inline Result Client::Post(const std::string &path) { return cli_->Post(path); }
9349inline Result Client::Post(const std::string &path, const Headers &headers) { return cli_->Post(path, headers); }
9350inline Result Client::Post(const std::string &path, const char *body, size_t content_length,
9351 const std::string &content_type) {
9352 return cli_->Post(path, body, content_length, content_type);
9353}
9354inline Result Client::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9355 const std::string &content_type) {
9356 return cli_->Post(path, headers, body, content_length, content_type);
9357}
9358inline Result Client::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9359 const std::string &content_type, Progress progress) {
9360 return cli_->Post(path, headers, body, content_length, content_type, progress);
9361}
9362inline Result Client::Post(const std::string &path, const std::string &body, const std::string &content_type) {
9363 return cli_->Post(path, body, content_type);
9364}
9365inline Result Client::Post(const std::string &path, const std::string &body, const std::string &content_type,
9366 Progress progress) {
9367 return cli_->Post(path, body, content_type, progress);
9368}
9369inline Result Client::Post(const std::string &path, const Headers &headers, const std::string &body,
9370 const std::string &content_type) {
9371 return cli_->Post(path, headers, body, content_type);
9372}
9373inline Result Client::Post(const std::string &path, const Headers &headers, const std::string &body,
9374 const std::string &content_type, Progress progress) {
9375 return cli_->Post(path, headers, body, content_type, progress);
9376}
9377inline Result Client::Post(const std::string &path, size_t content_length, ContentProvider content_provider,
9378 const std::string &content_type) {
9379 return cli_->Post(path, content_length, std::move(content_provider), content_type);
9380}
9381inline Result Client::Post(const std::string &path, ContentProviderWithoutLength content_provider,
9382 const std::string &content_type) {
9383 return cli_->Post(path, std::move(content_provider), content_type);
9384}
9385inline Result Client::Post(const std::string &path, const Headers &headers, size_t content_length,
9386 ContentProvider content_provider, const std::string &content_type) {
9387 return cli_->Post(path, headers, content_length, std::move(content_provider), content_type);
9388}
9389inline Result Client::Post(const std::string &path, const Headers &headers,
9390 ContentProviderWithoutLength content_provider, const std::string &content_type) {
9391 return cli_->Post(path, headers, std::move(content_provider), content_type);
9392}
9393inline Result Client::Post(const std::string &path, const Params &params) { return cli_->Post(path, params); }
9394inline Result Client::Post(const std::string &path, const Headers &headers, const Params &params) {
9395 return cli_->Post(path, headers, params);
9396}
9397inline Result Client::Post(const std::string &path, const Headers &headers, const Params &params, Progress progress) {
9398 return cli_->Post(path, headers, params, progress);
9399}
9400inline Result Client::Post(const std::string &path, const MultipartFormDataItems &items) {
9401 return cli_->Post(path, items);
9402}
9403inline Result Client::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) {
9404 return cli_->Post(path, headers, items);
9405}
9406inline Result Client::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
9407 const std::string &boundary) {
9408 return cli_->Post(path, headers, items, boundary);
9409}
9410inline Result Client::Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
9411 const MultipartFormDataProviderItems &provider_items) {
9412 return cli_->Post(path, headers, items, provider_items);
9413}
9414inline Result Client::Put(const std::string &path) { return cli_->Put(path); }
9415inline Result Client::Put(const std::string &path, const char *body, size_t content_length,
9416 const std::string &content_type) {
9417 return cli_->Put(path, body, content_length, content_type);
9418}
9419inline Result Client::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9420 const std::string &content_type) {
9421 return cli_->Put(path, headers, body, content_length, content_type);
9422}
9423inline Result Client::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9424 const std::string &content_type, Progress progress) {
9425 return cli_->Put(path, headers, body, content_length, content_type, progress);
9426}
9427inline Result Client::Put(const std::string &path, const std::string &body, const std::string &content_type) {
9428 return cli_->Put(path, body, content_type);
9429}
9430inline Result Client::Put(const std::string &path, const std::string &body, const std::string &content_type,
9431 Progress progress) {
9432 return cli_->Put(path, body, content_type, progress);
9433}
9434inline Result Client::Put(const std::string &path, const Headers &headers, const std::string &body,
9435 const std::string &content_type) {
9436 return cli_->Put(path, headers, body, content_type);
9437}
9438inline Result Client::Put(const std::string &path, const Headers &headers, const std::string &body,
9439 const std::string &content_type, Progress progress) {
9440 return cli_->Put(path, headers, body, content_type, progress);
9441}
9442inline Result Client::Put(const std::string &path, size_t content_length, ContentProvider content_provider,
9443 const std::string &content_type) {
9444 return cli_->Put(path, content_length, std::move(content_provider), content_type);
9445}
9446inline Result Client::Put(const std::string &path, ContentProviderWithoutLength content_provider,
9447 const std::string &content_type) {
9448 return cli_->Put(path, std::move(content_provider), content_type);
9449}
9450inline Result Client::Put(const std::string &path, const Headers &headers, size_t content_length,
9451 ContentProvider content_provider, const std::string &content_type) {
9452 return cli_->Put(path, headers, content_length, std::move(content_provider), content_type);
9453}
9454inline Result Client::Put(const std::string &path, const Headers &headers,
9455 ContentProviderWithoutLength content_provider, const std::string &content_type) {
9456 return cli_->Put(path, headers, std::move(content_provider), content_type);
9457}
9458inline Result Client::Put(const std::string &path, const Params &params) { return cli_->Put(path, params); }
9459inline Result Client::Put(const std::string &path, const Headers &headers, const Params &params) {
9460 return cli_->Put(path, headers, params);
9461}
9462inline Result Client::Put(const std::string &path, const Headers &headers, const Params &params, Progress progress) {
9463 return cli_->Put(path, headers, params, progress);
9464}
9465inline Result Client::Put(const std::string &path, const MultipartFormDataItems &items) {
9466 return cli_->Put(path, items);
9467}
9468inline Result Client::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items) {
9469 return cli_->Put(path, headers, items);
9470}
9471inline Result Client::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
9472 const std::string &boundary) {
9473 return cli_->Put(path, headers, items, boundary);
9474}
9475inline Result Client::Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items,
9476 const MultipartFormDataProviderItems &provider_items) {
9477 return cli_->Put(path, headers, items, provider_items);
9478}
9479inline Result Client::Patch(const std::string &path) { return cli_->Patch(path); }
9480inline Result Client::Patch(const std::string &path, const char *body, size_t content_length,
9481 const std::string &content_type) {
9482 return cli_->Patch(path, body, content_length, content_type);
9483}
9484inline Result Client::Patch(const std::string &path, const char *body, size_t content_length,
9485 const std::string &content_type, Progress progress) {
9486 return cli_->Patch(path, body, content_length, content_type, progress);
9487}
9488inline Result Client::Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9489 const std::string &content_type) {
9490 return cli_->Patch(path, headers, body, content_length, content_type);
9491}
9492inline Result Client::Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9493 const std::string &content_type, Progress progress) {
9494 return cli_->Patch(path, headers, body, content_length, content_type, progress);
9495}
9496inline Result Client::Patch(const std::string &path, const std::string &body, const std::string &content_type) {
9497 return cli_->Patch(path, body, content_type);
9498}
9499inline Result Client::Patch(const std::string &path, const std::string &body, const std::string &content_type,
9500 Progress progress) {
9501 return cli_->Patch(path, body, content_type, progress);
9502}
9503inline Result Client::Patch(const std::string &path, const Headers &headers, const std::string &body,
9504 const std::string &content_type) {
9505 return cli_->Patch(path, headers, body, content_type);
9506}
9507inline Result Client::Patch(const std::string &path, const Headers &headers, const std::string &body,
9508 const std::string &content_type, Progress progress) {
9509 return cli_->Patch(path, headers, body, content_type, progress);
9510}
9511inline Result Client::Patch(const std::string &path, size_t content_length, ContentProvider content_provider,
9512 const std::string &content_type) {
9513 return cli_->Patch(path, content_length, std::move(content_provider), content_type);
9514}
9515inline Result Client::Patch(const std::string &path, ContentProviderWithoutLength content_provider,
9516 const std::string &content_type) {
9517 return cli_->Patch(path, std::move(content_provider), content_type);
9518}
9519inline Result Client::Patch(const std::string &path, const Headers &headers, size_t content_length,
9520 ContentProvider content_provider, const std::string &content_type) {
9521 return cli_->Patch(path, headers, content_length, std::move(content_provider), content_type);
9522}
9523inline Result Client::Patch(const std::string &path, const Headers &headers,
9524 ContentProviderWithoutLength content_provider, const std::string &content_type) {
9525 return cli_->Patch(path, headers, std::move(content_provider), content_type);
9526}
9527inline Result Client::Delete(const std::string &path) { return cli_->Delete(path); }
9528inline Result Client::Delete(const std::string &path, const Headers &headers) { return cli_->Delete(path, headers); }
9529inline Result Client::Delete(const std::string &path, const char *body, size_t content_length,
9530 const std::string &content_type) {
9531 return cli_->Delete(path, body, content_length, content_type);
9532}
9533inline Result Client::Delete(const std::string &path, const char *body, size_t content_length,
9534 const std::string &content_type, Progress progress) {
9535 return cli_->Delete(path, body, content_length, content_type, progress);
9536}
9537inline Result Client::Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9538 const std::string &content_type) {
9539 return cli_->Delete(path, headers, body, content_length, content_type);
9540}
9541inline Result Client::Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length,
9542 const std::string &content_type, Progress progress) {
9543 return cli_->Delete(path, headers, body, content_length, content_type, progress);
9544}
9545inline Result Client::Delete(const std::string &path, const std::string &body, const std::string &content_type) {
9546 return cli_->Delete(path, body, content_type);
9547}
9548inline Result Client::Delete(const std::string &path, const std::string &body, const std::string &content_type,
9549 Progress progress) {
9550 return cli_->Delete(path, body, content_type, progress);
9551}
9552inline Result Client::Delete(const std::string &path, const Headers &headers, const std::string &body,
9553 const std::string &content_type) {
9554 return cli_->Delete(path, headers, body, content_type);
9555}
9556inline Result Client::Delete(const std::string &path, const Headers &headers, const std::string &body,
9557 const std::string &content_type, Progress progress) {
9558 return cli_->Delete(path, headers, body, content_type, progress);
9559}
9560inline Result Client::Options(const std::string &path) { return cli_->Options(path); }
9561inline Result Client::Options(const std::string &path, const Headers &headers) { return cli_->Options(path, headers); }
9562
9563inline bool Client::send(Request &req, Response &res, Error &error) { return cli_->send(req, res, error); }
9564
9565inline Result Client::send(const Request &req) { return cli_->send(req); }
9566
9567inline void Client::stop() { cli_->stop(); }
9568
9569inline std::string Client::host() const { return cli_->host(); }
9570
9571inline int Client::port() const { return cli_->port(); }
9572
9573inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
9574
9575inline socket_t Client::socket() const { return cli_->socket(); }
9576
9577inline void Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
9578 cli_->set_hostname_addr_map(std::move(addr_map));
9579}
9580
9581inline void Client::set_default_headers(Headers headers) { cli_->set_default_headers(std::move(headers)); }
9582
9583inline void Client::set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer) {
9584 cli_->set_header_writer(writer);
9585}
9586
9587inline void Client::set_address_family(int family) { cli_->set_address_family(family); }
9588
9589inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
9590
9591inline void Client::set_socket_options(SocketOptions socket_options) {
9592 cli_->set_socket_options(std::move(socket_options));
9593}
9594
9595inline void Client::set_connection_timeout(time_t sec, time_t usec) { cli_->set_connection_timeout(sec, usec); }
9596
9597inline void Client::set_read_timeout(time_t sec, time_t usec) { cli_->set_read_timeout(sec, usec); }
9598
9599inline void Client::set_write_timeout(time_t sec, time_t usec) { cli_->set_write_timeout(sec, usec); }
9600
9601inline void Client::set_basic_auth(const std::string &username, const std::string &password) {
9602 cli_->set_basic_auth(username, password);
9603}
9604inline void Client::set_bearer_token_auth(const std::string &token) { cli_->set_bearer_token_auth(token); }
9605#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9606inline void Client::set_digest_auth(const std::string &username, const std::string &password) {
9607 cli_->set_digest_auth(username, password);
9608}
9609#endif
9610
9611inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
9612inline void Client::set_follow_location(bool on) { cli_->set_follow_location(on); }
9613
9614inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }
9615
9616inline void Client::set_compress(bool on) { cli_->set_compress(on); }
9617
9618inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
9619
9620inline void Client::set_interface(const std::string &intf) { cli_->set_interface(intf); }
9621
9622inline void Client::set_proxy(const std::string &host, int port) { cli_->set_proxy(host, port); }
9623inline void Client::set_proxy_basic_auth(const std::string &username, const std::string &password) {
9624 cli_->set_proxy_basic_auth(username, password);
9625}
9626inline void Client::set_proxy_bearer_token_auth(const std::string &token) { cli_->set_proxy_bearer_token_auth(token); }
9627#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9628inline void Client::set_proxy_digest_auth(const std::string &username, const std::string &password) {
9629 cli_->set_proxy_digest_auth(username, password);
9630}
9631#endif
9632
9633#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9634inline void Client::enable_server_certificate_verification(bool enabled) {
9635 cli_->enable_server_certificate_verification(enabled);
9636}
9637
9638inline void Client::enable_server_hostname_verification(bool enabled) {
9639 cli_->enable_server_hostname_verification(enabled);
9640}
9641
9642inline void Client::set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier) {
9643 cli_->set_server_certificate_verifier(verifier);
9644}
9645#endif
9646
9647inline void Client::set_logger(Logger logger) { cli_->set_logger(std::move(logger)); }
9648
9649#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9650inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path) {
9651 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
9652}
9653
9654inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
9655 if (is_ssl_) {
9656 static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
9657 } else {
9658 cli_->set_ca_cert_store(ca_cert_store);
9659 }
9660}
9661
9662inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
9663 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
9664}
9665
9666inline long Client::get_openssl_verify_result() const {
9667 if (is_ssl_) {
9668 return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
9669 }
9670 return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
9671}
9672
9673inline SSL_CTX *Client::ssl_context() const {
9674 if (is_ssl_) {
9675 return static_cast<SSLClient &>(*cli_).ssl_context();
9676 }
9677 return nullptr;
9678}
9679#endif
9680
9681// ----------------------------------------------------------------------------
9682
9683} // namespace httplib
9684
9685#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
9686#undef poll
9687#endif
9688
9689#endif // CPPHTTPLIB_HTTPLIB_H
9690
9691#endif
bool operator==(const DeferredMessage &test) const
uint8_t l
Definition bl0906.h:0
uint8_t m
Definition bl0906.h:1
uint8_t h
Definition bl0906.h:2
uint8_t status
Definition bl0942.h:8
float position
Definition cover.h:0
uint8_t type
void setup()
uint32_t socklen_t
Definition headers.h:97
__int64 ssize_t
Definition httplib.h:175
SOCKET socket_t
Definition httplib.h:201
mopeka_std_values val[4]
uint8_t duration
Definition msa3xx.h:0
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
sa_family_t ss_family
Definition headers.h:92
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint32_t len
uint16_t trailer
uint16_t length
Definition tt21100.cpp:0
uint16_t x
Definition tt21100.cpp:5