Compare commits

..

29 Commits

Author SHA1 Message Date
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
yhirose bb8e45383e Update README 2022-03-17 08:38:15 -04:00
yhirose a1df576e4f Fix #1212 2022-03-16 22:00:40 -04:00
yhirose 7fb0254794 Fix #1215 2022-03-16 12:50:13 -04:00
yhirose c82d1e52cc Fix #1214 2022-03-16 10:56:33 -04:00
yhirose 846151b605 Added a unit test case for large multipart form data 2022-03-16 09:37:13 -04:00
Alexandre Bouvier e44e31dd5b Add soversion (#1209) 2022-03-03 21:06:08 -05:00
yhirose f7b9501662 clangformat 2022-02-27 14:31:22 -05:00
yhirose e12fe4cbbb Performance improvement 2022-02-27 14:30:49 -05:00
yhirose 49d2e1f135 Fix problem with InvalidPort test 2022-02-27 14:29:34 -05:00
Sebastien Blanchet 8191fd8e6c Add optional private key password to SSLServer ctor (#1205) 2022-02-27 14:16:15 -05:00
yhirose d73395e1dc Release v0.10.3 2022-02-22 10:21:27 -05:00
au-ee 64d001162b CPPHTTPLIB_NO_DEFAULT_USER_AGENT skips default user agent (#1201) 2022-02-22 09:39:26 -05:00
yhirose bb00a23116 Apply clangformat 2022-02-09 17:16:47 -05:00
yhirose 63d6e9b91b Removed up.sh and down.sh 2022-02-09 17:16:18 -05:00
Edwin Kofler 66eed5681a Fix typo in Dockerfile (#1187) 2022-02-06 09:15:15 -05:00
Andrea Pappacoda 8ecdb11979 build(meson): always install a pkg-config file (#1182)
A pkg-config file was previously installed only if cpp-httplib was being
built as a compiled library.

Since architecture-independent .pc files
can exist in /usr/share/pkgconfig, it can be useful to install one even
when installing the header-only version (for example, it could be used
by third party projects to easily find out if cpp-httplib is installed
and its version, using something like Meson's `dependency()` or CMake's
`pkg_check_modules()`).

The change makes the Meson build behave a bit more like the CMake one,
as it also always installs a CMake Config file, but here the pkg-config
file gets installed to the correct architecture-independent directory
(`datadir` represents /usr/share on Linux and simiar systems).

Lastly, I made some minor cleanups.
2022-02-03 19:50:49 -05:00
Andrea Pappacoda 894fcc8e02 test: add missing "_Online" suffix (#1183)
This test fails reproducibly in a Debian build chroot, and they generally don't have internet access
2022-01-30 12:34:52 -05:00
Rockybilly 7f43f0f3ff User-Agent update cpp-httplib/0.10.2 (#1181) 2022-01-28 12:27:27 -05:00
11 changed files with 280 additions and 82 deletions
+8 -3
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.
@@ -172,6 +172,11 @@ if(HTTPLIB_COMPILE)
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
)
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${${PROJECT_NAME}_VERSION}
SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR}
)
else()
# This is for header-only.
set(_INTERFACE_OR_PUBLIC INTERFACE)
+9 -5
View File
@@ -48,11 +48,6 @@ 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
-----------
@@ -183,6 +178,15 @@ The followings are built-in mappings:
| webm | video/webm | zip | application/zip |
| mp3 | audio/mp3 | wasm | application/wasm |
### File request handler
```cpp
// The handler is called right before the response is sent to a client
svr.set_file_request_handler([](const Request &req, Response &res) {
...
});
```
NOTE: These static file server methods are not thread-safe.
### Logging
+2 -2
View File
@@ -1,4 +1,4 @@
From alpine as builder
FROM alpine as builder
WORKDIR /src/example
RUN apk add g++ make openssl-dev zlib-dev brotli-dev
COPY ./httplib.h /src
@@ -6,7 +6,7 @@ COPY ./example/hello.cc /src/example
COPY ./example/Makefile /src/example
RUN make hello
From alpine
FROM alpine
RUN apk --no-cache add brotli libstdc++
COPY --from=builder /src/example/hello /bin/hello
CMD ["/bin/hello"]
+106 -60
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.6"
/*
* Configuration
*/
@@ -170,6 +172,7 @@ using socket_t = SOCKET;
#include <arpa/inet.h>
#include <cstring>
#include <ifaddrs.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef __linux__
@@ -227,7 +230,7 @@ using socket_t = int;
#endif
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
@@ -973,7 +976,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 +1311,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);
@@ -1384,7 +1387,8 @@ class SSLServer : public Server {
public:
SSLServer(const char *cert_path, const char *private_key_path,
const char *client_ca_cert_file_path = nullptr,
const char *client_ca_cert_dir_path = nullptr);
const char *client_ca_cert_dir_path = nullptr,
const char *private_key_password = nullptr);
SSLServer(X509 *cert, EVP_PKEY *private_key,
X509_STORE *client_ca_cert_store = nullptr);
@@ -2315,7 +2319,8 @@ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
#endif
}
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
inline 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;
@@ -2325,9 +2330,7 @@ inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec)
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;
@@ -2359,9 +2362,7 @@ inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec)
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;
@@ -2651,11 +2652,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];
@@ -2663,11 +2667,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
@@ -2682,7 +2701,7 @@ inline socket_t create_client_socket(
[&](socket_t sock2, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
#ifdef USE_IF2IP
auto ip = if2ip(intf);
auto ip = if2ip(address_family, intf);
if (ip.empty()) { ip = intf; }
if (!bind_ip_address(sock2, ip.c_str())) {
error = Error::BindIPAddress;
@@ -2703,9 +2722,7 @@ inline socket_t create_client_socket(
}
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);
@@ -2817,10 +2834,12 @@ find_content_type(const std::string &path,
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";
@@ -2852,8 +2871,6 @@ find_content_type(const std::string &path,
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";
@@ -2938,14 +2955,21 @@ inline const char *status_message(int status) {
}
inline 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;
}
}
inline EncodingType encoding_type(const Request &req, const Response &res) {
@@ -3024,7 +3048,6 @@ inline bool gzip_compressor::compress(const char *data, size_t data_length,
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;
@@ -3436,7 +3459,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
return ret;
});
}
} // namespace detail
inline ssize_t write_headers(Stream &strm, const Headers &headers) {
ssize_t write_len = 0;
@@ -3750,10 +3773,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(
"^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";
@@ -4143,36 +4167,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
@@ -4209,10 +4233,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_;
@@ -4963,6 +4991,14 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
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(), '?',
@@ -6158,9 +6194,12 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
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_) {
@@ -6936,8 +6975,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);
}
@@ -7253,7 +7292,8 @@ static SSLInit sslinit_;
// SSL HTTP server implementation
inline 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_) {
@@ -7263,6 +7303,12 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
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) {
@@ -8055,8 +8101,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));
}
+12 -3
View File
@@ -19,12 +19,13 @@ project(
# Check just in case downstream decides to edit the source
# and add a project version
version = meson.project_version()
python = import('python').find_installation('python3')
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(
python, '-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))'
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()
endif
@@ -68,7 +69,7 @@ if get_option('cpp-httplib_compile')
'split',
input: 'httplib.h',
output: ['httplib.cc', 'httplib.h'],
command: [python, files('split.py'), '--out', meson.current_build_dir()],
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
install: true,
install_dir: [false, get_option('includedir')]
)
@@ -92,6 +93,14 @@ if get_option('cpp-httplib_compile')
else
install_headers('httplib.h')
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: include_directories('.'))
import('pkgconfig').generate(
name: 'cpp-httplib',
description: 'A C++ HTTP/HTTPS server and client library',
install_dir: join_paths(get_option('datadir'), 'pkgconfig'),
url: 'https://github.com/yhirose/cpp-httplib',
version: version
)
endif
if meson.version().version_compare('>=0.54.0')
+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 (requires Python 3)')
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file')
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
+2
View File
@@ -65,6 +65,8 @@ cert.pem:
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
openssl genrsa 2048 > client.key.pem
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem
openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem
#c_rehash .
clean:
+13
View File
@@ -33,6 +33,19 @@ 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,
output: 'cert_encrypted.pem',
command: [openssl, 'req', '-x509', '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
)
rootca_key_pem = custom_target(
'rootca_key_pem',
output: 'rootCA.key.pem',
-1
View File
@@ -1 +0,0 @@
docker-compose down --rmi all
-1
View File
@@ -1 +0,0 @@
docker-compose up -d
+127 -6
View File
@@ -18,6 +18,9 @@
#define CLIENT_CA_CERT_DIR "."
#define CLIENT_CERT_FILE "./client.cert.pem"
#define CLIENT_PRIVATE_KEY_FILE "./client.key.pem"
#define SERVER_ENCRYPTED_CERT_FILE "./cert_encrypted.pem"
#define SERVER_ENCRYPTED_PRIVATE_KEY_FILE "./key_encrypted.pem"
#define SERVER_ENCRYPTED_PRIVATE_KEY_PASS "test123!"
using namespace std;
using namespace httplib;
@@ -607,10 +610,11 @@ TEST(ConnectionErrorTest, InvalidPort) {
auto res = cli.Get("/");
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Connection, res.error());
EXPECT_TRUE(Error::Connection == res.error() ||
Error::ConnectionTimeout == res.error());
}
TEST(ConnectionErrorTest, Timeout) {
TEST(ConnectionErrorTest, Timeout_Online) {
auto host = "google.com";
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -1194,6 +1198,17 @@ TEST(BindServerTest, BindAndListenSeparatelySSL) {
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(BindServerTest, BindAndListenSeparatelySSLEncryptedKey) {
SSLServer svr(SERVER_ENCRYPTED_CERT_FILE, SERVER_ENCRYPTED_PRIVATE_KEY_FILE,
nullptr, nullptr, SERVER_ENCRYPTED_PRIVATE_KEY_PASS);
int port = svr.bind_to_any_port("0.0.0.0");
ASSERT_TRUE(svr.is_valid());
ASSERT_TRUE(port > 0);
svr.stop();
}
#endif
TEST(ErrorHandlerTest, ContentLength) {
Server svr;
@@ -1401,6 +1416,34 @@ TEST(InvalidFormatTest, StatusCode) {
ASSERT_FALSE(svr.is_running());
}
TEST(URLFragmentTest, WithFragment) {
Server svr;
svr.Get("/hi", [](const Request &req, Response & /*res*/) {
EXPECT_TRUE(req.target == "/hi");
});
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli(HOST, PORT);
auto res = cli.Get("/hi#key1=val1=key2=val2");
EXPECT_TRUE(res);
EXPECT_EQ(200, res->status);
res = cli.Get("/hi%23key1=val1=key2=val2");
EXPECT_TRUE(res);
EXPECT_EQ(404, res->status);
}
svr.stop();
thread.join();
ASSERT_FALSE(svr.is_running());
}
class ServerTest : public ::testing::Test {
protected:
ServerTest()
@@ -1669,10 +1712,10 @@ protected:
res.set_content("empty-no-content-type", "text/plain");
})
.Post("/post-large",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, LARGE_DATA);
res.set_content(req.body, "text/plain");
})
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, LARGE_DATA);
res.set_content(req.body, "text/plain");
})
.Put("/empty-no-content-type",
[&](const Request &req, Response &res) {
EXPECT_EQ(req.body, "");
@@ -4325,6 +4368,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);
@@ -4776,4 +4833,68 @@ TEST(HttpToHttpsRedirectTest, CertFile) {
t.join();
t2.join();
}
TEST(MultipartFormDataTest, LargeData) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
svr.Post("/post", [&](const Request &req, Response & /*res*/,
const ContentReader &content_reader) {
if (req.is_multipart_form_data()) {
MultipartFormDataItems files;
content_reader(
[&](const MultipartFormData &file) {
files.push_back(file);
return true;
},
[&](const char *data, size_t data_length) {
files.back().content.append(data, data_length);
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(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) {
body.append(data, data_length);
return true;
});
}
});
auto t = std::thread([&]() { svr.listen("localhost", 8080); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '.');
std::stringstream buffer;
buffer << data;
Client cli("https://localhost:8080");
cli.enable_server_certificate_verification(false);
MultipartFormDataItems items{
{"document", buffer.str(), "2MB_data", "application/octet-stream"},
{"hello", "world", "", ""},
};
auto res = cli.Post("/post", items);
ASSERT_TRUE(res);
ASSERT_EQ(200, res->status);
}
svr.stop();
t.join();
}
#endif