auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (sock != INVALID_SOCKET) {
- sockaddr_un addr;
+ sockaddr_un addr{};
addr.sun_family = AF_UNIX;
std::copy(host.begin(), host.end(), addr.sun_path);
if (sock == INVALID_SOCKET) { continue; }
#ifndef _WIN32
- if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
+ if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
+ close_socket(sock);
+ continue;
+ }
#endif
if (tcp_nodelay) {
constexpr unsigned int str2tag_core(const char *s, size_t l,
unsigned int h) {
- return (l == 0) ? h
- : str2tag_core(s + 1, l - 1,
- (h * 33) ^ static_cast<unsigned char>(*s));
+ return (l == 0)
+ ? h
+ : str2tag_core(
+ s + 1, l - 1,
+ // Unsets the 6 high bits of h, therefore no overflow happens
+ (((std::numeric_limits<unsigned int>::max)() >> 6) &
+ h * 33) ^
+ static_cast<unsigned char>(*s));
}
unsigned int str2tag(const std::string &s) {
return def;
}
+bool compare_case_ignore(const std::string &a, const std::string &b) {
+ if (a.size() != b.size()) { return false; }
+ for (size_t i = 0; i < b.size(); i++) {
+ if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
+ }
+ return true;
+}
+
template <typename T>
bool parse_header(const char *beg, const char *end, T fn) {
// Skip trailing spaces and tabs.
}
if (p < end) {
- fn(std::string(beg, key_end), decode_url(std::string(p, end), 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(std::move(key), std::move(val));
return true;
}
return true;
}
-bool read_content_chunked(Stream &strm,
+template <typename T>
+bool read_content_chunked(Stream &strm, T &x,
ContentReceiverWithProgress out) {
const auto bufsiz = 16;
char buf[bufsiz];
if (!line_reader.getline()) { return false; }
- if (strcmp(line_reader.ptr(), "\r\n")) { break; }
+ if (strcmp(line_reader.ptr(), "\r\n")) { return false; }
if (!line_reader.getline()) { return false; }
}
- if (chunk_len == 0) {
- // Reader terminator after chunks
- if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n"))
- return false;
+ assert(chunk_len == 0);
+
+ // Trailer
+ if (!line_reader.getline()) { return false; }
+
+ while (strcmp(line_reader.ptr(), "\r\n")) {
+ if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
+
+ // Exclude line terminator
+ constexpr auto line_terminator_len = 2;
+ auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
+
+ parse_header(line_reader.ptr(), end,
+ [&](std::string &&key, std::string &&val) {
+ x.headers.emplace(std::move(key), std::move(val));
+ });
+
+ if (!line_reader.getline()) { return false; }
}
return true;
auto exceed_payload_max_length = false;
if (is_chunked_transfer_encoding(x.headers)) {
- ret = read_content_chunked(strm, out);
+ ret = read_content_chunked(strm, x, out);
} else if (!has_header(x.headers, "Content-Length")) {
ret = read_content_without_length(strm, out);
} else {
data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) {
- if (write_data(strm, d, l)) {
+ if (strm.is_writable() && write_data(strm, d, l)) {
offset += l;
} else {
ok = false;
return ok;
};
- data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
-
while (offset < end_offset && !is_shutting_down()) {
- if (!content_provider(offset, end_offset - offset, data_sink)) {
+ if (!strm.is_writable()) {
+ error = Error::Write;
+ return false;
+ } else if (!content_provider(offset, end_offset - offset, data_sink)) {
error = Error::Canceled;
return false;
- }
- if (!ok) {
+ } else if (!ok) {
error = Error::Write;
return false;
}
data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) {
offset += l;
- if (!write_data(strm, d, l)) { ok = false; }
+ if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }
}
return ok;
};
data_sink.done = [&](void) { data_available = false; };
- data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
-
while (data_available && !is_shutting_down()) {
- if (!content_provider(offset, 0, data_sink)) { return false; }
- if (!ok) { return false; }
+ if (!strm.is_writable()) {
+ return false;
+ } else if (!content_provider(offset, 0, data_sink)) {
+ return false;
+ } else if (!ok) {
+ return false;
+ }
}
return true;
}
// Emit chunked response header and footer for each chunk
auto chunk =
from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
- if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
+ if (!strm.is_writable() ||
+ !write_data(strm, chunk.data(), chunk.size())) {
+ ok = false;
+ }
}
} else {
ok = false;
return ok;
};
- data_sink.done = [&](void) {
+ auto done_with_trailer = [&](const Headers *trailer) {
if (!ok) { return; }
data_available = false;
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
- if (!write_data(strm, chunk.data(), chunk.size())) {
+ if (!strm.is_writable() ||
+ !write_data(strm, chunk.data(), chunk.size())) {
ok = false;
return;
}
}
- static const std::string done_marker("0\r\n\r\n");
+ static const std::string done_marker("0\r\n");
if (!write_data(strm, done_marker.data(), done_marker.size())) {
ok = false;
}
+
+ // Trailer
+ if (trailer) {
+ for (const auto &kv : *trailer) {
+ std::string field_line = kv.first + ": " + kv.second + "\r\n";
+ if (!write_data(strm, field_line.data(), field_line.size())) {
+ ok = false;
+ }
+ }
+ }
+
+ static const std::string crlf("\r\n");
+ if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }
};
- data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
+ data_sink.done = [&](void) { done_with_trailer(nullptr); };
+
+ data_sink.done_with_trailer = [&](const Headers &trailer) {
+ done_with_trailer(&trailer);
+ };
while (data_available && !is_shutting_down()) {
- if (!content_provider(offset, 0, data_sink)) {
+ if (!strm.is_writable()) {
+ error = Error::Write;
+ return false;
+ } else if (!content_provider(offset, 0, data_sink)) {
error = Error::Canceled;
return false;
- }
- if (!ok) {
+ } else if (!ok) {
error = Error::Write;
return false;
}
bool parse_multipart_boundary(const std::string &content_type,
std::string &boundary) {
- auto pos = content_type.find("boundary=");
+ auto boundary_keyword = "boundary=";
+ auto pos = content_type.find(boundary_keyword);
if (pos == std::string::npos) { return false; }
- boundary = content_type.substr(pos + 9);
+ 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);
if (std::regex_match(header, m, re_content_disposition)) {
file_.name = m[1];
file_.filename = m[2];
+ } else {
+ is_valid_ = false;
+ return false;
}
}
buf_erase(pos + crlf_.size());
}
#endif
-#ifdef _WIN32
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+#ifdef _WIN32
// NOTE: This code came up with the following stackoverflow post:
// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
bool load_system_certs_on_windows(X509_STORE *store) {
auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
-
if (!hStore) { return false; }
+ auto result = false;
PCCERT_CONTEXT pContext = NULL;
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr) {
if (x509) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
+ result = true;
}
}
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
+ return result;
+}
+#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
+#if TARGET_OS_OSX
+template <typename T>
+using CFObjectPtr =
+ std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;
+
+void cf_object_ptr_deleter(CFTypeRef obj) {
+ if (obj) { CFRelease(obj); }
+}
+
+bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
+ CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};
+ CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,
+ kCFBooleanTrue};
+
+ CFObjectPtr<CFDictionaryRef> query(
+ CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,
+ sizeof(keys) / sizeof(keys[0]),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks),
+ cf_object_ptr_deleter);
+
+ if (!query) { return false; }
+
+ CFTypeRef security_items = nullptr;
+ if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||
+ CFArrayGetTypeID() != CFGetTypeID(security_items)) {
+ return false;
+ }
+
+ certs.reset(reinterpret_cast<CFArrayRef>(security_items));
return true;
}
-#endif
+bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {
+ CFArrayRef root_security_items = nullptr;
+ if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {
+ return false;
+ }
+
+ certs.reset(root_security_items);
+ return true;
+}
+
+bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {
+ auto result = false;
+ for (int i = 0; i < CFArrayGetCount(certs); ++i) {
+ const auto cert = reinterpret_cast<const __SecCertificate *>(
+ CFArrayGetValueAtIndex(certs, i));
+
+ if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }
+
+ CFDataRef cert_data = nullptr;
+ if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=
+ errSecSuccess) {
+ continue;
+ }
+
+ CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);
+
+ auto encoded_cert = static_cast<const unsigned char *>(
+ CFDataGetBytePtr(cert_data_ptr.get()));
+
+ auto x509 =
+ d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));
+
+ if (x509) {
+ X509_STORE_add_cert(store, x509);
+ X509_free(x509);
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool load_system_certs_on_macos(X509_STORE *store) {
+ auto result = false;
+ CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);
+ if (retrieve_certs_from_keychain(certs) && certs) {
+ result = add_certs_to_x509_store(certs.get(), store);
+ }
+
+ if (retrieve_root_certs_from_keychain(certs) && certs) {
+ result = add_certs_to_x509_store(certs.get(), store) || result;
+ }
+
+ return result;
+}
+#endif // TARGET_OS_OSX
+#endif // _WIN32
+#endif // CPPHTTPLIB_OPENSSL_SUPPORT
+
+#ifdef _WIN32
class WSInit {
public:
WSInit() {
return MultipartFormData();
}
+std::vector<MultipartFormData>
+Request::get_file_values(const std::string &key) const {
+ std::vector<MultipartFormData> values;
+ auto rng = files.equal_range(key);
+ for (auto it = rng.first; it != rng.second; it++) {
+ values.push_back(it->second);
+ }
+ return values;
+}
+
// Response implementation
bool Response::has_header(const std::string &key) const {
return headers.find(key) != headers.end();
void Response::set_content_provider(
size_t in_length, const std::string &content_type, ContentProvider provider,
ContentProviderResourceReleaser resource_releaser) {
- assert(in_length > 0);
set_header("Content-Type", content_type);
content_length_ = in_length;
- content_provider_ = std::move(provider);
+ if (in_length > 0) { content_provider_ = std::move(provider); }
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider_ = false;
}
// HTTP server implementation
Server::Server()
: new_task_queue(
- [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
- svr_sock_(INVALID_SOCKET), is_running_(false) {
+ [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
return bind_internal(host, 0, socket_flags);
}
-bool Server::listen_after_bind() { return listen_internal(); }
+bool Server::listen_after_bind() {
+ auto se = detail::scope_exit([&]() { done_ = true; });
+ return listen_internal();
+}
bool Server::listen(const std::string &host, int port,
int socket_flags) {
+ auto se = detail::scope_exit([&]() { done_ = true; });
return bind_to_port(host, port, socket_flags) && listen_internal();
}
bool Server::is_running() const { return is_running_; }
+void Server::wait_until_ready() const {
+ while (!is_running() && !done_) {
+ std::this_thread::sleep_for(std::chrono::milliseconds{1});
+ }
+}
+
void Server::stop() {
if (is_running_) {
assert(svr_sock_ != INVALID_SOCKET);
bool Server::read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
- MultipartContentHeader mulitpart_header,
+ MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) {
detail::MultipartFormDataParser multipart_form_data_parser;
ContentReceiverWithProgress out;
while (pos < n) {
auto read_size = (std::min)<size_t>(1, n - pos);
auto ret = multipart_form_data_parser.parse(
- buf + pos, read_size, multipart_receiver, mulitpart_header);
+ buf + pos, read_size, multipart_receiver, multipart_header);
if (!ret) { return false; }
pos += read_size;
}
return true;
*/
return multipart_form_data_parser.parse(buf, n, multipart_receiver,
- mulitpart_header);
+ multipart_header);
};
} else {
out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
bool Server::listen_internal() {
auto ret = true;
is_running_ = true;
+ auto se = detail::scope_exit([&]() { is_running_ = false; });
{
std::unique_ptr<TaskQueue> task_queue(new_task_queue());
#endif
}
-#if __cplusplus > 201703L
- task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
-#else
- task_queue->enqueue([=]() { process_and_close_socket(sock); });
-#endif
+ task_queue->enqueue([this, sock]() { process_and_close_socket(sock); });
}
task_queue->shutdown();
}
- is_running_ = false;
return ret;
}
bool ClientImpl::send(Request &req, Response &res, Error &error) {
std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
+ auto ret = send_(req, res, error);
+ if (error == Error::SSLPeerCouldBeClosed_) {
+ assert(!ret);
+ ret = send_(req, res, error);
+ }
+ return ret;
+}
+bool ClientImpl::send_(Request &req, Response &res, Error &error) {
{
std::lock_guard<std::mutex> guard(socket_mutex_);
if (is_ssl()) {
auto &scli = static_cast<SSLClient &>(*this);
if (!proxy_host_.empty() && proxy_port_ != -1) {
- bool success = false;
+ auto success = false;
if (!scli.connect_with_proxy(socket_, res, success, error)) {
return success;
}
}
}
+ auto ret = false;
auto close_connection = !keep_alive_;
- auto ret = process_socket(socket_, [&](Stream &strm) {
- return handle_request(strm, req, res, close_connection, error);
- });
- // Briefly lock mutex in order to mark that a request is no longer ongoing
- {
+ auto se = detail::scope_exit([&]() {
+ // Briefly lock mutex in order to mark that a request is no longer ongoing
std::lock_guard<std::mutex> guard(socket_mutex_);
socket_requests_in_flight_ -= 1;
if (socket_requests_in_flight_ <= 0) {
shutdown_socket(socket_);
close_socket(socket_);
}
- }
+ });
+
+ ret = process_socket(socket_, [&](Stream &strm) {
+ return handle_request(strm, req, res, close_connection, error);
+ });
if (!ret) {
if (error == Error::Success) { error = Error::Unknown; }
return false;
}
- auto location = detail::decode_url(res.get_header_value("location"), true);
+ auto location = res.get_header_value("location");
if (location.empty()) { return false; }
const static std::regex re(
- R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
+ R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
std::smatch m;
if (!std::regex_match(location, m, re)) { return false; }
if (next_host.empty()) { next_host = m[3].str(); }
auto port_str = m[4].str();
auto next_path = m[5].str();
+ auto next_query = m[6].str();
auto next_port = port_;
if (!port_str.empty()) {
if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; }
+ auto path = detail::decode_url(next_path, true) + next_query;
+
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
- return detail::redirect(*this, req, res, next_path, location, error);
+ return detail::redirect(*this, req, res, path, location, error);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
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, next_path, location, error);
+ return detail::redirect(cli, req, res, path, location, error);
#else
return false;
#endif
} else {
ClientImpl cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
- return detail::redirect(cli, req, res, next_path, location, error);
+ return detail::redirect(cli, req, res, path, location, error);
}
}
}
auto is_shutting_down = []() { return false; };
if (req.is_chunked_content_provider_) {
- // TODO: Brotli suport
+ // TODO: Brotli support
std::unique_ptr<detail::compressor> compressor;
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) {
return detail::write_content(strm, req.content_provider_, 0,
req.content_length_, is_shutting_down, error);
}
-} // namespace httplib
+}
bool ClientImpl::write_request(Stream &strm, Request &req,
bool close_connection, Error &error) {
return ok;
};
- data_sink.is_writable = [&](void) { return ok && true; };
-
while (ok && offset < content_length) {
if (!content_provider(offset, content_length - offset, data_sink)) {
error = Error::Canceled;
// Send request
if (!write_request(strm, req, close_connection, error)) { return false; }
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ if (is_ssl()) {
+ auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
+ if (!is_proxy_enabled) {
+ char buf[1];
+ if (SSL_peek(socket_.ssl, buf, 1) == 0 &&
+ SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {
+ error = Error::SSLPeerCouldBeClosed_;
+ return false;
+ }
+ }
+ }
+#endif
+
// Receive response and headers
if (!read_response_line(strm, req, res) ||
!detail::read_headers(strm, res.headers)) {
bool has_data = true;
cur_sink.write = sink.write;
cur_sink.done = [&]() { has_data = false; };
- cur_sink.is_writable = sink.is_writable;
if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
return false;
return;
}
- // Otherwise, sitll holding the mutex, we can shut everything down ourselves
+ // Otherwise, still holding the mutex, we can shut everything down ourselves
shutdown_ssl(socket_, true);
shutdown_socket(socket_);
close_socket(socket_);
},
[](SSL * /*ssl2*/) { return true; });
- bool ret = false;
+ auto ret = false;
if (ssl) {
ret = detail::process_server_socket_ssl(
svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
ret = false;
}
} else {
+ auto loaded = false;
#ifdef _WIN32
- detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
-#else
- SSL_CTX_set_default_verify_paths(ctx_);
-#endif
+ loaded =
+ detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
+#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
+#if TARGET_OS_OSX
+ loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));
+#endif // TARGET_OS_OSX
+#endif // _WIN32
+ if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }
}
});
if (alt_names) {
auto dsn_matched = false;
- auto ip_mached = false;
+ auto ip_matched = false;
auto count = sk_GENERAL_NAME_num(alt_names);
case GEN_IPADD:
if (!memcmp(&addr6, name, addr_len) ||
!memcmp(&addr, name, addr_len)) {
- ip_mached = true;
+ ip_matched = true;
}
break;
}
}
}
- if (dsn_matched || ip_mached) { ret = true; }
+ if (dsn_matched || ip_matched) { ret = true; }
}
GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
//
// httplib.h
//
-// Copyright (c) 2022 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.11.4"
+#define CPPHTTPLIB_VERSION "0.12.3"
/*
* Configuration
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
-#endif //_WIN32
+#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
+#include <TargetConditionals.h>
+#if TARGET_OS_OSX
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#endif // TARGET_OS_OSX
+#endif // _WIN32
#include <openssl/err.h>
#include <openssl/evp.h>
}
};
+// This is based on
+// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
+
+struct scope_exit {
+ explicit scope_exit(std::function<void(void)> &&f)
+ : exit_function(std::move(f)), execute_on_destruction{true} {}
+
+ scope_exit(scope_exit &&rhs)
+ : exit_function(std::move(rhs.exit_function)),
+ execute_on_destruction{rhs.execute_on_destruction} {
+ rhs.release();
+ }
+
+ ~scope_exit() {
+ if (execute_on_destruction) { this->exit_function(); }
+ }
+
+ void release() { this->execute_on_destruction = false; }
+
+private:
+ scope_exit(const scope_exit &) = delete;
+ void operator=(const scope_exit &) = delete;
+ scope_exit &operator=(scope_exit &&) = delete;
+
+ std::function<void(void)> exit_function;
+ bool execute_on_destruction;
+};
+
} // namespace detail
using Headers = std::multimap<std::string, std::string, detail::ci>;
std::function<bool(const char *data, size_t data_len)> write;
std::function<void()> done;
- std::function<bool()> is_writable;
+ std::function<void(const Headers &trailer)> done_with_trailer;
std::ostream os;
private:
bool has_file(const std::string &key) const;
MultipartFormData get_file_value(const std::string &key) const;
+ std::vector<MultipartFormData> get_file_values(const std::string &key) const;
// private members...
size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
bool listen(const std::string &host, int port, int socket_flags = 0);
bool is_running() const;
+ void wait_until_ready() const;
void stop();
std::function<TaskQueue *(void)> new_task_queue;
bool &connection_closed,
const std::function<void(Request &)> &setup_request);
- std::atomic<socket_t> svr_sock_;
+ std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
- MultipartContentHeader mulitpart_header,
+ MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver);
virtual bool process_and_close_socket(socket_t sock);
};
std::vector<MountPointEntry> base_dirs_;
- std::atomic<bool> is_running_;
+ std::atomic<bool> is_running_{false};
+ std::atomic<bool> done_{false};
std::map<std::string, std::string> file_extension_and_mimetype_map_;
Handler file_request_handler_;
Handlers get_handlers_;
UnsupportedMultipartBoundaryChars,
Compression,
ConnectionTimeout,
+
+ // For internal use only
+ SSLPeerCouldBeClosed_,
};
std::string to_string(const Error error);
bool is_open() const { return sock != INVALID_SOCKET; }
};
- Result send_(Request &&req);
-
virtual bool create_and_connect_socket(Socket &socket, Error &error);
// All of:
void copy_settings(const ClientImpl &rhs);
- // Socket endoint information
+ // Socket endpoint information
const std::string host_;
const int port_;
const std::string host_and_port_;
Logger logger_;
private:
+ bool send_(Request &req, Response &res, Error &error);
+ Result send_(Request &&req);
+
socket_t create_client_socket(Error &error) const;
bool read_response_line(Stream &strm, const Request &req, Response &res);
bool write_request(Stream &strm, Request &req, bool close_connection,
inline std::string to_string(const Error error) {
switch (error) {
- case Error::Success: return "Success";
- case Error::Connection: return "Connection";
- case Error::BindIPAddress: return "BindIPAddress";
- case Error::Read: return "Read";
- case Error::Write: return "Write";
- case Error::ExceedRedirectCount: return "ExceedRedirectCount";
- case Error::Canceled: return "Canceled";
- case Error::SSLConnection: return "SSLConnection";
- case Error::SSLLoadingCerts: return "SSLLoadingCerts";
- case Error::SSLServerVerification: return "SSLServerVerification";
+ case Error::Success: return "Success (no error)";
+ case Error::Connection: return "Could not establish connection";
+ case Error::BindIPAddress: return "Failed to bind IP address";
+ case Error::Read: return "Failed to read connection";
+ case Error::Write: return "Failed to write connection";
+ case Error::ExceedRedirectCount: return "Maximum redirect count exceeded";
+ case Error::Canceled: return "Connection handling canceled";
+ case Error::SSLConnection: return "SSL connection failed";
+ case Error::SSLLoadingCerts: return "SSL certificate loading failed";
+ case Error::SSLServerVerification: return "SSL server verification failed";
case Error::UnsupportedMultipartBoundaryChars:
- return "UnsupportedMultipartBoundaryChars";
- case Error::Compression: return "Compression";
- case Error::ConnectionTimeout: return "ConnectionTimeout";
+ return "Unsupported HTTP multipart boundary characters";
+ case Error::Compression: return "Compression failed";
+ case Error::ConnectionTimeout: return "Connection timed out";
case Error::Unknown: return "Unknown";
default: break;
}
void parse_query_text(const std::string &s, Params ¶ms);
+bool parse_multipart_boundary(const std::string &content_type,
+ std::string &boundary);
+
bool parse_range_header(const std::string &s, Ranges &ranges);
int close_socket(socket_t sock);