#endif
}
-Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
+Error wait_until_socket_is_ready(socket_t sock, time_t sec,
+ time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
- if (poll_res == 0) {
- return Error::ConnectionTimeout;
- }
+ if (poll_res == 0) { return Error::ConnectionTimeout; }
if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
int error = 0;
return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
});
- if (ret == 0) {
- return Error::ConnectionTimeout;
- }
+ if (ret == 0) { return Error::ConnectionTimeout; }
if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int error = 0;
#endif
#ifdef USE_IF2IP
-std::string if2ip(const std::string &ifn) {
+std::string if2ip(int address_family, const std::string &ifn) {
struct ifaddrs *ifap;
getifaddrs(&ifap);
+ std::string addr_candidate;
for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr && ifn == ifa->ifa_name) {
+ if (ifa->ifa_addr && ifn == ifa->ifa_name &&
+ (AF_UNSPEC == address_family ||
+ ifa->ifa_addr->sa_family == address_family)) {
if (ifa->ifa_addr->sa_family == AF_INET) {
auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
char buf[INET_ADDRSTRLEN];
freeifaddrs(ifap);
return std::string(buf, INET_ADDRSTRLEN);
}
+ } else if (ifa->ifa_addr->sa_family == AF_INET6) {
+ auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);
+ if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {
+ char buf[INET6_ADDRSTRLEN] = {};
+ if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {
+ // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL
+ auto s6_addr_head = sa->sin6_addr.s6_addr[0];
+ if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
+ addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
+ } else {
+ freeifaddrs(ifap);
+ return std::string(buf, INET6_ADDRSTRLEN);
+ }
+ }
+ }
}
}
}
freeifaddrs(ifap);
- return std::string();
+ return addr_candidate;
}
#endif
[&](socket_t sock2, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
#ifdef USE_IF2IP
- auto ip = if2ip(intf);
- if (ip.empty()) { ip = intf; }
- if (!bind_ip_address(sock2, ip.c_str())) {
+ 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.c_str())) {
error = Error::BindIPAddress;
return false;
}
}
error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
connection_timeout_usec);
- if (error != Error::Success) {
- return false;
- }
+ if (error != Error::Success) { return false; }
}
set_nonblocking(sock2, false);
default: return nullptr;
case "css"_t: return "text/css";
case "csv"_t: return "text/csv";
- case "txt"_t: return "text/plain";
- case "vtt"_t: return "text/vtt";
case "htm"_t:
case "html"_t: return "text/html";
+ case "js"_t:
+ case "mjs"_t: return "text/javascript";
+ case "txt"_t: return "text/plain";
+ case "vtt"_t: return "text/vtt";
case "apng"_t: return "image/apng";
case "avif"_t: return "image/avif";
case "7z"_t: return "application/x-7z-compressed";
case "atom"_t: return "application/atom+xml";
case "pdf"_t: return "application/pdf";
- case "js"_t:
- case "mjs"_t: return "application/javascript";
case "json"_t: return "application/json";
case "rss"_t: return "application/rss+xml";
case "tar"_t: return "application/x-tar";
}
bool can_compress_content_type(const std::string &content_type) {
- return (!content_type.rfind("text/", 0) &&
- content_type != "text/event-stream") ||
- content_type == "image/svg+xml" ||
- content_type == "application/javascript" ||
- content_type == "application/json" ||
- content_type == "application/xml" ||
- content_type == "application/protobuf" ||
- content_type == "application/xhtml+xml";
+ using udl::operator""_t;
+
+ auto tag = str2tag(content_type);
+
+ switch (tag) {
+ case "image/svg+xml"_t:
+ case "application/javascript"_t:
+ case "application/json"_t:
+ case "application/xml"_t:
+ case "application/protobuf"_t:
+ case "application/xhtml+xml"_t: return true;
+
+ default:
+ return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t;
+ }
}
EncodingType encoding_type(const Request &req, const Response &res) {
assert((flush == Z_FINISH && ret == Z_STREAM_END) ||
(flush == Z_NO_FLUSH && ret == Z_OK));
assert(strm_.avail_in == 0);
-
} while (data_length > 0);
return true;
if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
return ret;
});
-}
+} // namespace detail
ssize_t write_headers(Stream &strm, const Headers &headers) {
ssize_t write_len = 0;
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(
- "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
- "\"(.*?)\")?\\s*$",
+ R"~(^Content-Disposition:\s*form-data;\s*name="(.*?)"(?:;\s*filename="(.*?)")?(?:;\s*filename\*=\S+)?\s*$)~",
std::regex_constants::icase);
+
static const std::string dash_ = "--";
static const std::string crlf_ = "\r\n";
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
-template <typename CTX, typename Init, typename Update, typename Final>
-std::string message_digest(const std::string &s, Init init,
- Update update, Final final,
- size_t digest_length) {
- std::vector<unsigned char> md(digest_length, 0);
- CTX ctx;
- init(&ctx);
- update(&ctx, s.data(), s.size());
- final(md.data(), &ctx);
+std::string message_digest(const std::string &s, const EVP_MD *algo) {
+ auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(
+ EVP_MD_CTX_new(), EVP_MD_CTX_free);
+
+ unsigned int hash_length = 0;
+ unsigned char hash[EVP_MAX_MD_SIZE];
+
+ EVP_DigestInit_ex(context.get(), algo, nullptr);
+ EVP_DigestUpdate(context.get(), s.c_str(), s.size());
+ EVP_DigestFinal_ex(context.get(), hash, &hash_length);
std::stringstream ss;
- for (auto c : md) {
- ss << std::setfill('0') << std::setw(2) << std::hex << (unsigned int)c;
+ for (auto i = 0u; i < hash_length; ++i) {
+ ss << std::hex << std::setw(2) << std::setfill('0')
+ << (unsigned int)hash[i];
}
+
return ss.str();
}
std::string MD5(const std::string &s) {
- return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
- MD5_DIGEST_LENGTH);
+ return message_digest(s, EVP_md5());
}
std::string SHA_256(const std::string &s) {
- return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final,
- SHA256_DIGEST_LENGTH);
+ return message_digest(s, EVP_sha256());
}
std::string SHA_512(const std::string &s) {
- return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final,
- SHA512_DIGEST_LENGTH);
+ return message_digest(s, EVP_sha512());
}
#endif
public:
WSInit() {
WSADATA wsaData;
- WSAStartup(0x0002, &wsaData);
+ if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;
}
- ~WSInit() { WSACleanup(); }
+ ~WSInit() {
+ if (is_valid_) WSACleanup();
+ }
+
+ bool is_valid_ = false;
};
static WSInit wsinit_;
addrs.push_back(ip);
}
}
+
+ freeaddrinfo(result);
}
std::string append_query_params(const char *path, const Params ¶ms) {
ssize_t SocketStream::write(const char *ptr, size_t size) {
if (!is_writable()) { return -1; }
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(_WIN64)
size =
(std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
#endif
if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; }
{
+ // Skip URL fragment
+ for (size_t i = 0; i < req.target.size(); i++) {
+ if (req.target[i] == '#') {
+ req.target.erase(i);
+ break;
+ }
+ }
+
size_t count = 0;
detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
})) {
const auto &content_type = req.get_header_value("Content-Type");
if (!content_type.find("application/x-www-form-urlencoded")) {
- if (req.body.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
+ if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
res.status = 413; // NOTE: should be 414?
return false;
}
if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); }
+#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
if (!req.has_header("User-Agent")) {
- req.headers.emplace("User-Agent", "cpp-httplib/0.10.1");
+ auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
+ req.headers.emplace("User-Agent", agent);
}
+#endif
if (req.body.empty()) {
if (req.content_provider_) {
auto last = offset + data_len == content_length;
auto ret = compressor.compress(
- data, data_len, last, [&](const char *data, size_t data_len) {
- req.body.append(data, data_len);
+ data, data_len, last, [&](const char *compressed_data, size_t compressed_data_len) {
+ req.body.append(compressed_data, compressed_data_len);
return true;
});
void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
-void ClientImpl::set_hostname_addr_map(
- const std::map<std::string, std::string> addr_map) {
+void
+ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
addr_map_ = std::move(addr_map);
}
}
ssize_t SSLSocketStream::read(char *ptr, size_t size) {
+ size_t readbytes = 0;
if (SSL_pending(ssl_) > 0) {
- return SSL_read(ssl_, ptr, static_cast<int>(size));
- } else if (is_readable()) {
- auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
- if (ret < 0) {
- auto err = SSL_get_error(ssl_, ret);
- int n = 1000;
+ auto ret = SSL_read_ex(ssl_, ptr, size, &readbytes);
+ if (ret == 1) { return static_cast<ssize_t>(readbytes); }
+ if (SSL_get_error(ssl_, ret) == SSL_ERROR_ZERO_RETURN) { return 0; }
+ return -1;
+ }
+ if (!is_readable()) { return -1; }
+
+ auto ret = SSL_read_ex(ssl_, ptr, size, &readbytes);
+ if (ret == 1) { return static_cast<ssize_t>(readbytes); }
+ auto err = SSL_get_error(ssl_, ret);
+ int n = 1000;
#ifdef _WIN32
- while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||
- (err == SSL_ERROR_SYSCALL &&
- WSAGetLastError() == WSAETIMEDOUT))) {
+ while (--n >= 0 &&
+ (err == SSL_ERROR_WANT_READ ||
+ (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
#else
- while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
+ while (--n >= 0 && err == SSL_ERROR_WANT_READ) {
#endif
- if (SSL_pending(ssl_) > 0) {
- return SSL_read(ssl_, ptr, static_cast<int>(size));
- } else if (is_readable()) {
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- ret = SSL_read(ssl_, ptr, static_cast<int>(size));
- if (ret >= 0) { return ret; }
- err = SSL_get_error(ssl_, ret);
- } else {
- return -1;
- }
- }
+ if (SSL_pending(ssl_) > 0) {
+ ret = SSL_read_ex(ssl_, ptr, size, &readbytes);
+ if (ret == 1) { return static_cast<ssize_t>(readbytes); }
+ if (SSL_get_error(ssl_, ret) == SSL_ERROR_ZERO_RETURN) { return 0; }
+ return -1;
}
- return ret;
+ if (!is_readable()) { return -1; }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ ret = SSL_read_ex(ssl_, ptr, size, &readbytes);
+ if (ret == 1) { return static_cast<ssize_t>(readbytes); }
+ err = SSL_get_error(ssl_, ret);
}
+ if (err == SSL_ERROR_ZERO_RETURN) { return 0; }
return -1;
}
ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
- if (is_writable()) {
- auto ret = SSL_write(ssl_, ptr, static_cast<int>(size));
- if (ret < 0) {
- auto err = SSL_get_error(ssl_, ret);
- int n = 1000;
+ if (!is_writable()) { return -1; }
+ size_t written = 0;
+ auto ret = SSL_write_ex(ssl_, ptr, size, &written);
+ if (ret == 1) { return static_cast<ssize_t>(written); }
+ auto err = SSL_get_error(ssl_, ret);
+ int n = 1000;
#ifdef _WIN32
- while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||
- (err == SSL_ERROR_SYSCALL &&
- WSAGetLastError() == WSAETIMEDOUT))) {
+ while (--n >= 0 &&
+ (err == SSL_ERROR_WANT_WRITE ||
+ (err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT))) {
#else
- while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
+ while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
#endif
- if (is_writable()) {
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- ret = SSL_write(ssl_, ptr, static_cast<int>(size));
- if (ret >= 0) { return ret; }
- err = SSL_get_error(ssl_, ret);
- } else {
- return -1;
- }
- }
- }
- return ret;
+ if (!is_writable()) { return -1; }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ ret = SSL_write_ex(ssl_, ptr, size, &written);
+ if (ret == 1) { return static_cast<ssize_t>(written); }
+ err = SSL_get_error(ssl_, ret);
}
+ if (err == SSL_ERROR_ZERO_RETURN) { return 0; }
return -1;
}
// SSL HTTP server implementation
SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
const char *client_ca_cert_file_path,
- const char *client_ca_cert_dir_path) {
+ const char *client_ca_cert_dir_path,
+ const char *private_key_password) {
ctx_ = SSL_CTX_new(TLS_server_method());
if (ctx_) {
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_,
+ (char *)private_key_password);
+ }
+
if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
1) {
bool SSLServer::process_and_close_socket(socket_t sock) {
auto ssl = detail::ssl_new(
sock, ctx_, ctx_mutex_,
- [&](SSL *ssl) {
+ [&](SSL *ssl2) {
return detail::ssl_connect_or_accept_nonblocking(
- sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_);
+ sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_);
},
- [](SSL * /*ssl*/) { return true; });
+ [](SSL * /*ssl2*/) { return true; });
bool ret = false;
if (ssl) {
bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
auto ssl = detail::ssl_new(
socket.sock, ctx_, ctx_mutex_,
- [&](SSL *ssl) {
+ [&](SSL *ssl2) {
if (server_certificate_verification_) {
if (!load_certs()) {
error = Error::SSLLoadingCerts;
return false;
}
- SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
+ SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);
}
if (!detail::ssl_connect_or_accept_nonblocking(
- socket.sock, ssl, SSL_connect, connection_timeout_sec_,
+ socket.sock, ssl2, SSL_connect, connection_timeout_sec_,
connection_timeout_usec_)) {
error = Error::SSLConnection;
return false;
}
if (server_certificate_verification_) {
- verify_result_ = SSL_get_verify_result(ssl);
+ verify_result_ = SSL_get_verify_result(ssl2);
if (verify_result_ != X509_V_OK) {
error = Error::SSLServerVerification;
return false;
}
- auto server_cert = SSL_get_peer_certificate(ssl);
+ auto server_cert = SSL_get_peer_certificate(ssl2);
if (server_cert == nullptr) {
error = Error::SSLServerVerification;
return true;
},
- [&](SSL *ssl) {
- SSL_set_tlsext_host_name(ssl, host_.c_str());
+ [&](SSL *ssl2) {
+ SSL_set_tlsext_host_name(ssl2, host_.c_str());
return true;
});
void Client::stop() { cli_->stop(); }
-void Client::set_hostname_addr_map(
- const std::map<std::string, std::string> addr_map) {
+void
+Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
cli_->set_hostname_addr_map(std::move(addr_map));
}