1 //
2 // httplib.h
3 //
4 // Copyright (c) 2020 Yuji Hirose. All rights reserved.
5 // MIT License
6 //
7
8 #ifndef CPPHTTPLIB_HTTPLIB_H
9 #define CPPHTTPLIB_HTTPLIB_H
10
11 /*
12 * Configuration
13 */
14
15 #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
16 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
17 #endif
18
19 #ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
20 #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
21 #endif
22
23 #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
24 #define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
25 #endif
26
27 #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
28 #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
29 #endif
30
31 #ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
32 #define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
33 #endif
34
35 #ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
36 #define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
37 #endif
38
39 #ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
40 #define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
41 #endif
42
43 #ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
44 #define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
45 #endif
46
47 #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
48 #define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
49 #endif
50
51 #ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
52 #ifdef _WIN32
53 #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
54 #else
55 #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
56 #endif
57 #endif
58
59 #ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
60 #define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
61 #endif
62
63 #ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
64 #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
65 #endif
66
67 #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
68 #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
69 #endif
70
71 #ifndef CPPHTTPLIB_TCP_NODELAY
72 #define CPPHTTPLIB_TCP_NODELAY false
73 #endif
74
75 #ifndef CPPHTTPLIB_RECV_BUFSIZ
76 #define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
77 #endif
78
79 #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
80 #define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
81 #endif
82
83 #ifndef CPPHTTPLIB_THREAD_POOL_COUNT
84 #define CPPHTTPLIB_THREAD_POOL_COUNT \
85 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
86 ? std::thread::hardware_concurrency() - 1 \
87 : 0))
88 #endif
89
90 #ifndef CPPHTTPLIB_RECV_FLAGS
91 #define CPPHTTPLIB_RECV_FLAGS 0
92 #endif
93
94 #ifndef CPPHTTPLIB_SEND_FLAGS
95 #define CPPHTTPLIB_SEND_FLAGS 0
96 #endif
97
98 /*
99 * Headers
100 */
101
102 #ifdef _WIN32
103 #ifndef _CRT_SECURE_NO_WARNINGS
104 #define _CRT_SECURE_NO_WARNINGS
105 #endif //_CRT_SECURE_NO_WARNINGS
106
107 #ifndef _CRT_NONSTDC_NO_DEPRECATE
108 #define _CRT_NONSTDC_NO_DEPRECATE
109 #endif //_CRT_NONSTDC_NO_DEPRECATE
110
111 #if defined(_MSC_VER)
112 #ifdef _WIN64
113 using ssize_t = __int64;
114 #else
115 using ssize_t = int;
116 #endif
117
118 #if _MSC_VER < 1900
119 #define snprintf _snprintf_s
120 #endif
121 #endif // _MSC_VER
122
123 #ifndef S_ISREG
124 #define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
125 #endif // S_ISREG
126
127 #ifndef S_ISDIR
128 #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
129 #endif // S_ISDIR
130
131 #ifndef NOMINMAX
132 #define NOMINMAX
133 #endif // NOMINMAX
134
135 #include <io.h>
136 #include <winsock2.h>
137
138 #include <wincrypt.h>
139 #include <ws2tcpip.h>
140
141 #ifndef WSA_FLAG_NO_HANDLE_INHERIT
142 #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
143 #endif
144
145 #ifdef _MSC_VER
146 #pragma comment(lib, "ws2_32.lib")
147 #pragma comment(lib, "crypt32.lib")
148 #pragma comment(lib, "cryptui.lib")
149 #endif
150
151 #ifndef strcasecmp
152 #define strcasecmp _stricmp
153 #endif // strcasecmp
154
155 using socket_t = SOCKET;
156 #ifdef CPPHTTPLIB_USE_POLL
157 #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
158 #endif
159
160 #else // not _WIN32
161
162 #include <arpa/inet.h>
163 #include <cstring>
164 #include <ifaddrs.h>
165 #include <netdb.h>
166 #include <netinet/in.h>
167 #ifdef __linux__
168 #include <resolv.h>
169 #endif
170 #include <netinet/tcp.h>
171 #ifdef CPPHTTPLIB_USE_POLL
172 #include <poll.h>
173 #endif
174 #include <csignal>
175 #include <pthread.h>
176 #include <sys/select.h>
177 #include <sys/socket.h>
178 #include <unistd.h>
179
180 using socket_t = int;
181 #define INVALID_SOCKET (-1)
182 #endif //_WIN32
183
184 #include <algorithm>
185 #include <array>
186 #include <atomic>
187 #include <cassert>
188 #include <cctype>
189 #include <climits>
190 #include <condition_variable>
191 #include <errno.h>
192 #include <fcntl.h>
193 #include <fstream>
194 #include <functional>
195 #include <iostream>
196 #include <list>
197 #include <map>
198 #include <memory>
199 #include <mutex>
200 #include <random>
201 #include <regex>
202 #include <sstream>
203 #include <string>
204 #include <sys/stat.h>
205 #include <thread>
206
207 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
208 #include <openssl/err.h>
209 #include <openssl/md5.h>
210 #include <openssl/ssl.h>
211 #include <openssl/x509v3.h>
212
213 #if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
214 #include <openssl/applink.c>
215 #endif
216
217 #include <iomanip>
218 #include <iostream>
219 #include <sstream>
220
221 #if OPENSSL_VERSION_NUMBER < 0x1010100fL
222 #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
223 #endif
224
225 #if OPENSSL_VERSION_NUMBER < 0x10100000L
226 #include <openssl/crypto.h>
ASN1_STRING_get0_data(const ASN1_STRING * asn1)227 inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
228 return M_ASN1_STRING_data(asn1);
229 }
230 #endif
231 #endif
232
233 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
234 #include <zlib.h>
235 #endif
236
237 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
238 #include <brotli/decode.h>
239 #include <brotli/encode.h>
240 #endif
241
242 /*
243 * Declaration
244 */
245 namespace httplib {
246
247 namespace detail {
248
249 /*
250 * Backport std::make_unique from C++14.
251 *
252 * NOTE: This code came up with the following stackoverflow post:
253 * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
254 *
255 */
256
257 template <class T, class... Args>
258 typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args &&...args)259 make_unique(Args &&... args) {
260 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
261 }
262
263 template <class T>
264 typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(std::size_t n)265 make_unique(std::size_t n) {
266 typedef typename std::remove_extent<T>::type RT;
267 return std::unique_ptr<T>(new RT[n]);
268 }
269
270 struct ci {
operator ()httplib::detail::ci271 bool operator()(const std::string &s1, const std::string &s2) const {
272 return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),
273 s2.end(),
274 [](unsigned char c1, unsigned char c2) {
275 return ::tolower(c1) < ::tolower(c2);
276 });
277 }
278 };
279
280 } // namespace detail
281
282 using Headers = std::multimap<std::string, std::string, detail::ci>;
283
284 using Params = std::multimap<std::string, std::string>;
285 using Match = std::smatch;
286
287 using Progress = std::function<bool(uint64_t current, uint64_t total)>;
288
289 struct Response;
290 using ResponseHandler = std::function<bool(const Response &response)>;
291
292 struct MultipartFormData {
293 std::string name;
294 std::string content;
295 std::string filename;
296 std::string content_type;
297 };
298 using MultipartFormDataItems = std::vector<MultipartFormData>;
299 using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
300
301 class DataSink {
302 public:
DataSink()303 DataSink() : os(&sb_), sb_(*this) {}
304
305 DataSink(const DataSink &) = delete;
306 DataSink &operator=(const DataSink &) = delete;
307 DataSink(DataSink &&) = delete;
308 DataSink &operator=(DataSink &&) = delete;
309
310 std::function<void(const char *data, size_t data_len)> write;
311 std::function<void()> done;
312 std::function<bool()> is_writable;
313 std::ostream os;
314
315 private:
316 class data_sink_streambuf : public std::streambuf {
317 public:
data_sink_streambuf(DataSink & sink)318 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
319
320 protected:
xsputn(const char * s,std::streamsize n)321 std::streamsize xsputn(const char *s, std::streamsize n) {
322 sink_.write(s, static_cast<size_t>(n));
323 return n;
324 }
325
326 private:
327 DataSink &sink_;
328 };
329
330 data_sink_streambuf sb_;
331 };
332
333 using ContentProvider =
334 std::function<bool(size_t offset, size_t length, DataSink &sink)>;
335
336 using ContentProviderWithoutLength =
337 std::function<bool(size_t offset, DataSink &sink)>;
338
339 using ContentReceiverWithProgress =
340 std::function<bool(const char *data, size_t data_length, uint64_t offset,
341 uint64_t total_length)>;
342
343 using ContentReceiver =
344 std::function<bool(const char *data, size_t data_length)>;
345
346 using MultipartContentHeader =
347 std::function<bool(const MultipartFormData &file)>;
348
349 class ContentReader {
350 public:
351 using Reader = std::function<bool(ContentReceiver receiver)>;
352 using MultipartReader = std::function<bool(MultipartContentHeader header,
353 ContentReceiver receiver)>;
354
ContentReader(Reader reader,MultipartReader multipart_reader)355 ContentReader(Reader reader, MultipartReader multipart_reader)
356 : reader_(std::move(reader)),
357 multipart_reader_(std::move(multipart_reader)) {}
358
operator ()(MultipartContentHeader header,ContentReceiver receiver) const359 bool operator()(MultipartContentHeader header,
360 ContentReceiver receiver) const {
361 return multipart_reader_(std::move(header), std::move(receiver));
362 }
363
operator ()(ContentReceiver receiver) const364 bool operator()(ContentReceiver receiver) const {
365 return reader_(std::move(receiver));
366 }
367
368 Reader reader_;
369 MultipartReader multipart_reader_;
370 };
371
372 using Range = std::pair<ssize_t, ssize_t>;
373 using Ranges = std::vector<Range>;
374
375 struct Request {
376 std::string method;
377 std::string path;
378 Headers headers;
379 std::string body;
380
381 std::string remote_addr;
382 int remote_port = -1;
383
384 // for server
385 std::string version;
386 std::string target;
387 Params params;
388 MultipartFormDataMap files;
389 Ranges ranges;
390 Match matches;
391
392 // for client
393 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
394 const SSL *ssl;
395 #endif
396
397 bool has_header(const char *key) const;
398 std::string get_header_value(const char *key, size_t id = 0) const;
399 template <typename T>
400 T get_header_value(const char *key, size_t id = 0) const;
401 size_t get_header_value_count(const char *key) const;
402 void set_header(const char *key, const char *val);
403 void set_header(const char *key, const std::string &val);
404
405 bool has_param(const char *key) const;
406 std::string get_param_value(const char *key, size_t id = 0) const;
407 size_t get_param_value_count(const char *key) const;
408
409 bool is_multipart_form_data() const;
410
411 bool has_file(const char *key) const;
412 MultipartFormData get_file_value(const char *key) const;
413
414 // private members...
415 size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
416 ResponseHandler response_handler_;
417 ContentReceiverWithProgress content_receiver_;
418 size_t content_length_ = 0;
419 ContentProvider content_provider_;
420 bool is_chunked_content_provider_ = false;
421 Progress progress_;
422 size_t authorization_count_ = 0;
423 };
424
425 struct Response {
426 std::string version;
427 int status = -1;
428 std::string reason;
429 Headers headers;
430 std::string body;
431 std::string location; // Redirect location
432
433 bool has_header(const char *key) const;
434 std::string get_header_value(const char *key, size_t id = 0) const;
435 template <typename T>
436 T get_header_value(const char *key, size_t id = 0) const;
437 size_t get_header_value_count(const char *key) const;
438 void set_header(const char *key, const char *val);
439 void set_header(const char *key, const std::string &val);
440
441 void set_redirect(const char *url, int status = 302);
442 void set_redirect(const std::string &url, int status = 302);
443 void set_content(const char *s, size_t n, const char *content_type);
444 void set_content(const std::string &s, const char *content_type);
445
446 void set_content_provider(
447 size_t length, const char *content_type, ContentProvider provider,
448 const std::function<void()> &resource_releaser = nullptr);
449
450 void set_content_provider(
451 const char *content_type, ContentProviderWithoutLength provider,
452 const std::function<void()> &resource_releaser = nullptr);
453
454 void set_chunked_content_provider(
455 const char *content_type, ContentProviderWithoutLength provider,
456 const std::function<void()> &resource_releaser = nullptr);
457
458 Response() = default;
459 Response(const Response &) = default;
460 Response &operator=(const Response &) = default;
461 Response(Response &&) = default;
462 Response &operator=(Response &&) = default;
~Responsehttplib::Response463 ~Response() {
464 if (content_provider_resource_releaser_) {
465 content_provider_resource_releaser_();
466 }
467 }
468
469 // private members...
470 size_t content_length_ = 0;
471 ContentProvider content_provider_;
472 std::function<void()> content_provider_resource_releaser_;
473 bool is_chunked_content_provider_ = false;
474 };
475
476 class Stream {
477 public:
478 virtual ~Stream() = default;
479
480 virtual bool is_readable() const = 0;
481 virtual bool is_writable() const = 0;
482
483 virtual ssize_t read(char *ptr, size_t size) = 0;
484 virtual ssize_t write(const char *ptr, size_t size) = 0;
485 virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
486 virtual socket_t socket() const = 0;
487
488 template <typename... Args>
489 ssize_t write_format(const char *fmt, const Args &... args);
490 ssize_t write(const char *ptr);
491 ssize_t write(const std::string &s);
492 };
493
494 class TaskQueue {
495 public:
496 TaskQueue() = default;
497 virtual ~TaskQueue() = default;
498
499 virtual void enqueue(std::function<void()> fn) = 0;
500 virtual void shutdown() = 0;
501
on_idle()502 virtual void on_idle(){};
503 };
504
505 class ThreadPool : public TaskQueue {
506 public:
ThreadPool(size_t n)507 explicit ThreadPool(size_t n) : shutdown_(false) {
508 while (n) {
509 threads_.emplace_back(worker(*this));
510 n--;
511 }
512 }
513
514 ThreadPool(const ThreadPool &) = delete;
515 ~ThreadPool() override = default;
516
enqueue(std::function<void ()> fn)517 void enqueue(std::function<void()> fn) override {
518 std::unique_lock<std::mutex> lock(mutex_);
519 jobs_.push_back(std::move(fn));
520 cond_.notify_one();
521 }
522
shutdown()523 void shutdown() override {
524 // Stop all worker threads...
525 {
526 std::unique_lock<std::mutex> lock(mutex_);
527 shutdown_ = true;
528 }
529
530 cond_.notify_all();
531
532 // Join...
533 for (auto &t : threads_) {
534 t.join();
535 }
536 }
537
538 private:
539 struct worker {
workerhttplib::ThreadPool::worker540 explicit worker(ThreadPool &pool) : pool_(pool) {}
541
operator ()httplib::ThreadPool::worker542 void operator()() {
543 for (;;) {
544 std::function<void()> fn;
545 {
546 std::unique_lock<std::mutex> lock(pool_.mutex_);
547
548 pool_.cond_.wait(
549 lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
550
551 if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
552
553 fn = pool_.jobs_.front();
554 pool_.jobs_.pop_front();
555 }
556
557 assert(true == static_cast<bool>(fn));
558 fn();
559 }
560 }
561
562 ThreadPool &pool_;
563 };
564 friend struct worker;
565
566 std::vector<std::thread> threads_;
567 std::list<std::function<void()>> jobs_;
568
569 bool shutdown_;
570
571 std::condition_variable cond_;
572 std::mutex mutex_;
573 };
574
575 using Logger = std::function<void(const Request &, const Response &)>;
576
577 using SocketOptions = std::function<void(socket_t sock)>;
578
default_socket_options(socket_t sock)579 inline void default_socket_options(socket_t sock) {
580 int yes = 1;
581 #ifdef _WIN32
582 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
583 sizeof(yes));
584 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
585 reinterpret_cast<char *>(&yes), sizeof(yes));
586 #else
587 #ifdef SO_REUSEPORT
588 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes),
589 sizeof(yes));
590 #else
591 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes),
592 sizeof(yes));
593 #endif
594 #endif
595 }
596
597 class Server {
598 public:
599 using Handler = std::function<void(const Request &, Response &)>;
600 using HandlerWithContentReader = std::function<void(
601 const Request &, Response &, const ContentReader &content_reader)>;
602 using Expect100ContinueHandler =
603 std::function<int(const Request &, Response &)>;
604
605 Server();
606
607 virtual ~Server();
608
609 virtual bool is_valid() const;
610
611 Server &Get(const char *pattern, Handler handler);
612 Server &Post(const char *pattern, Handler handler);
613 Server &Post(const char *pattern, HandlerWithContentReader handler);
614 Server &Put(const char *pattern, Handler handler);
615 Server &Put(const char *pattern, HandlerWithContentReader handler);
616 Server &Patch(const char *pattern, Handler handler);
617 Server &Patch(const char *pattern, HandlerWithContentReader handler);
618 Server &Delete(const char *pattern, Handler handler);
619 Server &Delete(const char *pattern, HandlerWithContentReader handler);
620 Server &Options(const char *pattern, Handler handler);
621
622 bool set_base_dir(const char *dir, const char *mount_point = nullptr);
623 bool set_mount_point(const char *mount_point, const char *dir,
624 Headers headers = Headers());
625 bool remove_mount_point(const char *mount_point);
626 void set_file_extension_and_mimetype_mapping(const char *ext,
627 const char *mime);
628 void set_file_request_handler(Handler handler);
629
630 void set_error_handler(Handler handler);
631 void set_expect_100_continue_handler(Expect100ContinueHandler handler);
632 void set_logger(Logger logger);
633
634 void set_tcp_nodelay(bool on);
635 void set_socket_options(SocketOptions socket_options);
636
637 void set_keep_alive_max_count(size_t count);
638 void set_keep_alive_timeout(time_t sec);
639 void set_read_timeout(time_t sec, time_t usec = 0);
640 void set_write_timeout(time_t sec, time_t usec = 0);
641 void set_idle_interval(time_t sec, time_t usec = 0);
642
643 void set_payload_max_length(size_t length);
644
645 bool bind_to_port(const char *host, int port, int socket_flags = 0);
646 int bind_to_any_port(const char *host, int socket_flags = 0);
647 bool listen_after_bind();
648
649 bool listen(const char *host, int port, int socket_flags = 0);
650
651 bool is_running() const;
652 void stop();
653
654 std::function<TaskQueue *(void)> new_task_queue;
655
656 protected:
657 bool process_request(Stream &strm, bool close_connection,
658 bool &connection_closed,
659 const std::function<void(Request &)> &setup_request);
660
661 std::atomic<socket_t> svr_sock_;
662 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
663 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
664 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
665 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
666 time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
667 time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
668 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
669 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
670 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
671
672 private:
673 using Handlers = std::vector<std::pair<std::regex, Handler>>;
674 using HandlersForContentReader =
675 std::vector<std::pair<std::regex, HandlerWithContentReader>>;
676
677 socket_t create_server_socket(const char *host, int port, int socket_flags,
678 SocketOptions socket_options) const;
679 int bind_internal(const char *host, int port, int socket_flags);
680 bool listen_internal();
681
682 bool routing(Request &req, Response &res, Stream &strm);
683 bool handle_file_request(Request &req, Response &res, bool head = false);
684 bool dispatch_request(Request &req, Response &res, const Handlers &handlers);
685 bool
686 dispatch_request_for_content_reader(Request &req, Response &res,
687 ContentReader content_reader,
688 const HandlersForContentReader &handlers);
689
690 bool parse_request_line(const char *s, Request &req);
691 void apply_ranges(const Request &req, Response &res,
692 std::string &content_type, std::string &boundary);
693 bool write_response(Stream &strm, bool close_connection, const Request &req,
694 Response &res);
695 bool write_response_with_content(Stream &strm, bool close_connection,
696 const Request &req, Response &res);
697 bool write_response_core(Stream &strm, bool close_connection,
698 const Request &req, Response &res,
699 bool need_apply_ranges);
700 bool write_content_with_provider(Stream &strm, const Request &req,
701 Response &res, const std::string &boundary,
702 const std::string &content_type);
703 bool read_content(Stream &strm, Request &req, Response &res);
704 bool
705 read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
706 ContentReceiver receiver,
707 MultipartContentHeader multipart_header,
708 ContentReceiver multipart_receiver);
709 bool read_content_core(Stream &strm, Request &req, Response &res,
710 ContentReceiver receiver,
711 MultipartContentHeader mulitpart_header,
712 ContentReceiver multipart_receiver);
713
714 virtual bool process_and_close_socket(socket_t sock);
715
716 struct MountPointEntry {
717 std::string mount_point;
718 std::string base_dir;
719 Headers headers;
720 };
721 std::vector<MountPointEntry> base_dirs_;
722
723 std::atomic<bool> is_running_;
724 std::map<std::string, std::string> file_extension_and_mimetype_map_;
725 Handler file_request_handler_;
726 Handlers get_handlers_;
727 Handlers post_handlers_;
728 HandlersForContentReader post_handlers_for_content_reader_;
729 Handlers put_handlers_;
730 HandlersForContentReader put_handlers_for_content_reader_;
731 Handlers patch_handlers_;
732 HandlersForContentReader patch_handlers_for_content_reader_;
733 Handlers delete_handlers_;
734 HandlersForContentReader delete_handlers_for_content_reader_;
735 Handlers options_handlers_;
736 Handler error_handler_;
737 Logger logger_;
738 Expect100ContinueHandler expect_100_continue_handler_;
739
740 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
741 SocketOptions socket_options_ = default_socket_options;
742 };
743
744 enum Error {
745 Success = 0,
746 Unknown,
747 Connection,
748 BindIPAddress,
749 Read,
750 Write,
751 ExceedRedirectCount,
752 Canceled,
753 SSLConnection,
754 SSLLoadingCerts,
755 SSLServerVerification,
756 UnsupportedMultipartBoundaryChars,
757 Compression,
758 };
759
760 class Result {
761 public:
Result(std::unique_ptr<Response> res,Error err)762 Result(std::unique_ptr<Response> res, Error err)
763 : res_(std::move(res)), err_(err) {}
operator bool() const764 operator bool() const { return res_ != nullptr; }
operator ==(std::nullptr_t) const765 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
operator !=(std::nullptr_t) const766 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
value() const767 const Response &value() const { return *res_; }
value()768 Response &value() { return *res_; }
operator *() const769 const Response &operator*() const { return *res_; }
operator *()770 Response &operator*() { return *res_; }
operator ->() const771 const Response *operator->() const { return res_.get(); }
operator ->()772 Response *operator->() { return res_.get(); }
error() const773 Error error() const { return err_; }
774
775 private:
776 std::unique_ptr<Response> res_;
777 Error err_;
778 };
779
780 class ClientImpl {
781 public:
782 explicit ClientImpl(const std::string &host);
783
784 explicit ClientImpl(const std::string &host, int port);
785
786 explicit ClientImpl(const std::string &host, int port,
787 const std::string &client_cert_path,
788 const std::string &client_key_path);
789
790 virtual ~ClientImpl();
791
792 virtual bool is_valid() const;
793
794 Result Get(const char *path);
795 Result Get(const char *path, const Headers &headers);
796 Result Get(const char *path, Progress progress);
797 Result Get(const char *path, const Headers &headers, Progress progress);
798 Result Get(const char *path, ContentReceiver content_receiver);
799 Result Get(const char *path, const Headers &headers,
800 ContentReceiver content_receiver);
801 Result Get(const char *path, ContentReceiver content_receiver,
802 Progress progress);
803 Result Get(const char *path, const Headers &headers,
804 ContentReceiver content_receiver, Progress progress);
805 Result Get(const char *path, ResponseHandler response_handler,
806 ContentReceiver content_receiver);
807 Result Get(const char *path, const Headers &headers,
808 ResponseHandler response_handler,
809 ContentReceiver content_receiver);
810 Result Get(const char *path, ResponseHandler response_handler,
811 ContentReceiver content_receiver, Progress progress);
812 Result Get(const char *path, const Headers &headers,
813 ResponseHandler response_handler, ContentReceiver content_receiver,
814 Progress progress);
815
816 Result Head(const char *path);
817 Result Head(const char *path, const Headers &headers);
818
819 Result Post(const char *path);
820 Result Post(const char *path, const std::string &body,
821 const char *content_type);
822 Result Post(const char *path, const Headers &headers, const std::string &body,
823 const char *content_type);
824 Result Post(const char *path, size_t content_length,
825 ContentProvider content_provider, const char *content_type);
826 Result Post(const char *path, ContentProviderWithoutLength content_provider,
827 const char *content_type);
828 Result Post(const char *path, const Headers &headers, size_t content_length,
829 ContentProvider content_provider, const char *content_type);
830 Result Post(const char *path, const Headers &headers,
831 ContentProviderWithoutLength content_provider,
832 const char *content_type);
833 Result Post(const char *path, const Params ¶ms);
834 Result Post(const char *path, const Headers &headers, const Params ¶ms);
835 Result Post(const char *path, const MultipartFormDataItems &items);
836 Result Post(const char *path, const Headers &headers,
837 const MultipartFormDataItems &items);
838 Result Post(const char *path, const Headers &headers,
839 const MultipartFormDataItems &items, const std::string &boundary);
840
841 Result Put(const char *path);
842 Result Put(const char *path, const std::string &body,
843 const char *content_type);
844 Result Put(const char *path, const Headers &headers, const std::string &body,
845 const char *content_type);
846 Result Put(const char *path, size_t content_length,
847 ContentProvider content_provider, const char *content_type);
848 Result Put(const char *path, ContentProviderWithoutLength content_provider,
849 const char *content_type);
850 Result Put(const char *path, const Headers &headers, size_t content_length,
851 ContentProvider content_provider, const char *content_type);
852 Result Put(const char *path, const Headers &headers,
853 ContentProviderWithoutLength content_provider,
854 const char *content_type);
855 Result Put(const char *path, const Params ¶ms);
856 Result Put(const char *path, const Headers &headers, const Params ¶ms);
857
858 Result Patch(const char *path, const std::string &body,
859 const char *content_type);
860 Result Patch(const char *path, const Headers &headers,
861 const std::string &body, const char *content_type);
862 Result Patch(const char *path, size_t content_length,
863 ContentProvider content_provider, const char *content_type);
864 Result Patch(const char *path, ContentProviderWithoutLength content_provider,
865 const char *content_type);
866 Result Patch(const char *path, const Headers &headers, size_t content_length,
867 ContentProvider content_provider, const char *content_type);
868 Result Patch(const char *path, const Headers &headers,
869 ContentProviderWithoutLength content_provider,
870 const char *content_type);
871
872 Result Delete(const char *path);
873 Result Delete(const char *path, const std::string &body,
874 const char *content_type);
875 Result Delete(const char *path, const Headers &headers);
876 Result Delete(const char *path, const Headers &headers,
877 const std::string &body, const char *content_type);
878
879 Result Options(const char *path);
880 Result Options(const char *path, const Headers &headers);
881
882 bool send(const Request &req, Response &res, Error &error);
883 Result send(const Request &req);
884
885 size_t is_socket_open() const;
886
887 void stop();
888
889 void set_default_headers(Headers headers);
890
891 void set_tcp_nodelay(bool on);
892 void set_socket_options(SocketOptions socket_options);
893
894 void set_connection_timeout(time_t sec, time_t usec = 0);
895 void set_read_timeout(time_t sec, time_t usec = 0);
896 void set_write_timeout(time_t sec, time_t usec = 0);
897
898 void set_basic_auth(const char *username, const char *password);
899 void set_bearer_token_auth(const char *token);
900 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
901 void set_digest_auth(const char *username, const char *password);
902 #endif
903
904 void set_keep_alive(bool on);
905 void set_follow_location(bool on);
906
907 void set_compress(bool on);
908
909 void set_decompress(bool on);
910
911 void set_interface(const char *intf);
912
913 void set_proxy(const char *host, int port);
914 void set_proxy_basic_auth(const char *username, const char *password);
915 void set_proxy_bearer_token_auth(const char *token);
916 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
917 void set_proxy_digest_auth(const char *username, const char *password);
918 #endif
919
920 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
921 void enable_server_certificate_verification(bool enabled);
922 #endif
923
924 void set_logger(Logger logger);
925
926 protected:
927 struct Socket {
928 socket_t sock = INVALID_SOCKET;
929 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
930 SSL *ssl = nullptr;
931 #endif
932
is_openhttplib::ClientImpl::Socket933 bool is_open() const { return sock != INVALID_SOCKET; }
934 };
935
936 virtual bool create_and_connect_socket(Socket &socket, Error &error);
937
938 // All of:
939 // shutdown_ssl
940 // shutdown_socket
941 // close_socket
942 // should ONLY be called when socket_mutex_ is locked.
943 // Also, shutdown_ssl and close_socket should also NOT be called concurrently
944 // with a DIFFERENT thread sending requests using that socket.
945 virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
946 void shutdown_socket(Socket &socket);
947 void close_socket(Socket &socket);
948
949 // Similar to shutdown_ssl and close_socket, this should NOT be called
950 // concurrently with a DIFFERENT thread sending requests from the socket
951 void lock_socket_and_shutdown_and_close();
952
953 bool process_request(Stream &strm, const Request &req, Response &res,
954 bool close_connection, Error &error);
955
956 bool write_content_with_provider(Stream &strm, const Request &req,
957 Error &error);
958
959 void copy_settings(const ClientImpl &rhs);
960
961 // Socket endoint information
962 const std::string host_;
963 const int port_;
964 const std::string host_and_port_;
965
966 // Current open socket
967 Socket socket_;
968 mutable std::mutex socket_mutex_;
969 std::recursive_mutex request_mutex_;
970
971 // These are all protected under socket_mutex
972 size_t socket_requests_in_flight_ = 0;
973 std::thread::id socket_requests_are_from_thread_ = std::thread::id();
974 bool socket_should_be_closed_when_request_is_done_ = false;
975
976 // Default headers
977 Headers default_headers_;
978
979 // Settings
980 std::string client_cert_path_;
981 std::string client_key_path_;
982
983 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
984 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
985 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
986 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
987 time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
988 time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
989
990 std::string basic_auth_username_;
991 std::string basic_auth_password_;
992 std::string bearer_token_auth_token_;
993 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
994 std::string digest_auth_username_;
995 std::string digest_auth_password_;
996 #endif
997
998 bool keep_alive_ = false;
999 bool follow_location_ = false;
1000
1001 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1002 SocketOptions socket_options_ = nullptr;
1003
1004 bool compress_ = false;
1005 bool decompress_ = true;
1006
1007 std::string interface_;
1008
1009 std::string proxy_host_;
1010 int proxy_port_ = -1;
1011
1012 std::string proxy_basic_auth_username_;
1013 std::string proxy_basic_auth_password_;
1014 std::string proxy_bearer_token_auth_token_;
1015 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1016 std::string proxy_digest_auth_username_;
1017 std::string proxy_digest_auth_password_;
1018 #endif
1019
1020 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1021 bool server_certificate_verification_ = true;
1022 #endif
1023
1024 Logger logger_;
1025
1026 private:
1027 socket_t create_client_socket(Error &error) const;
1028 bool read_response_line(Stream &strm, Response &res);
1029 bool write_request(Stream &strm, const Request &req, bool close_connection,
1030 Error &error);
1031 bool redirect(const Request &req, Response &res, Error &error);
1032 bool handle_request(Stream &strm, const Request &req, Response &res,
1033 bool close_connection, Error &error);
1034 std::unique_ptr<Response> send_with_content_provider(
1035 const char *method, const char *path, const Headers &headers,
1036 const std::string &body, size_t content_length,
1037 ContentProvider content_provider,
1038 ContentProviderWithoutLength content_provider_without_length,
1039 const char *content_type, Error &error);
1040 Result send_with_content_provider(
1041 const char *method, const char *path, const Headers &headers,
1042 const std::string &body, size_t content_length,
1043 ContentProvider content_provider,
1044 ContentProviderWithoutLength content_provider_without_length,
1045 const char *content_type);
1046
1047 virtual bool process_socket(const Socket &socket,
1048 std::function<bool(Stream &strm)> callback);
1049 virtual bool is_ssl() const;
1050 };
1051
1052 class Client {
1053 public:
1054 // Universal interface
1055 explicit Client(const char *scheme_host_port);
1056
1057 explicit Client(const char *scheme_host_port,
1058 const std::string &client_cert_path,
1059 const std::string &client_key_path);
1060
1061 // HTTP only interface
1062 explicit Client(const std::string &host, int port);
1063
1064 explicit Client(const std::string &host, int port,
1065 const std::string &client_cert_path,
1066 const std::string &client_key_path);
1067
1068 ~Client();
1069
1070 bool is_valid() const;
1071
1072 Result Get(const char *path);
1073 Result Get(const char *path, const Headers &headers);
1074 Result Get(const char *path, Progress progress);
1075 Result Get(const char *path, const Headers &headers, Progress progress);
1076 Result Get(const char *path, ContentReceiver content_receiver);
1077 Result Get(const char *path, const Headers &headers,
1078 ContentReceiver content_receiver);
1079 Result Get(const char *path, ContentReceiver content_receiver,
1080 Progress progress);
1081 Result Get(const char *path, const Headers &headers,
1082 ContentReceiver content_receiver, Progress progress);
1083 Result Get(const char *path, ResponseHandler response_handler,
1084 ContentReceiver content_receiver);
1085 Result Get(const char *path, const Headers &headers,
1086 ResponseHandler response_handler,
1087 ContentReceiver content_receiver);
1088 Result Get(const char *path, const Headers &headers,
1089 ResponseHandler response_handler, ContentReceiver content_receiver,
1090 Progress progress);
1091 Result Get(const char *path, ResponseHandler response_handler,
1092 ContentReceiver content_receiver, Progress progress);
1093
1094 Result Head(const char *path);
1095 Result Head(const char *path, const Headers &headers);
1096
1097 Result Post(const char *path);
1098 Result Post(const char *path, const std::string &body,
1099 const char *content_type);
1100 Result Post(const char *path, const Headers &headers, const std::string &body,
1101 const char *content_type);
1102 Result Post(const char *path, size_t content_length,
1103 ContentProvider content_provider, const char *content_type);
1104 Result Post(const char *path, ContentProviderWithoutLength content_provider,
1105 const char *content_type);
1106 Result Post(const char *path, const Headers &headers, size_t content_length,
1107 ContentProvider content_provider, const char *content_type);
1108 Result Post(const char *path, const Headers &headers,
1109 ContentProviderWithoutLength content_provider,
1110 const char *content_type);
1111 Result Post(const char *path, const Params ¶ms);
1112 Result Post(const char *path, const Headers &headers, const Params ¶ms);
1113 Result Post(const char *path, const MultipartFormDataItems &items);
1114 Result Post(const char *path, const Headers &headers,
1115 const MultipartFormDataItems &items);
1116 Result Post(const char *path, const Headers &headers,
1117 const MultipartFormDataItems &items, const std::string &boundary);
1118 Result Put(const char *path);
1119 Result Put(const char *path, const std::string &body,
1120 const char *content_type);
1121 Result Put(const char *path, const Headers &headers, const std::string &body,
1122 const char *content_type);
1123 Result Put(const char *path, size_t content_length,
1124 ContentProvider content_provider, const char *content_type);
1125 Result Put(const char *path, ContentProviderWithoutLength content_provider,
1126 const char *content_type);
1127 Result Put(const char *path, const Headers &headers, size_t content_length,
1128 ContentProvider content_provider, const char *content_type);
1129 Result Put(const char *path, const Headers &headers,
1130 ContentProviderWithoutLength content_provider,
1131 const char *content_type);
1132 Result Put(const char *path, const Params ¶ms);
1133 Result Put(const char *path, const Headers &headers, const Params ¶ms);
1134 Result Patch(const char *path, const std::string &body,
1135 const char *content_type);
1136 Result Patch(const char *path, const Headers &headers,
1137 const std::string &body, const char *content_type);
1138 Result Patch(const char *path, size_t content_length,
1139 ContentProvider content_provider, const char *content_type);
1140 Result Patch(const char *path, ContentProviderWithoutLength content_provider,
1141 const char *content_type);
1142 Result Patch(const char *path, const Headers &headers, size_t content_length,
1143 ContentProvider content_provider, const char *content_type);
1144 Result Patch(const char *path, const Headers &headers,
1145 ContentProviderWithoutLength content_provider,
1146 const char *content_type);
1147
1148 Result Delete(const char *path);
1149 Result Delete(const char *path, const std::string &body,
1150 const char *content_type);
1151 Result Delete(const char *path, const Headers &headers);
1152 Result Delete(const char *path, const Headers &headers,
1153 const std::string &body, const char *content_type);
1154
1155 Result Options(const char *path);
1156 Result Options(const char *path, const Headers &headers);
1157
1158 bool send(const Request &req, Response &res, Error &error);
1159 Result send(const Request &req);
1160
1161 size_t is_socket_open() const;
1162
1163 void stop();
1164
1165 void set_default_headers(Headers headers);
1166
1167 void set_tcp_nodelay(bool on);
1168 void set_socket_options(SocketOptions socket_options);
1169
1170 void set_connection_timeout(time_t sec, time_t usec = 0);
1171 void set_read_timeout(time_t sec, time_t usec = 0);
1172 void set_write_timeout(time_t sec, time_t usec = 0);
1173
1174 void set_basic_auth(const char *username, const char *password);
1175 void set_bearer_token_auth(const char *token);
1176 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1177 void set_digest_auth(const char *username, const char *password);
1178 #endif
1179
1180 void set_keep_alive(bool on);
1181 void set_follow_location(bool on);
1182
1183 void set_compress(bool on);
1184
1185 void set_decompress(bool on);
1186
1187 void set_interface(const char *intf);
1188
1189 void set_proxy(const char *host, int port);
1190 void set_proxy_basic_auth(const char *username, const char *password);
1191 void set_proxy_bearer_token_auth(const char *token);
1192 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1193 void set_proxy_digest_auth(const char *username, const char *password);
1194 #endif
1195
1196 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1197 void enable_server_certificate_verification(bool enabled);
1198 #endif
1199
1200 void set_logger(Logger logger);
1201
1202 // SSL
1203 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1204 void set_ca_cert_path(const char *ca_cert_file_path,
1205 const char *ca_cert_dir_path = nullptr);
1206
1207 void set_ca_cert_store(X509_STORE *ca_cert_store);
1208
1209 long get_openssl_verify_result() const;
1210
1211 SSL_CTX *ssl_context() const;
1212 #endif
1213
1214 private:
1215 std::unique_ptr<ClientImpl> cli_;
1216
1217 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1218 bool is_ssl_ = false;
1219 #endif
1220 };
1221
1222 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1223 class SSLServer : public Server {
1224 public:
1225 SSLServer(const char *cert_path, const char *private_key_path,
1226 const char *client_ca_cert_file_path = nullptr,
1227 const char *client_ca_cert_dir_path = nullptr);
1228
1229 SSLServer(X509 *cert, EVP_PKEY *private_key,
1230 X509_STORE *client_ca_cert_store = nullptr);
1231
1232 ~SSLServer() override;
1233
1234 bool is_valid() const override;
1235
1236 private:
1237 bool process_and_close_socket(socket_t sock) override;
1238
1239 SSL_CTX *ctx_;
1240 std::mutex ctx_mutex_;
1241 };
1242
1243 class SSLClient : public ClientImpl {
1244 public:
1245 explicit SSLClient(const std::string &host);
1246
1247 explicit SSLClient(const std::string &host, int port);
1248
1249 explicit SSLClient(const std::string &host, int port,
1250 const std::string &client_cert_path,
1251 const std::string &client_key_path);
1252
1253 explicit SSLClient(const std::string &host, int port, X509 *client_cert,
1254 EVP_PKEY *client_key);
1255
1256 ~SSLClient() override;
1257
1258 bool is_valid() const override;
1259
1260 void set_ca_cert_path(const char *ca_cert_file_path,
1261 const char *ca_cert_dir_path = nullptr);
1262
1263 void set_ca_cert_store(X509_STORE *ca_cert_store);
1264
1265 long get_openssl_verify_result() const;
1266
1267 SSL_CTX *ssl_context() const;
1268
1269 private:
1270 bool create_and_connect_socket(Socket &socket, Error &error) override;
1271 void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1272
1273 bool process_socket(const Socket &socket,
1274 std::function<bool(Stream &strm)> callback) override;
1275 bool is_ssl() const override;
1276
1277 bool connect_with_proxy(Socket &sock, Response &res, bool &success,
1278 Error &error);
1279 bool initialize_ssl(Socket &socket, Error &error);
1280
1281 bool load_certs();
1282
1283 bool verify_host(X509 *server_cert) const;
1284 bool verify_host_with_subject_alt_name(X509 *server_cert) const;
1285 bool verify_host_with_common_name(X509 *server_cert) const;
1286 bool check_host_name(const char *pattern, size_t pattern_len) const;
1287
1288 SSL_CTX *ctx_;
1289 std::mutex ctx_mutex_;
1290 std::once_flag initialize_cert_;
1291
1292 std::vector<std::string> host_components_;
1293
1294 std::string ca_cert_file_path_;
1295 std::string ca_cert_dir_path_;
1296 long verify_result_ = 0;
1297
1298 friend class ClientImpl;
1299 };
1300 #endif
1301
1302 // ----------------------------------------------------------------------------
1303
1304 /*
1305 * Implementation
1306 */
1307
1308 namespace detail {
1309
is_hex(char c,int & v)1310 inline bool is_hex(char c, int &v) {
1311 if (0x20 <= c && isdigit(c)) {
1312 v = c - '0';
1313 return true;
1314 } else if ('A' <= c && c <= 'F') {
1315 v = c - 'A' + 10;
1316 return true;
1317 } else if ('a' <= c && c <= 'f') {
1318 v = c - 'a' + 10;
1319 return true;
1320 }
1321 return false;
1322 }
1323
from_hex_to_i(const std::string & s,size_t i,size_t cnt,int & val)1324 inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
1325 int &val) {
1326 if (i >= s.size()) { return false; }
1327
1328 val = 0;
1329 for (; cnt; i++, cnt--) {
1330 if (!s[i]) { return false; }
1331 int v = 0;
1332 if (is_hex(s[i], v)) {
1333 val = val * 16 + v;
1334 } else {
1335 return false;
1336 }
1337 }
1338 return true;
1339 }
1340
from_i_to_hex(size_t n)1341 inline std::string from_i_to_hex(size_t n) {
1342 const char *charset = "0123456789abcdef";
1343 std::string ret;
1344 do {
1345 ret = charset[n & 15] + ret;
1346 n >>= 4;
1347 } while (n > 0);
1348 return ret;
1349 }
1350
to_utf8(int code,char * buff)1351 inline size_t to_utf8(int code, char *buff) {
1352 if (code < 0x0080) {
1353 buff[0] = (code & 0x7F);
1354 return 1;
1355 } else if (code < 0x0800) {
1356 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
1357 buff[1] = static_cast<char>(0x80 | (code & 0x3F));
1358 return 2;
1359 } else if (code < 0xD800) {
1360 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
1361 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
1362 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
1363 return 3;
1364 } else if (code < 0xE000) { // D800 - DFFF is invalid...
1365 return 0;
1366 } else if (code < 0x10000) {
1367 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
1368 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
1369 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
1370 return 3;
1371 } else if (code < 0x110000) {
1372 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
1373 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
1374 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
1375 buff[3] = static_cast<char>(0x80 | (code & 0x3F));
1376 return 4;
1377 }
1378
1379 // NOTREACHED
1380 return 0;
1381 }
1382
1383 // NOTE: This code came up with the following stackoverflow post:
1384 // https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
base64_encode(const std::string & in)1385 inline std::string base64_encode(const std::string &in) {
1386 static const auto lookup =
1387 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1388
1389 std::string out;
1390 out.reserve(in.size());
1391
1392 int val = 0;
1393 int valb = -6;
1394
1395 for (auto c : in) {
1396 val = (val << 8) + static_cast<uint8_t>(c);
1397 valb += 8;
1398 while (valb >= 0) {
1399 out.push_back(lookup[(val >> valb) & 0x3F]);
1400 valb -= 6;
1401 }
1402 }
1403
1404 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
1405
1406 while (out.size() % 4) {
1407 out.push_back('=');
1408 }
1409
1410 return out;
1411 }
1412
is_file(const std::string & path)1413 inline bool is_file(const std::string &path) {
1414 struct stat st;
1415 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
1416 }
1417
is_dir(const std::string & path)1418 inline bool is_dir(const std::string &path) {
1419 struct stat st;
1420 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
1421 }
1422
is_valid_path(const std::string & path)1423 inline bool is_valid_path(const std::string &path) {
1424 size_t level = 0;
1425 size_t i = 0;
1426
1427 // Skip slash
1428 while (i < path.size() && path[i] == '/') {
1429 i++;
1430 }
1431
1432 while (i < path.size()) {
1433 // Read component
1434 auto beg = i;
1435 while (i < path.size() && path[i] != '/') {
1436 i++;
1437 }
1438
1439 auto len = i - beg;
1440 assert(len > 0);
1441
1442 if (!path.compare(beg, len, ".")) {
1443 ;
1444 } else if (!path.compare(beg, len, "..")) {
1445 if (level == 0) { return false; }
1446 level--;
1447 } else {
1448 level++;
1449 }
1450
1451 // Skip slash
1452 while (i < path.size() && path[i] == '/') {
1453 i++;
1454 }
1455 }
1456
1457 return true;
1458 }
1459
encode_url(const std::string & s)1460 inline std::string encode_url(const std::string &s) {
1461 std::string result;
1462
1463 for (size_t i = 0; s[i]; i++) {
1464 switch (s[i]) {
1465 case ' ': result += "%20"; break;
1466 case '+': result += "%2B"; break;
1467 case '\r': result += "%0D"; break;
1468 case '\n': result += "%0A"; break;
1469 case '\'': result += "%27"; break;
1470 case ',': result += "%2C"; break;
1471 // case ':': result += "%3A"; break; // ok? probably...
1472 case ';': result += "%3B"; break;
1473 default:
1474 auto c = static_cast<uint8_t>(s[i]);
1475 if (c >= 0x80) {
1476 result += '%';
1477 char hex[4];
1478 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
1479 assert(len == 2);
1480 result.append(hex, static_cast<size_t>(len));
1481 } else {
1482 result += s[i];
1483 }
1484 break;
1485 }
1486 }
1487
1488 return result;
1489 }
1490
decode_url(const std::string & s,bool convert_plus_to_space)1491 inline std::string decode_url(const std::string &s,
1492 bool convert_plus_to_space) {
1493 std::string result;
1494
1495 for (size_t i = 0; i < s.size(); i++) {
1496 if (s[i] == '%' && i + 1 < s.size()) {
1497 if (s[i + 1] == 'u') {
1498 int val = 0;
1499 if (from_hex_to_i(s, i + 2, 4, val)) {
1500 // 4 digits Unicode codes
1501 char buff[4];
1502 size_t len = to_utf8(val, buff);
1503 if (len > 0) { result.append(buff, len); }
1504 i += 5; // 'u0000'
1505 } else {
1506 result += s[i];
1507 }
1508 } else {
1509 int val = 0;
1510 if (from_hex_to_i(s, i + 1, 2, val)) {
1511 // 2 digits hex codes
1512 result += static_cast<char>(val);
1513 i += 2; // '00'
1514 } else {
1515 result += s[i];
1516 }
1517 }
1518 } else if (convert_plus_to_space && s[i] == '+') {
1519 result += ' ';
1520 } else {
1521 result += s[i];
1522 }
1523 }
1524
1525 return result;
1526 }
1527
read_file(const std::string & path,std::string & out)1528 inline void read_file(const std::string &path, std::string &out) {
1529 std::ifstream fs(path, std::ios_base::binary);
1530 fs.seekg(0, std::ios_base::end);
1531 auto size = fs.tellg();
1532 fs.seekg(0);
1533 out.resize(static_cast<size_t>(size));
1534 fs.read(&out[0], static_cast<std::streamsize>(size));
1535 }
1536
file_extension(const std::string & path)1537 inline std::string file_extension(const std::string &path) {
1538 std::smatch m;
1539 static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
1540 if (std::regex_search(path, m, re)) { return m[1].str(); }
1541 return std::string();
1542 }
1543
is_space_or_tab(char c)1544 inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
1545
trim(const char * b,const char * e,size_t left,size_t right)1546 inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
1547 size_t right) {
1548 while (b + left < e && is_space_or_tab(b[left])) {
1549 left++;
1550 }
1551 while (right > 0 && is_space_or_tab(b[right - 1])) {
1552 right--;
1553 }
1554 return std::make_pair(left, right);
1555 }
1556
trim_copy(const std::string & s)1557 inline std::string trim_copy(const std::string &s) {
1558 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
1559 return s.substr(r.first, r.second - r.first);
1560 }
1561
split(const char * b,const char * e,char d,Fn fn)1562 template <class Fn> void split(const char *b, const char *e, char d, Fn fn) {
1563 size_t i = 0;
1564 size_t beg = 0;
1565
1566 while (e ? (b + i < e) : (b[i] != '\0')) {
1567 if (b[i] == d) {
1568 auto r = trim(b, e, beg, i);
1569 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
1570 beg = i + 1;
1571 }
1572 i++;
1573 }
1574
1575 if (i) {
1576 auto r = trim(b, e, beg, i);
1577 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
1578 }
1579 }
1580
1581 // NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
1582 // to store data. The call can set memory on stack for performance.
1583 class stream_line_reader {
1584 public:
stream_line_reader(Stream & strm,char * fixed_buffer,size_t fixed_buffer_size)1585 stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size)
1586 : strm_(strm), fixed_buffer_(fixed_buffer),
1587 fixed_buffer_size_(fixed_buffer_size) {}
1588
ptr() const1589 const char *ptr() const {
1590 if (glowable_buffer_.empty()) {
1591 return fixed_buffer_;
1592 } else {
1593 return glowable_buffer_.data();
1594 }
1595 }
1596
size() const1597 size_t size() const {
1598 if (glowable_buffer_.empty()) {
1599 return fixed_buffer_used_size_;
1600 } else {
1601 return glowable_buffer_.size();
1602 }
1603 }
1604
end_with_crlf() const1605 bool end_with_crlf() const {
1606 auto end = ptr() + size();
1607 return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
1608 }
1609
getline()1610 bool getline() {
1611 fixed_buffer_used_size_ = 0;
1612 glowable_buffer_.clear();
1613
1614 for (size_t i = 0;; i++) {
1615 char byte;
1616 auto n = strm_.read(&byte, 1);
1617
1618 if (n < 0) {
1619 return false;
1620 } else if (n == 0) {
1621 if (i == 0) {
1622 return false;
1623 } else {
1624 break;
1625 }
1626 }
1627
1628 append(byte);
1629
1630 if (byte == '\n') { break; }
1631 }
1632
1633 return true;
1634 }
1635
1636 private:
append(char c)1637 void append(char c) {
1638 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
1639 fixed_buffer_[fixed_buffer_used_size_++] = c;
1640 fixed_buffer_[fixed_buffer_used_size_] = '\0';
1641 } else {
1642 if (glowable_buffer_.empty()) {
1643 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
1644 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
1645 }
1646 glowable_buffer_ += c;
1647 }
1648 }
1649
1650 Stream &strm_;
1651 char *fixed_buffer_;
1652 const size_t fixed_buffer_size_;
1653 size_t fixed_buffer_used_size_ = 0;
1654 std::string glowable_buffer_;
1655 };
1656
close_socket(socket_t sock)1657 inline int close_socket(socket_t sock) {
1658 #ifdef _WIN32
1659 return closesocket(sock);
1660 #else
1661 return close(sock);
1662 #endif
1663 }
1664
handle_EINTR(T fn)1665 template <typename T> inline ssize_t handle_EINTR(T fn) {
1666 ssize_t res = false;
1667 while (true) {
1668 res = fn();
1669 if (res < 0 && errno == EINTR) { continue; }
1670 break;
1671 }
1672 return res;
1673 }
1674
select_read(socket_t sock,time_t sec,time_t usec)1675 inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
1676 #ifdef CPPHTTPLIB_USE_POLL
1677 struct pollfd pfd_read;
1678 pfd_read.fd = sock;
1679 pfd_read.events = POLLIN;
1680
1681 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1682
1683 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
1684 #else
1685 #ifndef _WIN32
1686 if (sock >= FD_SETSIZE) { return 1; }
1687 #endif
1688
1689 fd_set fds;
1690 FD_ZERO(&fds);
1691 FD_SET(sock, &fds);
1692
1693 timeval tv;
1694 tv.tv_sec = static_cast<long>(sec);
1695 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
1696
1697 return handle_EINTR([&]() {
1698 return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
1699 });
1700 #endif
1701 }
1702
select_write(socket_t sock,time_t sec,time_t usec)1703 inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
1704 #ifdef CPPHTTPLIB_USE_POLL
1705 struct pollfd pfd_read;
1706 pfd_read.fd = sock;
1707 pfd_read.events = POLLOUT;
1708
1709 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1710
1711 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
1712 #else
1713 #ifndef _WIN32
1714 if (sock >= FD_SETSIZE) { return 1; }
1715 #endif
1716
1717 fd_set fds;
1718 FD_ZERO(&fds);
1719 FD_SET(sock, &fds);
1720
1721 timeval tv;
1722 tv.tv_sec = static_cast<long>(sec);
1723 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
1724
1725 return handle_EINTR([&]() {
1726 return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
1727 });
1728 #endif
1729 }
1730
wait_until_socket_is_ready(socket_t sock,time_t sec,time_t usec)1731 inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1732 #ifdef CPPHTTPLIB_USE_POLL
1733 struct pollfd pfd_read;
1734 pfd_read.fd = sock;
1735 pfd_read.events = POLLIN | POLLOUT;
1736
1737 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1738
1739 auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
1740
1741 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
1742 int error = 0;
1743 socklen_t len = sizeof(error);
1744 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
1745 reinterpret_cast<char *>(&error), &len);
1746 return res >= 0 && !error;
1747 }
1748 return false;
1749 #else
1750 #ifndef _WIN32
1751 if (sock >= FD_SETSIZE) { return false; }
1752 #endif
1753
1754 fd_set fdsr;
1755 FD_ZERO(&fdsr);
1756 FD_SET(sock, &fdsr);
1757
1758 auto fdsw = fdsr;
1759 auto fdse = fdsr;
1760
1761 timeval tv;
1762 tv.tv_sec = static_cast<long>(sec);
1763 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
1764
1765 auto ret = handle_EINTR([&]() {
1766 return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
1767 });
1768
1769 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
1770 int error = 0;
1771 socklen_t len = sizeof(error);
1772 return getsockopt(sock, SOL_SOCKET, SO_ERROR,
1773 reinterpret_cast<char *>(&error), &len) >= 0 &&
1774 !error;
1775 }
1776 return false;
1777 #endif
1778 }
1779
1780 class SocketStream : public Stream {
1781 public:
1782 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
1783 time_t write_timeout_sec, time_t write_timeout_usec);
1784 ~SocketStream() override;
1785
1786 bool is_readable() const override;
1787 bool is_writable() const override;
1788 ssize_t read(char *ptr, size_t size) override;
1789 ssize_t write(const char *ptr, size_t size) override;
1790 void get_remote_ip_and_port(std::string &ip, int &port) const override;
1791 socket_t socket() const override;
1792
1793 private:
1794 socket_t sock_;
1795 time_t read_timeout_sec_;
1796 time_t read_timeout_usec_;
1797 time_t write_timeout_sec_;
1798 time_t write_timeout_usec_;
1799 };
1800
1801 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1802 class SSLSocketStream : public Stream {
1803 public:
1804 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
1805 time_t read_timeout_usec, time_t write_timeout_sec,
1806 time_t write_timeout_usec);
1807 ~SSLSocketStream() override;
1808
1809 bool is_readable() const override;
1810 bool is_writable() const override;
1811 ssize_t read(char *ptr, size_t size) override;
1812 ssize_t write(const char *ptr, size_t size) override;
1813 void get_remote_ip_and_port(std::string &ip, int &port) const override;
1814 socket_t socket() const override;
1815
1816 private:
1817 socket_t sock_;
1818 SSL *ssl_;
1819 time_t read_timeout_sec_;
1820 time_t read_timeout_usec_;
1821 time_t write_timeout_sec_;
1822 time_t write_timeout_usec_;
1823 };
1824 #endif
1825
1826 class BufferStream : public Stream {
1827 public:
1828 BufferStream() = default;
1829 ~BufferStream() override = default;
1830
1831 bool is_readable() const override;
1832 bool is_writable() const override;
1833 ssize_t read(char *ptr, size_t size) override;
1834 ssize_t write(const char *ptr, size_t size) override;
1835 void get_remote_ip_and_port(std::string &ip, int &port) const override;
1836 socket_t socket() const override;
1837
1838 const std::string &get_buffer() const;
1839
1840 private:
1841 std::string buffer;
1842 size_t position = 0;
1843 };
1844
keep_alive(socket_t sock,time_t keep_alive_timeout_sec)1845 inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
1846 using namespace std::chrono;
1847 auto start = steady_clock::now();
1848 while (true) {
1849 auto val = select_read(sock, 0, 10000);
1850 if (val < 0) {
1851 return false;
1852 } else if (val == 0) {
1853 auto current = steady_clock::now();
1854 auto duration = duration_cast<milliseconds>(current - start);
1855 auto timeout = keep_alive_timeout_sec * 1000;
1856 if (duration.count() > timeout) { return false; }
1857 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1858 } else {
1859 return true;
1860 }
1861 }
1862 }
1863
1864 template <typename T>
1865 inline bool
process_server_socket_core(socket_t sock,size_t keep_alive_max_count,time_t keep_alive_timeout_sec,T callback)1866 process_server_socket_core(socket_t sock, size_t keep_alive_max_count,
1867 time_t keep_alive_timeout_sec, T callback) {
1868 assert(keep_alive_max_count > 0);
1869 auto ret = false;
1870 auto count = keep_alive_max_count;
1871 while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) {
1872 auto close_connection = count == 1;
1873 auto connection_closed = false;
1874 ret = callback(close_connection, connection_closed);
1875 if (!ret || connection_closed) { break; }
1876 count--;
1877 }
1878 return ret;
1879 }
1880
1881 template <typename T>
1882 inline bool
process_server_socket(socket_t sock,size_t keep_alive_max_count,time_t keep_alive_timeout_sec,time_t read_timeout_sec,time_t read_timeout_usec,time_t write_timeout_sec,time_t write_timeout_usec,T callback)1883 process_server_socket(socket_t sock, size_t keep_alive_max_count,
1884 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
1885 time_t read_timeout_usec, time_t write_timeout_sec,
1886 time_t write_timeout_usec, T callback) {
1887 return process_server_socket_core(
1888 sock, keep_alive_max_count, keep_alive_timeout_sec,
1889 [&](bool close_connection, bool &connection_closed) {
1890 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
1891 write_timeout_sec, write_timeout_usec);
1892 return callback(strm, close_connection, connection_closed);
1893 });
1894 }
1895
1896 template <typename T>
process_client_socket(socket_t sock,time_t read_timeout_sec,time_t read_timeout_usec,time_t write_timeout_sec,time_t write_timeout_usec,T callback)1897 inline bool process_client_socket(socket_t sock, time_t read_timeout_sec,
1898 time_t read_timeout_usec,
1899 time_t write_timeout_sec,
1900 time_t write_timeout_usec, T callback) {
1901 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
1902 write_timeout_sec, write_timeout_usec);
1903 return callback(strm);
1904 }
1905
shutdown_socket(socket_t sock)1906 inline int shutdown_socket(socket_t sock) {
1907 #ifdef _WIN32
1908 return shutdown(sock, SD_BOTH);
1909 #else
1910 return shutdown(sock, SHUT_RDWR);
1911 #endif
1912 }
1913
1914 template <typename BindOrConnect>
create_socket(const char * host,int port,int socket_flags,bool tcp_nodelay,SocketOptions socket_options,BindOrConnect bind_or_connect)1915 socket_t create_socket(const char *host, int port, int socket_flags,
1916 bool tcp_nodelay, SocketOptions socket_options,
1917 BindOrConnect bind_or_connect) {
1918 // Get address info
1919 struct addrinfo hints;
1920 struct addrinfo *result;
1921
1922 memset(&hints, 0, sizeof(struct addrinfo));
1923 hints.ai_family = AF_UNSPEC;
1924 hints.ai_socktype = SOCK_STREAM;
1925 hints.ai_flags = socket_flags;
1926 hints.ai_protocol = 0;
1927
1928 auto service = std::to_string(port);
1929
1930 if (getaddrinfo(host, service.c_str(), &hints, &result)) {
1931 #ifdef __linux__
1932 res_init();
1933 #endif
1934 return INVALID_SOCKET;
1935 }
1936
1937 for (auto rp = result; rp; rp = rp->ai_next) {
1938 // Create a socket
1939 #ifdef _WIN32
1940 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
1941 nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);
1942 /**
1943 * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
1944 * and above the socket creation fails on older Windows Systems.
1945 *
1946 * Let's try to create a socket the old way in this case.
1947 *
1948 * Reference:
1949 * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
1950 *
1951 * WSA_FLAG_NO_HANDLE_INHERIT:
1952 * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
1953 * SP1, and later
1954 *
1955 */
1956 if (sock == INVALID_SOCKET) {
1957 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1958 }
1959 #else
1960 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1961 #endif
1962 if (sock == INVALID_SOCKET) { continue; }
1963
1964 #ifndef _WIN32
1965 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
1966 #endif
1967
1968 if (tcp_nodelay) {
1969 int yes = 1;
1970 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes),
1971 sizeof(yes));
1972 }
1973
1974 if (socket_options) { socket_options(sock); }
1975
1976 if (rp->ai_family == AF_INET6) {
1977 int no = 0;
1978 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no),
1979 sizeof(no));
1980 }
1981
1982 // bind or connect
1983 if (bind_or_connect(sock, *rp)) {
1984 freeaddrinfo(result);
1985 return sock;
1986 }
1987
1988 close_socket(sock);
1989 }
1990
1991 freeaddrinfo(result);
1992 return INVALID_SOCKET;
1993 }
1994
set_nonblocking(socket_t sock,bool nonblocking)1995 inline void set_nonblocking(socket_t sock, bool nonblocking) {
1996 #ifdef _WIN32
1997 auto flags = nonblocking ? 1UL : 0UL;
1998 ioctlsocket(sock, FIONBIO, &flags);
1999 #else
2000 auto flags = fcntl(sock, F_GETFL, 0);
2001 fcntl(sock, F_SETFL,
2002 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
2003 #endif
2004 }
2005
is_connection_error()2006 inline bool is_connection_error() {
2007 #ifdef _WIN32
2008 return WSAGetLastError() != WSAEWOULDBLOCK;
2009 #else
2010 return errno != EINPROGRESS;
2011 #endif
2012 }
2013
bind_ip_address(socket_t sock,const char * host)2014 inline bool bind_ip_address(socket_t sock, const char *host) {
2015 struct addrinfo hints;
2016 struct addrinfo *result;
2017
2018 memset(&hints, 0, sizeof(struct addrinfo));
2019 hints.ai_family = AF_UNSPEC;
2020 hints.ai_socktype = SOCK_STREAM;
2021 hints.ai_protocol = 0;
2022
2023 if (getaddrinfo(host, "0", &hints, &result)) { return false; }
2024
2025 auto ret = false;
2026 for (auto rp = result; rp; rp = rp->ai_next) {
2027 const auto &ai = *rp;
2028 if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
2029 ret = true;
2030 break;
2031 }
2032 }
2033
2034 freeaddrinfo(result);
2035 return ret;
2036 }
2037
2038 #if !defined _WIN32 && !defined ANDROID
2039 #define USE_IF2IP
2040 #endif
2041
2042 #ifdef USE_IF2IP
if2ip(const std::string & ifn)2043 inline std::string if2ip(const std::string &ifn) {
2044 struct ifaddrs *ifap;
2045 getifaddrs(&ifap);
2046 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
2047 if (ifa->ifa_addr && ifn == ifa->ifa_name) {
2048 if (ifa->ifa_addr->sa_family == AF_INET) {
2049 auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
2050 char buf[INET_ADDRSTRLEN];
2051 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
2052 freeifaddrs(ifap);
2053 return std::string(buf, INET_ADDRSTRLEN);
2054 }
2055 }
2056 }
2057 }
2058 freeifaddrs(ifap);
2059 return std::string();
2060 }
2061 #endif
2062
create_client_socket(const char * host,int port,bool tcp_nodelay,SocketOptions socket_options,time_t timeout_sec,time_t timeout_usec,const std::string & intf,Error & error)2063 inline socket_t create_client_socket(const char *host, int port,
2064 bool tcp_nodelay,
2065 SocketOptions socket_options,
2066 time_t timeout_sec, time_t timeout_usec,
2067 const std::string &intf, Error &error) {
2068 auto sock = create_socket(
2069 host, port, 0, tcp_nodelay, std::move(socket_options),
2070 [&](socket_t sock, struct addrinfo &ai) -> bool {
2071 if (!intf.empty()) {
2072 #ifdef USE_IF2IP
2073 auto ip = if2ip(intf);
2074 if (ip.empty()) { ip = intf; }
2075 if (!bind_ip_address(sock, ip.c_str())) {
2076 error = Error::BindIPAddress;
2077 return false;
2078 }
2079 #endif
2080 }
2081
2082 set_nonblocking(sock, true);
2083
2084 auto ret =
2085 ::connect(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
2086
2087 if (ret < 0) {
2088 if (is_connection_error() ||
2089 !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) {
2090 close_socket(sock);
2091 error = Error::Connection;
2092 return false;
2093 }
2094 }
2095
2096 set_nonblocking(sock, false);
2097 error = Error::Success;
2098 return true;
2099 });
2100
2101 if (sock != INVALID_SOCKET) {
2102 error = Error::Success;
2103 } else {
2104 if (error == Error::Success) { error = Error::Connection; }
2105 }
2106
2107 return sock;
2108 }
2109
get_remote_ip_and_port(const struct sockaddr_storage & addr,socklen_t addr_len,std::string & ip,int & port)2110 inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
2111 socklen_t addr_len, std::string &ip,
2112 int &port) {
2113 if (addr.ss_family == AF_INET) {
2114 port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
2115 } else if (addr.ss_family == AF_INET6) {
2116 port =
2117 ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
2118 }
2119
2120 std::array<char, NI_MAXHOST> ipstr{};
2121 if (!getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
2122 ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
2123 0, NI_NUMERICHOST)) {
2124 ip = ipstr.data();
2125 }
2126 }
2127
get_remote_ip_and_port(socket_t sock,std::string & ip,int & port)2128 inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
2129 struct sockaddr_storage addr;
2130 socklen_t addr_len = sizeof(addr);
2131
2132 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
2133 &addr_len)) {
2134 get_remote_ip_and_port(addr, addr_len, ip, port);
2135 }
2136 }
2137
2138 inline const char *
find_content_type(const std::string & path,const std::map<std::string,std::string> & user_data)2139 find_content_type(const std::string &path,
2140 const std::map<std::string, std::string> &user_data) {
2141 auto ext = file_extension(path);
2142
2143 auto it = user_data.find(ext);
2144 if (it != user_data.end()) { return it->second.c_str(); }
2145
2146 if (ext == "txt") {
2147 return "text/plain";
2148 } else if (ext == "html" || ext == "htm") {
2149 return "text/html";
2150 } else if (ext == "css") {
2151 return "text/css";
2152 } else if (ext == "jpeg" || ext == "jpg") {
2153 return "image/jpg";
2154 } else if (ext == "png") {
2155 return "image/png";
2156 } else if (ext == "gif") {
2157 return "image/gif";
2158 } else if (ext == "svg") {
2159 return "image/svg+xml";
2160 } else if (ext == "ico") {
2161 return "image/x-icon";
2162 } else if (ext == "json") {
2163 return "application/json";
2164 } else if (ext == "pdf") {
2165 return "application/pdf";
2166 } else if (ext == "js") {
2167 return "application/javascript";
2168 } else if (ext == "wasm") {
2169 return "application/wasm";
2170 } else if (ext == "xml") {
2171 return "application/xml";
2172 } else if (ext == "xhtml") {
2173 return "application/xhtml+xml";
2174 }
2175 return nullptr;
2176 }
2177
status_message(int status)2178 inline const char *status_message(int status) {
2179 switch (status) {
2180 case 100: return "Continue";
2181 case 101: return "Switching Protocol";
2182 case 102: return "Processing";
2183 case 103: return "Early Hints";
2184 case 200: return "OK";
2185 case 201: return "Created";
2186 case 202: return "Accepted";
2187 case 203: return "Non-Authoritative Information";
2188 case 204: return "No Content";
2189 case 205: return "Reset Content";
2190 case 206: return "Partial Content";
2191 case 207: return "Multi-Status";
2192 case 208: return "Already Reported";
2193 case 226: return "IM Used";
2194 case 300: return "Multiple Choice";
2195 case 301: return "Moved Permanently";
2196 case 302: return "Found";
2197 case 303: return "See Other";
2198 case 304: return "Not Modified";
2199 case 305: return "Use Proxy";
2200 case 306: return "unused";
2201 case 307: return "Temporary Redirect";
2202 case 308: return "Permanent Redirect";
2203 case 400: return "Bad Request";
2204 case 401: return "Unauthorized";
2205 case 402: return "Payment Required";
2206 case 403: return "Forbidden";
2207 case 404: return "Not Found";
2208 case 405: return "Method Not Allowed";
2209 case 406: return "Not Acceptable";
2210 case 407: return "Proxy Authentication Required";
2211 case 408: return "Request Timeout";
2212 case 409: return "Conflict";
2213 case 410: return "Gone";
2214 case 411: return "Length Required";
2215 case 412: return "Precondition Failed";
2216 case 413: return "Payload Too Large";
2217 case 414: return "URI Too Long";
2218 case 415: return "Unsupported Media Type";
2219 case 416: return "Range Not Satisfiable";
2220 case 417: return "Expectation Failed";
2221 case 418: return "I'm a teapot";
2222 case 421: return "Misdirected Request";
2223 case 422: return "Unprocessable Entity";
2224 case 423: return "Locked";
2225 case 424: return "Failed Dependency";
2226 case 425: return "Too Early";
2227 case 426: return "Upgrade Required";
2228 case 428: return "Precondition Required";
2229 case 429: return "Too Many Requests";
2230 case 431: return "Request Header Fields Too Large";
2231 case 451: return "Unavailable For Legal Reasons";
2232 case 501: return "Not Implemented";
2233 case 502: return "Bad Gateway";
2234 case 503: return "Service Unavailable";
2235 case 504: return "Gateway Timeout";
2236 case 505: return "HTTP Version Not Supported";
2237 case 506: return "Variant Also Negotiates";
2238 case 507: return "Insufficient Storage";
2239 case 508: return "Loop Detected";
2240 case 510: return "Not Extended";
2241 case 511: return "Network Authentication Required";
2242
2243 default:
2244 case 500: return "Internal Server Error";
2245 }
2246 }
2247
can_compress_content_type(const std::string & content_type)2248 inline bool can_compress_content_type(const std::string &content_type) {
2249 return (!content_type.find("text/") && content_type != "text/event-stream") ||
2250 content_type == "image/svg+xml" ||
2251 content_type == "application/javascript" ||
2252 content_type == "application/json" ||
2253 content_type == "application/xml" ||
2254 content_type == "application/xhtml+xml";
2255 }
2256
2257 enum class EncodingType { None = 0, Gzip, Brotli };
2258
encoding_type(const Request & req,const Response & res)2259 inline EncodingType encoding_type(const Request &req, const Response &res) {
2260 auto ret =
2261 detail::can_compress_content_type(res.get_header_value("Content-Type"));
2262 if (!ret) { return EncodingType::None; }
2263
2264 const auto &s = req.get_header_value("Accept-Encoding");
2265 (void)(s);
2266
2267 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2268 // TODO: 'Accept-Encoding' has br, not br;q=0
2269 ret = s.find("br") != std::string::npos;
2270 if (ret) { return EncodingType::Brotli; }
2271 #endif
2272
2273 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2274 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
2275 ret = s.find("gzip") != std::string::npos;
2276 if (ret) { return EncodingType::Gzip; }
2277 #endif
2278
2279 return EncodingType::None;
2280 }
2281
2282 class compressor {
2283 public:
~compressor()2284 virtual ~compressor(){};
2285
2286 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2287 virtual bool compress(const char *data, size_t data_length, bool last,
2288 Callback callback) = 0;
2289 };
2290
2291 class decompressor {
2292 public:
~decompressor()2293 virtual ~decompressor() {}
2294
2295 virtual bool is_valid() const = 0;
2296
2297 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2298 virtual bool decompress(const char *data, size_t data_length,
2299 Callback callback) = 0;
2300 };
2301
2302 class nocompressor : public compressor {
2303 public:
~nocompressor()2304 ~nocompressor(){};
2305
compress(const char * data,size_t data_length,bool,Callback callback)2306 bool compress(const char *data, size_t data_length, bool /*last*/,
2307 Callback callback) override {
2308 if (!data_length) { return true; }
2309 return callback(data, data_length);
2310 }
2311 };
2312
2313 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2314 class gzip_compressor : public compressor {
2315 public:
gzip_compressor()2316 gzip_compressor() {
2317 std::memset(&strm_, 0, sizeof(strm_));
2318 strm_.zalloc = Z_NULL;
2319 strm_.zfree = Z_NULL;
2320 strm_.opaque = Z_NULL;
2321
2322 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
2323 Z_DEFAULT_STRATEGY) == Z_OK;
2324 }
2325
~gzip_compressor()2326 ~gzip_compressor() { deflateEnd(&strm_); }
2327
compress(const char * data,size_t data_length,bool last,Callback callback)2328 bool compress(const char *data, size_t data_length, bool last,
2329 Callback callback) override {
2330 assert(is_valid_);
2331
2332 auto flush = last ? Z_FINISH : Z_NO_FLUSH;
2333
2334 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
2335 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
2336
2337 int ret = Z_OK;
2338
2339 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2340 do {
2341 strm_.avail_out = buff.size();
2342 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
2343
2344 ret = deflate(&strm_, flush);
2345 assert(ret != Z_STREAM_ERROR);
2346
2347 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
2348 return false;
2349 }
2350 } while (strm_.avail_out == 0);
2351
2352 assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK));
2353 assert(strm_.avail_in == 0);
2354 return true;
2355 }
2356
2357 private:
2358 bool is_valid_ = false;
2359 z_stream strm_;
2360 };
2361
2362 class gzip_decompressor : public decompressor {
2363 public:
gzip_decompressor()2364 gzip_decompressor() {
2365 std::memset(&strm_, 0, sizeof(strm_));
2366 strm_.zalloc = Z_NULL;
2367 strm_.zfree = Z_NULL;
2368 strm_.opaque = Z_NULL;
2369
2370 // 15 is the value of wbits, which should be at the maximum possible value
2371 // to ensure that any gzip stream can be decoded. The offset of 32 specifies
2372 // that the stream type should be automatically detected either gzip or
2373 // deflate.
2374 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
2375 }
2376
~gzip_decompressor()2377 ~gzip_decompressor() { inflateEnd(&strm_); }
2378
is_valid() const2379 bool is_valid() const override { return is_valid_; }
2380
decompress(const char * data,size_t data_length,Callback callback)2381 bool decompress(const char *data, size_t data_length,
2382 Callback callback) override {
2383 assert(is_valid_);
2384
2385 int ret = Z_OK;
2386
2387 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
2388 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
2389
2390 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2391 while (strm_.avail_in > 0) {
2392 strm_.avail_out = buff.size();
2393 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
2394
2395 ret = inflate(&strm_, Z_NO_FLUSH);
2396 assert(ret != Z_STREAM_ERROR);
2397 switch (ret) {
2398 case Z_NEED_DICT:
2399 case Z_DATA_ERROR:
2400 case Z_MEM_ERROR: inflateEnd(&strm_); return false;
2401 }
2402
2403 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
2404 return false;
2405 }
2406 }
2407
2408 return ret == Z_OK || ret == Z_STREAM_END;
2409 }
2410
2411 private:
2412 bool is_valid_ = false;
2413 z_stream strm_;
2414 };
2415 #endif
2416
2417 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2418 class brotli_compressor : public compressor {
2419 public:
brotli_compressor()2420 brotli_compressor() {
2421 state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
2422 }
2423
~brotli_compressor()2424 ~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
2425
compress(const char * data,size_t data_length,bool last,Callback callback)2426 bool compress(const char *data, size_t data_length, bool last,
2427 Callback callback) override {
2428 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2429
2430 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
2431 auto available_in = data_length;
2432 auto next_in = reinterpret_cast<const uint8_t *>(data);
2433
2434 for (;;) {
2435 if (last) {
2436 if (BrotliEncoderIsFinished(state_)) { break; }
2437 } else {
2438 if (!available_in) { break; }
2439 }
2440
2441 auto available_out = buff.size();
2442 auto next_out = buff.data();
2443
2444 if (!BrotliEncoderCompressStream(state_, operation, &available_in,
2445 &next_in, &available_out, &next_out,
2446 nullptr)) {
2447 return false;
2448 }
2449
2450 auto output_bytes = buff.size() - available_out;
2451 if (output_bytes) {
2452 callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
2453 }
2454 }
2455
2456 return true;
2457 }
2458
2459 private:
2460 BrotliEncoderState *state_ = nullptr;
2461 };
2462
2463 class brotli_decompressor : public decompressor {
2464 public:
brotli_decompressor()2465 brotli_decompressor() {
2466 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
2467 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
2468 : BROTLI_DECODER_RESULT_ERROR;
2469 }
2470
~brotli_decompressor()2471 ~brotli_decompressor() {
2472 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
2473 }
2474
is_valid() const2475 bool is_valid() const override { return decoder_s; }
2476
decompress(const char * data,size_t data_length,Callback callback)2477 bool decompress(const char *data, size_t data_length,
2478 Callback callback) override {
2479 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
2480 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
2481 return 0;
2482 }
2483
2484 const uint8_t *next_in = (const uint8_t *)data;
2485 size_t avail_in = data_length;
2486 size_t total_out;
2487
2488 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
2489
2490 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2491 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
2492 char *next_out = buff.data();
2493 size_t avail_out = buff.size();
2494
2495 decoder_r = BrotliDecoderDecompressStream(
2496 decoder_s, &avail_in, &next_in, &avail_out,
2497 reinterpret_cast<uint8_t **>(&next_out), &total_out);
2498
2499 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
2500
2501 if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
2502 }
2503
2504 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
2505 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
2506 }
2507
2508 private:
2509 BrotliDecoderResult decoder_r;
2510 BrotliDecoderState *decoder_s = nullptr;
2511 };
2512 #endif
2513
has_header(const Headers & headers,const char * key)2514 inline bool has_header(const Headers &headers, const char *key) {
2515 return headers.find(key) != headers.end();
2516 }
2517
get_header_value(const Headers & headers,const char * key,size_t id=0,const char * def=nullptr)2518 inline const char *get_header_value(const Headers &headers, const char *key,
2519 size_t id = 0, const char *def = nullptr) {
2520 auto rng = headers.equal_range(key);
2521 auto it = rng.first;
2522 std::advance(it, static_cast<ssize_t>(id));
2523 if (it != rng.second) { return it->second.c_str(); }
2524 return def;
2525 }
2526
2527 template <typename T>
get_header_value(const Headers &,const char *,size_t=0,uint64_t=0)2528 inline T get_header_value(const Headers & /*headers*/, const char * /*key*/,
2529 size_t /*id*/ = 0, uint64_t /*def*/ = 0) {}
2530
2531 template <>
get_header_value(const Headers & headers,const char * key,size_t id,uint64_t def)2532 inline uint64_t get_header_value<uint64_t>(const Headers &headers,
2533 const char *key, size_t id,
2534 uint64_t def) {
2535 auto rng = headers.equal_range(key);
2536 auto it = rng.first;
2537 std::advance(it, static_cast<ssize_t>(id));
2538 if (it != rng.second) {
2539 return std::strtoull(it->second.data(), nullptr, 10);
2540 }
2541 return def;
2542 }
2543
2544 template <typename T>
parse_header(const char * beg,const char * end,T fn)2545 inline bool parse_header(const char *beg, const char *end, T fn) {
2546 // Skip trailing spaces and tabs.
2547 while (beg < end && is_space_or_tab(end[-1])) {
2548 end--;
2549 }
2550
2551 auto p = beg;
2552 while (p < end && *p != ':') {
2553 p++;
2554 }
2555
2556 if (p == end) { return false; }
2557
2558 auto key_end = p;
2559
2560 if (*p++ != ':') { return false; }
2561
2562 while (p < end && is_space_or_tab(*p)) {
2563 p++;
2564 }
2565
2566 if (p < end) {
2567 fn(std::string(beg, key_end), decode_url(std::string(p, end), false));
2568 return true;
2569 }
2570
2571 return false;
2572 }
2573
read_headers(Stream & strm,Headers & headers)2574 inline bool read_headers(Stream &strm, Headers &headers) {
2575 const auto bufsiz = 2048;
2576 char buf[bufsiz];
2577 stream_line_reader line_reader(strm, buf, bufsiz);
2578
2579 for (;;) {
2580 if (!line_reader.getline()) { return false; }
2581
2582 // Check if the line ends with CRLF.
2583 if (line_reader.end_with_crlf()) {
2584 // Blank line indicates end of headers.
2585 if (line_reader.size() == 2) { break; }
2586 } else {
2587 continue; // Skip invalid line.
2588 }
2589
2590 // Exclude CRLF
2591 auto end = line_reader.ptr() + line_reader.size() - 2;
2592
2593 parse_header(line_reader.ptr(), end,
2594 [&](std::string &&key, std::string &&val) {
2595 headers.emplace(std::move(key), std::move(val));
2596 });
2597 }
2598
2599 return true;
2600 }
2601
read_content_with_length(Stream & strm,uint64_t len,Progress progress,ContentReceiverWithProgress out)2602 inline bool read_content_with_length(Stream &strm, uint64_t len,
2603 Progress progress,
2604 ContentReceiverWithProgress out) {
2605 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2606
2607 uint64_t r = 0;
2608 while (r < len) {
2609 auto read_len = static_cast<size_t>(len - r);
2610 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
2611 if (n <= 0) { return false; }
2612
2613 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
2614 r += static_cast<uint64_t>(n);
2615
2616 if (progress) {
2617 if (!progress(r, len)) { return false; }
2618 }
2619 }
2620
2621 return true;
2622 }
2623
skip_content_with_length(Stream & strm,uint64_t len)2624 inline void skip_content_with_length(Stream &strm, uint64_t len) {
2625 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2626 uint64_t r = 0;
2627 while (r < len) {
2628 auto read_len = static_cast<size_t>(len - r);
2629 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
2630 if (n <= 0) { return; }
2631 r += static_cast<uint64_t>(n);
2632 }
2633 }
2634
read_content_without_length(Stream & strm,ContentReceiverWithProgress out)2635 inline bool read_content_without_length(Stream &strm,
2636 ContentReceiverWithProgress out) {
2637 char buf[CPPHTTPLIB_RECV_BUFSIZ];
2638 uint64_t r = 0;
2639 for (;;) {
2640 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
2641 if (n < 0) {
2642 return false;
2643 } else if (n == 0) {
2644 return true;
2645 }
2646
2647 if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
2648 r += static_cast<uint64_t>(n);
2649 }
2650
2651 return true;
2652 }
2653
read_content_chunked(Stream & strm,ContentReceiverWithProgress out)2654 inline bool read_content_chunked(Stream &strm,
2655 ContentReceiverWithProgress out) {
2656 const auto bufsiz = 16;
2657 char buf[bufsiz];
2658
2659 stream_line_reader line_reader(strm, buf, bufsiz);
2660
2661 if (!line_reader.getline()) { return false; }
2662
2663 unsigned long chunk_len;
2664 while (true) {
2665 char *end_ptr;
2666
2667 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
2668
2669 if (end_ptr == line_reader.ptr()) { return false; }
2670 if (chunk_len == ULONG_MAX) { return false; }
2671
2672 if (chunk_len == 0) { break; }
2673
2674 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
2675 return false;
2676 }
2677
2678 if (!line_reader.getline()) { return false; }
2679
2680 if (strcmp(line_reader.ptr(), "\r\n")) { break; }
2681
2682 if (!line_reader.getline()) { return false; }
2683 }
2684
2685 if (chunk_len == 0) {
2686 // Reader terminator after chunks
2687 if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n"))
2688 return false;
2689 }
2690
2691 return true;
2692 }
2693
is_chunked_transfer_encoding(const Headers & headers)2694 inline bool is_chunked_transfer_encoding(const Headers &headers) {
2695 return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""),
2696 "chunked");
2697 }
2698
2699 template <typename T, typename U>
prepare_content_receiver(T & x,int & status,ContentReceiverWithProgress receiver,bool decompress,U callback)2700 bool prepare_content_receiver(T &x, int &status,
2701 ContentReceiverWithProgress receiver,
2702 bool decompress, U callback) {
2703 if (decompress) {
2704 std::string encoding = x.get_header_value("Content-Encoding");
2705 std::unique_ptr<decompressor> decompressor;
2706
2707 if (encoding.find("gzip") != std::string::npos ||
2708 encoding.find("deflate") != std::string::npos) {
2709 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2710 decompressor = detail::make_unique<gzip_decompressor>();
2711 #else
2712 status = 415;
2713 return false;
2714 #endif
2715 } else if (encoding.find("br") != std::string::npos) {
2716 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
2717 decompressor = detail::make_unique<brotli_decompressor>();
2718 #else
2719 status = 415;
2720 return false;
2721 #endif
2722 }
2723
2724 if (decompressor) {
2725 if (decompressor->is_valid()) {
2726 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
2727 uint64_t off, uint64_t len) {
2728 return decompressor->decompress(buf, n,
2729 [&](const char *buf, size_t n) {
2730 return receiver(buf, n, off, len);
2731 });
2732 };
2733 return callback(std::move(out));
2734 } else {
2735 status = 500;
2736 return false;
2737 }
2738 }
2739 }
2740
2741 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
2742 uint64_t len) {
2743 return receiver(buf, n, off, len);
2744 };
2745 return callback(std::move(out));
2746 }
2747
2748 template <typename T>
read_content(Stream & strm,T & x,size_t payload_max_length,int & status,Progress progress,ContentReceiverWithProgress receiver,bool decompress)2749 bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2750 Progress progress, ContentReceiverWithProgress receiver,
2751 bool decompress) {
2752 return prepare_content_receiver(
2753 x, status, std::move(receiver), decompress,
2754 [&](const ContentReceiverWithProgress &out) {
2755 auto ret = true;
2756 auto exceed_payload_max_length = false;
2757
2758 if (is_chunked_transfer_encoding(x.headers)) {
2759 ret = read_content_chunked(strm, out);
2760 } else if (!has_header(x.headers, "Content-Length")) {
2761 ret = read_content_without_length(strm, out);
2762 } else {
2763 auto len = get_header_value<uint64_t>(x.headers, "Content-Length");
2764 if (len > payload_max_length) {
2765 exceed_payload_max_length = true;
2766 skip_content_with_length(strm, len);
2767 ret = false;
2768 } else if (len > 0) {
2769 ret = read_content_with_length(strm, len, std::move(progress), out);
2770 }
2771 }
2772
2773 if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
2774 return ret;
2775 });
2776 }
2777
2778 template <typename T>
write_headers(Stream & strm,const T & info,const Headers & headers)2779 inline ssize_t write_headers(Stream &strm, const T &info,
2780 const Headers &headers) {
2781 ssize_t write_len = 0;
2782 for (const auto &x : info.headers) {
2783 if (x.first == "EXCEPTION_WHAT") { continue; }
2784 auto len =
2785 strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
2786 if (len < 0) { return len; }
2787 write_len += len;
2788 }
2789 for (const auto &x : headers) {
2790 auto len =
2791 strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
2792 if (len < 0) { return len; }
2793 write_len += len;
2794 }
2795 auto len = strm.write("\r\n");
2796 if (len < 0) { return len; }
2797 write_len += len;
2798 return write_len;
2799 }
2800
write_data(Stream & strm,const char * d,size_t l)2801 inline bool write_data(Stream &strm, const char *d, size_t l) {
2802 size_t offset = 0;
2803 while (offset < l) {
2804 auto length = strm.write(d + offset, l - offset);
2805 if (length < 0) { return false; }
2806 offset += static_cast<size_t>(length);
2807 }
2808 return true;
2809 }
2810
2811 template <typename T>
write_content(Stream & strm,const ContentProvider & content_provider,size_t offset,size_t length,T is_shutting_down,Error & error)2812 inline bool write_content(Stream &strm, const ContentProvider &content_provider,
2813 size_t offset, size_t length, T is_shutting_down,
2814 Error &error) {
2815 size_t end_offset = offset + length;
2816 auto ok = true;
2817 DataSink data_sink;
2818
2819 data_sink.write = [&](const char *d, size_t l) {
2820 if (ok) {
2821 if (write_data(strm, d, l)) {
2822 offset += l;
2823 } else {
2824 ok = false;
2825 }
2826 }
2827 };
2828
2829 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
2830
2831 while (offset < end_offset && !is_shutting_down()) {
2832 if (!content_provider(offset, end_offset - offset, data_sink)) {
2833 error = Error::Canceled;
2834 return false;
2835 }
2836 if (!ok) {
2837 error = Error::Write;
2838 return false;
2839 }
2840 }
2841
2842 error = Error::Success;
2843 return true;
2844 }
2845
2846 template <typename T>
write_content(Stream & strm,const ContentProvider & content_provider,size_t offset,size_t length,const T & is_shutting_down)2847 inline bool write_content(Stream &strm, const ContentProvider &content_provider,
2848 size_t offset, size_t length,
2849 const T &is_shutting_down) {
2850 auto error = Error::Success;
2851 return write_content(strm, content_provider, offset, length, is_shutting_down,
2852 error);
2853 }
2854
2855 template <typename T>
2856 inline bool
write_content_without_length(Stream & strm,const ContentProvider & content_provider,const T & is_shutting_down)2857 write_content_without_length(Stream &strm,
2858 const ContentProvider &content_provider,
2859 const T &is_shutting_down) {
2860 size_t offset = 0;
2861 auto data_available = true;
2862 auto ok = true;
2863 DataSink data_sink;
2864
2865 data_sink.write = [&](const char *d, size_t l) {
2866 if (ok) {
2867 offset += l;
2868 if (!write_data(strm, d, l)) { ok = false; }
2869 }
2870 };
2871
2872 data_sink.done = [&](void) { data_available = false; };
2873
2874 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
2875
2876 while (data_available && !is_shutting_down()) {
2877 if (!content_provider(offset, 0, data_sink)) { return false; }
2878 if (!ok) { return false; }
2879 }
2880 return true;
2881 }
2882
2883 template <typename T, typename U>
2884 inline bool
write_content_chunked(Stream & strm,const ContentProvider & content_provider,const T & is_shutting_down,U & compressor,Error & error)2885 write_content_chunked(Stream &strm, const ContentProvider &content_provider,
2886 const T &is_shutting_down, U &compressor, Error &error) {
2887 size_t offset = 0;
2888 auto data_available = true;
2889 auto ok = true;
2890 DataSink data_sink;
2891
2892 data_sink.write = [&](const char *d, size_t l) {
2893 if (!ok) { return; }
2894
2895 data_available = l > 0;
2896 offset += l;
2897
2898 std::string payload;
2899 if (!compressor.compress(d, l, false,
2900 [&](const char *data, size_t data_len) {
2901 payload.append(data, data_len);
2902 return true;
2903 })) {
2904 ok = false;
2905 return;
2906 }
2907
2908 if (!payload.empty()) {
2909 // Emit chunked response header and footer for each chunk
2910 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
2911 if (!write_data(strm, chunk.data(), chunk.size())) {
2912 ok = false;
2913 return;
2914 }
2915 }
2916 };
2917
2918 data_sink.done = [&](void) {
2919 if (!ok) { return; }
2920
2921 data_available = false;
2922
2923 std::string payload;
2924 if (!compressor.compress(nullptr, 0, true,
2925 [&](const char *data, size_t data_len) {
2926 payload.append(data, data_len);
2927 return true;
2928 })) {
2929 ok = false;
2930 return;
2931 }
2932
2933 if (!payload.empty()) {
2934 // Emit chunked response header and footer for each chunk
2935 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
2936 if (!write_data(strm, chunk.data(), chunk.size())) {
2937 ok = false;
2938 return;
2939 }
2940 }
2941
2942 static const std::string done_marker("0\r\n\r\n");
2943 if (!write_data(strm, done_marker.data(), done_marker.size())) {
2944 ok = false;
2945 }
2946 };
2947
2948 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
2949
2950 while (data_available && !is_shutting_down()) {
2951 if (!content_provider(offset, 0, data_sink)) {
2952 error = Error::Canceled;
2953 return false;
2954 }
2955 if (!ok) {
2956 error = Error::Write;
2957 return false;
2958 }
2959 }
2960
2961 error = Error::Success;
2962 return true;
2963 }
2964
2965 template <typename T, typename U>
write_content_chunked(Stream & strm,const ContentProvider & content_provider,const T & is_shutting_down,U & compressor)2966 inline bool write_content_chunked(Stream &strm,
2967 const ContentProvider &content_provider,
2968 const T &is_shutting_down, U &compressor) {
2969 auto error = Error::Success;
2970 return write_content_chunked(strm, content_provider, is_shutting_down,
2971 compressor, error);
2972 }
2973
2974 template <typename T>
redirect(T & cli,const Request & req,Response & res,const std::string & path,const std::string & location,Error & error)2975 inline bool redirect(T &cli, const Request &req, Response &res,
2976 const std::string &path, const std::string &location,
2977 Error &error) {
2978 Request new_req = req;
2979 new_req.path = path;
2980 new_req.redirect_count_ -= 1;
2981
2982 if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
2983 new_req.method = "GET";
2984 new_req.body.clear();
2985 new_req.headers.clear();
2986 }
2987
2988 Response new_res;
2989
2990 auto ret = cli.send(new_req, new_res, error);
2991 if (ret) {
2992 new_res.location = location;
2993 res = new_res;
2994 }
2995 return ret;
2996 }
2997
params_to_query_str(const Params & params)2998 inline std::string params_to_query_str(const Params ¶ms) {
2999 std::string query;
3000
3001 for (auto it = params.begin(); it != params.end(); ++it) {
3002 if (it != params.begin()) { query += "&"; }
3003 query += it->first;
3004 query += "=";
3005 query += encode_url(it->second);
3006 }
3007 return query;
3008 }
3009
parse_query_text(const std::string & s,Params & params)3010 inline void parse_query_text(const std::string &s, Params ¶ms) {
3011 split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
3012 std::string key;
3013 std::string val;
3014 split(b, e, '=', [&](const char *b2, const char *e2) {
3015 if (key.empty()) {
3016 key.assign(b2, e2);
3017 } else {
3018 val.assign(b2, e2);
3019 }
3020 });
3021
3022 if (!key.empty()) {
3023 params.emplace(decode_url(key, true), decode_url(val, true));
3024 }
3025 });
3026 }
3027
parse_multipart_boundary(const std::string & content_type,std::string & boundary)3028 inline bool parse_multipart_boundary(const std::string &content_type,
3029 std::string &boundary) {
3030 auto pos = content_type.find("boundary=");
3031 if (pos == std::string::npos) { return false; }
3032 boundary = content_type.substr(pos + 9);
3033 if (boundary.length() >= 2 && boundary.front() == '"' &&
3034 boundary.back() == '"') {
3035 boundary = boundary.substr(1, boundary.size() - 2);
3036 }
3037 return !boundary.empty();
3038 }
3039
parse_range_header(const std::string & s,Ranges & ranges)3040 inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
3041 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
3042 std::smatch m;
3043 if (std::regex_match(s, m, re_first_range)) {
3044 auto pos = static_cast<size_t>(m.position(1));
3045 auto len = static_cast<size_t>(m.length(1));
3046 bool all_valid_ranges = true;
3047 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
3048 if (!all_valid_ranges) return;
3049 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
3050 std::cmatch cm;
3051 if (std::regex_match(b, e, cm, re_another_range)) {
3052 ssize_t first = -1;
3053 if (!cm.str(1).empty()) {
3054 first = static_cast<ssize_t>(std::stoll(cm.str(1)));
3055 }
3056
3057 ssize_t last = -1;
3058 if (!cm.str(2).empty()) {
3059 last = static_cast<ssize_t>(std::stoll(cm.str(2)));
3060 }
3061
3062 if (first != -1 && last != -1 && first > last) {
3063 all_valid_ranges = false;
3064 return;
3065 }
3066 ranges.emplace_back(std::make_pair(first, last));
3067 }
3068 });
3069 return all_valid_ranges;
3070 }
3071 return false;
3072 } catch (...) { return false; }
3073
3074 class MultipartFormDataParser {
3075 public:
3076 MultipartFormDataParser() = default;
3077
set_boundary(std::string && boundary)3078 void set_boundary(std::string &&boundary) { boundary_ = boundary; }
3079
is_valid() const3080 bool is_valid() const { return is_valid_; }
3081
parse(const char * buf,size_t n,const ContentReceiver & content_callback,const MultipartContentHeader & header_callback)3082 bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,
3083 const MultipartContentHeader &header_callback) {
3084
3085 static const std::regex re_content_disposition(
3086 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
3087 "\"(.*?)\")?\\s*$",
3088 std::regex_constants::icase);
3089 static const std::string dash_ = "--";
3090 static const std::string crlf_ = "\r\n";
3091
3092 buf_.append(buf, n); // TODO: performance improvement
3093
3094 while (!buf_.empty()) {
3095 switch (state_) {
3096 case 0: { // Initial boundary
3097 auto pattern = dash_ + boundary_ + crlf_;
3098 if (pattern.size() > buf_.size()) { return true; }
3099 auto pos = buf_.find(pattern);
3100 if (pos != 0) { return false; }
3101 buf_.erase(0, pattern.size());
3102 off_ += pattern.size();
3103 state_ = 1;
3104 break;
3105 }
3106 case 1: { // New entry
3107 clear_file_info();
3108 state_ = 2;
3109 break;
3110 }
3111 case 2: { // Headers
3112 auto pos = buf_.find(crlf_);
3113 while (pos != std::string::npos) {
3114 // Empty line
3115 if (pos == 0) {
3116 if (!header_callback(file_)) {
3117 is_valid_ = false;
3118 return false;
3119 }
3120 buf_.erase(0, crlf_.size());
3121 off_ += crlf_.size();
3122 state_ = 3;
3123 break;
3124 }
3125
3126 static const std::string header_name = "content-type:";
3127 const auto header = buf_.substr(0, pos);
3128 if (start_with_case_ignore(header, header_name)) {
3129 file_.content_type = trim_copy(header.substr(header_name.size()));
3130 } else {
3131 std::smatch m;
3132 if (std::regex_match(header, m, re_content_disposition)) {
3133 file_.name = m[1];
3134 file_.filename = m[2];
3135 }
3136 }
3137
3138 buf_.erase(0, pos + crlf_.size());
3139 off_ += pos + crlf_.size();
3140 pos = buf_.find(crlf_);
3141 }
3142 if (state_ != 3) { return true; }
3143 break;
3144 }
3145 case 3: { // Body
3146 {
3147 auto pattern = crlf_ + dash_;
3148 if (pattern.size() > buf_.size()) { return true; }
3149
3150 auto pos = find_string(buf_, pattern);
3151
3152 if (!content_callback(buf_.data(), pos)) {
3153 is_valid_ = false;
3154 return false;
3155 }
3156
3157 off_ += pos;
3158 buf_.erase(0, pos);
3159 }
3160 {
3161 auto pattern = crlf_ + dash_ + boundary_;
3162 if (pattern.size() > buf_.size()) { return true; }
3163
3164 auto pos = buf_.find(pattern);
3165 if (pos != std::string::npos) {
3166 if (!content_callback(buf_.data(), pos)) {
3167 is_valid_ = false;
3168 return false;
3169 }
3170
3171 off_ += pos + pattern.size();
3172 buf_.erase(0, pos + pattern.size());
3173 state_ = 4;
3174 } else {
3175 if (!content_callback(buf_.data(), pattern.size())) {
3176 is_valid_ = false;
3177 return false;
3178 }
3179
3180 off_ += pattern.size();
3181 buf_.erase(0, pattern.size());
3182 }
3183 }
3184 break;
3185 }
3186 case 4: { // Boundary
3187 if (crlf_.size() > buf_.size()) { return true; }
3188 if (buf_.compare(0, crlf_.size(), crlf_) == 0) {
3189 buf_.erase(0, crlf_.size());
3190 off_ += crlf_.size();
3191 state_ = 1;
3192 } else {
3193 auto pattern = dash_ + crlf_;
3194 if (pattern.size() > buf_.size()) { return true; }
3195 if (buf_.compare(0, pattern.size(), pattern) == 0) {
3196 buf_.erase(0, pattern.size());
3197 off_ += pattern.size();
3198 is_valid_ = true;
3199 state_ = 5;
3200 } else {
3201 return true;
3202 }
3203 }
3204 break;
3205 }
3206 case 5: { // Done
3207 is_valid_ = false;
3208 return false;
3209 }
3210 }
3211 }
3212
3213 return true;
3214 }
3215
3216 private:
clear_file_info()3217 void clear_file_info() {
3218 file_.name.clear();
3219 file_.filename.clear();
3220 file_.content_type.clear();
3221 }
3222
start_with_case_ignore(const std::string & a,const std::string & b) const3223 bool start_with_case_ignore(const std::string &a,
3224 const std::string &b) const {
3225 if (a.size() < b.size()) { return false; }
3226 for (size_t i = 0; i < b.size(); i++) {
3227 if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
3228 }
3229 return true;
3230 }
3231
start_with(const std::string & a,size_t off,const std::string & b) const3232 bool start_with(const std::string &a, size_t off,
3233 const std::string &b) const {
3234 if (a.size() - off < b.size()) { return false; }
3235 for (size_t i = 0; i < b.size(); i++) {
3236 if (a[i + off] != b[i]) { return false; }
3237 }
3238 return true;
3239 }
3240
find_string(const std::string & s,const std::string & pattern) const3241 size_t find_string(const std::string &s, const std::string &pattern) const {
3242 auto c = pattern.front();
3243
3244 size_t off = 0;
3245 while (off < s.size()) {
3246 auto pos = s.find(c, off);
3247 if (pos == std::string::npos) { return s.size(); }
3248
3249 auto rem = s.size() - pos;
3250 if (pattern.size() > rem) { return pos; }
3251
3252 if (start_with(s, pos, pattern)) { return pos; }
3253
3254 off = pos + 1;
3255 }
3256
3257 return s.size();
3258 }
3259
3260 std::string boundary_;
3261
3262 std::string buf_;
3263 size_t state_ = 0;
3264 bool is_valid_ = false;
3265 size_t off_ = 0;
3266 MultipartFormData file_;
3267 };
3268
to_lower(const char * beg,const char * end)3269 inline std::string to_lower(const char *beg, const char *end) {
3270 std::string out;
3271 auto it = beg;
3272 while (it != end) {
3273 out += static_cast<char>(::tolower(*it));
3274 it++;
3275 }
3276 return out;
3277 }
3278
make_multipart_data_boundary()3279 inline std::string make_multipart_data_boundary() {
3280 static const char data[] =
3281 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3282
3283 // std::random_device might actually be deterministic on some
3284 // platforms, but due to lack of support in the c++ standard library,
3285 // doing better requires either some ugly hacks or breaking portability.
3286 std::random_device seed_gen;
3287 // Request 128 bits of entropy for initialization
3288 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
3289 std::mt19937 engine(seed_sequence);
3290
3291 std::string result = "--cpp-httplib-multipart-data-";
3292
3293 for (auto i = 0; i < 16; i++) {
3294 result += data[engine() % (sizeof(data) - 1)];
3295 }
3296
3297 return result;
3298 }
3299
3300 inline std::pair<size_t, size_t>
get_range_offset_and_length(const Request & req,size_t content_length,size_t index)3301 get_range_offset_and_length(const Request &req, size_t content_length,
3302 size_t index) {
3303 auto r = req.ranges[index];
3304
3305 if (r.first == -1 && r.second == -1) {
3306 return std::make_pair(0, content_length);
3307 }
3308
3309 auto slen = static_cast<ssize_t>(content_length);
3310
3311 if (r.first == -1) {
3312 r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
3313 r.second = slen - 1;
3314 }
3315
3316 if (r.second == -1) { r.second = slen - 1; }
3317 return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
3318 }
3319
make_content_range_header_field(size_t offset,size_t length,size_t content_length)3320 inline std::string make_content_range_header_field(size_t offset, size_t length,
3321 size_t content_length) {
3322 std::string field = "bytes ";
3323 field += std::to_string(offset);
3324 field += "-";
3325 field += std::to_string(offset + length - 1);
3326 field += "/";
3327 field += std::to_string(content_length);
3328 return field;
3329 }
3330
3331 template <typename SToken, typename CToken, typename Content>
process_multipart_ranges_data(const Request & req,Response & res,const std::string & boundary,const std::string & content_type,SToken stoken,CToken ctoken,Content content)3332 bool process_multipart_ranges_data(const Request &req, Response &res,
3333 const std::string &boundary,
3334 const std::string &content_type,
3335 SToken stoken, CToken ctoken,
3336 Content content) {
3337 for (size_t i = 0; i < req.ranges.size(); i++) {
3338 ctoken("--");
3339 stoken(boundary);
3340 ctoken("\r\n");
3341 if (!content_type.empty()) {
3342 ctoken("Content-Type: ");
3343 stoken(content_type);
3344 ctoken("\r\n");
3345 }
3346
3347 auto offsets = get_range_offset_and_length(req, res.body.size(), i);
3348 auto offset = offsets.first;
3349 auto length = offsets.second;
3350
3351 ctoken("Content-Range: ");
3352 stoken(make_content_range_header_field(offset, length, res.body.size()));
3353 ctoken("\r\n");
3354 ctoken("\r\n");
3355 if (!content(offset, length)) { return false; }
3356 ctoken("\r\n");
3357 }
3358
3359 ctoken("--");
3360 stoken(boundary);
3361 ctoken("--\r\n");
3362
3363 return true;
3364 }
3365
make_multipart_ranges_data(const Request & req,Response & res,const std::string & boundary,const std::string & content_type,std::string & data)3366 inline bool make_multipart_ranges_data(const Request &req, Response &res,
3367 const std::string &boundary,
3368 const std::string &content_type,
3369 std::string &data) {
3370 return process_multipart_ranges_data(
3371 req, res, boundary, content_type,
3372 [&](const std::string &token) { data += token; },
3373 [&](const char *token) { data += token; },
3374 [&](size_t offset, size_t length) {
3375 if (offset < res.body.size()) {
3376 data += res.body.substr(offset, length);
3377 return true;
3378 }
3379 return false;
3380 });
3381 }
3382
3383 inline size_t
get_multipart_ranges_data_length(const Request & req,Response & res,const std::string & boundary,const std::string & content_type)3384 get_multipart_ranges_data_length(const Request &req, Response &res,
3385 const std::string &boundary,
3386 const std::string &content_type) {
3387 size_t data_length = 0;
3388
3389 process_multipart_ranges_data(
3390 req, res, boundary, content_type,
3391 [&](const std::string &token) { data_length += token.size(); },
3392 [&](const char *token) { data_length += strlen(token); },
3393 [&](size_t /*offset*/, size_t length) {
3394 data_length += length;
3395 return true;
3396 });
3397
3398 return data_length;
3399 }
3400
3401 template <typename T>
write_multipart_ranges_data(Stream & strm,const Request & req,Response & res,const std::string & boundary,const std::string & content_type,const T & is_shutting_down)3402 inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
3403 Response &res,
3404 const std::string &boundary,
3405 const std::string &content_type,
3406 const T &is_shutting_down) {
3407 return process_multipart_ranges_data(
3408 req, res, boundary, content_type,
3409 [&](const std::string &token) { strm.write(token); },
3410 [&](const char *token) { strm.write(token); },
3411 [&](size_t offset, size_t length) {
3412 return write_content(strm, res.content_provider_, offset, length,
3413 is_shutting_down);
3414 });
3415 }
3416
3417 inline std::pair<size_t, size_t>
get_range_offset_and_length(const Request & req,const Response & res,size_t index)3418 get_range_offset_and_length(const Request &req, const Response &res,
3419 size_t index) {
3420 auto r = req.ranges[index];
3421
3422 if (r.second == -1) {
3423 r.second = static_cast<ssize_t>(res.content_length_) - 1;
3424 }
3425
3426 return std::make_pair(r.first, r.second - r.first + 1);
3427 }
3428
expect_content(const Request & req)3429 inline bool expect_content(const Request &req) {
3430 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
3431 req.method == "PRI" || req.method == "DELETE") {
3432 return true;
3433 }
3434 // TODO: check if Content-Length is set
3435 return false;
3436 }
3437
has_crlf(const char * s)3438 inline bool has_crlf(const char *s) {
3439 auto p = s;
3440 while (*p) {
3441 if (*p == '\r' || *p == '\n') { return true; }
3442 p++;
3443 }
3444 return false;
3445 }
3446
3447 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3448 template <typename CTX, typename Init, typename Update, typename Final>
message_digest(const std::string & s,Init init,Update update,Final final,size_t digest_length)3449 inline std::string message_digest(const std::string &s, Init init,
3450 Update update, Final final,
3451 size_t digest_length) {
3452 using namespace std;
3453
3454 std::vector<unsigned char> md(digest_length, 0);
3455 CTX ctx;
3456 init(&ctx);
3457 update(&ctx, s.data(), s.size());
3458 final(md.data(), &ctx);
3459
3460 stringstream ss;
3461 for (auto c : md) {
3462 ss << setfill('0') << setw(2) << hex << (unsigned int)c;
3463 }
3464 return ss.str();
3465 }
3466
MD5(const std::string & s)3467 inline std::string MD5(const std::string &s) {
3468 return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
3469 MD5_DIGEST_LENGTH);
3470 }
3471
SHA_256(const std::string & s)3472 inline std::string SHA_256(const std::string &s) {
3473 return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final,
3474 SHA256_DIGEST_LENGTH);
3475 }
3476
SHA_512(const std::string & s)3477 inline std::string SHA_512(const std::string &s) {
3478 return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final,
3479 SHA512_DIGEST_LENGTH);
3480 }
3481 #endif
3482
3483 #ifdef _WIN32
3484 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3485 // NOTE: This code came up with the following stackoverflow post:
3486 // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
load_system_certs_on_windows(X509_STORE * store)3487 inline bool load_system_certs_on_windows(X509_STORE *store) {
3488 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
3489
3490 if (!hStore) { return false; }
3491
3492 PCCERT_CONTEXT pContext = NULL;
3493 while (pContext = CertEnumCertificatesInStore(hStore, pContext)) {
3494 auto encoded_cert =
3495 static_cast<const unsigned char *>(pContext->pbCertEncoded);
3496
3497 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
3498 if (x509) {
3499 X509_STORE_add_cert(store, x509);
3500 X509_free(x509);
3501 }
3502 }
3503
3504 CertFreeCertificateContext(pContext);
3505 CertCloseStore(hStore, 0);
3506
3507 return true;
3508 }
3509 #endif
3510
3511 class WSInit {
3512 public:
WSInit()3513 WSInit() {
3514 WSADATA wsaData;
3515 WSAStartup(0x0002, &wsaData);
3516 }
3517
~WSInit()3518 ~WSInit() { WSACleanup(); }
3519 };
3520
3521 static WSInit wsinit_;
3522 #endif
3523
3524 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
make_digest_authentication_header(const Request & req,const std::map<std::string,std::string> & auth,size_t cnonce_count,const std::string & cnonce,const std::string & username,const std::string & password,bool is_proxy=false)3525 inline std::pair<std::string, std::string> make_digest_authentication_header(
3526 const Request &req, const std::map<std::string, std::string> &auth,
3527 size_t cnonce_count, const std::string &cnonce, const std::string &username,
3528 const std::string &password, bool is_proxy = false) {
3529 using namespace std;
3530
3531 string nc;
3532 {
3533 stringstream ss;
3534 ss << setfill('0') << setw(8) << hex << cnonce_count;
3535 nc = ss.str();
3536 }
3537
3538 auto qop = auth.at("qop");
3539 if (qop.find("auth-int") != std::string::npos) {
3540 qop = "auth-int";
3541 } else {
3542 qop = "auth";
3543 }
3544
3545 std::string algo = "MD5";
3546 if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
3547
3548 string response;
3549 {
3550 auto H = algo == "SHA-256"
3551 ? detail::SHA_256
3552 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
3553
3554 auto A1 = username + ":" + auth.at("realm") + ":" + password;
3555
3556 auto A2 = req.method + ":" + req.path;
3557 if (qop == "auth-int") { A2 += ":" + H(req.body); }
3558
3559 response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
3560 ":" + qop + ":" + H(A2));
3561 }
3562
3563 auto field = "Digest username=\"" + username + "\", realm=\"" +
3564 auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
3565 "\", uri=\"" + req.path + "\", algorithm=" + algo +
3566 ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce +
3567 "\", response=\"" + response + "\"";
3568
3569 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3570 return std::make_pair(key, field);
3571 }
3572 #endif
3573
parse_www_authenticate(const Response & res,std::map<std::string,std::string> & auth,bool is_proxy)3574 inline bool parse_www_authenticate(const Response &res,
3575 std::map<std::string, std::string> &auth,
3576 bool is_proxy) {
3577 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
3578 if (res.has_header(auth_key)) {
3579 static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
3580 auto s = res.get_header_value(auth_key);
3581 auto pos = s.find(' ');
3582 if (pos != std::string::npos) {
3583 auto type = s.substr(0, pos);
3584 if (type == "Basic") {
3585 return false;
3586 } else if (type == "Digest") {
3587 s = s.substr(pos + 1);
3588 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
3589 for (auto i = beg; i != std::sregex_iterator(); ++i) {
3590 auto m = *i;
3591 auto key = s.substr(static_cast<size_t>(m.position(1)),
3592 static_cast<size_t>(m.length(1)));
3593 auto val = m.length(2) > 0
3594 ? s.substr(static_cast<size_t>(m.position(2)),
3595 static_cast<size_t>(m.length(2)))
3596 : s.substr(static_cast<size_t>(m.position(3)),
3597 static_cast<size_t>(m.length(3)));
3598 auth[key] = val;
3599 }
3600 return true;
3601 }
3602 }
3603 }
3604 return false;
3605 }
3606
3607 // https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
random_string(size_t length)3608 inline std::string random_string(size_t length) {
3609 auto randchar = []() -> char {
3610 const char charset[] = "0123456789"
3611 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
3612 "abcdefghijklmnopqrstuvwxyz";
3613 const size_t max_index = (sizeof(charset) - 1);
3614 return charset[static_cast<size_t>(rand()) % max_index];
3615 };
3616 std::string str(length, 0);
3617 std::generate_n(str.begin(), length, randchar);
3618 return str;
3619 }
3620
3621 class ContentProviderAdapter {
3622 public:
ContentProviderAdapter(ContentProviderWithoutLength && content_provider)3623 explicit ContentProviderAdapter(
3624 ContentProviderWithoutLength &&content_provider)
3625 : content_provider_(content_provider) {}
3626
operator ()(size_t offset,size_t,DataSink & sink)3627 bool operator()(size_t offset, size_t, DataSink &sink) {
3628 return content_provider_(offset, sink);
3629 }
3630
3631 private:
3632 ContentProviderWithoutLength content_provider_;
3633 };
3634
3635 } // namespace detail
3636
3637 // Header utilities
make_range_header(Ranges ranges)3638 inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
3639 std::string field = "bytes=";
3640 auto i = 0;
3641 for (auto r : ranges) {
3642 if (i != 0) { field += ", "; }
3643 if (r.first != -1) { field += std::to_string(r.first); }
3644 field += '-';
3645 if (r.second != -1) { field += std::to_string(r.second); }
3646 i++;
3647 }
3648 return std::make_pair("Range", std::move(field));
3649 }
3650
3651 inline std::pair<std::string, std::string>
make_basic_authentication_header(const std::string & username,const std::string & password,bool is_proxy=false)3652 make_basic_authentication_header(const std::string &username,
3653 const std::string &password,
3654 bool is_proxy = false) {
3655 auto field = "Basic " + detail::base64_encode(username + ":" + password);
3656 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3657 return std::make_pair(key, std::move(field));
3658 }
3659
3660 inline std::pair<std::string, std::string>
make_bearer_token_authentication_header(const std::string & token,bool is_proxy=false)3661 make_bearer_token_authentication_header(const std::string &token,
3662 bool is_proxy = false) {
3663 auto field = "Bearer " + token;
3664 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3665 return std::make_pair(key, std::move(field));
3666 }
3667
3668 // Request implementation
has_header(const char * key) const3669 inline bool Request::has_header(const char *key) const {
3670 return detail::has_header(headers, key);
3671 }
3672
get_header_value(const char * key,size_t id) const3673 inline std::string Request::get_header_value(const char *key, size_t id) const {
3674 return detail::get_header_value(headers, key, id, "");
3675 }
3676
3677 template <typename T>
get_header_value(const char * key,size_t id) const3678 inline T Request::get_header_value(const char *key, size_t id) const {
3679 return detail::get_header_value<T>(headers, key, id, 0);
3680 }
3681
get_header_value_count(const char * key) const3682 inline size_t Request::get_header_value_count(const char *key) const {
3683 auto r = headers.equal_range(key);
3684 return static_cast<size_t>(std::distance(r.first, r.second));
3685 }
3686
set_header(const char * key,const char * val)3687 inline void Request::set_header(const char *key, const char *val) {
3688 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
3689 headers.emplace(key, val);
3690 }
3691 }
3692
set_header(const char * key,const std::string & val)3693 inline void Request::set_header(const char *key, const std::string &val) {
3694 if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
3695 headers.emplace(key, val);
3696 }
3697 }
3698
has_param(const char * key) const3699 inline bool Request::has_param(const char *key) const {
3700 return params.find(key) != params.end();
3701 }
3702
get_param_value(const char * key,size_t id) const3703 inline std::string Request::get_param_value(const char *key, size_t id) const {
3704 auto rng = params.equal_range(key);
3705 auto it = rng.first;
3706 std::advance(it, static_cast<ssize_t>(id));
3707 if (it != rng.second) { return it->second; }
3708 return std::string();
3709 }
3710
get_param_value_count(const char * key) const3711 inline size_t Request::get_param_value_count(const char *key) const {
3712 auto r = params.equal_range(key);
3713 return static_cast<size_t>(std::distance(r.first, r.second));
3714 }
3715
is_multipart_form_data() const3716 inline bool Request::is_multipart_form_data() const {
3717 const auto &content_type = get_header_value("Content-Type");
3718 return !content_type.find("multipart/form-data");
3719 }
3720
has_file(const char * key) const3721 inline bool Request::has_file(const char *key) const {
3722 return files.find(key) != files.end();
3723 }
3724
get_file_value(const char * key) const3725 inline MultipartFormData Request::get_file_value(const char *key) const {
3726 auto it = files.find(key);
3727 if (it != files.end()) { return it->second; }
3728 return MultipartFormData();
3729 }
3730
3731 // Response implementation
has_header(const char * key) const3732 inline bool Response::has_header(const char *key) const {
3733 return headers.find(key) != headers.end();
3734 }
3735
get_header_value(const char * key,size_t id) const3736 inline std::string Response::get_header_value(const char *key,
3737 size_t id) const {
3738 return detail::get_header_value(headers, key, id, "");
3739 }
3740
3741 template <typename T>
get_header_value(const char * key,size_t id) const3742 inline T Response::get_header_value(const char *key, size_t id) const {
3743 return detail::get_header_value<T>(headers, key, id, 0);
3744 }
3745
get_header_value_count(const char * key) const3746 inline size_t Response::get_header_value_count(const char *key) const {
3747 auto r = headers.equal_range(key);
3748 return static_cast<size_t>(std::distance(r.first, r.second));
3749 }
3750
set_header(const char * key,const char * val)3751 inline void Response::set_header(const char *key, const char *val) {
3752 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
3753 headers.emplace(key, val);
3754 }
3755 }
3756
set_header(const char * key,const std::string & val)3757 inline void Response::set_header(const char *key, const std::string &val) {
3758 if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
3759 headers.emplace(key, val);
3760 }
3761 }
3762
set_redirect(const char * url,int stat)3763 inline void Response::set_redirect(const char *url, int stat) {
3764 if (!detail::has_crlf(url)) {
3765 set_header("Location", url);
3766 if (300 <= stat && stat < 400) {
3767 this->status = stat;
3768 } else {
3769 this->status = 302;
3770 }
3771 }
3772 }
3773
set_redirect(const std::string & url,int stat)3774 inline void Response::set_redirect(const std::string &url, int stat) {
3775 set_redirect(url.c_str(), stat);
3776 }
3777
set_content(const char * s,size_t n,const char * content_type)3778 inline void Response::set_content(const char *s, size_t n,
3779 const char *content_type) {
3780 body.assign(s, n);
3781
3782 auto rng = headers.equal_range("Content-Type");
3783 headers.erase(rng.first, rng.second);
3784 set_header("Content-Type", content_type);
3785 }
3786
set_content(const std::string & s,const char * content_type)3787 inline void Response::set_content(const std::string &s,
3788 const char *content_type) {
3789 set_content(s.data(), s.size(), content_type);
3790 }
3791
3792 inline void
set_content_provider(size_t in_length,const char * content_type,ContentProvider provider,const std::function<void ()> & resource_releaser)3793 Response::set_content_provider(size_t in_length, const char *content_type,
3794 ContentProvider provider,
3795 const std::function<void()> &resource_releaser) {
3796 assert(in_length > 0);
3797 set_header("Content-Type", content_type);
3798 content_length_ = in_length;
3799 content_provider_ = std::move(provider);
3800 content_provider_resource_releaser_ = resource_releaser;
3801 is_chunked_content_provider_ = false;
3802 }
3803
3804 inline void
set_content_provider(const char * content_type,ContentProviderWithoutLength provider,const std::function<void ()> & resource_releaser)3805 Response::set_content_provider(const char *content_type,
3806 ContentProviderWithoutLength provider,
3807 const std::function<void()> &resource_releaser) {
3808 set_header("Content-Type", content_type);
3809 content_length_ = 0;
3810 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
3811 content_provider_resource_releaser_ = resource_releaser;
3812 is_chunked_content_provider_ = false;
3813 }
3814
set_chunked_content_provider(const char * content_type,ContentProviderWithoutLength provider,const std::function<void ()> & resource_releaser)3815 inline void Response::set_chunked_content_provider(
3816 const char *content_type, ContentProviderWithoutLength provider,
3817 const std::function<void()> &resource_releaser) {
3818 set_header("Content-Type", content_type);
3819 content_length_ = 0;
3820 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
3821 content_provider_resource_releaser_ = resource_releaser;
3822 is_chunked_content_provider_ = true;
3823 }
3824
3825 // Rstream implementation
write(const char * ptr)3826 inline ssize_t Stream::write(const char *ptr) {
3827 return write(ptr, strlen(ptr));
3828 }
3829
write(const std::string & s)3830 inline ssize_t Stream::write(const std::string &s) {
3831 return write(s.data(), s.size());
3832 }
3833
3834 template <typename... Args>
write_format(const char * fmt,const Args &...args)3835 inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
3836 const auto bufsiz = 2048;
3837 std::array<char, bufsiz> buf;
3838
3839 #if defined(_MSC_VER) && _MSC_VER < 1900
3840 auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...);
3841 #else
3842 auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
3843 #endif
3844 if (sn <= 0) { return sn; }
3845
3846 auto n = static_cast<size_t>(sn);
3847
3848 if (n >= buf.size() - 1) {
3849 std::vector<char> glowable_buf(buf.size());
3850
3851 while (n >= glowable_buf.size() - 1) {
3852 glowable_buf.resize(glowable_buf.size() * 2);
3853 #if defined(_MSC_VER) && _MSC_VER < 1900
3854 n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
3855 glowable_buf.size() - 1, fmt,
3856 args...));
3857 #else
3858 n = static_cast<size_t>(
3859 snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
3860 #endif
3861 }
3862 return write(&glowable_buf[0], n);
3863 } else {
3864 return write(buf.data(), n);
3865 }
3866 }
3867
3868 namespace detail {
3869
3870 // Socket stream implementation
SocketStream(socket_t sock,time_t read_timeout_sec,time_t read_timeout_usec,time_t write_timeout_sec,time_t write_timeout_usec)3871 inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
3872 time_t read_timeout_usec,
3873 time_t write_timeout_sec,
3874 time_t write_timeout_usec)
3875 : sock_(sock), read_timeout_sec_(read_timeout_sec),
3876 read_timeout_usec_(read_timeout_usec),
3877 write_timeout_sec_(write_timeout_sec),
3878 write_timeout_usec_(write_timeout_usec) {}
3879
~SocketStream()3880 inline SocketStream::~SocketStream() {}
3881
is_readable() const3882 inline bool SocketStream::is_readable() const {
3883 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
3884 }
3885
is_writable() const3886 inline bool SocketStream::is_writable() const {
3887 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
3888 }
3889
read(char * ptr,size_t size)3890 inline ssize_t SocketStream::read(char *ptr, size_t size) {
3891 if (!is_readable()) { return -1; }
3892
3893 #ifdef _WIN32
3894 if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
3895 return -1;
3896 }
3897 return recv(sock_, ptr, static_cast<int>(size), CPPHTTPLIB_RECV_FLAGS);
3898 #else
3899 return handle_EINTR(
3900 [&]() { return recv(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); });
3901 #endif
3902 }
3903
write(const char * ptr,size_t size)3904 inline ssize_t SocketStream::write(const char *ptr, size_t size) {
3905 if (!is_writable()) { return -1; }
3906
3907 #ifdef _WIN32
3908 if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
3909 return -1;
3910 }
3911 return send(sock_, ptr, static_cast<int>(size), CPPHTTPLIB_SEND_FLAGS);
3912 #else
3913 return handle_EINTR(
3914 [&]() { return send(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); });
3915 #endif
3916 }
3917
get_remote_ip_and_port(std::string & ip,int & port) const3918 inline void SocketStream::get_remote_ip_and_port(std::string &ip,
3919 int &port) const {
3920 return detail::get_remote_ip_and_port(sock_, ip, port);
3921 }
3922
socket() const3923 inline socket_t SocketStream::socket() const { return sock_; }
3924
3925 // Buffer stream implementation
is_readable() const3926 inline bool BufferStream::is_readable() const { return true; }
3927
is_writable() const3928 inline bool BufferStream::is_writable() const { return true; }
3929
read(char * ptr,size_t size)3930 inline ssize_t BufferStream::read(char *ptr, size_t size) {
3931 #if defined(_MSC_VER) && _MSC_VER <= 1900
3932 auto len_read = buffer._Copy_s(ptr, size, size, position);
3933 #else
3934 auto len_read = buffer.copy(ptr, size, position);
3935 #endif
3936 position += static_cast<size_t>(len_read);
3937 return static_cast<ssize_t>(len_read);
3938 }
3939
write(const char * ptr,size_t size)3940 inline ssize_t BufferStream::write(const char *ptr, size_t size) {
3941 buffer.append(ptr, size);
3942 return static_cast<ssize_t>(size);
3943 }
3944
get_remote_ip_and_port(std::string &,int &) const3945 inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
3946 int & /*port*/) const {}
3947
socket() const3948 inline socket_t BufferStream::socket() const { return 0; }
3949
get_buffer() const3950 inline const std::string &BufferStream::get_buffer() const { return buffer; }
3951
3952 } // namespace detail
3953
3954 // HTTP server implementation
Server()3955 inline Server::Server()
3956 : new_task_queue(
3957 [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
3958 svr_sock_(INVALID_SOCKET), is_running_(false) {
3959 #ifndef _WIN32
3960 signal(SIGPIPE, SIG_IGN);
3961 #endif
3962 }
3963
~Server()3964 inline Server::~Server() {}
3965
Get(const char * pattern,Handler handler)3966 inline Server &Server::Get(const char *pattern, Handler handler) {
3967 get_handlers_.push_back(
3968 std::make_pair(std::regex(pattern), std::move(handler)));
3969 return *this;
3970 }
3971
Post(const char * pattern,Handler handler)3972 inline Server &Server::Post(const char *pattern, Handler handler) {
3973 post_handlers_.push_back(
3974 std::make_pair(std::regex(pattern), std::move(handler)));
3975 return *this;
3976 }
3977
Post(const char * pattern,HandlerWithContentReader handler)3978 inline Server &Server::Post(const char *pattern,
3979 HandlerWithContentReader handler) {
3980 post_handlers_for_content_reader_.push_back(
3981 std::make_pair(std::regex(pattern), std::move(handler)));
3982 return *this;
3983 }
3984
Put(const char * pattern,Handler handler)3985 inline Server &Server::Put(const char *pattern, Handler handler) {
3986 put_handlers_.push_back(
3987 std::make_pair(std::regex(pattern), std::move(handler)));
3988 return *this;
3989 }
3990
Put(const char * pattern,HandlerWithContentReader handler)3991 inline Server &Server::Put(const char *pattern,
3992 HandlerWithContentReader handler) {
3993 put_handlers_for_content_reader_.push_back(
3994 std::make_pair(std::regex(pattern), std::move(handler)));
3995 return *this;
3996 }
3997
Patch(const char * pattern,Handler handler)3998 inline Server &Server::Patch(const char *pattern, Handler handler) {
3999 patch_handlers_.push_back(
4000 std::make_pair(std::regex(pattern), std::move(handler)));
4001 return *this;
4002 }
4003
Patch(const char * pattern,HandlerWithContentReader handler)4004 inline Server &Server::Patch(const char *pattern,
4005 HandlerWithContentReader handler) {
4006 patch_handlers_for_content_reader_.push_back(
4007 std::make_pair(std::regex(pattern), std::move(handler)));
4008 return *this;
4009 }
4010
Delete(const char * pattern,Handler handler)4011 inline Server &Server::Delete(const char *pattern, Handler handler) {
4012 delete_handlers_.push_back(
4013 std::make_pair(std::regex(pattern), std::move(handler)));
4014 return *this;
4015 }
4016
Delete(const char * pattern,HandlerWithContentReader handler)4017 inline Server &Server::Delete(const char *pattern,
4018 HandlerWithContentReader handler) {
4019 delete_handlers_for_content_reader_.push_back(
4020 std::make_pair(std::regex(pattern), std::move(handler)));
4021 return *this;
4022 }
4023
Options(const char * pattern,Handler handler)4024 inline Server &Server::Options(const char *pattern, Handler handler) {
4025 options_handlers_.push_back(
4026 std::make_pair(std::regex(pattern), std::move(handler)));
4027 return *this;
4028 }
4029
set_base_dir(const char * dir,const char * mount_point)4030 inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
4031 return set_mount_point(mount_point, dir);
4032 }
4033
set_mount_point(const char * mount_point,const char * dir,Headers headers)4034 inline bool Server::set_mount_point(const char *mount_point, const char *dir,
4035 Headers headers) {
4036 if (detail::is_dir(dir)) {
4037 std::string mnt = mount_point ? mount_point : "/";
4038 if (!mnt.empty() && mnt[0] == '/') {
4039 base_dirs_.push_back({mnt, dir, std::move(headers)});
4040 return true;
4041 }
4042 }
4043 return false;
4044 }
4045
remove_mount_point(const char * mount_point)4046 inline bool Server::remove_mount_point(const char *mount_point) {
4047 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
4048 if (it->mount_point == mount_point) {
4049 base_dirs_.erase(it);
4050 return true;
4051 }
4052 }
4053 return false;
4054 }
4055
set_file_extension_and_mimetype_mapping(const char * ext,const char * mime)4056 inline void Server::set_file_extension_and_mimetype_mapping(const char *ext,
4057 const char *mime) {
4058 file_extension_and_mimetype_map_[ext] = mime;
4059 }
4060
set_file_request_handler(Handler handler)4061 inline void Server::set_file_request_handler(Handler handler) {
4062 file_request_handler_ = std::move(handler);
4063 }
4064
set_error_handler(Handler handler)4065 inline void Server::set_error_handler(Handler handler) {
4066 error_handler_ = std::move(handler);
4067 }
4068
set_tcp_nodelay(bool on)4069 inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
4070
set_socket_options(SocketOptions socket_options)4071 inline void Server::set_socket_options(SocketOptions socket_options) {
4072 socket_options_ = std::move(socket_options);
4073 }
4074
set_logger(Logger logger)4075 inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
4076
4077 inline void
set_expect_100_continue_handler(Expect100ContinueHandler handler)4078 Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
4079 expect_100_continue_handler_ = std::move(handler);
4080 }
4081
set_keep_alive_max_count(size_t count)4082 inline void Server::set_keep_alive_max_count(size_t count) {
4083 keep_alive_max_count_ = count;
4084 }
4085
set_keep_alive_timeout(time_t sec)4086 inline void Server::set_keep_alive_timeout(time_t sec) {
4087 keep_alive_timeout_sec_ = sec;
4088 }
4089
set_read_timeout(time_t sec,time_t usec)4090 inline void Server::set_read_timeout(time_t sec, time_t usec) {
4091 read_timeout_sec_ = sec;
4092 read_timeout_usec_ = usec;
4093 }
4094
set_write_timeout(time_t sec,time_t usec)4095 inline void Server::set_write_timeout(time_t sec, time_t usec) {
4096 write_timeout_sec_ = sec;
4097 write_timeout_usec_ = usec;
4098 }
4099
set_idle_interval(time_t sec,time_t usec)4100 inline void Server::set_idle_interval(time_t sec, time_t usec) {
4101 idle_interval_sec_ = sec;
4102 idle_interval_usec_ = usec;
4103 }
4104
set_payload_max_length(size_t length)4105 inline void Server::set_payload_max_length(size_t length) {
4106 payload_max_length_ = length;
4107 }
4108
bind_to_port(const char * host,int port,int socket_flags)4109 inline bool Server::bind_to_port(const char *host, int port, int socket_flags) {
4110 if (bind_internal(host, port, socket_flags) < 0) return false;
4111 return true;
4112 }
bind_to_any_port(const char * host,int socket_flags)4113 inline int Server::bind_to_any_port(const char *host, int socket_flags) {
4114 return bind_internal(host, 0, socket_flags);
4115 }
4116
listen_after_bind()4117 inline bool Server::listen_after_bind() { return listen_internal(); }
4118
listen(const char * host,int port,int socket_flags)4119 inline bool Server::listen(const char *host, int port, int socket_flags) {
4120 return bind_to_port(host, port, socket_flags) && listen_internal();
4121 }
4122
is_running() const4123 inline bool Server::is_running() const { return is_running_; }
4124
stop()4125 inline void Server::stop() {
4126 if (is_running_) {
4127 assert(svr_sock_ != INVALID_SOCKET);
4128 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
4129 detail::shutdown_socket(sock);
4130 detail::close_socket(sock);
4131 }
4132 }
4133
parse_request_line(const char * s,Request & req)4134 inline bool Server::parse_request_line(const char *s, Request &req) {
4135 const static std::regex re(
4136 "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
4137 "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n");
4138
4139 std::cmatch m;
4140 if (std::regex_match(s, m, re)) {
4141 req.version = std::string(m[5]);
4142 req.method = std::string(m[1]);
4143 req.target = std::string(m[2]);
4144 req.path = detail::decode_url(m[3], false);
4145
4146 // Parse query text
4147 auto len = std::distance(m[4].first, m[4].second);
4148 if (len > 0) { detail::parse_query_text(m[4], req.params); }
4149
4150 return true;
4151 }
4152
4153 return false;
4154 }
4155
write_response(Stream & strm,bool close_connection,const Request & req,Response & res)4156 inline bool Server::write_response(Stream &strm, bool close_connection,
4157 const Request &req, Response &res) {
4158 return write_response_core(strm, close_connection, req, res, false);
4159 }
4160
write_response_with_content(Stream & strm,bool close_connection,const Request & req,Response & res)4161 inline bool Server::write_response_with_content(Stream &strm,
4162 bool close_connection,
4163 const Request &req,
4164 Response &res) {
4165 return write_response_core(strm, close_connection, req, res, true);
4166 }
4167
write_response_core(Stream & strm,bool close_connection,const Request & req,Response & res,bool need_apply_ranges)4168 inline bool Server::write_response_core(Stream &strm, bool close_connection,
4169 const Request &req, Response &res,
4170 bool need_apply_ranges) {
4171 assert(res.status != -1);
4172
4173 if (400 <= res.status && error_handler_) { error_handler_(req, res); }
4174
4175 std::string content_type;
4176 std::string boundary;
4177 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
4178
4179 // Headers
4180 if (close_connection || req.get_header_value("Connection") == "close") {
4181 res.set_header("Connection", "close");
4182 } else {
4183 std::stringstream ss;
4184 ss << "timeout=" << keep_alive_timeout_sec_
4185 << ", max=" << keep_alive_max_count_;
4186 res.set_header("Keep-Alive", ss.str());
4187 }
4188
4189 if (!res.has_header("Content-Type") &&
4190 (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
4191 res.set_header("Content-Type", "text/plain");
4192 }
4193
4194 if (!res.has_header("Content-Length") && res.body.empty() &&
4195 !res.content_length_ && !res.content_provider_) {
4196 res.set_header("Content-Length", "0");
4197 }
4198
4199 if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
4200 res.set_header("Accept-Ranges", "bytes");
4201 }
4202
4203 detail::BufferStream bstrm;
4204
4205 // Response line
4206 if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
4207 detail::status_message(res.status))) {
4208 return false;
4209 }
4210
4211 if (!detail::write_headers(bstrm, res, Headers())) { return false; }
4212
4213 // Flush buffer
4214 auto &data = bstrm.get_buffer();
4215 strm.write(data.data(), data.size());
4216
4217 // Body
4218 auto ret = true;
4219 if (req.method != "HEAD") {
4220 if (!res.body.empty()) {
4221 if (!strm.write(res.body)) { ret = false; }
4222 } else if (res.content_provider_) {
4223 if (!write_content_with_provider(strm, req, res, boundary,
4224 content_type)) {
4225 ret = false;
4226 }
4227 }
4228 }
4229
4230 // Log
4231 if (logger_) { logger_(req, res); }
4232
4233 return ret;
4234 }
4235
4236 inline bool
write_content_with_provider(Stream & strm,const Request & req,Response & res,const std::string & boundary,const std::string & content_type)4237 Server::write_content_with_provider(Stream &strm, const Request &req,
4238 Response &res, const std::string &boundary,
4239 const std::string &content_type) {
4240 auto is_shutting_down = [this]() {
4241 return this->svr_sock_ == INVALID_SOCKET;
4242 };
4243
4244 if (res.content_length_ > 0) {
4245 if (req.ranges.empty()) {
4246 return detail::write_content(strm, res.content_provider_, 0,
4247 res.content_length_, is_shutting_down);
4248 } else if (req.ranges.size() == 1) {
4249 auto offsets =
4250 detail::get_range_offset_and_length(req, res.content_length_, 0);
4251 auto offset = offsets.first;
4252 auto length = offsets.second;
4253 return detail::write_content(strm, res.content_provider_, offset, length,
4254 is_shutting_down);
4255 } else {
4256 return detail::write_multipart_ranges_data(
4257 strm, req, res, boundary, content_type, is_shutting_down);
4258 }
4259 } else {
4260 if (res.is_chunked_content_provider_) {
4261 auto type = detail::encoding_type(req, res);
4262
4263 std::unique_ptr<detail::compressor> compressor;
4264 if (type == detail::EncodingType::Gzip) {
4265 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4266 compressor = detail::make_unique<detail::gzip_compressor>();
4267 #endif
4268 } else if (type == detail::EncodingType::Brotli) {
4269 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4270 compressor = detail::make_unique<detail::brotli_compressor>();
4271 #endif
4272 } else {
4273 compressor = detail::make_unique<detail::nocompressor>();
4274 }
4275 assert(compressor != nullptr);
4276
4277 return detail::write_content_chunked(strm, res.content_provider_,
4278 is_shutting_down, *compressor);
4279 } else {
4280 return detail::write_content_without_length(strm, res.content_provider_,
4281 is_shutting_down);
4282 }
4283 }
4284 return true;
4285 }
4286
read_content(Stream & strm,Request & req,Response & res)4287 inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
4288 MultipartFormDataMap::iterator cur;
4289 if (read_content_core(
4290 strm, req, res,
4291 // Regular
4292 [&](const char *buf, size_t n) {
4293 if (req.body.size() + n > req.body.max_size()) { return false; }
4294 req.body.append(buf, n);
4295 return true;
4296 },
4297 // Multipart
4298 [&](const MultipartFormData &file) {
4299 cur = req.files.emplace(file.name, file);
4300 return true;
4301 },
4302 [&](const char *buf, size_t n) {
4303 auto &content = cur->second.content;
4304 if (content.size() + n > content.max_size()) { return false; }
4305 content.append(buf, n);
4306 return true;
4307 })) {
4308 const auto &content_type = req.get_header_value("Content-Type");
4309 if (!content_type.find("application/x-www-form-urlencoded")) {
4310 detail::parse_query_text(req.body, req.params);
4311 }
4312 return true;
4313 }
4314 return false;
4315 }
4316
read_content_with_content_receiver(Stream & strm,Request & req,Response & res,ContentReceiver receiver,MultipartContentHeader multipart_header,ContentReceiver multipart_receiver)4317 inline bool Server::read_content_with_content_receiver(
4318 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
4319 MultipartContentHeader multipart_header,
4320 ContentReceiver multipart_receiver) {
4321 return read_content_core(strm, req, res, std::move(receiver),
4322 std::move(multipart_header),
4323 std::move(multipart_receiver));
4324 }
4325
read_content_core(Stream & strm,Request & req,Response & res,ContentReceiver receiver,MultipartContentHeader mulitpart_header,ContentReceiver multipart_receiver)4326 inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
4327 ContentReceiver receiver,
4328 MultipartContentHeader mulitpart_header,
4329 ContentReceiver multipart_receiver) {
4330 detail::MultipartFormDataParser multipart_form_data_parser;
4331 ContentReceiverWithProgress out;
4332
4333 if (req.is_multipart_form_data()) {
4334 const auto &content_type = req.get_header_value("Content-Type");
4335 std::string boundary;
4336 if (!detail::parse_multipart_boundary(content_type, boundary)) {
4337 res.status = 400;
4338 return false;
4339 }
4340
4341 multipart_form_data_parser.set_boundary(std::move(boundary));
4342 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
4343 /* For debug
4344 size_t pos = 0;
4345 while (pos < n) {
4346 auto read_size = std::min<size_t>(1, n - pos);
4347 auto ret = multipart_form_data_parser.parse(
4348 buf + pos, read_size, multipart_receiver, mulitpart_header);
4349 if (!ret) { return false; }
4350 pos += read_size;
4351 }
4352 return true;
4353 */
4354 return multipart_form_data_parser.parse(buf, n, multipart_receiver,
4355 mulitpart_header);
4356 };
4357 } else {
4358 out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
4359 uint64_t /*len*/) { return receiver(buf, n); };
4360 }
4361
4362 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
4363 return true;
4364 }
4365
4366 if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
4367 out, true)) {
4368 return false;
4369 }
4370
4371 if (req.is_multipart_form_data()) {
4372 if (!multipart_form_data_parser.is_valid()) {
4373 res.status = 400;
4374 return false;
4375 }
4376 }
4377
4378 return true;
4379 }
4380
handle_file_request(Request & req,Response & res,bool head)4381 inline bool Server::handle_file_request(Request &req, Response &res,
4382 bool head) {
4383 for (const auto &entry : base_dirs_) {
4384 // Prefix match
4385 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
4386 std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
4387 if (detail::is_valid_path(sub_path)) {
4388 auto path = entry.base_dir + sub_path;
4389 if (path.back() == '/') { path += "index.html"; }
4390
4391 if (detail::is_file(path)) {
4392 detail::read_file(path, res.body);
4393 auto type =
4394 detail::find_content_type(path, file_extension_and_mimetype_map_);
4395 if (type) { res.set_header("Content-Type", type); }
4396 for (const auto &kv : entry.headers) {
4397 res.set_header(kv.first.c_str(), kv.second);
4398 }
4399 res.status = 200;
4400 if (!head && file_request_handler_) {
4401 file_request_handler_(req, res);
4402 }
4403 return true;
4404 }
4405 }
4406 }
4407 }
4408 return false;
4409 }
4410
4411 inline socket_t
create_server_socket(const char * host,int port,int socket_flags,SocketOptions socket_options) const4412 Server::create_server_socket(const char *host, int port, int socket_flags,
4413 SocketOptions socket_options) const {
4414 return detail::create_socket(
4415 host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
4416 [](socket_t sock, struct addrinfo &ai) -> bool {
4417 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
4418 return false;
4419 }
4420 if (::listen(sock, 5)) { // Listen through 5 channels
4421 return false;
4422 }
4423 return true;
4424 });
4425 }
4426
bind_internal(const char * host,int port,int socket_flags)4427 inline int Server::bind_internal(const char *host, int port, int socket_flags) {
4428 if (!is_valid()) { return -1; }
4429
4430 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
4431 if (svr_sock_ == INVALID_SOCKET) { return -1; }
4432
4433 if (port == 0) {
4434 struct sockaddr_storage addr;
4435 socklen_t addr_len = sizeof(addr);
4436 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
4437 &addr_len) == -1) {
4438 return -1;
4439 }
4440 if (addr.ss_family == AF_INET) {
4441 return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
4442 } else if (addr.ss_family == AF_INET6) {
4443 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
4444 } else {
4445 return -1;
4446 }
4447 } else {
4448 return port;
4449 }
4450 }
4451
listen_internal()4452 inline bool Server::listen_internal() {
4453 auto ret = true;
4454 is_running_ = true;
4455
4456 {
4457 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
4458
4459 while (svr_sock_ != INVALID_SOCKET) {
4460 #ifndef _WIN32
4461 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
4462 #endif
4463 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
4464 idle_interval_usec_);
4465 if (val == 0) { // Timeout
4466 task_queue->on_idle();
4467 continue;
4468 }
4469 #ifndef _WIN32
4470 }
4471 #endif
4472 socket_t sock = accept(svr_sock_, nullptr, nullptr);
4473
4474 if (sock == INVALID_SOCKET) {
4475 if (errno == EMFILE) {
4476 // The per-process limit of open file descriptors has been reached.
4477 // Try to accept new connections after a short sleep.
4478 std::this_thread::sleep_for(std::chrono::milliseconds(1));
4479 continue;
4480 }
4481 if (svr_sock_ != INVALID_SOCKET) {
4482 detail::close_socket(svr_sock_);
4483 ret = false;
4484 } else {
4485 ; // The server socket was closed by user.
4486 }
4487 break;
4488 }
4489
4490 #if __cplusplus > 201703L
4491 task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
4492 #else
4493 task_queue->enqueue([=]() { process_and_close_socket(sock); });
4494 #endif
4495 }
4496
4497 task_queue->shutdown();
4498 }
4499
4500 is_running_ = false;
4501 return ret;
4502 }
4503
routing(Request & req,Response & res,Stream & strm)4504 inline bool Server::routing(Request &req, Response &res, Stream &strm) {
4505 // File handler
4506 bool is_head_request = req.method == "HEAD";
4507 if ((req.method == "GET" || is_head_request) &&
4508 handle_file_request(req, res, is_head_request)) {
4509 return true;
4510 }
4511
4512 if (detail::expect_content(req)) {
4513 // Content reader handler
4514 {
4515 ContentReader reader(
4516 [&](ContentReceiver receiver) {
4517 return read_content_with_content_receiver(
4518 strm, req, res, std::move(receiver), nullptr, nullptr);
4519 },
4520 [&](MultipartContentHeader header, ContentReceiver receiver) {
4521 return read_content_with_content_receiver(strm, req, res, nullptr,
4522 std::move(header),
4523 std::move(receiver));
4524 });
4525
4526 if (req.method == "POST") {
4527 if (dispatch_request_for_content_reader(
4528 req, res, std::move(reader),
4529 post_handlers_for_content_reader_)) {
4530 return true;
4531 }
4532 } else if (req.method == "PUT") {
4533 if (dispatch_request_for_content_reader(
4534 req, res, std::move(reader),
4535 put_handlers_for_content_reader_)) {
4536 return true;
4537 }
4538 } else if (req.method == "PATCH") {
4539 if (dispatch_request_for_content_reader(
4540 req, res, std::move(reader),
4541 patch_handlers_for_content_reader_)) {
4542 return true;
4543 }
4544 } else if (req.method == "DELETE") {
4545 if (dispatch_request_for_content_reader(
4546 req, res, std::move(reader),
4547 delete_handlers_for_content_reader_)) {
4548 return true;
4549 }
4550 }
4551 }
4552
4553 // Read content into `req.body`
4554 if (!read_content(strm, req, res)) { return false; }
4555 }
4556
4557 // Regular handler
4558 if (req.method == "GET" || req.method == "HEAD") {
4559 return dispatch_request(req, res, get_handlers_);
4560 } else if (req.method == "POST") {
4561 return dispatch_request(req, res, post_handlers_);
4562 } else if (req.method == "PUT") {
4563 return dispatch_request(req, res, put_handlers_);
4564 } else if (req.method == "DELETE") {
4565 return dispatch_request(req, res, delete_handlers_);
4566 } else if (req.method == "OPTIONS") {
4567 return dispatch_request(req, res, options_handlers_);
4568 } else if (req.method == "PATCH") {
4569 return dispatch_request(req, res, patch_handlers_);
4570 }
4571
4572 res.status = 400;
4573 return false;
4574 }
4575
dispatch_request(Request & req,Response & res,const Handlers & handlers)4576 inline bool Server::dispatch_request(Request &req, Response &res,
4577 const Handlers &handlers) {
4578 try {
4579 for (const auto &x : handlers) {
4580 const auto &pattern = x.first;
4581 const auto &handler = x.second;
4582
4583 if (std::regex_match(req.path, req.matches, pattern)) {
4584 handler(req, res);
4585 return true;
4586 }
4587 }
4588 } catch (const std::exception &ex) {
4589 res.status = 500;
4590 res.set_header("EXCEPTION_WHAT", ex.what());
4591 } catch (...) {
4592 res.status = 500;
4593 res.set_header("EXCEPTION_WHAT", "UNKNOWN");
4594 }
4595 return false;
4596 }
4597
apply_ranges(const Request & req,Response & res,std::string & content_type,std::string & boundary)4598 inline void Server::apply_ranges(const Request &req, Response &res,
4599 std::string &content_type,
4600 std::string &boundary) {
4601 if (req.ranges.size() > 1) {
4602 boundary = detail::make_multipart_data_boundary();
4603
4604 auto it = res.headers.find("Content-Type");
4605 if (it != res.headers.end()) {
4606 content_type = it->second;
4607 res.headers.erase(it);
4608 }
4609
4610 res.headers.emplace("Content-Type",
4611 "multipart/byteranges; boundary=" + boundary);
4612 }
4613
4614 auto type = detail::encoding_type(req, res);
4615
4616 if (res.body.empty()) {
4617 if (res.content_length_ > 0) {
4618 size_t length = 0;
4619 if (req.ranges.empty()) {
4620 length = res.content_length_;
4621 } else if (req.ranges.size() == 1) {
4622 auto offsets =
4623 detail::get_range_offset_and_length(req, res.content_length_, 0);
4624 auto offset = offsets.first;
4625 length = offsets.second;
4626 auto content_range = detail::make_content_range_header_field(
4627 offset, length, res.content_length_);
4628 res.set_header("Content-Range", content_range);
4629 } else {
4630 length = detail::get_multipart_ranges_data_length(req, res, boundary,
4631 content_type);
4632 }
4633 res.set_header("Content-Length", std::to_string(length));
4634 } else {
4635 if (res.content_provider_) {
4636 if (res.is_chunked_content_provider_) {
4637 res.set_header("Transfer-Encoding", "chunked");
4638 if (type == detail::EncodingType::Gzip) {
4639 res.set_header("Content-Encoding", "gzip");
4640 } else if (type == detail::EncodingType::Brotli) {
4641 res.set_header("Content-Encoding", "br");
4642 }
4643 }
4644 }
4645 }
4646 } else {
4647 if (req.ranges.empty()) {
4648 ;
4649 } else if (req.ranges.size() == 1) {
4650 auto offsets =
4651 detail::get_range_offset_and_length(req, res.body.size(), 0);
4652 auto offset = offsets.first;
4653 auto length = offsets.second;
4654 auto content_range = detail::make_content_range_header_field(
4655 offset, length, res.body.size());
4656 res.set_header("Content-Range", content_range);
4657 if (offset < res.body.size()) {
4658 res.body = res.body.substr(offset, length);
4659 } else {
4660 res.body.clear();
4661 res.status = 416;
4662 }
4663 } else {
4664 std::string data;
4665 if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
4666 data)) {
4667 res.body.swap(data);
4668 } else {
4669 res.body.clear();
4670 res.status = 416;
4671 }
4672 }
4673
4674 if (type != detail::EncodingType::None) {
4675 std::unique_ptr<detail::compressor> compressor;
4676 std::string content_encoding;
4677
4678 if (type == detail::EncodingType::Gzip) {
4679 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4680 compressor = detail::make_unique<detail::gzip_compressor>();
4681 content_encoding = "gzip";
4682 #endif
4683 } else if (type == detail::EncodingType::Brotli) {
4684 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4685 compressor = detail::make_unique<detail::brotli_compressor>();
4686 content_encoding = "br";
4687 #endif
4688 }
4689
4690 if (compressor) {
4691 std::string compressed;
4692 if (compressor->compress(res.body.data(), res.body.size(), true,
4693 [&](const char *data, size_t data_len) {
4694 compressed.append(data, data_len);
4695 return true;
4696 })) {
4697 res.body.swap(compressed);
4698 res.set_header("Content-Encoding", content_encoding);
4699 }
4700 }
4701 }
4702
4703 auto length = std::to_string(res.body.size());
4704 res.set_header("Content-Length", length);
4705 }
4706 }
4707
dispatch_request_for_content_reader(Request & req,Response & res,ContentReader content_reader,const HandlersForContentReader & handlers)4708 inline bool Server::dispatch_request_for_content_reader(
4709 Request &req, Response &res, ContentReader content_reader,
4710 const HandlersForContentReader &handlers) {
4711 for (const auto &x : handlers) {
4712 const auto &pattern = x.first;
4713 const auto &handler = x.second;
4714
4715 if (std::regex_match(req.path, req.matches, pattern)) {
4716 handler(req, res, content_reader);
4717 return true;
4718 }
4719 }
4720 return false;
4721 }
4722
4723 inline bool
process_request(Stream & strm,bool close_connection,bool & connection_closed,const std::function<void (Request &)> & setup_request)4724 Server::process_request(Stream &strm, bool close_connection,
4725 bool &connection_closed,
4726 const std::function<void(Request &)> &setup_request) {
4727 std::array<char, 2048> buf{};
4728
4729 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
4730
4731 // Connection has been closed on client
4732 if (!line_reader.getline()) { return false; }
4733
4734 Request req;
4735 Response res;
4736
4737 res.version = "HTTP/1.1";
4738
4739 #ifdef _WIN32
4740 // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).
4741 #else
4742 #ifndef CPPHTTPLIB_USE_POLL
4743 // Socket file descriptor exceeded FD_SETSIZE...
4744 if (strm.socket() >= FD_SETSIZE) {
4745 Headers dummy;
4746 detail::read_headers(strm, dummy);
4747 res.status = 500;
4748 return write_response(strm, close_connection, req, res);
4749 }
4750 #endif
4751 #endif
4752
4753 // Check if the request URI doesn't exceed the limit
4754 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
4755 Headers dummy;
4756 detail::read_headers(strm, dummy);
4757 res.status = 414;
4758 return write_response(strm, close_connection, req, res);
4759 }
4760
4761 // Request line and headers
4762 if (!parse_request_line(line_reader.ptr(), req) ||
4763 !detail::read_headers(strm, req.headers)) {
4764 res.status = 400;
4765 return write_response(strm, close_connection, req, res);
4766 }
4767
4768 if (req.get_header_value("Connection") == "close") {
4769 connection_closed = true;
4770 }
4771
4772 if (req.version == "HTTP/1.0" &&
4773 req.get_header_value("Connection") != "Keep-Alive") {
4774 connection_closed = true;
4775 }
4776
4777 strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
4778 req.set_header("REMOTE_ADDR", req.remote_addr);
4779 req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
4780
4781 if (req.has_header("Range")) {
4782 const auto &range_header_value = req.get_header_value("Range");
4783 if (!detail::parse_range_header(range_header_value, req.ranges)) {
4784 res.status = 416;
4785 return write_response(strm, close_connection, req, res);
4786 }
4787 }
4788
4789 if (setup_request) { setup_request(req); }
4790
4791 if (req.get_header_value("Expect") == "100-continue") {
4792 auto status = 100;
4793 if (expect_100_continue_handler_) {
4794 status = expect_100_continue_handler_(req, res);
4795 }
4796 switch (status) {
4797 case 100:
4798 case 417:
4799 strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
4800 detail::status_message(status));
4801 break;
4802 default: return write_response(strm, close_connection, req, res);
4803 }
4804 }
4805
4806 // Rounting
4807 if (routing(req, res, strm)) {
4808 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
4809 return write_response_with_content(strm, close_connection, req, res);
4810 } else {
4811 if (res.status == -1) { res.status = 404; }
4812 return write_response(strm, close_connection, req, res);
4813 }
4814 }
4815
is_valid() const4816 inline bool Server::is_valid() const { return true; }
4817
process_and_close_socket(socket_t sock)4818 inline bool Server::process_and_close_socket(socket_t sock) {
4819 auto ret = detail::process_server_socket(
4820 sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
4821 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
4822 [this](Stream &strm, bool close_connection, bool &connection_closed) {
4823 return process_request(strm, close_connection, connection_closed,
4824 nullptr);
4825 });
4826
4827 detail::shutdown_socket(sock);
4828 detail::close_socket(sock);
4829 return ret;
4830 }
4831
4832 // HTTP client implementation
ClientImpl(const std::string & host)4833 inline ClientImpl::ClientImpl(const std::string &host)
4834 : ClientImpl(host, 80, std::string(), std::string()) {}
4835
ClientImpl(const std::string & host,int port)4836 inline ClientImpl::ClientImpl(const std::string &host, int port)
4837 : ClientImpl(host, port, std::string(), std::string()) {}
4838
ClientImpl(const std::string & host,int port,const std::string & client_cert_path,const std::string & client_key_path)4839 inline ClientImpl::ClientImpl(const std::string &host, int port,
4840 const std::string &client_cert_path,
4841 const std::string &client_key_path)
4842 // : (Error::Success), host_(host), port_(port),
4843 : host_(host), port_(port),
4844 host_and_port_(host_ + ":" + std::to_string(port_)),
4845 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
4846
~ClientImpl()4847 inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
4848
is_valid() const4849 inline bool ClientImpl::is_valid() const { return true; }
4850
copy_settings(const ClientImpl & rhs)4851 inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
4852 client_cert_path_ = rhs.client_cert_path_;
4853 client_key_path_ = rhs.client_key_path_;
4854 connection_timeout_sec_ = rhs.connection_timeout_sec_;
4855 read_timeout_sec_ = rhs.read_timeout_sec_;
4856 read_timeout_usec_ = rhs.read_timeout_usec_;
4857 write_timeout_sec_ = rhs.write_timeout_sec_;
4858 write_timeout_usec_ = rhs.write_timeout_usec_;
4859 basic_auth_username_ = rhs.basic_auth_username_;
4860 basic_auth_password_ = rhs.basic_auth_password_;
4861 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
4862 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4863 digest_auth_username_ = rhs.digest_auth_username_;
4864 digest_auth_password_ = rhs.digest_auth_password_;
4865 #endif
4866 keep_alive_ = rhs.keep_alive_;
4867 follow_location_ = rhs.follow_location_;
4868 tcp_nodelay_ = rhs.tcp_nodelay_;
4869 socket_options_ = rhs.socket_options_;
4870 compress_ = rhs.compress_;
4871 decompress_ = rhs.decompress_;
4872 interface_ = rhs.interface_;
4873 proxy_host_ = rhs.proxy_host_;
4874 proxy_port_ = rhs.proxy_port_;
4875 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
4876 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
4877 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
4878 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4879 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
4880 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
4881 #endif
4882 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4883 server_certificate_verification_ = rhs.server_certificate_verification_;
4884 #endif
4885 logger_ = rhs.logger_;
4886 }
4887
create_client_socket(Error & error) const4888 inline socket_t ClientImpl::create_client_socket(Error &error) const {
4889 if (!proxy_host_.empty() && proxy_port_ != -1) {
4890 return detail::create_client_socket(
4891 proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_,
4892 connection_timeout_sec_, connection_timeout_usec_, interface_, error);
4893 }
4894 return detail::create_client_socket(
4895 host_.c_str(), port_, tcp_nodelay_, socket_options_,
4896 connection_timeout_sec_, connection_timeout_usec_, interface_, error);
4897 }
4898
create_and_connect_socket(Socket & socket,Error & error)4899 inline bool ClientImpl::create_and_connect_socket(Socket &socket,
4900 Error &error) {
4901 auto sock = create_client_socket(error);
4902 if (sock == INVALID_SOCKET) { return false; }
4903 socket.sock = sock;
4904 return true;
4905 }
4906
shutdown_ssl(Socket &,bool)4907 inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
4908 bool /*shutdown_gracefully*/) {
4909 // If there are any requests in flight from threads other than us, then it's
4910 // a thread-unsafe race because individual ssl* objects are not thread-safe.
4911 assert(socket_requests_in_flight_ == 0 ||
4912 socket_requests_are_from_thread_ == std::this_thread::get_id());
4913 }
4914
shutdown_socket(Socket & socket)4915 inline void ClientImpl::shutdown_socket(Socket &socket) {
4916 if (socket.sock == INVALID_SOCKET) { return; }
4917 detail::shutdown_socket(socket.sock);
4918 }
4919
close_socket(Socket & socket)4920 inline void ClientImpl::close_socket(Socket &socket) {
4921 // If there are requests in flight in another thread, usually closing
4922 // the socket will be fine and they will simply receive an error when
4923 // using the closed socket, but it is still a bug since rarely the OS
4924 // may reassign the socket id to be used for a new socket, and then
4925 // suddenly they will be operating on a live socket that is different
4926 // than the one they intended!
4927 assert(socket_requests_in_flight_ == 0 ||
4928 socket_requests_are_from_thread_ == std::this_thread::get_id());
4929
4930 // It is also a bug if this happens while SSL is still active
4931 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4932 assert(socket.ssl == nullptr);
4933 #endif
4934 if (socket.sock == INVALID_SOCKET) { return; }
4935 detail::close_socket(socket.sock);
4936 socket.sock = INVALID_SOCKET;
4937 }
4938
lock_socket_and_shutdown_and_close()4939 inline void ClientImpl::lock_socket_and_shutdown_and_close() {
4940 std::lock_guard<std::mutex> guard(socket_mutex_);
4941 shutdown_ssl(socket_, true);
4942 shutdown_socket(socket_);
4943 close_socket(socket_);
4944 }
4945
read_response_line(Stream & strm,Response & res)4946 inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
4947 std::array<char, 2048> buf;
4948
4949 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
4950
4951 if (!line_reader.getline()) { return false; }
4952
4953 const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n");
4954
4955 std::cmatch m;
4956 if (!std::regex_match(line_reader.ptr(), m, re)) { return true; }
4957 res.version = std::string(m[1]);
4958 res.status = std::stoi(std::string(m[2]));
4959 res.reason = std::string(m[3]);
4960
4961 // Ignore '100 Continue'
4962 while (res.status == 100) {
4963 if (!line_reader.getline()) { return false; } // CRLF
4964 if (!line_reader.getline()) { return false; } // next response line
4965
4966 if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
4967 res.version = std::string(m[1]);
4968 res.status = std::stoi(std::string(m[2]));
4969 res.reason = std::string(m[3]);
4970 }
4971
4972 return true;
4973 }
4974
send(const Request & req,Response & res,Error & error)4975 inline bool ClientImpl::send(const Request &req, Response &res, Error &error) {
4976 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
4977
4978 {
4979 std::lock_guard<std::mutex> guard(socket_mutex_);
4980 // Set this to false immediately - if it ever gets set to true by the end of
4981 // the request, we know another thread instructed us to close the socket.
4982 socket_should_be_closed_when_request_is_done_ = false;
4983
4984 auto is_alive = false;
4985 if (socket_.is_open()) {
4986 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
4987 if (!is_alive) {
4988 // Attempt to avoid sigpipe by shutting down nongracefully if it seems
4989 // like the other side has already closed the connection Also, there
4990 // cannot be any requests in flight from other threads since we locked
4991 // request_mutex_, so safe to close everything immediately
4992 const bool shutdown_gracefully = false;
4993 shutdown_ssl(socket_, shutdown_gracefully);
4994 shutdown_socket(socket_);
4995 close_socket(socket_);
4996 }
4997 }
4998
4999 if (!is_alive) {
5000 if (!create_and_connect_socket(socket_, error)) { return false; }
5001
5002 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5003 // TODO: refactoring
5004 if (is_ssl()) {
5005 auto &scli = static_cast<SSLClient &>(*this);
5006 if (!proxy_host_.empty() && proxy_port_ != -1) {
5007 bool success = false;
5008 if (!scli.connect_with_proxy(socket_, res, success, error)) {
5009 return success;
5010 }
5011 }
5012
5013 if (!scli.initialize_ssl(socket_, error)) { return false; }
5014 }
5015 #endif
5016 }
5017
5018 // Mark the current socket as being in use so that it cannot be closed by
5019 // anyone else while this request is ongoing, even though we will be
5020 // releasing the mutex.
5021 if (socket_requests_in_flight_ > 1) {
5022 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
5023 }
5024 socket_requests_in_flight_ += 1;
5025 socket_requests_are_from_thread_ = std::this_thread::get_id();
5026 }
5027
5028 auto close_connection = !keep_alive_;
5029 auto ret = process_socket(socket_, [&](Stream &strm) {
5030 return handle_request(strm, req, res, close_connection, error);
5031 });
5032
5033 // Briefly lock mutex in order to mark that a request is no longer ongoing
5034 {
5035 std::lock_guard<std::mutex> guard(socket_mutex_);
5036 socket_requests_in_flight_ -= 1;
5037 if (socket_requests_in_flight_ <= 0) {
5038 assert(socket_requests_in_flight_ == 0);
5039 socket_requests_are_from_thread_ = std::thread::id();
5040 }
5041
5042 if (socket_should_be_closed_when_request_is_done_ || close_connection ||
5043 !ret) {
5044 shutdown_ssl(socket_, true);
5045 shutdown_socket(socket_);
5046 close_socket(socket_);
5047 }
5048 }
5049
5050 if (!ret) {
5051 if (error == Error::Success) { error = Error::Unknown; }
5052 }
5053
5054 return ret;
5055 }
5056
send(const Request & req)5057 inline Result ClientImpl::send(const Request &req) {
5058 auto res = detail::make_unique<Response>();
5059 auto error = Error::Success;
5060 auto ret = send(req, *res, error);
5061 return Result{ret ? std::move(res) : nullptr, error};
5062 }
5063
handle_request(Stream & strm,const Request & req,Response & res,bool close_connection,Error & error)5064 inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
5065 Response &res, bool close_connection,
5066 Error &error) {
5067 if (req.path.empty()) {
5068 error = Error::Connection;
5069 return false;
5070 }
5071
5072 bool ret;
5073
5074 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
5075 auto req2 = req;
5076 req2.path = "http://" + host_and_port_ + req.path;
5077 ret = process_request(strm, req2, res, close_connection, error);
5078 } else {
5079 ret = process_request(strm, req, res, close_connection, error);
5080 }
5081
5082 if (!ret) { return false; }
5083
5084 if (300 < res.status && res.status < 400 && follow_location_) {
5085 ret = redirect(req, res, error);
5086 }
5087
5088 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5089 if ((res.status == 401 || res.status == 407) &&
5090 req.authorization_count_ < 5) {
5091 auto is_proxy = res.status == 407;
5092 const auto &username =
5093 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
5094 const auto &password =
5095 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
5096
5097 if (!username.empty() && !password.empty()) {
5098 std::map<std::string, std::string> auth;
5099 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
5100 Request new_req = req;
5101 new_req.authorization_count_ += 1;
5102 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5103 new_req.headers.erase(key);
5104 new_req.headers.insert(detail::make_digest_authentication_header(
5105 req, auth, new_req.authorization_count_, detail::random_string(10),
5106 username, password, is_proxy));
5107
5108 Response new_res;
5109
5110 ret = send(new_req, new_res, error);
5111 if (ret) { res = new_res; }
5112 }
5113 }
5114 }
5115 #endif
5116
5117 return ret;
5118 }
5119
redirect(const Request & req,Response & res,Error & error)5120 inline bool ClientImpl::redirect(const Request &req, Response &res,
5121 Error &error) {
5122 if (req.redirect_count_ == 0) {
5123 error = Error::ExceedRedirectCount;
5124 return false;
5125 }
5126
5127 auto location = detail::decode_url(res.get_header_value("location"), true);
5128 if (location.empty()) { return false; }
5129
5130 const static std::regex re(
5131 R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
5132
5133 std::smatch m;
5134 if (!std::regex_match(location, m, re)) { return false; }
5135
5136 auto scheme = is_ssl() ? "https" : "http";
5137
5138 auto next_scheme = m[1].str();
5139 auto next_host = m[2].str();
5140 auto port_str = m[3].str();
5141 auto next_path = m[4].str();
5142
5143 auto next_port = port_;
5144 if (!port_str.empty()) {
5145 next_port = std::stoi(port_str);
5146 } else if (!next_scheme.empty()) {
5147 next_port = next_scheme == "https" ? 443 : 80;
5148 }
5149
5150 if (next_scheme.empty()) { next_scheme = scheme; }
5151 if (next_host.empty()) { next_host = host_; }
5152 if (next_path.empty()) { next_path = "/"; }
5153
5154 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
5155 return detail::redirect(*this, req, res, next_path, location, error);
5156 } else {
5157 if (next_scheme == "https") {
5158 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5159 SSLClient cli(next_host.c_str(), next_port);
5160 cli.copy_settings(*this);
5161 return detail::redirect(cli, req, res, next_path, location, error);
5162 #else
5163 return false;
5164 #endif
5165 } else {
5166 ClientImpl cli(next_host.c_str(), next_port);
5167 cli.copy_settings(*this);
5168 return detail::redirect(cli, req, res, next_path, location, error);
5169 }
5170 }
5171 }
5172
write_content_with_provider(Stream & strm,const Request & req,Error & error)5173 inline bool ClientImpl::write_content_with_provider(Stream &strm,
5174 const Request &req,
5175 Error &error) {
5176 auto is_shutting_down = []() { return false; };
5177
5178 if (req.is_chunked_content_provider_) {
5179 // TODO: Brotli suport
5180 std::unique_ptr<detail::compressor> compressor;
5181 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
5182 if (compress_) {
5183 compressor = detail::make_unique<detail::gzip_compressor>();
5184 } else
5185 #endif
5186 {
5187 compressor = detail::make_unique<detail::nocompressor>();
5188 }
5189
5190 return detail::write_content_chunked(strm, req.content_provider_,
5191 is_shutting_down, *compressor, error);
5192 } else {
5193 return detail::write_content(strm, req.content_provider_, 0,
5194 req.content_length_, is_shutting_down, error);
5195 }
5196 } // namespace httplib
5197
write_request(Stream & strm,const Request & req,bool close_connection,Error & error)5198 inline bool ClientImpl::write_request(Stream &strm, const Request &req,
5199 bool close_connection, Error &error) {
5200 detail::BufferStream bstrm;
5201
5202 // Request line
5203 const auto &path = detail::encode_url(req.path);
5204
5205 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
5206
5207 // Additonal headers
5208 Headers headers;
5209 if (close_connection) { headers.emplace("Connection", "close"); }
5210
5211 if (!req.has_header("Host")) {
5212 if (is_ssl()) {
5213 if (port_ == 443) {
5214 headers.emplace("Host", host_);
5215 } else {
5216 headers.emplace("Host", host_and_port_);
5217 }
5218 } else {
5219 if (port_ == 80) {
5220 headers.emplace("Host", host_);
5221 } else {
5222 headers.emplace("Host", host_and_port_);
5223 }
5224 }
5225 }
5226
5227 if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
5228
5229 if (!req.has_header("User-Agent")) {
5230 headers.emplace("User-Agent", "cpp-httplib/0.7");
5231 }
5232
5233 if (req.body.empty()) {
5234 if (req.content_provider_) {
5235 if (!req.is_chunked_content_provider_) {
5236 auto length = std::to_string(req.content_length_);
5237 headers.emplace("Content-Length", length);
5238 }
5239 } else {
5240 if (req.method == "POST" || req.method == "PUT" ||
5241 req.method == "PATCH") {
5242 headers.emplace("Content-Length", "0");
5243 }
5244 }
5245 } else {
5246 if (!req.has_header("Content-Type")) {
5247 headers.emplace("Content-Type", "text/plain");
5248 }
5249
5250 if (!req.has_header("Content-Length")) {
5251 auto length = std::to_string(req.body.size());
5252 headers.emplace("Content-Length", length);
5253 }
5254 }
5255
5256 if (!basic_auth_password_.empty()) {
5257 headers.insert(make_basic_authentication_header(
5258 basic_auth_username_, basic_auth_password_, false));
5259 }
5260
5261 if (!proxy_basic_auth_username_.empty() &&
5262 !proxy_basic_auth_password_.empty()) {
5263 headers.insert(make_basic_authentication_header(
5264 proxy_basic_auth_username_, proxy_basic_auth_password_, true));
5265 }
5266
5267 if (!bearer_token_auth_token_.empty()) {
5268 headers.insert(make_bearer_token_authentication_header(
5269 bearer_token_auth_token_, false));
5270 }
5271
5272 if (!proxy_bearer_token_auth_token_.empty()) {
5273 headers.insert(make_bearer_token_authentication_header(
5274 proxy_bearer_token_auth_token_, true));
5275 }
5276
5277 detail::write_headers(bstrm, req, headers);
5278
5279 // Flush buffer
5280 auto &data = bstrm.get_buffer();
5281 if (!detail::write_data(strm, data.data(), data.size())) {
5282 error = Error::Write;
5283 return false;
5284 }
5285
5286 // Body
5287 if (req.body.empty()) {
5288 return write_content_with_provider(strm, req, error);
5289 } else {
5290 return detail::write_data(strm, req.body.data(), req.body.size());
5291 }
5292
5293 return true;
5294 }
5295
send_with_content_provider(const char * method,const char * path,const Headers & headers,const std::string & body,size_t content_length,ContentProvider content_provider,ContentProviderWithoutLength content_provider_without_length,const char * content_type,Error & error)5296 inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
5297 const char *method, const char *path, const Headers &headers,
5298 const std::string &body, size_t content_length,
5299 ContentProvider content_provider,
5300 ContentProviderWithoutLength content_provider_without_length,
5301 const char *content_type, Error &error) {
5302
5303 Request req;
5304 req.method = method;
5305 req.headers = default_headers_;
5306 req.headers.insert(headers.begin(), headers.end());
5307 req.path = path;
5308
5309 if (content_type) { req.headers.emplace("Content-Type", content_type); }
5310
5311 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
5312 if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); }
5313 #endif
5314
5315 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
5316 if (compress_ && !content_provider_without_length) {
5317 // TODO: Brotli support
5318 detail::gzip_compressor compressor;
5319
5320 if (content_provider) {
5321 auto ok = true;
5322 size_t offset = 0;
5323 DataSink data_sink;
5324
5325 data_sink.write = [&](const char *data, size_t data_len) {
5326 if (ok) {
5327 auto last = offset + data_len == content_length;
5328
5329 auto ret = compressor.compress(
5330 data, data_len, last, [&](const char *data, size_t data_len) {
5331 req.body.append(data, data_len);
5332 return true;
5333 });
5334
5335 if (ret) {
5336 offset += data_len;
5337 } else {
5338 ok = false;
5339 }
5340 }
5341 };
5342
5343 data_sink.is_writable = [&](void) { return ok && true; };
5344
5345 while (ok && offset < content_length) {
5346 if (!content_provider(offset, content_length - offset, data_sink)) {
5347 error = Error::Canceled;
5348 return nullptr;
5349 }
5350 }
5351 } else {
5352 if (!compressor.compress(body.data(), body.size(), true,
5353 [&](const char *data, size_t data_len) {
5354 req.body.append(data, data_len);
5355 return true;
5356 })) {
5357 error = Error::Compression;
5358 return nullptr;
5359 }
5360 }
5361 } else
5362 #endif
5363 {
5364 if (content_provider) {
5365 req.content_length_ = content_length;
5366 req.content_provider_ = std::move(content_provider);
5367 req.is_chunked_content_provider_ = false;
5368 } else if (content_provider_without_length) {
5369 req.content_length_ = 0;
5370 req.content_provider_ = detail::ContentProviderAdapter(
5371 std::move(content_provider_without_length));
5372 req.is_chunked_content_provider_ = true;
5373 req.headers.emplace("Transfer-Encoding", "chunked");
5374 } else {
5375 req.body = body;
5376 }
5377 }
5378
5379 auto res = detail::make_unique<Response>();
5380 return send(req, *res, error) ? std::move(res) : nullptr;
5381 }
5382
send_with_content_provider(const char * method,const char * path,const Headers & headers,const std::string & body,size_t content_length,ContentProvider content_provider,ContentProviderWithoutLength content_provider_without_length,const char * content_type)5383 inline Result ClientImpl::send_with_content_provider(
5384 const char *method, const char *path, const Headers &headers,
5385 const std::string &body, size_t content_length,
5386 ContentProvider content_provider,
5387 ContentProviderWithoutLength content_provider_without_length,
5388 const char *content_type) {
5389 auto error = Error::Success;
5390 auto res = send_with_content_provider(
5391 method, path, headers, body, content_length, std::move(content_provider),
5392 std::move(content_provider_without_length), content_type, error);
5393 return Result{std::move(res), error};
5394 }
5395
process_request(Stream & strm,const Request & req,Response & res,bool close_connection,Error & error)5396 inline bool ClientImpl::process_request(Stream &strm, const Request &req,
5397 Response &res, bool close_connection,
5398 Error &error) {
5399 // Send request
5400 if (!write_request(strm, req, close_connection, error)) { return false; }
5401
5402 // Receive response and headers
5403 if (!read_response_line(strm, res) ||
5404 !detail::read_headers(strm, res.headers)) {
5405 error = Error::Read;
5406 return false;
5407 }
5408
5409 if (req.response_handler_) {
5410 if (!req.response_handler_(res)) {
5411 error = Error::Canceled;
5412 return false;
5413 }
5414 }
5415
5416 // Body
5417 if (req.method != "HEAD" && req.method != "CONNECT") {
5418 auto out =
5419 req.content_receiver_
5420 ? static_cast<ContentReceiverWithProgress>(
5421 [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
5422 auto ret = req.content_receiver_(buf, n, off, len);
5423 if (!ret) { error = Error::Canceled; }
5424 return ret;
5425 })
5426 : static_cast<ContentReceiverWithProgress>(
5427 [&](const char *buf, size_t n, uint64_t /*off*/,
5428 uint64_t /*len*/) {
5429 if (res.body.size() + n > res.body.max_size()) {
5430 return false;
5431 }
5432 res.body.append(buf, n);
5433 return true;
5434 });
5435
5436 auto progress = [&](uint64_t current, uint64_t total) {
5437 if (!req.progress_) { return true; }
5438 auto ret = req.progress_(current, total);
5439 if (!ret) { error = Error::Canceled; }
5440 return ret;
5441 };
5442
5443 int dummy_status;
5444 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
5445 dummy_status, std::move(progress), std::move(out),
5446 decompress_)) {
5447 if (error != Error::Canceled) {
5448 error = Error::Read;
5449 }
5450 return false;
5451 }
5452 }
5453
5454 if (res.get_header_value("Connection") == "close" ||
5455 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
5456 // TODO this requires a not-entirely-obvious chain of calls to be correct
5457 // for this to be safe. Maybe a code refactor (such as moving this out to
5458 // the send function and getting rid of the recursiveness of the mutex)
5459 // could make this more obvious.
5460
5461 // This is safe to call because process_request is only called by
5462 // handle_request which is only called by send, which locks the request
5463 // mutex during the process. It would be a bug to call it from a different
5464 // thread since it's a thread-safety issue to do these things to the socket
5465 // if another thread is using the socket.
5466 lock_socket_and_shutdown_and_close();
5467 }
5468
5469 // Log
5470 if (logger_) { logger_(req, res); }
5471
5472 return true;
5473 }
5474
5475 inline bool
process_socket(const Socket & socket,std::function<bool (Stream & strm)> callback)5476 ClientImpl::process_socket(const Socket &socket,
5477 std::function<bool(Stream &strm)> callback) {
5478 return detail::process_client_socket(
5479 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5480 write_timeout_usec_, std::move(callback));
5481 }
5482
is_ssl() const5483 inline bool ClientImpl::is_ssl() const { return false; }
5484
Get(const char * path)5485 inline Result ClientImpl::Get(const char *path) {
5486 return Get(path, Headers(), Progress());
5487 }
5488
Get(const char * path,Progress progress)5489 inline Result ClientImpl::Get(const char *path, Progress progress) {
5490 return Get(path, Headers(), std::move(progress));
5491 }
5492
Get(const char * path,const Headers & headers)5493 inline Result ClientImpl::Get(const char *path, const Headers &headers) {
5494 return Get(path, headers, Progress());
5495 }
5496
Get(const char * path,const Headers & headers,Progress progress)5497 inline Result ClientImpl::Get(const char *path, const Headers &headers,
5498 Progress progress) {
5499 Request req;
5500 req.method = "GET";
5501 req.path = path;
5502 req.headers = default_headers_;
5503 req.headers.insert(headers.begin(), headers.end());
5504 req.progress_ = std::move(progress);
5505
5506 return send(req);
5507 }
5508
Get(const char * path,ContentReceiver content_receiver)5509 inline Result ClientImpl::Get(const char *path,
5510 ContentReceiver content_receiver) {
5511 return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
5512 }
5513
Get(const char * path,ContentReceiver content_receiver,Progress progress)5514 inline Result ClientImpl::Get(const char *path,
5515 ContentReceiver content_receiver,
5516 Progress progress) {
5517 return Get(path, Headers(), nullptr, std::move(content_receiver),
5518 std::move(progress));
5519 }
5520
Get(const char * path,const Headers & headers,ContentReceiver content_receiver)5521 inline Result ClientImpl::Get(const char *path, const Headers &headers,
5522 ContentReceiver content_receiver) {
5523 return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
5524 }
5525
Get(const char * path,const Headers & headers,ContentReceiver content_receiver,Progress progress)5526 inline Result ClientImpl::Get(const char *path, const Headers &headers,
5527 ContentReceiver content_receiver,
5528 Progress progress) {
5529 return Get(path, headers, nullptr, std::move(content_receiver),
5530 std::move(progress));
5531 }
5532
Get(const char * path,ResponseHandler response_handler,ContentReceiver content_receiver)5533 inline Result ClientImpl::Get(const char *path,
5534 ResponseHandler response_handler,
5535 ContentReceiver content_receiver) {
5536 return Get(path, Headers(), std::move(response_handler),
5537 std::move(content_receiver), nullptr);
5538 }
5539
Get(const char * path,const Headers & headers,ResponseHandler response_handler,ContentReceiver content_receiver)5540 inline Result ClientImpl::Get(const char *path, const Headers &headers,
5541 ResponseHandler response_handler,
5542 ContentReceiver content_receiver) {
5543 return Get(path, headers, std::move(response_handler),
5544 std::move(content_receiver), nullptr);
5545 }
5546
Get(const char * path,ResponseHandler response_handler,ContentReceiver content_receiver,Progress progress)5547 inline Result ClientImpl::Get(const char *path,
5548 ResponseHandler response_handler,
5549 ContentReceiver content_receiver,
5550 Progress progress) {
5551 return Get(path, Headers(), std::move(response_handler),
5552 std::move(content_receiver), std::move(progress));
5553 }
5554
Get(const char * path,const Headers & headers,ResponseHandler response_handler,ContentReceiver content_receiver,Progress progress)5555 inline Result ClientImpl::Get(const char *path, const Headers &headers,
5556 ResponseHandler response_handler,
5557 ContentReceiver content_receiver,
5558 Progress progress) {
5559 Request req;
5560 req.method = "GET";
5561 req.path = path;
5562 req.headers = default_headers_;
5563 req.headers.insert(headers.begin(), headers.end());
5564 req.response_handler_ = std::move(response_handler);
5565 req.content_receiver_ =
5566 [content_receiver](const char *data, size_t data_length,
5567 uint64_t /*offset*/, uint64_t /*total_length*/) {
5568 return content_receiver(data, data_length);
5569 };
5570 req.progress_ = std::move(progress);
5571
5572 return send(req);
5573 }
5574
Head(const char * path)5575 inline Result ClientImpl::Head(const char *path) {
5576 return Head(path, Headers());
5577 }
5578
Head(const char * path,const Headers & headers)5579 inline Result ClientImpl::Head(const char *path, const Headers &headers) {
5580 Request req;
5581 req.method = "HEAD";
5582 req.headers = default_headers_;
5583 req.headers.insert(headers.begin(), headers.end());
5584 req.path = path;
5585
5586 return send(req);
5587 }
5588
Post(const char * path)5589 inline Result ClientImpl::Post(const char *path) {
5590 return Post(path, std::string(), nullptr);
5591 }
5592
Post(const char * path,const std::string & body,const char * content_type)5593 inline Result ClientImpl::Post(const char *path, const std::string &body,
5594 const char *content_type) {
5595 return Post(path, Headers(), body, content_type);
5596 }
5597
Post(const char * path,const Headers & headers,const std::string & body,const char * content_type)5598 inline Result ClientImpl::Post(const char *path, const Headers &headers,
5599 const std::string &body,
5600 const char *content_type) {
5601 return send_with_content_provider("POST", path, headers, body, 0, nullptr,
5602 nullptr, content_type);
5603 }
5604
Post(const char * path,const Params & params)5605 inline Result ClientImpl::Post(const char *path, const Params ¶ms) {
5606 return Post(path, Headers(), params);
5607 }
5608
Post(const char * path,size_t content_length,ContentProvider content_provider,const char * content_type)5609 inline Result ClientImpl::Post(const char *path, size_t content_length,
5610 ContentProvider content_provider,
5611 const char *content_type) {
5612 return Post(path, Headers(), content_length, std::move(content_provider),
5613 content_type);
5614 }
5615
Post(const char * path,ContentProviderWithoutLength content_provider,const char * content_type)5616 inline Result ClientImpl::Post(const char *path,
5617 ContentProviderWithoutLength content_provider,
5618 const char *content_type) {
5619 return Post(path, Headers(), std::move(content_provider), content_type);
5620 }
5621
Post(const char * path,const Headers & headers,size_t content_length,ContentProvider content_provider,const char * content_type)5622 inline Result ClientImpl::Post(const char *path, const Headers &headers,
5623 size_t content_length,
5624 ContentProvider content_provider,
5625 const char *content_type) {
5626 return send_with_content_provider("POST", path, headers, std::string(),
5627 content_length, std::move(content_provider),
5628 nullptr, content_type);
5629 }
5630
Post(const char * path,const Headers & headers,ContentProviderWithoutLength content_provider,const char * content_type)5631 inline Result ClientImpl::Post(const char *path, const Headers &headers,
5632 ContentProviderWithoutLength content_provider,
5633 const char *content_type) {
5634 return send_with_content_provider("POST", path, headers, std::string(), 0,
5635 nullptr, std::move(content_provider),
5636 content_type);
5637 }
5638
Post(const char * path,const Headers & headers,const Params & params)5639 inline Result ClientImpl::Post(const char *path, const Headers &headers,
5640 const Params ¶ms) {
5641 auto query = detail::params_to_query_str(params);
5642 return Post(path, headers, query, "application/x-www-form-urlencoded");
5643 }
5644
Post(const char * path,const MultipartFormDataItems & items)5645 inline Result ClientImpl::Post(const char *path,
5646 const MultipartFormDataItems &items) {
5647 return Post(path, Headers(), items);
5648 }
5649
Post(const char * path,const Headers & headers,const MultipartFormDataItems & items)5650 inline Result ClientImpl::Post(const char *path, const Headers &headers,
5651 const MultipartFormDataItems &items) {
5652 return Post(path, headers, items, detail::make_multipart_data_boundary());
5653 }
Post(const char * path,const Headers & headers,const MultipartFormDataItems & items,const std::string & boundary)5654 inline Result ClientImpl::Post(const char *path, const Headers &headers,
5655 const MultipartFormDataItems &items,
5656 const std::string &boundary) {
5657 for (size_t i = 0; i < boundary.size(); i++) {
5658 char c = boundary[i];
5659 if (!std::isalnum(c) && c != '-' && c != '_') {
5660 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
5661 }
5662 }
5663
5664 std::string body;
5665
5666 for (const auto &item : items) {
5667 body += "--" + boundary + "\r\n";
5668 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
5669 if (!item.filename.empty()) {
5670 body += "; filename=\"" + item.filename + "\"";
5671 }
5672 body += "\r\n";
5673 if (!item.content_type.empty()) {
5674 body += "Content-Type: " + item.content_type + "\r\n";
5675 }
5676 body += "\r\n";
5677 body += item.content + "\r\n";
5678 }
5679
5680 body += "--" + boundary + "--\r\n";
5681
5682 std::string content_type = "multipart/form-data; boundary=" + boundary;
5683 return Post(path, headers, body, content_type.c_str());
5684 }
5685
Put(const char * path)5686 inline Result ClientImpl::Put(const char *path) {
5687 return Put(path, std::string(), nullptr);
5688 }
5689
Put(const char * path,const std::string & body,const char * content_type)5690 inline Result ClientImpl::Put(const char *path, const std::string &body,
5691 const char *content_type) {
5692 return Put(path, Headers(), body, content_type);
5693 }
5694
Put(const char * path,const Headers & headers,const std::string & body,const char * content_type)5695 inline Result ClientImpl::Put(const char *path, const Headers &headers,
5696 const std::string &body,
5697 const char *content_type) {
5698 return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
5699 nullptr, content_type);
5700 }
5701
Put(const char * path,size_t content_length,ContentProvider content_provider,const char * content_type)5702 inline Result ClientImpl::Put(const char *path, size_t content_length,
5703 ContentProvider content_provider,
5704 const char *content_type) {
5705 return Put(path, Headers(), content_length, std::move(content_provider),
5706 content_type);
5707 }
5708
Put(const char * path,ContentProviderWithoutLength content_provider,const char * content_type)5709 inline Result ClientImpl::Put(const char *path,
5710 ContentProviderWithoutLength content_provider,
5711 const char *content_type) {
5712 return Put(path, Headers(), std::move(content_provider), content_type);
5713 }
5714
Put(const char * path,const Headers & headers,size_t content_length,ContentProvider content_provider,const char * content_type)5715 inline Result ClientImpl::Put(const char *path, const Headers &headers,
5716 size_t content_length,
5717 ContentProvider content_provider,
5718 const char *content_type) {
5719 return send_with_content_provider("PUT", path, headers, std::string(),
5720 content_length, std::move(content_provider),
5721 nullptr, content_type);
5722 }
5723
Put(const char * path,const Headers & headers,ContentProviderWithoutLength content_provider,const char * content_type)5724 inline Result ClientImpl::Put(const char *path, const Headers &headers,
5725 ContentProviderWithoutLength content_provider,
5726 const char *content_type) {
5727 return send_with_content_provider("PUT", path, headers, std::string(), 0,
5728 nullptr, std::move(content_provider),
5729 content_type);
5730 }
5731
Put(const char * path,const Params & params)5732 inline Result ClientImpl::Put(const char *path, const Params ¶ms) {
5733 return Put(path, Headers(), params);
5734 }
5735
Put(const char * path,const Headers & headers,const Params & params)5736 inline Result ClientImpl::Put(const char *path, const Headers &headers,
5737 const Params ¶ms) {
5738 auto query = detail::params_to_query_str(params);
5739 return Put(path, headers, query, "application/x-www-form-urlencoded");
5740 }
5741
Patch(const char * path,const std::string & body,const char * content_type)5742 inline Result ClientImpl::Patch(const char *path, const std::string &body,
5743 const char *content_type) {
5744 return Patch(path, Headers(), body, content_type);
5745 }
5746
Patch(const char * path,const Headers & headers,const std::string & body,const char * content_type)5747 inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5748 const std::string &body,
5749 const char *content_type) {
5750 return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
5751 nullptr, content_type);
5752 }
5753
Patch(const char * path,size_t content_length,ContentProvider content_provider,const char * content_type)5754 inline Result ClientImpl::Patch(const char *path, size_t content_length,
5755 ContentProvider content_provider,
5756 const char *content_type) {
5757 return Patch(path, Headers(), content_length, std::move(content_provider),
5758 content_type);
5759 }
5760
Patch(const char * path,ContentProviderWithoutLength content_provider,const char * content_type)5761 inline Result ClientImpl::Patch(const char *path,
5762 ContentProviderWithoutLength content_provider,
5763 const char *content_type) {
5764 return Patch(path, Headers(), std::move(content_provider), content_type);
5765 }
5766
Patch(const char * path,const Headers & headers,size_t content_length,ContentProvider content_provider,const char * content_type)5767 inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5768 size_t content_length,
5769 ContentProvider content_provider,
5770 const char *content_type) {
5771 return send_with_content_provider("PATCH", path, headers, std::string(),
5772 content_length, std::move(content_provider),
5773 nullptr, content_type);
5774 }
5775
Patch(const char * path,const Headers & headers,ContentProviderWithoutLength content_provider,const char * content_type)5776 inline Result ClientImpl::Patch(const char *path, const Headers &headers,
5777 ContentProviderWithoutLength content_provider,
5778 const char *content_type) {
5779 return send_with_content_provider("PATCH", path, headers, std::string(), 0,
5780 nullptr, std::move(content_provider),
5781 content_type);
5782 }
5783
Delete(const char * path)5784 inline Result ClientImpl::Delete(const char *path) {
5785 return Delete(path, Headers(), std::string(), nullptr);
5786 }
5787
Delete(const char * path,const std::string & body,const char * content_type)5788 inline Result ClientImpl::Delete(const char *path, const std::string &body,
5789 const char *content_type) {
5790 return Delete(path, Headers(), body, content_type);
5791 }
5792
Delete(const char * path,const Headers & headers)5793 inline Result ClientImpl::Delete(const char *path, const Headers &headers) {
5794 return Delete(path, headers, std::string(), nullptr);
5795 }
5796
Delete(const char * path,const Headers & headers,const std::string & body,const char * content_type)5797 inline Result ClientImpl::Delete(const char *path, const Headers &headers,
5798 const std::string &body,
5799 const char *content_type) {
5800 Request req;
5801 req.method = "DELETE";
5802 req.headers = default_headers_;
5803 req.headers.insert(headers.begin(), headers.end());
5804 req.path = path;
5805
5806 if (content_type) { req.headers.emplace("Content-Type", content_type); }
5807 req.body = body;
5808
5809 return send(req);
5810 }
5811
Options(const char * path)5812 inline Result ClientImpl::Options(const char *path) {
5813 return Options(path, Headers());
5814 }
5815
Options(const char * path,const Headers & headers)5816 inline Result ClientImpl::Options(const char *path, const Headers &headers) {
5817 Request req;
5818 req.method = "OPTIONS";
5819 req.headers = default_headers_;
5820 req.headers.insert(headers.begin(), headers.end());
5821 req.path = path;
5822
5823 return send(req);
5824 }
5825
is_socket_open() const5826 inline size_t ClientImpl::is_socket_open() const {
5827 std::lock_guard<std::mutex> guard(socket_mutex_);
5828 return socket_.is_open();
5829 }
5830
stop()5831 inline void ClientImpl::stop() {
5832 std::lock_guard<std::mutex> guard(socket_mutex_);
5833
5834 // If there is anything ongoing right now, the ONLY thread-safe thing we can
5835 // do is to shutdown_socket, so that threads using this socket suddenly
5836 // discover they can't read/write any more and error out. Everything else
5837 // (closing the socket, shutting ssl down) is unsafe because these actions are
5838 // not thread-safe.
5839 if (socket_requests_in_flight_ > 0) {
5840 shutdown_socket(socket_);
5841
5842 // Aside from that, we set a flag for the socket to be closed when we're
5843 // done.
5844 socket_should_be_closed_when_request_is_done_ = true;
5845 return;
5846 }
5847
5848 // Otherwise, sitll holding the mutex, we can shut everything down ourselves
5849 shutdown_ssl(socket_, true);
5850 shutdown_socket(socket_);
5851 close_socket(socket_);
5852 }
5853
set_connection_timeout(time_t sec,time_t usec)5854 inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
5855 connection_timeout_sec_ = sec;
5856 connection_timeout_usec_ = usec;
5857 }
5858
set_read_timeout(time_t sec,time_t usec)5859 inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
5860 read_timeout_sec_ = sec;
5861 read_timeout_usec_ = usec;
5862 }
5863
set_write_timeout(time_t sec,time_t usec)5864 inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
5865 write_timeout_sec_ = sec;
5866 write_timeout_usec_ = usec;
5867 }
5868
set_basic_auth(const char * username,const char * password)5869 inline void ClientImpl::set_basic_auth(const char *username,
5870 const char *password) {
5871 basic_auth_username_ = username;
5872 basic_auth_password_ = password;
5873 }
5874
set_bearer_token_auth(const char * token)5875 inline void ClientImpl::set_bearer_token_auth(const char *token) {
5876 bearer_token_auth_token_ = token;
5877 }
5878
5879 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
set_digest_auth(const char * username,const char * password)5880 inline void ClientImpl::set_digest_auth(const char *username,
5881 const char *password) {
5882 digest_auth_username_ = username;
5883 digest_auth_password_ = password;
5884 }
5885 #endif
5886
set_keep_alive(bool on)5887 inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
5888
set_follow_location(bool on)5889 inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
5890
set_default_headers(Headers headers)5891 inline void ClientImpl::set_default_headers(Headers headers) {
5892 default_headers_ = std::move(headers);
5893 }
5894
set_tcp_nodelay(bool on)5895 inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5896
set_socket_options(SocketOptions socket_options)5897 inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
5898 socket_options_ = std::move(socket_options);
5899 }
5900
set_compress(bool on)5901 inline void ClientImpl::set_compress(bool on) { compress_ = on; }
5902
set_decompress(bool on)5903 inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
5904
set_interface(const char * intf)5905 inline void ClientImpl::set_interface(const char *intf) { interface_ = intf; }
5906
set_proxy(const char * host,int port)5907 inline void ClientImpl::set_proxy(const char *host, int port) {
5908 proxy_host_ = host;
5909 proxy_port_ = port;
5910 }
5911
set_proxy_basic_auth(const char * username,const char * password)5912 inline void ClientImpl::set_proxy_basic_auth(const char *username,
5913 const char *password) {
5914 proxy_basic_auth_username_ = username;
5915 proxy_basic_auth_password_ = password;
5916 }
5917
set_proxy_bearer_token_auth(const char * token)5918 inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) {
5919 proxy_bearer_token_auth_token_ = token;
5920 }
5921
5922 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
set_proxy_digest_auth(const char * username,const char * password)5923 inline void ClientImpl::set_proxy_digest_auth(const char *username,
5924 const char *password) {
5925 proxy_digest_auth_username_ = username;
5926 proxy_digest_auth_password_ = password;
5927 }
5928 #endif
5929
5930 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
enable_server_certificate_verification(bool enabled)5931 inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
5932 server_certificate_verification_ = enabled;
5933 }
5934 #endif
5935
set_logger(Logger logger)5936 inline void ClientImpl::set_logger(Logger logger) {
5937 logger_ = std::move(logger);
5938 }
5939
5940 /*
5941 * SSL Implementation
5942 */
5943 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5944 namespace detail {
5945
5946 template <typename U, typename V>
ssl_new(socket_t sock,SSL_CTX * ctx,std::mutex & ctx_mutex,U SSL_connect_or_accept,V setup)5947 inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
5948 U SSL_connect_or_accept, V setup) {
5949 SSL *ssl = nullptr;
5950 {
5951 std::lock_guard<std::mutex> guard(ctx_mutex);
5952 ssl = SSL_new(ctx);
5953 }
5954
5955 if (ssl) {
5956 set_nonblocking(sock, true);
5957 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
5958 BIO_set_nbio(bio, 1);
5959 SSL_set_bio(ssl, bio, bio);
5960
5961 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
5962 SSL_shutdown(ssl);
5963 {
5964 std::lock_guard<std::mutex> guard(ctx_mutex);
5965 SSL_free(ssl);
5966 }
5967 set_nonblocking(sock, false);
5968 return nullptr;
5969 }
5970 BIO_set_nbio(bio, 0);
5971 set_nonblocking(sock, false);
5972 }
5973
5974 return ssl;
5975 }
5976
ssl_delete(std::mutex & ctx_mutex,SSL * ssl,bool shutdown_gracefully)5977 inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
5978 bool shutdown_gracefully) {
5979 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
5980 // the remote has closed the network connection
5981 // Note that it is not always possible to avoid SIGPIPE, this is merely a
5982 // best-efforts.
5983 if (shutdown_gracefully) { SSL_shutdown(ssl); }
5984
5985 std::lock_guard<std::mutex> guard(ctx_mutex);
5986 SSL_free(ssl);
5987 }
5988
5989 template <typename U>
ssl_connect_or_accept_nonblocking(socket_t sock,SSL * ssl,U ssl_connect_or_accept,time_t timeout_sec,time_t timeout_usec)5990 bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
5991 U ssl_connect_or_accept,
5992 time_t timeout_sec,
5993 time_t timeout_usec) {
5994 int res = 0;
5995 while ((res = ssl_connect_or_accept(ssl)) != 1) {
5996 auto err = SSL_get_error(ssl, res);
5997 switch (err) {
5998 case SSL_ERROR_WANT_READ:
5999 if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
6000 break;
6001 case SSL_ERROR_WANT_WRITE:
6002 if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
6003 break;
6004 default: break;
6005 }
6006 return false;
6007 }
6008 return true;
6009 }
6010
6011 template <typename T>
6012 inline bool
process_server_socket_ssl(SSL * ssl,socket_t sock,size_t keep_alive_max_count,time_t keep_alive_timeout_sec,time_t read_timeout_sec,time_t read_timeout_usec,time_t write_timeout_sec,time_t write_timeout_usec,T callback)6013 process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count,
6014 time_t keep_alive_timeout_sec,
6015 time_t read_timeout_sec, time_t read_timeout_usec,
6016 time_t write_timeout_sec, time_t write_timeout_usec,
6017 T callback) {
6018 return process_server_socket_core(
6019 sock, keep_alive_max_count, keep_alive_timeout_sec,
6020 [&](bool close_connection, bool &connection_closed) {
6021 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
6022 write_timeout_sec, write_timeout_usec);
6023 return callback(strm, close_connection, connection_closed);
6024 });
6025 }
6026
6027 template <typename T>
6028 inline bool
process_client_socket_ssl(SSL * ssl,socket_t sock,time_t read_timeout_sec,time_t read_timeout_usec,time_t write_timeout_sec,time_t write_timeout_usec,T callback)6029 process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,
6030 time_t read_timeout_usec, time_t write_timeout_sec,
6031 time_t write_timeout_usec, T callback) {
6032 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
6033 write_timeout_sec, write_timeout_usec);
6034 return callback(strm);
6035 }
6036
6037 #if OPENSSL_VERSION_NUMBER < 0x10100000L
6038 static std::shared_ptr<std::vector<std::mutex>> openSSL_locks_;
6039
6040 class SSLThreadLocks {
6041 public:
SSLThreadLocks()6042 SSLThreadLocks() {
6043 openSSL_locks_ =
6044 std::make_shared<std::vector<std::mutex>>(CRYPTO_num_locks());
6045 CRYPTO_set_locking_callback(locking_callback);
6046 }
6047
~SSLThreadLocks()6048 ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); }
6049
6050 private:
locking_callback(int mode,int type,const char *,int)6051 static void locking_callback(int mode, int type, const char * /*file*/,
6052 int /*line*/) {
6053 auto &lk = (*openSSL_locks_)[static_cast<size_t>(type)];
6054 if (mode & CRYPTO_LOCK) {
6055 lk.lock();
6056 } else {
6057 lk.unlock();
6058 }
6059 }
6060 };
6061
6062 #endif
6063
6064 class SSLInit {
6065 public:
SSLInit()6066 SSLInit() {
6067 #if OPENSSL_VERSION_NUMBER < 0x1010001fL
6068 SSL_load_error_strings();
6069 SSL_library_init();
6070 #else
6071 OPENSSL_init_ssl(
6072 OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
6073 #endif
6074 }
6075
~SSLInit()6076 ~SSLInit() {
6077 #if OPENSSL_VERSION_NUMBER < 0x1010001fL
6078 ERR_free_strings();
6079 #endif
6080 }
6081
6082 private:
6083 #if OPENSSL_VERSION_NUMBER < 0x10100000L
6084 SSLThreadLocks thread_init_;
6085 #endif
6086 };
6087
6088 // SSL socket stream implementation
SSLSocketStream(socket_t sock,SSL * ssl,time_t read_timeout_sec,time_t read_timeout_usec,time_t write_timeout_sec,time_t write_timeout_usec)6089 inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
6090 time_t read_timeout_sec,
6091 time_t read_timeout_usec,
6092 time_t write_timeout_sec,
6093 time_t write_timeout_usec)
6094 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
6095 read_timeout_usec_(read_timeout_usec),
6096 write_timeout_sec_(write_timeout_sec),
6097 write_timeout_usec_(write_timeout_usec) {
6098 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
6099 }
6100
~SSLSocketStream()6101 inline SSLSocketStream::~SSLSocketStream() {}
6102
is_readable() const6103 inline bool SSLSocketStream::is_readable() const {
6104 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
6105 }
6106
is_writable() const6107 inline bool SSLSocketStream::is_writable() const {
6108 return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) >
6109 0;
6110 }
6111
read(char * ptr,size_t size)6112 inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
6113 if (SSL_pending(ssl_) > 0) {
6114 return SSL_read(ssl_, ptr, static_cast<int>(size));
6115 } else if (is_readable()) {
6116 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
6117 if (ret < 0) {
6118 auto err = SSL_get_error(ssl_, ret);
6119 while (err == SSL_ERROR_WANT_READ) {
6120 if (SSL_pending(ssl_) > 0) {
6121 return SSL_read(ssl_, ptr, static_cast<int>(size));
6122 } else if (is_readable()) {
6123 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
6124 if (ret >= 0) { return ret; }
6125 err = SSL_get_error(ssl_, ret);
6126 } else {
6127 return -1;
6128 }
6129 }
6130 }
6131 return ret;
6132 }
6133 return -1;
6134 }
6135
write(const char * ptr,size_t size)6136 inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
6137 if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); }
6138 return -1;
6139 }
6140
get_remote_ip_and_port(std::string & ip,int & port) const6141 inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
6142 int &port) const {
6143 detail::get_remote_ip_and_port(sock_, ip, port);
6144 }
6145
socket() const6146 inline socket_t SSLSocketStream::socket() const { return sock_; }
6147
6148 static SSLInit sslinit_;
6149
6150 } // namespace detail
6151
6152 // SSL HTTP server implementation
SSLServer(const char * cert_path,const char * private_key_path,const char * client_ca_cert_file_path,const char * client_ca_cert_dir_path)6153 inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
6154 const char *client_ca_cert_file_path,
6155 const char *client_ca_cert_dir_path) {
6156 ctx_ = SSL_CTX_new(SSLv23_server_method());
6157
6158 if (ctx_) {
6159 SSL_CTX_set_options(ctx_,
6160 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
6161 SSL_OP_NO_COMPRESSION |
6162 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
6163
6164 // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
6165 // SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
6166 // EC_KEY_free(ecdh);
6167
6168 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
6169 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
6170 1) {
6171 SSL_CTX_free(ctx_);
6172 ctx_ = nullptr;
6173 } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
6174 // if (client_ca_cert_file_path) {
6175 // auto list = SSL_load_client_CA_file(client_ca_cert_file_path);
6176 // SSL_CTX_set_client_CA_list(ctx_, list);
6177 // }
6178
6179 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
6180 client_ca_cert_dir_path);
6181
6182 SSL_CTX_set_verify(
6183 ctx_,
6184 SSL_VERIFY_PEER |
6185 SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
6186 nullptr);
6187 }
6188 }
6189 }
6190
SSLServer(X509 * cert,EVP_PKEY * private_key,X509_STORE * client_ca_cert_store)6191 inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
6192 X509_STORE *client_ca_cert_store) {
6193 ctx_ = SSL_CTX_new(SSLv23_server_method());
6194
6195 if (ctx_) {
6196 SSL_CTX_set_options(ctx_,
6197 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
6198 SSL_OP_NO_COMPRESSION |
6199 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
6200
6201 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
6202 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
6203 SSL_CTX_free(ctx_);
6204 ctx_ = nullptr;
6205 } else if (client_ca_cert_store) {
6206
6207 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
6208
6209 SSL_CTX_set_verify(
6210 ctx_,
6211 SSL_VERIFY_PEER |
6212 SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
6213 nullptr);
6214 }
6215 }
6216 }
6217
~SSLServer()6218 inline SSLServer::~SSLServer() {
6219 if (ctx_) { SSL_CTX_free(ctx_); }
6220 }
6221
is_valid() const6222 inline bool SSLServer::is_valid() const { return ctx_; }
6223
process_and_close_socket(socket_t sock)6224 inline bool SSLServer::process_and_close_socket(socket_t sock) {
6225 auto ssl = detail::ssl_new(
6226 sock, ctx_, ctx_mutex_,
6227 [&](SSL *ssl) {
6228 return detail::ssl_connect_or_accept_nonblocking(
6229 sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_);
6230 },
6231 [](SSL * /*ssl*/) { return true; });
6232
6233 if (ssl) {
6234 auto ret = detail::process_server_socket_ssl(
6235 ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
6236 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
6237 write_timeout_usec_,
6238 [this, ssl](Stream &strm, bool close_connection,
6239 bool &connection_closed) {
6240 return process_request(strm, close_connection, connection_closed,
6241 [&](Request &req) { req.ssl = ssl; });
6242 });
6243
6244 // Shutdown gracefully if the result seemed successful, non-gracefully if
6245 // the connection appeared to be closed.
6246 const bool shutdown_gracefully = ret;
6247 detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
6248 return ret;
6249 }
6250
6251 detail::shutdown_socket(sock);
6252 detail::close_socket(sock);
6253 return false;
6254 }
6255
6256 // SSL HTTP client implementation
SSLClient(const std::string & host)6257 inline SSLClient::SSLClient(const std::string &host)
6258 : SSLClient(host, 443, std::string(), std::string()) {}
6259
SSLClient(const std::string & host,int port)6260 inline SSLClient::SSLClient(const std::string &host, int port)
6261 : SSLClient(host, port, std::string(), std::string()) {}
6262
SSLClient(const std::string & host,int port,const std::string & client_cert_path,const std::string & client_key_path)6263 inline SSLClient::SSLClient(const std::string &host, int port,
6264 const std::string &client_cert_path,
6265 const std::string &client_key_path)
6266 : ClientImpl(host, port, client_cert_path, client_key_path) {
6267 ctx_ = SSL_CTX_new(SSLv23_client_method());
6268
6269 detail::split(&host_[0], &host_[host_.size()], '.',
6270 [&](const char *b, const char *e) {
6271 host_components_.emplace_back(std::string(b, e));
6272 });
6273 if (!client_cert_path.empty() && !client_key_path.empty()) {
6274 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
6275 SSL_FILETYPE_PEM) != 1 ||
6276 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
6277 SSL_FILETYPE_PEM) != 1) {
6278 SSL_CTX_free(ctx_);
6279 ctx_ = nullptr;
6280 }
6281 }
6282 }
6283
SSLClient(const std::string & host,int port,X509 * client_cert,EVP_PKEY * client_key)6284 inline SSLClient::SSLClient(const std::string &host, int port,
6285 X509 *client_cert, EVP_PKEY *client_key)
6286 : ClientImpl(host, port) {
6287 ctx_ = SSL_CTX_new(SSLv23_client_method());
6288
6289 detail::split(&host_[0], &host_[host_.size()], '.',
6290 [&](const char *b, const char *e) {
6291 host_components_.emplace_back(std::string(b, e));
6292 });
6293 if (client_cert != nullptr && client_key != nullptr) {
6294 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
6295 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
6296 SSL_CTX_free(ctx_);
6297 ctx_ = nullptr;
6298 }
6299 }
6300 }
6301
~SSLClient()6302 inline SSLClient::~SSLClient() {
6303 if (ctx_) { SSL_CTX_free(ctx_); }
6304 // Make sure to shut down SSL since shutdown_ssl will resolve to the
6305 // base function rather than the derived function once we get to the
6306 // base class destructor, and won't free the SSL (causing a leak).
6307 SSLClient::shutdown_ssl(socket_, true);
6308 }
6309
is_valid() const6310 inline bool SSLClient::is_valid() const { return ctx_; }
6311
set_ca_cert_path(const char * ca_cert_file_path,const char * ca_cert_dir_path)6312 inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
6313 const char *ca_cert_dir_path) {
6314 if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; }
6315 if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
6316 }
6317
set_ca_cert_store(X509_STORE * ca_cert_store)6318 inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
6319 if (ca_cert_store) {
6320 if (ctx_) {
6321 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
6322 // Free memory allocated for old cert and use new store `ca_cert_store`
6323 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
6324 }
6325 } else {
6326 X509_STORE_free(ca_cert_store);
6327 }
6328 }
6329 }
6330
get_openssl_verify_result() const6331 inline long SSLClient::get_openssl_verify_result() const {
6332 return verify_result_;
6333 }
6334
ssl_context() const6335 inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
6336
create_and_connect_socket(Socket & socket,Error & error)6337 inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
6338 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
6339 }
6340
6341 // Assumes that socket_mutex_ is locked and that there are no requests in flight
connect_with_proxy(Socket & socket,Response & res,bool & success,Error & error)6342 inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
6343 bool &success, Error &error) {
6344 success = true;
6345 Response res2;
6346 if (!detail::process_client_socket(
6347 socket.sock, read_timeout_sec_, read_timeout_usec_,
6348 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
6349 Request req2;
6350 req2.method = "CONNECT";
6351 req2.path = host_and_port_;
6352 return process_request(strm, req2, res2, false, error);
6353 })) {
6354 // Thread-safe to close everything because we are assuming there are no
6355 // requests in flight
6356 shutdown_ssl(socket, true);
6357 shutdown_socket(socket);
6358 close_socket(socket);
6359 success = false;
6360 return false;
6361 }
6362
6363 if (res2.status == 407) {
6364 if (!proxy_digest_auth_username_.empty() &&
6365 !proxy_digest_auth_password_.empty()) {
6366 std::map<std::string, std::string> auth;
6367 if (detail::parse_www_authenticate(res2, auth, true)) {
6368 Response res3;
6369 if (!detail::process_client_socket(
6370 socket.sock, read_timeout_sec_, read_timeout_usec_,
6371 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
6372 Request req3;
6373 req3.method = "CONNECT";
6374 req3.path = host_and_port_;
6375 req3.headers.insert(detail::make_digest_authentication_header(
6376 req3, auth, 1, detail::random_string(10),
6377 proxy_digest_auth_username_, proxy_digest_auth_password_,
6378 true));
6379 return process_request(strm, req3, res3, false, error);
6380 })) {
6381 // Thread-safe to close everything because we are assuming there are
6382 // no requests in flight
6383 shutdown_ssl(socket, true);
6384 shutdown_socket(socket);
6385 close_socket(socket);
6386 success = false;
6387 return false;
6388 }
6389 }
6390 } else {
6391 res = res2;
6392 return false;
6393 }
6394 }
6395
6396 return true;
6397 }
6398
load_certs()6399 inline bool SSLClient::load_certs() {
6400 bool ret = true;
6401
6402 std::call_once(initialize_cert_, [&]() {
6403 std::lock_guard<std::mutex> guard(ctx_mutex_);
6404 if (!ca_cert_file_path_.empty()) {
6405 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
6406 nullptr)) {
6407 ret = false;
6408 }
6409 } else if (!ca_cert_dir_path_.empty()) {
6410 if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
6411 ca_cert_dir_path_.c_str())) {
6412 ret = false;
6413 }
6414 } else {
6415 #ifdef _WIN32
6416 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
6417 #else
6418 SSL_CTX_set_default_verify_paths(ctx_);
6419 #endif
6420 }
6421 });
6422
6423 return ret;
6424 }
6425
initialize_ssl(Socket & socket,Error & error)6426 inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
6427 auto ssl = detail::ssl_new(
6428 socket.sock, ctx_, ctx_mutex_,
6429 [&](SSL *ssl) {
6430 if (server_certificate_verification_) {
6431 if (!load_certs()) {
6432 error = Error::SSLLoadingCerts;
6433 return false;
6434 }
6435 SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
6436 }
6437
6438 if (!detail::ssl_connect_or_accept_nonblocking(
6439 socket.sock, ssl, SSL_connect, connection_timeout_sec_,
6440 connection_timeout_usec_)) {
6441 error = Error::SSLConnection;
6442 return false;
6443 }
6444
6445 if (server_certificate_verification_) {
6446 verify_result_ = SSL_get_verify_result(ssl);
6447
6448 if (verify_result_ != X509_V_OK) {
6449 error = Error::SSLServerVerification;
6450 return false;
6451 }
6452
6453 auto server_cert = SSL_get_peer_certificate(ssl);
6454
6455 if (server_cert == nullptr) {
6456 error = Error::SSLServerVerification;
6457 return false;
6458 }
6459
6460 if (!verify_host(server_cert)) {
6461 X509_free(server_cert);
6462 error = Error::SSLServerVerification;
6463 return false;
6464 }
6465 X509_free(server_cert);
6466 }
6467
6468 return true;
6469 },
6470 [&](SSL *ssl) {
6471 SSL_set_tlsext_host_name(ssl, host_.c_str());
6472 return true;
6473 });
6474
6475 if (ssl) {
6476 socket.ssl = ssl;
6477 return true;
6478 }
6479
6480 shutdown_socket(socket);
6481 close_socket(socket);
6482 return false;
6483 }
6484
shutdown_ssl(Socket & socket,bool shutdown_gracefully)6485 inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
6486 if (socket.sock == INVALID_SOCKET) {
6487 assert(socket.ssl == nullptr);
6488 return;
6489 }
6490 if (socket.ssl) {
6491 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
6492 socket.ssl = nullptr;
6493 }
6494 assert(socket.ssl == nullptr);
6495 }
6496
6497 inline bool
process_socket(const Socket & socket,std::function<bool (Stream & strm)> callback)6498 SSLClient::process_socket(const Socket &socket,
6499 std::function<bool(Stream &strm)> callback) {
6500 assert(socket.ssl);
6501 return detail::process_client_socket_ssl(
6502 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
6503 write_timeout_sec_, write_timeout_usec_, std::move(callback));
6504 }
6505
is_ssl() const6506 inline bool SSLClient::is_ssl() const { return true; }
6507
verify_host(X509 * server_cert) const6508 inline bool SSLClient::verify_host(X509 *server_cert) const {
6509 /* Quote from RFC2818 section 3.1 "Server Identity"
6510
6511 If a subjectAltName extension of type dNSName is present, that MUST
6512 be used as the identity. Otherwise, the (most specific) Common Name
6513 field in the Subject field of the certificate MUST be used. Although
6514 the use of the Common Name is existing practice, it is deprecated and
6515 Certification Authorities are encouraged to use the dNSName instead.
6516
6517 Matching is performed using the matching rules specified by
6518 [RFC2459]. If more than one identity of a given type is present in
6519 the certificate (e.g., more than one dNSName name, a match in any one
6520 of the set is considered acceptable.) Names may contain the wildcard
6521 character * which is considered to match any single domain name
6522 component or component fragment. E.g., *.a.com matches foo.a.com but
6523 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
6524
6525 In some cases, the URI is specified as an IP address rather than a
6526 hostname. In this case, the iPAddress subjectAltName must be present
6527 in the certificate and must exactly match the IP in the URI.
6528
6529 */
6530 return verify_host_with_subject_alt_name(server_cert) ||
6531 verify_host_with_common_name(server_cert);
6532 }
6533
6534 inline bool
verify_host_with_subject_alt_name(X509 * server_cert) const6535 SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
6536 auto ret = false;
6537
6538 auto type = GEN_DNS;
6539
6540 struct in6_addr addr6;
6541 struct in_addr addr;
6542 size_t addr_len = 0;
6543
6544 #ifndef __MINGW32__
6545 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
6546 type = GEN_IPADD;
6547 addr_len = sizeof(struct in6_addr);
6548 } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
6549 type = GEN_IPADD;
6550 addr_len = sizeof(struct in_addr);
6551 }
6552 #endif
6553
6554 auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
6555 X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
6556
6557 if (alt_names) {
6558 auto dsn_matched = false;
6559 auto ip_mached = false;
6560
6561 auto count = sk_GENERAL_NAME_num(alt_names);
6562
6563 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
6564 auto val = sk_GENERAL_NAME_value(alt_names, i);
6565 if (val->type == type) {
6566 auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
6567 auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
6568
6569 switch (type) {
6570 case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
6571
6572 case GEN_IPADD:
6573 if (!memcmp(&addr6, name, addr_len) ||
6574 !memcmp(&addr, name, addr_len)) {
6575 ip_mached = true;
6576 }
6577 break;
6578 }
6579 }
6580 }
6581
6582 if (dsn_matched || ip_mached) { ret = true; }
6583 }
6584
6585 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
6586 return ret;
6587 }
6588
verify_host_with_common_name(X509 * server_cert) const6589 inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
6590 const auto subject_name = X509_get_subject_name(server_cert);
6591
6592 if (subject_name != nullptr) {
6593 char name[BUFSIZ];
6594 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
6595 name, sizeof(name));
6596
6597 if (name_len != -1) {
6598 return check_host_name(name, static_cast<size_t>(name_len));
6599 }
6600 }
6601
6602 return false;
6603 }
6604
check_host_name(const char * pattern,size_t pattern_len) const6605 inline bool SSLClient::check_host_name(const char *pattern,
6606 size_t pattern_len) const {
6607 if (host_.size() == pattern_len && host_ == pattern) { return true; }
6608
6609 // Wildcard match
6610 // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
6611 std::vector<std::string> pattern_components;
6612 detail::split(&pattern[0], &pattern[pattern_len], '.',
6613 [&](const char *b, const char *e) {
6614 pattern_components.emplace_back(std::string(b, e));
6615 });
6616
6617 if (host_components_.size() != pattern_components.size()) { return false; }
6618
6619 auto itr = pattern_components.begin();
6620 for (const auto &h : host_components_) {
6621 auto &p = *itr;
6622 if (p != h && p != "*") {
6623 auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
6624 !p.compare(0, p.size() - 1, h));
6625 if (!partial_match) { return false; }
6626 }
6627 ++itr;
6628 }
6629
6630 return true;
6631 }
6632 #endif
6633
6634 // Universal client implementation
Client(const char * scheme_host_port)6635 inline Client::Client(const char *scheme_host_port)
6636 : Client(scheme_host_port, std::string(), std::string()) {}
6637
Client(const char * scheme_host_port,const std::string & client_cert_path,const std::string & client_key_path)6638 inline Client::Client(const char *scheme_host_port,
6639 const std::string &client_cert_path,
6640 const std::string &client_key_path) {
6641 const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)");
6642
6643 std::cmatch m;
6644 if (std::regex_match(scheme_host_port, m, re)) {
6645 auto scheme = m[1].str();
6646
6647 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6648 if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
6649 #else
6650 if (!scheme.empty() && scheme != "http") {
6651 #endif
6652 std::string msg = "'" + scheme + "' scheme is not supported.";
6653 throw std::invalid_argument(msg);
6654 return;
6655 }
6656
6657 auto is_ssl = scheme == "https";
6658
6659 auto host = m[2].str();
6660
6661 auto port_str = m[3].str();
6662 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
6663
6664 if (is_ssl) {
6665 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6666 cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
6667 client_cert_path, client_key_path);
6668 is_ssl_ = is_ssl;
6669 #endif
6670 } else {
6671 cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
6672 client_cert_path, client_key_path);
6673 }
6674 } else {
6675 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
6676 client_cert_path, client_key_path);
6677 }
6678 }
6679
6680 inline Client::Client(const std::string &host, int port)
6681 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
6682
6683 inline Client::Client(const std::string &host, int port,
6684 const std::string &client_cert_path,
6685 const std::string &client_key_path)
6686 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
6687 client_key_path)) {}
6688
6689 inline Client::~Client() {}
6690
6691 inline bool Client::is_valid() const {
6692 return cli_ != nullptr && cli_->is_valid();
6693 }
6694
6695 inline Result Client::Get(const char *path) { return cli_->Get(path); }
6696 inline Result Client::Get(const char *path, const Headers &headers) {
6697 return cli_->Get(path, headers);
6698 }
6699 inline Result Client::Get(const char *path, Progress progress) {
6700 return cli_->Get(path, std::move(progress));
6701 }
6702 inline Result Client::Get(const char *path, const Headers &headers,
6703 Progress progress) {
6704 return cli_->Get(path, headers, std::move(progress));
6705 }
6706 inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
6707 return cli_->Get(path, std::move(content_receiver));
6708 }
6709 inline Result Client::Get(const char *path, const Headers &headers,
6710 ContentReceiver content_receiver) {
6711 return cli_->Get(path, headers, std::move(content_receiver));
6712 }
6713 inline Result Client::Get(const char *path, ContentReceiver content_receiver,
6714 Progress progress) {
6715 return cli_->Get(path, std::move(content_receiver), std::move(progress));
6716 }
6717 inline Result Client::Get(const char *path, const Headers &headers,
6718 ContentReceiver content_receiver, Progress progress) {
6719 return cli_->Get(path, headers, std::move(content_receiver),
6720 std::move(progress));
6721 }
6722 inline Result Client::Get(const char *path, ResponseHandler response_handler,
6723 ContentReceiver content_receiver) {
6724 return cli_->Get(path, std::move(response_handler),
6725 std::move(content_receiver));
6726 }
6727 inline Result Client::Get(const char *path, const Headers &headers,
6728 ResponseHandler response_handler,
6729 ContentReceiver content_receiver) {
6730 return cli_->Get(path, headers, std::move(response_handler),
6731 std::move(content_receiver));
6732 }
6733 inline Result Client::Get(const char *path, ResponseHandler response_handler,
6734 ContentReceiver content_receiver, Progress progress) {
6735 return cli_->Get(path, std::move(response_handler),
6736 std::move(content_receiver), std::move(progress));
6737 }
6738 inline Result Client::Get(const char *path, const Headers &headers,
6739 ResponseHandler response_handler,
6740 ContentReceiver content_receiver, Progress progress) {
6741 return cli_->Get(path, headers, std::move(response_handler),
6742 std::move(content_receiver), std::move(progress));
6743 }
6744
6745 inline Result Client::Head(const char *path) { return cli_->Head(path); }
6746 inline Result Client::Head(const char *path, const Headers &headers) {
6747 return cli_->Head(path, headers);
6748 }
6749
6750 inline Result Client::Post(const char *path) { return cli_->Post(path); }
6751 inline Result Client::Post(const char *path, const std::string &body,
6752 const char *content_type) {
6753 return cli_->Post(path, body, content_type);
6754 }
6755 inline Result Client::Post(const char *path, const Headers &headers,
6756 const std::string &body, const char *content_type) {
6757 return cli_->Post(path, headers, body, content_type);
6758 }
6759 inline Result Client::Post(const char *path, size_t content_length,
6760 ContentProvider content_provider,
6761 const char *content_type) {
6762 return cli_->Post(path, content_length, std::move(content_provider),
6763 content_type);
6764 }
6765 inline Result Client::Post(const char *path,
6766 ContentProviderWithoutLength content_provider,
6767 const char *content_type) {
6768 return cli_->Post(path, std::move(content_provider), content_type);
6769 }
6770 inline Result Client::Post(const char *path, const Headers &headers,
6771 size_t content_length,
6772 ContentProvider content_provider,
6773 const char *content_type) {
6774 return cli_->Post(path, headers, content_length, std::move(content_provider),
6775 content_type);
6776 }
6777 inline Result Client::Post(const char *path, const Headers &headers,
6778 ContentProviderWithoutLength content_provider,
6779 const char *content_type) {
6780 return cli_->Post(path, headers, std::move(content_provider), content_type);
6781 }
6782 inline Result Client::Post(const char *path, const Params ¶ms) {
6783 return cli_->Post(path, params);
6784 }
6785 inline Result Client::Post(const char *path, const Headers &headers,
6786 const Params ¶ms) {
6787 return cli_->Post(path, headers, params);
6788 }
6789 inline Result Client::Post(const char *path,
6790 const MultipartFormDataItems &items) {
6791 return cli_->Post(path, items);
6792 }
6793 inline Result Client::Post(const char *path, const Headers &headers,
6794 const MultipartFormDataItems &items) {
6795 return cli_->Post(path, headers, items);
6796 }
6797 inline Result Client::Post(const char *path, const Headers &headers,
6798 const MultipartFormDataItems &items,
6799 const std::string &boundary) {
6800 return cli_->Post(path, headers, items, boundary);
6801 }
6802 inline Result Client::Put(const char *path) { return cli_->Put(path); }
6803 inline Result Client::Put(const char *path, const std::string &body,
6804 const char *content_type) {
6805 return cli_->Put(path, body, content_type);
6806 }
6807 inline Result Client::Put(const char *path, const Headers &headers,
6808 const std::string &body, const char *content_type) {
6809 return cli_->Put(path, headers, body, content_type);
6810 }
6811 inline Result Client::Put(const char *path, size_t content_length,
6812 ContentProvider content_provider,
6813 const char *content_type) {
6814 return cli_->Put(path, content_length, std::move(content_provider),
6815 content_type);
6816 }
6817 inline Result Client::Put(const char *path,
6818 ContentProviderWithoutLength content_provider,
6819 const char *content_type) {
6820 return cli_->Put(path, std::move(content_provider), content_type);
6821 }
6822 inline Result Client::Put(const char *path, const Headers &headers,
6823 size_t content_length,
6824 ContentProvider content_provider,
6825 const char *content_type) {
6826 return cli_->Put(path, headers, content_length, std::move(content_provider),
6827 content_type);
6828 }
6829 inline Result Client::Put(const char *path, const Headers &headers,
6830 ContentProviderWithoutLength content_provider,
6831 const char *content_type) {
6832 return cli_->Put(path, headers, std::move(content_provider), content_type);
6833 }
6834 inline Result Client::Put(const char *path, const Params ¶ms) {
6835 return cli_->Put(path, params);
6836 }
6837 inline Result Client::Put(const char *path, const Headers &headers,
6838 const Params ¶ms) {
6839 return cli_->Put(path, headers, params);
6840 }
6841 inline Result Client::Patch(const char *path, const std::string &body,
6842 const char *content_type) {
6843 return cli_->Patch(path, body, content_type);
6844 }
6845 inline Result Client::Patch(const char *path, const Headers &headers,
6846 const std::string &body, const char *content_type) {
6847 return cli_->Patch(path, headers, body, content_type);
6848 }
6849 inline Result Client::Patch(const char *path, size_t content_length,
6850 ContentProvider content_provider,
6851 const char *content_type) {
6852 return cli_->Patch(path, content_length, std::move(content_provider),
6853 content_type);
6854 }
6855 inline Result Client::Patch(const char *path,
6856 ContentProviderWithoutLength content_provider,
6857 const char *content_type) {
6858 return cli_->Patch(path, std::move(content_provider), content_type);
6859 }
6860 inline Result Client::Patch(const char *path, const Headers &headers,
6861 size_t content_length,
6862 ContentProvider content_provider,
6863 const char *content_type) {
6864 return cli_->Patch(path, headers, content_length, std::move(content_provider),
6865 content_type);
6866 }
6867 inline Result Client::Patch(const char *path, const Headers &headers,
6868 ContentProviderWithoutLength content_provider,
6869 const char *content_type) {
6870 return cli_->Patch(path, headers, std::move(content_provider), content_type);
6871 }
6872 inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
6873 inline Result Client::Delete(const char *path, const std::string &body,
6874 const char *content_type) {
6875 return cli_->Delete(path, body, content_type);
6876 }
6877 inline Result Client::Delete(const char *path, const Headers &headers) {
6878 return cli_->Delete(path, headers);
6879 }
6880 inline Result Client::Delete(const char *path, const Headers &headers,
6881 const std::string &body,
6882 const char *content_type) {
6883 return cli_->Delete(path, headers, body, content_type);
6884 }
6885 inline Result Client::Options(const char *path) { return cli_->Options(path); }
6886 inline Result Client::Options(const char *path, const Headers &headers) {
6887 return cli_->Options(path, headers);
6888 }
6889
6890 inline bool Client::send(const Request &req, Response &res, Error &error) {
6891 return cli_->send(req, res, error);
6892 }
6893
6894 inline Result Client::send(const Request &req) { return cli_->send(req); }
6895
6896 inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
6897
6898 inline void Client::stop() { cli_->stop(); }
6899
6900 inline void Client::set_default_headers(Headers headers) {
6901 cli_->set_default_headers(std::move(headers));
6902 }
6903
6904 inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6905 inline void Client::set_socket_options(SocketOptions socket_options) {
6906 cli_->set_socket_options(std::move(socket_options));
6907 }
6908
6909 inline void Client::set_connection_timeout(time_t sec, time_t usec) {
6910 cli_->set_connection_timeout(sec, usec);
6911 }
6912 inline void Client::set_read_timeout(time_t sec, time_t usec) {
6913 cli_->set_read_timeout(sec, usec);
6914 }
6915 inline void Client::set_write_timeout(time_t sec, time_t usec) {
6916 cli_->set_write_timeout(sec, usec);
6917 }
6918
6919 inline void Client::set_basic_auth(const char *username, const char *password) {
6920 cli_->set_basic_auth(username, password);
6921 }
6922 inline void Client::set_bearer_token_auth(const char *token) {
6923 cli_->set_bearer_token_auth(token);
6924 }
6925 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6926 inline void Client::set_digest_auth(const char *username,
6927 const char *password) {
6928 cli_->set_digest_auth(username, password);
6929 }
6930 #endif
6931
6932 inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
6933 inline void Client::set_follow_location(bool on) {
6934 cli_->set_follow_location(on);
6935 }
6936
6937 inline void Client::set_compress(bool on) { cli_->set_compress(on); }
6938
6939 inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
6940
6941 inline void Client::set_interface(const char *intf) {
6942 cli_->set_interface(intf);
6943 }
6944
6945 inline void Client::set_proxy(const char *host, int port) {
6946 cli_->set_proxy(host, port);
6947 }
6948 inline void Client::set_proxy_basic_auth(const char *username,
6949 const char *password) {
6950 cli_->set_proxy_basic_auth(username, password);
6951 }
6952 inline void Client::set_proxy_bearer_token_auth(const char *token) {
6953 cli_->set_proxy_bearer_token_auth(token);
6954 }
6955 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6956 inline void Client::set_proxy_digest_auth(const char *username,
6957 const char *password) {
6958 cli_->set_proxy_digest_auth(username, password);
6959 }
6960 #endif
6961
6962 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6963 inline void Client::enable_server_certificate_verification(bool enabled) {
6964 cli_->enable_server_certificate_verification(enabled);
6965 }
6966 #endif
6967
6968 inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
6969
6970 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6971 inline void Client::set_ca_cert_path(const char *ca_cert_file_path,
6972 const char *ca_cert_dir_path) {
6973 if (is_ssl_) {
6974 static_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path,
6975 ca_cert_dir_path);
6976 }
6977 }
6978
6979 inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
6980 if (is_ssl_) {
6981 static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
6982 }
6983 }
6984
6985 inline long Client::get_openssl_verify_result() const {
6986 if (is_ssl_) {
6987 return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
6988 }
6989 return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
6990 }
6991
6992 inline SSL_CTX *Client::ssl_context() const {
6993 if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
6994 return nullptr;
6995 }
6996 #endif
6997
6998 // ----------------------------------------------------------------------------
6999
7000 } // namespace httplib
7001
7002 #endif // CPPHTTPLIB_HTTPLIB_H
7003