From bfe2505efcbc9adfcbe11a1d381faa56fcf4da9b Mon Sep 17 00:00:00 2001 From: Chee Yee Date: Mon, 17 Mar 2025 00:28:16 -0700 Subject: [PATCH] fix installing pkg via RPI --- CMakeLists.txt | 2 +- source/clients/baseclient.cpp | 2 +- source/http/httplib.cpp | 1452 +++++++++++---------------------- source/http/httplib.h | 486 ++--------- source/windows.cpp | 71 +- 5 files changed, 595 insertions(+), 1418 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0395073..571035a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ add_executable(ezremote_client add_self(ezremote_client) -add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.36" 32 0) +add_pkg(ezremote_client ${CMAKE_SOURCE_DIR}/data "RMTC00001" "ezRemote Client" "01.37" 32 0) target_link_libraries(ezremote_client c diff --git a/source/clients/baseclient.cpp b/source/clients/baseclient.cpp index 51dfef3..115a04e 100644 --- a/source/clients/baseclient.cpp +++ b/source/clients/baseclient.cpp @@ -138,7 +138,7 @@ int BaseClient::Size(const std::string &path, int64_t *size) } else { - sprintf(this->response, "%d - %s", res->status, http_status_message(res->status)); + sprintf(this->response, "%d - %s", res->status, detail::status_message(res->status)); } } else diff --git a/source/http/httplib.cpp b/source/http/httplib.cpp index 8eb2a08..1b5f6d7 100644 --- a/source/http/httplib.cpp +++ b/source/http/httplib.cpp @@ -50,7 +50,7 @@ std::string from_i_to_hex(size_t n) { size_t to_utf8(int code, char *buff) { if (code < 0x0080) { - buff[0] = static_cast(code & 0x7F); + buff[0] = (code & 0x7F); return 1; } else if (code < 0x0800) { buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); @@ -137,11 +137,6 @@ bool is_valid_path(const std::string &path) { // Read component auto beg = i; while (i < path.size() && path[i] != '/') { - if (path[i] == '\0') { - return false; - } else if (path[i] == '\\') { - return false; - } i++; } @@ -290,51 +285,16 @@ std::string trim_copy(const std::string &s) { return s.substr(r.first, r.second - r.first); } -std::string trim_double_quotes_copy(const std::string &s) { - if (s.length() >= 2 && s.front() == '"' && s.back() == '"') { - return s.substr(1, s.size() - 2); - } - return s; -} - -void -divide(const char *data, std::size_t size, char d, - std::function - fn) { - const auto it = std::find(data, data + size, d); - const auto found = static_cast(it != data + size); - const auto lhs_data = data; - const auto lhs_size = static_cast(it - data); - const auto rhs_data = it + found; - const auto rhs_size = size - lhs_size - found; - - fn(lhs_data, lhs_size, rhs_data, rhs_size); -} - -void -divide(const std::string &str, char d, - std::function - fn) { - divide(str.data(), str.size(), d, std::move(fn)); -} - void split(const char *b, const char *e, char d, std::function fn) { - return split(b, e, d, (std::numeric_limits::max)(), std::move(fn)); -} - -void split(const char *b, const char *e, char d, size_t m, - std::function fn) { size_t i = 0; size_t beg = 0; - size_t count = 1; while (e ? (b + i < e) : (b[i] != '\0')) { - if (b[i] == d && count < m) { + if (b[i] == d) { auto r = trim(b, e, beg, i); if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } beg = i + 1; - count++; } i++; } @@ -410,132 +370,6 @@ void stream_line_reader::append(char c) { } } -mmap::mmap(const char *path) -#if defined(_WIN32) - : hFile_(NULL), hMapping_(NULL) -#else - : fd_(-1) -#endif - , - size_(0), addr_(nullptr) { - open(path); -} - -mmap::~mmap() { close(); } - -bool mmap::open(const char *path) { - close(); - -#if defined(_WIN32) - std::wstring wpath; - for (size_t i = 0; i < strlen(path); i++) { - wpath += path[i]; - } - -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | \ - WINAPI_PARTITION_GAMES) && \ - (_WIN32_WINNT >= _WIN32_WINNT_WIN8) - hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, - OPEN_EXISTING, NULL); -#else - hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -#endif - - if (hFile_ == INVALID_HANDLE_VALUE) { return false; } - -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | \ - WINAPI_PARTITION_GAMES) - LARGE_INTEGER size{}; - if (!::GetFileSizeEx(hFile_, &size)) { return false; } - size_ = static_cast(size.QuadPart); -#else - DWORD sizeHigh; - DWORD sizeLow; - sizeLow = ::GetFileSize(hFile_, &sizeHigh); - if (sizeLow == INVALID_FILE_SIZE) { return false; } - size_ = (static_cast(sizeHigh) << (sizeof(DWORD) * 8)) | sizeLow; -#endif - -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && \ - (_WIN32_WINNT >= _WIN32_WINNT_WIN8) - hMapping_ = - ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL); -#else - hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, size.HighPart, - size.LowPart, NULL); -#endif - - if (hMapping_ == NULL) { - close(); - return false; - } - -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && \ - (_WIN32_WINNT >= _WIN32_WINNT_WIN8) - addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0); -#else - addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0); -#endif -#else - fd_ = ::open(path, O_RDONLY); - if (fd_ == -1) { return false; } - - struct stat sb; - if (fstat(fd_, &sb) == -1) { - close(); - return false; - } - size_ = static_cast(sb.st_size); - - addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0); -#endif - - if (addr_ == nullptr) { - close(); - return false; - } - - return true; -} - -bool mmap::is_open() const { return addr_ != nullptr; } - -size_t mmap::size() const { return size_; } - -const char *mmap::data() const { - return static_cast(addr_); -} - -void mmap::close() { -#if defined(_WIN32) - if (addr_) { - ::UnmapViewOfFile(addr_); - addr_ = nullptr; - } - - if (hMapping_) { - ::CloseHandle(hMapping_); - hMapping_ = NULL; - } - - if (hFile_ != INVALID_HANDLE_VALUE) { - ::CloseHandle(hFile_); - hFile_ = INVALID_HANDLE_VALUE; - } -#else - if (addr_ != nullptr) { - munmap(addr_, size_); - addr_ = nullptr; - } - - if (fd_ != -1) { - ::close(fd_); - fd_ = -1; - } -#endif - size_ = 0; -} int close_socket(socket_t sock) { #ifdef _WIN32 return closesocket(sock); @@ -590,7 +424,7 @@ ssize_t select_read(socket_t sock, time_t sec, time_t usec) { return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); #else #ifndef _WIN32 - if (sock >= FD_SETSIZE) { return -1; } + if (sock >= FD_SETSIZE) { return 1; } #endif fd_set fds; @@ -618,7 +452,7 @@ ssize_t select_write(socket_t sock, time_t sec, time_t usec) { return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); #else #ifndef _WIN32 - if (sock >= FD_SETSIZE) { return -1; } + if (sock >= FD_SETSIZE) { return 1; } #endif fd_set fds; @@ -703,7 +537,7 @@ bool is_socket_alive(socket_t sock) { return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0; } -class SocketStream final : public Stream { +class SocketStream : public Stream { public: SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec); @@ -728,11 +562,11 @@ private: size_t read_buff_off_ = 0; size_t read_buff_content_size_ = 0; - static const size_t read_buff_size_ = 1024l * 4; + static const size_t read_buff_size_ = 1024 * 512; }; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT -class SSLSocketStream final : public Stream { +class SSLSocketStream : public Stream { public: SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, @@ -857,7 +691,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port, #ifndef _WIN32 if (hints.ai_family == AF_UNIX) { const auto addrlen = host.length(); - if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; } + if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET; auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); if (sock != INVALID_SOCKET) { @@ -1061,7 +895,7 @@ socket_t create_client_socket( #ifdef USE_IF2IP auto ip_from_if = if2ip(address_family, intf); if (ip_from_if.empty()) { ip_from_if = intf; } - if (!bind_ip_address(sock2, ip_from_if)) { + if (!bind_ip_address(sock2, ip_from_if.c_str())) { error = Error::BindIPAddress; return false; } @@ -1113,6 +947,10 @@ socket_t create_client_socket( setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&tv), sizeof(tv)); #endif + + int const size = 1048576; + setsockopt(sock2, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + setsockopt(sock2, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); } error = Error::Success; @@ -1211,20 +1049,18 @@ constexpr unsigned int operator"" _t(const char *s, size_t l) { } // namespace udl -std::string +const char * find_content_type(const std::string &path, - const std::map &user_data, - const std::string &default_content_type) { + const std::map &user_data) { auto ext = file_extension(path); auto it = user_data.find(ext); - if (it != user_data.end()) { return it->second; } + if (it != user_data.end()) { return it->second.c_str(); } using udl::operator""_t; switch (str2tag(ext)) { - default: return default_content_type; - + default: return nullptr; case "css"_t: return "text/css"; case "csv"_t: return "text/csv"; case "htm"_t: @@ -1277,6 +1113,76 @@ find_content_type(const std::string &path, } } +const char *status_message(int status) { + switch (status) { + case 100: return "Continue"; + case 101: return "Switching Protocol"; + case 102: return "Processing"; + case 103: return "Early Hints"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 208: return "Already Reported"; + case 226: return "IM Used"; + case 300: return "Multiple Choice"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 306: return "unused"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 418: return "I'm a teapot"; + case 421: return "Misdirected Request"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 425: return "Too Early"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication Required"; + + default: + case 500: return "Internal Server Error"; + } +} + bool can_compress_content_type(const std::string &content_type) { using udl::operator""_t; @@ -1429,7 +1335,7 @@ bool gzip_decompressor::decompress(const char *data, size_t data_length, } } - if (ret != Z_OK && ret != Z_STREAM_END) { return false; } + if (ret != Z_OK && ret != Z_STREAM_END) return false; } while (data_length > 0); @@ -1568,14 +1474,11 @@ bool parse_header(const char *beg, const char *end, T fn) { } if (p < end) { - auto key_len = key_end - beg; - if (!key_len) { return false; } - auto key = std::string(beg, key_end); auto val = compare_case_ignore(key, "Location") ? std::string(p, end) : decode_url(std::string(p, end), false); - fn(key, val); + fn(std::move(key), std::move(val)); return true; } @@ -1613,8 +1516,8 @@ bool read_headers(Stream &strm, Headers &headers) { auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; parse_header(line_reader.ptr(), end, - [&](const std::string &key, const std::string &val) { - headers.emplace(key, val); + [&](std::string &&key, std::string &&val) { + headers.emplace(std::move(key), std::move(val)); }); } @@ -1660,7 +1563,11 @@ bool read_content_without_length(Stream &strm, uint64_t r = 0; for (;;) { auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); - if (n <= 0) { return true; } + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } if (!out(buf, static_cast(n), r, 0)) { return false; } r += static_cast(n); @@ -1696,7 +1603,7 @@ bool read_content_chunked(Stream &strm, T &x, if (!line_reader.getline()) { return false; } - if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; } + if (strcmp(line_reader.ptr(), "\r\n")) { return false; } if (!line_reader.getline()) { return false; } } @@ -1706,7 +1613,7 @@ bool read_content_chunked(Stream &strm, T &x, // Trailer if (!line_reader.getline()) { return false; } - while (strcmp(line_reader.ptr(), "\r\n") != 0) { + while (strcmp(line_reader.ptr(), "\r\n")) { if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } // Exclude line terminator @@ -1714,8 +1621,8 @@ bool read_content_chunked(Stream &strm, T &x, auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; parse_header(line_reader.ptr(), end, - [&](const std::string &key, const std::string &val) { - x.headers.emplace(key, val); + [&](std::string &&key, std::string &&val) { + x.headers.emplace(std::move(key), std::move(val)); }); if (!line_reader.getline()) { return false; } @@ -1725,8 +1632,8 @@ bool read_content_chunked(Stream &strm, T &x, } bool is_chunked_transfer_encoding(const Headers &headers) { - return compare_case_ignore( - get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), + "chunked"); } template @@ -1741,14 +1648,14 @@ bool prepare_content_receiver(T &x, int &status, #ifdef CPPHTTPLIB_ZLIB_SUPPORT decompressor = detail::make_unique(); #else - status = StatusCode::UnsupportedMediaType_415; + status = 415; return false; #endif } else if (encoding.find("br") != std::string::npos) { #ifdef CPPHTTPLIB_BROTLI_SUPPORT decompressor = detail::make_unique(); #else - status = StatusCode::UnsupportedMediaType_415; + status = 415; return false; #endif } @@ -1764,7 +1671,7 @@ bool prepare_content_receiver(T &x, int &status, }; return callback(std::move(out)); } else { - status = StatusCode::InternalServerError_500; + status = 500; return false; } } @@ -1792,7 +1699,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, } else if (!has_header(x.headers, "Content-Length")) { ret = read_content_without_length(strm, out); } else { - auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0); + auto len = get_header_value(x.headers, "Content-Length"); if (len > payload_max_length) { exceed_payload_max_length = true; skip_content_with_length(strm, len); @@ -1802,10 +1709,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, } } - if (!ret) { - status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 - : StatusCode::BadRequest_400; - } + if (!ret) { status = exceed_payload_max_length ? 413 : 400; } return ret; }); } // namespace detail @@ -1853,8 +1757,6 @@ bool write_content(Stream &strm, const ContentProvider &content_provider, return ok; }; - data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; - while (offset < end_offset && !is_shutting_down()) { if (!strm.is_writable()) { error = Error::Write; @@ -1899,8 +1801,6 @@ write_content_without_length(Stream &strm, return ok; }; - data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; - data_sink.done = [&](void) { data_available = false; }; while (data_available && !is_shutting_down()) { @@ -1951,8 +1851,6 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider, return ok; }; - data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; - auto done_with_trailer = [&](const Headers *trailer) { if (!ok) { return; } @@ -2037,8 +1935,7 @@ bool redirect(T &cli, Request &req, Response &res, new_req.path = path; new_req.redirect_count_ -= 1; - if (res.status == StatusCode::SeeOther_303 && - (req.method != "GET" && req.method != "HEAD")) { + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { new_req.method = "GET"; new_req.body.clear(); new_req.headers.clear(); @@ -2051,7 +1948,7 @@ bool redirect(T &cli, Request &req, Response &res, req = new_req; res = new_res; - if (res.location.empty()) { res.location = location; } + if (res.location.empty()) res.location = location; } return ret; } @@ -2068,47 +1965,9 @@ std::string params_to_query_str(const Params ¶ms) { return query; } -void parse_query_text(const char *data, std::size_t size, - Params ¶ms) { - std::set cache; - split(data, data + size, '&', [&](const char *b, const char *e) { - std::string kv(b, e); - if (cache.find(kv) != cache.end()) { return; } - cache.insert(std::move(kv)); - - std::string key; - std::string val; - divide(b, static_cast(e - b), '=', - [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data, - std::size_t rhs_size) { - key.assign(lhs_data, lhs_size); - val.assign(rhs_data, rhs_size); - }); - - if (!key.empty()) { - params.emplace(decode_url(key, true), decode_url(val, true)); - } - }); -} - void parse_query_text(const std::string &s, Params ¶ms) { - parse_query_text(s.data(), s.size(), params); -} - -bool parse_multipart_boundary(const std::string &content_type, - std::string &boundary) { - auto boundary_keyword = "boundary="; - auto pos = content_type.find(boundary_keyword); - if (pos == std::string::npos) { return false; } - auto end = content_type.find(';', pos); - auto beg = pos + strlen(boundary_keyword); - boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg)); - return !boundary.empty(); -} - -void parse_disposition_params(const std::string &s, Params ¶ms) { std::set cache; - split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) { + split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) { std::string kv(b, e); if (cache.find(kv) != cache.end()) { return; } cache.insert(kv); @@ -2124,55 +1983,60 @@ void parse_disposition_params(const std::string &s, Params ¶ms) { }); if (!key.empty()) { - params.emplace(trim_double_quotes_copy((key)), - trim_double_quotes_copy((val))); + params.emplace(decode_url(key, true), decode_url(val, true)); } }); } +bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary) { + auto boundary_keyword = "boundary="; + auto pos = content_type.find(boundary_keyword); + if (pos == std::string::npos) { return false; } + auto end = content_type.find(';', pos); + auto beg = pos + strlen(boundary_keyword); + boundary = content_type.substr(beg, end - beg); + if (boundary.length() >= 2 && boundary.front() == '"' && + boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); +} + #ifdef CPPHTTPLIB_NO_EXCEPTIONS bool parse_range_header(const std::string &s, Ranges &ranges) { #else bool parse_range_header(const std::string &s, Ranges &ranges) try { #endif - auto is_valid = [](const std::string &str) { - return std::all_of(str.cbegin(), str.cend(), - [](unsigned char c) { return std::isdigit(c); }); - }; - - if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) { - const auto pos = static_cast(6); - const auto len = static_cast(s.size() - 6); - auto all_valid_ranges = true; + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { - if (!all_valid_ranges) { return; } + if (!all_valid_ranges) return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } - const auto it = std::find(b, e, '-'); - if (it == e) { - all_valid_ranges = false; - return; + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } + + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); } - - const auto lhs = std::string(b, it); - const auto rhs = std::string(it + 1, e); - if (!is_valid(lhs) || !is_valid(rhs)) { - all_valid_ranges = false; - return; - } - - const auto first = - static_cast(lhs.empty() ? -1 : std::stoll(lhs)); - const auto last = - static_cast(rhs.empty() ? -1 : std::stoll(rhs)); - if ((first == -1 && last == -1) || - (first != -1 && last != -1 && first > last)) { - all_valid_ranges = false; - return; - } - - ranges.emplace_back(first, last); }); - return all_valid_ranges && !ranges.empty(); + return all_valid_ranges; } return false; #ifdef CPPHTTPLIB_NO_EXCEPTIONS @@ -2196,6 +2060,11 @@ public: bool parse(const char *buf, size_t n, const ContentReceiver &content_callback, const MultipartContentHeader &header_callback) { + // TODO: support 'filename*' + static const std::regex re_content_disposition( + R"~(^Content-Disposition:\s*form-data;\s*name="(.*?)"(?:;\s*filename="(.*?)")?(?:;\s*filename\*=\S+)?\s*$)~", + std::regex_constants::icase); + buf_append(buf, n); while (buf_size() > 0) { @@ -2228,54 +2097,18 @@ public: break; } + static const std::string header_name = "content-type:"; const auto header = buf_head(pos); - - if (!parse_header(header.data(), header.data() + header.size(), - [&](const std::string &, const std::string &) {})) { - is_valid_ = false; - return false; - } - - static const std::string header_content_type = "Content-Type:"; - - if (start_with_case_ignore(header, header_content_type)) { - file_.content_type = - trim_copy(header.substr(header_content_type.size())); + if (start_with_case_ignore(header, header_name)) { + file_.content_type = trim_copy(header.substr(header_name.size())); } else { - static const std::regex re_content_disposition( - R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~", - std::regex_constants::icase); - std::smatch m; if (std::regex_match(header, m, re_content_disposition)) { - Params params; - parse_disposition_params(m[1], params); - - auto it = params.find("name"); - if (it != params.end()) { - file_.name = it->second; - } else { - is_valid_ = false; - return false; - } - - it = params.find("filename"); - if (it != params.end()) { file_.filename = it->second; } - - it = params.find("filename*"); - if (it != params.end()) { - // Only allow UTF-8 enconnding... - static const std::regex re_rfc5987_encoding( - R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase); - - std::smatch m2; - if (std::regex_match(it->second, m2, re_rfc5987_encoding)) { - file_.filename = decode_url(m2[1], false); // override... - } else { - is_valid_ = false; - return false; - } - } + file_.name = m[1]; + file_.filename = m[2]; + } else { + is_valid_ = false; + return false; } } buf_erase(pos + crlf_.size()); @@ -2313,9 +2146,9 @@ public: buf_erase(crlf_.size()); state_ = 1; } else { - if (dash_.size() > buf_size()) { return true; } - if (buf_start_with(dash_)) { - buf_erase(dash_.size()); + if (dash_crlf_.size() > buf_size()) { return true; } + if (buf_start_with(dash_crlf_)) { + buf_erase(dash_crlf_.size()); is_valid_ = true; buf_erase(buf_size()); // Remove epilogue } else { @@ -2348,6 +2181,7 @@ private: const std::string dash_ = "--"; const std::string crlf_ = "\r\n"; + const std::string dash_crlf_ = "--\r\n"; std::string boundary_; std::string dash_boundary_crlf_; std::string crlf_dash_boundary_; @@ -2434,30 +2268,26 @@ std::string to_lower(const char *beg, const char *end) { return out; } -std::string random_string(size_t length) { +std::string make_multipart_data_boundary() { static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // std::random_device might actually be deterministic on some // platforms, but due to lack of support in the c++ standard library, // doing better requires either some ugly hacks or breaking portability. - static std::random_device seed_gen; + std::random_device seed_gen; // Request 128 bits of entropy for initialization - static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), - seed_gen()}; + std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()}; + std::mt19937 engine(seed_sequence); - static std::mt19937 engine(seed_sequence); + std::string result = "--cpp-httplib-multipart-data-"; - std::string result; - for (size_t i = 0; i < length; i++) { + for (auto i = 0; i < 16; i++) { result += data[engine() % (sizeof(data) - 1)]; } - return result; -} -std::string make_multipart_data_boundary() { - return "--cpp-httplib-multipart-data-" + detail::random_string(16); + return result; } bool is_multipart_boundary_chars_valid(const std::string &boundary) { @@ -2512,96 +2342,48 @@ serialize_multipart_formdata(const MultipartFormDataItems &items, body += item.content + serialize_multipart_formdata_item_end(); } - if (finish) { body += serialize_multipart_formdata_finish(boundary); } + if (finish) body += serialize_multipart_formdata_finish(boundary); return body; } -bool range_error(Request &req, Response &res) { - if (!req.ranges.empty() && 200 <= res.status && res.status < 300) { - ssize_t contant_len = static_cast( - res.content_length_ ? res.content_length_ : res.body.size()); +std::pair +get_range_offset_and_length(const Request &req, size_t content_length, + size_t index) { + auto r = req.ranges[index]; - ssize_t prev_first_pos = -1; - ssize_t prev_last_pos = -1; - size_t overwrapping_count = 0; - - // NOTE: The following Range check is based on '14.2. Range' in RFC 9110 - // 'HTTP Semantics' to avoid potential denial-of-service attacks. - // https://www.rfc-editor.org/rfc/rfc9110#section-14.2 - - // Too many ranges - if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; } - - for (auto &r : req.ranges) { - auto &first_pos = r.first; - auto &last_pos = r.second; - - if (first_pos == -1 && last_pos == -1) { - first_pos = 0; - last_pos = contant_len; - } - - if (first_pos == -1) { - first_pos = contant_len - last_pos; - last_pos = contant_len - 1; - } - - if (last_pos == -1) { last_pos = contant_len - 1; } - - // Range must be within content length - if (!(0 <= first_pos && first_pos <= last_pos && - last_pos <= contant_len - 1)) { - return true; - } - - // Ranges must be in ascending order - if (first_pos <= prev_first_pos) { return true; } - - // Request must not have more than two overlapping ranges - if (first_pos <= prev_last_pos) { - overwrapping_count++; - if (overwrapping_count > 2) { return true; } - } - - prev_first_pos = (std::max)(prev_first_pos, first_pos); - prev_last_pos = (std::max)(prev_last_pos, last_pos); - } + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); } - return false; -} + auto slen = static_cast(content_length); -std::pair -get_range_offset_and_length(Range r, size_t content_length) { - assert(r.first != -1 && r.second != -1); - assert(0 <= r.first && r.first < static_cast(content_length)); - assert(r.first <= r.second && - r.second < static_cast(content_length)); - (void)(content_length); + if (r.first == -1) { + r.first = (std::max)(static_cast(0), slen - r.second); + r.second = slen - 1; + } + + if (r.second == -1) { r.second = slen - 1; } return std::make_pair(r.first, static_cast(r.second - r.first) + 1); } -std::string make_content_range_header_field( - const std::pair &offset_and_length, size_t content_length) { - auto st = offset_and_length.first; - auto ed = st + offset_and_length.second - 1; - +std::string make_content_range_header_field(size_t offset, size_t length, + size_t content_length) { std::string field = "bytes "; - field += std::to_string(st); + field += std::to_string(offset); field += "-"; - field += std::to_string(ed); + field += std::to_string(offset + length - 1); field += "/"; field += std::to_string(content_length); return field; } template -bool process_multipart_ranges_data(const Request &req, +bool process_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary, const std::string &content_type, - size_t content_length, SToken stoken, - CToken ctoken, Content content) { + SToken stoken, CToken ctoken, + Content content) { for (size_t i = 0; i < req.ranges.size(); i++) { ctoken("--"); stoken(boundary); @@ -2612,51 +2394,50 @@ bool process_multipart_ranges_data(const Request &req, ctoken("\r\n"); } - auto offset_and_length = - get_range_offset_and_length(req.ranges[i], content_length); + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; ctoken("Content-Range: "); - stoken(make_content_range_header_field(offset_and_length, content_length)); + stoken(make_content_range_header_field(offset, length, res.body.size())); ctoken("\r\n"); ctoken("\r\n"); - - if (!content(offset_and_length.first, offset_and_length.second)) { - return false; - } + if (!content(offset, length)) { return false; } ctoken("\r\n"); } ctoken("--"); stoken(boundary); - ctoken("--"); + ctoken("--\r\n"); return true; } -void make_multipart_ranges_data(const Request &req, Response &res, +bool make_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary, const std::string &content_type, - size_t content_length, std::string &data) { - process_multipart_ranges_data( - req, boundary, content_type, content_length, + return process_multipart_ranges_data( + req, res, boundary, content_type, [&](const std::string &token) { data += token; }, [&](const std::string &token) { data += token; }, [&](size_t offset, size_t length) { - assert(offset + length <= content_length); - data += res.body.substr(offset, length); - return true; + if (offset < res.body.size()) { + data += res.body.substr(offset, length); + return true; + } + return false; }); } -size_t get_multipart_ranges_data_length(const Request &req, - const std::string &boundary, - const std::string &content_type, - size_t content_length) { +size_t +get_multipart_ranges_data_length(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type) { size_t data_length = 0; process_multipart_ranges_data( - req, boundary, content_type, content_length, + req, res, boundary, content_type, [&](const std::string &token) { data_length += token.size(); }, [&](const std::string &token) { data_length += token.size(); }, [&](size_t /*offset*/, size_t length) { @@ -2668,13 +2449,13 @@ size_t get_multipart_ranges_data_length(const Request &req, } template -bool -write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, - const std::string &boundary, - const std::string &content_type, - size_t content_length, const T &is_shutting_down) { +bool write_multipart_ranges_data(Stream &strm, const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + const T &is_shutting_down) { return process_multipart_ranges_data( - req, boundary, content_type, content_length, + req, res, boundary, content_type, [&](const std::string &token) { strm.write(token); }, [&](const std::string &token) { strm.write(token); }, [&](size_t offset, size_t length) { @@ -2683,6 +2464,18 @@ write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, }); } +std::pair +get_range_offset_and_length(const Request &req, const Response &res, + size_t index) { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + bool expect_content(const Request &req) { if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI" || req.method == "DELETE") { @@ -2952,7 +2745,7 @@ bool parse_www_authenticate(const Response &res, s = s.substr(pos + 1); auto beg = std::sregex_iterator(s.begin(), s.end(), re); for (auto i = beg; i != std::sregex_iterator(); ++i) { - const auto &m = *i; + auto m = *i; auto key = s.substr(static_cast(m.position(1)), static_cast(m.length(1))); auto val = m.length(2) > 0 @@ -2969,6 +2762,20 @@ bool parse_www_authenticate(const Response &res, return false; } +// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 +std::string random_string(size_t length) { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(std::rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; +} + class ContentProviderAdapter { public: explicit ContentProviderAdapter( @@ -3033,11 +2840,10 @@ std::string append_query_params(const std::string &path, } // Header utilities -std::pair -make_range_header(const Ranges &ranges) { +std::pair make_range_header(Ranges ranges) { std::string field = "bytes="; auto i = 0; - for (const auto &r : ranges) { + for (auto r : ranges) { if (i != 0) { field += ", "; } if (r.first != -1) { field += std::to_string(r.first); } field += '-'; @@ -3156,7 +2962,7 @@ void Response::set_redirect(const std::string &url, int stat) { if (300 <= stat && stat < 400) { this->status = stat; } else { - this->status = StatusCode::Found_302; + this->status = 302; } } } @@ -3175,22 +2981,13 @@ void Response::set_content(const std::string &s, set_content(s.data(), s.size(), content_type); } -void Response::set_content(std::string &&s, - const std::string &content_type) { - body = std::move(s); - - auto rng = headers.equal_range("Content-Type"); - headers.erase(rng.first, rng.second); - set_header("Content-Type", content_type); -} - void Response::set_content_provider( size_t in_length, const std::string &content_type, ContentProvider provider, ContentProviderResourceReleaser resource_releaser) { set_header("Content-Type", content_type); content_length_ = in_length; if (in_length > 0) { content_provider_ = std::move(provider); } - content_provider_resource_releaser_ = std::move(resource_releaser); + content_provider_resource_releaser_ = resource_releaser; is_chunked_content_provider_ = false; } @@ -3200,7 +2997,7 @@ void Response::set_content_provider( set_header("Content-Type", content_type); content_length_ = 0; content_provider_ = detail::ContentProviderAdapter(std::move(provider)); - content_provider_resource_releaser_ = std::move(resource_releaser); + content_provider_resource_releaser_ = resource_releaser; is_chunked_content_provider_ = false; } @@ -3210,7 +3007,7 @@ void Response::set_chunked_content_provider( set_header("Content-Type", content_type); content_length_ = 0; content_provider_ = detail::ContentProviderAdapter(std::move(provider)); - content_provider_resource_releaser_ = std::move(resource_releaser); + content_provider_resource_releaser_ = resource_releaser; is_chunked_content_provider_ = true; } @@ -3251,7 +3048,7 @@ SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, write_timeout_sec_(write_timeout_sec), write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {} -SocketStream::~SocketStream() = default; +SocketStream::~SocketStream() {} bool SocketStream::is_readable() const { return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; @@ -3465,7 +3262,7 @@ Server::Server() #endif } -Server::~Server() = default; +Server::~Server() {} std::unique_ptr Server::make_matcher(const std::string &pattern) { @@ -3477,60 +3274,66 @@ Server::make_matcher(const std::string &pattern) { } Server &Server::Get(const std::string &pattern, Handler handler) { - get_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + get_handlers_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Post(const std::string &pattern, Handler handler) { - post_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + post_handlers_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Post(const std::string &pattern, HandlerWithContentReader handler) { - post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); + post_handlers_for_content_reader_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Put(const std::string &pattern, Handler handler) { - put_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + put_handlers_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Put(const std::string &pattern, HandlerWithContentReader handler) { - put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); + put_handlers_for_content_reader_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Patch(const std::string &pattern, Handler handler) { - patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + patch_handlers_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Patch(const std::string &pattern, HandlerWithContentReader handler) { - patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); + patch_handlers_for_content_reader_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Delete(const std::string &pattern, Handler handler) { - delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + delete_handlers_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Delete(const std::string &pattern, HandlerWithContentReader handler) { - delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), - std::move(handler)); + delete_handlers_for_content_reader_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } Server &Server::Options(const std::string &pattern, Handler handler) { - options_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + options_handlers_.push_back( + std::make_pair(make_matcher(pattern), std::move(handler))); return *this; } @@ -3568,24 +3371,17 @@ Server::set_file_extension_and_mimetype_mapping(const std::string &ext, return *this; } -Server &Server::set_default_file_mimetype(const std::string &mime) { - default_file_mimetype_ = mime; - return *this; -} - Server &Server::set_file_request_handler(Handler handler) { file_request_handler_ = std::move(handler); return *this; } -Server &Server::set_error_handler_core(HandlerWithResponse handler, - std::true_type) { +Server &Server::set_error_handler(HandlerWithResponse handler) { error_handler_ = std::move(handler); return *this; } -Server &Server::set_error_handler_core(Handler handler, - std::false_type) { +Server &Server::set_error_handler(Handler handler) { error_handler_ = [handler](const Request &req, Response &res) { handler(req, res); return HandlerResponse::Handled; @@ -3639,12 +3435,6 @@ Server &Server::set_default_headers(Headers headers) { return *this; } -Server &Server::set_header_writer( - std::function const &writer) { - header_writer_ = writer; - return *this; -} - Server &Server::set_keep_alive_max_count(size_t count) { keep_alive_max_count_ = count; return *this; @@ -3680,7 +3470,8 @@ Server &Server::set_payload_max_length(size_t length) { bool Server::bind_to_port(const std::string &host, int port, int socket_flags) { - return bind_internal(host, port, socket_flags) >= 0; + if (bind_internal(host, port, socket_flags) < 0) return false; + return true; } int Server::bind_to_any_port(const std::string &host, int socket_flags) { return bind_internal(host, 0, socket_flags); @@ -3714,7 +3505,7 @@ void Server::stop() { } } -bool Server::parse_request_line(const char *s, Request &req) const { +bool Server::parse_request_line(const char *s, Request &req) { auto len = strlen(s); if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; } len -= 2; @@ -3752,23 +3543,33 @@ bool Server::parse_request_line(const char *s, Request &req) const { } } - detail::divide(req.target, '?', - [&](const char *lhs_data, std::size_t lhs_size, - const char *rhs_data, std::size_t rhs_size) { - req.path = detail::decode_url( - std::string(lhs_data, lhs_size), false); - detail::parse_query_text(rhs_data, rhs_size, req.params); - }); + size_t count = 0; + + detail::split(req.target.data(), req.target.data() + req.target.size(), '?', + [&](const char *b, const char *e) { + switch (count) { + case 0: + req.path = detail::decode_url(std::string(b, e), false); + break; + case 1: { + if (e - b > 0) { + detail::parse_query_text(std::string(b, e), req.params); + } + break; + } + default: break; + } + count++; + }); + + if (count > 2) { return false; } } return true; } bool Server::write_response(Stream &strm, bool close_connection, - Request &req, Response &res) { - // NOTE: `req.ranges` should be empty, otherwise it will be applied - // incorrectly to the error content. - req.ranges.clear(); + const Request &req, Response &res) { return write_response_core(strm, close_connection, req, res, false); } @@ -3824,11 +3625,11 @@ bool Server::write_response_core(Stream &strm, bool close_connection, detail::BufferStream bstrm; if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, - http_status_message(res.status))) { + detail::status_message(res.status))) { return false; } - if (!header_writer_(bstrm, res.headers)) { return false; } + if (!detail::write_headers(bstrm, res.headers)) { return false; } // Flush buffer auto &data = bstrm.get_buffer(); @@ -3846,6 +3647,7 @@ bool Server::write_response_core(Stream &strm, bool close_connection, if (write_content_with_provider(strm, req, res, boundary, content_type)) { res.content_provider_success_ = true; } else { + res.content_provider_success_ = false; ret = false; } } @@ -3870,16 +3672,15 @@ Server::write_content_with_provider(Stream &strm, const Request &req, return detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down); } else if (req.ranges.size() == 1) { - auto offset_and_length = detail::get_range_offset_and_length( - req.ranges[0], res.content_length_); - - return detail::write_content(strm, res.content_provider_, - offset_and_length.first, - offset_and_length.second, is_shutting_down); + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + return detail::write_content(strm, res.content_provider_, offset, length, + is_shutting_down); } else { return detail::write_multipart_ranges_data( - strm, req, res, boundary, content_type, res.content_length_, - is_shutting_down); + strm, req, res, boundary, content_type, is_shutting_down); } } else { if (res.is_chunked_content_provider_) { @@ -3936,7 +3737,7 @@ bool Server::read_content(Stream &strm, Request &req, Response &res) { const auto &content_type = req.get_header_value("Content-Type"); if (!content_type.find("application/x-www-form-urlencoded")) { if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) { - res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414? + res.status = 413; // NOTE: should be 414? return false; } detail::parse_query_text(req.body, req.params); @@ -3955,11 +3756,10 @@ bool Server::read_content_with_content_receiver( std::move(multipart_receiver)); } -bool -Server::read_content_core(Stream &strm, Request &req, Response &res, - ContentReceiver receiver, - MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) const { +bool Server::read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { detail::MultipartFormDataParser multipart_form_data_parser; ContentReceiverWithProgress out; @@ -3967,7 +3767,7 @@ Server::read_content_core(Stream &strm, Request &req, Response &res, const auto &content_type = req.get_header_value("Content-Type"); std::string boundary; if (!detail::parse_multipart_boundary(content_type, boundary)) { - res.status = StatusCode::BadRequest_400; + res.status = 400; return false; } @@ -4003,7 +3803,7 @@ Server::read_content_core(Stream &strm, Request &req, Response &res, if (req.is_multipart_form_data()) { if (!multipart_form_data_parser.is_valid()) { - res.status = StatusCode::BadRequest_400; + res.status = 400; return false; } } @@ -4022,26 +3822,17 @@ bool Server::handle_file_request(const Request &req, Response &res, if (path.back() == '/') { path += "index.html"; } if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = + detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { res.set_header("Content-Type", type); } for (const auto &kv : entry.headers) { - res.set_header(kv.first, kv.second); + res.set_header(kv.first.c_str(), kv.second); } - - auto mm = std::make_shared(path.c_str()); - if (!mm->is_open()) { return false; } - - res.set_content_provider( - mm->size(), - detail::find_content_type(path, file_extension_and_mimetype_map_, - default_file_mimetype_), - [mm](size_t offset, size_t length, DataSink &sink) -> bool { - sink.write(mm->data() + offset, length); - return true; - }); - + res.status = req.has_header("Range") ? 206 : 200; if (!head && file_request_handler_) { file_request_handler_(req, res); } - return true; } } @@ -4163,11 +3954,7 @@ bool Server::listen_internal() { #endif } - if (!task_queue->enqueue( - [this, sock]() { process_and_close_socket(sock); })) { - detail::shutdown_socket(sock); - detail::close_socket(sock); - } + task_queue->enqueue([this, sock]() { process_and_close_socket(sock); }); } task_queue->shutdown(); @@ -4249,12 +4036,12 @@ bool Server::routing(Request &req, Response &res, Stream &strm) { return dispatch_request(req, res, patch_handlers_); } - res.status = StatusCode::BadRequest_400; + res.status = 400; return false; } bool Server::dispatch_request(Request &req, Response &res, - const Handlers &handlers) const { + const Handlers &handlers) { for (const auto &x : handlers) { const auto &matcher = x.first; const auto &handler = x.second; @@ -4269,16 +4056,16 @@ bool Server::dispatch_request(Request &req, Response &res, void Server::apply_ranges(const Request &req, Response &res, std::string &content_type, - std::string &boundary) const { - if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) { + std::string &boundary) { + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); + auto it = res.headers.find("Content-Type"); if (it != res.headers.end()) { content_type = it->second; res.headers.erase(it); } - boundary = detail::make_multipart_data_boundary(); - res.set_header("Content-Type", "multipart/byteranges; boundary=" + boundary); } @@ -4288,20 +4075,19 @@ void Server::apply_ranges(const Request &req, Response &res, if (res.body.empty()) { if (res.content_length_ > 0) { size_t length = 0; - if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) { + if (req.ranges.empty()) { length = res.content_length_; } else if (req.ranges.size() == 1) { - auto offset_and_length = detail::get_range_offset_and_length( - req.ranges[0], res.content_length_); - - length = offset_and_length.second; - + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; auto content_range = detail::make_content_range_header_field( - offset_and_length, res.content_length_); + offset, length, res.content_length_); res.set_header("Content-Range", content_range); } else { - length = detail::get_multipart_ranges_data_length( - req, boundary, content_type, res.content_length_); + length = detail::get_multipart_ranges_data_length(req, res, boundary, + content_type); } res.set_header("Content-Length", std::to_string(length)); } else { @@ -4317,25 +4103,31 @@ void Server::apply_ranges(const Request &req, Response &res, } } } else { - if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) { + if (req.ranges.empty()) { ; } else if (req.ranges.size() == 1) { - auto offset_and_length = - detail::get_range_offset_and_length(req.ranges[0], res.body.size()); - auto offset = offset_and_length.first; - auto length = offset_and_length.second; - + auto offsets = + detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; auto content_range = detail::make_content_range_header_field( - offset_and_length, res.body.size()); + offset, length, res.body.size()); res.set_header("Content-Range", content_range); - - assert(offset + length <= res.body.size()); - res.body = res.body.substr(offset, length); + if (offset < res.body.size()) { + res.body = res.body.substr(offset, length); + } else { + res.body.clear(); + res.status = 416; + } } else { std::string data; - detail::make_multipart_ranges_data(req, res, boundary, content_type, - res.body.size(), data); - res.body.swap(data); + if (detail::make_multipart_ranges_data(req, res, boundary, content_type, + data)) { + res.body.swap(data); + } else { + res.body.clear(); + res.status = 416; + } } if (type != detail::EncodingType::None) { @@ -4374,7 +4166,7 @@ void Server::apply_ranges(const Request &req, Response &res, bool Server::dispatch_request_for_content_reader( Request &req, Response &res, ContentReader content_reader, - const HandlersForContentReader &handlers) const { + const HandlersForContentReader &handlers) { for (const auto &x : handlers) { const auto &matcher = x.first; const auto &handler = x.second; @@ -4412,7 +4204,7 @@ Server::process_request(Stream &strm, bool close_connection, if (strm.socket() >= FD_SETSIZE) { Headers dummy; detail::read_headers(strm, dummy); - res.status = StatusCode::InternalServerError_500; + res.status = 500; return write_response(strm, close_connection, req, res); } #endif @@ -4422,14 +4214,14 @@ Server::process_request(Stream &strm, bool close_connection, if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { Headers dummy; detail::read_headers(strm, dummy); - res.status = StatusCode::UriTooLong_414; + res.status = 414; return write_response(strm, close_connection, req, res); } // Request line and headers if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { - res.status = StatusCode::BadRequest_400; + res.status = 400; return write_response(strm, close_connection, req, res); } @@ -4453,7 +4245,7 @@ Server::process_request(Stream &strm, bool close_connection, if (req.has_header("Range")) { const auto &range_header_value = req.get_header_value("Range"); if (!detail::parse_range_header(range_header_value, req.ranges)) { - res.status = StatusCode::RangeNotSatisfiable_416; + res.status = 416; return write_response(strm, close_connection, req, res); } } @@ -4461,22 +4253,22 @@ Server::process_request(Stream &strm, bool close_connection, if (setup_request) { setup_request(req); } if (req.get_header_value("Expect") == "100-continue") { - int status = StatusCode::Continue_100; + auto status = 100; if (expect_100_continue_handler_) { status = expect_100_continue_handler_(req, res); } switch (status) { - case StatusCode::Continue_100: - case StatusCode::ExpectationFailed_417: + case 100: + case 417: strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, - http_status_message(status)); + detail::status_message(status)); break; default: return write_response(strm, close_connection, req, res); } } - // Routing - auto routed = false; + // Rounting + bool routed = false; #ifdef CPPHTTPLIB_NO_EXCEPTIONS routed = routing(req, res, strm); #else @@ -4488,7 +4280,7 @@ Server::process_request(Stream &strm, bool close_connection, exception_handler_(req, res, ep); routed = true; } else { - res.status = StatusCode::InternalServerError_500; + res.status = 500; std::string val; auto s = e.what(); for (size_t i = 0; s[i]; i++) { @@ -4506,29 +4298,17 @@ Server::process_request(Stream &strm, bool close_connection, exception_handler_(req, res, ep); routed = true; } else { - res.status = StatusCode::InternalServerError_500; + res.status = 500; res.set_header("EXCEPTION_WHAT", "UNKNOWN"); } } #endif + if (routed) { - if (res.status == -1) { - res.status = req.ranges.empty() ? StatusCode::OK_200 - : StatusCode::PartialContent_206; - } - - if (detail::range_error(req, res)) { - res.body.clear(); - res.content_length_ = 0; - res.content_provider_ = nullptr; - res.status = StatusCode::RangeNotSatisfiable_416; - return write_response(strm, close_connection, req, res); - } - + if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } return write_response_with_content(strm, close_connection, req, res); } else { - if (res.status == -1) { res.status = StatusCode::NotFound_404; } - + if (res.status == -1) { res.status = 404; } return write_response(strm, close_connection, req, res); } } @@ -4628,7 +4408,7 @@ socket_t ClientImpl::create_client_socket(Error &error) const { // Check is custom IP specified for host_ std::string ip; auto it = addr_map_.find(host_); - if (it != addr_map_.end()) { ip = it->second; } + if (it != addr_map_.end()) ip = it->second; return detail::create_client_socket( host_, ip, port_, address_family_, tcp_nodelay_, socket_options_, @@ -4653,7 +4433,7 @@ void ClientImpl::shutdown_ssl(Socket & /*socket*/, socket_requests_are_from_thread_ == std::this_thread::get_id()); } -void ClientImpl::shutdown_socket(Socket &socket) const { +void ClientImpl::shutdown_socket(Socket &socket) { if (socket.sock == INVALID_SOCKET) { return; } detail::shutdown_socket(socket.sock); } @@ -4678,7 +4458,7 @@ void ClientImpl::close_socket(Socket &socket) { } bool ClientImpl::read_response_line(Stream &strm, const Request &req, - Response &res) const { + Response &res) { std::array buf{}; detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); @@ -4686,9 +4466,9 @@ bool ClientImpl::read_response_line(Stream &strm, const Request &req, if (!line_reader.getline()) { return false; } #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR - const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n"); -#else const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); +#else + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n"); #endif std::cmatch m; @@ -4700,7 +4480,7 @@ bool ClientImpl::read_response_line(Stream &strm, const Request &req, res.reason = std::string(m[3]); // Ignore '100 Continue' - while (res.status == StatusCode::Continue_100) { + while (res.status == 100) { if (!line_reader.getline()) { return false; } // CRLF if (!line_reader.getline()) { return false; } // next response line @@ -4848,31 +4628,15 @@ bool ClientImpl::handle_request(Stream &strm, Request &req, if (!ret) { return false; } - if (res.get_header_value("Connection") == "close" || - (res.version == "HTTP/1.0" && res.reason != "Connection established")) { - // TODO this requires a not-entirely-obvious chain of calls to be correct - // for this to be safe. - - // This is safe to call because handle_request is only called by send_ - // which locks the request mutex during the process. It would be a bug - // to call it from a different thread since it's a thread-safety issue - // to do these things to the socket if another thread is using the socket. - std::lock_guard guard(socket_mutex_); - shutdown_ssl(socket_, true); - shutdown_socket(socket_); - close_socket(socket_); - } - if (300 < res.status && res.status < 400 && follow_location_) { req = req_save; ret = redirect(req, res, error); } #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if ((res.status == StatusCode::Unauthorized_401 || - res.status == StatusCode::ProxyAuthenticationRequired_407) && + if ((res.status == 401 || res.status == 407) && req.authorization_count_ < 5) { - auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407; + auto is_proxy = res.status == 407; const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; const auto &password = @@ -4911,7 +4675,7 @@ bool ClientImpl::redirect(Request &req, Response &res, Error &error) { if (location.empty()) { return false; } const static std::regex re( - R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)"); + R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)"); std::smatch m; if (!std::regex_match(location, m, re)) { return false; } @@ -4943,7 +4707,7 @@ bool ClientImpl::redirect(Request &req, Response &res, Error &error) { } else { if (next_scheme == "https") { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLClient cli(next_host, next_port); + SSLClient cli(next_host.c_str(), next_port); cli.copy_settings(*this); if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); } return detail::redirect(cli, req, res, path, location, error); @@ -4951,7 +4715,7 @@ bool ClientImpl::redirect(Request &req, Response &res, Error &error) { return false; #endif } else { - ClientImpl cli(next_host, next_port); + ClientImpl cli(next_host.c_str(), next_port); cli.copy_settings(*this); return detail::redirect(cli, req, res, path, location, error); } @@ -4960,7 +4724,7 @@ bool ClientImpl::redirect(Request &req, Response &res, Error &error) { bool ClientImpl::write_content_with_provider(Stream &strm, const Request &req, - Error &error) const { + Error &error) { auto is_shutting_down = []() { return false; }; if (req.is_chunked_content_provider_) { @@ -5078,7 +4842,7 @@ bool ClientImpl::write_request(Stream &strm, Request &req, const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); - header_writer_(bstrm, req.headers); + detail::write_headers(bstrm, req.headers); // Flush buffer auto &data = bstrm.get_buffer(); @@ -5184,12 +4948,11 @@ Result ClientImpl::send_with_content_provider( const std::string &method, const std::string &path, const Headers &headers, const char *body, size_t content_length, ContentProvider content_provider, ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type, Progress progress) { + const std::string &content_type) { Request req; req.method = method; req.headers = headers; req.path = path; - req.progress = progress; auto error = Error::Success; @@ -5234,8 +4997,7 @@ bool ClientImpl::process_request(Stream &strm, Request &req, } // Body - if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" && - req.method != "CONNECT") { + if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") { auto redirect = 300 < res.status && res.status < 400 && follow_location_; if (req.response_handler && !redirect) { @@ -5280,6 +5042,24 @@ bool ClientImpl::process_request(Stream &strm, Request &req, } } + if (res.get_header_value("Connection") == "close" || + (res.version == "HTTP/1.0" && res.reason != "Connection established")) { + // TODO this requires a not-entirely-obvious chain of calls to be correct + // for this to be safe. Maybe a code refactor (such as moving this out to + // the send function and getting rid of the recursiveness of the mutex) + // could make this more obvious. + + // This is safe to call because process_request is only called by + // handle_request which is only called by send, which locks the request + // mutex during the process. It would be a bug to call it from a different + // thread since it's a thread-safety issue to do these things to the socket + // if another thread is using the socket. + std::lock_guard guard(socket_mutex_); + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + // Log if (logger_) { logger_(req, res); } @@ -5288,14 +5068,13 @@ bool ClientImpl::process_request(Stream &strm, Request &req, ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( const std::string &boundary, const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) const { - size_t cur_item = 0; - size_t cur_start = 0; + const MultipartFormDataProviderItems &provider_items) { + size_t cur_item = 0, cur_start = 0; // cur_item and cur_start are copied to within the std::function and maintain // state between successive calls return [&, cur_item, cur_start](size_t offset, DataSink &sink) mutable -> bool { - if (!offset && !items.empty()) { + if (!offset && items.size()) { sink.os << detail::serialize_multipart_formdata(items, boundary, false); return true; } else if (cur_item < provider_items.size()) { @@ -5308,13 +5087,12 @@ ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( } DataSink cur_sink; - auto has_data = true; + bool has_data = true; cur_sink.write = sink.write; cur_sink.done = [&]() { has_data = false; }; - if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) { + if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) return false; - } if (!has_data) { sink.os << detail::serialize_multipart_formdata_item_end(); @@ -5433,15 +5211,14 @@ Result ClientImpl::Get(const std::string &path, const Params ¶ms, if (params.empty()) { return Get(path, headers); } std::string path_with_query = append_query_params(path, params); - return Get(path_with_query, headers, std::move(progress)); + return Get(path_with_query.c_str(), headers, progress); } Result ClientImpl::Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, Progress progress) { - return Get(path, params, headers, nullptr, std::move(content_receiver), - std::move(progress)); + return Get(path, params, headers, nullptr, content_receiver, progress); } Result ClientImpl::Get(const std::string &path, const Params ¶ms, @@ -5450,13 +5227,12 @@ Result ClientImpl::Get(const std::string &path, const Params ¶ms, ContentReceiver content_receiver, Progress progress) { if (params.empty()) { - return Get(path, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); + return Get(path, headers, response_handler, content_receiver, progress); } std::string path_with_query = append_query_params(path, params); - return Get(path_with_query, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); + return Get(path_with_query.c_str(), headers, response_handler, + content_receiver, progress); } Result ClientImpl::Head(const std::string &path) { @@ -5485,22 +5261,14 @@ Result ClientImpl::Post(const std::string &path, Result ClientImpl::Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type) { - return Post(path, Headers(), body, content_length, content_type, nullptr); + return Post(path, Headers(), body, content_length, content_type); } Result ClientImpl::Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return send_with_content_provider("POST", path, headers, body, content_length, - nullptr, nullptr, content_type, nullptr); -} - -Result ClientImpl::Post(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, - Progress progress) { - return send_with_content_provider("POST", path, headers, body, content_length, - nullptr, nullptr, content_type, progress); + nullptr, nullptr, content_type); } Result ClientImpl::Post(const std::string &path, const std::string &body, @@ -5508,27 +5276,12 @@ Result ClientImpl::Post(const std::string &path, const std::string &body, return Post(path, Headers(), body, content_type); } -Result ClientImpl::Post(const std::string &path, const std::string &body, - const std::string &content_type, - Progress progress) { - return Post(path, Headers(), body, content_type, progress); -} - Result ClientImpl::Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return send_with_content_provider("POST", path, headers, body.data(), - body.size(), nullptr, nullptr, content_type, - nullptr); -} - -Result ClientImpl::Post(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, - Progress progress) { - return send_with_content_provider("POST", path, headers, body.data(), - body.size(), nullptr, nullptr, content_type, - progress); + body.size(), nullptr, nullptr, + content_type); } Result ClientImpl::Post(const std::string &path, const Params ¶ms) { @@ -5554,15 +5307,14 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, const std::string &content_type) { return send_with_content_provider("POST", path, headers, nullptr, content_length, std::move(content_provider), - nullptr, content_type, nullptr); + nullptr, content_type); } Result ClientImpl::Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, - std::move(content_provider), content_type, - nullptr); + std::move(content_provider), content_type); } Result ClientImpl::Post(const std::string &path, const Headers &headers, @@ -5571,13 +5323,6 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, return Post(path, headers, query, "application/x-www-form-urlencoded"); } -Result ClientImpl::Post(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress) { - auto query = detail::params_to_query_str(params); - return Post(path, headers, query, "application/x-www-form-urlencoded", - progress); -} - Result ClientImpl::Post(const std::string &path, const MultipartFormDataItems &items) { return Post(path, Headers(), items); @@ -5589,7 +5334,7 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary); const auto &body = detail::serialize_multipart_formdata(items, boundary); - return Post(path, headers, body, content_type); + return Post(path, headers, body, content_type.c_str()); } Result ClientImpl::Post(const std::string &path, const Headers &headers, @@ -5602,7 +5347,7 @@ Result ClientImpl::Post(const std::string &path, const Headers &headers, const auto &content_type = detail::serialize_multipart_formdata_get_content_type(boundary); const auto &body = detail::serialize_multipart_formdata(items, boundary); - return Post(path, headers, body, content_type); + return Post(path, headers, body, content_type.c_str()); } Result @@ -5615,7 +5360,7 @@ ClientImpl::Post(const std::string &path, const Headers &headers, return send_with_content_provider( "POST", path, headers, nullptr, 0, nullptr, get_multipart_content_provider(boundary, items, provider_items), - content_type, nullptr); + content_type); } Result ClientImpl::Put(const std::string &path) { @@ -5632,15 +5377,7 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, body, content_length, - nullptr, nullptr, content_type, nullptr); -} - -Result ClientImpl::Put(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, - Progress progress) { - return send_with_content_provider("PUT", path, headers, body, content_length, - nullptr, nullptr, content_type, progress); + nullptr, nullptr, content_type); } Result ClientImpl::Put(const std::string &path, const std::string &body, @@ -5648,27 +5385,12 @@ Result ClientImpl::Put(const std::string &path, const std::string &body, return Put(path, Headers(), body, content_type); } -Result ClientImpl::Put(const std::string &path, const std::string &body, - const std::string &content_type, - Progress progress) { - return Put(path, Headers(), body, content_type, progress); -} - Result ClientImpl::Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, body.data(), - body.size(), nullptr, nullptr, content_type, - nullptr); -} - -Result ClientImpl::Put(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, - Progress progress) { - return send_with_content_provider("PUT", path, headers, body.data(), - body.size(), nullptr, nullptr, content_type, - progress); + body.size(), nullptr, nullptr, + content_type); } Result ClientImpl::Put(const std::string &path, size_t content_length, @@ -5690,15 +5412,14 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, nullptr, content_length, std::move(content_provider), - nullptr, content_type, nullptr); + nullptr, content_type); } Result ClientImpl::Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, - std::move(content_provider), content_type, - nullptr); + std::move(content_provider), content_type); } Result ClientImpl::Put(const std::string &path, const Params ¶ms) { @@ -5711,13 +5432,6 @@ Result ClientImpl::Put(const std::string &path, const Headers &headers, return Put(path, headers, query, "application/x-www-form-urlencoded"); } -Result ClientImpl::Put(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress) { - auto query = detail::params_to_query_str(params); - return Put(path, headers, query, "application/x-www-form-urlencoded", - progress); -} - Result ClientImpl::Put(const std::string &path, const MultipartFormDataItems &items) { return Put(path, Headers(), items); @@ -5755,7 +5469,7 @@ ClientImpl::Put(const std::string &path, const Headers &headers, return send_with_content_provider( "PUT", path, headers, nullptr, 0, nullptr, get_multipart_content_provider(boundary, items, provider_items), - content_type, nullptr); + content_type); } Result ClientImpl::Patch(const std::string &path) { return Patch(path, std::string(), std::string()); @@ -5767,26 +5481,12 @@ Result ClientImpl::Patch(const std::string &path, const char *body, return Patch(path, Headers(), body, content_length, content_type); } -Result ClientImpl::Patch(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type, - Progress progress) { - return Patch(path, Headers(), body, content_length, content_type, progress); -} - Result ClientImpl::Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { - return Patch(path, headers, body, content_length, content_type, nullptr); -} - -Result ClientImpl::Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, - Progress progress) { return send_with_content_provider("PATCH", path, headers, body, content_length, nullptr, nullptr, - content_type, progress); + content_type); } Result ClientImpl::Patch(const std::string &path, @@ -5795,26 +5495,12 @@ Result ClientImpl::Patch(const std::string &path, return Patch(path, Headers(), body, content_type); } -Result ClientImpl::Patch(const std::string &path, - const std::string &body, - const std::string &content_type, - Progress progress) { - return Patch(path, Headers(), body, content_type, progress); -} - Result ClientImpl::Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { - return Patch(path, headers, body, content_type, nullptr); -} - -Result ClientImpl::Patch(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, - Progress progress) { return send_with_content_provider("PATCH", path, headers, body.data(), - body.size(), nullptr, nullptr, content_type, - progress); + body.size(), nullptr, nullptr, + content_type); } Result ClientImpl::Patch(const std::string &path, size_t content_length, @@ -5836,15 +5522,14 @@ Result ClientImpl::Patch(const std::string &path, const Headers &headers, const std::string &content_type) { return send_with_content_provider("PATCH", path, headers, nullptr, content_length, std::move(content_provider), - nullptr, content_type, nullptr); + nullptr, content_type); } Result ClientImpl::Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type) { return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, - std::move(content_provider), content_type, - nullptr); + std::move(content_provider), content_type); } Result ClientImpl::Delete(const std::string &path) { @@ -5862,30 +5547,14 @@ Result ClientImpl::Delete(const std::string &path, const char *body, return Delete(path, Headers(), body, content_length, content_type); } -Result ClientImpl::Delete(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type, - Progress progress) { - return Delete(path, Headers(), body, content_length, content_type, progress); -} - Result ClientImpl::Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { - return Delete(path, headers, body, content_length, content_type, nullptr); -} - -Result ClientImpl::Delete(const std::string &path, - const Headers &headers, const char *body, - size_t content_length, - const std::string &content_type, - Progress progress) { Request req; req.method = "DELETE"; req.headers = headers; req.path = path; - req.progress = progress; if (!content_type.empty()) { req.set_header("Content-Type", content_type); } req.body.assign(body, content_length); @@ -5899,14 +5568,6 @@ Result ClientImpl::Delete(const std::string &path, return Delete(path, Headers(), body.data(), body.size(), content_type); } -Result ClientImpl::Delete(const std::string &path, - const std::string &body, - const std::string &content_type, - Progress progress) { - return Delete(path, Headers(), body.data(), body.size(), content_type, - progress); -} - Result ClientImpl::Delete(const std::string &path, const Headers &headers, const std::string &body, @@ -5914,15 +5575,6 @@ Result ClientImpl::Delete(const std::string &path, return Delete(path, headers, body.data(), body.size(), content_type); } -Result ClientImpl::Delete(const std::string &path, - const Headers &headers, - const std::string &body, - const std::string &content_type, - Progress progress) { - return Delete(path, headers, body.data(), body.size(), content_type, - progress); -} - Result ClientImpl::Options(const std::string &path) { return Options(path, Headers()); } @@ -6019,11 +5671,6 @@ void ClientImpl::set_default_headers(Headers headers) { default_headers_ = std::move(headers); } -void ClientImpl::set_header_writer( - std::function const &writer) { - header_writer_ = writer; -} - void ClientImpl::set_address_family(int family) { address_family_ = family; } @@ -6077,9 +5724,9 @@ void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { } X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, - std::size_t size) const { + std::size_t size) { auto mem = BIO_new_mem_buf(ca_cert, static_cast(size)); - if (!mem) { return nullptr; } + if (!mem) return nullptr; auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr); if (!inf) { @@ -6149,29 +5796,13 @@ SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, return ssl; } -void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock, +void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, bool shutdown_gracefully) { // sometimes we may want to skip this to try to avoid SIGPIPE if we know // the remote has closed the network connection // Note that it is not always possible to avoid SIGPIPE, this is merely a // best-efforts. - if (shutdown_gracefully) { -#ifdef _WIN32 - SSL_shutdown(ssl); -#else - timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, - reinterpret_cast(&tv), sizeof(tv)); - - auto ret = SSL_shutdown(ssl); - while (ret == 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - ret = SSL_shutdown(ssl); - } -#endif - } + if (shutdown_gracefully) { SSL_shutdown(ssl); } std::lock_guard guard(ctx_mutex); SSL_free(ssl); @@ -6245,7 +5876,7 @@ SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); } -SSLSocketStream::~SSLSocketStream() = default; +SSLSocketStream::~SSLSocketStream() {} bool SSLSocketStream::is_readable() const { return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; @@ -6347,8 +5978,9 @@ SSLServer::SSLServer(const char *cert_path, const char *private_key_path, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION); + SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); + // add default password callback before opening encrypted private key if (private_key_password != nullptr && (private_key_password[0] != '\0')) { SSL_CTX_set_default_passwd_cb_userdata( ctx_, @@ -6379,7 +6011,7 @@ SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION); + SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); if (SSL_CTX_use_certificate(ctx_, cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { @@ -6413,19 +6045,6 @@ bool SSLServer::is_valid() const { return ctx_; } SSL_CTX *SSLServer::ssl_context() const { return ctx_; } -void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store) { - - std::lock_guard guard(ctx_mutex_); - - SSL_CTX_use_certificate(ctx_, cert); - SSL_CTX_use_PrivateKey(ctx_, private_key); - - if (client_ca_cert_store != nullptr) { - SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); - } -} - bool SSLServer::process_and_close_socket(socket_t sock) { auto ssl = detail::ssl_new( sock, ctx_, ctx_mutex_, @@ -6450,7 +6069,7 @@ bool SSLServer::process_and_close_socket(socket_t sock) { // Shutdown gracefully if the result seemed successful, non-gracefully if // the connection appeared to be closed. const bool shutdown_gracefully = ret; - detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully); + detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully); } detail::shutdown_socket(sock); @@ -6467,23 +6086,16 @@ SSLClient::SSLClient(const std::string &host, int port) SSLClient::SSLClient(const std::string &host, int port, const std::string &client_cert_path, - const std::string &client_key_path, - const std::string &private_key_password) + const std::string &client_key_path) : ClientImpl(host, port, client_cert_path, client_key_path) { ctx_ = SSL_CTX_new(TLS_client_method()); detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { - host_components_.emplace_back(b, e); + host_components_.emplace_back(std::string(b, e)); }); if (!client_cert_path.empty() && !client_key_path.empty()) { - if (!private_key_password.empty()) { - SSL_CTX_set_default_passwd_cb_userdata( - ctx_, reinterpret_cast( - const_cast(private_key_password.c_str()))); - } - if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 || SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), @@ -6495,23 +6107,16 @@ SSLClient::SSLClient(const std::string &host, int port, } SSLClient::SSLClient(const std::string &host, int port, - X509 *client_cert, EVP_PKEY *client_key, - const std::string &private_key_password) + X509 *client_cert, EVP_PKEY *client_key) : ClientImpl(host, port) { ctx_ = SSL_CTX_new(TLS_client_method()); detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { - host_components_.emplace_back(b, e); + host_components_.emplace_back(std::string(b, e)); }); if (client_cert != nullptr && client_key != nullptr) { - if (!private_key_password.empty()) { - SSL_CTX_set_default_passwd_cb_userdata( - ctx_, reinterpret_cast( - const_cast(private_key_password.c_str()))); - } - if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { SSL_CTX_free(ctx_); @@ -6562,14 +6167,14 @@ bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { bool SSLClient::connect_with_proxy(Socket &socket, Response &res, bool &success, Error &error) { success = true; - Response proxy_res; + Response res2; if (!detail::process_client_socket( socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { Request req2; req2.method = "CONNECT"; req2.path = host_and_port_; - return process_request(strm, req2, proxy_res, false, error); + return process_request(strm, req2, res2, false, error); })) { // Thread-safe to close everything because we are assuming there are no // requests in flight @@ -6580,12 +6185,12 @@ bool SSLClient::connect_with_proxy(Socket &socket, Response &res, return false; } - if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) { + if (res2.status == 407) { if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) { std::map auth; - if (detail::parse_www_authenticate(proxy_res, auth, true)) { - proxy_res = Response(); + if (detail::parse_www_authenticate(res2, auth, true)) { + Response res3; if (!detail::process_client_socket( socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { @@ -6596,7 +6201,7 @@ bool SSLClient::connect_with_proxy(Socket &socket, Response &res, req3, auth, 1, detail::random_string(10), proxy_digest_auth_username_, proxy_digest_auth_password_, true)); - return process_request(strm, req3, proxy_res, false, error); + return process_request(strm, req3, res3, false, error); })) { // Thread-safe to close everything because we are assuming there are // no requests in flight @@ -6607,28 +6212,17 @@ bool SSLClient::connect_with_proxy(Socket &socket, Response &res, return false; } } + } else { + res = res2; + return false; } } - // If status code is not 200, proxy request is failed. - // Set error to ProxyConnection and return proxy response - // as the response of the request - if (proxy_res.status != StatusCode::OK_200) { - error = Error::ProxyConnection; - res = std::move(proxy_res); - // Thread-safe to close everything because we are assuming there are - // no requests in flight - shutdown_ssl(socket, true); - shutdown_socket(socket); - close_socket(socket); - return false; - } - return true; } bool SSLClient::load_certs() { - auto ret = true; + bool ret = true; std::call_once(initialize_cert_, [&]() { std::lock_guard guard(ctx_mutex_); @@ -6704,11 +6298,11 @@ bool SSLClient::initialize_ssl(Socket &socket, Error &error) { return true; }, [&](SSL *ssl2) { - // NOTE: Direct call instead of using the OpenSSL macro to suppress - // -Wold-style-cast warning - // SSL_set_tlsext_host_name(ssl2, host_.c_str()); - SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, - static_cast(const_cast(host_.c_str()))); + // NOTE: With -Wold-style-cast, this can produce a warning, since + // SSL_set_tlsext_host_name is a macro (in OpenSSL), which contains + // an old style cast. Short of doing compiler specific pragma's + // here, we can't get rid of this warning. :'( + SSL_set_tlsext_host_name(ssl2, host_.c_str()); return true; }); @@ -6733,8 +6327,7 @@ void SSLClient::shutdown_ssl_impl(Socket &socket, return; } if (socket.ssl) { - detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock, - shutdown_gracefully); + detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully); socket.ssl = nullptr; } assert(socket.ssl == nullptr); @@ -6783,8 +6376,8 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { auto type = GEN_DNS; - struct in6_addr addr6 {}; - struct in_addr addr {}; + struct in6_addr addr6; + struct in_addr addr; size_t addr_len = 0; #ifndef __MINGW32__ @@ -6859,7 +6452,7 @@ bool SSLClient::check_host_name(const char *pattern, std::vector pattern_components; detail::split(&pattern[0], &pattern[pattern_len], '.', [&](const char *b, const char *e) { - pattern_components.emplace_back(b, e); + pattern_components.emplace_back(std::string(b, e)); }); if (host_components_.size() != pattern_components.size()) { return false; } @@ -6887,7 +6480,7 @@ Client::Client(const std::string &scheme_host_port, const std::string &client_cert_path, const std::string &client_key_path) { const static std::regex re( - R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); + R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); std::smatch m; if (std::regex_match(scheme_host_port, m, re)) { @@ -6924,8 +6517,6 @@ Client::Client(const std::string &scheme_host_port, client_key_path); } } else { - // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress) - // if port param below changes. cli_ = detail::make_unique(scheme_host_port, 80, client_cert_path, client_key_path); } @@ -6940,7 +6531,7 @@ Client::Client(const std::string &host, int port, : cli_(detail::make_unique(host, port, client_cert_path, client_key_path)) {} -Client::~Client() = default; +Client::~Client() {} bool Client::is_valid() const { return cli_ != nullptr && cli_->is_valid(); @@ -7000,20 +6591,19 @@ Result Client::Get(const std::string &path, const Headers &headers, } Result Client::Get(const std::string &path, const Params ¶ms, const Headers &headers, Progress progress) { - return cli_->Get(path, params, headers, std::move(progress)); + return cli_->Get(path, params, headers, progress); } Result Client::Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, params, headers, std::move(content_receiver), - std::move(progress)); + return cli_->Get(path, params, headers, content_receiver, progress); } Result Client::Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, Progress progress) { - return cli_->Get(path, params, headers, std::move(response_handler), - std::move(content_receiver), std::move(progress)); + return cli_->Get(path, params, headers, response_handler, content_receiver, + progress); } Result Client::Head(const std::string &path) { return cli_->Head(path); } @@ -7035,30 +6625,15 @@ Result Client::Post(const std::string &path, const Headers &headers, const std::string &content_type) { return cli_->Post(path, headers, body, content_length, content_type); } -Result Client::Post(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, Progress progress) { - return cli_->Post(path, headers, body, content_length, content_type, - progress); -} Result Client::Post(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Post(path, body, content_type); } -Result Client::Post(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress) { - return cli_->Post(path, body, content_type, progress); -} Result Client::Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Post(path, headers, body, content_type); } -Result Client::Post(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, Progress progress) { - return cli_->Post(path, headers, body, content_type, progress); -} Result Client::Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { @@ -7089,10 +6664,6 @@ Result Client::Post(const std::string &path, const Headers &headers, const Params ¶ms) { return cli_->Post(path, headers, params); } -Result Client::Post(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress) { - return cli_->Post(path, headers, params, progress); -} Result Client::Post(const std::string &path, const MultipartFormDataItems &items) { return cli_->Post(path, items); @@ -7123,29 +6694,15 @@ Result Client::Put(const std::string &path, const Headers &headers, const std::string &content_type) { return cli_->Put(path, headers, body, content_length, content_type); } -Result Client::Put(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, Progress progress) { - return cli_->Put(path, headers, body, content_length, content_type, progress); -} Result Client::Put(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Put(path, body, content_type); } -Result Client::Put(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress) { - return cli_->Put(path, body, content_type, progress); -} Result Client::Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Put(path, headers, body, content_type); } -Result Client::Put(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, Progress progress) { - return cli_->Put(path, headers, body, content_type, progress); -} Result Client::Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { @@ -7176,10 +6733,6 @@ Result Client::Put(const std::string &path, const Headers &headers, const Params ¶ms) { return cli_->Put(path, headers, params); } -Result Client::Put(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress) { - return cli_->Put(path, headers, params, progress); -} Result Client::Put(const std::string &path, const MultipartFormDataItems &items) { return cli_->Put(path, items); @@ -7207,44 +6760,20 @@ Result Client::Patch(const std::string &path, const char *body, const std::string &content_type) { return cli_->Patch(path, body, content_length, content_type); } -Result Client::Patch(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type, - Progress progress) { - return cli_->Patch(path, body, content_length, content_type, progress); -} Result Client::Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return cli_->Patch(path, headers, body, content_length, content_type); } -Result Client::Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, - Progress progress) { - return cli_->Patch(path, headers, body, content_length, content_type, - progress); -} Result Client::Patch(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Patch(path, body, content_type); } -Result Client::Patch(const std::string &path, const std::string &body, - const std::string &content_type, - Progress progress) { - return cli_->Patch(path, body, content_type, progress); -} Result Client::Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Patch(path, headers, body, content_type); } -Result Client::Patch(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, - Progress progress) { - return cli_->Patch(path, headers, body, content_type, progress); -} Result Client::Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type) { @@ -7279,44 +6808,20 @@ Result Client::Delete(const std::string &path, const char *body, const std::string &content_type) { return cli_->Delete(path, body, content_length, content_type); } -Result Client::Delete(const std::string &path, const char *body, - size_t content_length, - const std::string &content_type, - Progress progress) { - return cli_->Delete(path, body, content_length, content_type, progress); -} Result Client::Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type) { return cli_->Delete(path, headers, body, content_length, content_type); } -Result Client::Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, - Progress progress) { - return cli_->Delete(path, headers, body, content_length, content_type, - progress); -} Result Client::Delete(const std::string &path, const std::string &body, const std::string &content_type) { return cli_->Delete(path, body, content_type); } -Result Client::Delete(const std::string &path, const std::string &body, - const std::string &content_type, - Progress progress) { - return cli_->Delete(path, body, content_type, progress); -} Result Client::Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type) { return cli_->Delete(path, headers, body, content_type); } -Result Client::Delete(const std::string &path, const Headers &headers, - const std::string &body, - const std::string &content_type, - Progress progress) { - return cli_->Delete(path, headers, body, content_type, progress); -} Result Client::Options(const std::string &path) { return cli_->Options(path); } @@ -7349,11 +6854,6 @@ void Client::set_default_headers(Headers headers) { cli_->set_default_headers(std::move(headers)); } -void Client::set_header_writer( - std::function const &writer) { - cli_->set_header_writer(writer); -} - void Client::set_address_family(int family) { cli_->set_address_family(family); } diff --git a/source/http/httplib.h b/source/http/httplib.h index e429de3..3ebcf60 100644 --- a/source/http/httplib.h +++ b/source/http/httplib.h @@ -1,14 +1,14 @@ // // httplib.h // -// Copyright (c) 2024 Yuji Hirose. All rights reserved. +// Copyright (c) 2023 Yuji Hirose. All rights reserved. // MIT License // #ifndef CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H -#define CPPHTTPLIB_VERSION "0.16.1" +#define CPPHTTPLIB_VERSION "0.13.1" /* * Configuration @@ -82,20 +82,16 @@ #define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 #endif -#ifndef CPPHTTPLIB_RANGE_MAX_COUNT -#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 -#endif - #ifndef CPPHTTPLIB_TCP_NODELAY #define CPPHTTPLIB_TCP_NODELAY false #endif #ifndef CPPHTTPLIB_RECV_BUFSIZ -#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u) +#define CPPHTTPLIB_RECV_BUFSIZ size_t(524288u) #endif #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ -#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) +#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(524288u) #endif #ifndef CPPHTTPLIB_THREAD_POOL_COUNT @@ -145,11 +141,11 @@ using ssize_t = long; #endif // _MSC_VER #ifndef S_ISREG -#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) +#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) #endif // S_ISREG #ifndef S_ISDIR -#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) #endif // S_ISDIR #ifndef NOMINMAX @@ -164,6 +160,10 @@ using ssize_t = long; #define WSA_FLAG_NO_HANDLE_INHERIT 0x80 #endif +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp + using socket_t = SOCKET; #ifdef CPPHTTPLIB_USE_POLL #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) @@ -193,7 +193,6 @@ using socket_t = SOCKET; #endif #include #include -#include #include #include #include @@ -214,7 +213,6 @@ using socket_t = int; #include #include #include -#include #include #include #include @@ -248,6 +246,7 @@ using socket_t = int; #ifdef _MSC_VER #pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "cryptui.lib") #endif #elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) #include @@ -331,7 +330,7 @@ struct scope_exit { explicit scope_exit(std::function &&f) : exit_function(std::move(f)), execute_on_destruction{true} {} - scope_exit(scope_exit &&rhs) noexcept + scope_exit(scope_exit &&rhs) : exit_function(std::move(rhs.exit_function)), execute_on_destruction{rhs.execute_on_destruction} { rhs.release(); @@ -354,81 +353,6 @@ private: } // namespace detail -enum StatusCode { - // Information responses - Continue_100 = 100, - SwitchingProtocol_101 = 101, - Processing_102 = 102, - EarlyHints_103 = 103, - - // Successful responses - OK_200 = 200, - Created_201 = 201, - Accepted_202 = 202, - NonAuthoritativeInformation_203 = 203, - NoContent_204 = 204, - ResetContent_205 = 205, - PartialContent_206 = 206, - MultiStatus_207 = 207, - AlreadyReported_208 = 208, - IMUsed_226 = 226, - - // Redirection messages - MultipleChoices_300 = 300, - MovedPermanently_301 = 301, - Found_302 = 302, - SeeOther_303 = 303, - NotModified_304 = 304, - UseProxy_305 = 305, - unused_306 = 306, - TemporaryRedirect_307 = 307, - PermanentRedirect_308 = 308, - - // Client error responses - BadRequest_400 = 400, - Unauthorized_401 = 401, - PaymentRequired_402 = 402, - Forbidden_403 = 403, - NotFound_404 = 404, - MethodNotAllowed_405 = 405, - NotAcceptable_406 = 406, - ProxyAuthenticationRequired_407 = 407, - RequestTimeout_408 = 408, - Conflict_409 = 409, - Gone_410 = 410, - LengthRequired_411 = 411, - PreconditionFailed_412 = 412, - PayloadTooLarge_413 = 413, - UriTooLong_414 = 414, - UnsupportedMediaType_415 = 415, - RangeNotSatisfiable_416 = 416, - ExpectationFailed_417 = 417, - ImATeapot_418 = 418, - MisdirectedRequest_421 = 421, - UnprocessableContent_422 = 422, - Locked_423 = 423, - FailedDependency_424 = 424, - TooEarly_425 = 425, - UpgradeRequired_426 = 426, - PreconditionRequired_428 = 428, - TooManyRequests_429 = 429, - RequestHeaderFieldsTooLarge_431 = 431, - UnavailableForLegalReasons_451 = 451, - - // Server error responses - InternalServerError_500 = 500, - NotImplemented_501 = 501, - BadGateway_502 = 502, - ServiceUnavailable_503 = 503, - GatewayTimeout_504 = 504, - HttpVersionNotSupported_505 = 505, - VariantAlsoNegotiates_506 = 506, - InsufficientStorage_507 = 507, - LoopDetected_508 = 508, - NotExtended_510 = 510, - NetworkAuthenticationRequired_511 = 511, -}; - using Headers = std::multimap; using Params = std::multimap; @@ -458,18 +382,17 @@ public: DataSink &operator=(DataSink &&) = delete; std::function write; - std::function is_writable; std::function done; std::function done_with_trailer; std::ostream os; private: - class data_sink_streambuf final : public std::streambuf { + class data_sink_streambuf : public std::streambuf { public: explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} protected: - std::streamsize xsputn(const char *s, std::streamsize n) override { + std::streamsize xsputn(const char *s, std::streamsize n) { sink_.write(s, static_cast(n)); return n; } @@ -563,7 +486,8 @@ struct Request { bool has_header(const std::string &key) const; std::string get_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + template + T get_header_value(const std::string &key, size_t id = 0) const; size_t get_header_value_count(const std::string &key) const; void set_header(const std::string &key, const std::string &val); @@ -595,14 +519,14 @@ struct Response { bool has_header(const std::string &key) const; std::string get_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + template + T get_header_value(const std::string &key, size_t id = 0) const; size_t get_header_value_count(const std::string &key) const; void set_header(const std::string &key, const std::string &val); - void set_redirect(const std::string &url, int status = StatusCode::Found_302); + void set_redirect(const std::string &url, int status = 302); void set_content(const char *s, size_t n, const std::string &content_type); void set_content(const std::string &s, const std::string &content_type); - void set_content(std::string &&s, const std::string &content_type); void set_content_provider( size_t length, const std::string &content_type, ContentProvider provider, @@ -659,16 +583,15 @@ public: TaskQueue() = default; virtual ~TaskQueue() = default; - virtual bool enqueue(std::function fn) = 0; + virtual void enqueue(std::function fn) = 0; virtual void shutdown() = 0; virtual void on_idle() {} }; -class ThreadPool final : public TaskQueue { +class ThreadPool : public TaskQueue { public: - explicit ThreadPool(size_t n, size_t mqr = 0) - : shutdown_(false), max_queued_requests_(mqr) { + explicit ThreadPool(size_t n) : shutdown_(false) { while (n) { threads_.emplace_back(worker(*this)); n--; @@ -678,17 +601,13 @@ public: ThreadPool(const ThreadPool &) = delete; ~ThreadPool() override = default; - bool enqueue(std::function fn) override { + void enqueue(std::function fn) override { { std::unique_lock lock(mutex_); - if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) { - return false; - } jobs_.push_back(std::move(fn)); } cond_.notify_one(); - return true; } void shutdown() override { @@ -721,17 +640,13 @@ private: if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } - fn = pool_.jobs_.front(); + fn = std::move(pool_.jobs_.front()); pool_.jobs_.pop_front(); } assert(true == static_cast(fn)); fn(); } - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - OPENSSL_thread_stop(); -#endif } ThreadPool &pool_; @@ -742,7 +657,6 @@ private: std::list> jobs_; bool shutdown_; - size_t max_queued_requests_ = 0; std::condition_variable cond_; std::mutex mutex_; @@ -754,10 +668,6 @@ using SocketOptions = std::function; void default_socket_options(socket_t sock); -const char *http_status_message(int status); - -std::string get_bearer_token_auth(const Request &req); - namespace detail { class MatcherBase { @@ -786,7 +696,7 @@ public: * the resulting capture will be * {{"capture", "1"}, {"second_capture", "2"}} */ -class PathParamsMatcher final : public MatcherBase { +class PathParamsMatcher : public MatcherBase { public: PathParamsMatcher(const std::string &pattern); @@ -816,7 +726,7 @@ private: * This means that wildcard patterns may match multiple path segments with /: * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". */ -class RegexMatcher final : public MatcherBase { +class RegexMatcher : public MatcherBase { public: RegexMatcher(const std::string &pattern) : regex_(pattern) {} @@ -826,8 +736,6 @@ private: std::regex regex_; }; -ssize_t write_headers(Stream &strm, const Headers &headers); - } // namespace detail class Server { @@ -874,16 +782,10 @@ public: bool remove_mount_point(const std::string &mount_point); Server &set_file_extension_and_mimetype_mapping(const std::string &ext, const std::string &mime); - Server &set_default_file_mimetype(const std::string &mime); Server &set_file_request_handler(Handler handler); - template - Server &set_error_handler(ErrorHandlerFunc &&handler) { - return set_error_handler_core( - std::forward(handler), - std::is_convertible{}); - } - + Server &set_error_handler(HandlerWithResponse handler); + Server &set_error_handler(Handler handler); Server &set_exception_handler(ExceptionHandler handler); Server &set_pre_routing_handler(HandlerWithResponse handler); Server &set_post_routing_handler(Handler handler); @@ -896,8 +798,6 @@ public: Server &set_socket_options(SocketOptions socket_options); Server &set_default_headers(Headers headers); - Server & - set_header_writer(std::function const &writer); Server &set_keep_alive_max_count(size_t count); Server &set_keep_alive_timeout(time_t sec); @@ -954,9 +854,6 @@ private: static std::unique_ptr make_matcher(const std::string &pattern); - Server &set_error_handler_core(HandlerWithResponse handler, std::true_type); - Server &set_error_handler_core(Handler handler, std::false_type); - socket_t create_server_socket(const std::string &host, int port, int socket_flags, SocketOptions socket_options) const; @@ -966,16 +863,16 @@ private: bool routing(Request &req, Response &res, Stream &strm); bool handle_file_request(const Request &req, Response &res, bool head = false); - bool dispatch_request(Request &req, Response &res, - const Handlers &handlers) const; - bool dispatch_request_for_content_reader( - Request &req, Response &res, ContentReader content_reader, - const HandlersForContentReader &handlers) const; + bool dispatch_request(Request &req, Response &res, const Handlers &handlers); + bool + dispatch_request_for_content_reader(Request &req, Response &res, + ContentReader content_reader, + const HandlersForContentReader &handlers); - bool parse_request_line(const char *s, Request &req) const; + bool parse_request_line(const char *s, Request &req); void apply_ranges(const Request &req, Response &res, - std::string &content_type, std::string &boundary) const; - bool write_response(Stream &strm, bool close_connection, Request &req, + std::string &content_type, std::string &boundary); + bool write_response(Stream &strm, bool close_connection, const Request &req, Response &res); bool write_response_with_content(Stream &strm, bool close_connection, const Request &req, Response &res); @@ -994,7 +891,7 @@ private: bool read_content_core(Stream &strm, Request &req, Response &res, ContentReceiver receiver, MultipartContentHeader multipart_header, - ContentReceiver multipart_receiver) const; + ContentReceiver multipart_receiver); virtual bool process_and_close_socket(socket_t sock); @@ -1008,7 +905,6 @@ private: }; std::vector base_dirs_; std::map file_extension_and_mimetype_map_; - std::string default_file_mimetype_ = "application/octet-stream"; Handler file_request_handler_; Handlers get_handlers_; @@ -1035,8 +931,6 @@ private: SocketOptions socket_options_ = default_socket_options; Headers default_headers_; - std::function header_writer_ = - detail::write_headers; }; enum class Error { @@ -1054,13 +948,12 @@ enum class Error { UnsupportedMultipartBoundaryChars, Compression, ConnectionTimeout, - ProxyConnection, // For internal use only SSLPeerCouldBeClosed_, }; -std::string to_string(Error error); +std::string to_string(const Error error); std::ostream &operator<<(std::ostream &os, const Error &obj); @@ -1089,8 +982,8 @@ public: bool has_request_header(const std::string &key) const; std::string get_request_header_value(const std::string &key, size_t id = 0) const; - uint64_t get_request_header_value_u64(const std::string &key, - size_t id = 0) const; + template + T get_request_header_value(const std::string &key, size_t id = 0) const; size_t get_request_header_value_count(const std::string &key) const; private: @@ -1154,18 +1047,10 @@ public: const std::string &content_type); Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type, - Progress progress); Result Post(const std::string &path, const std::string &body, const std::string &content_type); - Result Post(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type); @@ -1181,8 +1066,6 @@ public: Result Post(const std::string &path, const Params ¶ms); Result Post(const std::string &path, const Headers &headers, const Params ¶ms); - Result Post(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress); Result Post(const std::string &path, const MultipartFormDataItems &items); Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items); @@ -1197,18 +1080,10 @@ public: const std::string &content_type); Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type, - Progress progress); Result Put(const std::string &path, const std::string &body, const std::string &content_type); - Result Put(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type); Result Put(const std::string &path, @@ -1223,8 +1098,6 @@ public: Result Put(const std::string &path, const Params ¶ms); Result Put(const std::string &path, const Headers &headers, const Params ¶ms); - Result Put(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress); Result Put(const std::string &path, const MultipartFormDataItems &items); Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items); @@ -1237,23 +1110,13 @@ public: Result Patch(const std::string &path); Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type); - Result Patch(const std::string &path, const char *body, size_t content_length, - const std::string &content_type, Progress progress); Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, Progress progress); Result Patch(const std::string &path, const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type); @@ -1271,24 +1134,13 @@ public: Result Delete(const std::string &path, const Headers &headers); Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const char *body, - size_t content_length, const std::string &content_type, - Progress progress); Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, Progress progress); Result Delete(const std::string &path, const std::string &body, const std::string &content_type); - Result Delete(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Options(const std::string &path); Result Options(const std::string &path, const Headers &headers); @@ -1308,9 +1160,6 @@ public: void set_default_headers(Headers headers); - void - set_header_writer(std::function const &writer); - void set_address_family(int family); void set_tcp_nodelay(bool on); void set_socket_options(SocketOptions socket_options); @@ -1359,7 +1208,7 @@ public: void set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path = std::string()); void set_ca_cert_store(X509_STORE *ca_cert_store); - X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const; + X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size); #endif #ifdef CPPHTTPLIB_OPENSSL_SUPPORT @@ -1388,14 +1237,14 @@ protected: // Also, shutdown_ssl and close_socket should also NOT be called concurrently // with a DIFFERENT thread sending requests using that socket. virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); - void shutdown_socket(Socket &socket) const; + void shutdown_socket(Socket &socket); void close_socket(Socket &socket); bool process_request(Stream &strm, Request &req, Response &res, bool close_connection, Error &error); bool write_content_with_provider(Stream &strm, const Request &req, - Error &error) const; + Error &error); void copy_settings(const ClientImpl &rhs); @@ -1420,10 +1269,6 @@ protected: // Default headers Headers default_headers_; - // Header writer - std::function header_writer_ = - detail::write_headers; - // Settings std::string client_cert_path_; std::string client_key_path_; @@ -1486,8 +1331,7 @@ private: Result send_(Request &&req); socket_t create_client_socket(Error &error) const; - bool read_response_line(Stream &strm, const Request &req, - Response &res) const; + bool read_response_line(Stream &strm, const Request &req, Response &res); bool write_request(Stream &strm, Request &req, bool close_connection, Error &error); bool redirect(Request &req, Response &res, Error &error); @@ -1503,10 +1347,10 @@ private: const Headers &headers, const char *body, size_t content_length, ContentProvider content_provider, ContentProviderWithoutLength content_provider_without_length, - const std::string &content_type, Progress progress); + const std::string &content_type); ContentProviderWithoutLength get_multipart_content_provider( const std::string &boundary, const MultipartFormDataItems &items, - const MultipartFormDataProviderItems &provider_items) const; + const MultipartFormDataProviderItems &provider_items); std::string adjust_host_string(const std::string &host) const; @@ -1532,7 +1376,6 @@ public: const std::string &client_key_path); Client(Client &&) = default; - Client &operator=(Client &&) = default; ~Client(); @@ -1579,18 +1422,10 @@ public: const std::string &content_type); Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type, - Progress progress); Result Post(const std::string &path, const std::string &body, const std::string &content_type); - Result Post(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Post(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type); @@ -1606,8 +1441,6 @@ public: Result Post(const std::string &path, const Params ¶ms); Result Post(const std::string &path, const Headers &headers, const Params ¶ms); - Result Post(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress); Result Post(const std::string &path, const MultipartFormDataItems &items); Result Post(const std::string &path, const Headers &headers, const MultipartFormDataItems &items); @@ -1622,18 +1455,10 @@ public: const std::string &content_type); Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, const char *body, - size_t content_length, const std::string &content_type, - Progress progress); Result Put(const std::string &path, const std::string &body, const std::string &content_type); - Result Put(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Put(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type); Result Put(const std::string &path, @@ -1648,8 +1473,6 @@ public: Result Put(const std::string &path, const Params ¶ms); Result Put(const std::string &path, const Headers &headers, const Params ¶ms); - Result Put(const std::string &path, const Headers &headers, - const Params ¶ms, Progress progress); Result Put(const std::string &path, const MultipartFormDataItems &items); Result Put(const std::string &path, const Headers &headers, const MultipartFormDataItems &items); @@ -1662,23 +1485,13 @@ public: Result Patch(const std::string &path); Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type); - Result Patch(const std::string &path, const char *body, size_t content_length, - const std::string &content_type, Progress progress); Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, Progress progress); Result Patch(const std::string &path, const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Patch(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type); @@ -1696,24 +1509,13 @@ public: Result Delete(const std::string &path, const Headers &headers); Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const char *body, - size_t content_length, const std::string &content_type, - Progress progress); Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const char *body, size_t content_length, - const std::string &content_type, Progress progress); Result Delete(const std::string &path, const std::string &body, const std::string &content_type); - Result Delete(const std::string &path, const std::string &body, - const std::string &content_type, Progress progress); Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type); - Result Delete(const std::string &path, const Headers &headers, - const std::string &body, const std::string &content_type, - Progress progress); Result Options(const std::string &path); Result Options(const std::string &path, const Headers &headers); @@ -1733,9 +1535,6 @@ public: void set_default_headers(Headers headers); - void - set_header_writer(std::function const &writer); - void set_address_family(int family); void set_tcp_nodelay(bool on); void set_socket_options(SocketOptions socket_options); @@ -1827,9 +1626,6 @@ public: SSL_CTX *ssl_context() const; - void update_certs(X509 *cert, EVP_PKEY *private_key, - X509_STORE *client_ca_cert_store = nullptr); - private: bool process_and_close_socket(socket_t sock) override; @@ -1837,7 +1633,7 @@ private: std::mutex ctx_mutex_; }; -class SSLClient final : public ClientImpl { +class SSLClient : public ClientImpl { public: explicit SSLClient(const std::string &host); @@ -1845,12 +1641,10 @@ public: explicit SSLClient(const std::string &host, int port, const std::string &client_cert_path, - const std::string &client_key_path, - const std::string &private_key_password = std::string()); + const std::string &client_key_path); explicit SSLClient(const std::string &host, int port, X509 *client_cert, - EVP_PKEY *client_key, - const std::string &private_key_password = std::string()); + EVP_PKEY *client_key); ~SSLClient() override; @@ -1866,7 +1660,7 @@ public: private: bool create_and_connect_socket(Socket &socket, Error &error) override; void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; - void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully); + void shutdown_ssl_impl(Socket &socket, bool shutdown_socket); bool process_socket(const Socket &socket, std::function callback) override; @@ -1910,9 +1704,15 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) { callback(static_cast(sec), static_cast(usec)); } -inline uint64_t get_header_value_u64(const Headers &headers, - const std::string &key, size_t id, - uint64_t def) { +template +inline T get_header_value(const Headers & /*headers*/, + const std::string & /*key*/, size_t /*id*/ = 0, + uint64_t /*def*/ = 0) {} + +template <> +inline uint64_t get_header_value(const Headers &headers, + const std::string &key, size_t id, + uint64_t def) { auto rng = headers.equal_range(key); auto it = rng.first; std::advance(it, static_cast(id)); @@ -1924,14 +1724,14 @@ inline uint64_t get_header_value_u64(const Headers &headers, } // namespace detail -inline uint64_t Request::get_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(headers, key, id, 0); +template +inline T Request::get_header_value(const std::string &key, size_t id) const { + return detail::get_header_value(headers, key, id, 0); } -inline uint64_t Response::get_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(headers, key, id, 0); +template +inline T Response::get_header_value(const std::string &key, size_t id) const { + return detail::get_header_value(headers, key, id, 0); } template @@ -1974,91 +1774,9 @@ inline void default_socket_options(socket_t sock) { reinterpret_cast(&yes), sizeof(yes)); #endif #endif -} - -inline const char *http_status_message(int status) { - switch (status) { - case StatusCode::Continue_100: return "Continue"; - case StatusCode::SwitchingProtocol_101: return "Switching Protocol"; - case StatusCode::Processing_102: return "Processing"; - case StatusCode::EarlyHints_103: return "Early Hints"; - case StatusCode::OK_200: return "OK"; - case StatusCode::Created_201: return "Created"; - case StatusCode::Accepted_202: return "Accepted"; - case StatusCode::NonAuthoritativeInformation_203: - return "Non-Authoritative Information"; - case StatusCode::NoContent_204: return "No Content"; - case StatusCode::ResetContent_205: return "Reset Content"; - case StatusCode::PartialContent_206: return "Partial Content"; - case StatusCode::MultiStatus_207: return "Multi-Status"; - case StatusCode::AlreadyReported_208: return "Already Reported"; - case StatusCode::IMUsed_226: return "IM Used"; - case StatusCode::MultipleChoices_300: return "Multiple Choices"; - case StatusCode::MovedPermanently_301: return "Moved Permanently"; - case StatusCode::Found_302: return "Found"; - case StatusCode::SeeOther_303: return "See Other"; - case StatusCode::NotModified_304: return "Not Modified"; - case StatusCode::UseProxy_305: return "Use Proxy"; - case StatusCode::unused_306: return "unused"; - case StatusCode::TemporaryRedirect_307: return "Temporary Redirect"; - case StatusCode::PermanentRedirect_308: return "Permanent Redirect"; - case StatusCode::BadRequest_400: return "Bad Request"; - case StatusCode::Unauthorized_401: return "Unauthorized"; - case StatusCode::PaymentRequired_402: return "Payment Required"; - case StatusCode::Forbidden_403: return "Forbidden"; - case StatusCode::NotFound_404: return "Not Found"; - case StatusCode::MethodNotAllowed_405: return "Method Not Allowed"; - case StatusCode::NotAcceptable_406: return "Not Acceptable"; - case StatusCode::ProxyAuthenticationRequired_407: - return "Proxy Authentication Required"; - case StatusCode::RequestTimeout_408: return "Request Timeout"; - case StatusCode::Conflict_409: return "Conflict"; - case StatusCode::Gone_410: return "Gone"; - case StatusCode::LengthRequired_411: return "Length Required"; - case StatusCode::PreconditionFailed_412: return "Precondition Failed"; - case StatusCode::PayloadTooLarge_413: return "Payload Too Large"; - case StatusCode::UriTooLong_414: return "URI Too Long"; - case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type"; - case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable"; - case StatusCode::ExpectationFailed_417: return "Expectation Failed"; - case StatusCode::ImATeapot_418: return "I'm a teapot"; - case StatusCode::MisdirectedRequest_421: return "Misdirected Request"; - case StatusCode::UnprocessableContent_422: return "Unprocessable Content"; - case StatusCode::Locked_423: return "Locked"; - case StatusCode::FailedDependency_424: return "Failed Dependency"; - case StatusCode::TooEarly_425: return "Too Early"; - case StatusCode::UpgradeRequired_426: return "Upgrade Required"; - case StatusCode::PreconditionRequired_428: return "Precondition Required"; - case StatusCode::TooManyRequests_429: return "Too Many Requests"; - case StatusCode::RequestHeaderFieldsTooLarge_431: - return "Request Header Fields Too Large"; - case StatusCode::UnavailableForLegalReasons_451: - return "Unavailable For Legal Reasons"; - case StatusCode::NotImplemented_501: return "Not Implemented"; - case StatusCode::BadGateway_502: return "Bad Gateway"; - case StatusCode::ServiceUnavailable_503: return "Service Unavailable"; - case StatusCode::GatewayTimeout_504: return "Gateway Timeout"; - case StatusCode::HttpVersionNotSupported_505: - return "HTTP Version Not Supported"; - case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates"; - case StatusCode::InsufficientStorage_507: return "Insufficient Storage"; - case StatusCode::LoopDetected_508: return "Loop Detected"; - case StatusCode::NotExtended_510: return "Not Extended"; - case StatusCode::NetworkAuthenticationRequired_511: - return "Network Authentication Required"; - - default: - case StatusCode::InternalServerError_500: return "Internal Server Error"; - } -} - -inline std::string get_bearer_token_auth(const Request &req) { - if (req.has_header("Authorization")) { - static std::string BearerHeaderPrefix = "Bearer "; - return req.get_header_value("Authorization") - .substr(BearerHeaderPrefix.length()); - } - return ""; + int const size = 1048576; + setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); + setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); } template @@ -2101,7 +1819,6 @@ inline std::string to_string(const Error error) { return "Unsupported HTTP multipart boundary characters"; case Error::Compression: return "Compression failed"; case Error::ConnectionTimeout: return "Connection timed out"; - case Error::ProxyConnection: return "Proxy connection failed"; case Error::Unknown: return "Unknown"; default: break; } @@ -2115,9 +1832,10 @@ inline std::ostream &operator<<(std::ostream &os, const Error &obj) { return os; } -inline uint64_t Result::get_request_header_value_u64(const std::string &key, - size_t id) const { - return detail::get_header_value_u64(request_headers_, key, id, 0); +template +inline T Result::get_request_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(request_headers_, key, id, 0); } template @@ -2171,7 +1889,7 @@ void hosted_at(const std::string &hostname, std::vector &addrs); std::string append_query_params(const std::string &path, const Params ¶ms); -std::pair make_range_header(const Ranges &ranges); +std::pair make_range_header(Ranges ranges); std::pair make_basic_authentication_header(const std::string &username, @@ -2180,6 +1898,8 @@ make_basic_authentication_header(const std::string &username, namespace detail { +const char *status_message(int status); + std::string encode_query_param(const std::string &value); std::string encode_url(const std::string &s); @@ -2189,22 +1909,9 @@ void read_file(const std::string &path, std::string &out); std::string trim_copy(const std::string &s); -void divide( - const char *data, std::size_t size, char d, - std::function - fn); - -void divide( - const std::string &str, char d, - std::function - fn); - void split(const char *b, const char *e, char d, std::function fn); -void split(const char *b, const char *e, char d, size_t m, - std::function fn); - bool process_client_socket(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, time_t write_timeout_usec, @@ -2222,8 +1929,6 @@ const char *get_header_value(const Headers &headers, const std::string &key, std::string params_to_query_str(const Params ¶ms); -void parse_query_text(const char *data, std::size_t size, Params ¶ms); - void parse_query_text(const std::string &s, Params ¶ms); bool parse_multipart_boundary(const std::string &content_type, @@ -2241,7 +1946,7 @@ enum class EncodingType { None = 0, Gzip, Brotli }; EncodingType encoding_type(const Request &req, const Response &res); -class BufferStream final : public Stream { +class BufferStream : public Stream { public: BufferStream() = default; ~BufferStream() override = default; @@ -2281,19 +1986,19 @@ public: Callback callback) = 0; }; -class nocompressor final : public compressor { +class nocompressor : public compressor { public: - ~nocompressor() override = default; + virtual ~nocompressor() = default; bool compress(const char *data, size_t data_length, bool /*last*/, Callback callback) override; }; #ifdef CPPHTTPLIB_ZLIB_SUPPORT -class gzip_compressor final : public compressor { +class gzip_compressor : public compressor { public: gzip_compressor(); - ~gzip_compressor() override; + ~gzip_compressor(); bool compress(const char *data, size_t data_length, bool last, Callback callback) override; @@ -2303,10 +2008,10 @@ private: z_stream strm_; }; -class gzip_decompressor final : public decompressor { +class gzip_decompressor : public decompressor { public: gzip_decompressor(); - ~gzip_decompressor() override; + ~gzip_decompressor(); bool is_valid() const override; @@ -2320,7 +2025,7 @@ private: #endif #ifdef CPPHTTPLIB_BROTLI_SUPPORT -class brotli_compressor final : public compressor { +class brotli_compressor : public compressor { public: brotli_compressor(); ~brotli_compressor(); @@ -2332,7 +2037,7 @@ private: BrotliEncoderState *state_ = nullptr; }; -class brotli_decompressor final : public decompressor { +class brotli_decompressor : public decompressor { public: brotli_decompressor(); ~brotli_decompressor(); @@ -2369,29 +2074,6 @@ private: std::string glowable_buffer_; }; -class mmap { -public: - mmap(const char *path); - ~mmap(); - - bool open(const char *path); - void close(); - - bool is_open() const; - size_t size() const; - const char *data() const; - -private: -#if defined(_WIN32) - HANDLE hFile_; - HANDLE hMapping_; -#else - int fd_; -#endif - size_t size_; - void *addr_; -}; - } // namespace detail diff --git a/source/windows.cpp b/source/windows.cpp index e0d8561..bb70c42 100644 --- a/source/windows.cpp +++ b/source/windows.cpp @@ -439,45 +439,42 @@ namespace Windows } } - if (strcmp(remote_settings->http_server_type, HTTP_SERVER_GITHUB) != 0) + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); + ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_RPI]); + ImGui::SameLine(); + + if (ImGui::Checkbox("###enable_rpi", &remote_settings->enable_rpi)) { - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); - ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_RPI]); - ImGui::SameLine(); + CONFIG::SaveConfig(); + } + if (ImGui::IsItemHovered()) + { + ImGui::SetNextWindowSize(ImVec2(450, 110)); + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 440); + ImGui::Text("%s", lang_strings[STR_ENABLE_RPI_FTP_SMB_MSG]); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } - if (ImGui::Checkbox("###enable_rpi", &remote_settings->enable_rpi)) - { - CONFIG::SaveConfig(); - } - if (ImGui::IsItemHovered()) - { - ImGui::SetNextWindowSize(ImVec2(450, 110)); - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 440); - ImGui::Text("%s", lang_strings[STR_ENABLE_RPI_FTP_SMB_MSG]); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); + ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_DISK_CACHE]); + ImGui::SameLine(); - ImGui::SameLine(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); - ImGui::TextColored(colors[ImGuiCol_ButtonHovered], "%s:", lang_strings[STR_ENABLE_DISK_CACHE]); - ImGui::SameLine(); - - if (ImGui::Checkbox("###enable_disk_cache", &remote_settings->enable_disk_cache)) - { - CONFIG::SaveConfig(); - } - if (ImGui::IsItemHovered()) - { - ImGui::SetNextWindowSize(ImVec2(550, 110)); - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 540); - ImGui::Text("%s", lang_strings[STR_ENABLE_DISC_CACHE_MSG]); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } + if (ImGui::Checkbox("###enable_disk_cache", &remote_settings->enable_disk_cache)) + { + CONFIG::SaveConfig(); + } + if (ImGui::IsItemHovered()) + { + ImGui::SetNextWindowSize(ImVec2(550, 110)); + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 540); + ImGui::Text("%s", lang_strings[STR_ENABLE_DISC_CACHE_MSG]); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); } ImGui::PopStyleVar(); @@ -2653,8 +2650,6 @@ namespace Windows else if (strncasecmp(remote_settings->server, "https://github.com/", 19) == 0) { snprintf(remote_settings->http_server_type, 24, "%s", HTTP_SERVER_GITHUB); - remote_settings->enable_rpi = false; - remote_settings->enable_disk_cache = false; } } }