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