summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--externals/httplib/httplib.h1153
1 files changed, 793 insertions, 360 deletions
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index 002b874b9..ad4ce279e 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -114,13 +114,14 @@ using socket_t = SOCKET;
114 114
115#include <arpa/inet.h> 115#include <arpa/inet.h>
116#include <cstring> 116#include <cstring>
117#include <ifaddrs.h>
117#include <netdb.h> 118#include <netdb.h>
118#include <netinet/in.h> 119#include <netinet/in.h>
119#ifdef CPPHTTPLIB_USE_POLL 120#ifdef CPPHTTPLIB_USE_POLL
120#include <poll.h> 121#include <poll.h>
121#endif 122#endif
122#include <pthread.h>
123#include <csignal> 123#include <csignal>
124#include <pthread.h>
124#include <sys/select.h> 125#include <sys/select.h>
125#include <sys/socket.h> 126#include <sys/socket.h>
126#include <unistd.h> 127#include <unistd.h>
@@ -129,8 +130,9 @@ using socket_t = int;
129#define INVALID_SOCKET (-1) 130#define INVALID_SOCKET (-1)
130#endif //_WIN32 131#endif //_WIN32
131 132
132#include <cassert> 133#include <array>
133#include <atomic> 134#include <atomic>
135#include <cassert>
134#include <condition_variable> 136#include <condition_variable>
135#include <errno.h> 137#include <errno.h>
136#include <fcntl.h> 138#include <fcntl.h>
@@ -145,13 +147,16 @@ using socket_t = int;
145#include <string> 147#include <string>
146#include <sys/stat.h> 148#include <sys/stat.h>
147#include <thread> 149#include <thread>
148#include <array>
149 150
150#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 151#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
151#include <openssl/err.h> 152#include <openssl/err.h>
153#include <openssl/md5.h>
152#include <openssl/ssl.h> 154#include <openssl/ssl.h>
153#include <openssl/x509v3.h> 155#include <openssl/x509v3.h>
154 156
157#include <iomanip>
158#include <sstream>
159
155// #if OPENSSL_VERSION_NUMBER < 0x1010100fL 160// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
156// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported 161// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
157// #endif 162// #endif
@@ -196,27 +201,17 @@ using DataSink = std::function<void(const char *data, size_t data_len)>;
196 201
197using Done = std::function<void()>; 202using Done = std::function<void()>;
198 203
199using ContentProvider = std::function<void(size_t offset, size_t length, DataSink sink)>; 204using ContentProvider =
200 205 std::function<void(size_t offset, size_t length, DataSink sink)>;
201using ContentProviderWithCloser = std::function<void(size_t offset, size_t length, DataSink sink, Done done)>;
202
203using ContentReceiver = std::function<bool(const char *data, size_t data_length)>;
204 206
205using ContentReader = std::function<bool(ContentReceiver receiver)>; 207using ContentProviderWithCloser =
208 std::function<void(size_t offset, size_t length, DataSink sink, Done done)>;
206 209
207using Progress = std::function<bool(uint64_t current, uint64_t total)>; 210using Progress = std::function<bool(uint64_t current, uint64_t total)>;
208 211
209struct Response; 212struct Response;
210using ResponseHandler = std::function<bool(const Response &response)>; 213using ResponseHandler = std::function<bool(const Response &response)>;
211 214
212struct MultipartFile {
213 std::string filename;
214 std::string content_type;
215 size_t offset = 0;
216 size_t length = 0;
217};
218using MultipartFiles = std::multimap<std::string, MultipartFile>;
219
220struct MultipartFormData { 215struct MultipartFormData {
221 std::string name; 216 std::string name;
222 std::string content; 217 std::string content;
@@ -224,6 +219,33 @@ struct MultipartFormData {
224 std::string content_type; 219 std::string content_type;
225}; 220};
226using MultipartFormDataItems = std::vector<MultipartFormData>; 221using MultipartFormDataItems = std::vector<MultipartFormData>;
222using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
223
224using ContentReceiver =
225 std::function<bool(const char *data, size_t data_length)>;
226
227using MultipartContentHeader =
228 std::function<bool(const MultipartFormData &file)>;
229
230class ContentReader {
231public:
232 using Reader = std::function<bool(ContentReceiver receiver)>;
233 using MultipartReader = std::function<bool(MultipartContentHeader header,
234 ContentReceiver receiver)>;
235
236 ContentReader(Reader reader, MultipartReader muitlpart_reader)
237 : reader_(reader), muitlpart_reader_(muitlpart_reader) {}
238
239 bool operator()(MultipartContentHeader header,
240 ContentReceiver receiver) const {
241 return muitlpart_reader_(header, receiver);
242 }
243
244 bool operator()(ContentReceiver receiver) const { return reader_(receiver); }
245
246 Reader reader_;
247 MultipartReader muitlpart_reader_;
248};
227 249
228using Range = std::pair<ssize_t, ssize_t>; 250using Range = std::pair<ssize_t, ssize_t>;
229using Ranges = std::vector<Range>; 251using Ranges = std::vector<Range>;
@@ -238,7 +260,7 @@ struct Request {
238 std::string version; 260 std::string version;
239 std::string target; 261 std::string target;
240 Params params; 262 Params params;
241 MultipartFiles files; 263 MultipartFormDataMap files;
242 Ranges ranges; 264 Ranges ranges;
243 Match matches; 265 Match matches;
244 266
@@ -262,8 +284,10 @@ struct Request {
262 std::string get_param_value(const char *key, size_t id = 0) const; 284 std::string get_param_value(const char *key, size_t id = 0) const;
263 size_t get_param_value_count(const char *key) const; 285 size_t get_param_value_count(const char *key) const;
264 286
287 bool is_multipart_form_data() const;
288
265 bool has_file(const char *key) const; 289 bool has_file(const char *key) const;
266 MultipartFile get_file_value(const char *key) const; 290 MultipartFormData get_file_value(const char *key) const;
267 291
268 // private members... 292 // private members...
269 size_t content_length; 293 size_t content_length;
@@ -394,7 +418,7 @@ public:
394 cond_.notify_all(); 418 cond_.notify_all();
395 419
396 // Join... 420 // Join...
397 for (auto& t : threads_) { 421 for (auto &t : threads_) {
398 t.join(); 422 t.join();
399 } 423 }
400 } 424 }
@@ -475,20 +499,17 @@ public:
475 NoThread() {} 499 NoThread() {}
476 virtual ~NoThread() {} 500 virtual ~NoThread() {}
477 501
478 virtual void enqueue(std::function<void()> fn) override { 502 virtual void enqueue(std::function<void()> fn) override { fn(); }
479 fn();
480 }
481 503
482 virtual void shutdown() override { 504 virtual void shutdown() override {}
483 }
484}; 505};
485#endif 506#endif
486 507
487class Server { 508class Server {
488public: 509public:
489 using Handler = std::function<void(const Request &, Response &)>; 510 using Handler = std::function<void(const Request &, Response &)>;
490 using HandlerWithContentReader = std::function<void(const Request &, Response &, 511 using HandlerWithContentReader = std::function<void(
491 const ContentReader &content_reader)>; 512 const Request &, Response &, const ContentReader &content_reader)>;
492 using Logger = std::function<void(const Request &, const Response &)>; 513 using Logger = std::function<void(const Request &, const Response &)>;
493 514
494 Server(); 515 Server();
@@ -507,7 +528,7 @@ public:
507 Server &Delete(const char *pattern, Handler handler); 528 Server &Delete(const char *pattern, Handler handler);
508 Server &Options(const char *pattern, Handler handler); 529 Server &Options(const char *pattern, Handler handler);
509 530
510 bool set_base_dir(const char *path); 531 bool set_base_dir(const char *dir, const char *mount_point = nullptr);
511 void set_file_request_handler(Handler handler); 532 void set_file_request_handler(Handler handler);
512 533
513 void set_error_handler(Handler handler); 534 void set_error_handler(Handler handler);
@@ -531,7 +552,7 @@ public:
531protected: 552protected:
532 bool process_request(Stream &strm, bool last_connection, 553 bool process_request(Stream &strm, bool last_connection,
533 bool &connection_close, 554 bool &connection_close,
534 const std::function<void(Request &)>& setup_request); 555 const std::function<void(Request &)> &setup_request);
535 556
536 size_t keep_alive_max_count_; 557 size_t keep_alive_max_count_;
537 time_t read_timeout_sec_; 558 time_t read_timeout_sec_;
@@ -540,7 +561,8 @@ protected:
540 561
541private: 562private:
542 using Handlers = std::vector<std::pair<std::regex, Handler>>; 563 using Handlers = std::vector<std::pair<std::regex, Handler>>;
543 using HandersForContentReader = std::vector<std::pair<std::regex, HandlerWithContentReader>>; 564 using HandersForContentReader =
565 std::vector<std::pair<std::regex, HandlerWithContentReader>>;
544 566
545 socket_t create_server_socket(const char *host, int port, 567 socket_t create_server_socket(const char *host, int port,
546 int socket_flags) const; 568 int socket_flags) const;
@@ -562,23 +584,28 @@ private:
562 const std::string &content_type); 584 const std::string &content_type);
563 bool read_content(Stream &strm, bool last_connection, Request &req, 585 bool read_content(Stream &strm, bool last_connection, Request &req,
564 Response &res); 586 Response &res);
565 bool read_content_with_content_receiver(Stream &strm, bool last_connection, 587 bool read_content_with_content_receiver(
566 Request &req, Response &res, 588 Stream &strm, bool last_connection, Request &req, Response &res,
567 ContentReceiver reveiver); 589 ContentReceiver receiver, MultipartContentHeader multipart_header,
590 ContentReceiver multipart_receiver);
591 bool read_content_core(Stream &strm, bool last_connection, Request &req,
592 Response &res, ContentReceiver receiver,
593 MultipartContentHeader mulitpart_header,
594 ContentReceiver multipart_receiver);
568 595
569 virtual bool process_and_close_socket(socket_t sock); 596 virtual bool process_and_close_socket(socket_t sock);
570 597
571 std::atomic<bool> is_running_; 598 std::atomic<bool> is_running_;
572 std::atomic<socket_t> svr_sock_; 599 std::atomic<socket_t> svr_sock_;
573 std::string base_dir_; 600 std::vector<std::pair<std::string, std::string>> base_dirs_;
574 Handler file_request_handler_; 601 Handler file_request_handler_;
575 Handlers get_handlers_; 602 Handlers get_handlers_;
576 Handlers post_handlers_; 603 Handlers post_handlers_;
577 HandersForContentReader post_handlers_for_content_reader; 604 HandersForContentReader post_handlers_for_content_reader_;
578 Handlers put_handlers_; 605 Handlers put_handlers_;
579 HandersForContentReader put_handlers_for_content_reader; 606 HandersForContentReader put_handlers_for_content_reader_;
580 Handlers patch_handlers_; 607 Handlers patch_handlers_;
581 HandersForContentReader patch_handlers_for_content_reader; 608 HandersForContentReader patch_handlers_for_content_reader_;
582 Handlers delete_handlers_; 609 Handlers delete_handlers_;
583 Handlers options_handlers_; 610 Handlers options_handlers_;
584 Handler error_handler_; 611 Handler error_handler_;
@@ -629,78 +656,63 @@ public:
629 std::shared_ptr<Response> Head(const char *path, const Headers &headers); 656 std::shared_ptr<Response> Head(const char *path, const Headers &headers);
630 657
631 std::shared_ptr<Response> Post(const char *path, const std::string &body, 658 std::shared_ptr<Response> Post(const char *path, const std::string &body,
632 const char *content_type, 659 const char *content_type);
633 bool compress = false);
634 660
635 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 661 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
636 const std::string &body, 662 const std::string &body,
637 const char *content_type, 663 const char *content_type);
638 bool compress = false);
639 664
640 std::shared_ptr<Response> Post(const char *path, size_t content_length, 665 std::shared_ptr<Response> Post(const char *path, size_t content_length,
641 ContentProvider content_provider, 666 ContentProvider content_provider,
642 const char *content_type, 667 const char *content_type);
643 bool compress = false);
644 668
645 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 669 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
646 size_t content_length, 670 size_t content_length,
647 ContentProvider content_provider, 671 ContentProvider content_provider,
648 const char *content_type, 672 const char *content_type);
649 bool compress = false);
650 673
651 std::shared_ptr<Response> Post(const char *path, const Params &params, 674 std::shared_ptr<Response> Post(const char *path, const Params &params);
652 bool compress = false);
653 675
654 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 676 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
655 const Params &params, bool compress = false); 677 const Params &params);
656 678
657 std::shared_ptr<Response> Post(const char *path, 679 std::shared_ptr<Response> Post(const char *path,
658 const MultipartFormDataItems &items, 680 const MultipartFormDataItems &items);
659 bool compress = false);
660 681
661 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 682 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
662 const MultipartFormDataItems &items, 683 const MultipartFormDataItems &items);
663 bool compress = false);
664 684
665 std::shared_ptr<Response> Put(const char *path, const std::string &body, 685 std::shared_ptr<Response> Put(const char *path, const std::string &body,
666 const char *content_type, 686 const char *content_type);
667 bool compress = false);
668 687
669 std::shared_ptr<Response> Put(const char *path, const Headers &headers, 688 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
670 const std::string &body, 689 const std::string &body,
671 const char *content_type, 690 const char *content_type);
672 bool compress = false);
673 691
674 std::shared_ptr<Response> Put(const char *path, size_t content_length, 692 std::shared_ptr<Response> Put(const char *path, size_t content_length,
675 ContentProvider content_provider, 693 ContentProvider content_provider,
676 const char *content_type, 694 const char *content_type);
677 bool compress = false);
678 695
679 std::shared_ptr<Response> Put(const char *path, const Headers &headers, 696 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
680 size_t content_length, 697 size_t content_length,
681 ContentProvider content_provider, 698 ContentProvider content_provider,
682 const char *content_type, 699 const char *content_type);
683 bool compress = false);
684 700
685 std::shared_ptr<Response> Patch(const char *path, const std::string &body, 701 std::shared_ptr<Response> Patch(const char *path, const std::string &body,
686 const char *content_type, 702 const char *content_type);
687 bool compress = false);
688 703
689 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, 704 std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
690 const std::string &body, 705 const std::string &body,
691 const char *content_type, 706 const char *content_type);
692 bool compress = false);
693 707
694 std::shared_ptr<Response> Patch(const char *path, size_t content_length, 708 std::shared_ptr<Response> Patch(const char *path, size_t content_length,
695 ContentProvider content_provider, 709 ContentProvider content_provider,
696 const char *content_type, 710 const char *content_type);
697 bool compress = false);
698 711
699 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, 712 std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
700 size_t content_length, 713 size_t content_length,
701 ContentProvider content_provider, 714 ContentProvider content_provider,
702 const char *content_type, 715 const char *content_type);
703 bool compress = false);
704 716
705 std::shared_ptr<Response> Delete(const char *path); 717 std::shared_ptr<Response> Delete(const char *path);
706 718
@@ -723,9 +735,16 @@ public:
723 std::vector<Response> &responses); 735 std::vector<Response> &responses);
724 736
725 void set_keep_alive_max_count(size_t count); 737 void set_keep_alive_max_count(size_t count);
738
726 void set_read_timeout(time_t sec, time_t usec); 739 void set_read_timeout(time_t sec, time_t usec);
727 740
728 void follow_location(bool on); 741 void set_auth(const char *username, const char *password);
742
743 void set_follow_location(bool on);
744
745 void set_compress(bool on);
746
747 void set_interface(const char *intf);
729 748
730protected: 749protected:
731 bool process_request(Stream &strm, const Request &req, Response &res, 750 bool process_request(Stream &strm, const Request &req, Response &res,
@@ -738,20 +757,22 @@ protected:
738 size_t keep_alive_max_count_; 757 size_t keep_alive_max_count_;
739 time_t read_timeout_sec_; 758 time_t read_timeout_sec_;
740 time_t read_timeout_usec_; 759 time_t read_timeout_usec_;
741 size_t follow_location_; 760 bool follow_location_;
761 std::string username_;
762 std::string password_;
763 bool compress_;
764 std::string interface_;
742 765
743private: 766private:
744 socket_t create_client_socket() const; 767 socket_t create_client_socket() const;
745 bool read_response_line(Stream &strm, Response &res); 768 bool read_response_line(Stream &strm, Response &res);
746 void write_request(Stream &strm, const Request &req, bool last_connection); 769 bool write_request(Stream &strm, const Request &req, bool last_connection);
747 bool redirect(const Request &req, Response &res); 770 bool redirect(const Request &req, Response &res);
748 771
749 std::shared_ptr<Response> 772 std::shared_ptr<Response> send_with_content_provider(
750 send_with_content_provider(const char *method, const char *path, 773 const char *method, const char *path, const Headers &headers,
751 const Headers &headers, const std::string &body, 774 const std::string &body, size_t content_length,
752 size_t content_length, 775 ContentProvider content_provider, const char *content_type);
753 ContentProvider content_provider,
754 const char *content_type, bool compress);
755 776
756 virtual bool process_and_close_socket( 777 virtual bool process_and_close_socket(
757 socket_t sock, size_t request_count, 778 socket_t sock, size_t request_count,
@@ -870,6 +891,8 @@ private:
870}; 891};
871#endif 892#endif
872 893
894// ----------------------------------------------------------------------------
895
873/* 896/*
874 * Implementation 897 * Implementation
875 */ 898 */
@@ -1037,7 +1060,7 @@ inline void read_file(const std::string &path, std::string &out) {
1037 1060
1038inline std::string file_extension(const std::string &path) { 1061inline std::string file_extension(const std::string &path) {
1039 std::smatch m; 1062 std::smatch m;
1040 auto re = std::regex("\\.([a-zA-Z0-9]+)$"); 1063 static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
1041 if (std::regex_search(path, m, re)) { return m[1].str(); } 1064 if (std::regex_search(path, m, re)) { return m[1].str(); }
1042 return std::string(); 1065 return std::string();
1043} 1066}
@@ -1081,6 +1104,11 @@ public:
1081 } 1104 }
1082 } 1105 }
1083 1106
1107 bool end_with_crlf() const {
1108 auto end = ptr() + size();
1109 return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
1110 }
1111
1084 bool getline() { 1112 bool getline() {
1085 fixed_buffer_used_size_ = 0; 1113 fixed_buffer_used_size_ = 0;
1086 glowable_buffer_.clear(); 1114 glowable_buffer_.clear();
@@ -1191,7 +1219,8 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1191 (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { 1219 (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
1192 int error = 0; 1220 int error = 0;
1193 socklen_t len = sizeof(error); 1221 socklen_t len = sizeof(error);
1194 return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) >= 0 && 1222 return getsockopt(sock, SOL_SOCKET, SO_ERROR,
1223 reinterpret_cast<char *>(&error), &len) >= 0 &&
1195 !error; 1224 !error;
1196 } 1225 }
1197 return false; 1226 return false;
@@ -1323,6 +1352,78 @@ inline bool is_connection_error() {
1323#endif 1352#endif
1324} 1353}
1325 1354
1355inline bool bind_ip_address(socket_t sock, const char *host) {
1356 struct addrinfo hints;
1357 struct addrinfo *result;
1358
1359 memset(&hints, 0, sizeof(struct addrinfo));
1360 hints.ai_family = AF_UNSPEC;
1361 hints.ai_socktype = SOCK_STREAM;
1362 hints.ai_protocol = 0;
1363
1364 if (getaddrinfo(host, "0", &hints, &result)) { return false; }
1365
1366 bool ret = false;
1367 for (auto rp = result; rp; rp = rp->ai_next) {
1368 const auto &ai = *rp;
1369 if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
1370 ret = true;
1371 break;
1372 }
1373 }
1374
1375 freeaddrinfo(result);
1376 return ret;
1377}
1378
1379inline std::string if2ip(const std::string &ifn) {
1380#ifndef _WIN32
1381 struct ifaddrs *ifap;
1382 getifaddrs(&ifap);
1383 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
1384 if (ifa->ifa_addr && ifn == ifa->ifa_name) {
1385 if (ifa->ifa_addr->sa_family == AF_INET) {
1386 auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
1387 char buf[INET_ADDRSTRLEN];
1388 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
1389 freeifaddrs(ifap);
1390 return std::string(buf, INET_ADDRSTRLEN);
1391 }
1392 }
1393 }
1394 }
1395 freeifaddrs(ifap);
1396#endif
1397 return std::string();
1398}
1399
1400inline socket_t create_client_socket(const char *host, int port,
1401 time_t timeout_sec,
1402 const std::string &intf) {
1403 return create_socket(
1404 host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
1405 if (!intf.empty()) {
1406 auto ip = if2ip(intf);
1407 if (ip.empty()) { ip = intf; }
1408 if (!bind_ip_address(sock, ip.c_str())) { return false; }
1409 }
1410
1411 set_nonblocking(sock, true);
1412
1413 auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
1414 if (ret < 0) {
1415 if (is_connection_error() ||
1416 !wait_until_socket_is_ready(sock, timeout_sec, 0)) {
1417 close_socket(sock);
1418 return false;
1419 }
1420 }
1421
1422 set_nonblocking(sock, false);
1423 return true;
1424 });
1425}
1426
1326inline std::string get_remote_addr(socket_t sock) { 1427inline std::string get_remote_addr(socket_t sock) {
1327 struct sockaddr_storage addr; 1428 struct sockaddr_storage addr;
1328 socklen_t len = sizeof(addr); 1429 socklen_t len = sizeof(addr);
@@ -1330,8 +1431,8 @@ inline std::string get_remote_addr(socket_t sock) {
1330 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) { 1431 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) {
1331 std::array<char, NI_MAXHOST> ipstr{}; 1432 std::array<char, NI_MAXHOST> ipstr{};
1332 1433
1333 if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, ipstr.data(), ipstr.size(), 1434 if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len,
1334 nullptr, 0, NI_NUMERICHOST)) { 1435 ipstr.data(), ipstr.size(), nullptr, 0, NI_NUMERICHOST)) {
1335 return ipstr.data(); 1436 return ipstr.data();
1336 } 1437 }
1337 } 1438 }
@@ -1380,6 +1481,7 @@ inline const char *status_message(int status) {
1380 case 303: return "See Other"; 1481 case 303: return "See Other";
1381 case 304: return "Not Modified"; 1482 case 304: return "Not Modified";
1382 case 400: return "Bad Request"; 1483 case 400: return "Bad Request";
1484 case 401: return "Unauthorized";
1383 case 403: return "Forbidden"; 1485 case 403: return "Forbidden";
1384 case 404: return "Not Found"; 1486 case 404: return "Not Found";
1385 case 413: return "Payload Too Large"; 1487 case 413: return "Payload Too Large";
@@ -1420,7 +1522,7 @@ inline bool compress(std::string &content) {
1420 std::array<char, 16384> buff{}; 1522 std::array<char, 16384> buff{};
1421 do { 1523 do {
1422 strm.avail_out = buff.size(); 1524 strm.avail_out = buff.size();
1423 strm.next_out = reinterpret_cast<Bytef*>(buff.data()); 1525 strm.next_out = reinterpret_cast<Bytef *>(buff.data());
1424 ret = deflate(&strm, Z_FINISH); 1526 ret = deflate(&strm, Z_FINISH);
1425 assert(ret != Z_STREAM_ERROR); 1527 assert(ret != Z_STREAM_ERROR);
1426 compressed.append(buff.data(), buff.size() - strm.avail_out); 1528 compressed.append(buff.data(), buff.size() - strm.avail_out);
@@ -1462,7 +1564,7 @@ public:
1462 std::array<char, 16384> buff{}; 1564 std::array<char, 16384> buff{};
1463 do { 1565 do {
1464 strm.avail_out = buff.size(); 1566 strm.avail_out = buff.size();
1465 strm.next_out = reinterpret_cast<Bytef*>(buff.data()); 1567 strm.next_out = reinterpret_cast<Bytef *>(buff.data());
1466 1568
1467 ret = inflate(&strm, Z_NO_FLUSH); 1569 ret = inflate(&strm, Z_NO_FLUSH);
1468 assert(ret != Z_STREAM_ERROR); 1570 assert(ret != Z_STREAM_ERROR);
@@ -1472,7 +1574,9 @@ public:
1472 case Z_MEM_ERROR: inflateEnd(&strm); return false; 1574 case Z_MEM_ERROR: inflateEnd(&strm); return false;
1473 } 1575 }
1474 1576
1475 if (!callback(buff.data(), buff.size() - strm.avail_out)) { return false; } 1577 if (!callback(buff.data(), buff.size() - strm.avail_out)) {
1578 return false;
1579 }
1476 } while (strm.avail_out == 0); 1580 } while (strm.avail_out == 0);
1477 1581
1478 return ret == Z_OK || ret == Z_STREAM_END; 1582 return ret == Z_OK || ret == Z_STREAM_END;
@@ -1506,18 +1610,35 @@ inline uint64_t get_header_value_uint64(const Headers &headers, const char *key,
1506} 1610}
1507 1611
1508inline bool read_headers(Stream &strm, Headers &headers) { 1612inline bool read_headers(Stream &strm, Headers &headers) {
1509 static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)");
1510
1511 const auto bufsiz = 2048; 1613 const auto bufsiz = 2048;
1512 char buf[bufsiz]; 1614 char buf[bufsiz];
1513
1514 stream_line_reader line_reader(strm, buf, bufsiz); 1615 stream_line_reader line_reader(strm, buf, bufsiz);
1515 1616
1516 for (;;) { 1617 for (;;) {
1517 if (!line_reader.getline()) { return false; } 1618 if (!line_reader.getline()) { return false; }
1518 if (!strcmp(line_reader.ptr(), "\r\n")) { break; } 1619
1620 // Check if the line ends with CRLF.
1621 if (line_reader.end_with_crlf()) {
1622 // Blank line indicates end of headers.
1623 if (line_reader.size() == 2) { break; }
1624 } else {
1625 continue; // Skip invalid line.
1626 }
1627
1628 // Skip trailing spaces and tabs.
1629 auto end = line_reader.ptr() + line_reader.size() - 2;
1630 while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) {
1631 end--;
1632 }
1633
1634 // Horizontal tab and ' ' are considered whitespace and are ignored when on
1635 // the left or right side of the header value:
1636 // - https://stackoverflow.com/questions/50179659/
1637 // - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
1638 static const std::regex re(R"((.+?):[\t ]*(.+))");
1639
1519 std::cmatch m; 1640 std::cmatch m;
1520 if (std::regex_match(line_reader.ptr(), m, re)) { 1641 if (std::regex_match(line_reader.ptr(), end, m, re)) {
1521 auto key = std::string(m[1]); 1642 auto key = std::string(m[1]);
1522 auto val = std::string(m[2]); 1643 auto val = std::string(m[2]);
1523 headers.emplace(key, val); 1644 headers.emplace(key, val);
@@ -1844,113 +1965,206 @@ inline bool parse_multipart_boundary(const std::string &content_type,
1844 return true; 1965 return true;
1845} 1966}
1846 1967
1847inline bool parse_multipart_formdata(const std::string &boundary, 1968inline bool parse_range_header(const std::string &s, Ranges &ranges) {
1848 const std::string &body, 1969 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
1849 MultipartFiles &files) { 1970 std::smatch m;
1850 static std::string dash = "--"; 1971 if (std::regex_match(s, m, re_first_range)) {
1851 static std::string crlf = "\r\n"; 1972 auto pos = m.position(1);
1852 1973 auto len = m.length(1);
1853 static std::regex re_content_type("Content-Type: (.*?)$", 1974 bool all_valid_ranges = true;
1854 std::regex_constants::icase); 1975 detail::split(
1855 1976 &s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
1856 static std::regex re_content_disposition( 1977 if (!all_valid_ranges) return;
1857 "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", 1978 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
1858 std::regex_constants::icase); 1979 std::cmatch m;
1859 1980 if (std::regex_match(b, e, m, re_another_range)) {
1860 auto dash_boundary = dash + boundary; 1981 ssize_t first = -1;
1861 1982 if (!m.str(1).empty()) {
1862 auto pos = body.find(dash_boundary); 1983 first = static_cast<ssize_t>(std::stoll(m.str(1)));
1863 if (pos != 0) { return false; } 1984 }
1864
1865 pos += dash_boundary.size();
1866 1985
1867 auto next_pos = body.find(crlf, pos); 1986 ssize_t last = -1;
1868 if (next_pos == std::string::npos) { return false; } 1987 if (!m.str(2).empty()) {
1988 last = static_cast<ssize_t>(std::stoll(m.str(2)));
1989 }
1869 1990
1870 pos = next_pos + crlf.size(); 1991 if (first != -1 && last != -1 && first > last) {
1992 all_valid_ranges = false;
1993 return;
1994 }
1995 ranges.emplace_back(std::make_pair(first, last));
1996 }
1997 });
1998 return all_valid_ranges;
1999 }
2000 return false;
2001}
1871 2002
1872 while (pos < body.size()) { 2003class MultipartFormDataParser {
1873 next_pos = body.find(crlf, pos); 2004public:
1874 if (next_pos == std::string::npos) { return false; } 2005 MultipartFormDataParser() {}
1875 2006
1876 std::string name; 2007 void set_boundary(const std::string &boundary) { boundary_ = boundary; }
1877 MultipartFile file;
1878 2008
1879 auto header = body.substr(pos, (next_pos - pos)); 2009 bool is_valid() const { return is_valid_; }
1880 2010
1881 while (pos != next_pos) { 2011 template <typename T, typename U>
1882 std::smatch m; 2012 bool parse(const char *buf, size_t n, T content_callback, U header_callback) {
1883 if (std::regex_match(header, m, re_content_type)) { 2013 static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)",
1884 file.content_type = m[1]; 2014 std::regex_constants::icase);
1885 } else if (std::regex_match(header, m, re_content_disposition)) { 2015
1886 name = m[1]; 2016 static const std::regex re_content_disposition(
1887 file.filename = m[2]; 2017 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
2018 "\"(.*?)\")?\\s*$",
2019 std::regex_constants::icase);
2020
2021 buf_.append(buf, n); // TODO: performance improvement
2022
2023 while (!buf_.empty()) {
2024 switch (state_) {
2025 case 0: { // Initial boundary
2026 auto pattern = dash_ + boundary_ + crlf_;
2027 if (pattern.size() > buf_.size()) { return true; }
2028 auto pos = buf_.find(pattern);
2029 if (pos != 0) {
2030 is_done_ = true;
2031 return false;
2032 }
2033 buf_.erase(0, pattern.size());
2034 off_ += pattern.size();
2035 state_ = 1;
2036 break;
1888 } 2037 }
2038 case 1: { // New entry
2039 clear_file_info();
2040 state_ = 2;
2041 break;
2042 }
2043 case 2: { // Headers
2044 auto pos = buf_.find(crlf_);
2045 while (pos != std::string::npos) {
2046 // Empty line
2047 if (pos == 0) {
2048 if (!header_callback(file_)) {
2049 is_valid_ = false;
2050 is_done_ = false;
2051 return false;
2052 }
2053 buf_.erase(0, crlf_.size());
2054 off_ += crlf_.size();
2055 state_ = 3;
2056 break;
2057 }
1889 2058
1890 pos = next_pos + crlf.size(); 2059 auto header = buf_.substr(0, pos);
1891 2060 {
1892 next_pos = body.find(crlf, pos); 2061 std::smatch m;
1893 if (next_pos == std::string::npos) { return false; } 2062 if (std::regex_match(header, m, re_content_type)) {
1894 2063 file_.content_type = m[1];
1895 header = body.substr(pos, (next_pos - pos)); 2064 } else if (std::regex_match(header, m, re_content_disposition)) {
1896 } 2065 file_.name = m[1];
1897 2066 file_.filename = m[2];
1898 pos = next_pos + crlf.size(); 2067 }
1899 2068 }
1900 next_pos = body.find(crlf + dash_boundary, pos);
1901 2069
1902 if (next_pos == std::string::npos) { return false; } 2070 buf_.erase(0, pos + crlf_.size());
2071 off_ += pos + crlf_.size();
2072 pos = buf_.find(crlf_);
2073 }
2074 break;
2075 }
2076 case 3: { // Body
2077 {
2078 auto pattern = crlf_ + dash_;
2079 auto pos = buf_.find(pattern);
2080 if (pos == std::string::npos) { pos = buf_.size(); }
2081 if (!content_callback(buf_.data(), pos)) {
2082 is_valid_ = false;
2083 is_done_ = false;
2084 return false;
2085 }
1903 2086
1904 file.offset = pos; 2087 off_ += pos;
1905 file.length = next_pos - pos; 2088 buf_.erase(0, pos);
2089 }
1906 2090
1907 pos = next_pos + crlf.size() + dash_boundary.size(); 2091 {
2092 auto pattern = crlf_ + dash_ + boundary_;
2093 if (pattern.size() > buf_.size()) { return true; }
2094
2095 auto pos = buf_.find(pattern);
2096 if (pos != std::string::npos) {
2097 if (!content_callback(buf_.data(), pos)) {
2098 is_valid_ = false;
2099 is_done_ = false;
2100 return false;
2101 }
1908 2102
1909 next_pos = body.find(crlf, pos); 2103 off_ += pos + pattern.size();
1910 if (next_pos == std::string::npos) { return false; } 2104 buf_.erase(0, pos + pattern.size());
2105 state_ = 4;
2106 } else {
2107 if (!content_callback(buf_.data(), pattern.size())) {
2108 is_valid_ = false;
2109 is_done_ = false;
2110 return false;
2111 }
1911 2112
1912 files.emplace(name, file); 2113 off_ += pattern.size();
2114 buf_.erase(0, pattern.size());
2115 }
2116 }
2117 break;
2118 }
2119 case 4: { // Boundary
2120 if (crlf_.size() > buf_.size()) { return true; }
2121 if (buf_.find(crlf_) == 0) {
2122 buf_.erase(0, crlf_.size());
2123 off_ += crlf_.size();
2124 state_ = 1;
2125 } else {
2126 auto pattern = dash_ + crlf_;
2127 if (pattern.size() > buf_.size()) { return true; }
2128 if (buf_.find(pattern) == 0) {
2129 buf_.erase(0, pattern.size());
2130 off_ += pattern.size();
2131 is_valid_ = true;
2132 state_ = 5;
2133 } else {
2134 is_done_ = true;
2135 return true;
2136 }
2137 }
2138 break;
2139 }
2140 case 5: { // Done
2141 is_valid_ = false;
2142 return false;
2143 }
2144 }
2145 }
1913 2146
1914 pos = next_pos + crlf.size(); 2147 return true;
1915 } 2148 }
1916 2149
1917 return true; 2150private:
1918} 2151 void clear_file_info() {
1919 2152 file_.name.clear();
1920inline bool parse_range_header(const std::string &s, Ranges &ranges) { 2153 file_.filename.clear();
1921 try { 2154 file_.content_type.clear();
1922 static auto re_first_range = 2155 }
1923 std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
1924 std::smatch m;
1925 if (std::regex_match(s, m, re_first_range)) {
1926 auto pos = m.position(1);
1927 auto len = m.length(1);
1928 detail::split(
1929 &s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
1930 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
1931 std::cmatch m;
1932 if (std::regex_match(b, e, m, re_another_range)) {
1933 ssize_t first = -1;
1934 if (!m.str(1).empty()) {
1935 first = static_cast<ssize_t>(std::stoll(m.str(1)));
1936 }
1937 2156
1938 ssize_t last = -1; 2157 const std::string dash_ = "--";
1939 if (!m.str(2).empty()) { 2158 const std::string crlf_ = "\r\n";
1940 last = static_cast<ssize_t>(std::stoll(m.str(2))); 2159 std::string boundary_;
1941 }
1942 2160
1943 if (first != -1 && last != -1 && first > last) { 2161 std::string buf_;
1944 throw std::runtime_error("invalid range error"); 2162 size_t state_ = 0;
1945 } 2163 size_t is_valid_ = false;
1946 ranges.emplace_back(std::make_pair(first, last)); 2164 size_t is_done_ = false;
1947 } 2165 size_t off_ = 0;
1948 }); 2166 MultipartFormData file_;
1949 return true; 2167};
1950 }
1951 return false;
1952 } catch (...) { return false; }
1953}
1954 2168
1955inline std::string to_lower(const char *beg, const char *end) { 2169inline std::string to_lower(const char *beg, const char *end) {
1956 std::string out; 2170 std::string out;
@@ -2102,6 +2316,52 @@ get_range_offset_and_length(const Request &req, const Response &res,
2102 return std::make_pair(r.first, r.second - r.first + 1); 2316 return std::make_pair(r.first, r.second - r.first + 1);
2103} 2317}
2104 2318
2319inline bool expect_content(const Request &req) {
2320 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
2321 req.method == "PRI") {
2322 return true;
2323 }
2324 // TODO: check if Content-Length is set
2325 return false;
2326}
2327
2328#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2329template <typename CTX, typename Init, typename Update, typename Final>
2330inline std::string message_digest(const std::string &s, Init init,
2331 Update update, Final final,
2332 size_t digest_length) {
2333 using namespace std;
2334
2335 std::vector<unsigned char> md(digest_length, 0);
2336 CTX ctx;
2337 init(&ctx);
2338 update(&ctx, s.data(), s.size());
2339 final(md.data(), &ctx);
2340
2341 stringstream ss;
2342 for (auto c : md) {
2343 ss << setfill('0') << setw(2) << hex << (unsigned int)c;
2344 }
2345 return ss.str();
2346}
2347
2348inline std::string MD5(const std::string &s) {
2349 using namespace detail;
2350 return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
2351 MD5_DIGEST_LENGTH);
2352}
2353
2354inline std::string SHA_256(const std::string &s) {
2355 return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final,
2356 SHA256_DIGEST_LENGTH);
2357}
2358
2359inline std::string SHA_512(const std::string &s) {
2360 return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final,
2361 SHA512_DIGEST_LENGTH);
2362}
2363#endif
2364
2105#ifdef _WIN32 2365#ifdef _WIN32
2106class WSInit { 2366class WSInit {
2107public: 2367public:
@@ -2139,6 +2399,96 @@ make_basic_authentication_header(const std::string &username,
2139 return std::make_pair("Authorization", field); 2399 return std::make_pair("Authorization", field);
2140} 2400}
2141 2401
2402#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2403inline std::pair<std::string, std::string> make_digest_authentication_header(
2404 const Request &req, const std::map<std::string, std::string> &auth,
2405 size_t cnonce_count, const std::string &cnonce, const std::string &username,
2406 const std::string &password) {
2407 using namespace std;
2408
2409 string nc;
2410 {
2411 stringstream ss;
2412 ss << setfill('0') << setw(8) << hex << cnonce_count;
2413 nc = ss.str();
2414 }
2415
2416 auto qop = auth.at("qop");
2417 if (qop.find("auth-int") != std::string::npos) {
2418 qop = "auth-int";
2419 } else {
2420 qop = "auth";
2421 }
2422
2423 string response;
2424 {
2425 auto algo = auth.at("algorithm");
2426
2427 auto H = algo == "SHA-256"
2428 ? detail::SHA_256
2429 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
2430
2431 auto A1 = username + ":" + auth.at("realm") + ":" + password;
2432
2433 auto A2 = req.method + ":" + req.path;
2434 if (qop == "auth-int") { A2 += ":" + H(req.body); }
2435
2436 response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
2437 ":" + qop + ":" + H(A2));
2438 }
2439
2440 auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") +
2441 "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
2442 "\", algorithm=" + auth.at("algorithm") + ", qop=" + qop +
2443 ", nc=\"" + nc + "\", cnonce=\"" + cnonce + "\", response=\"" +
2444 response + "\"";
2445
2446 return make_pair("Authorization", field);
2447}
2448#endif
2449
2450inline int
2451parse_www_authenticate(const httplib::Response &res,
2452 std::map<std::string, std::string> &digest_auth) {
2453 if (res.has_header("WWW-Authenticate")) {
2454 static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
2455 auto s = res.get_header_value("WWW-Authenticate");
2456 auto pos = s.find(' ');
2457 if (pos != std::string::npos) {
2458 auto type = s.substr(0, pos);
2459 if (type == "Basic") {
2460 return 1;
2461 } else if (type == "Digest") {
2462 s = s.substr(pos + 1);
2463 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
2464 for (auto i = beg; i != std::sregex_iterator(); ++i) {
2465 auto m = *i;
2466 auto key = s.substr(m.position(1), m.length(1));
2467 auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2))
2468 : s.substr(m.position(3), m.length(3));
2469 digest_auth[key] = val;
2470 }
2471 return 2;
2472 }
2473 }
2474 }
2475 return 0;
2476}
2477
2478// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
2479inline std::string random_string(size_t length) {
2480 auto randchar = []() -> char {
2481 const char charset[] = "0123456789"
2482 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2483 "abcdefghijklmnopqrstuvwxyz";
2484 const size_t max_index = (sizeof(charset) - 1);
2485 return charset[rand() % max_index];
2486 };
2487 std::string str(length, 0);
2488 std::generate_n(str.begin(), length, randchar);
2489 return str;
2490}
2491
2142// Request implementation 2492// Request implementation
2143inline bool Request::has_header(const char *key) const { 2493inline bool Request::has_header(const char *key) const {
2144 return detail::has_header(headers, key); 2494 return detail::has_header(headers, key);
@@ -2177,14 +2527,19 @@ inline size_t Request::get_param_value_count(const char *key) const {
2177 return std::distance(r.first, r.second); 2527 return std::distance(r.first, r.second);
2178} 2528}
2179 2529
2530inline bool Request::is_multipart_form_data() const {
2531 const auto &content_type = get_header_value("Content-Type");
2532 return !content_type.find("multipart/form-data");
2533}
2534
2180inline bool Request::has_file(const char *key) const { 2535inline bool Request::has_file(const char *key) const {
2181 return files.find(key) != files.end(); 2536 return files.find(key) != files.end();
2182} 2537}
2183 2538
2184inline MultipartFile Request::get_file_value(const char *key) const { 2539inline MultipartFormData Request::get_file_value(const char *key) const {
2185 auto it = files.find(key); 2540 auto it = files.find(key);
2186 if (it != files.end()) { return it->second; } 2541 if (it != files.end()) { return it->second; }
2187 return MultipartFile(); 2542 return MultipartFormData();
2188} 2543}
2189 2544
2190// Response implementation 2545// Response implementation
@@ -2369,7 +2724,7 @@ inline Server &Server::Post(const char *pattern, Handler handler) {
2369 2724
2370inline Server &Server::Post(const char *pattern, 2725inline Server &Server::Post(const char *pattern,
2371 HandlerWithContentReader handler) { 2726 HandlerWithContentReader handler) {
2372 post_handlers_for_content_reader.push_back( 2727 post_handlers_for_content_reader_.push_back(
2373 std::make_pair(std::regex(pattern), handler)); 2728 std::make_pair(std::regex(pattern), handler));
2374 return *this; 2729 return *this;
2375} 2730}
@@ -2381,7 +2736,7 @@ inline Server &Server::Put(const char *pattern, Handler handler) {
2381 2736
2382inline Server &Server::Put(const char *pattern, 2737inline Server &Server::Put(const char *pattern,
2383 HandlerWithContentReader handler) { 2738 HandlerWithContentReader handler) {
2384 put_handlers_for_content_reader.push_back( 2739 put_handlers_for_content_reader_.push_back(
2385 std::make_pair(std::regex(pattern), handler)); 2740 std::make_pair(std::regex(pattern), handler));
2386 return *this; 2741 return *this;
2387} 2742}
@@ -2393,7 +2748,7 @@ inline Server &Server::Patch(const char *pattern, Handler handler) {
2393 2748
2394inline Server &Server::Patch(const char *pattern, 2749inline Server &Server::Patch(const char *pattern,
2395 HandlerWithContentReader handler) { 2750 HandlerWithContentReader handler) {
2396 patch_handlers_for_content_reader.push_back( 2751 patch_handlers_for_content_reader_.push_back(
2397 std::make_pair(std::regex(pattern), handler)); 2752 std::make_pair(std::regex(pattern), handler));
2398 return *this; 2753 return *this;
2399} 2754}
@@ -2408,10 +2763,13 @@ inline Server &Server::Options(const char *pattern, Handler handler) {
2408 return *this; 2763 return *this;
2409} 2764}
2410 2765
2411inline bool Server::set_base_dir(const char *path) { 2766inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
2412 if (detail::is_dir(path)) { 2767 if (detail::is_dir(dir)) {
2413 base_dir_ = path; 2768 std::string mnt = mount_point ? mount_point : "/";
2414 return true; 2769 if (!mnt.empty() && mnt[0] == '/') {
2770 base_dirs_.emplace_back(mnt, dir);
2771 return true;
2772 }
2415 } 2773 }
2416 return false; 2774 return false;
2417} 2775}
@@ -2465,7 +2823,7 @@ inline void Server::stop() {
2465} 2823}
2466 2824
2467inline bool Server::parse_request_line(const char *s, Request &req) { 2825inline bool Server::parse_request_line(const char *s, Request &req) {
2468 static std::regex re( 2826 const static std::regex re(
2469 "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " 2827 "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
2470 "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); 2828 "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n");
2471 2829
@@ -2643,62 +3001,106 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
2643 3001
2644inline bool Server::read_content(Stream &strm, bool last_connection, 3002inline bool Server::read_content(Stream &strm, bool last_connection,
2645 Request &req, Response &res) { 3003 Request &req, Response &res) {
2646 if (!detail::read_content(strm, req, payload_max_length_, res.status, 3004 MultipartFormDataMap::iterator cur;
2647 Progress(), [&](const char *buf, size_t n) { 3005 auto ret = read_content_core(
2648 if (req.body.size() + n > req.body.max_size()) { 3006 strm, last_connection, req, res,
2649 return false; 3007 // Regular
2650 } 3008 [&](const char *buf, size_t n) {
2651 req.body.append(buf, n); 3009 if (req.body.size() + n > req.body.max_size()) { return false; }
2652 return true; 3010 req.body.append(buf, n);
2653 })) { 3011 return true;
2654 return write_response(strm, last_connection, req, res); 3012 },
2655 } 3013 // Multipart
3014 [&](const MultipartFormData &file) {
3015 cur = req.files.emplace(file.name, file);
3016 return true;
3017 },
3018 [&](const char *buf, size_t n) {
3019 auto &content = cur->second.content;
3020 if (content.size() + n > content.max_size()) { return false; }
3021 content.append(buf, n);
3022 return true;
3023 });
2656 3024
2657 const auto &content_type = req.get_header_value("Content-Type"); 3025 const auto &content_type = req.get_header_value("Content-Type");
2658
2659 if (!content_type.find("application/x-www-form-urlencoded")) { 3026 if (!content_type.find("application/x-www-form-urlencoded")) {
2660 detail::parse_query_text(req.body, req.params); 3027 detail::parse_query_text(req.body, req.params);
2661 } else if (!content_type.find("multipart/form-data")) { 3028 }
3029
3030 return ret;
3031}
3032
3033inline bool Server::read_content_with_content_receiver(
3034 Stream &strm, bool last_connection, Request &req, Response &res,
3035 ContentReceiver receiver, MultipartContentHeader multipart_header,
3036 ContentReceiver multipart_receiver) {
3037 return read_content_core(strm, last_connection, req, res, receiver,
3038 multipart_header, multipart_receiver);
3039}
3040
3041inline bool Server::read_content_core(Stream &strm, bool last_connection,
3042 Request &req, Response &res,
3043 ContentReceiver receiver,
3044 MultipartContentHeader mulitpart_header,
3045 ContentReceiver multipart_receiver) {
3046 detail::MultipartFormDataParser multipart_form_data_parser;
3047 ContentReceiver out;
3048
3049 if (req.is_multipart_form_data()) {
3050 const auto &content_type = req.get_header_value("Content-Type");
2662 std::string boundary; 3051 std::string boundary;
2663 if (!detail::parse_multipart_boundary(content_type, boundary) || 3052 if (!detail::parse_multipart_boundary(content_type, boundary)) {
2664 !detail::parse_multipart_formdata(boundary, req.body, req.files)) {
2665 res.status = 400; 3053 res.status = 400;
2666 return write_response(strm, last_connection, req, res); 3054 return write_response(strm, last_connection, req, res);
2667 } 3055 }
2668 }
2669 3056
2670 return true; 3057 multipart_form_data_parser.set_boundary(boundary);
2671} 3058 out = [&](const char *buf, size_t n) {
3059 return multipart_form_data_parser.parse(buf, n, multipart_receiver,
3060 mulitpart_header);
3061 };
3062 } else {
3063 out = receiver;
3064 }
2672 3065
2673inline bool 3066 if (!detail::read_content(strm, req, payload_max_length_, res.status,
2674Server::read_content_with_content_receiver(Stream &strm, bool last_connection, 3067 Progress(), out)) {
2675 Request &req, Response &res,
2676 ContentReceiver receiver) {
2677 if (!detail::read_content(
2678 strm, req, payload_max_length_, res.status, Progress(),
2679 [&](const char *buf, size_t n) { return receiver(buf, n); })) {
2680 return write_response(strm, last_connection, req, res); 3068 return write_response(strm, last_connection, req, res);
2681 } 3069 }
2682 3070
3071 if (req.is_multipart_form_data()) {
3072 if (!multipart_form_data_parser.is_valid()) {
3073 res.status = 400;
3074 return write_response(strm, last_connection, req, res);
3075 }
3076 }
3077
2683 return true; 3078 return true;
2684} 3079}
2685 3080
2686inline bool Server::handle_file_request(Request &req, Response &res) { 3081inline bool Server::handle_file_request(Request &req, Response &res) {
2687 if (!base_dir_.empty() && detail::is_valid_path(req.path)) { 3082 for (const auto &kv : base_dirs_) {
2688 std::string path = base_dir_ + req.path; 3083 const auto &mount_point = kv.first;
2689 3084 const auto &base_dir = kv.second;
2690 if (!path.empty() && path.back() == '/') { path += "index.html"; } 3085
2691 3086 // Prefix match
2692 if (detail::is_file(path)) { 3087 if (!req.path.find(mount_point)) {
2693 detail::read_file(path, res.body); 3088 std::string sub_path = "/" + req.path.substr(mount_point.size());
2694 auto type = detail::find_content_type(path); 3089 if (detail::is_valid_path(sub_path)) {
2695 if (type) { res.set_header("Content-Type", type); } 3090 auto path = base_dir + sub_path;
2696 res.status = 200; 3091 if (path.back() == '/') { path += "index.html"; }
2697 if (file_request_handler_) { file_request_handler_(req, res); } 3092
2698 return true; 3093 if (detail::is_file(path)) {
3094 detail::read_file(path, res.body);
3095 auto type = detail::find_content_type(path);
3096 if (type) { res.set_header("Content-Type", type); }
3097 res.status = 200;
3098 if (file_request_handler_) { file_request_handler_(req, res); }
3099 return true;
3100 }
3101 }
2699 } 3102 }
2700 } 3103 }
2701
2702 return false; 3104 return false;
2703} 3105}
2704 3106
@@ -2734,7 +3136,8 @@ inline int Server::bind_internal(const char *host, int port, int socket_flags) {
2734 if (address.ss_family == AF_INET) { 3136 if (address.ss_family == AF_INET) {
2735 return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port); 3137 return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port);
2736 } else if (address.ss_family == AF_INET6) { 3138 } else if (address.ss_family == AF_INET6) {
2737 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port); 3139 return ntohs(
3140 reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
2738 } else { 3141 } else {
2739 return -1; 3142 return -1;
2740 } 3143 }
@@ -2790,39 +3193,44 @@ inline bool Server::listen_internal() {
2790 return ret; 3193 return ret;
2791} 3194}
2792 3195
2793inline bool Server::routing(Request &req, Response &res, Stream &strm, bool last_connection) { 3196inline bool Server::routing(Request &req, Response &res, Stream &strm,
3197 bool last_connection) {
2794 // File handler 3198 // File handler
2795 if (req.method == "GET" && handle_file_request(req, res)) { return true; } 3199 if (req.method == "GET" && handle_file_request(req, res)) { return true; }
2796 3200
2797 // Content reader handler 3201 if (detail::expect_content(req)) {
2798 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { 3202 // Content reader handler
2799 ContentReader content_reader = [&](ContentReceiver receiver) { 3203 {
2800 return read_content_with_content_receiver(strm, last_connection, req, res, receiver); 3204 ContentReader reader(
2801 }; 3205 [&](ContentReceiver receiver) {
3206 return read_content_with_content_receiver(
3207 strm, last_connection, req, res, receiver, nullptr, nullptr);
3208 },
3209 [&](MultipartContentHeader header, ContentReceiver receiver) {
3210 return read_content_with_content_receiver(
3211 strm, last_connection, req, res, nullptr, header, receiver);
3212 });
2802 3213
2803 if (req.method == "POST") { 3214 if (req.method == "POST") {
2804 if (dispatch_request_for_content_reader(req, res, content_reader, 3215 if (dispatch_request_for_content_reader(
2805 post_handlers_for_content_reader)) { 3216 req, res, reader, post_handlers_for_content_reader_)) {
2806 return true; 3217 return true;
2807 } 3218 }
2808 } else if (req.method == "PUT") { 3219 } else if (req.method == "PUT") {
2809 if (dispatch_request_for_content_reader(req, res, content_reader, 3220 if (dispatch_request_for_content_reader(
2810 put_handlers_for_content_reader)) { 3221 req, res, reader, put_handlers_for_content_reader_)) {
2811 return true; 3222 return true;
2812 } 3223 }
2813 } else if (req.method == "PATCH") { 3224 } else if (req.method == "PATCH") {
2814 if (dispatch_request_for_content_reader( 3225 if (dispatch_request_for_content_reader(
2815 req, res, content_reader, patch_handlers_for_content_reader)) { 3226 req, res, reader, patch_handlers_for_content_reader_)) {
2816 return true; 3227 return true;
3228 }
2817 } 3229 }
2818 } 3230 }
2819 }
2820 3231
2821 // Read content into `req.body` 3232 // Read content into `req.body`
2822 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI") { 3233 if (!read_content(strm, last_connection, req, res)) { return false; }
2823 if (!read_content(strm, last_connection, req, res)) {
2824 return false;
2825 }
2826 } 3234 }
2827 3235
2828 // Regular handler 3236 // Regular handler
@@ -2877,7 +3285,7 @@ Server::dispatch_request_for_content_reader(Request &req, Response &res,
2877inline bool 3285inline bool
2878Server::process_request(Stream &strm, bool last_connection, 3286Server::process_request(Stream &strm, bool last_connection,
2879 bool &connection_close, 3287 bool &connection_close,
2880 const std::function<void(Request &)>& setup_request) { 3288 const std::function<void(Request &)> &setup_request) {
2881 std::array<char, 2048> buf{}; 3289 std::array<char, 2048> buf{};
2882 3290
2883 detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); 3291 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
@@ -2953,29 +3361,15 @@ inline Client::Client(const char *host, int port, time_t timeout_sec)
2953 keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), 3361 keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
2954 read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND), 3362 read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
2955 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND), 3363 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
2956 follow_location_(false) {} 3364 follow_location_(false), compress_(false) {}
2957 3365
2958inline Client::~Client() {} 3366inline Client::~Client() {}
2959 3367
2960inline bool Client::is_valid() const { return true; } 3368inline bool Client::is_valid() const { return true; }
2961 3369
2962inline socket_t Client::create_client_socket() const { 3370inline socket_t Client::create_client_socket() const {
2963 return detail::create_socket( 3371 return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
2964 host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool { 3372 interface_);
2965 detail::set_nonblocking(sock, true);
2966
2967 auto ret = connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
2968 if (ret < 0) {
2969 if (detail::is_connection_error() ||
2970 !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) {
2971 detail::close_socket(sock);
2972 return false;
2973 }
2974 }
2975
2976 detail::set_nonblocking(sock, false);
2977 return true;
2978 });
2979} 3373}
2980 3374
2981inline bool Client::read_response_line(Stream &strm, Response &res) { 3375inline bool Client::read_response_line(Stream &strm, Response &res) {
@@ -3012,6 +3406,43 @@ inline bool Client::send(const Request &req, Response &res) {
3012 ret = redirect(req, res); 3406 ret = redirect(req, res);
3013 } 3407 }
3014 3408
3409 if (ret && !username_.empty() && !password_.empty() && res.status == 401) {
3410 int type;
3411 std::map<std::string, std::string> digest_auth;
3412
3413 if ((type = parse_www_authenticate(res, digest_auth)) > 0) {
3414 std::pair<std::string, std::string> header;
3415
3416 if (type == 1) {
3417 header = make_basic_authentication_header(username_, password_);
3418 } else if (type == 2) {
3419#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3420 size_t cnonce_count = 1;
3421 auto cnonce = random_string(10);
3422
3423 header = make_digest_authentication_header(
3424 req, digest_auth, cnonce_count, cnonce, username_, password_);
3425#endif
3426 }
3427
3428 Request new_req;
3429 new_req.method = req.method;
3430 new_req.path = req.path;
3431 new_req.headers = req.headers;
3432 new_req.body = req.body;
3433 new_req.response_handler = req.response_handler;
3434 new_req.content_receiver = req.content_receiver;
3435 new_req.progress = req.progress;
3436
3437 new_req.headers.insert(header);
3438
3439 Response new_res;
3440 auto ret = send(new_req, new_res);
3441 if (ret) { res = new_res; }
3442 return ret;
3443 }
3444 }
3445
3015 return ret; 3446 return ret;
3016} 3447}
3017 3448
@@ -3056,46 +3487,51 @@ inline bool Client::redirect(const Request &req, Response &res) {
3056 auto location = res.get_header_value("location"); 3487 auto location = res.get_header_value("location");
3057 if (location.empty()) { return false; } 3488 if (location.empty()) { return false; }
3058 3489
3059 std::regex re( 3490 const static std::regex re(
3060 R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); 3491 R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
3061 3492
3493 std::smatch m;
3494 if (!regex_match(location, m, re)) { return false; }
3495
3496 auto next_scheme = m[1].str();
3497 auto next_host = m[2].str();
3498 auto next_path = m[3].str();
3499 if (next_host.empty()) { next_host = host_; }
3500 if (next_path.empty()) { next_path = "/"; }
3501
3062 auto scheme = is_ssl() ? "https" : "http"; 3502 auto scheme = is_ssl() ? "https" : "http";
3063 3503
3064 std::smatch m; 3504 if (next_scheme == scheme && next_host == host_) {
3065 if (regex_match(location, m, re)) { 3505 return detail::redirect(*this, req, res, next_path);
3066 auto next_scheme = m[1].str(); 3506 } else {
3067 auto next_host = m[2].str(); 3507 if (next_scheme == "https") {
3068 auto next_path = m[3].str();
3069 if (next_host.empty()) { next_host = host_; }
3070 if (next_path.empty()) { next_path = "/"; }
3071
3072 if (next_scheme == scheme && next_host == host_) {
3073 return detail::redirect(*this, req, res, next_path);
3074 } else {
3075 if (next_scheme == "https") {
3076#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 3508#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3077 SSLClient cli(next_host.c_str()); 3509 SSLClient cli(next_host.c_str());
3078 cli.follow_location(true); 3510 cli.set_follow_location(true);
3079 return detail::redirect(cli, req, res, next_path); 3511 return detail::redirect(cli, req, res, next_path);
3080#else 3512#else
3081 return false; 3513 return false;
3082#endif 3514#endif
3083 } else { 3515 } else {
3084 Client cli(next_host.c_str()); 3516 Client cli(next_host.c_str());
3085 cli.follow_location(true); 3517 cli.set_follow_location(true);
3086 return detail::redirect(cli, req, res, next_path); 3518 return detail::redirect(cli, req, res, next_path);
3087 }
3088 } 3519 }
3089 } 3520 }
3090 return false;
3091} 3521}
3092 3522
3093inline void Client::write_request(Stream &strm, const Request &req, 3523inline bool Client::write_request(Stream &strm, const Request &req,
3094 bool last_connection) { 3524 bool last_connection) {
3095 BufferStream bstrm; 3525 BufferStream bstrm;
3096 3526
3097 // Request line 3527 // Request line
3098 auto path = detail::encode_url(req.path); 3528 const static std::regex re(
3529 R"(^([^:/?#]+://[^/?#]*)?([^?#]*(?:\?[^#]*)?(?:#.*)?))");
3530
3531 std::smatch m;
3532 if (!regex_match(req.path, m, re)) { return false; }
3533
3534 auto path = m[1].str() + detail::encode_url(m[2].str());
3099 3535
3100 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); 3536 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
3101 3537
@@ -3165,16 +3601,14 @@ inline void Client::write_request(Stream &strm, const Request &req,
3165 } else { 3601 } else {
3166 strm.write(req.body); 3602 strm.write(req.body);
3167 } 3603 }
3604
3605 return true;
3168} 3606}
3169 3607
3170inline std::shared_ptr<Response> Client::send_with_content_provider( 3608inline std::shared_ptr<Response> Client::send_with_content_provider(
3171 const char *method, const char *path, const Headers &headers, 3609 const char *method, const char *path, const Headers &headers,
3172 const std::string &body, size_t content_length, 3610 const std::string &body, size_t content_length,
3173 ContentProvider content_provider, const char *content_type, bool compress) { 3611 ContentProvider content_provider, const char *content_type) {
3174#ifndef CPPHTTPLIB_ZLIB_SUPPORT
3175 (void)compress;
3176#endif
3177
3178 Request req; 3612 Request req;
3179 req.method = method; 3613 req.method = method;
3180 req.headers = headers; 3614 req.headers = headers;
@@ -3183,7 +3617,7 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
3183 req.headers.emplace("Content-Type", content_type); 3617 req.headers.emplace("Content-Type", content_type);
3184 3618
3185#ifdef CPPHTTPLIB_ZLIB_SUPPORT 3619#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3186 if (compress) { 3620 if (compress_) {
3187 if (content_provider) { 3621 if (content_provider) {
3188 size_t offset = 0; 3622 size_t offset = 0;
3189 while (offset < content_length) { 3623 while (offset < content_length) {
@@ -3219,7 +3653,7 @@ inline bool Client::process_request(Stream &strm, const Request &req,
3219 Response &res, bool last_connection, 3653 Response &res, bool last_connection,
3220 bool &connection_close) { 3654 bool &connection_close) {
3221 // Send request 3655 // Send request
3222 write_request(strm, req, last_connection); 3656 if (!write_request(strm, req, last_connection)) { return false; }
3223 3657
3224 // Receive response and headers 3658 // Receive response and headers
3225 if (!read_response_line(strm, res) || 3659 if (!read_response_line(strm, res) ||
@@ -3332,7 +3766,8 @@ inline std::shared_ptr<Response> Client::Get(const char *path,
3332 ResponseHandler response_handler, 3766 ResponseHandler response_handler,
3333 ContentReceiver content_receiver) { 3767 ContentReceiver content_receiver) {
3334 Progress dummy; 3768 Progress dummy;
3335 return Get(path, headers, std::move(response_handler), content_receiver, dummy); 3769 return Get(path, headers, std::move(response_handler), content_receiver,
3770 dummy);
3336} 3771}
3337 3772
3338inline std::shared_ptr<Response> Client::Get(const char *path, 3773inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3370,45 +3805,40 @@ inline std::shared_ptr<Response> Client::Head(const char *path,
3370 3805
3371inline std::shared_ptr<Response> Client::Post(const char *path, 3806inline std::shared_ptr<Response> Client::Post(const char *path,
3372 const std::string &body, 3807 const std::string &body,
3373 const char *content_type, 3808 const char *content_type) {
3374 bool compress) { 3809 return Post(path, Headers(), body, content_type);
3375 return Post(path, Headers(), body, content_type, compress);
3376} 3810}
3377 3811
3378inline std::shared_ptr<Response> 3812inline std::shared_ptr<Response> Client::Post(const char *path,
3379Client::Post(const char *path, const Headers &headers, const std::string &body, 3813 const Headers &headers,
3380 const char *content_type, bool compress) { 3814 const std::string &body,
3815 const char *content_type) {
3381 return send_with_content_provider("POST", path, headers, body, 0, nullptr, 3816 return send_with_content_provider("POST", path, headers, body, 0, nullptr,
3382 content_type, compress); 3817 content_type);
3383} 3818}
3384 3819
3385inline std::shared_ptr<Response> 3820inline std::shared_ptr<Response> Client::Post(const char *path,
3386Client::Post(const char *path, const Params &params, bool compress) { 3821 const Params &params) {
3387 return Post(path, Headers(), params, compress); 3822 return Post(path, Headers(), params);
3388} 3823}
3389 3824
3390inline std::shared_ptr<Response> Client::Post(const char *path, 3825inline std::shared_ptr<Response> Client::Post(const char *path,
3391 size_t content_length, 3826 size_t content_length,
3392 ContentProvider content_provider, 3827 ContentProvider content_provider,
3393 const char *content_type, 3828 const char *content_type) {
3394 bool compress) { 3829 return Post(path, Headers(), content_length, content_provider, content_type);
3395 return Post(path, Headers(), content_length, content_provider, content_type,
3396 compress);
3397} 3830}
3398 3831
3399inline std::shared_ptr<Response> 3832inline std::shared_ptr<Response>
3400Client::Post(const char *path, const Headers &headers, size_t content_length, 3833Client::Post(const char *path, const Headers &headers, size_t content_length,
3401 ContentProvider content_provider, const char *content_type, 3834 ContentProvider content_provider, const char *content_type) {
3402 bool compress) {
3403 return send_with_content_provider("POST", path, headers, std::string(), 3835 return send_with_content_provider("POST", path, headers, std::string(),
3404 content_length, content_provider, 3836 content_length, content_provider,
3405 content_type, compress); 3837 content_type);
3406} 3838}
3407 3839
3408inline std::shared_ptr<Response> Client::Post(const char *path, 3840inline std::shared_ptr<Response>
3409 const Headers &headers, 3841Client::Post(const char *path, const Headers &headers, const Params &params) {
3410 const Params &params,
3411 bool compress) {
3412 std::string query; 3842 std::string query;
3413 for (auto it = params.begin(); it != params.end(); ++it) { 3843 for (auto it = params.begin(); it != params.end(); ++it) {
3414 if (it != params.begin()) { query += "&"; } 3844 if (it != params.begin()) { query += "&"; }
@@ -3417,19 +3847,17 @@ inline std::shared_ptr<Response> Client::Post(const char *path,
3417 query += detail::encode_url(it->second); 3847 query += detail::encode_url(it->second);
3418 } 3848 }
3419 3849
3420 return Post(path, headers, query, "application/x-www-form-urlencoded", 3850 return Post(path, headers, query, "application/x-www-form-urlencoded");
3421 compress);
3422} 3851}
3423 3852
3424inline std::shared_ptr<Response> 3853inline std::shared_ptr<Response>
3425Client::Post(const char *path, const MultipartFormDataItems &items, 3854Client::Post(const char *path, const MultipartFormDataItems &items) {
3426 bool compress) { 3855 return Post(path, Headers(), items);
3427 return Post(path, Headers(), items, compress);
3428} 3856}
3429 3857
3430inline std::shared_ptr<Response> 3858inline std::shared_ptr<Response>
3431Client::Post(const char *path, const Headers &headers, 3859Client::Post(const char *path, const Headers &headers,
3432 const MultipartFormDataItems &items, bool compress) { 3860 const MultipartFormDataItems &items) {
3433 auto boundary = detail::make_multipart_data_boundary(); 3861 auto boundary = detail::make_multipart_data_boundary();
3434 3862
3435 std::string body; 3863 std::string body;
@@ -3451,71 +3879,65 @@ Client::Post(const char *path, const Headers &headers,
3451 body += "--" + boundary + "--\r\n"; 3879 body += "--" + boundary + "--\r\n";
3452 3880
3453 std::string content_type = "multipart/form-data; boundary=" + boundary; 3881 std::string content_type = "multipart/form-data; boundary=" + boundary;
3454 return Post(path, headers, body, content_type.c_str(), compress); 3882 return Post(path, headers, body, content_type.c_str());
3455} 3883}
3456 3884
3457inline std::shared_ptr<Response> Client::Put(const char *path, 3885inline std::shared_ptr<Response> Client::Put(const char *path,
3458 const std::string &body, 3886 const std::string &body,
3459 const char *content_type, 3887 const char *content_type) {
3460 bool compress) { 3888 return Put(path, Headers(), body, content_type);
3461 return Put(path, Headers(), body, content_type, compress);
3462} 3889}
3463 3890
3464inline std::shared_ptr<Response> 3891inline std::shared_ptr<Response> Client::Put(const char *path,
3465Client::Put(const char *path, const Headers &headers, const std::string &body, 3892 const Headers &headers,
3466 const char *content_type, bool compress) { 3893 const std::string &body,
3894 const char *content_type) {
3467 return send_with_content_provider("PUT", path, headers, body, 0, nullptr, 3895 return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
3468 content_type, compress); 3896 content_type);
3469} 3897}
3470 3898
3471inline std::shared_ptr<Response> Client::Put(const char *path, 3899inline std::shared_ptr<Response> Client::Put(const char *path,
3472 size_t content_length, 3900 size_t content_length,
3473 ContentProvider content_provider, 3901 ContentProvider content_provider,
3474 const char *content_type, 3902 const char *content_type) {
3475 bool compress) { 3903 return Put(path, Headers(), content_length, content_provider, content_type);
3476 return Put(path, Headers(), content_length, content_provider, content_type,
3477 compress);
3478} 3904}
3479 3905
3480inline std::shared_ptr<Response> 3906inline std::shared_ptr<Response>
3481Client::Put(const char *path, const Headers &headers, size_t content_length, 3907Client::Put(const char *path, const Headers &headers, size_t content_length,
3482 ContentProvider content_provider, const char *content_type, 3908 ContentProvider content_provider, const char *content_type) {
3483 bool compress) {
3484 return send_with_content_provider("PUT", path, headers, std::string(), 3909 return send_with_content_provider("PUT", path, headers, std::string(),
3485 content_length, content_provider, 3910 content_length, content_provider,
3486 content_type, compress); 3911 content_type);
3487} 3912}
3488 3913
3489inline std::shared_ptr<Response> Client::Patch(const char *path, 3914inline std::shared_ptr<Response> Client::Patch(const char *path,
3490 const std::string &body, 3915 const std::string &body,
3491 const char *content_type, 3916 const char *content_type) {
3492 bool compress) { 3917 return Patch(path, Headers(), body, content_type);
3493 return Patch(path, Headers(), body, content_type, compress);
3494} 3918}
3495 3919
3496inline std::shared_ptr<Response> 3920inline std::shared_ptr<Response> Client::Patch(const char *path,
3497Client::Patch(const char *path, const Headers &headers, const std::string &body, 3921 const Headers &headers,
3498 const char *content_type, bool compress) { 3922 const std::string &body,
3923 const char *content_type) {
3499 return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, 3924 return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
3500 content_type, compress); 3925 content_type);
3501} 3926}
3502 3927
3503inline std::shared_ptr<Response> Client::Patch(const char *path, 3928inline std::shared_ptr<Response> Client::Patch(const char *path,
3504 size_t content_length, 3929 size_t content_length,
3505 ContentProvider content_provider, 3930 ContentProvider content_provider,
3506 const char *content_type, 3931 const char *content_type) {
3507 bool compress) { 3932 return Patch(path, Headers(), content_length, content_provider, content_type);
3508 return Patch(path, Headers(), content_length, content_provider, content_type,
3509 compress);
3510} 3933}
3511 3934
3512inline std::shared_ptr<Response> 3935inline std::shared_ptr<Response>
3513Client::Patch(const char *path, const Headers &headers, size_t content_length, 3936Client::Patch(const char *path, const Headers &headers, size_t content_length,
3514 ContentProvider content_provider, const char *content_type, 3937 ContentProvider content_provider, const char *content_type) {
3515 bool compress) {
3516 return send_with_content_provider("PATCH", path, headers, std::string(), 3938 return send_with_content_provider("PATCH", path, headers, std::string(),
3517 content_length, content_provider, 3939 content_length, content_provider,
3518 content_type, compress); 3940 content_type);
3519} 3941}
3520 3942
3521inline std::shared_ptr<Response> Client::Delete(const char *path) { 3943inline std::shared_ptr<Response> Client::Delete(const char *path) {
@@ -3575,7 +3997,16 @@ inline void Client::set_read_timeout(time_t sec, time_t usec) {
3575 read_timeout_usec_ = usec; 3997 read_timeout_usec_ = usec;
3576} 3998}
3577 3999
3578inline void Client::follow_location(bool on) { follow_location_ = on; } 4000inline void Client::set_auth(const char *username, const char *password) {
4001 username_ = username;
4002 password_ = password;
4003}
4004
4005inline void Client::set_follow_location(bool on) { follow_location_ = on; }
4006
4007inline void Client::set_compress(bool on) { compress_ = on; }
4008
4009inline void Client::set_interface(const char *intf) { interface_ = intf; }
3579 4010
3580/* 4011/*
3581 * SSL Implementation 4012 * SSL Implementation
@@ -4021,6 +4452,8 @@ inline bool SSLClient::check_host_name(const char *pattern,
4021} 4452}
4022#endif 4453#endif
4023 4454
4455// ----------------------------------------------------------------------------
4456
4024} // namespace httplib 4457} // namespace httplib
4025 4458
4026#endif // CPPHTTPLIB_HTTPLIB_H 4459#endif // CPPHTTPLIB_HTTPLIB_H