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