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