Compare commits

..

33 Commits

Author SHA1 Message Date
yhirose 5e6f973b99 Release v0.10.9 2022-07-08 17:40:05 -04:00
yhirose 7ed77b02ad Disable YouTubeNoSSLDigest 2022-07-08 17:40:05 -04:00
yhirose 127a64d5a0 Skip preamble and epilogue in multipart/form-data (Fix #1317) (#1320)
* fix: skip MIME preamble (#1317)

* Skip epilogue in multipart/form-data

Co-authored-by: Gavin1937 <71205842+Gavin1937@users.noreply.github.com>
2022-07-08 17:26:50 -04:00
yhirose caa31aafda Accept large data transfer over SSL (Fix #1261, Close #1312) 2022-07-02 07:50:33 -04:00
yhirose dae318495f Revert "Accept large data transfer over SSL (#1261)"
This reverts commit 307b729549.
2022-07-02 07:18:59 -04:00
Andrea Pappacoda 305a7abcb9 fix: update CPPHTTPLIB_VERSION to 0.10.8 (#1305) 2022-06-08 16:44:10 -04:00
yhirose 219d13b718 Fix #1303 2022-06-07 09:52:08 -04:00
conghuawang df20c27696 resolve http server can't send file large than 2GB (Fix #1290) (#1294)
* resolve problem: http server can't send file large than 2GB.
add unit test for http server send large file.
add /bigobj compile option to msvc x64.

* disable unit test "ServerLargeContentTest" due to out-of-memory on GitHub Actions.
2022-05-27 11:56:20 -04:00
yhirose a5a62768c0 Fix #1292 (#1296) 2022-05-27 11:54:43 -04:00
yhirose 4001637beb Added CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 2022-05-26 10:16:32 -04:00
Martín Córdova 47044c05a8 Fix compile error with MINGW-64 GCC-12.1.0 (#1283) 2022-05-24 07:16:54 -04:00
Andrea Pappacoda a449d82723 build(cmake): minor tweaks (#1274)
- Enable THREADS_PREFER_PTHREAD_FLAG to use -pthread where supported
- Remove low-level compile features (closes #1272)
- Remove unneeded DESTINATION options where possible
2022-05-17 07:02:44 -04:00
yhirose fee8e97b4e Rename fuzzing test corpus for #1264 2022-05-03 14:53:59 -04:00
yhirose 72d9ed4056 Added fuzzing corpus for #1264 2022-05-02 18:06:14 -04:00
Andrea Pappacoda 1be1b3a86d build(meson): don't require python3 (#1267)
Thanks to abf3a67dd0 the use of python3
isn't required anymore to configure the build, so I moved the
find_program('python3') inside the "if compile" block.

This makes it possible to configure cpp-httplib on systems where python
isn't available with tools like muon: https://sr.ht/~lattis/muon/
2022-04-30 17:40:47 -04:00
yhirose 9452c0a4b6 Release v0.10.7 2022-04-28 10:21:14 -04:00
Yoshiki Matsuda 307b729549 Accept large data transfer over SSL (#1261)
* Add large data transfer test

* Replace `SSL_read` and `SSL_write` with `ex` functions

* Reflect review comment

* Fix return value of `SSLSocketStream::read/write`

* Fix return value in the case of `SSL_ERROR_ZERO_RETURN`

* Disable `LargeDataTransfer` test due to OoM in CI
2022-04-27 21:08:39 -04:00
mylogin 696239d6e1 Link Windows crypto libs only when CPPHTTPLIB_OPENSSL_SUPPORT is set (#1254) 2022-04-20 22:04:55 -04:00
Andrea Pappacoda 6929d90353 build(meson): allow using OpenSSL 3.0 (#1256)
Following 0857eba17b cpp-httplib is fully compatible with OpenSSL versions newer than 1.1.1
2022-04-20 21:39:52 -04:00
yhirose 348d032029 Updated README 2022-04-19 23:02:30 -04:00
Andrea Pappacoda d1d3fcdfd5 build(meson): mark *_encrypted_pem as test deps (#1255)
Meson only runs required targets. The key_encrypted_pem and
cert_encrypted_pem targets added in 020b0db090
and 8191fd8e6c weren't added to the list
of targets required by the test target, so the generation of the
encrypted certs was skipped, resulting in the failure of
BindServerTest.BindAndListenSeparatelySSLEncryptedKey.
2022-04-19 07:12:00 -04:00
Eli Schwartz abf3a67dd0 meson: fix regression that broke extracting version (#1253)
* meson: fix regression that broke extracting version

In commit 33f67386fe the code that
heuristically parsed the version broke due to the version being moved
around into a more easily accessible define.

While we are at it, pass the exact path of httplib.h to un-break usage
as a meson subproject. This was broken in commit
8ecdb11979 which checked the return code
of trying to get the version; it was always broken, but formerly failed
in silence and resulted in no version number.

* meson: use the compiler builtins to extract the version from the header

As a convenient string define, it is now possible to ask the
preprocessor what the version of cpp-httplib is. This can be used from
meson too, in order to avoid encoding C++ file structure into python
regexes.
2022-04-19 07:11:51 -04:00
yhirose d87abeecf0 Release v0.10.6 2022-04-17 17:34:48 -04:00
yhirose 4e28e4f741 Fix #1251 2022-04-17 11:53:41 -04:00
yhirose 80a55cedeb Removed Repl.it examples 2022-04-16 21:11:17 -04:00
yhirose d05c343602 Release v0.10.5 2022-04-16 21:02:30 -04:00
yhirose 33f67386fe Fix #1249 2022-04-16 08:54:56 -04:00
yhirose 56d8168dc4 clangformat 2022-04-16 08:52:55 -04:00
greenfish 5d87cc0558 resolve compiler warnings (#1246)
* resolve compiler warnings

- check `WSAStartup` return.
- `const` is not suitable for `std::move`.

* resolve compiler warnings

- bool startup => bool is_valid_.
- remove `const` not removed.
2022-04-14 11:46:10 -04:00
yhirose cb41947eb4 Fix #1235 (#1243)
* Fix #1235

* fix BindIPAddress error (#1242)

* Code cleanup

* Added a unit test

* Commented out 'SSLClientTest.SetInterfaceWithINET6'

* Fixed incorrect return value from if2ip

* Removed if_nametoindex call

Co-authored-by: Kotarou <2918558+CyberKoo@users.noreply.github.com>
2022-04-13 21:32:46 -04:00
Kotarou 0857eba17b replace deprecated OpenSSL functions with evp functions (#1241) 2022-04-11 13:40:58 -04:00
Andrea Pappacoda 020b0db090 build(meson): generate key_encrypted.pem (#1221)
8191fd8e6c only added one of the two files
2022-03-20 12:21:45 -04:00
Andrea Pappacoda bf0760fde4 fix: update user agent (#1218) 2022-03-18 18:12:51 -04:00
11 changed files with 307 additions and 119 deletions
+7 -18
View File
@@ -60,10 +60,10 @@
]]
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
# Get the user agent and use it as a version
# This gets the string with the user agent from the header.
# Get the CPPHTTPLIB_VERSION value and use it as a version
# This gets the string with the CPPHTTPLIB_VERSION value from the header.
# This is so the maintainer doesn't actually need to update this manually.
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"")
# Needed since git tags have "v" prefixing them.
# Also used if the fallback to user agent string is being used.
@@ -100,6 +100,7 @@ if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
endif()
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
set(THREADS_PREFER_PTHREAD_FLAG true)
find_package(Threads REQUIRED)
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
if(HTTPLIB_REQUIRE_OPENSSL)
@@ -160,7 +161,7 @@ if(HTTPLIB_COMPILE)
ERROR_VARIABLE _httplib_split_error
)
if(_httplib_split_error)
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
endif()
# split.py puts output in "out"
@@ -187,19 +188,9 @@ endif()
# Only useful if building in-tree, versus using it from an installation.
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
# Might be missing some, but this list is somewhat comprehensive
# Require C++11
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
cxx_std_11
cxx_nullptr
cxx_lambdas
cxx_override
cxx_defaulted_functions
cxx_attribute_deprecated
cxx_auto_type
cxx_decltype
cxx_deleted_functions
cxx_range_for
cxx_sizeof_member
)
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
@@ -274,11 +265,9 @@ endif()
# and linkage information (doesn't find deps though).
install(TARGETS ${PROJECT_NAME}
EXPORT httplibTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+1 -6
View File
@@ -48,17 +48,12 @@ res->status;
res->body;
```
### Try out the examples on Repl.it!
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
SSL Support
-----------
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
NOTE: cpp-httplib currently supports only version 1.1.1.
NOTE: cpp-httplib currently supports only version 1.1.1 and 3.0.
```c++
#define CPPHTTPLIB_OPENSSL_SUPPORT
+101 -63
View File
@@ -1,13 +1,15 @@
//
// httplib.h
//
// Copyright (c) 2021 Yuji Hirose. All rights reserved.
// Copyright (c) 2022 Yuji Hirose. All rights reserved.
// MIT License
//
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.10.9"
/*
* Configuration
*/
@@ -72,6 +74,10 @@
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
#endif
#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
#endif
#ifndef CPPHTTPLIB_TCP_NODELAY
#define CPPHTTPLIB_TCP_NODELAY false
#endif
@@ -142,8 +148,6 @@ using ssize_t = int;
#include <io.h>
#include <winsock2.h>
#include <wincrypt.h>
#include <ws2tcpip.h>
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
@@ -152,8 +156,6 @@ using ssize_t = int;
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
#ifndef strcasecmp
@@ -168,8 +170,8 @@ using socket_t = SOCKET;
#else // not _WIN32
#include <arpa/inet.h>
#include <cstring>
#include <ifaddrs.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef __linux__
@@ -198,6 +200,7 @@ using socket_t = int;
#include <cctype>
#include <climits>
#include <condition_variable>
#include <cstring>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
@@ -217,17 +220,24 @@ using socket_t = int;
#include <thread>
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
#include <wincrypt.h>
// these are defined in wincrypt.h and it breaks compilation if BoringSSL is
// used
#ifdef _WIN32
#undef X509_NAME
#undef X509_CERT_PAIR
#undef X509_EXTENSIONS
#undef PKCS7_SIGNER_INFO
#ifdef _MSC_VER
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
#endif //_WIN32
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
@@ -973,7 +983,7 @@ public:
void stop();
void set_hostname_addr_map(const std::map<std::string, std::string> addr_map);
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
void set_default_headers(Headers headers);
@@ -1308,7 +1318,7 @@ public:
void stop();
void set_hostname_addr_map(const std::map<std::string, std::string> addr_map);
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
void set_default_headers(Headers headers);
@@ -2649,11 +2659,14 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
#endif
#ifdef USE_IF2IP
inline std::string if2ip(const std::string &ifn) {
inline 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];
@@ -2661,11 +2674,26 @@ inline std::string if2ip(const std::string &ifn) {
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
@@ -2680,9 +2708,9 @@ inline socket_t create_client_socket(
[&](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;
}
@@ -3766,6 +3794,7 @@ public:
switch (state_) {
case 0: { // Initial boundary
auto pattern = dash_ + boundary_ + crlf_;
buf_erase(buf_find(pattern));
if (pattern.size() > buf_size()) { return true; }
if (!buf_start_with(pattern)) { return false; }
buf_erase(pattern.size());
@@ -3859,17 +3888,13 @@ public:
if (buf_start_with(pattern)) {
buf_erase(pattern.size());
is_valid_ = true;
state_ = 5;
buf_erase(buf_size()); // Remove epilogue
} else {
return true;
}
}
break;
}
case 5: { // Done
is_valid_ = false;
return false;
}
}
}
@@ -4146,36 +4171,36 @@ inline bool has_crlf(const char *s) {
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
template <typename CTX, typename Init, typename Update, typename Final>
inline 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);
inline 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();
}
inline 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());
}
inline 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());
}
inline 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
@@ -4212,10 +4237,14 @@ class WSInit {
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_;
@@ -4379,6 +4408,8 @@ inline void hosted_at(const char *hostname, std::vector<std::string> &addrs) {
addrs.push_back(ip);
}
}
freeaddrinfo(result);
}
inline std::string append_query_params(const char *path, const Params &params) {
@@ -4659,7 +4690,7 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
inline 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
@@ -5064,14 +5095,16 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
// Flush buffer
auto &data = bstrm.get_buffer();
strm.write(data.data(), data.size());
detail::write_data(strm, data.data(), data.size());
}
// Body
auto ret = true;
if (req.method != "HEAD") {
if (!res.body.empty()) {
if (!strm.write(res.body)) { ret = false; }
if (!detail::write_data(strm, res.body.data(), res.body.size())) {
ret = false;
}
} else if (res.content_provider_) {
if (write_content_with_provider(strm, req, res, boundary, content_type)) {
res.content_provider_success_ = true;
@@ -5161,7 +5194,7 @@ inline 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_REQUEST_URI_MAX_LENGTH) {
if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
res.status = 413; // NOTE: should be 414?
return false;
}
@@ -6171,7 +6204,8 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
if (!req.has_header("User-Agent")) {
req.headers.emplace("User-Agent", "cpp-httplib/0.10.3");
auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
req.headers.emplace("User-Agent", agent);
}
#endif
@@ -6287,8 +6321,9 @@ inline std::unique_ptr<Response> ClientImpl::send_with_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;
});
@@ -6949,8 +6984,8 @@ inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; }
inline void ClientImpl::set_hostname_addr_map(
const std::map<std::string, std::string> addr_map) {
inline void
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
addr_map_ = std::move(addr_map);
}
@@ -7226,7 +7261,10 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
if (is_writable()) {
auto ret = SSL_write(ssl_, ptr, static_cast<int>(size));
auto handle_size = static_cast<int>(
std::min<size_t>(size, std::numeric_limits<int>::max()));
auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
if (ret < 0) {
auto err = SSL_get_error(ssl_, ret);
int n = 1000;
@@ -7239,7 +7277,7 @@ inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
#endif
if (is_writable()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
ret = SSL_write(ssl_, ptr, static_cast<int>(size));
ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
if (ret >= 0) { return ret; }
err = SSL_get_error(ssl_, ret);
} else {
@@ -7344,11 +7382,11 @@ inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
inline 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) {
@@ -7542,31 +7580,31 @@ inline bool SSLClient::load_certs() {
inline 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;
@@ -7583,8 +7621,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
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;
});
@@ -8075,8 +8113,8 @@ inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
inline void Client::stop() { cli_->stop(); }
inline void Client::set_hostname_addr_map(
const std::map<std::string, std::string> addr_map) {
inline void
Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
cli_->set_hostname_addr_map(std::move(addr_map));
}
+8 -10
View File
@@ -19,22 +19,18 @@ project(
# Check just in case downstream decides to edit the source
# and add a project version
version = meson.project_version()
python3 = find_program('python3')
if version == 'undefined'
# Meson doesn't have regular expressions, but since it is implemented
# in python we can be sure we can use it to parse the file manually
version = run_command(
python3, '-c', 'import re; raw_version = re.search("User\-Agent.*cpp\-httplib/([0-9]+\.?)+", open("httplib.h").read()).group(0); print(re.search("([0-9]+\\.?)+", raw_version).group(0))',
check: true
).stdout().strip()
cxx = meson.get_compiler('cpp')
version = cxx.get_define('CPPHTTPLIB_VERSION',
prefix: '#include <httplib.h>',
include_directories: include_directories('.')).strip('"')
assert(version != '', 'failed to get version from httplib.h')
endif
message('cpp-httplib version ' + version)
deps = [dependency('threads')]
args = []
openssl_dep = dependency('openssl', version: ['>=1.1.1', '<1.1.2'], required: get_option('cpp-httplib_openssl'))
openssl_dep = dependency('openssl', version: '>=1.1.1', required: get_option('cpp-httplib_openssl'))
if openssl_dep.found()
deps += openssl_dep
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
@@ -65,6 +61,8 @@ endif
cpp_httplib_dep = dependency('', required: false)
if get_option('cpp-httplib_compile')
python3 = find_program('python3')
httplib_ch = custom_target(
'split',
input: 'httplib.h',
+1 -1
View File
@@ -5,5 +5,5 @@
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
+1 -1
View File
@@ -1,5 +1,5 @@
CXX = clang++
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
PREFIX = /usr/local
#PREFIX = $(shell brew --prefix)
File diff suppressed because one or more lines are too long
+8
View File
@@ -33,6 +33,12 @@ cert2_pem = custom_target(
command: [openssl, 'req', '-x509', '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
)
key_encrypted_pem = custom_target(
'key_encrypted_pem',
output: 'key_encrypted.pem',
command: [openssl, 'genrsa', '-passout', 'pass:test123!', '-out', '@OUTPUT@', '2048']
)
cert_encrypted_pem = custom_target(
'cert_encrypted_pem',
input: key_encrypted_pem,
@@ -94,6 +100,8 @@ test(
key_pem,
cert_pem,
cert2_pem,
key_encrypted_pem,
cert_encrypted_pem,
rootca_key_pem,
rootca_cert_pem,
client_key_pem,
+158 -19
View File
@@ -109,11 +109,11 @@ TEST(SplitTest, ParseQueryString) {
detail::split(s.c_str(), s.c_str() + s.size(), '&',
[&](const char *b, const char *e) {
string key, val;
detail::split(b, e, '=', [&](const char *b, const char *e) {
detail::split(b, e, '=', [&](const char *b2, const char *e2) {
if (key.empty()) {
key.assign(b, e);
key.assign(b2, e2);
} else {
val.assign(b, e);
val.assign(b2, e2);
}
});
dic.emplace(key, val);
@@ -1419,10 +1419,9 @@ TEST(InvalidFormatTest, StatusCode) {
TEST(URLFragmentTest, WithFragment) {
Server svr;
svr.Get("/hi",
[](const Request &req, Response &/*res*/) {
EXPECT_TRUE(req.target == "/hi");
});
svr.Get("/hi", [](const Request &req, Response & /*res*/) {
EXPECT_TRUE(req.target == "/hi");
});
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
@@ -3016,8 +3015,10 @@ TEST(GzipDecompressor, ChunkedDecompression) {
httplib::detail::gzip_compressor compressor;
bool result = compressor.compress(
data.data(), data.size(),
/*last=*/true, [&](const char *data, size_t size) {
compressed_data.insert(compressed_data.size(), data, size);
/*last=*/true,
[&](const char *compressed_data_chunk, size_t compressed_data_size) {
compressed_data.insert(compressed_data.size(), compressed_data_chunk,
compressed_data_size);
return true;
});
ASSERT_TRUE(result);
@@ -3036,8 +3037,11 @@ TEST(GzipDecompressor, ChunkedDecompression) {
std::min(compressed_data.size() - chunk_begin, chunk_size);
bool result = decompressor.decompress(
compressed_data.data() + chunk_begin, current_chunk_size,
[&](const char *data, size_t size) {
decompressed_data.insert(decompressed_data.size(), data, size);
[&](const char *decompressed_data_chunk,
size_t decompressed_data_chunk_size) {
decompressed_data.insert(decompressed_data.size(),
decompressed_data_chunk,
decompressed_data_chunk_size);
return true;
});
ASSERT_TRUE(result);
@@ -4369,6 +4373,20 @@ TEST(SSLClientTest, WildcardHostNameMatch_Online) {
ASSERT_EQ(200, res->status);
}
#if 0
TEST(SSLClientTest, SetInterfaceWithINET6) {
auto cli = std::make_shared<httplib::Client>("https://httpbin.org");
ASSERT_TRUE(cli != nullptr);
cli->set_address_family(AF_INET6);
cli->set_interface("en0");
auto res = cli->Get("/get");
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
}
#endif
TEST(SSLClientServerTest, ClientCertPresent) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
CLIENT_CA_CERT_DIR);
@@ -4647,6 +4665,50 @@ TEST(SSLClientServerTest, CustomizeServerSSLCtx) {
t.join();
}
// Disabled due to the out-of-memory problem on GitHub Actions Workflows
TEST(SSLClientServerTest, DISABLED_LargeDataTransfer) {
// prepare large data
std::random_device seed_gen;
std::mt19937 random(seed_gen());
constexpr auto large_size_byte = 2147483648UL + 1048576UL; // 2GiB + 1MiB
std::vector<std::uint32_t> binary(large_size_byte / sizeof(std::uint32_t));
std::generate(binary.begin(), binary.end(), [&random]() { return random(); });
// server
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
ASSERT_TRUE(svr.is_valid());
svr.Post("/binary", [&](const Request &req, Response &res) {
EXPECT_EQ(large_size_byte, req.body.size());
EXPECT_EQ(0, std::memcmp(binary.data(), req.body.data(), large_size_byte));
res.set_content(req.body, "application/octet-stream");
});
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// client POST
SSLClient cli("localhost", PORT);
cli.enable_server_certificate_verification(false);
cli.set_read_timeout(std::chrono::seconds(100));
cli.set_write_timeout(std::chrono::seconds(100));
auto res = cli.Post("/binary", reinterpret_cast<char *>(binary.data()),
large_size_byte, "application/octet-stream");
// compare
EXPECT_EQ(200, res->status);
EXPECT_EQ(large_size_byte, res->body.size());
EXPECT_EQ(0, std::memcmp(binary.data(), res->body.data(), large_size_byte));
// cleanup
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
#endif
#ifdef _WIN32
@@ -4685,6 +4747,40 @@ TEST(SendAPI, SimpleInterface_Online) {
EXPECT_EQ(301, res->status);
}
// Disabled due to out-of-memory problem on GitHub Actions
#ifdef _WIN64
TEST(ServerLargeContentTest, DISABLED_SendLargeContent) {
// allocate content size larger than 2GB in memory
const size_t content_size = 2LL * 1024LL * 1024LL * 1024LL + 1LL;
char *content = (char *)malloc(content_size);
ASSERT_TRUE(content);
Server svr;
svr.Get("/foo", [=](const httplib::Request &req, httplib::Response &resp) {
resp.set_content(content, content_size, "application/octet-stream");
});
auto listen_thread = std::thread([&svr]() { svr.listen(HOST, PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli(HOST, PORT);
auto res = cli.Get("/foo");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ(content_size, res->body.length());
free(content);
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(YahooRedirectTest2, SimpleInterface_Online) {
Client cli("http://yahoo.com");
@@ -4838,15 +4934,15 @@ TEST(MultipartFormDataTest, LargeData) {
return true;
});
EXPECT_TRUE(std::string(files[0].name) == "document");
EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size());
EXPECT_TRUE(files[0].filename == "2MB_data");
EXPECT_TRUE(files[0].content_type == "application/octet-stream");
EXPECT_TRUE(std::string(files[0].name) == "document");
EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size());
EXPECT_TRUE(files[0].filename == "2MB_data");
EXPECT_TRUE(files[0].content_type == "application/octet-stream");
EXPECT_TRUE(files[1].name == "hello");
EXPECT_TRUE(files[1].content == "world");
EXPECT_TRUE(files[1].filename == "");
EXPECT_TRUE(files[1].content_type == "");
EXPECT_TRUE(files[1].name == "hello");
EXPECT_TRUE(files[1].content == "world");
EXPECT_TRUE(files[1].filename == "");
EXPECT_TRUE(files[1].content_type == "");
} else {
std::string body;
content_reader([&](const char *data, size_t data_length) {
@@ -4883,5 +4979,48 @@ TEST(MultipartFormDataTest, LargeData) {
svr.stop();
t.join();
}
TEST(MultipartFormDataTest, WithPreamble) {
Server svr;
svr.Post("/post", [&](const Request &req, Response &res) {
res.set_content("ok", "text/plain");
});
thread t = thread([&] { svr.listen(HOST, PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
const std::string body =
"This is the preamble. It is to be ignored, though it\r\n"
"is a handy place for composition agents to include an\r\n"
"explanatory note to non-MIME conformant readers.\r\n"
"\r\n"
"\r\n"
"--simple boundary\r\n"
"Content-Disposition: form-data; name=\"field1\"\r\n"
"\r\n"
"value1\r\n"
"--simple boundary\r\n"
"Content-Disposition: form-data; name=\"field2\"; "
"filename=\"example.txt\"\r\n"
"\r\n"
"value2\r\n"
"--simple boundary--\r\n"
"This is the epilogue. It is also to be ignored.\r\n";
std::string content_type =
R"(multipart/form-data; boundary="simple boundary")";
Client cli(HOST, PORT);
auto res = cli.Post("/post", body, content_type.c_str());
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
svr.stop();
t.join();
}
#endif
+2
View File
@@ -116,6 +116,7 @@
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -158,6 +159,7 @@
<AdditionalUsingDirectories>
</AdditionalUsingDirectories>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
+1 -1
View File
@@ -82,7 +82,7 @@ TEST(RedirectTest, YouTubeNoSSLBasic) {
RedirectProxyText(cli, "/", true);
}
TEST(RedirectTest, YouTubeNoSSLDigest) {
TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) {
Client cli("youtube.com");
RedirectProxyText(cli, "/", false);
}