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