|
|
|
@@ -8,7 +8,7 @@
|
|
|
|
|
#ifndef CPPHTTPLIB_HTTPLIB_H
|
|
|
|
|
#define CPPHTTPLIB_HTTPLIB_H
|
|
|
|
|
|
|
|
|
|
#define CPPHTTPLIB_VERSION "0.17.0"
|
|
|
|
|
#define CPPHTTPLIB_VERSION "0.17.1"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Configuration
|
|
|
|
@@ -2198,6 +2198,10 @@ make_basic_authentication_header(const std::string &username,
|
|
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
|
|
bool is_file(const std::string &path);
|
|
|
|
|
|
|
|
|
|
bool is_dir(const std::string &path);
|
|
|
|
|
|
|
|
|
|
std::string encode_query_param(const std::string &value);
|
|
|
|
|
|
|
|
|
|
std::string decode_url(const std::string &s, bool convert_plus_to_space);
|
|
|
|
@@ -2525,20 +2529,6 @@ inline std::string base64_encode(const std::string &in) {
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool is_file(const std::string &path) {
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
return _access_s(path.c_str(), 0) == 0;
|
|
|
|
|
#else
|
|
|
|
|
struct stat st;
|
|
|
|
|
return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool is_dir(const std::string &path) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool is_valid_path(const std::string &path) {
|
|
|
|
|
size_t level = 0;
|
|
|
|
|
size_t i = 0;
|
|
|
|
@@ -2581,6 +2571,16 @@ inline bool is_valid_path(const std::string &path) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool is_file(const std::string &path) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool is_dir(const std::string &path) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string encode_query_param(const std::string &value) {
|
|
|
|
|
std::ostringstream escaped;
|
|
|
|
|
escaped.fill('0');
|
|
|
|
@@ -2790,6 +2790,10 @@ inline bool stream_line_reader::getline() {
|
|
|
|
|
fixed_buffer_used_size_ = 0;
|
|
|
|
|
glowable_buffer_.clear();
|
|
|
|
|
|
|
|
|
|
#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
|
|
|
|
|
char prev_byte = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0;; i++) {
|
|
|
|
|
char byte;
|
|
|
|
|
auto n = strm_.read(&byte, 1);
|
|
|
|
@@ -2806,7 +2810,12 @@ inline bool stream_line_reader::getline() {
|
|
|
|
|
|
|
|
|
|
append(byte);
|
|
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
|
|
|
|
|
if (byte == '\n') { break; }
|
|
|
|
|
#else
|
|
|
|
|
if (prev_byte == '\r' && byte == '\n') { break; }
|
|
|
|
|
prev_byte = byte;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
@@ -2847,9 +2856,7 @@ inline bool mmap::open(const char *path) {
|
|
|
|
|
wpath += path[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | \
|
|
|
|
|
WINAPI_PARTITION_GAMES) && \
|
|
|
|
|
(_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
|
|
|
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
|
|
|
|
hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
|
|
|
|
|
OPEN_EXISTING, NULL);
|
|
|
|
|
#else
|
|
|
|
@@ -2859,26 +2866,23 @@ inline bool mmap::open(const char *path) {
|
|
|
|
|
|
|
|
|
|
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; }
|
|
|
|
|
// If the following line doesn't compile due to QuadPart, update Windows SDK.
|
|
|
|
|
// See:
|
|
|
|
|
// https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
|
|
|
|
|
if (static_cast<ULONGLONG>(size.QuadPart) >
|
|
|
|
|
std::numeric_limits<decltype(size_)>::max()) {
|
|
|
|
|
// `size_t` might be 32-bits, on 32-bits Windows.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
size_ = static_cast<size_t>(size.QuadPart);
|
|
|
|
|
#else
|
|
|
|
|
DWORD sizeHigh;
|
|
|
|
|
DWORD sizeLow;
|
|
|
|
|
sizeLow = ::GetFileSize(hFile_, &sizeHigh);
|
|
|
|
|
if (sizeLow == INVALID_FILE_SIZE) { return false; }
|
|
|
|
|
size_ = (static_cast<size_t>(sizeHigh) << (sizeof(DWORD) * 8)) | sizeLow;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && \
|
|
|
|
|
(_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
|
|
|
#if _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);
|
|
|
|
|
hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (hMapping_ == NULL) {
|
|
|
|
@@ -2886,8 +2890,7 @@ inline bool mmap::open(const char *path) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && \
|
|
|
|
|
(_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
|
|
|
#if _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
|
|
|
|
addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
|
|
|
|
|
#else
|
|
|
|
|
addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
@@ -3247,6 +3250,24 @@ inline int shutdown_socket(socket_t sock) {
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string escape_abstract_namespace_unix_domain(const std::string& s) {
|
|
|
|
|
if (s.size() > 1 && s[0] == '\0') {
|
|
|
|
|
auto ret = s;
|
|
|
|
|
ret[0] = '@';
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string unescape_abstract_namespace_unix_domain(const std::string& s) {
|
|
|
|
|
if (s.size() > 1 && s[0] == '@') {
|
|
|
|
|
auto ret = s;
|
|
|
|
|
ret[0] = '\0';
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename BindOrConnect>
|
|
|
|
|
socket_t create_socket(const std::string &host, const std::string &ip, int port,
|
|
|
|
|
int address_family, int socket_flags, bool tcp_nodelay,
|
|
|
|
@@ -3259,7 +3280,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
|
|
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
|
hints.ai_protocol = 0;
|
|
|
|
|
hints.ai_protocol = IPPROTO_IP;
|
|
|
|
|
|
|
|
|
|
if (!ip.empty()) {
|
|
|
|
|
node = ip.c_str();
|
|
|
|
@@ -3287,7 +3308,9 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
|
|
|
|
|
if (sock != INVALID_SOCKET) {
|
|
|
|
|
sockaddr_un addr{};
|
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
|
std::copy(host.begin(), host.end(), addr.sun_path);
|
|
|
|
|
|
|
|
|
|
auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
|
|
|
|
|
std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
|
|
|
|
|
|
|
|
|
|
hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
|
|
|
|
|
hints.ai_addrlen = static_cast<socklen_t>(
|
|
|
|
@@ -4018,6 +4041,18 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
|
|
|
|
|
auto val = compare_case_ignore(key, "Location")
|
|
|
|
|
? std::string(p, end)
|
|
|
|
|
: decode_url(std::string(p, end), false);
|
|
|
|
|
|
|
|
|
|
// NOTE: From RFC 9110:
|
|
|
|
|
// Field values containing CR, LF, or NUL characters are
|
|
|
|
|
// invalid and dangerous, due to the varying ways that
|
|
|
|
|
// implementations might parse and interpret those
|
|
|
|
|
// characters; a recipient of CR, LF, or NUL within a field
|
|
|
|
|
// value MUST either reject the message or replace each of
|
|
|
|
|
// those characters with SP before further processing or
|
|
|
|
|
// forwarding of that message.
|
|
|
|
|
static const std::string CR_LF_NUL("\r\n\0", 3);
|
|
|
|
|
if (val.find_first_of(CR_LF_NUL) != std::string::npos) { return false; }
|
|
|
|
|
|
|
|
|
|
fn(key, val);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@@ -4055,10 +4090,12 @@ inline bool read_headers(Stream &strm, Headers &headers) {
|
|
|
|
|
// Exclude line terminator
|
|
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
if (!parse_header(line_reader.ptr(), end,
|
|
|
|
|
[&](const std::string &key, std::string &val) {
|
|
|
|
|
headers.emplace(key, val);
|
|
|
|
|
})) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
@@ -7027,9 +7064,10 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
|
|
|
|
|
inline ClientImpl::ClientImpl(const std::string &host, int port,
|
|
|
|
|
const std::string &client_cert_path,
|
|
|
|
|
const std::string &client_key_path)
|
|
|
|
|
: host_(host), port_(port),
|
|
|
|
|
host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)),
|
|
|
|
|
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
|
|
|
|
|
: host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
|
|
|
|
|
host_and_port_(adjust_host_string(host_) + ":" + std::to_string(port)),
|
|
|
|
|
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline ClientImpl::~ClientImpl() {
|
|
|
|
|
std::lock_guard<std::mutex> guard(socket_mutex_);
|
|
|
|
|