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