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