1 #include "external-lib/include-httplib.h"
6 #pragma warning(push, 0)
11 * Implementation that will be part of the .cc file if split into .h + .cc.
16 bool is_hex(char c, int &v) {
17 if (0x20 <= c && isdigit(c)) {
20 } else if ('A' <= c && c <= 'F') {
23 } else if ('a' <= c && c <= 'f') {
30 bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
32 if (i >= s.size()) { return false; }
35 for (; cnt; i++, cnt--) {
36 if (!s[i]) { return false; }
38 if (is_hex(s[i], v)) {
47 std::string from_i_to_hex(size_t n) {
48 const char *charset = "0123456789abcdef";
51 ret = charset[n & 15] + ret;
57 size_t to_utf8(int code, char *buff) {
59 buff[0] = (code & 0x7F);
61 } else if (code < 0x0800) {
62 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
63 buff[1] = static_cast<char>(0x80 | (code & 0x3F));
65 } else if (code < 0xD800) {
66 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
67 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
68 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
70 } else if (code < 0xE000) { // D800 - DFFF is invalid...
72 } else if (code < 0x10000) {
73 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
74 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
75 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
77 } else if (code < 0x110000) {
78 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
79 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
80 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
81 buff[3] = static_cast<char>(0x80 | (code & 0x3F));
89 // NOTE: This code came up with the following stackoverflow post:
90 // https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
91 std::string base64_encode(const std::string &in) {
92 static const auto lookup =
93 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
96 out.reserve(in.size());
102 val = (val << 8) + static_cast<uint8_t>(c);
105 out.push_back(lookup[(val >> valb) & 0x3F]);
110 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
112 while (out.size() % 4) {
119 bool is_file(const std::string &path) {
121 return _access_s(path.c_str(), 0) == 0;
124 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
128 bool is_dir(const std::string &path) {
130 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
133 bool is_valid_path(const std::string &path) {
138 while (i < path.size() && path[i] == '/') {
142 while (i < path.size()) {
145 while (i < path.size() && path[i] != '/') {
152 if (!path.compare(beg, len, ".")) {
154 } else if (!path.compare(beg, len, "..")) {
155 if (level == 0) { return false; }
162 while (i < path.size() && path[i] == '/') {
170 std::string encode_query_param(const std::string &value) {
171 std::ostringstream escaped;
175 for (auto c : value) {
176 if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
177 c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
181 escaped << std::uppercase;
182 escaped << '%' << std::setw(2)
183 << static_cast<int>(static_cast<unsigned char>(c));
184 escaped << std::nouppercase;
188 return escaped.str();
191 std::string encode_url(const std::string &s) {
193 result.reserve(s.size());
195 for (size_t i = 0; s[i]; i++) {
197 case ' ': result += "%20"; break;
198 case '+': result += "%2B"; break;
199 case '\r': result += "%0D"; break;
200 case '\n': result += "%0A"; break;
201 case '\'': result += "%27"; break;
202 case ',': result += "%2C"; break;
203 // case ':': result += "%3A"; break; // ok? probably...
204 case ';': result += "%3B"; break;
206 auto c = static_cast<uint8_t>(s[i]);
210 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
212 result.append(hex, static_cast<size_t>(len));
223 std::string decode_url(const std::string &s,
224 bool convert_plus_to_space) {
227 for (size_t i = 0; i < s.size(); i++) {
228 if (s[i] == '%' && i + 1 < s.size()) {
229 if (s[i + 1] == 'u') {
231 if (from_hex_to_i(s, i + 2, 4, val)) {
232 // 4 digits Unicode codes
234 size_t len = to_utf8(val, buff);
235 if (len > 0) { result.append(buff, len); }
242 if (from_hex_to_i(s, i + 1, 2, val)) {
243 // 2 digits hex codes
244 result += static_cast<char>(val);
250 } else if (convert_plus_to_space && s[i] == '+') {
260 void read_file(const std::string &path, std::string &out) {
261 std::ifstream fs(path, std::ios_base::binary);
262 fs.seekg(0, std::ios_base::end);
263 auto size = fs.tellg();
265 out.resize(static_cast<size_t>(size));
266 fs.read(&out[0], static_cast<std::streamsize>(size));
269 std::string file_extension(const std::string &path) {
271 static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
272 if (std::regex_search(path, m, re)) { return m[1].str(); }
273 return std::string();
276 bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
278 std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
280 while (b + left < e && is_space_or_tab(b[left])) {
283 while (right > 0 && is_space_or_tab(b[right - 1])) {
286 return std::make_pair(left, right);
289 std::string trim_copy(const std::string &s) {
290 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
291 return s.substr(r.first, r.second - r.first);
294 void split(const char *b, const char *e, char d,
295 std::function<void(const char *, const char *)> fn) {
299 while (e ? (b + i < e) : (b[i] != '\0')) {
301 auto r = trim(b, e, beg, i);
302 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
309 auto r = trim(b, e, beg, i);
310 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
314 stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
315 size_t fixed_buffer_size)
316 : strm_(strm), fixed_buffer_(fixed_buffer),
317 fixed_buffer_size_(fixed_buffer_size) {}
319 const char *stream_line_reader::ptr() const {
320 if (glowable_buffer_.empty()) {
321 return fixed_buffer_;
323 return glowable_buffer_.data();
327 size_t stream_line_reader::size() const {
328 if (glowable_buffer_.empty()) {
329 return fixed_buffer_used_size_;
331 return glowable_buffer_.size();
335 bool stream_line_reader::end_with_crlf() const {
336 auto end = ptr() + size();
337 return size() >= 2 && end[-2] == '\r' && end[-1] == '\n';
340 bool stream_line_reader::getline() {
341 fixed_buffer_used_size_ = 0;
342 glowable_buffer_.clear();
344 for (size_t i = 0;; i++) {
346 auto n = strm_.read(&byte, 1);
360 if (byte == '\n') { break; }
366 void stream_line_reader::append(char c) {
367 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
368 fixed_buffer_[fixed_buffer_used_size_++] = c;
369 fixed_buffer_[fixed_buffer_used_size_] = '\0';
371 if (glowable_buffer_.empty()) {
372 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
373 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
375 glowable_buffer_ += c;
379 int close_socket(socket_t sock) {
381 return closesocket(sock);
387 template <typename T> ssize_t handle_EINTR(T fn) {
391 if (res < 0 && errno == EINTR) { continue; }
397 ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
398 return handle_EINTR([&]() {
401 static_cast<char *>(ptr), static_cast<int>(size),
409 ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
411 return handle_EINTR([&]() {
414 static_cast<const char *>(ptr), static_cast<int>(size),
422 ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
423 #ifdef CPPHTTPLIB_USE_POLL
424 struct pollfd pfd_read;
426 pfd_read.events = POLLIN;
428 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
430 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
433 if (sock >= FD_SETSIZE) { return 1; }
441 tv.tv_sec = static_cast<long>(sec);
442 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
444 return handle_EINTR([&]() {
445 return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
450 ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
451 #ifdef CPPHTTPLIB_USE_POLL
452 struct pollfd pfd_read;
454 pfd_read.events = POLLOUT;
456 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
458 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
461 if (sock >= FD_SETSIZE) { return 1; }
469 tv.tv_sec = static_cast<long>(sec);
470 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
472 return handle_EINTR([&]() {
473 return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
478 Error wait_until_socket_is_ready(socket_t sock, time_t sec,
480 #ifdef CPPHTTPLIB_USE_POLL
481 struct pollfd pfd_read;
483 pfd_read.events = POLLIN | POLLOUT;
485 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
487 auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
489 if (poll_res == 0) { return Error::ConnectionTimeout; }
491 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
493 socklen_t len = sizeof(error);
494 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
495 reinterpret_cast<char *>(&error), &len);
496 auto successful = res >= 0 && !error;
497 return successful ? Error::Success : Error::Connection;
500 return Error::Connection;
503 if (sock >= FD_SETSIZE) { return Error::Connection; }
514 tv.tv_sec = static_cast<long>(sec);
515 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
517 auto ret = handle_EINTR([&]() {
518 return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
521 if (ret == 0) { return Error::ConnectionTimeout; }
523 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
525 socklen_t len = sizeof(error);
526 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
527 reinterpret_cast<char *>(&error), &len);
528 auto successful = res >= 0 && !error;
529 return successful ? Error::Success : Error::Connection;
531 return Error::Connection;
535 bool is_socket_alive(socket_t sock) {
536 const auto val = detail::select_read(sock, 0, 0);
539 } else if (val < 0 && errno == EBADF) {
543 return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
546 class SocketStream : public Stream {
548 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
549 time_t write_timeout_sec, time_t write_timeout_usec);
550 ~SocketStream() override;
552 bool is_readable() const override;
553 bool is_writable() const override;
554 ssize_t read(char *ptr, size_t size) override;
555 ssize_t write(const char *ptr, size_t size) override;
556 void get_remote_ip_and_port(std::string &ip, int &port) const override;
557 void get_local_ip_and_port(std::string &ip, int &port) const override;
558 socket_t socket() const override;
562 time_t read_timeout_sec_;
563 time_t read_timeout_usec_;
564 time_t write_timeout_sec_;
565 time_t write_timeout_usec_;
567 std::vector<char> read_buff_;
568 size_t read_buff_off_ = 0;
569 size_t read_buff_content_size_ = 0;
571 static const size_t read_buff_size_ = 1024 * 4;
574 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
575 class SSLSocketStream : public Stream {
577 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
578 time_t read_timeout_usec, time_t write_timeout_sec,
579 time_t write_timeout_usec);
580 ~SSLSocketStream() override;
582 bool is_readable() const override;
583 bool is_writable() const override;
584 ssize_t read(char *ptr, size_t size) override;
585 ssize_t write(const char *ptr, size_t size) override;
586 void get_remote_ip_and_port(std::string &ip, int &port) const override;
587 void get_local_ip_and_port(std::string &ip, int &port) const override;
588 socket_t socket() const override;
593 time_t read_timeout_sec_;
594 time_t read_timeout_usec_;
595 time_t write_timeout_sec_;
596 time_t write_timeout_usec_;
600 bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
601 using namespace std::chrono;
602 auto start = steady_clock::now();
604 auto val = select_read(sock, 0, 10000);
607 } else if (val == 0) {
608 auto current = steady_clock::now();
609 auto duration = duration_cast<milliseconds>(current - start);
610 auto timeout = keep_alive_timeout_sec * 1000;
611 if (duration.count() > timeout) { return false; }
612 std::this_thread::sleep_for(std::chrono::milliseconds(1));
619 template <typename T>
621 process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
622 size_t keep_alive_max_count,
623 time_t keep_alive_timeout_sec, T callback) {
624 assert(keep_alive_max_count > 0);
626 auto count = keep_alive_max_count;
627 while (svr_sock != INVALID_SOCKET && count > 0 &&
628 keep_alive(sock, keep_alive_timeout_sec)) {
629 auto close_connection = count == 1;
630 auto connection_closed = false;
631 ret = callback(close_connection, connection_closed);
632 if (!ret || connection_closed) { break; }
638 template <typename T>
640 process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
641 size_t keep_alive_max_count,
642 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
643 time_t read_timeout_usec, time_t write_timeout_sec,
644 time_t write_timeout_usec, T callback) {
645 return process_server_socket_core(
646 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
647 [&](bool close_connection, bool &connection_closed) {
648 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
649 write_timeout_sec, write_timeout_usec);
650 return callback(strm, close_connection, connection_closed);
654 bool process_client_socket(socket_t sock, time_t read_timeout_sec,
655 time_t read_timeout_usec,
656 time_t write_timeout_sec,
657 time_t write_timeout_usec,
658 std::function<bool(Stream &)> callback) {
659 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
660 write_timeout_sec, write_timeout_usec);
661 return callback(strm);
664 int shutdown_socket(socket_t sock) {
666 return shutdown(sock, SD_BOTH);
668 return shutdown(sock, SHUT_RDWR);
672 template <typename BindOrConnect>
673 socket_t create_socket(const std::string &host, const std::string &ip, int port,
674 int address_family, int socket_flags, bool tcp_nodelay,
675 SocketOptions socket_options,
676 BindOrConnect bind_or_connect) {
678 const char *node = nullptr;
679 struct addrinfo hints;
680 struct addrinfo *result;
682 memset(&hints, 0, sizeof(struct addrinfo));
683 hints.ai_socktype = SOCK_STREAM;
684 hints.ai_protocol = 0;
688 // Ask getaddrinfo to convert IP in c-string to address
689 hints.ai_family = AF_UNSPEC;
690 hints.ai_flags = AI_NUMERICHOST;
692 if (!host.empty()) { node = host.c_str(); }
693 hints.ai_family = address_family;
694 hints.ai_flags = socket_flags;
698 if (hints.ai_family == AF_UNIX) {
699 const auto addrlen = host.length();
700 if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET;
702 auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
703 if (sock != INVALID_SOCKET) {
705 addr.sun_family = AF_UNIX;
706 std::copy(host.begin(), host.end(), addr.sun_path);
708 hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
709 hints.ai_addrlen = static_cast<socklen_t>(
710 sizeof(addr) - sizeof(addr.sun_path) + addrlen);
712 fcntl(sock, F_SETFD, FD_CLOEXEC);
713 if (socket_options) { socket_options(sock); }
715 if (!bind_or_connect(sock, hints)) {
717 sock = INVALID_SOCKET;
724 auto service = std::to_string(port);
726 if (getaddrinfo(node, service.c_str(), &hints, &result)) {
727 #if defined __linux__ && !defined __ANDROID__
730 return INVALID_SOCKET;
733 for (auto rp = result; rp; rp = rp->ai_next) {
737 WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
738 WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
740 * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
741 * and above the socket creation fails on older Windows Systems.
743 * Let's try to create a socket the old way in this case.
746 * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
748 * WSA_FLAG_NO_HANDLE_INHERIT:
749 * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
753 if (sock == INVALID_SOCKET) {
754 sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
757 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
759 if (sock == INVALID_SOCKET) { continue; }
762 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
770 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes),
774 if (socket_options) { socket_options(sock); }
776 if (rp->ai_family == AF_INET6) {
778 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no),
783 if (bind_or_connect(sock, *rp)) {
784 freeaddrinfo(result);
791 freeaddrinfo(result);
792 return INVALID_SOCKET;
795 void set_nonblocking(socket_t sock, bool nonblocking) {
797 auto flags = nonblocking ? 1UL : 0UL;
798 ioctlsocket(sock, FIONBIO, &flags);
800 auto flags = fcntl(sock, F_GETFL, 0);
802 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
806 bool is_connection_error() {
808 return WSAGetLastError() != WSAEWOULDBLOCK;
810 return errno != EINPROGRESS;
814 bool bind_ip_address(socket_t sock, const std::string &host) {
815 struct addrinfo hints;
816 struct addrinfo *result;
818 memset(&hints, 0, sizeof(struct addrinfo));
819 hints.ai_family = AF_UNSPEC;
820 hints.ai_socktype = SOCK_STREAM;
821 hints.ai_protocol = 0;
823 if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; }
826 for (auto rp = result; rp; rp = rp->ai_next) {
827 const auto &ai = *rp;
828 if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
834 freeaddrinfo(result);
838 #if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__
843 std::string if2ip(int address_family, const std::string &ifn) {
844 struct ifaddrs *ifap;
846 std::string addr_candidate;
847 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
848 if (ifa->ifa_addr && ifn == ifa->ifa_name &&
849 (AF_UNSPEC == address_family ||
850 ifa->ifa_addr->sa_family == address_family)) {
851 if (ifa->ifa_addr->sa_family == AF_INET) {
852 auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
853 char buf[INET_ADDRSTRLEN];
854 if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
856 return std::string(buf, INET_ADDRSTRLEN);
858 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
859 auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
860 if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
861 char buf[INET6_ADDRSTRLEN] = {};
862 if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
863 // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
864 auto s6_addr_head = sa->sin6_addr.s6_addr[0];
865 if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
866 addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
869 return std::string(buf, INET6_ADDRSTRLEN);
877 return addr_candidate;
881 socket_t create_client_socket(
882 const std::string &host, const std::string &ip, int port,
883 int address_family, bool tcp_nodelay, SocketOptions socket_options,
884 time_t connection_timeout_sec, time_t connection_timeout_usec,
885 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
886 time_t write_timeout_usec, const std::string &intf, Error &error) {
887 auto sock = create_socket(
888 host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options),
889 [&](socket_t sock2, struct addrinfo &ai) -> bool {
892 auto ip_from_if = if2ip(address_family, intf);
893 if (ip_from_if.empty()) { ip_from_if = intf; }
894 if (!bind_ip_address(sock2, ip_from_if.c_str())) {
895 error = Error::BindIPAddress;
901 set_nonblocking(sock2, true);
904 ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
907 if (is_connection_error()) {
908 error = Error::Connection;
911 error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
912 connection_timeout_usec);
913 if (error != Error::Success) { return false; }
916 set_nonblocking(sock2, false);
920 auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 +
921 read_timeout_usec / 1000);
922 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
926 tv.tv_sec = static_cast<long>(read_timeout_sec);
927 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
928 setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
934 auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 +
935 write_timeout_usec / 1000);
936 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
940 tv.tv_sec = static_cast<long>(write_timeout_sec);
941 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
942 setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
946 error = Error::Success;
950 if (sock != INVALID_SOCKET) {
951 error = Error::Success;
953 if (error == Error::Success) { error = Error::Connection; }
959 bool get_ip_and_port(const struct sockaddr_storage &addr,
960 socklen_t addr_len, std::string &ip, int &port) {
961 if (addr.ss_family == AF_INET) {
962 port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
963 } else if (addr.ss_family == AF_INET6) {
965 ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
970 std::array<char, NI_MAXHOST> ipstr{};
971 if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
972 ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
973 0, NI_NUMERICHOST)) {
981 void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {
982 struct sockaddr_storage addr;
983 socklen_t addr_len = sizeof(addr);
984 if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),
986 get_ip_and_port(addr, addr_len, ip, port);
990 void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
991 struct sockaddr_storage addr;
992 socklen_t addr_len = sizeof(addr);
994 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
997 if (addr.ss_family == AF_UNIX) {
998 #if defined(__linux__)
1000 socklen_t len = sizeof(ucred);
1001 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {
1004 #elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__
1006 socklen_t len = sizeof(pid);
1007 if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {
1014 get_ip_and_port(addr, addr_len, ip, port);
1018 constexpr unsigned int str2tag_core(const char *s, size_t l,
1024 // Unsets the 6 high bits of h, therefore no overflow happens
1025 (((std::numeric_limits<unsigned int>::max)() >> 6) &
1027 static_cast<unsigned char>(*s));
1030 unsigned int str2tag(const std::string &s) {
1031 return str2tag_core(s.data(), s.size(), 0);
1036 constexpr unsigned int operator"" _t(const char *s, size_t l) {
1037 return str2tag_core(s, l, 0);
1043 find_content_type(const std::string &path,
1044 const std::map<std::string, std::string> &user_data) {
1045 auto ext = file_extension(path);
1047 auto it = user_data.find(ext);
1048 if (it != user_data.end()) { return it->second.c_str(); }
1050 using udl::operator""_t;
1052 switch (str2tag(ext)) {
1053 default: return nullptr;
1054 case "css"_t: return "text/css";
1055 case "csv"_t: return "text/csv";
1057 case "html"_t: return "text/html";
1059 case "mjs"_t: return "text/javascript";
1060 case "txt"_t: return "text/plain";
1061 case "vtt"_t: return "text/vtt";
1063 case "apng"_t: return "image/apng";
1064 case "avif"_t: return "image/avif";
1065 case "bmp"_t: return "image/bmp";
1066 case "gif"_t: return "image/gif";
1067 case "png"_t: return "image/png";
1068 case "svg"_t: return "image/svg+xml";
1069 case "webp"_t: return "image/webp";
1070 case "ico"_t: return "image/x-icon";
1071 case "tif"_t: return "image/tiff";
1072 case "tiff"_t: return "image/tiff";
1074 case "jpeg"_t: return "image/jpeg";
1076 case "mp4"_t: return "video/mp4";
1077 case "mpeg"_t: return "video/mpeg";
1078 case "webm"_t: return "video/webm";
1080 case "mp3"_t: return "audio/mp3";
1081 case "mpga"_t: return "audio/mpeg";
1082 case "weba"_t: return "audio/webm";
1083 case "wav"_t: return "audio/wave";
1085 case "otf"_t: return "font/otf";
1086 case "ttf"_t: return "font/ttf";
1087 case "woff"_t: return "font/woff";
1088 case "woff2"_t: return "font/woff2";
1090 case "7z"_t: return "application/x-7z-compressed";
1091 case "atom"_t: return "application/atom+xml";
1092 case "pdf"_t: return "application/pdf";
1093 case "json"_t: return "application/json";
1094 case "rss"_t: return "application/rss+xml";
1095 case "tar"_t: return "application/x-tar";
1097 case "xhtml"_t: return "application/xhtml+xml";
1098 case "xslt"_t: return "application/xslt+xml";
1099 case "xml"_t: return "application/xml";
1100 case "gz"_t: return "application/gzip";
1101 case "zip"_t: return "application/zip";
1102 case "wasm"_t: return "application/wasm";
1106 const char *status_message(int status) {
1108 case 100: return "Continue";
1109 case 101: return "Switching Protocol";
1110 case 102: return "Processing";
1111 case 103: return "Early Hints";
1112 case 200: return "OK";
1113 case 201: return "Created";
1114 case 202: return "Accepted";
1115 case 203: return "Non-Authoritative Information";
1116 case 204: return "No Content";
1117 case 205: return "Reset Content";
1118 case 206: return "Partial Content";
1119 case 207: return "Multi-Status";
1120 case 208: return "Already Reported";
1121 case 226: return "IM Used";
1122 case 300: return "Multiple Choice";
1123 case 301: return "Moved Permanently";
1124 case 302: return "Found";
1125 case 303: return "See Other";
1126 case 304: return "Not Modified";
1127 case 305: return "Use Proxy";
1128 case 306: return "unused";
1129 case 307: return "Temporary Redirect";
1130 case 308: return "Permanent Redirect";
1131 case 400: return "Bad Request";
1132 case 401: return "Unauthorized";
1133 case 402: return "Payment Required";
1134 case 403: return "Forbidden";
1135 case 404: return "Not Found";
1136 case 405: return "Method Not Allowed";
1137 case 406: return "Not Acceptable";
1138 case 407: return "Proxy Authentication Required";
1139 case 408: return "Request Timeout";
1140 case 409: return "Conflict";
1141 case 410: return "Gone";
1142 case 411: return "Length Required";
1143 case 412: return "Precondition Failed";
1144 case 413: return "Payload Too Large";
1145 case 414: return "URI Too Long";
1146 case 415: return "Unsupported Media Type";
1147 case 416: return "Range Not Satisfiable";
1148 case 417: return "Expectation Failed";
1149 case 418: return "I'm a teapot";
1150 case 421: return "Misdirected Request";
1151 case 422: return "Unprocessable Entity";
1152 case 423: return "Locked";
1153 case 424: return "Failed Dependency";
1154 case 425: return "Too Early";
1155 case 426: return "Upgrade Required";
1156 case 428: return "Precondition Required";
1157 case 429: return "Too Many Requests";
1158 case 431: return "Request Header Fields Too Large";
1159 case 451: return "Unavailable For Legal Reasons";
1160 case 501: return "Not Implemented";
1161 case 502: return "Bad Gateway";
1162 case 503: return "Service Unavailable";
1163 case 504: return "Gateway Timeout";
1164 case 505: return "HTTP Version Not Supported";
1165 case 506: return "Variant Also Negotiates";
1166 case 507: return "Insufficient Storage";
1167 case 508: return "Loop Detected";
1168 case 510: return "Not Extended";
1169 case 511: return "Network Authentication Required";
1172 case 500: return "Internal Server Error";
1176 bool can_compress_content_type(const std::string &content_type) {
1177 using udl::operator""_t;
1179 auto tag = str2tag(content_type);
1182 case "image/svg+xml"_t:
1183 case "application/javascript"_t:
1184 case "application/json"_t:
1185 case "application/xml"_t:
1186 case "application/protobuf"_t:
1187 case "application/xhtml+xml"_t: return true;
1190 return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t;
1194 EncodingType encoding_type(const Request &req, const Response &res) {
1196 detail::can_compress_content_type(res.get_header_value("Content-Type"));
1197 if (!ret) { return EncodingType::None; }
1199 const auto &s = req.get_header_value("Accept-Encoding");
1202 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
1203 // TODO: 'Accept-Encoding' has br, not br;q=0
1204 ret = s.find("br") != std::string::npos;
1205 if (ret) { return EncodingType::Brotli; }
1208 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1209 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
1210 ret = s.find("gzip") != std::string::npos;
1211 if (ret) { return EncodingType::Gzip; }
1214 return EncodingType::None;
1217 bool nocompressor::compress(const char *data, size_t data_length,
1218 bool /*last*/, Callback callback) {
1219 if (!data_length) { return true; }
1220 return callback(data, data_length);
1223 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1224 gzip_compressor::gzip_compressor() {
1225 std::memset(&strm_, 0, sizeof(strm_));
1226 strm_.zalloc = Z_NULL;
1227 strm_.zfree = Z_NULL;
1228 strm_.opaque = Z_NULL;
1230 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
1231 Z_DEFAULT_STRATEGY) == Z_OK;
1234 gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }
1236 bool gzip_compressor::compress(const char *data, size_t data_length,
1237 bool last, Callback callback) {
1241 constexpr size_t max_avail_in =
1242 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
1244 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
1245 (std::min)(data_length, max_avail_in));
1246 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
1248 data_length -= strm_.avail_in;
1249 data += strm_.avail_in;
1251 auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;
1254 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
1256 strm_.avail_out = static_cast<uInt>(buff.size());
1257 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
1259 ret = deflate(&strm_, flush);
1260 if (ret == Z_STREAM_ERROR) { return false; }
1262 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
1265 } while (strm_.avail_out == 0);
1267 assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
1268 (flush == Z_NO_FLUSH && ret == Z_OK));
1269 assert(strm_.avail_in == 0);
1270 } while (data_length > 0);
1275 gzip_decompressor::gzip_decompressor() {
1276 std::memset(&strm_, 0, sizeof(strm_));
1277 strm_.zalloc = Z_NULL;
1278 strm_.zfree = Z_NULL;
1279 strm_.opaque = Z_NULL;
1281 // 15 is the value of wbits, which should be at the maximum possible value
1282 // to ensure that any gzip stream can be decoded. The offset of 32 specifies
1283 // that the stream type should be automatically detected either gzip or
1285 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
1288 gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }
1290 bool gzip_decompressor::is_valid() const { return is_valid_; }
1292 bool gzip_decompressor::decompress(const char *data, size_t data_length,
1293 Callback callback) {
1299 constexpr size_t max_avail_in =
1300 (std::numeric_limits<decltype(strm_.avail_in)>::max)();
1302 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
1303 (std::min)(data_length, max_avail_in));
1304 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
1306 data_length -= strm_.avail_in;
1307 data += strm_.avail_in;
1309 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
1310 while (strm_.avail_in > 0) {
1311 strm_.avail_out = static_cast<uInt>(buff.size());
1312 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
1314 auto prev_avail_in = strm_.avail_in;
1316 ret = inflate(&strm_, Z_NO_FLUSH);
1318 if (prev_avail_in - strm_.avail_in == 0) { return false; }
1320 assert(ret != Z_STREAM_ERROR);
1324 case Z_MEM_ERROR: inflateEnd(&strm_); return false;
1327 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
1332 if (ret != Z_OK && ret != Z_STREAM_END) return false;
1334 } while (data_length > 0);
1340 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
1341 brotli_compressor::brotli_compressor() {
1342 state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
1345 brotli_compressor::~brotli_compressor() {
1346 BrotliEncoderDestroyInstance(state_);
1349 bool brotli_compressor::compress(const char *data, size_t data_length,
1350 bool last, Callback callback) {
1351 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
1353 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
1354 auto available_in = data_length;
1355 auto next_in = reinterpret_cast<const uint8_t *>(data);
1359 if (BrotliEncoderIsFinished(state_)) { break; }
1361 if (!available_in) { break; }
1364 auto available_out = buff.size();
1365 auto next_out = buff.data();
1367 if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,
1368 &available_out, &next_out, nullptr)) {
1372 auto output_bytes = buff.size() - available_out;
1374 callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
1381 brotli_decompressor::brotli_decompressor() {
1382 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
1383 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
1384 : BROTLI_DECODER_RESULT_ERROR;
1387 brotli_decompressor::~brotli_decompressor() {
1388 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
1391 bool brotli_decompressor::is_valid() const { return decoder_s; }
1393 bool brotli_decompressor::decompress(const char *data,
1395 Callback callback) {
1396 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
1397 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
1401 const uint8_t *next_in = (const uint8_t *)data;
1402 size_t avail_in = data_length;
1405 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
1407 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
1408 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
1409 char *next_out = buff.data();
1410 size_t avail_out = buff.size();
1412 decoder_r = BrotliDecoderDecompressStream(
1413 decoder_s, &avail_in, &next_in, &avail_out,
1414 reinterpret_cast<uint8_t **>(&next_out), &total_out);
1416 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
1418 if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
1421 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
1422 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
1426 bool has_header(const Headers &headers, const std::string &key) {
1427 return headers.find(key) != headers.end();
1430 const char *get_header_value(const Headers &headers,
1431 const std::string &key, size_t id,
1433 auto rng = headers.equal_range(key);
1434 auto it = rng.first;
1435 std::advance(it, static_cast<ssize_t>(id));
1436 if (it != rng.second) { return it->second.c_str(); }
1440 bool compare_case_ignore(const std::string &a, const std::string &b) {
1441 if (a.size() != b.size()) { return false; }
1442 for (size_t i = 0; i < b.size(); i++) {
1443 if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
1448 template <typename T>
1449 bool parse_header(const char *beg, const char *end, T fn) {
1450 // Skip trailing spaces and tabs.
1451 while (beg < end && is_space_or_tab(end[-1])) {
1456 while (p < end && *p != ':') {
1460 if (p == end) { return false; }
1464 if (*p++ != ':') { return false; }
1466 while (p < end && is_space_or_tab(*p)) {
1471 auto key = std::string(beg, key_end);
1472 auto val = compare_case_ignore(key, "Location")
1473 ? std::string(p, end)
1474 : decode_url(std::string(p, end), false);
1475 fn(std::move(key), std::move(val));
1482 bool read_headers(Stream &strm, Headers &headers) {
1483 const auto bufsiz = 2048;
1485 stream_line_reader line_reader(strm, buf, bufsiz);
1488 if (!line_reader.getline()) { return false; }
1490 // Check if the line ends with CRLF.
1491 auto line_terminator_len = 2;
1492 if (line_reader.end_with_crlf()) {
1493 // Blank line indicates end of headers.
1494 if (line_reader.size() == 2) { break; }
1495 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
1497 // Blank line indicates end of headers.
1498 if (line_reader.size() == 1) { break; }
1499 line_terminator_len = 1;
1503 continue; // Skip invalid line.
1507 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
1509 // Exclude line terminator
1510 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
1512 parse_header(line_reader.ptr(), end,
1513 [&](std::string &&key, std::string &&val) {
1514 headers.emplace(std::move(key), std::move(val));
1521 bool read_content_with_length(Stream &strm, uint64_t len,
1523 ContentReceiverWithProgress out) {
1524 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1528 auto read_len = static_cast<size_t>(len - r);
1529 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
1530 if (n <= 0) { return false; }
1532 if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
1533 r += static_cast<uint64_t>(n);
1536 if (!progress(r, len)) { return false; }
1543 void skip_content_with_length(Stream &strm, uint64_t len) {
1544 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1547 auto read_len = static_cast<size_t>(len - r);
1548 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
1549 if (n <= 0) { return; }
1550 r += static_cast<uint64_t>(n);
1554 bool read_content_without_length(Stream &strm,
1555 ContentReceiverWithProgress out) {
1556 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1559 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
1562 } else if (n == 0) {
1566 if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
1567 r += static_cast<uint64_t>(n);
1573 template <typename T>
1574 bool read_content_chunked(Stream &strm, T &x,
1575 ContentReceiverWithProgress out) {
1576 const auto bufsiz = 16;
1579 stream_line_reader line_reader(strm, buf, bufsiz);
1581 if (!line_reader.getline()) { return false; }
1583 unsigned long chunk_len;
1587 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
1589 if (end_ptr == line_reader.ptr()) { return false; }
1590 if (chunk_len == ULONG_MAX) { return false; }
1592 if (chunk_len == 0) { break; }
1594 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
1598 if (!line_reader.getline()) { return false; }
1600 if (strcmp(line_reader.ptr(), "\r\n")) { return false; }
1602 if (!line_reader.getline()) { return false; }
1605 assert(chunk_len == 0);
1608 if (!line_reader.getline()) { return false; }
1610 while (strcmp(line_reader.ptr(), "\r\n")) {
1611 if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
1613 // Exclude line terminator
1614 constexpr auto line_terminator_len = 2;
1615 auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
1617 parse_header(line_reader.ptr(), end,
1618 [&](std::string &&key, std::string &&val) {
1619 x.headers.emplace(std::move(key), std::move(val));
1622 if (!line_reader.getline()) { return false; }
1628 bool is_chunked_transfer_encoding(const Headers &headers) {
1629 return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""),
1633 template <typename T, typename U>
1634 bool prepare_content_receiver(T &x, int &status,
1635 ContentReceiverWithProgress receiver,
1636 bool decompress, U callback) {
1638 std::string encoding = x.get_header_value("Content-Encoding");
1639 std::unique_ptr<decompressor> decompressor;
1641 if (encoding == "gzip" || encoding == "deflate") {
1642 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1643 decompressor = detail::make_unique<gzip_decompressor>();
1648 } else if (encoding.find("br") != std::string::npos) {
1649 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
1650 decompressor = detail::make_unique<brotli_decompressor>();
1658 if (decompressor->is_valid()) {
1659 ContentReceiverWithProgress out = [&](const char *buf, size_t n,
1660 uint64_t off, uint64_t len) {
1661 return decompressor->decompress(buf, n,
1662 [&](const char *buf2, size_t n2) {
1663 return receiver(buf2, n2, off, len);
1666 return callback(std::move(out));
1674 ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
1676 return receiver(buf, n, off, len);
1678 return callback(std::move(out));
1681 template <typename T>
1682 bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
1683 Progress progress, ContentReceiverWithProgress receiver,
1685 return prepare_content_receiver(
1686 x, status, std::move(receiver), decompress,
1687 [&](const ContentReceiverWithProgress &out) {
1689 auto exceed_payload_max_length = false;
1691 if (is_chunked_transfer_encoding(x.headers)) {
1692 ret = read_content_chunked(strm, x, out);
1693 } else if (!has_header(x.headers, "Content-Length")) {
1694 ret = read_content_without_length(strm, out);
1696 auto len = get_header_value<uint64_t>(x.headers, "Content-Length");
1697 if (len > payload_max_length) {
1698 exceed_payload_max_length = true;
1699 skip_content_with_length(strm, len);
1701 } else if (len > 0) {
1702 ret = read_content_with_length(strm, len, std::move(progress), out);
1706 if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
1709 } // namespace detail
1711 ssize_t write_headers(Stream &strm, const Headers &headers) {
1712 ssize_t write_len = 0;
1713 for (const auto &x : headers) {
1715 strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
1716 if (len < 0) { return len; }
1719 auto len = strm.write("\r\n");
1720 if (len < 0) { return len; }
1725 bool write_data(Stream &strm, const char *d, size_t l) {
1727 while (offset < l) {
1728 auto length = strm.write(d + offset, l - offset);
1729 if (length < 0) { return false; }
1730 offset += static_cast<size_t>(length);
1735 template <typename T>
1736 bool write_content(Stream &strm, const ContentProvider &content_provider,
1737 size_t offset, size_t length, T is_shutting_down,
1739 size_t end_offset = offset + length;
1743 data_sink.write = [&](const char *d, size_t l) -> bool {
1745 if (strm.is_writable() && write_data(strm, d, l)) {
1754 while (offset < end_offset && !is_shutting_down()) {
1755 if (!strm.is_writable()) {
1756 error = Error::Write;
1758 } else if (!content_provider(offset, end_offset - offset, data_sink)) {
1759 error = Error::Canceled;
1762 error = Error::Write;
1767 error = Error::Success;
1771 template <typename T>
1772 bool write_content(Stream &strm, const ContentProvider &content_provider,
1773 size_t offset, size_t length,
1774 const T &is_shutting_down) {
1775 auto error = Error::Success;
1776 return write_content(strm, content_provider, offset, length, is_shutting_down,
1780 template <typename T>
1782 write_content_without_length(Stream &strm,
1783 const ContentProvider &content_provider,
1784 const T &is_shutting_down) {
1786 auto data_available = true;
1790 data_sink.write = [&](const char *d, size_t l) -> bool {
1793 if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }
1798 data_sink.done = [&](void) { data_available = false; };
1800 while (data_available && !is_shutting_down()) {
1801 if (!strm.is_writable()) {
1803 } else if (!content_provider(offset, 0, data_sink)) {
1812 template <typename T, typename U>
1814 write_content_chunked(Stream &strm, const ContentProvider &content_provider,
1815 const T &is_shutting_down, U &compressor, Error &error) {
1817 auto data_available = true;
1821 data_sink.write = [&](const char *d, size_t l) -> bool {
1823 data_available = l > 0;
1826 std::string payload;
1827 if (compressor.compress(d, l, false,
1828 [&](const char *data, size_t data_len) {
1829 payload.append(data, data_len);
1832 if (!payload.empty()) {
1833 // Emit chunked response header and footer for each chunk
1835 from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
1836 if (!strm.is_writable() ||
1837 !write_data(strm, chunk.data(), chunk.size())) {
1848 auto done_with_trailer = [&](const Headers *trailer) {
1849 if (!ok) { return; }
1851 data_available = false;
1853 std::string payload;
1854 if (!compressor.compress(nullptr, 0, true,
1855 [&](const char *data, size_t data_len) {
1856 payload.append(data, data_len);
1863 if (!payload.empty()) {
1864 // Emit chunked response header and footer for each chunk
1865 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
1866 if (!strm.is_writable() ||
1867 !write_data(strm, chunk.data(), chunk.size())) {
1873 static const std::string done_marker("0\r\n");
1874 if (!write_data(strm, done_marker.data(), done_marker.size())) {
1880 for (const auto &kv : *trailer) {
1881 std::string field_line = kv.first + ": " + kv.second + "\r\n";
1882 if (!write_data(strm, field_line.data(), field_line.size())) {
1888 static const std::string crlf("\r\n");
1889 if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }
1892 data_sink.done = [&](void) { done_with_trailer(nullptr); };
1894 data_sink.done_with_trailer = [&](const Headers &trailer) {
1895 done_with_trailer(&trailer);
1898 while (data_available && !is_shutting_down()) {
1899 if (!strm.is_writable()) {
1900 error = Error::Write;
1902 } else if (!content_provider(offset, 0, data_sink)) {
1903 error = Error::Canceled;
1906 error = Error::Write;
1911 error = Error::Success;
1915 template <typename T, typename U>
1916 bool write_content_chunked(Stream &strm,
1917 const ContentProvider &content_provider,
1918 const T &is_shutting_down, U &compressor) {
1919 auto error = Error::Success;
1920 return write_content_chunked(strm, content_provider, is_shutting_down,
1924 template <typename T>
1925 bool redirect(T &cli, Request &req, Response &res,
1926 const std::string &path, const std::string &location,
1928 Request new_req = req;
1929 new_req.path = path;
1930 new_req.redirect_count_ -= 1;
1932 if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
1933 new_req.method = "GET";
1934 new_req.body.clear();
1935 new_req.headers.clear();
1940 auto ret = cli.send(new_req, new_res, error);
1944 res.location = location;
1949 std::string params_to_query_str(const Params ¶ms) {
1952 for (auto it = params.begin(); it != params.end(); ++it) {
1953 if (it != params.begin()) { query += "&"; }
1956 query += encode_query_param(it->second);
1961 void parse_query_text(const std::string &s, Params ¶ms) {
1962 std::set<std::string> cache;
1963 split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
1964 std::string kv(b, e);
1965 if (cache.find(kv) != cache.end()) { return; }
1970 split(b, e, '=', [&](const char *b2, const char *e2) {
1979 params.emplace(decode_url(key, true), decode_url(val, true));
1984 bool parse_multipart_boundary(const std::string &content_type,
1985 std::string &boundary) {
1986 auto boundary_keyword = "boundary=";
1987 auto pos = content_type.find(boundary_keyword);
1988 if (pos == std::string::npos) { return false; }
1989 auto end = content_type.find(';', pos);
1990 auto beg = pos + strlen(boundary_keyword);
1991 boundary = content_type.substr(beg, end - beg);
1992 if (boundary.length() >= 2 && boundary.front() == '"' &&
1993 boundary.back() == '"') {
1994 boundary = boundary.substr(1, boundary.size() - 2);
1996 return !boundary.empty();
1999 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
2000 bool parse_range_header(const std::string &s, Ranges &ranges) {
2002 bool parse_range_header(const std::string &s, Ranges &ranges) try {
2004 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
2006 if (std::regex_match(s, m, re_first_range)) {
2007 auto pos = static_cast<size_t>(m.position(1));
2008 auto len = static_cast<size_t>(m.length(1));
2009 bool all_valid_ranges = true;
2010 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
2011 if (!all_valid_ranges) return;
2012 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
2014 if (std::regex_match(b, e, cm, re_another_range)) {
2016 if (!cm.str(1).empty()) {
2017 first = static_cast<ssize_t>(std::stoll(cm.str(1)));
2021 if (!cm.str(2).empty()) {
2022 last = static_cast<ssize_t>(std::stoll(cm.str(2)));
2025 if (first != -1 && last != -1 && first > last) {
2026 all_valid_ranges = false;
2029 ranges.emplace_back(std::make_pair(first, last));
2032 return all_valid_ranges;
2035 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
2038 } catch (...) { return false; }
2041 class MultipartFormDataParser {
2043 MultipartFormDataParser() = default;
2045 void set_boundary(std::string &&boundary) {
2046 boundary_ = boundary;
2047 dash_boundary_crlf_ = dash_ + boundary_ + crlf_;
2048 crlf_dash_boundary_ = crlf_ + dash_ + boundary_;
2051 bool is_valid() const { return is_valid_; }
2053 bool parse(const char *buf, size_t n, const ContentReceiver &content_callback,
2054 const MultipartContentHeader &header_callback) {
2056 // TODO: support 'filename*'
2057 static const std::regex re_content_disposition(
2058 R"~(^Content-Disposition:\s*form-data;\s*name="(.*?)"(?:;\s*filename="(.*?)")?(?:;\s*filename\*=\S+)?\s*$)~",
2059 std::regex_constants::icase);
2063 while (buf_size() > 0) {
2065 case 0: { // Initial boundary
2066 buf_erase(buf_find(dash_boundary_crlf_));
2067 if (dash_boundary_crlf_.size() > buf_size()) { return true; }
2068 if (!buf_start_with(dash_boundary_crlf_)) { return false; }
2069 buf_erase(dash_boundary_crlf_.size());
2073 case 1: { // New entry
2078 case 2: { // Headers
2079 auto pos = buf_find(crlf_);
2080 if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
2081 while (pos < buf_size()) {
2084 if (!header_callback(file_)) {
2088 buf_erase(crlf_.size());
2093 static const std::string header_name = "content-type:";
2094 const auto header = buf_head(pos);
2095 if (start_with_case_ignore(header, header_name)) {
2096 file_.content_type = trim_copy(header.substr(header_name.size()));
2099 if (std::regex_match(header, m, re_content_disposition)) {
2101 file_.filename = m[2];
2107 buf_erase(pos + crlf_.size());
2108 pos = buf_find(crlf_);
2110 if (state_ != 3) { return true; }
2114 if (crlf_dash_boundary_.size() > buf_size()) { return true; }
2115 auto pos = buf_find(crlf_dash_boundary_);
2116 if (pos < buf_size()) {
2117 if (!content_callback(buf_data(), pos)) {
2121 buf_erase(pos + crlf_dash_boundary_.size());
2124 auto len = buf_size() - crlf_dash_boundary_.size();
2126 if (!content_callback(buf_data(), len)) {
2136 case 4: { // Boundary
2137 if (crlf_.size() > buf_size()) { return true; }
2138 if (buf_start_with(crlf_)) {
2139 buf_erase(crlf_.size());
2142 if (dash_crlf_.size() > buf_size()) { return true; }
2143 if (buf_start_with(dash_crlf_)) {
2144 buf_erase(dash_crlf_.size());
2146 buf_erase(buf_size()); // Remove epilogue
2160 void clear_file_info() {
2162 file_.filename.clear();
2163 file_.content_type.clear();
2166 bool start_with_case_ignore(const std::string &a,
2167 const std::string &b) const {
2168 if (a.size() < b.size()) { return false; }
2169 for (size_t i = 0; i < b.size(); i++) {
2170 if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
2175 const std::string dash_ = "--";
2176 const std::string crlf_ = "\r\n";
2177 const std::string dash_crlf_ = "--\r\n";
2178 std::string boundary_;
2179 std::string dash_boundary_crlf_;
2180 std::string crlf_dash_boundary_;
2183 bool is_valid_ = false;
2184 MultipartFormData file_;
2187 bool start_with(const std::string &a, size_t spos, size_t epos,
2188 const std::string &b) const {
2189 if (epos - spos < b.size()) { return false; }
2190 for (size_t i = 0; i < b.size(); i++) {
2191 if (a[i + spos] != b[i]) { return false; }
2196 size_t buf_size() const { return buf_epos_ - buf_spos_; }
2198 const char *buf_data() const { return &buf_[buf_spos_]; }
2200 std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
2202 bool buf_start_with(const std::string &s) const {
2203 return start_with(buf_, buf_spos_, buf_epos_, s);
2206 size_t buf_find(const std::string &s) const {
2209 size_t off = buf_spos_;
2210 while (off < buf_epos_) {
2213 if (pos == buf_epos_) { return buf_size(); }
2214 if (buf_[pos] == c) { break; }
2218 auto remaining_size = buf_epos_ - pos;
2219 if (s.size() > remaining_size) { return buf_size(); }
2221 if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
2229 void buf_append(const char *data, size_t n) {
2230 auto remaining_size = buf_size();
2231 if (remaining_size > 0 && buf_spos_ > 0) {
2232 for (size_t i = 0; i < remaining_size; i++) {
2233 buf_[i] = buf_[buf_spos_ + i];
2237 buf_epos_ = remaining_size;
2239 if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }
2241 for (size_t i = 0; i < n; i++) {
2242 buf_[buf_epos_ + i] = data[i];
2247 void buf_erase(size_t size) { buf_spos_ += size; }
2250 size_t buf_spos_ = 0;
2251 size_t buf_epos_ = 0;
2254 std::string to_lower(const char *beg, const char *end) {
2258 out += static_cast<char>(::tolower(*it));
2264 std::string make_multipart_data_boundary() {
2265 static const char data[] =
2266 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2268 // std::random_device might actually be deterministic on some
2269 // platforms, but due to lack of support in the c++ standard library,
2270 // doing better requires either some ugly hacks or breaking portability.
2271 std::random_device seed_gen;
2273 // Request 128 bits of entropy for initialization
2274 std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
2275 std::mt19937 engine(seed_sequence);
2277 std::string result = "--cpp-httplib-multipart-data-";
2279 for (auto i = 0; i < 16; i++) {
2280 result += data[engine() % (sizeof(data) - 1)];
2286 bool is_multipart_boundary_chars_valid(const std::string &boundary) {
2288 for (size_t i = 0; i < boundary.size(); i++) {
2289 auto c = boundary[i];
2290 if (!std::isalnum(c) && c != '-' && c != '_') {
2298 template <typename T>
2300 serialize_multipart_formdata_item_begin(const T &item,
2301 const std::string &boundary) {
2302 std::string body = "--" + boundary + "\r\n";
2303 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
2304 if (!item.filename.empty()) {
2305 body += "; filename=\"" + item.filename + "\"";
2308 if (!item.content_type.empty()) {
2309 body += "Content-Type: " + item.content_type + "\r\n";
2316 std::string serialize_multipart_formdata_item_end() { return "\r\n"; }
2319 serialize_multipart_formdata_finish(const std::string &boundary) {
2320 return "--" + boundary + "--\r\n";
2324 serialize_multipart_formdata_get_content_type(const std::string &boundary) {
2325 return "multipart/form-data; boundary=" + boundary;
2329 serialize_multipart_formdata(const MultipartFormDataItems &items,
2330 const std::string &boundary, bool finish = true) {
2333 for (const auto &item : items) {
2334 body += serialize_multipart_formdata_item_begin(item, boundary);
2335 body += item.content + serialize_multipart_formdata_item_end();
2338 if (finish) body += serialize_multipart_formdata_finish(boundary);
2343 std::pair<size_t, size_t>
2344 get_range_offset_and_length(const Request &req, size_t content_length,
2346 auto r = req.ranges[index];
2348 if (r.first == -1 && r.second == -1) {
2349 return std::make_pair(0, content_length);
2352 auto slen = static_cast<ssize_t>(content_length);
2354 if (r.first == -1) {
2355 r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
2356 r.second = slen - 1;
2359 if (r.second == -1) { r.second = slen - 1; }
2360 return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
2363 std::string make_content_range_header_field(size_t offset, size_t length,
2364 size_t content_length) {
2365 std::string field = "bytes ";
2366 field += std::to_string(offset);
2368 field += std::to_string(offset + length - 1);
2370 field += std::to_string(content_length);
2374 template <typename SToken, typename CToken, typename Content>
2375 bool process_multipart_ranges_data(const Request &req, Response &res,
2376 const std::string &boundary,
2377 const std::string &content_type,
2378 SToken stoken, CToken ctoken,
2380 for (size_t i = 0; i < req.ranges.size(); i++) {
2384 if (!content_type.empty()) {
2385 ctoken("Content-Type: ");
2386 stoken(content_type);
2390 auto offsets = get_range_offset_and_length(req, res.body.size(), i);
2391 auto offset = offsets.first;
2392 auto length = offsets.second;
2394 ctoken("Content-Range: ");
2395 stoken(make_content_range_header_field(offset, length, res.body.size()));
2398 if (!content(offset, length)) { return false; }
2409 bool make_multipart_ranges_data(const Request &req, Response &res,
2410 const std::string &boundary,
2411 const std::string &content_type,
2412 std::string &data) {
2413 return process_multipart_ranges_data(
2414 req, res, boundary, content_type,
2415 [&](const std::string &token) { data += token; },
2416 [&](const std::string &token) { data += token; },
2417 [&](size_t offset, size_t length) {
2418 if (offset < res.body.size()) {
2419 data += res.body.substr(offset, length);
2427 get_multipart_ranges_data_length(const Request &req, Response &res,
2428 const std::string &boundary,
2429 const std::string &content_type) {
2430 size_t data_length = 0;
2432 process_multipart_ranges_data(
2433 req, res, boundary, content_type,
2434 [&](const std::string &token) { data_length += token.size(); },
2435 [&](const std::string &token) { data_length += token.size(); },
2436 [&](size_t /*offset*/, size_t length) {
2437 data_length += length;
2444 template <typename T>
2445 bool write_multipart_ranges_data(Stream &strm, const Request &req,
2447 const std::string &boundary,
2448 const std::string &content_type,
2449 const T &is_shutting_down) {
2450 return process_multipart_ranges_data(
2451 req, res, boundary, content_type,
2452 [&](const std::string &token) { strm.write(token); },
2453 [&](const std::string &token) { strm.write(token); },
2454 [&](size_t offset, size_t length) {
2455 return write_content(strm, res.content_provider_, offset, length,
2460 std::pair<size_t, size_t>
2461 get_range_offset_and_length(const Request &req, const Response &res,
2463 auto r = req.ranges[index];
2465 if (r.second == -1) {
2466 r.second = static_cast<ssize_t>(res.content_length_) - 1;
2469 return std::make_pair(r.first, r.second - r.first + 1);
2472 bool expect_content(const Request &req) {
2473 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
2474 req.method == "PRI" || req.method == "DELETE") {
2477 // TODO: check if Content-Length is set
2481 bool has_crlf(const std::string &s) {
2484 if (*p == '\r' || *p == '\n') { return true; }
2490 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2491 std::string message_digest(const std::string &s, const EVP_MD *algo) {
2492 auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
2493 EVP_MD_CTX_new(), EVP_MD_CTX_free);
2495 unsigned int hash_length = 0;
2496 unsigned char hash[EVP_MAX_MD_SIZE];
2498 EVP_DigestInit_ex(context.get(), algo, nullptr);
2499 EVP_DigestUpdate(context.get(), s.c_str(), s.size());
2500 EVP_DigestFinal_ex(context.get(), hash, &hash_length);
2502 std::stringstream ss;
2503 for (auto i = 0u; i < hash_length; ++i) {
2504 ss << std::hex << std::setw(2) << std::setfill('0')
2505 << (unsigned int)hash[i];
2511 std::string MD5(const std::string &s) {
2512 return message_digest(s, EVP_md5());
2515 std::string SHA_256(const std::string &s) {
2516 return message_digest(s, EVP_sha256());
2519 std::string SHA_512(const std::string &s) {
2520 return message_digest(s, EVP_sha512());
2524 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2526 // NOTE: This code came up with the following stackoverflow post:
2527 // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
2528 bool load_system_certs_on_windows(X509_STORE *store) {
2529 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
2530 if (!hStore) { return false; }
2532 auto result = false;
2533 PCCERT_CONTEXT pContext = NULL;
2534 while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
2537 static_cast<const unsigned char *>(pContext->pbCertEncoded);
2539 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
2541 X509_STORE_add_cert(store, x509);
2547 CertFreeCertificateContext(pContext);
2548 CertCloseStore(hStore, 0);
2552 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
2554 template <typename T>
2556 std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
2558 void cf_object_ptr_deleter(CFTypeRef obj) {
2559 if (obj) { CFRelease(obj); }
2562 bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
2563 CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
2564 CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
2567 CFObjectPtr<CFDictionaryRef> query(
2568 CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
2569 sizeof(keys) / sizeof(keys[0]),
2570 &kCFTypeDictionaryKeyCallBacks,
2571 &kCFTypeDictionaryValueCallBacks),
2572 cf_object_ptr_deleter);
2574 if (!query) { return false; }
2576 CFTypeRef security_items = nullptr;
2577 if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
2578 CFArrayGetTypeID() != CFGetTypeID(security_items)) {
2582 certs.reset(reinterpret_cast<CFArrayRef>(security_items));
2586 bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
2587 CFArrayRef root_security_items = nullptr;
2588 if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
2592 certs.reset(root_security_items);
2596 bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
2597 auto result = false;
2598 for (int i = 0; i < CFArrayGetCount(certs); ++i) {
2599 const auto cert = reinterpret_cast<const __SecCertificate *>(
2600 CFArrayGetValueAtIndex(certs, i));
2602 if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
2604 CFDataRef cert_data = nullptr;
2605 if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
2610 CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
2612 auto encoded_cert = static_cast<const unsigned char *>(
2613 CFDataGetBytePtr(cert_data_ptr.get()));
2616 d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
2619 X509_STORE_add_cert(store, x509);
2628 bool load_system_certs_on_macos(X509_STORE *store) {
2629 auto result = false;
2630 CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
2631 if (retrieve_certs_from_keychain(certs) && certs) {
2632 result = add_certs_to_x509_store(certs.get(), store);
2635 if (retrieve_root_certs_from_keychain(certs) && certs) {
2636 result = add_certs_to_x509_store(certs.get(), store) || result;
2641 #endif // TARGET_OS_OSX
2643 #endif // CPPHTTPLIB_OPENSSL_SUPPORT
2650 if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
2654 if (is_valid_) WSACleanup();
2657 bool is_valid_ = false;
2660 static WSInit wsinit_;
2663 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2664 std::pair<std::string, std::string> make_digest_authentication_header(
2665 const Request &req, const std::map<std::string, std::string> &auth,
2666 size_t cnonce_count, const std::string &cnonce, const std::string &username,
2667 const std::string &password, bool is_proxy = false) {
2670 std::stringstream ss;
2671 ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
2676 if (auth.find("qop") != auth.end()) {
2677 qop = auth.at("qop");
2678 if (qop.find("auth-int") != std::string::npos) {
2680 } else if (qop.find("auth") != std::string::npos) {
2687 std::string algo = "MD5";
2688 if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
2690 std::string response;
2692 auto H = algo == "SHA-256" ? detail::SHA_256
2693 : algo == "SHA-512" ? detail::SHA_512
2696 auto A1 = username + ":" + auth.at("realm") + ":" + password;
2698 auto A2 = req.method + ":" + req.path;
2699 if (qop == "auth-int") { A2 += ":" + H(req.body); }
2702 response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
2704 response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
2705 ":" + qop + ":" + H(A2));
2709 auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
2711 auto field = "Digest username=\"" + username + "\", realm=\"" +
2712 auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
2713 "\", uri=\"" + req.path + "\", algorithm=" + algo +
2714 (qop.empty() ? ", response=\""
2715 : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
2716 cnonce + "\", response=\"") +
2718 (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
2720 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2721 return std::make_pair(key, field);
2725 bool parse_www_authenticate(const Response &res,
2726 std::map<std::string, std::string> &auth,
2728 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
2729 if (res.has_header(auth_key)) {
2730 static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
2731 auto s = res.get_header_value(auth_key);
2732 auto pos = s.find(' ');
2733 if (pos != std::string::npos) {
2734 auto type = s.substr(0, pos);
2735 if (type == "Basic") {
2737 } else if (type == "Digest") {
2738 s = s.substr(pos + 1);
2739 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
2740 for (auto i = beg; i != std::sregex_iterator(); ++i) {
2742 auto key = s.substr(static_cast<size_t>(m.position(1)),
2743 static_cast<size_t>(m.length(1)));
2744 auto val = m.length(2) > 0
2745 ? s.substr(static_cast<size_t>(m.position(2)),
2746 static_cast<size_t>(m.length(2)))
2747 : s.substr(static_cast<size_t>(m.position(3)),
2748 static_cast<size_t>(m.length(3)));
2758 // https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
2759 std::string random_string(size_t length) {
2760 auto randchar = []() -> char {
2761 const char charset[] = "0123456789"
2762 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2763 "abcdefghijklmnopqrstuvwxyz";
2764 const size_t max_index = (sizeof(charset) - 1);
2765 return charset[static_cast<size_t>(std::rand()) % max_index];
2767 std::string str(length, 0);
2768 std::generate_n(str.begin(), length, randchar);
2772 class ContentProviderAdapter {
2774 explicit ContentProviderAdapter(
2775 ContentProviderWithoutLength &&content_provider)
2776 : content_provider_(content_provider) {}
2778 bool operator()(size_t offset, size_t, DataSink &sink) {
2779 return content_provider_(offset, sink);
2783 ContentProviderWithoutLength content_provider_;
2786 } // namespace detail
2788 std::string hosted_at(const std::string &hostname) {
2789 std::vector<std::string> addrs;
2790 hosted_at(hostname, addrs);
2791 if (addrs.empty()) { return std::string(); }
2795 void hosted_at(const std::string &hostname,
2796 std::vector<std::string> &addrs) {
2797 struct addrinfo hints;
2798 struct addrinfo *result;
2800 memset(&hints, 0, sizeof(struct addrinfo));
2801 hints.ai_family = AF_UNSPEC;
2802 hints.ai_socktype = SOCK_STREAM;
2803 hints.ai_protocol = 0;
2805 if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {
2806 #if defined __linux__ && !defined __ANDROID__
2812 for (auto rp = result; rp; rp = rp->ai_next) {
2814 *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
2817 if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,
2819 addrs.push_back(ip);
2823 freeaddrinfo(result);
2826 std::string append_query_params(const std::string &path,
2827 const Params ¶ms) {
2828 std::string path_with_query = path;
2829 const static std::regex re("[^?]+\\?.*");
2830 auto delm = std::regex_match(path, re) ? '&' : '?';
2831 path_with_query += delm + detail::params_to_query_str(params);
2832 return path_with_query;
2836 std::pair<std::string, std::string> make_range_header(Ranges ranges) {
2837 std::string field = "bytes=";
2839 for (auto r : ranges) {
2840 if (i != 0) { field += ", "; }
2841 if (r.first != -1) { field += std::to_string(r.first); }
2843 if (r.second != -1) { field += std::to_string(r.second); }
2846 return std::make_pair("Range", std::move(field));
2849 std::pair<std::string, std::string>
2850 make_basic_authentication_header(const std::string &username,
2851 const std::string &password, bool is_proxy) {
2852 auto field = "Basic " + detail::base64_encode(username + ":" + password);
2853 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2854 return std::make_pair(key, std::move(field));
2857 std::pair<std::string, std::string>
2858 make_bearer_token_authentication_header(const std::string &token,
2859 bool is_proxy = false) {
2860 auto field = "Bearer " + token;
2861 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2862 return std::make_pair(key, std::move(field));
2865 // Request implementation
2866 bool Request::has_header(const std::string &key) const {
2867 return detail::has_header(headers, key);
2870 std::string Request::get_header_value(const std::string &key,
2872 return detail::get_header_value(headers, key, id, "");
2875 size_t Request::get_header_value_count(const std::string &key) const {
2876 auto r = headers.equal_range(key);
2877 return static_cast<size_t>(std::distance(r.first, r.second));
2880 void Request::set_header(const std::string &key,
2881 const std::string &val) {
2882 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
2883 headers.emplace(key, val);
2887 bool Request::has_param(const std::string &key) const {
2888 return params.find(key) != params.end();
2891 std::string Request::get_param_value(const std::string &key,
2893 auto rng = params.equal_range(key);
2894 auto it = rng.first;
2895 std::advance(it, static_cast<ssize_t>(id));
2896 if (it != rng.second) { return it->second; }
2897 return std::string();
2900 size_t Request::get_param_value_count(const std::string &key) const {
2901 auto r = params.equal_range(key);
2902 return static_cast<size_t>(std::distance(r.first, r.second));
2905 bool Request::is_multipart_form_data() const {
2906 const auto &content_type = get_header_value("Content-Type");
2907 return !content_type.rfind("multipart/form-data", 0);
2910 bool Request::has_file(const std::string &key) const {
2911 return files.find(key) != files.end();
2914 MultipartFormData Request::get_file_value(const std::string &key) const {
2915 auto it = files.find(key);
2916 if (it != files.end()) { return it->second; }
2917 return MultipartFormData();
2920 std::vector<MultipartFormData>
2921 Request::get_file_values(const std::string &key) const {
2922 std::vector<MultipartFormData> values;
2923 auto rng = files.equal_range(key);
2924 for (auto it = rng.first; it != rng.second; it++) {
2925 values.push_back(it->second);
2930 // Response implementation
2931 bool Response::has_header(const std::string &key) const {
2932 return headers.find(key) != headers.end();
2935 std::string Response::get_header_value(const std::string &key,
2937 return detail::get_header_value(headers, key, id, "");
2940 size_t Response::get_header_value_count(const std::string &key) const {
2941 auto r = headers.equal_range(key);
2942 return static_cast<size_t>(std::distance(r.first, r.second));
2945 void Response::set_header(const std::string &key,
2946 const std::string &val) {
2947 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
2948 headers.emplace(key, val);
2952 void Response::set_redirect(const std::string &url, int stat) {
2953 if (!detail::has_crlf(url)) {
2954 set_header("Location", url);
2955 if (300 <= stat && stat < 400) {
2956 this->status = stat;
2963 void Response::set_content(const char *s, size_t n,
2964 const std::string &content_type) {
2967 auto rng = headers.equal_range("Content-Type");
2968 headers.erase(rng.first, rng.second);
2969 set_header("Content-Type", content_type);
2972 void Response::set_content(const std::string &s,
2973 const std::string &content_type) {
2974 set_content(s.data(), s.size(), content_type);
2977 void Response::set_content_provider(
2978 size_t in_length, const std::string &content_type, ContentProvider provider,
2979 ContentProviderResourceReleaser resource_releaser) {
2980 set_header("Content-Type", content_type);
2981 content_length_ = in_length;
2982 if (in_length > 0) { content_provider_ = std::move(provider); }
2983 content_provider_resource_releaser_ = resource_releaser;
2984 is_chunked_content_provider_ = false;
2987 void Response::set_content_provider(
2988 const std::string &content_type, ContentProviderWithoutLength provider,
2989 ContentProviderResourceReleaser resource_releaser) {
2990 set_header("Content-Type", content_type);
2991 content_length_ = 0;
2992 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
2993 content_provider_resource_releaser_ = resource_releaser;
2994 is_chunked_content_provider_ = false;
2997 void Response::set_chunked_content_provider(
2998 const std::string &content_type, ContentProviderWithoutLength provider,
2999 ContentProviderResourceReleaser resource_releaser) {
3000 set_header("Content-Type", content_type);
3001 content_length_ = 0;
3002 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
3003 content_provider_resource_releaser_ = resource_releaser;
3004 is_chunked_content_provider_ = true;
3007 // Result implementation
3008 bool Result::has_request_header(const std::string &key) const {
3009 return request_headers_.find(key) != request_headers_.end();
3012 std::string Result::get_request_header_value(const std::string &key,
3014 return detail::get_header_value(request_headers_, key, id, "");
3018 Result::get_request_header_value_count(const std::string &key) const {
3019 auto r = request_headers_.equal_range(key);
3020 return static_cast<size_t>(std::distance(r.first, r.second));
3023 // Stream implementation
3024 ssize_t Stream::write(const char *ptr) {
3025 return write(ptr, strlen(ptr));
3028 ssize_t Stream::write(const std::string &s) {
3029 return write(s.data(), s.size());
3034 // Socket stream implementation
3035 SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
3036 time_t read_timeout_usec,
3037 time_t write_timeout_sec,
3038 time_t write_timeout_usec)
3039 : sock_(sock), read_timeout_sec_(read_timeout_sec),
3040 read_timeout_usec_(read_timeout_usec),
3041 write_timeout_sec_(write_timeout_sec),
3042 write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
3044 SocketStream::~SocketStream() {}
3046 bool SocketStream::is_readable() const {
3047 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
3050 bool SocketStream::is_writable() const {
3051 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
3052 is_socket_alive(sock_);
3055 ssize_t SocketStream::read(char *ptr, size_t size) {
3058 (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
3060 size = (std::min)(size,
3061 static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
3064 if (read_buff_off_ < read_buff_content_size_) {
3065 auto remaining_size = read_buff_content_size_ - read_buff_off_;
3066 if (size <= remaining_size) {
3067 memcpy(ptr, read_buff_.data() + read_buff_off_, size);
3068 read_buff_off_ += size;
3069 return static_cast<ssize_t>(size);
3071 memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
3072 read_buff_off_ += remaining_size;
3073 return static_cast<ssize_t>(remaining_size);
3077 if (!is_readable()) { return -1; }
3080 read_buff_content_size_ = 0;
3082 if (size < read_buff_size_) {
3083 auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,
3084 CPPHTTPLIB_RECV_FLAGS);
3087 } else if (n <= static_cast<ssize_t>(size)) {
3088 memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
3091 memcpy(ptr, read_buff_.data(), size);
3092 read_buff_off_ = size;
3093 read_buff_content_size_ = static_cast<size_t>(n);
3094 return static_cast<ssize_t>(size);
3097 return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
3101 ssize_t SocketStream::write(const char *ptr, size_t size) {
3102 if (!is_writable()) { return -1; }
3104 #if defined(_WIN32) && !defined(_WIN64)
3106 (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
3109 return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
3112 void SocketStream::get_remote_ip_and_port(std::string &ip,
3114 return detail::get_remote_ip_and_port(sock_, ip, port);
3117 void SocketStream::get_local_ip_and_port(std::string &ip,
3119 return detail::get_local_ip_and_port(sock_, ip, port);
3122 socket_t SocketStream::socket() const { return sock_; }
3124 // Buffer stream implementation
3125 bool BufferStream::is_readable() const { return true; }
3127 bool BufferStream::is_writable() const { return true; }
3129 ssize_t BufferStream::read(char *ptr, size_t size) {
3130 #if defined(_MSC_VER) && _MSC_VER < 1910
3131 auto len_read = buffer._Copy_s(ptr, size, size, position);
3133 auto len_read = buffer.copy(ptr, size, position);
3135 position += static_cast<size_t>(len_read);
3136 return static_cast<ssize_t>(len_read);
3139 ssize_t BufferStream::write(const char *ptr, size_t size) {
3140 buffer.append(ptr, size);
3141 return static_cast<ssize_t>(size);
3144 void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
3145 int & /*port*/) const {}
3147 void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
3148 int & /*port*/) const {}
3150 socket_t BufferStream::socket() const { return 0; }
3152 const std::string &BufferStream::get_buffer() const { return buffer; }
3154 } // namespace detail
3156 // HTTP server implementation
3159 [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
3161 signal(SIGPIPE, SIG_IGN);
3165 Server::~Server() {}
3167 Server &Server::Get(const std::string &pattern, Handler handler) {
3168 get_handlers_.push_back(
3169 std::make_pair(std::regex(pattern), std::move(handler)));
3173 Server &Server::Post(const std::string &pattern, Handler handler) {
3174 post_handlers_.push_back(
3175 std::make_pair(std::regex(pattern), std::move(handler)));
3179 Server &Server::Post(const std::string &pattern,
3180 HandlerWithContentReader handler) {
3181 post_handlers_for_content_reader_.push_back(
3182 std::make_pair(std::regex(pattern), std::move(handler)));
3186 Server &Server::Put(const std::string &pattern, Handler handler) {
3187 put_handlers_.push_back(
3188 std::make_pair(std::regex(pattern), std::move(handler)));
3192 Server &Server::Put(const std::string &pattern,
3193 HandlerWithContentReader handler) {
3194 put_handlers_for_content_reader_.push_back(
3195 std::make_pair(std::regex(pattern), std::move(handler)));
3199 Server &Server::Patch(const std::string &pattern, Handler handler) {
3200 patch_handlers_.push_back(
3201 std::make_pair(std::regex(pattern), std::move(handler)));
3205 Server &Server::Patch(const std::string &pattern,
3206 HandlerWithContentReader handler) {
3207 patch_handlers_for_content_reader_.push_back(
3208 std::make_pair(std::regex(pattern), std::move(handler)));
3212 Server &Server::Delete(const std::string &pattern, Handler handler) {
3213 delete_handlers_.push_back(
3214 std::make_pair(std::regex(pattern), std::move(handler)));
3218 Server &Server::Delete(const std::string &pattern,
3219 HandlerWithContentReader handler) {
3220 delete_handlers_for_content_reader_.push_back(
3221 std::make_pair(std::regex(pattern), std::move(handler)));
3225 Server &Server::Options(const std::string &pattern, Handler handler) {
3226 options_handlers_.push_back(
3227 std::make_pair(std::regex(pattern), std::move(handler)));
3231 bool Server::set_base_dir(const std::string &dir,
3232 const std::string &mount_point) {
3233 return set_mount_point(mount_point, dir);
3236 bool Server::set_mount_point(const std::string &mount_point,
3237 const std::string &dir, Headers headers) {
3238 if (detail::is_dir(dir)) {
3239 std::string mnt = !mount_point.empty() ? mount_point : "/";
3240 if (!mnt.empty() && mnt[0] == '/') {
3241 base_dirs_.push_back({mnt, dir, std::move(headers)});
3248 bool Server::remove_mount_point(const std::string &mount_point) {
3249 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
3250 if (it->mount_point == mount_point) {
3251 base_dirs_.erase(it);
3259 Server::set_file_extension_and_mimetype_mapping(const std::string &ext,
3260 const std::string &mime) {
3261 file_extension_and_mimetype_map_[ext] = mime;
3265 Server &Server::set_file_request_handler(Handler handler) {
3266 file_request_handler_ = std::move(handler);
3270 Server &Server::set_error_handler(HandlerWithResponse handler) {
3271 error_handler_ = std::move(handler);
3275 Server &Server::set_error_handler(Handler handler) {
3276 error_handler_ = [handler](const Request &req, Response &res) {
3278 return HandlerResponse::Handled;
3283 Server &Server::set_exception_handler(ExceptionHandler handler) {
3284 exception_handler_ = std::move(handler);
3288 Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {
3289 pre_routing_handler_ = std::move(handler);
3293 Server &Server::set_post_routing_handler(Handler handler) {
3294 post_routing_handler_ = std::move(handler);
3298 Server &Server::set_logger(Logger logger) {
3299 logger_ = std::move(logger);
3304 Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
3305 expect_100_continue_handler_ = std::move(handler);
3309 Server &Server::set_address_family(int family) {
3310 address_family_ = family;
3314 Server &Server::set_tcp_nodelay(bool on) {
3319 Server &Server::set_socket_options(SocketOptions socket_options) {
3320 socket_options_ = std::move(socket_options);
3324 Server &Server::set_default_headers(Headers headers) {
3325 default_headers_ = std::move(headers);
3329 Server &Server::set_keep_alive_max_count(size_t count) {
3330 keep_alive_max_count_ = count;
3334 Server &Server::set_keep_alive_timeout(time_t sec) {
3335 keep_alive_timeout_sec_ = sec;
3339 Server &Server::set_read_timeout(time_t sec, time_t usec) {
3340 read_timeout_sec_ = sec;
3341 read_timeout_usec_ = usec;
3345 Server &Server::set_write_timeout(time_t sec, time_t usec) {
3346 write_timeout_sec_ = sec;
3347 write_timeout_usec_ = usec;
3351 Server &Server::set_idle_interval(time_t sec, time_t usec) {
3352 idle_interval_sec_ = sec;
3353 idle_interval_usec_ = usec;
3357 Server &Server::set_payload_max_length(size_t length) {
3358 payload_max_length_ = length;
3362 bool Server::bind_to_port(const std::string &host, int port,
3364 if (bind_internal(host, port, socket_flags) < 0) return false;
3367 int Server::bind_to_any_port(const std::string &host, int socket_flags) {
3368 return bind_internal(host, 0, socket_flags);
3371 bool Server::listen_after_bind() {
3372 auto se = detail::scope_exit([&]() { done_ = true; });
3373 return listen_internal();
3376 bool Server::listen(const std::string &host, int port,
3378 auto se = detail::scope_exit([&]() { done_ = true; });
3379 return bind_to_port(host, port, socket_flags) && listen_internal();
3382 bool Server::is_running() const { return is_running_; }
3384 void Server::wait_until_ready() const {
3385 while (!is_running() && !done_) {
3386 std::this_thread::sleep_for(std::chrono::milliseconds{1});
3390 void Server::stop() {
3392 assert(svr_sock_ != INVALID_SOCKET);
3393 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
3394 detail::shutdown_socket(sock);
3395 detail::close_socket(sock);
3399 bool Server::parse_request_line(const char *s, Request &req) {
3400 auto len = strlen(s);
3401 if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
3407 detail::split(s, s + len, ' ', [&](const char *b, const char *e) {
3409 case 0: req.method = std::string(b, e); break;
3410 case 1: req.target = std::string(b, e); break;
3411 case 2: req.version = std::string(b, e); break;
3417 if (count != 3) { return false; }
3420 static const std::set<std::string> methods{
3421 "GET", "HEAD", "POST", "PUT", "DELETE",
3422 "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
3424 if (methods.find(req.method) == methods.end()) { return false; }
3426 if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
3429 // Skip URL fragment
3430 for (size_t i = 0; i < req.target.size(); i++) {
3431 if (req.target[i] == '#') {
3432 req.target.erase(i);
3439 detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
3440 [&](const char *b, const char *e) {
3443 req.path = detail::decode_url(std::string(b, e), false);
3447 detail::parse_query_text(std::string(b, e), req.params);
3456 if (count > 2) { return false; }
3462 bool Server::write_response(Stream &strm, bool close_connection,
3463 const Request &req, Response &res) {
3464 return write_response_core(strm, close_connection, req, res, false);
3467 bool Server::write_response_with_content(Stream &strm,
3468 bool close_connection,
3471 return write_response_core(strm, close_connection, req, res, true);
3474 bool Server::write_response_core(Stream &strm, bool close_connection,
3475 const Request &req, Response &res,
3476 bool need_apply_ranges) {
3477 assert(res.status != -1);
3479 if (400 <= res.status && error_handler_ &&
3480 error_handler_(req, res) == HandlerResponse::Handled) {
3481 need_apply_ranges = true;
3484 std::string content_type;
3485 std::string boundary;
3486 if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
3488 // Prepare additional headers
3489 if (close_connection || req.get_header_value("Connection") == "close") {
3490 res.set_header("Connection", "close");
3492 std::stringstream ss;
3493 ss << "timeout=" << keep_alive_timeout_sec_
3494 << ", max=" << keep_alive_max_count_;
3495 res.set_header("Keep-Alive", ss.str());
3498 if (!res.has_header("Content-Type") &&
3499 (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
3500 res.set_header("Content-Type", "text/plain");
3503 if (!res.has_header("Content-Length") && res.body.empty() &&
3504 !res.content_length_ && !res.content_provider_) {
3505 res.set_header("Content-Length", "0");
3508 if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
3509 res.set_header("Accept-Ranges", "bytes");
3512 if (post_routing_handler_) { post_routing_handler_(req, res); }
3514 // Response line and headers
3516 detail::BufferStream bstrm;
3518 if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
3519 detail::status_message(res.status))) {
3523 if (!detail::write_headers(bstrm, res.headers)) { return false; }
3526 auto &data = bstrm.get_buffer();
3527 detail::write_data(strm, data.data(), data.size());
3532 if (req.method != "HEAD") {
3533 if (!res.body.empty()) {
3534 if (!detail::write_data(strm, res.body.data(), res.body.size())) {
3537 } else if (res.content_provider_) {
3538 if (write_content_with_provider(strm, req, res, boundary, content_type)) {
3539 res.content_provider_success_ = true;
3541 res.content_provider_success_ = false;
3548 if (logger_) { logger_(req, res); }
3554 Server::write_content_with_provider(Stream &strm, const Request &req,
3555 Response &res, const std::string &boundary,
3556 const std::string &content_type) {
3557 auto is_shutting_down = [this]() {
3558 return this->svr_sock_ == INVALID_SOCKET;
3561 if (res.content_length_ > 0) {
3562 if (req.ranges.empty()) {
3563 return detail::write_content(strm, res.content_provider_, 0,
3564 res.content_length_, is_shutting_down);
3565 } else if (req.ranges.size() == 1) {
3567 detail::get_range_offset_and_length(req, res.content_length_, 0);
3568 auto offset = offsets.first;
3569 auto length = offsets.second;
3570 return detail::write_content(strm, res.content_provider_, offset, length,
3573 return detail::write_multipart_ranges_data(
3574 strm, req, res, boundary, content_type, is_shutting_down);
3577 if (res.is_chunked_content_provider_) {
3578 auto type = detail::encoding_type(req, res);
3580 std::unique_ptr<detail::compressor> compressor;
3581 if (type == detail::EncodingType::Gzip) {
3582 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
3583 compressor = detail::make_unique<detail::gzip_compressor>();
3585 } else if (type == detail::EncodingType::Brotli) {
3586 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
3587 compressor = detail::make_unique<detail::brotli_compressor>();
3590 compressor = detail::make_unique<detail::nocompressor>();
3592 assert(compressor != nullptr);
3594 return detail::write_content_chunked(strm, res.content_provider_,
3595 is_shutting_down, *compressor);
3597 return detail::write_content_without_length(strm, res.content_provider_,
3603 bool Server::read_content(Stream &strm, Request &req, Response &res) {
3604 MultipartFormDataMap::iterator cur;
3605 auto file_count = 0;
3606 if (read_content_core(
3609 [&](const char *buf, size_t n) {
3610 if (req.body.size() + n > req.body.max_size()) { return false; }
3611 req.body.append(buf, n);
3615 [&](const MultipartFormData &file) {
3616 if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {
3619 cur = req.files.emplace(file.name, file);
3622 [&](const char *buf, size_t n) {
3623 auto &content = cur->second.content;
3624 if (content.size() + n > content.max_size()) { return false; }
3625 content.append(buf, n);
3628 const auto &content_type = req.get_header_value("Content-Type");
3629 if (!content_type.find("application/x-www-form-urlencoded")) {
3630 if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
3631 res.status = 413; // NOTE: should be 414?
3634 detail::parse_query_text(req.body, req.params);
3641 bool Server::read_content_with_content_receiver(
3642 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
3643 MultipartContentHeader multipart_header,
3644 ContentReceiver multipart_receiver) {
3645 return read_content_core(strm, req, res, std::move(receiver),
3646 std::move(multipart_header),
3647 std::move(multipart_receiver));
3650 bool Server::read_content_core(Stream &strm, Request &req, Response &res,
3651 ContentReceiver receiver,
3652 MultipartContentHeader multipart_header,
3653 ContentReceiver multipart_receiver) {
3654 detail::MultipartFormDataParser multipart_form_data_parser;
3655 ContentReceiverWithProgress out;
3657 if (req.is_multipart_form_data()) {
3658 const auto &content_type = req.get_header_value("Content-Type");
3659 std::string boundary;
3660 if (!detail::parse_multipart_boundary(content_type, boundary)) {
3665 multipart_form_data_parser.set_boundary(std::move(boundary));
3666 out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
3670 auto read_size = (std::min)<size_t>(1, n - pos);
3671 auto ret = multipart_form_data_parser.parse(
3672 buf + pos, read_size, multipart_receiver, multipart_header);
3673 if (!ret) { return false; }
3678 return multipart_form_data_parser.parse(buf, n, multipart_receiver,
3682 out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
3683 uint64_t /*len*/) { return receiver(buf, n); };
3686 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
3690 if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
3695 if (req.is_multipart_form_data()) {
3696 if (!multipart_form_data_parser.is_valid()) {
3705 bool Server::handle_file_request(const Request &req, Response &res,
3707 for (const auto &entry : base_dirs_) {
3709 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
3710 std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
3711 if (detail::is_valid_path(sub_path)) {
3712 auto path = entry.base_dir + sub_path;
3713 if (path.back() == '/') { path += "index.html"; }
3715 if (detail::is_file(path)) {
3716 detail::read_file(path, res.body);
3718 detail::find_content_type(path, file_extension_and_mimetype_map_);
3719 if (type) { res.set_header("Content-Type", type); }
3720 for (const auto &kv : entry.headers) {
3721 res.set_header(kv.first.c_str(), kv.second);
3723 res.status = req.has_header("Range") ? 206 : 200;
3724 if (!head && file_request_handler_) {
3725 file_request_handler_(req, res);
3736 Server::create_server_socket(const std::string &host, int port,
3738 SocketOptions socket_options) const {
3739 return detail::create_socket(
3740 host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
3741 std::move(socket_options),
3742 [](socket_t sock, struct addrinfo &ai) -> bool {
3743 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
3746 if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
3751 int Server::bind_internal(const std::string &host, int port,
3753 if (!is_valid()) { return -1; }
3755 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
3756 if (svr_sock_ == INVALID_SOCKET) { return -1; }
3759 struct sockaddr_storage addr;
3760 socklen_t addr_len = sizeof(addr);
3761 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
3765 if (addr.ss_family == AF_INET) {
3766 return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
3767 } else if (addr.ss_family == AF_INET6) {
3768 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
3777 bool Server::listen_internal() {
3780 auto se = detail::scope_exit([&]() { is_running_ = false; });
3783 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
3785 while (svr_sock_ != INVALID_SOCKET) {
3787 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
3789 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
3790 idle_interval_usec_);
3791 if (val == 0) { // Timeout
3792 task_queue->on_idle();
3798 socket_t sock = accept(svr_sock_, nullptr, nullptr);
3800 if (sock == INVALID_SOCKET) {
3801 if (errno == EMFILE) {
3802 // The per-process limit of open file descriptors has been reached.
3803 // Try to accept new connections after a short sleep.
3804 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3806 } else if (errno == EINTR || errno == EAGAIN) {
3809 if (svr_sock_ != INVALID_SOCKET) {
3810 detail::close_socket(svr_sock_);
3813 ; // The server socket was closed by user.
3820 auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 +
3821 read_timeout_usec_ / 1000);
3822 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
3826 tv.tv_sec = static_cast<long>(read_timeout_sec_);
3827 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);
3828 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
3834 auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 +
3835 write_timeout_usec_ / 1000);
3836 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
3840 tv.tv_sec = static_cast<long>(write_timeout_sec_);
3841 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);
3842 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
3846 task_queue->enqueue([this, sock]() { process_and_close_socket(sock); });
3849 task_queue->shutdown();
3855 bool Server::routing(Request &req, Response &res, Stream &strm) {
3856 if (pre_routing_handler_ &&
3857 pre_routing_handler_(req, res) == HandlerResponse::Handled) {
3862 bool is_head_request = req.method == "HEAD";
3863 if ((req.method == "GET" || is_head_request) &&
3864 handle_file_request(req, res, is_head_request)) {
3868 if (detail::expect_content(req)) {
3869 // Content reader handler
3871 ContentReader reader(
3872 [&](ContentReceiver receiver) {
3873 return read_content_with_content_receiver(
3874 strm, req, res, std::move(receiver), nullptr, nullptr);
3876 [&](MultipartContentHeader header, ContentReceiver receiver) {
3877 return read_content_with_content_receiver(strm, req, res, nullptr,
3879 std::move(receiver));
3882 if (req.method == "POST") {
3883 if (dispatch_request_for_content_reader(
3884 req, res, std::move(reader),
3885 post_handlers_for_content_reader_)) {
3888 } else if (req.method == "PUT") {
3889 if (dispatch_request_for_content_reader(
3890 req, res, std::move(reader),
3891 put_handlers_for_content_reader_)) {
3894 } else if (req.method == "PATCH") {
3895 if (dispatch_request_for_content_reader(
3896 req, res, std::move(reader),
3897 patch_handlers_for_content_reader_)) {
3900 } else if (req.method == "DELETE") {
3901 if (dispatch_request_for_content_reader(
3902 req, res, std::move(reader),
3903 delete_handlers_for_content_reader_)) {
3909 // Read content into `req.body`
3910 if (!read_content(strm, req, res)) { return false; }
3914 if (req.method == "GET" || req.method == "HEAD") {
3915 return dispatch_request(req, res, get_handlers_);
3916 } else if (req.method == "POST") {
3917 return dispatch_request(req, res, post_handlers_);
3918 } else if (req.method == "PUT") {
3919 return dispatch_request(req, res, put_handlers_);
3920 } else if (req.method == "DELETE") {
3921 return dispatch_request(req, res, delete_handlers_);
3922 } else if (req.method == "OPTIONS") {
3923 return dispatch_request(req, res, options_handlers_);
3924 } else if (req.method == "PATCH") {
3925 return dispatch_request(req, res, patch_handlers_);
3932 bool Server::dispatch_request(Request &req, Response &res,
3933 const Handlers &handlers) {
3934 for (const auto &x : handlers) {
3935 const auto &pattern = x.first;
3936 const auto &handler = x.second;
3938 if (std::regex_match(req.path, req.matches, pattern)) {
3946 void Server::apply_ranges(const Request &req, Response &res,
3947 std::string &content_type,
3948 std::string &boundary) {
3949 if (req.ranges.size() > 1) {
3950 boundary = detail::make_multipart_data_boundary();
3952 auto it = res.headers.find("Content-Type");
3953 if (it != res.headers.end()) {
3954 content_type = it->second;
3955 res.headers.erase(it);
3958 res.set_header("Content-Type",
3959 "multipart/byteranges; boundary=" + boundary);
3962 auto type = detail::encoding_type(req, res);
3964 if (res.body.empty()) {
3965 if (res.content_length_ > 0) {
3967 if (req.ranges.empty()) {
3968 length = res.content_length_;
3969 } else if (req.ranges.size() == 1) {
3971 detail::get_range_offset_and_length(req, res.content_length_, 0);
3972 auto offset = offsets.first;
3973 length = offsets.second;
3974 auto content_range = detail::make_content_range_header_field(
3975 offset, length, res.content_length_);
3976 res.set_header("Content-Range", content_range);
3978 length = detail::get_multipart_ranges_data_length(req, res, boundary,
3981 res.set_header("Content-Length", std::to_string(length));
3983 if (res.content_provider_) {
3984 if (res.is_chunked_content_provider_) {
3985 res.set_header("Transfer-Encoding", "chunked");
3986 if (type == detail::EncodingType::Gzip) {
3987 res.set_header("Content-Encoding", "gzip");
3988 } else if (type == detail::EncodingType::Brotli) {
3989 res.set_header("Content-Encoding", "br");
3995 if (req.ranges.empty()) {
3997 } else if (req.ranges.size() == 1) {
3999 detail::get_range_offset_and_length(req, res.body.size(), 0);
4000 auto offset = offsets.first;
4001 auto length = offsets.second;
4002 auto content_range = detail::make_content_range_header_field(
4003 offset, length, res.body.size());
4004 res.set_header("Content-Range", content_range);
4005 if (offset < res.body.size()) {
4006 res.body = res.body.substr(offset, length);
4013 if (detail::make_multipart_ranges_data(req, res, boundary, content_type,
4015 res.body.swap(data);
4022 if (type != detail::EncodingType::None) {
4023 std::unique_ptr<detail::compressor> compressor;
4024 std::string content_encoding;
4026 if (type == detail::EncodingType::Gzip) {
4027 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4028 compressor = detail::make_unique<detail::gzip_compressor>();
4029 content_encoding = "gzip";
4031 } else if (type == detail::EncodingType::Brotli) {
4032 #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4033 compressor = detail::make_unique<detail::brotli_compressor>();
4034 content_encoding = "br";
4039 std::string compressed;
4040 if (compressor->compress(res.body.data(), res.body.size(), true,
4041 [&](const char *data, size_t data_len) {
4042 compressed.append(data, data_len);
4045 res.body.swap(compressed);
4046 res.set_header("Content-Encoding", content_encoding);
4051 auto length = std::to_string(res.body.size());
4052 res.set_header("Content-Length", length);
4056 bool Server::dispatch_request_for_content_reader(
4057 Request &req, Response &res, ContentReader content_reader,
4058 const HandlersForContentReader &handlers) {
4059 for (const auto &x : handlers) {
4060 const auto &pattern = x.first;
4061 const auto &handler = x.second;
4063 if (std::regex_match(req.path, req.matches, pattern)) {
4064 handler(req, res, content_reader);
4072 Server::process_request(Stream &strm, bool close_connection,
4073 bool &connection_closed,
4074 const std::function<void(Request &)> &setup_request) {
4075 std::array<char, 2048> buf{};
4077 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
4079 // Connection has been closed on client
4080 if (!line_reader.getline()) { return false; }
4085 res.version = "HTTP/1.1";
4087 for (const auto &header : default_headers_) {
4088 if (res.headers.find(header.first) == res.headers.end()) {
4089 res.headers.insert(header);
4094 // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).
4096 #ifndef CPPHTTPLIB_USE_POLL
4097 // Socket file descriptor exceeded FD_SETSIZE...
4098 if (strm.socket() >= FD_SETSIZE) {
4100 detail::read_headers(strm, dummy);
4102 return write_response(strm, close_connection, req, res);
4107 // Check if the request URI doesn't exceed the limit
4108 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
4110 detail::read_headers(strm, dummy);
4112 return write_response(strm, close_connection, req, res);
4115 // Request line and headers
4116 if (!parse_request_line(line_reader.ptr(), req) ||
4117 !detail::read_headers(strm, req.headers)) {
4119 return write_response(strm, close_connection, req, res);
4122 if (req.get_header_value("Connection") == "close") {
4123 connection_closed = true;
4126 if (req.version == "HTTP/1.0" &&
4127 req.get_header_value("Connection") != "Keep-Alive") {
4128 connection_closed = true;
4131 strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
4132 req.set_header("REMOTE_ADDR", req.remote_addr);
4133 req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
4135 strm.get_local_ip_and_port(req.local_addr, req.local_port);
4136 req.set_header("LOCAL_ADDR", req.local_addr);
4137 req.set_header("LOCAL_PORT", std::to_string(req.local_port));
4139 if (req.has_header("Range")) {
4140 const auto &range_header_value = req.get_header_value("Range");
4141 if (!detail::parse_range_header(range_header_value, req.ranges)) {
4143 return write_response(strm, close_connection, req, res);
4147 if (setup_request) { setup_request(req); }
4149 if (req.get_header_value("Expect") == "100-continue") {
4151 if (expect_100_continue_handler_) {
4152 status = expect_100_continue_handler_(req, res);
4157 strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
4158 detail::status_message(status));
4160 default: return write_response(strm, close_connection, req, res);
4165 bool routed = false;
4166 #ifdef CPPHTTPLIB_NO_EXCEPTIONS
4167 routed = routing(req, res, strm);
4170 routed = routing(req, res, strm);
4171 } catch (std::exception &e) {
4172 if (exception_handler_) {
4173 auto ep = std::current_exception();
4174 exception_handler_(req, res, ep);
4180 for (size_t i = 0; s[i]; i++) {
4182 case '\r': val += "\\r"; break;
4183 case '\n': val += "\\n"; break;
4184 default: val += s[i]; break;
4187 res.set_header("EXCEPTION_WHAT", val);
4190 if (exception_handler_) {
4191 auto ep = std::current_exception();
4192 exception_handler_(req, res, ep);
4196 res.set_header("EXCEPTION_WHAT", "UNKNOWN");
4202 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
4203 return write_response_with_content(strm, close_connection, req, res);
4205 if (res.status == -1) { res.status = 404; }
4206 return write_response(strm, close_connection, req, res);
4210 bool Server::is_valid() const { return true; }
4212 bool Server::process_and_close_socket(socket_t sock) {
4213 auto ret = detail::process_server_socket(
4214 svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
4215 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
4216 write_timeout_usec_,
4217 [this](Stream &strm, bool close_connection, bool &connection_closed) {
4218 return process_request(strm, close_connection, connection_closed,
4222 detail::shutdown_socket(sock);
4223 detail::close_socket(sock);
4227 // HTTP client implementation
4228 ClientImpl::ClientImpl(const std::string &host)
4229 : ClientImpl(host, 80, std::string(), std::string()) {}
4231 ClientImpl::ClientImpl(const std::string &host, int port)
4232 : ClientImpl(host, port, std::string(), std::string()) {}
4234 ClientImpl::ClientImpl(const std::string &host, int port,
4235 const std::string &client_cert_path,
4236 const std::string &client_key_path)
4237 : host_(host), port_(port),
4238 host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)),
4239 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
4241 ClientImpl::~ClientImpl() {
4242 std::lock_guard<std::mutex> guard(socket_mutex_);
4243 shutdown_socket(socket_);
4244 close_socket(socket_);
4247 bool ClientImpl::is_valid() const { return true; }
4249 void ClientImpl::copy_settings(const ClientImpl &rhs) {
4250 client_cert_path_ = rhs.client_cert_path_;
4251 client_key_path_ = rhs.client_key_path_;
4252 connection_timeout_sec_ = rhs.connection_timeout_sec_;
4253 read_timeout_sec_ = rhs.read_timeout_sec_;
4254 read_timeout_usec_ = rhs.read_timeout_usec_;
4255 write_timeout_sec_ = rhs.write_timeout_sec_;
4256 write_timeout_usec_ = rhs.write_timeout_usec_;
4257 basic_auth_username_ = rhs.basic_auth_username_;
4258 basic_auth_password_ = rhs.basic_auth_password_;
4259 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
4260 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4261 digest_auth_username_ = rhs.digest_auth_username_;
4262 digest_auth_password_ = rhs.digest_auth_password_;
4264 keep_alive_ = rhs.keep_alive_;
4265 follow_location_ = rhs.follow_location_;
4266 url_encode_ = rhs.url_encode_;
4267 address_family_ = rhs.address_family_;
4268 tcp_nodelay_ = rhs.tcp_nodelay_;
4269 socket_options_ = rhs.socket_options_;
4270 compress_ = rhs.compress_;
4271 decompress_ = rhs.decompress_;
4272 interface_ = rhs.interface_;
4273 proxy_host_ = rhs.proxy_host_;
4274 proxy_port_ = rhs.proxy_port_;
4275 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
4276 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
4277 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
4278 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4279 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
4280 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
4282 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4283 ca_cert_file_path_ = rhs.ca_cert_file_path_;
4284 ca_cert_dir_path_ = rhs.ca_cert_dir_path_;
4285 ca_cert_store_ = rhs.ca_cert_store_;
4287 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4288 server_certificate_verification_ = rhs.server_certificate_verification_;
4290 logger_ = rhs.logger_;
4293 socket_t ClientImpl::create_client_socket(Error &error) const {
4294 if (!proxy_host_.empty() && proxy_port_ != -1) {
4295 return detail::create_client_socket(
4296 proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
4297 socket_options_, connection_timeout_sec_, connection_timeout_usec_,
4298 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
4299 write_timeout_usec_, interface_, error);
4302 // Check is custom IP specified for host_
4304 auto it = addr_map_.find(host_);
4305 if (it != addr_map_.end()) ip = it->second;
4307 return detail::create_client_socket(
4308 host_, ip, port_, address_family_, tcp_nodelay_, socket_options_,
4309 connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
4310 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_,
4314 bool ClientImpl::create_and_connect_socket(Socket &socket,
4316 auto sock = create_client_socket(error);
4317 if (sock == INVALID_SOCKET) { return false; }
4322 void ClientImpl::shutdown_ssl(Socket & /*socket*/,
4323 bool /*shutdown_gracefully*/) {
4324 // If there are any requests in flight from threads other than us, then it's
4325 // a thread-unsafe race because individual ssl* objects are not thread-safe.
4326 assert(socket_requests_in_flight_ == 0 ||
4327 socket_requests_are_from_thread_ == std::this_thread::get_id());
4330 void ClientImpl::shutdown_socket(Socket &socket) {
4331 if (socket.sock == INVALID_SOCKET) { return; }
4332 detail::shutdown_socket(socket.sock);
4335 void ClientImpl::close_socket(Socket &socket) {
4336 // If there are requests in flight in another thread, usually closing
4337 // the socket will be fine and they will simply receive an error when
4338 // using the closed socket, but it is still a bug since rarely the OS
4339 // may reassign the socket id to be used for a new socket, and then
4340 // suddenly they will be operating on a live socket that is different
4341 // than the one they intended!
4342 assert(socket_requests_in_flight_ == 0 ||
4343 socket_requests_are_from_thread_ == std::this_thread::get_id());
4345 // It is also a bug if this happens while SSL is still active
4346 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4347 assert(socket.ssl == nullptr);
4349 if (socket.sock == INVALID_SOCKET) { return; }
4350 detail::close_socket(socket.sock);
4351 socket.sock = INVALID_SOCKET;
4354 bool ClientImpl::read_response_line(Stream &strm, const Request &req,
4356 std::array<char, 2048> buf{};
4358 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
4360 if (!line_reader.getline()) { return false; }
4362 #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
4363 const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
4365 const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
4369 if (!std::regex_match(line_reader.ptr(), m, re)) {
4370 return req.method == "CONNECT";
4372 res.version = std::string(m[1]);
4373 res.status = std::stoi(std::string(m[2]));
4374 res.reason = std::string(m[3]);
4376 // Ignore '100 Continue'
4377 while (res.status == 100) {
4378 if (!line_reader.getline()) { return false; } // CRLF
4379 if (!line_reader.getline()) { return false; } // next response line
4381 if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
4382 res.version = std::string(m[1]);
4383 res.status = std::stoi(std::string(m[2]));
4384 res.reason = std::string(m[3]);
4390 bool ClientImpl::send(Request &req, Response &res, Error &error) {
4391 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
4392 auto ret = send_(req, res, error);
4393 if (error == Error::SSLPeerCouldBeClosed_) {
4395 ret = send_(req, res, error);
4400 bool ClientImpl::send_(Request &req, Response &res, Error &error) {
4402 std::lock_guard<std::mutex> guard(socket_mutex_);
4404 // Set this to false immediately - if it ever gets set to true by the end of
4405 // the request, we know another thread instructed us to close the socket.
4406 socket_should_be_closed_when_request_is_done_ = false;
4408 auto is_alive = false;
4409 if (socket_.is_open()) {
4410 is_alive = detail::is_socket_alive(socket_.sock);
4412 // Attempt to avoid sigpipe by shutting down nongracefully if it seems
4413 // like the other side has already closed the connection Also, there
4414 // cannot be any requests in flight from other threads since we locked
4415 // request_mutex_, so safe to close everything immediately
4416 const bool shutdown_gracefully = false;
4417 shutdown_ssl(socket_, shutdown_gracefully);
4418 shutdown_socket(socket_);
4419 close_socket(socket_);
4424 if (!create_and_connect_socket(socket_, error)) { return false; }
4426 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4427 // TODO: refactoring
4429 auto &scli = static_cast<SSLClient &>(*this);
4430 if (!proxy_host_.empty() && proxy_port_ != -1) {
4431 auto success = false;
4432 if (!scli.connect_with_proxy(socket_, res, success, error)) {
4437 if (!scli.initialize_ssl(socket_, error)) { return false; }
4442 // Mark the current socket as being in use so that it cannot be closed by
4443 // anyone else while this request is ongoing, even though we will be
4444 // releasing the mutex.
4445 if (socket_requests_in_flight_ > 1) {
4446 assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
4448 socket_requests_in_flight_ += 1;
4449 socket_requests_are_from_thread_ = std::this_thread::get_id();
4452 for (const auto &header : default_headers_) {
4453 if (req.headers.find(header.first) == req.headers.end()) {
4454 req.headers.insert(header);
4459 auto close_connection = !keep_alive_;
4461 auto se = detail::scope_exit([&]() {
4462 // Briefly lock mutex in order to mark that a request is no longer ongoing
4463 std::lock_guard<std::mutex> guard(socket_mutex_);
4464 socket_requests_in_flight_ -= 1;
4465 if (socket_requests_in_flight_ <= 0) {
4466 assert(socket_requests_in_flight_ == 0);
4467 socket_requests_are_from_thread_ = std::thread::id();
4470 if (socket_should_be_closed_when_request_is_done_ || close_connection ||
4472 shutdown_ssl(socket_, true);
4473 shutdown_socket(socket_);
4474 close_socket(socket_);
4478 ret = process_socket(socket_, [&](Stream &strm) {
4479 return handle_request(strm, req, res, close_connection, error);
4483 if (error == Error::Success) { error = Error::Unknown; }
4489 Result ClientImpl::send(const Request &req) {
4491 return send_(std::move(req2));
4494 Result ClientImpl::send_(Request &&req) {
4495 auto res = detail::make_unique<Response>();
4496 auto error = Error::Success;
4497 auto ret = send(req, *res, error);
4498 return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
4501 bool ClientImpl::handle_request(Stream &strm, Request &req,
4502 Response &res, bool close_connection,
4504 if (req.path.empty()) {
4505 error = Error::Connection;
4509 auto req_save = req;
4513 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
4515 req2.path = "http://" + host_and_port_ + req.path;
4516 ret = process_request(strm, req2, res, close_connection, error);
4518 req.path = req_save.path;
4520 ret = process_request(strm, req, res, close_connection, error);
4523 if (!ret) { return false; }
4525 if (300 < res.status && res.status < 400 && follow_location_) {
4527 ret = redirect(req, res, error);
4530 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4531 if ((res.status == 401 || res.status == 407) &&
4532 req.authorization_count_ < 5) {
4533 auto is_proxy = res.status == 407;
4534 const auto &username =
4535 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
4536 const auto &password =
4537 is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
4539 if (!username.empty() && !password.empty()) {
4540 std::map<std::string, std::string> auth;
4541 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
4542 Request new_req = req;
4543 new_req.authorization_count_ += 1;
4544 new_req.headers.erase(is_proxy ? "Proxy-Authorization"
4546 new_req.headers.insert(detail::make_digest_authentication_header(
4547 req, auth, new_req.authorization_count_, detail::random_string(10),
4548 username, password, is_proxy));
4552 ret = send(new_req, new_res, error);
4553 if (ret) { res = new_res; }
4562 bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
4563 if (req.redirect_count_ == 0) {
4564 error = Error::ExceedRedirectCount;
4568 auto location = res.get_header_value("location");
4569 if (location.empty()) { return false; }
4571 const static std::regex re(
4572 R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
4575 if (!std::regex_match(location, m, re)) { return false; }
4577 auto scheme = is_ssl() ? "https" : "http";
4579 auto next_scheme = m[1].str();
4580 auto next_host = m[2].str();
4581 if (next_host.empty()) { next_host = m[3].str(); }
4582 auto port_str = m[4].str();
4583 auto next_path = m[5].str();
4584 auto next_query = m[6].str();
4586 auto next_port = port_;
4587 if (!port_str.empty()) {
4588 next_port = std::stoi(port_str);
4589 } else if (!next_scheme.empty()) {
4590 next_port = next_scheme == "https" ? 443 : 80;
4593 if (next_scheme.empty()) { next_scheme = scheme; }
4594 if (next_host.empty()) { next_host = host_; }
4595 if (next_path.empty()) { next_path = "/"; }
4597 auto path = detail::decode_url(next_path, true) + next_query;
4599 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
4600 return detail::redirect(*this, req, res, path, location, error);
4602 if (next_scheme == "https") {
4603 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4604 SSLClient cli(next_host.c_str(), next_port);
4605 cli.copy_settings(*this);
4606 if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
4607 return detail::redirect(cli, req, res, path, location, error);
4612 ClientImpl cli(next_host.c_str(), next_port);
4613 cli.copy_settings(*this);
4614 return detail::redirect(cli, req, res, path, location, error);
4619 bool ClientImpl::write_content_with_provider(Stream &strm,
4622 auto is_shutting_down = []() { return false; };
4624 if (req.is_chunked_content_provider_) {
4625 // TODO: Brotli support
4626 std::unique_ptr<detail::compressor> compressor;
4627 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4629 compressor = detail::make_unique<detail::gzip_compressor>();
4633 compressor = detail::make_unique<detail::nocompressor>();
4636 return detail::write_content_chunked(strm, req.content_provider_,
4637 is_shutting_down, *compressor, error);
4639 return detail::write_content(strm, req.content_provider_, 0,
4640 req.content_length_, is_shutting_down, error);
4644 bool ClientImpl::write_request(Stream &strm, Request &req,
4645 bool close_connection, Error &error) {
4646 // Prepare additional headers
4647 if (close_connection) {
4648 if (!req.has_header("Connection")) {
4649 req.set_header("Connection", "close");
4653 if (!req.has_header("Host")) {
4656 req.set_header("Host", host_);
4658 req.set_header("Host", host_and_port_);
4662 req.set_header("Host", host_);
4664 req.set_header("Host", host_and_port_);
4669 if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }
4671 #ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
4672 if (!req.has_header("User-Agent")) {
4673 auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
4674 req.set_header("User-Agent", agent);
4678 if (req.body.empty()) {
4679 if (req.content_provider_) {
4680 if (!req.is_chunked_content_provider_) {
4681 if (!req.has_header("Content-Length")) {
4682 auto length = std::to_string(req.content_length_);
4683 req.set_header("Content-Length", length);
4687 if (req.method == "POST" || req.method == "PUT" ||
4688 req.method == "PATCH") {
4689 req.set_header("Content-Length", "0");
4693 if (!req.has_header("Content-Type")) {
4694 req.set_header("Content-Type", "text/plain");
4697 if (!req.has_header("Content-Length")) {
4698 auto length = std::to_string(req.body.size());
4699 req.set_header("Content-Length", length);
4703 if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {
4704 if (!req.has_header("Authorization")) {
4705 req.headers.insert(make_basic_authentication_header(
4706 basic_auth_username_, basic_auth_password_, false));
4710 if (!proxy_basic_auth_username_.empty() &&
4711 !proxy_basic_auth_password_.empty()) {
4712 if (!req.has_header("Proxy-Authorization")) {
4713 req.headers.insert(make_basic_authentication_header(
4714 proxy_basic_auth_username_, proxy_basic_auth_password_, true));
4718 if (!bearer_token_auth_token_.empty()) {
4719 if (!req.has_header("Authorization")) {
4720 req.headers.insert(make_bearer_token_authentication_header(
4721 bearer_token_auth_token_, false));
4725 if (!proxy_bearer_token_auth_token_.empty()) {
4726 if (!req.has_header("Proxy-Authorization")) {
4727 req.headers.insert(make_bearer_token_authentication_header(
4728 proxy_bearer_token_auth_token_, true));
4732 // Request line and headers
4734 detail::BufferStream bstrm;
4736 const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;
4737 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
4739 detail::write_headers(bstrm, req.headers);
4742 auto &data = bstrm.get_buffer();
4743 if (!detail::write_data(strm, data.data(), data.size())) {
4744 error = Error::Write;
4750 if (req.body.empty()) {
4751 return write_content_with_provider(strm, req, error);
4754 if (!detail::write_data(strm, req.body.data(), req.body.size())) {
4755 error = Error::Write;
4762 std::unique_ptr<Response> ClientImpl::send_with_content_provider(
4763 Request &req, const char *body, size_t content_length,
4764 ContentProvider content_provider,
4765 ContentProviderWithoutLength content_provider_without_length,
4766 const std::string &content_type, Error &error) {
4767 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
4769 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4770 if (compress_) { req.set_header("Content-Encoding", "gzip"); }
4773 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4774 if (compress_ && !content_provider_without_length) {
4775 // TODO: Brotli support
4776 detail::gzip_compressor compressor;
4778 if (content_provider) {
4783 data_sink.write = [&](const char *data, size_t data_len) -> bool {
4785 auto last = offset + data_len == content_length;
4787 auto ret = compressor.compress(
4788 data, data_len, last,
4789 [&](const char *compressed_data, size_t compressed_data_len) {
4790 req.body.append(compressed_data, compressed_data_len);
4803 while (ok && offset < content_length) {
4804 if (!content_provider(offset, content_length - offset, data_sink)) {
4805 error = Error::Canceled;
4810 if (!compressor.compress(body, content_length, true,
4811 [&](const char *data, size_t data_len) {
4812 req.body.append(data, data_len);
4815 error = Error::Compression;
4822 if (content_provider) {
4823 req.content_length_ = content_length;
4824 req.content_provider_ = std::move(content_provider);
4825 req.is_chunked_content_provider_ = false;
4826 } else if (content_provider_without_length) {
4827 req.content_length_ = 0;
4828 req.content_provider_ = detail::ContentProviderAdapter(
4829 std::move(content_provider_without_length));
4830 req.is_chunked_content_provider_ = true;
4831 req.set_header("Transfer-Encoding", "chunked");
4833 req.body.assign(body, content_length);
4837 auto res = detail::make_unique<Response>();
4838 return send(req, *res, error) ? std::move(res) : nullptr;
4841 Result ClientImpl::send_with_content_provider(
4842 const std::string &method, const std::string &path, const Headers &headers,
4843 const char *body, size_t content_length, ContentProvider content_provider,
4844 ContentProviderWithoutLength content_provider_without_length,
4845 const std::string &content_type) {
4847 req.method = method;
4848 req.headers = headers;
4851 auto error = Error::Success;
4853 auto res = send_with_content_provider(
4854 req, body, content_length, std::move(content_provider),
4855 std::move(content_provider_without_length), content_type, error);
4857 return Result{std::move(res), error, std::move(req.headers)};
4861 ClientImpl::adjust_host_string(const std::string &host) const {
4862 if (host.find(':') != std::string::npos) { return "[" + host + "]"; }
4866 bool ClientImpl::process_request(Stream &strm, Request &req,
4867 Response &res, bool close_connection,
4870 if (!write_request(strm, req, close_connection, error)) { return false; }
4872 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4874 auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
4875 if (!is_proxy_enabled) {
4877 if (SSL_peek(socket_.ssl, buf, 1) == 0 &&
4878 SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {
4879 error = Error::SSLPeerCouldBeClosed_;
4886 // Receive response and headers
4887 if (!read_response_line(strm, req, res) ||
4888 !detail::read_headers(strm, res.headers)) {
4889 error = Error::Read;
4894 if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") {
4895 auto redirect = 300 < res.status && res.status < 400 && follow_location_;
4897 if (req.response_handler && !redirect) {
4898 if (!req.response_handler(res)) {
4899 error = Error::Canceled;
4905 req.content_receiver
4906 ? static_cast<ContentReceiverWithProgress>(
4907 [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
4908 if (redirect) { return true; }
4909 auto ret = req.content_receiver(buf, n, off, len);
4910 if (!ret) { error = Error::Canceled; }
4913 : static_cast<ContentReceiverWithProgress>(
4914 [&](const char *buf, size_t n, uint64_t /*off*/,
4916 if (res.body.size() + n > res.body.max_size()) {
4919 res.body.append(buf, n);
4923 auto progress = [&](uint64_t current, uint64_t total) {
4924 if (!req.progress || redirect) { return true; }
4925 auto ret = req.progress(current, total);
4926 if (!ret) { error = Error::Canceled; }
4931 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
4932 dummy_status, std::move(progress), std::move(out),
4934 if (error != Error::Canceled) { error = Error::Read; }
4939 if (res.get_header_value("Connection") == "close" ||
4940 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
4941 // TODO this requires a not-entirely-obvious chain of calls to be correct
4942 // for this to be safe. Maybe a code refactor (such as moving this out to
4943 // the send function and getting rid of the recursiveness of the mutex)
4944 // could make this more obvious.
4946 // This is safe to call because process_request is only called by
4947 // handle_request which is only called by send, which locks the request
4948 // mutex during the process. It would be a bug to call it from a different
4949 // thread since it's a thread-safety issue to do these things to the socket
4950 // if another thread is using the socket.
4951 std::lock_guard<std::mutex> guard(socket_mutex_);
4952 shutdown_ssl(socket_, true);
4953 shutdown_socket(socket_);
4954 close_socket(socket_);
4958 if (logger_) { logger_(req, res); }
4963 ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
4964 const std::string &boundary, const MultipartFormDataItems &items,
4965 const MultipartFormDataProviderItems &provider_items) {
4966 size_t cur_item = 0, cur_start = 0;
4967 // cur_item and cur_start are copied to within the std::function and maintain
4968 // state between successive calls
4969 return [&, cur_item, cur_start](size_t offset,
4970 DataSink &sink) mutable -> bool {
4971 if (!offset && items.size()) {
4972 sink.os << detail::serialize_multipart_formdata(items, boundary, false);
4974 } else if (cur_item < provider_items.size()) {
4976 const auto &begin = detail::serialize_multipart_formdata_item_begin(
4977 provider_items[cur_item], boundary);
4978 offset += begin.size();
4984 bool has_data = true;
4985 cur_sink.write = sink.write;
4986 cur_sink.done = [&]() { has_data = false; };
4988 if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
4992 sink.os << detail::serialize_multipart_formdata_item_end();
4998 sink.os << detail::serialize_multipart_formdata_finish(boundary);
5006 ClientImpl::process_socket(const Socket &socket,
5007 std::function<bool(Stream &strm)> callback) {
5008 return detail::process_client_socket(
5009 socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5010 write_timeout_usec_, std::move(callback));
5013 bool ClientImpl::is_ssl() const { return false; }
5015 Result ClientImpl::Get(const std::string &path) {
5016 return Get(path, Headers(), Progress());
5019 Result ClientImpl::Get(const std::string &path, Progress progress) {
5020 return Get(path, Headers(), std::move(progress));
5023 Result ClientImpl::Get(const std::string &path, const Headers &headers) {
5024 return Get(path, headers, Progress());
5027 Result ClientImpl::Get(const std::string &path, const Headers &headers,
5028 Progress progress) {
5032 req.headers = headers;
5033 req.progress = std::move(progress);
5035 return send_(std::move(req));
5038 Result ClientImpl::Get(const std::string &path,
5039 ContentReceiver content_receiver) {
5040 return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
5043 Result ClientImpl::Get(const std::string &path,
5044 ContentReceiver content_receiver,
5045 Progress progress) {
5046 return Get(path, Headers(), nullptr, std::move(content_receiver),
5047 std::move(progress));
5050 Result ClientImpl::Get(const std::string &path, const Headers &headers,
5051 ContentReceiver content_receiver) {
5052 return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
5055 Result ClientImpl::Get(const std::string &path, const Headers &headers,
5056 ContentReceiver content_receiver,
5057 Progress progress) {
5058 return Get(path, headers, nullptr, std::move(content_receiver),
5059 std::move(progress));
5062 Result ClientImpl::Get(const std::string &path,
5063 ResponseHandler response_handler,
5064 ContentReceiver content_receiver) {
5065 return Get(path, Headers(), std::move(response_handler),
5066 std::move(content_receiver), nullptr);
5069 Result ClientImpl::Get(const std::string &path, const Headers &headers,
5070 ResponseHandler response_handler,
5071 ContentReceiver content_receiver) {
5072 return Get(path, headers, std::move(response_handler),
5073 std::move(content_receiver), nullptr);
5076 Result ClientImpl::Get(const std::string &path,
5077 ResponseHandler response_handler,
5078 ContentReceiver content_receiver,
5079 Progress progress) {
5080 return Get(path, Headers(), std::move(response_handler),
5081 std::move(content_receiver), std::move(progress));
5084 Result ClientImpl::Get(const std::string &path, const Headers &headers,
5085 ResponseHandler response_handler,
5086 ContentReceiver content_receiver,
5087 Progress progress) {
5091 req.headers = headers;
5092 req.response_handler = std::move(response_handler);
5093 req.content_receiver =
5094 [content_receiver](const char *data, size_t data_length,
5095 uint64_t /*offset*/, uint64_t /*total_length*/) {
5096 return content_receiver(data, data_length);
5098 req.progress = std::move(progress);
5100 return send_(std::move(req));
5103 Result ClientImpl::Get(const std::string &path, const Params ¶ms,
5104 const Headers &headers, Progress progress) {
5105 if (params.empty()) { return Get(path, headers); }
5107 std::string path_with_query = append_query_params(path, params);
5108 return Get(path_with_query.c_str(), headers, progress);
5111 Result ClientImpl::Get(const std::string &path, const Params ¶ms,
5112 const Headers &headers,
5113 ContentReceiver content_receiver,
5114 Progress progress) {
5115 return Get(path, params, headers, nullptr, content_receiver, progress);
5118 Result ClientImpl::Get(const std::string &path, const Params ¶ms,
5119 const Headers &headers,
5120 ResponseHandler response_handler,
5121 ContentReceiver content_receiver,
5122 Progress progress) {
5123 if (params.empty()) {
5124 return Get(path, headers, response_handler, content_receiver, progress);
5127 std::string path_with_query = append_query_params(path, params);
5128 return Get(path_with_query.c_str(), headers, response_handler,
5129 content_receiver, progress);
5132 Result ClientImpl::Head(const std::string &path) {
5133 return Head(path, Headers());
5136 Result ClientImpl::Head(const std::string &path,
5137 const Headers &headers) {
5139 req.method = "HEAD";
5140 req.headers = headers;
5143 return send_(std::move(req));
5146 Result ClientImpl::Post(const std::string &path) {
5147 return Post(path, std::string(), std::string());
5150 Result ClientImpl::Post(const std::string &path,
5151 const Headers &headers) {
5152 return Post(path, headers, nullptr, 0, std::string());
5155 Result ClientImpl::Post(const std::string &path, const char *body,
5156 size_t content_length,
5157 const std::string &content_type) {
5158 return Post(path, Headers(), body, content_length, content_type);
5161 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5162 const char *body, size_t content_length,
5163 const std::string &content_type) {
5164 return send_with_content_provider("POST", path, headers, body, content_length,
5165 nullptr, nullptr, content_type);
5168 Result ClientImpl::Post(const std::string &path, const std::string &body,
5169 const std::string &content_type) {
5170 return Post(path, Headers(), body, content_type);
5173 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5174 const std::string &body,
5175 const std::string &content_type) {
5176 return send_with_content_provider("POST", path, headers, body.data(),
5177 body.size(), nullptr, nullptr,
5181 Result ClientImpl::Post(const std::string &path, const Params ¶ms) {
5182 return Post(path, Headers(), params);
5185 Result ClientImpl::Post(const std::string &path, size_t content_length,
5186 ContentProvider content_provider,
5187 const std::string &content_type) {
5188 return Post(path, Headers(), content_length, std::move(content_provider),
5192 Result ClientImpl::Post(const std::string &path,
5193 ContentProviderWithoutLength content_provider,
5194 const std::string &content_type) {
5195 return Post(path, Headers(), std::move(content_provider), content_type);
5198 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5199 size_t content_length,
5200 ContentProvider content_provider,
5201 const std::string &content_type) {
5202 return send_with_content_provider("POST", path, headers, nullptr,
5203 content_length, std::move(content_provider),
5204 nullptr, content_type);
5207 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5208 ContentProviderWithoutLength content_provider,
5209 const std::string &content_type) {
5210 return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
5211 std::move(content_provider), content_type);
5214 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5215 const Params ¶ms) {
5216 auto query = detail::params_to_query_str(params);
5217 return Post(path, headers, query, "application/x-www-form-urlencoded");
5220 Result ClientImpl::Post(const std::string &path,
5221 const MultipartFormDataItems &items) {
5222 return Post(path, Headers(), items);
5225 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5226 const MultipartFormDataItems &items) {
5227 const auto &boundary = detail::make_multipart_data_boundary();
5228 const auto &content_type =
5229 detail::serialize_multipart_formdata_get_content_type(boundary);
5230 const auto &body = detail::serialize_multipart_formdata(items, boundary);
5231 return Post(path, headers, body, content_type.c_str());
5234 Result ClientImpl::Post(const std::string &path, const Headers &headers,
5235 const MultipartFormDataItems &items,
5236 const std::string &boundary) {
5237 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
5238 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
5241 const auto &content_type =
5242 detail::serialize_multipart_formdata_get_content_type(boundary);
5243 const auto &body = detail::serialize_multipart_formdata(items, boundary);
5244 return Post(path, headers, body, content_type.c_str());
5248 ClientImpl::Post(const std::string &path, const Headers &headers,
5249 const MultipartFormDataItems &items,
5250 const MultipartFormDataProviderItems &provider_items) {
5251 const auto &boundary = detail::make_multipart_data_boundary();
5252 const auto &content_type =
5253 detail::serialize_multipart_formdata_get_content_type(boundary);
5254 return send_with_content_provider(
5255 "POST", path, headers, nullptr, 0, nullptr,
5256 get_multipart_content_provider(boundary, items, provider_items),
5260 Result ClientImpl::Put(const std::string &path) {
5261 return Put(path, std::string(), std::string());
5264 Result ClientImpl::Put(const std::string &path, const char *body,
5265 size_t content_length,
5266 const std::string &content_type) {
5267 return Put(path, Headers(), body, content_length, content_type);
5270 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5271 const char *body, size_t content_length,
5272 const std::string &content_type) {
5273 return send_with_content_provider("PUT", path, headers, body, content_length,
5274 nullptr, nullptr, content_type);
5277 Result ClientImpl::Put(const std::string &path, const std::string &body,
5278 const std::string &content_type) {
5279 return Put(path, Headers(), body, content_type);
5282 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5283 const std::string &body,
5284 const std::string &content_type) {
5285 return send_with_content_provider("PUT", path, headers, body.data(),
5286 body.size(), nullptr, nullptr,
5290 Result ClientImpl::Put(const std::string &path, size_t content_length,
5291 ContentProvider content_provider,
5292 const std::string &content_type) {
5293 return Put(path, Headers(), content_length, std::move(content_provider),
5297 Result ClientImpl::Put(const std::string &path,
5298 ContentProviderWithoutLength content_provider,
5299 const std::string &content_type) {
5300 return Put(path, Headers(), std::move(content_provider), content_type);
5303 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5304 size_t content_length,
5305 ContentProvider content_provider,
5306 const std::string &content_type) {
5307 return send_with_content_provider("PUT", path, headers, nullptr,
5308 content_length, std::move(content_provider),
5309 nullptr, content_type);
5312 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5313 ContentProviderWithoutLength content_provider,
5314 const std::string &content_type) {
5315 return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
5316 std::move(content_provider), content_type);
5319 Result ClientImpl::Put(const std::string &path, const Params ¶ms) {
5320 return Put(path, Headers(), params);
5323 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5324 const Params ¶ms) {
5325 auto query = detail::params_to_query_str(params);
5326 return Put(path, headers, query, "application/x-www-form-urlencoded");
5329 Result ClientImpl::Put(const std::string &path,
5330 const MultipartFormDataItems &items) {
5331 return Put(path, Headers(), items);
5334 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5335 const MultipartFormDataItems &items) {
5336 const auto &boundary = detail::make_multipart_data_boundary();
5337 const auto &content_type =
5338 detail::serialize_multipart_formdata_get_content_type(boundary);
5339 const auto &body = detail::serialize_multipart_formdata(items, boundary);
5340 return Put(path, headers, body, content_type);
5343 Result ClientImpl::Put(const std::string &path, const Headers &headers,
5344 const MultipartFormDataItems &items,
5345 const std::string &boundary) {
5346 if (!detail::is_multipart_boundary_chars_valid(boundary)) {
5347 return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
5350 const auto &content_type =
5351 detail::serialize_multipart_formdata_get_content_type(boundary);
5352 const auto &body = detail::serialize_multipart_formdata(items, boundary);
5353 return Put(path, headers, body, content_type);
5357 ClientImpl::Put(const std::string &path, const Headers &headers,
5358 const MultipartFormDataItems &items,
5359 const MultipartFormDataProviderItems &provider_items) {
5360 const auto &boundary = detail::make_multipart_data_boundary();
5361 const auto &content_type =
5362 detail::serialize_multipart_formdata_get_content_type(boundary);
5363 return send_with_content_provider(
5364 "PUT", path, headers, nullptr, 0, nullptr,
5365 get_multipart_content_provider(boundary, items, provider_items),
5368 Result ClientImpl::Patch(const std::string &path) {
5369 return Patch(path, std::string(), std::string());
5372 Result ClientImpl::Patch(const std::string &path, const char *body,
5373 size_t content_length,
5374 const std::string &content_type) {
5375 return Patch(path, Headers(), body, content_length, content_type);
5378 Result ClientImpl::Patch(const std::string &path, const Headers &headers,
5379 const char *body, size_t content_length,
5380 const std::string &content_type) {
5381 return send_with_content_provider("PATCH", path, headers, body,
5382 content_length, nullptr, nullptr,
5386 Result ClientImpl::Patch(const std::string &path,
5387 const std::string &body,
5388 const std::string &content_type) {
5389 return Patch(path, Headers(), body, content_type);
5392 Result ClientImpl::Patch(const std::string &path, const Headers &headers,
5393 const std::string &body,
5394 const std::string &content_type) {
5395 return send_with_content_provider("PATCH", path, headers, body.data(),
5396 body.size(), nullptr, nullptr,
5400 Result ClientImpl::Patch(const std::string &path, size_t content_length,
5401 ContentProvider content_provider,
5402 const std::string &content_type) {
5403 return Patch(path, Headers(), content_length, std::move(content_provider),
5407 Result ClientImpl::Patch(const std::string &path,
5408 ContentProviderWithoutLength content_provider,
5409 const std::string &content_type) {
5410 return Patch(path, Headers(), std::move(content_provider), content_type);
5413 Result ClientImpl::Patch(const std::string &path, const Headers &headers,
5414 size_t content_length,
5415 ContentProvider content_provider,
5416 const std::string &content_type) {
5417 return send_with_content_provider("PATCH", path, headers, nullptr,
5418 content_length, std::move(content_provider),
5419 nullptr, content_type);
5422 Result ClientImpl::Patch(const std::string &path, const Headers &headers,
5423 ContentProviderWithoutLength content_provider,
5424 const std::string &content_type) {
5425 return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
5426 std::move(content_provider), content_type);
5429 Result ClientImpl::Delete(const std::string &path) {
5430 return Delete(path, Headers(), std::string(), std::string());
5433 Result ClientImpl::Delete(const std::string &path,
5434 const Headers &headers) {
5435 return Delete(path, headers, std::string(), std::string());
5438 Result ClientImpl::Delete(const std::string &path, const char *body,
5439 size_t content_length,
5440 const std::string &content_type) {
5441 return Delete(path, Headers(), body, content_length, content_type);
5444 Result ClientImpl::Delete(const std::string &path,
5445 const Headers &headers, const char *body,
5446 size_t content_length,
5447 const std::string &content_type) {
5449 req.method = "DELETE";
5450 req.headers = headers;
5453 if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
5454 req.body.assign(body, content_length);
5456 return send_(std::move(req));
5459 Result ClientImpl::Delete(const std::string &path,
5460 const std::string &body,
5461 const std::string &content_type) {
5462 return Delete(path, Headers(), body.data(), body.size(), content_type);
5465 Result ClientImpl::Delete(const std::string &path,
5466 const Headers &headers,
5467 const std::string &body,
5468 const std::string &content_type) {
5469 return Delete(path, headers, body.data(), body.size(), content_type);
5472 Result ClientImpl::Options(const std::string &path) {
5473 return Options(path, Headers());
5476 Result ClientImpl::Options(const std::string &path,
5477 const Headers &headers) {
5479 req.method = "OPTIONS";
5480 req.headers = headers;
5483 return send_(std::move(req));
5486 size_t ClientImpl::is_socket_open() const {
5487 std::lock_guard<std::mutex> guard(socket_mutex_);
5488 return socket_.is_open();
5491 socket_t ClientImpl::socket() const { return socket_.sock; }
5493 void ClientImpl::stop() {
5494 std::lock_guard<std::mutex> guard(socket_mutex_);
5496 // If there is anything ongoing right now, the ONLY thread-safe thing we can
5497 // do is to shutdown_socket, so that threads using this socket suddenly
5498 // discover they can't read/write any more and error out. Everything else
5499 // (closing the socket, shutting ssl down) is unsafe because these actions are
5501 if (socket_requests_in_flight_ > 0) {
5502 shutdown_socket(socket_);
5504 // Aside from that, we set a flag for the socket to be closed when we're
5506 socket_should_be_closed_when_request_is_done_ = true;
5510 // Otherwise, still holding the mutex, we can shut everything down ourselves
5511 shutdown_ssl(socket_, true);
5512 shutdown_socket(socket_);
5513 close_socket(socket_);
5516 void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
5517 connection_timeout_sec_ = sec;
5518 connection_timeout_usec_ = usec;
5521 void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
5522 read_timeout_sec_ = sec;
5523 read_timeout_usec_ = usec;
5526 void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
5527 write_timeout_sec_ = sec;
5528 write_timeout_usec_ = usec;
5531 void ClientImpl::set_basic_auth(const std::string &username,
5532 const std::string &password) {
5533 basic_auth_username_ = username;
5534 basic_auth_password_ = password;
5537 void ClientImpl::set_bearer_token_auth(const std::string &token) {
5538 bearer_token_auth_token_ = token;
5541 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5542 void ClientImpl::set_digest_auth(const std::string &username,
5543 const std::string &password) {
5544 digest_auth_username_ = username;
5545 digest_auth_password_ = password;
5549 void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
5551 void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
5553 void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
5556 ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
5557 addr_map_ = std::move(addr_map);
5560 void ClientImpl::set_default_headers(Headers headers) {
5561 default_headers_ = std::move(headers);
5564 void ClientImpl::set_address_family(int family) {
5565 address_family_ = family;
5568 void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5570 void ClientImpl::set_socket_options(SocketOptions socket_options) {
5571 socket_options_ = std::move(socket_options);
5574 void ClientImpl::set_compress(bool on) { compress_ = on; }
5576 void ClientImpl::set_decompress(bool on) { decompress_ = on; }
5578 void ClientImpl::set_interface(const std::string &intf) {
5582 void ClientImpl::set_proxy(const std::string &host, int port) {
5587 void ClientImpl::set_proxy_basic_auth(const std::string &username,
5588 const std::string &password) {
5589 proxy_basic_auth_username_ = username;
5590 proxy_basic_auth_password_ = password;
5593 void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {
5594 proxy_bearer_token_auth_token_ = token;
5597 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5598 void ClientImpl::set_proxy_digest_auth(const std::string &username,
5599 const std::string &password) {
5600 proxy_digest_auth_username_ = username;
5601 proxy_digest_auth_password_ = password;
5604 void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,
5605 const std::string &ca_cert_dir_path) {
5606 ca_cert_file_path_ = ca_cert_file_path;
5607 ca_cert_dir_path_ = ca_cert_dir_path;
5610 void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
5611 if (ca_cert_store && ca_cert_store != ca_cert_store_) {
5612 ca_cert_store_ = ca_cert_store;
5616 X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
5618 auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
5619 if (!mem) return nullptr;
5621 auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
5627 auto cts = X509_STORE_new();
5629 for (auto first = 0, last = sk_X509_INFO_num(inf); first < last; ++first) {
5630 auto itmp = sk_X509_INFO_value(inf, first);
5631 if (!itmp) { continue; }
5633 if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
5634 if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
5638 sk_X509_INFO_pop_free(inf, X509_INFO_free);
5643 void ClientImpl::enable_server_certificate_verification(bool enabled) {
5644 server_certificate_verification_ = enabled;
5648 void ClientImpl::set_logger(Logger logger) {
5649 logger_ = std::move(logger);
5653 * SSL Implementation
5655 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5658 template <typename U, typename V>
5659 SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
5660 U SSL_connect_or_accept, V setup) {
5663 std::lock_guard<std::mutex> guard(ctx_mutex);
5668 set_nonblocking(sock, true);
5669 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
5670 BIO_set_nbio(bio, 1);
5671 SSL_set_bio(ssl, bio, bio);
5673 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
5676 std::lock_guard<std::mutex> guard(ctx_mutex);
5679 set_nonblocking(sock, false);
5682 BIO_set_nbio(bio, 0);
5683 set_nonblocking(sock, false);
5689 void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
5690 bool shutdown_gracefully) {
5691 // sometimes we may want to skip this to try to avoid SIGPIPE if we know
5692 // the remote has closed the network connection
5693 // Note that it is not always possible to avoid SIGPIPE, this is merely a
5695 if (shutdown_gracefully) { SSL_shutdown(ssl); }
5697 std::lock_guard<std::mutex> guard(ctx_mutex);
5701 template <typename U>
5702 bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,
5703 U ssl_connect_or_accept,
5705 time_t timeout_usec) {
5707 while ((res = ssl_connect_or_accept(ssl)) != 1) {
5708 auto err = SSL_get_error(ssl, res);
5710 case SSL_ERROR_WANT_READ:
5711 if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
5713 case SSL_ERROR_WANT_WRITE:
5714 if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
5723 template <typename T>
5724 bool process_server_socket_ssl(
5725 const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,
5726 size_t keep_alive_max_count, time_t keep_alive_timeout_sec,
5727 time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
5728 time_t write_timeout_usec, T callback) {
5729 return process_server_socket_core(
5730 svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,
5731 [&](bool close_connection, bool &connection_closed) {
5732 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
5733 write_timeout_sec, write_timeout_usec);
5734 return callback(strm, close_connection, connection_closed);
5738 template <typename T>
5740 process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,
5741 time_t read_timeout_usec, time_t write_timeout_sec,
5742 time_t write_timeout_usec, T callback) {
5743 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
5744 write_timeout_sec, write_timeout_usec);
5745 return callback(strm);
5752 OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
5756 // SSL socket stream implementation
5757 SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
5758 time_t read_timeout_sec,
5759 time_t read_timeout_usec,
5760 time_t write_timeout_sec,
5761 time_t write_timeout_usec)
5762 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
5763 read_timeout_usec_(read_timeout_usec),
5764 write_timeout_sec_(write_timeout_sec),
5765 write_timeout_usec_(write_timeout_usec) {
5766 SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
5769 SSLSocketStream::~SSLSocketStream() {}
5771 bool SSLSocketStream::is_readable() const {
5772 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
5775 bool SSLSocketStream::is_writable() const {
5776 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
5777 is_socket_alive(sock_);
5780 ssize_t SSLSocketStream::read(char *ptr, size_t size) {
5781 if (SSL_pending(ssl_) > 0) {
5782 return SSL_read(ssl_, ptr, static_cast<int>(size));
5783 } else if (is_readable()) {
5784 auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5786 auto err = SSL_get_error(ssl_, ret);
5789 while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
5790 (err == SSL_ERROR_SYSCALL &&
5791 WSAGetLastError() == WSAETIMEDOUT))) {
5793 while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
5795 if (SSL_pending(ssl_) > 0) {
5796 return SSL_read(ssl_, ptr, static_cast<int>(size));
5797 } else if (is_readable()) {
5798 std::this_thread::sleep_for(std::chrono::milliseconds(1));
5799 ret = SSL_read(ssl_, ptr, static_cast<int>(size));
5800 if (ret >= 0) { return ret; }
5801 err = SSL_get_error(ssl_, ret);
5812 ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
5813 if (is_writable()) {
5814 auto handle_size = static_cast<int>(
5815 std::min<size_t>(size, (std::numeric_limits<int>::max)()));
5817 auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
5819 auto err = SSL_get_error(ssl_, ret);
5822 while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
5823 (err == SSL_ERROR_SYSCALL &&
5824 WSAGetLastError() == WSAETIMEDOUT))) {
5826 while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
5828 if (is_writable()) {
5829 std::this_thread::sleep_for(std::chrono::milliseconds(1));
5830 ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
5831 if (ret >= 0) { return ret; }
5832 err = SSL_get_error(ssl_, ret);
5843 void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
5845 detail::get_remote_ip_and_port(sock_, ip, port);
5848 void SSLSocketStream::get_local_ip_and_port(std::string &ip,
5850 detail::get_local_ip_and_port(sock_, ip, port);
5853 socket_t SSLSocketStream::socket() const { return sock_; }
5855 static SSLInit sslinit_;
5857 } // namespace detail
5859 // SSL HTTP server implementation
5860 SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
5861 const char *client_ca_cert_file_path,
5862 const char *client_ca_cert_dir_path,
5863 const char *private_key_password) {
5864 ctx_ = SSL_CTX_new(TLS_server_method());
5867 SSL_CTX_set_options(ctx_,
5868 SSL_OP_NO_COMPRESSION |
5869 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
5871 SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
5873 // add default password callback before opening encrypted private key
5874 if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
5875 SSL_CTX_set_default_passwd_cb_userdata(ctx_,
5876 (char *)private_key_password);
5879 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
5880 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
5884 } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
5885 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
5886 client_ca_cert_dir_path);
5889 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
5894 SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
5895 X509_STORE *client_ca_cert_store) {
5896 ctx_ = SSL_CTX_new(TLS_server_method());
5899 SSL_CTX_set_options(ctx_,
5900 SSL_OP_NO_COMPRESSION |
5901 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
5903 SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
5905 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
5906 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
5909 } else if (client_ca_cert_store) {
5910 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
5913 ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
5918 SSLServer::SSLServer(
5919 const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
5920 ctx_ = SSL_CTX_new(TLS_method());
5922 if (!setup_ssl_ctx_callback(*ctx_)) {
5929 SSLServer::~SSLServer() {
5930 if (ctx_) { SSL_CTX_free(ctx_); }
5933 bool SSLServer::is_valid() const { return ctx_; }
5935 SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
5937 bool SSLServer::process_and_close_socket(socket_t sock) {
5938 auto ssl = detail::ssl_new(
5939 sock, ctx_, ctx_mutex_,
5941 return detail::ssl_connect_or_accept_nonblocking(
5942 sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
5944 [](SSL * /*ssl2*/) { return true; });
5948 ret = detail::process_server_socket_ssl(
5949 svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
5950 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
5951 write_timeout_usec_,
5952 [this, ssl](Stream &strm, bool close_connection,
5953 bool &connection_closed) {
5954 return process_request(strm, close_connection, connection_closed,
5955 [&](Request &req) { req.ssl = ssl; });
5958 // Shutdown gracefully if the result seemed successful, non-gracefully if
5959 // the connection appeared to be closed.
5960 const bool shutdown_gracefully = ret;
5961 detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
5964 detail::shutdown_socket(sock);
5965 detail::close_socket(sock);
5969 // SSL HTTP client implementation
5970 SSLClient::SSLClient(const std::string &host)
5971 : SSLClient(host, 443, std::string(), std::string()) {}
5973 SSLClient::SSLClient(const std::string &host, int port)
5974 : SSLClient(host, port, std::string(), std::string()) {}
5976 SSLClient::SSLClient(const std::string &host, int port,
5977 const std::string &client_cert_path,
5978 const std::string &client_key_path)
5979 : ClientImpl(host, port, client_cert_path, client_key_path) {
5980 ctx_ = SSL_CTX_new(TLS_client_method());
5982 detail::split(&host_[0], &host_[host_.size()], '.',
5983 [&](const char *b, const char *e) {
5984 host_components_.emplace_back(std::string(b, e));
5987 if (!client_cert_path.empty() && !client_key_path.empty()) {
5988 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
5989 SSL_FILETYPE_PEM) != 1 ||
5990 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
5991 SSL_FILETYPE_PEM) != 1) {
5998 SSLClient::SSLClient(const std::string &host, int port,
5999 X509 *client_cert, EVP_PKEY *client_key)
6000 : ClientImpl(host, port) {
6001 ctx_ = SSL_CTX_new(TLS_client_method());
6003 detail::split(&host_[0], &host_[host_.size()], '.',
6004 [&](const char *b, const char *e) {
6005 host_components_.emplace_back(std::string(b, e));
6008 if (client_cert != nullptr && client_key != nullptr) {
6009 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
6010 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
6017 SSLClient::~SSLClient() {
6018 if (ctx_) { SSL_CTX_free(ctx_); }
6019 // Make sure to shut down SSL since shutdown_ssl will resolve to the
6020 // base function rather than the derived function once we get to the
6021 // base class destructor, and won't free the SSL (causing a leak).
6022 shutdown_ssl_impl(socket_, true);
6025 bool SSLClient::is_valid() const { return ctx_; }
6027 void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
6028 if (ca_cert_store) {
6030 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
6031 // Free memory allocated for old cert and use new store `ca_cert_store`
6032 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
6035 X509_STORE_free(ca_cert_store);
6040 void SSLClient::load_ca_cert_store(const char *ca_cert,
6042 set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));
6045 long SSLClient::get_openssl_verify_result() const {
6046 return verify_result_;
6049 SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
6051 bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
6052 return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
6055 // Assumes that socket_mutex_ is locked and that there are no requests in flight
6056 bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
6057 bool &success, Error &error) {
6060 if (!detail::process_client_socket(
6061 socket.sock, read_timeout_sec_, read_timeout_usec_,
6062 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
6064 req2.method = "CONNECT";
6065 req2.path = host_and_port_;
6066 return process_request(strm, req2, res2, false, error);
6068 // Thread-safe to close everything because we are assuming there are no
6069 // requests in flight
6070 shutdown_ssl(socket, true);
6071 shutdown_socket(socket);
6072 close_socket(socket);
6077 if (res2.status == 407) {
6078 if (!proxy_digest_auth_username_.empty() &&
6079 !proxy_digest_auth_password_.empty()) {
6080 std::map<std::string, std::string> auth;
6081 if (detail::parse_www_authenticate(res2, auth, true)) {
6083 if (!detail::process_client_socket(
6084 socket.sock, read_timeout_sec_, read_timeout_usec_,
6085 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
6087 req3.method = "CONNECT";
6088 req3.path = host_and_port_;
6089 req3.headers.insert(detail::make_digest_authentication_header(
6090 req3, auth, 1, detail::random_string(10),
6091 proxy_digest_auth_username_, proxy_digest_auth_password_,
6093 return process_request(strm, req3, res3, false, error);
6095 // Thread-safe to close everything because we are assuming there are
6096 // no requests in flight
6097 shutdown_ssl(socket, true);
6098 shutdown_socket(socket);
6099 close_socket(socket);
6113 bool SSLClient::load_certs() {
6116 std::call_once(initialize_cert_, [&]() {
6117 std::lock_guard<std::mutex> guard(ctx_mutex_);
6118 if (!ca_cert_file_path_.empty()) {
6119 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
6123 } else if (!ca_cert_dir_path_.empty()) {
6124 if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
6125 ca_cert_dir_path_.c_str())) {
6129 auto loaded = false;
6132 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
6133 #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
6135 loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
6136 #endif // TARGET_OS_OSX
6138 if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
6145 bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
6146 auto ssl = detail::ssl_new(
6147 socket.sock, ctx_, ctx_mutex_,
6149 if (server_certificate_verification_) {
6150 if (!load_certs()) {
6151 error = Error::SSLLoadingCerts;
6154 SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
6157 if (!detail::ssl_connect_or_accept_nonblocking(
6158 socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
6159 connection_timeout_usec_)) {
6160 error = Error::SSLConnection;
6164 if (server_certificate_verification_) {
6165 verify_result_ = SSL_get_verify_result(ssl2);
6167 if (verify_result_ != X509_V_OK) {
6168 error = Error::SSLServerVerification;
6172 auto server_cert = SSL_get1_peer_certificate(ssl2);
6174 if (server_cert == nullptr) {
6175 error = Error::SSLServerVerification;
6179 if (!verify_host(server_cert)) {
6180 X509_free(server_cert);
6181 error = Error::SSLServerVerification;
6184 X509_free(server_cert);
6190 SSL_set_tlsext_host_name(ssl2, host_.c_str());
6199 shutdown_socket(socket);
6200 close_socket(socket);
6204 void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
6205 shutdown_ssl_impl(socket, shutdown_gracefully);
6208 void SSLClient::shutdown_ssl_impl(Socket &socket,
6209 bool shutdown_gracefully) {
6210 if (socket.sock == INVALID_SOCKET) {
6211 assert(socket.ssl == nullptr);
6215 detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
6216 socket.ssl = nullptr;
6218 assert(socket.ssl == nullptr);
6222 SSLClient::process_socket(const Socket &socket,
6223 std::function<bool(Stream &strm)> callback) {
6225 return detail::process_client_socket_ssl(
6226 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
6227 write_timeout_sec_, write_timeout_usec_, std::move(callback));
6230 bool SSLClient::is_ssl() const { return true; }
6232 bool SSLClient::verify_host(X509 *server_cert) const {
6233 /* Quote from RFC2818 section 3.1 "Server Identity"
6235 If a subjectAltName extension of type dNSName is present, that MUST
6236 be used as the identity. Otherwise, the (most specific) Common Name
6237 field in the Subject field of the certificate MUST be used. Although
6238 the use of the Common Name is existing practice, it is deprecated and
6239 Certification Authorities are encouraged to use the dNSName instead.
6241 Matching is performed using the matching rules specified by
6242 [RFC2459]. If more than one identity of a given type is present in
6243 the certificate (e.g., more than one dNSName name, a match in any one
6244 of the set is considered acceptable.) Names may contain the wildcard
6245 character * which is considered to match any single domain name
6246 component or component fragment. E.g., *.a.com matches foo.a.com but
6247 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
6249 In some cases, the URI is specified as an IP address rather than a
6250 hostname. In this case, the iPAddress subjectAltName must be present
6251 in the certificate and must exactly match the IP in the URI.
6254 return verify_host_with_subject_alt_name(server_cert) ||
6255 verify_host_with_common_name(server_cert);
6259 SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
6262 auto type = GEN_DNS;
6264 struct in6_addr addr6;
6265 struct in_addr addr;
6266 size_t addr_len = 0;
6269 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
6271 addr_len = sizeof(struct in6_addr);
6272 } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
6274 addr_len = sizeof(struct in_addr);
6278 auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
6279 X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
6282 auto dsn_matched = false;
6283 auto ip_matched = false;
6285 auto count = sk_GENERAL_NAME_num(alt_names);
6287 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
6288 auto val = sk_GENERAL_NAME_value(alt_names, i);
6289 if (val->type == type) {
6290 auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
6291 auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
6294 case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;
6297 if (!memcmp(&addr6, name, addr_len) ||
6298 !memcmp(&addr, name, addr_len)) {
6306 if (dsn_matched || ip_matched) { ret = true; }
6309 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
6313 bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
6314 const auto subject_name = X509_get_subject_name(server_cert);
6316 if (subject_name != nullptr) {
6318 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
6319 name, sizeof(name));
6321 if (name_len != -1) {
6322 return check_host_name(name, static_cast<size_t>(name_len));
6329 bool SSLClient::check_host_name(const char *pattern,
6330 size_t pattern_len) const {
6331 if (host_.size() == pattern_len && host_ == pattern) { return true; }
6334 // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
6335 std::vector<std::string> pattern_components;
6336 detail::split(&pattern[0], &pattern[pattern_len], '.',
6337 [&](const char *b, const char *e) {
6338 pattern_components.emplace_back(std::string(b, e));
6341 if (host_components_.size() != pattern_components.size()) { return false; }
6343 auto itr = pattern_components.begin();
6344 for (const auto &h : host_components_) {
6346 if (p != h && p != "*") {
6347 auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
6348 !p.compare(0, p.size() - 1, h));
6349 if (!partial_match) { return false; }
6358 // Universal client implementation
6359 Client::Client(const std::string &scheme_host_port)
6360 : Client(scheme_host_port, std::string(), std::string()) {}
6362 Client::Client(const std::string &scheme_host_port,
6363 const std::string &client_cert_path,
6364 const std::string &client_key_path) {
6365 const static std::regex re(
6366 R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
6369 if (std::regex_match(scheme_host_port, m, re)) {
6370 auto scheme = m[1].str();
6372 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6373 if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
6375 if (!scheme.empty() && scheme != "http") {
6377 #ifndef CPPHTTPLIB_NO_EXCEPTIONS
6378 std::string msg = "'" + scheme + "' scheme is not supported.";
6379 throw std::invalid_argument(msg);
6384 auto is_ssl = scheme == "https";
6386 auto host = m[2].str();
6387 if (host.empty()) { host = m[3].str(); }
6389 auto port_str = m[4].str();
6390 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
6393 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6394 cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,
6399 cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,
6403 cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
6404 client_cert_path, client_key_path);
6408 Client::Client(const std::string &host, int port)
6409 : cli_(detail::make_unique<ClientImpl>(host, port)) {}
6411 Client::Client(const std::string &host, int port,
6412 const std::string &client_cert_path,
6413 const std::string &client_key_path)
6414 : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
6415 client_key_path)) {}
6417 Client::~Client() {}
6419 bool Client::is_valid() const {
6420 return cli_ != nullptr && cli_->is_valid();
6423 Result Client::Get(const std::string &path) { return cli_->Get(path); }
6424 Result Client::Get(const std::string &path, const Headers &headers) {
6425 return cli_->Get(path, headers);
6427 Result Client::Get(const std::string &path, Progress progress) {
6428 return cli_->Get(path, std::move(progress));
6430 Result Client::Get(const std::string &path, const Headers &headers,
6431 Progress progress) {
6432 return cli_->Get(path, headers, std::move(progress));
6434 Result Client::Get(const std::string &path,
6435 ContentReceiver content_receiver) {
6436 return cli_->Get(path, std::move(content_receiver));
6438 Result Client::Get(const std::string &path, const Headers &headers,
6439 ContentReceiver content_receiver) {
6440 return cli_->Get(path, headers, std::move(content_receiver));
6442 Result Client::Get(const std::string &path,
6443 ContentReceiver content_receiver, Progress progress) {
6444 return cli_->Get(path, std::move(content_receiver), std::move(progress));
6446 Result Client::Get(const std::string &path, const Headers &headers,
6447 ContentReceiver content_receiver, Progress progress) {
6448 return cli_->Get(path, headers, std::move(content_receiver),
6449 std::move(progress));
6451 Result Client::Get(const std::string &path,
6452 ResponseHandler response_handler,
6453 ContentReceiver content_receiver) {
6454 return cli_->Get(path, std::move(response_handler),
6455 std::move(content_receiver));
6457 Result Client::Get(const std::string &path, const Headers &headers,
6458 ResponseHandler response_handler,
6459 ContentReceiver content_receiver) {
6460 return cli_->Get(path, headers, std::move(response_handler),
6461 std::move(content_receiver));
6463 Result Client::Get(const std::string &path,
6464 ResponseHandler response_handler,
6465 ContentReceiver content_receiver, Progress progress) {
6466 return cli_->Get(path, std::move(response_handler),
6467 std::move(content_receiver), std::move(progress));
6469 Result Client::Get(const std::string &path, const Headers &headers,
6470 ResponseHandler response_handler,
6471 ContentReceiver content_receiver, Progress progress) {
6472 return cli_->Get(path, headers, std::move(response_handler),
6473 std::move(content_receiver), std::move(progress));
6475 Result Client::Get(const std::string &path, const Params ¶ms,
6476 const Headers &headers, Progress progress) {
6477 return cli_->Get(path, params, headers, progress);
6479 Result Client::Get(const std::string &path, const Params ¶ms,
6480 const Headers &headers,
6481 ContentReceiver content_receiver, Progress progress) {
6482 return cli_->Get(path, params, headers, content_receiver, progress);
6484 Result Client::Get(const std::string &path, const Params ¶ms,
6485 const Headers &headers,
6486 ResponseHandler response_handler,
6487 ContentReceiver content_receiver, Progress progress) {
6488 return cli_->Get(path, params, headers, response_handler, content_receiver,
6492 Result Client::Head(const std::string &path) { return cli_->Head(path); }
6493 Result Client::Head(const std::string &path, const Headers &headers) {
6494 return cli_->Head(path, headers);
6497 Result Client::Post(const std::string &path) { return cli_->Post(path); }
6498 Result Client::Post(const std::string &path, const Headers &headers) {
6499 return cli_->Post(path, headers);
6501 Result Client::Post(const std::string &path, const char *body,
6502 size_t content_length,
6503 const std::string &content_type) {
6504 return cli_->Post(path, body, content_length, content_type);
6506 Result Client::Post(const std::string &path, const Headers &headers,
6507 const char *body, size_t content_length,
6508 const std::string &content_type) {
6509 return cli_->Post(path, headers, body, content_length, content_type);
6511 Result Client::Post(const std::string &path, const std::string &body,
6512 const std::string &content_type) {
6513 return cli_->Post(path, body, content_type);
6515 Result Client::Post(const std::string &path, const Headers &headers,
6516 const std::string &body,
6517 const std::string &content_type) {
6518 return cli_->Post(path, headers, body, content_type);
6520 Result Client::Post(const std::string &path, size_t content_length,
6521 ContentProvider content_provider,
6522 const std::string &content_type) {
6523 return cli_->Post(path, content_length, std::move(content_provider),
6526 Result Client::Post(const std::string &path,
6527 ContentProviderWithoutLength content_provider,
6528 const std::string &content_type) {
6529 return cli_->Post(path, std::move(content_provider), content_type);
6531 Result Client::Post(const std::string &path, const Headers &headers,
6532 size_t content_length,
6533 ContentProvider content_provider,
6534 const std::string &content_type) {
6535 return cli_->Post(path, headers, content_length, std::move(content_provider),
6538 Result Client::Post(const std::string &path, const Headers &headers,
6539 ContentProviderWithoutLength content_provider,
6540 const std::string &content_type) {
6541 return cli_->Post(path, headers, std::move(content_provider), content_type);
6543 Result Client::Post(const std::string &path, const Params ¶ms) {
6544 return cli_->Post(path, params);
6546 Result Client::Post(const std::string &path, const Headers &headers,
6547 const Params ¶ms) {
6548 return cli_->Post(path, headers, params);
6550 Result Client::Post(const std::string &path,
6551 const MultipartFormDataItems &items) {
6552 return cli_->Post(path, items);
6554 Result Client::Post(const std::string &path, const Headers &headers,
6555 const MultipartFormDataItems &items) {
6556 return cli_->Post(path, headers, items);
6558 Result Client::Post(const std::string &path, const Headers &headers,
6559 const MultipartFormDataItems &items,
6560 const std::string &boundary) {
6561 return cli_->Post(path, headers, items, boundary);
6564 Client::Post(const std::string &path, const Headers &headers,
6565 const MultipartFormDataItems &items,
6566 const MultipartFormDataProviderItems &provider_items) {
6567 return cli_->Post(path, headers, items, provider_items);
6569 Result Client::Put(const std::string &path) { return cli_->Put(path); }
6570 Result Client::Put(const std::string &path, const char *body,
6571 size_t content_length,
6572 const std::string &content_type) {
6573 return cli_->Put(path, body, content_length, content_type);
6575 Result Client::Put(const std::string &path, const Headers &headers,
6576 const char *body, size_t content_length,
6577 const std::string &content_type) {
6578 return cli_->Put(path, headers, body, content_length, content_type);
6580 Result Client::Put(const std::string &path, const std::string &body,
6581 const std::string &content_type) {
6582 return cli_->Put(path, body, content_type);
6584 Result Client::Put(const std::string &path, const Headers &headers,
6585 const std::string &body,
6586 const std::string &content_type) {
6587 return cli_->Put(path, headers, body, content_type);
6589 Result Client::Put(const std::string &path, size_t content_length,
6590 ContentProvider content_provider,
6591 const std::string &content_type) {
6592 return cli_->Put(path, content_length, std::move(content_provider),
6595 Result Client::Put(const std::string &path,
6596 ContentProviderWithoutLength content_provider,
6597 const std::string &content_type) {
6598 return cli_->Put(path, std::move(content_provider), content_type);
6600 Result Client::Put(const std::string &path, const Headers &headers,
6601 size_t content_length,
6602 ContentProvider content_provider,
6603 const std::string &content_type) {
6604 return cli_->Put(path, headers, content_length, std::move(content_provider),
6607 Result Client::Put(const std::string &path, const Headers &headers,
6608 ContentProviderWithoutLength content_provider,
6609 const std::string &content_type) {
6610 return cli_->Put(path, headers, std::move(content_provider), content_type);
6612 Result Client::Put(const std::string &path, const Params ¶ms) {
6613 return cli_->Put(path, params);
6615 Result Client::Put(const std::string &path, const Headers &headers,
6616 const Params ¶ms) {
6617 return cli_->Put(path, headers, params);
6619 Result Client::Put(const std::string &path,
6620 const MultipartFormDataItems &items) {
6621 return cli_->Put(path, items);
6623 Result Client::Put(const std::string &path, const Headers &headers,
6624 const MultipartFormDataItems &items) {
6625 return cli_->Put(path, headers, items);
6627 Result Client::Put(const std::string &path, const Headers &headers,
6628 const MultipartFormDataItems &items,
6629 const std::string &boundary) {
6630 return cli_->Put(path, headers, items, boundary);
6633 Client::Put(const std::string &path, const Headers &headers,
6634 const MultipartFormDataItems &items,
6635 const MultipartFormDataProviderItems &provider_items) {
6636 return cli_->Put(path, headers, items, provider_items);
6638 Result Client::Patch(const std::string &path) {
6639 return cli_->Patch(path);
6641 Result Client::Patch(const std::string &path, const char *body,
6642 size_t content_length,
6643 const std::string &content_type) {
6644 return cli_->Patch(path, body, content_length, content_type);
6646 Result Client::Patch(const std::string &path, const Headers &headers,
6647 const char *body, size_t content_length,
6648 const std::string &content_type) {
6649 return cli_->Patch(path, headers, body, content_length, content_type);
6651 Result Client::Patch(const std::string &path, const std::string &body,
6652 const std::string &content_type) {
6653 return cli_->Patch(path, body, content_type);
6655 Result Client::Patch(const std::string &path, const Headers &headers,
6656 const std::string &body,
6657 const std::string &content_type) {
6658 return cli_->Patch(path, headers, body, content_type);
6660 Result Client::Patch(const std::string &path, size_t content_length,
6661 ContentProvider content_provider,
6662 const std::string &content_type) {
6663 return cli_->Patch(path, content_length, std::move(content_provider),
6666 Result Client::Patch(const std::string &path,
6667 ContentProviderWithoutLength content_provider,
6668 const std::string &content_type) {
6669 return cli_->Patch(path, std::move(content_provider), content_type);
6671 Result Client::Patch(const std::string &path, const Headers &headers,
6672 size_t content_length,
6673 ContentProvider content_provider,
6674 const std::string &content_type) {
6675 return cli_->Patch(path, headers, content_length, std::move(content_provider),
6678 Result Client::Patch(const std::string &path, const Headers &headers,
6679 ContentProviderWithoutLength content_provider,
6680 const std::string &content_type) {
6681 return cli_->Patch(path, headers, std::move(content_provider), content_type);
6683 Result Client::Delete(const std::string &path) {
6684 return cli_->Delete(path);
6686 Result Client::Delete(const std::string &path, const Headers &headers) {
6687 return cli_->Delete(path, headers);
6689 Result Client::Delete(const std::string &path, const char *body,
6690 size_t content_length,
6691 const std::string &content_type) {
6692 return cli_->Delete(path, body, content_length, content_type);
6694 Result Client::Delete(const std::string &path, const Headers &headers,
6695 const char *body, size_t content_length,
6696 const std::string &content_type) {
6697 return cli_->Delete(path, headers, body, content_length, content_type);
6699 Result Client::Delete(const std::string &path, const std::string &body,
6700 const std::string &content_type) {
6701 return cli_->Delete(path, body, content_type);
6703 Result Client::Delete(const std::string &path, const Headers &headers,
6704 const std::string &body,
6705 const std::string &content_type) {
6706 return cli_->Delete(path, headers, body, content_type);
6708 Result Client::Options(const std::string &path) {
6709 return cli_->Options(path);
6711 Result Client::Options(const std::string &path, const Headers &headers) {
6712 return cli_->Options(path, headers);
6715 bool Client::send(Request &req, Response &res, Error &error) {
6716 return cli_->send(req, res, error);
6719 Result Client::send(const Request &req) { return cli_->send(req); }
6721 size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
6723 socket_t Client::socket() const { return cli_->socket(); }
6725 void Client::stop() { cli_->stop(); }
6728 Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
6729 cli_->set_hostname_addr_map(std::move(addr_map));
6732 void Client::set_default_headers(Headers headers) {
6733 cli_->set_default_headers(std::move(headers));
6736 void Client::set_address_family(int family) {
6737 cli_->set_address_family(family);
6740 void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6742 void Client::set_socket_options(SocketOptions socket_options) {
6743 cli_->set_socket_options(std::move(socket_options));
6746 void Client::set_connection_timeout(time_t sec, time_t usec) {
6747 cli_->set_connection_timeout(sec, usec);
6750 void Client::set_read_timeout(time_t sec, time_t usec) {
6751 cli_->set_read_timeout(sec, usec);
6754 void Client::set_write_timeout(time_t sec, time_t usec) {
6755 cli_->set_write_timeout(sec, usec);
6758 void Client::set_basic_auth(const std::string &username,
6759 const std::string &password) {
6760 cli_->set_basic_auth(username, password);
6762 void Client::set_bearer_token_auth(const std::string &token) {
6763 cli_->set_bearer_token_auth(token);
6765 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6766 void Client::set_digest_auth(const std::string &username,
6767 const std::string &password) {
6768 cli_->set_digest_auth(username, password);
6772 void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
6773 void Client::set_follow_location(bool on) {
6774 cli_->set_follow_location(on);
6777 void Client::set_url_encode(bool on) { cli_->set_url_encode(on); }
6779 void Client::set_compress(bool on) { cli_->set_compress(on); }
6781 void Client::set_decompress(bool on) { cli_->set_decompress(on); }
6783 void Client::set_interface(const std::string &intf) {
6784 cli_->set_interface(intf);
6787 void Client::set_proxy(const std::string &host, int port) {
6788 cli_->set_proxy(host, port);
6790 void Client::set_proxy_basic_auth(const std::string &username,
6791 const std::string &password) {
6792 cli_->set_proxy_basic_auth(username, password);
6794 void Client::set_proxy_bearer_token_auth(const std::string &token) {
6795 cli_->set_proxy_bearer_token_auth(token);
6797 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6798 void Client::set_proxy_digest_auth(const std::string &username,
6799 const std::string &password) {
6800 cli_->set_proxy_digest_auth(username, password);
6804 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6805 void Client::enable_server_certificate_verification(bool enabled) {
6806 cli_->enable_server_certificate_verification(enabled);
6810 void Client::set_logger(Logger logger) {
6811 cli_->set_logger(std::move(logger));
6814 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6815 void Client::set_ca_cert_path(const std::string &ca_cert_file_path,
6816 const std::string &ca_cert_dir_path) {
6817 cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);
6820 void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
6822 static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
6824 cli_->set_ca_cert_store(ca_cert_store);
6828 void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
6829 set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));
6832 long Client::get_openssl_verify_result() const {
6834 return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
6836 return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
6839 SSL_CTX *Client::ssl_context() const {
6840 if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
6845 } // namespace httplib
6847 #endif // WORLD_SCORE