diff options
Diffstat (limited to 'externals/httplib/httplib.h')
| -rw-r--r-- | externals/httplib/httplib.h | 1153 |
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 | ||
| 197 | using Done = std::function<void()>; | 202 | using Done = std::function<void()>; |
| 198 | 203 | ||
| 199 | using ContentProvider = std::function<void(size_t offset, size_t length, DataSink sink)>; | 204 | using ContentProvider = |
| 200 | 205 | std::function<void(size_t offset, size_t length, DataSink sink)>; | |
| 201 | using ContentProviderWithCloser = std::function<void(size_t offset, size_t length, DataSink sink, Done done)>; | ||
| 202 | |||
| 203 | using ContentReceiver = std::function<bool(const char *data, size_t data_length)>; | ||
| 204 | 206 | ||
| 205 | using ContentReader = std::function<bool(ContentReceiver receiver)>; | 207 | using ContentProviderWithCloser = |
| 208 | std::function<void(size_t offset, size_t length, DataSink sink, Done done)>; | ||
| 206 | 209 | ||
| 207 | using Progress = std::function<bool(uint64_t current, uint64_t total)>; | 210 | using Progress = std::function<bool(uint64_t current, uint64_t total)>; |
| 208 | 211 | ||
| 209 | struct Response; | 212 | struct Response; |
| 210 | using ResponseHandler = std::function<bool(const Response &response)>; | 213 | using ResponseHandler = std::function<bool(const Response &response)>; |
| 211 | 214 | ||
| 212 | struct MultipartFile { | ||
| 213 | std::string filename; | ||
| 214 | std::string content_type; | ||
| 215 | size_t offset = 0; | ||
| 216 | size_t length = 0; | ||
| 217 | }; | ||
| 218 | using MultipartFiles = std::multimap<std::string, MultipartFile>; | ||
| 219 | |||
| 220 | struct MultipartFormData { | 215 | struct 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 | }; |
| 226 | using MultipartFormDataItems = std::vector<MultipartFormData>; | 221 | using MultipartFormDataItems = std::vector<MultipartFormData>; |
| 222 | using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>; | ||
| 223 | |||
| 224 | using ContentReceiver = | ||
| 225 | std::function<bool(const char *data, size_t data_length)>; | ||
| 226 | |||
| 227 | using MultipartContentHeader = | ||
| 228 | std::function<bool(const MultipartFormData &file)>; | ||
| 229 | |||
| 230 | class ContentReader { | ||
| 231 | public: | ||
| 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 | ||
| 228 | using Range = std::pair<ssize_t, ssize_t>; | 250 | using Range = std::pair<ssize_t, ssize_t>; |
| 229 | using Ranges = std::vector<Range>; | 251 | using 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 | ||
| 487 | class Server { | 508 | class Server { |
| 488 | public: | 509 | public: |
| 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: | |||
| 531 | protected: | 552 | protected: |
| 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 | ||
| 541 | private: | 562 | private: |
| 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 ¶ms, | 674 | std::shared_ptr<Response> Post(const char *path, const Params ¶ms); |
| 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 ¶ms, bool compress = false); | 677 | const Params ¶ms); |
| 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 | ||
| 730 | protected: | 749 | protected: |
| 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 | ||
| 743 | private: | 766 | private: |
| 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 | ||
| 1038 | inline std::string file_extension(const std::string &path) { | 1061 | inline 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 | ||
| 1355 | inline 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 | |||
| 1379 | inline 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 | |||
| 1400 | inline 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 | |||
| 1326 | inline std::string get_remote_addr(socket_t sock) { | 1427 | inline 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 | ||
| 1508 | inline bool read_headers(Stream &strm, Headers &headers) { | 1612 | inline 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 | ||
| 1847 | inline bool parse_multipart_formdata(const std::string &boundary, | 1968 | inline 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()) { | 2003 | class MultipartFormDataParser { |
| 1873 | next_pos = body.find(crlf, pos); | 2004 | public: |
| 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; | 2150 | private: |
| 1918 | } | 2151 | void clear_file_info() { |
| 1919 | 2152 | file_.name.clear(); | |
| 1920 | inline 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 | ||
| 1955 | inline std::string to_lower(const char *beg, const char *end) { | 2169 | inline 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 | ||
| 2319 | inline 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 | ||
| 2329 | template <typename CTX, typename Init, typename Update, typename Final> | ||
| 2330 | inline 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 | |||
| 2348 | inline 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 | |||
| 2354 | inline 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 | |||
| 2359 | inline 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 |
| 2106 | class WSInit { | 2366 | class WSInit { |
| 2107 | public: | 2367 | public: |
| @@ -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 | ||
| 2403 | inline 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 | |||
| 2450 | inline int | ||
| 2451 | parse_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 | ||
| 2479 | inline 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 |
| 2143 | inline bool Request::has_header(const char *key) const { | 2493 | inline 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 | ||
| 2530 | inline 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 | |||
| 2180 | inline bool Request::has_file(const char *key) const { | 2535 | inline 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 | ||
| 2184 | inline MultipartFile Request::get_file_value(const char *key) const { | 2539 | inline 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 | ||
| 2370 | inline Server &Server::Post(const char *pattern, | 2725 | inline 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 | ||
| 2382 | inline Server &Server::Put(const char *pattern, | 2737 | inline 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 | ||
| 2394 | inline Server &Server::Patch(const char *pattern, | 2749 | inline 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 | ||
| 2411 | inline bool Server::set_base_dir(const char *path) { | 2766 | inline 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 | ||
| 2467 | inline bool Server::parse_request_line(const char *s, Request &req) { | 2825 | inline 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 | ||
| 2644 | inline bool Server::read_content(Stream &strm, bool last_connection, | 3002 | inline 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 | |||
| 3033 | inline 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 | |||
| 3041 | inline 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 | ||
| 2673 | inline bool | 3066 | if (!detail::read_content(strm, req, payload_max_length_, res.status, |
| 2674 | Server::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 | ||
| 2686 | inline bool Server::handle_file_request(Request &req, Response &res) { | 3081 | inline 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 | ||
| 2793 | inline bool Server::routing(Request &req, Response &res, Stream &strm, bool last_connection) { | 3196 | inline 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, | |||
| 2877 | inline bool | 3285 | inline bool |
| 2878 | Server::process_request(Stream &strm, bool last_connection, | 3286 | Server::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 | ||
| 2958 | inline Client::~Client() {} | 3366 | inline Client::~Client() {} |
| 2959 | 3367 | ||
| 2960 | inline bool Client::is_valid() const { return true; } | 3368 | inline bool Client::is_valid() const { return true; } |
| 2961 | 3369 | ||
| 2962 | inline socket_t Client::create_client_socket() const { | 3370 | inline 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 | ||
| 2981 | inline bool Client::read_response_line(Stream &strm, Response &res) { | 3375 | inline 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 | ||
| 3093 | inline void Client::write_request(Stream &strm, const Request &req, | 3523 | inline 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 | ||
| 3170 | inline std::shared_ptr<Response> Client::send_with_content_provider( | 3608 | inline 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 | ||
| 3338 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3773 | inline 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 | ||
| 3371 | inline std::shared_ptr<Response> Client::Post(const char *path, | 3806 | inline 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 | ||
| 3378 | inline std::shared_ptr<Response> | 3812 | inline std::shared_ptr<Response> Client::Post(const char *path, |
| 3379 | Client::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 | ||
| 3385 | inline std::shared_ptr<Response> | 3820 | inline std::shared_ptr<Response> Client::Post(const char *path, |
| 3386 | Client::Post(const char *path, const Params ¶ms, bool compress) { | 3821 | const Params ¶ms) { |
| 3387 | return Post(path, Headers(), params, compress); | 3822 | return Post(path, Headers(), params); |
| 3388 | } | 3823 | } |
| 3389 | 3824 | ||
| 3390 | inline std::shared_ptr<Response> Client::Post(const char *path, | 3825 | inline 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 | ||
| 3399 | inline std::shared_ptr<Response> | 3832 | inline std::shared_ptr<Response> |
| 3400 | Client::Post(const char *path, const Headers &headers, size_t content_length, | 3833 | Client::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 | ||
| 3408 | inline std::shared_ptr<Response> Client::Post(const char *path, | 3840 | inline std::shared_ptr<Response> |
| 3409 | const Headers &headers, | 3841 | Client::Post(const char *path, const Headers &headers, const Params ¶ms) { |
| 3410 | const Params ¶ms, | ||
| 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 | ||
| 3424 | inline std::shared_ptr<Response> | 3853 | inline std::shared_ptr<Response> |
| 3425 | Client::Post(const char *path, const MultipartFormDataItems &items, | 3854 | Client::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 | ||
| 3430 | inline std::shared_ptr<Response> | 3858 | inline std::shared_ptr<Response> |
| 3431 | Client::Post(const char *path, const Headers &headers, | 3859 | Client::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 | ||
| 3457 | inline std::shared_ptr<Response> Client::Put(const char *path, | 3885 | inline 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 | ||
| 3464 | inline std::shared_ptr<Response> | 3891 | inline std::shared_ptr<Response> Client::Put(const char *path, |
| 3465 | Client::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 | ||
| 3471 | inline std::shared_ptr<Response> Client::Put(const char *path, | 3899 | inline 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 | ||
| 3480 | inline std::shared_ptr<Response> | 3906 | inline std::shared_ptr<Response> |
| 3481 | Client::Put(const char *path, const Headers &headers, size_t content_length, | 3907 | Client::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 | ||
| 3489 | inline std::shared_ptr<Response> Client::Patch(const char *path, | 3914 | inline 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 | ||
| 3496 | inline std::shared_ptr<Response> | 3920 | inline std::shared_ptr<Response> Client::Patch(const char *path, |
| 3497 | Client::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 | ||
| 3503 | inline std::shared_ptr<Response> Client::Patch(const char *path, | 3928 | inline 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 | ||
| 3512 | inline std::shared_ptr<Response> | 3935 | inline std::shared_ptr<Response> |
| 3513 | Client::Patch(const char *path, const Headers &headers, size_t content_length, | 3936 | Client::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 | ||
| 3521 | inline std::shared_ptr<Response> Client::Delete(const char *path) { | 3943 | inline 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 | ||
| 3578 | inline void Client::follow_location(bool on) { follow_location_ = on; } | 4000 | inline void Client::set_auth(const char *username, const char *password) { |
| 4001 | username_ = username; | ||
| 4002 | password_ = password; | ||
| 4003 | } | ||
| 4004 | |||
| 4005 | inline void Client::set_follow_location(bool on) { follow_location_ = on; } | ||
| 4006 | |||
| 4007 | inline void Client::set_compress(bool on) { compress_ = on; } | ||
| 4008 | |||
| 4009 | inline 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 |