Compare commits

...

16 Commits

Author SHA1 Message Date
David Pfahler d87d0672a8 Split the header only if needed (#1060)
* Update split.py

* Update split.py

* Update split.py

* Update split.py
2021-09-28 18:11:50 -04:00
estshorter 3da42fd1e8 Avoid min/max macro expansion on Windows (#1057) 2021-09-25 08:53:15 -04:00
yhirose 503aa61325 Fix problem with an empty parameter in set_base_dir 2021-09-20 17:40:05 -04:00
null e4c276d0c2 doc: fix typo in README (#1056)
fixed typo in README.md, replacing `Sutudio` with `Studio`.
2021-09-18 11:33:23 -04:00
yhirose e07f7691a8 Update README 2021-09-17 21:26:31 -04:00
yhirose 623ab4a96e Updated README regarding Visual Studio support 2021-09-17 11:36:08 -04:00
Zizheng Tai e1efa337a2 Make Client move-constructible (#1051) 2021-09-16 14:05:42 -04:00
Andrea Pappacoda 549cdf2f7d test: avoid infinite loop when IPV6 is unsupported (#1054) 2021-09-16 14:04:43 -04:00
yhirose 3c522386e9 Fix "Issue 38551 in oss-fuzz: cpp-httplib:server_fuzzer: Timeout in server_fuzze" 2021-09-12 19:24:48 -04:00
yhirose c202aa9ce9 Read buffer support. (Fix #1023) (#1046) 2021-09-12 00:26:02 -04:00
Andrea Pappacoda e3e28c6231 meson: add tests (#1044)
This integrates the "main" test suite (test/test.cc) in Meson.

This allows to run the tests in the CI with the Meson-built version of
the library to ensure that nothing breaks unexpectedly.

It also simplifies life of downstream packagers, that do not have to
write a custom build script to split the library and run tests but can
instead just let Meson do that for them.
2021-09-11 14:26:48 -04:00
yhirose 4e05368086 Fix #1054 2021-09-11 14:13:49 -04:00
yhirose e1afe74fe2 Fix #1037 2021-09-10 22:42:14 -04:00
yhirose 461acb02f5 Comment out SlowPostFail test for now 2021-09-10 22:37:31 -04:00
Gregor Jasny 415edc237c Set error variable for failed write_data (#1036) 2021-09-05 16:15:46 -04:00
Andrea Pappacoda e20ecd2574 Full Meson support (#1033)
* Full Meson support
cpp-httplib can be now built with Meson even in compiled library mode.

The library is built with LTO, supports OpenSSL, zlib and Brotli,
and the build system also generates a pkg-config file when needed.

Compared to the CMake file this one is quite small (more than five times
smaller!), and maintaining it won't be an issue :)

* meson: automatic versioning
2021-09-04 11:33:53 -04:00
11 changed files with 490 additions and 80 deletions
+52
View File
@@ -39,3 +39,55 @@ jobs:
cd test
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
x64\Release\test.exe
meson-build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- name: Prepare Git for checkout on Windows
if: matrix.os == 'windows-latest'
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v2
- name: Install dependencies on Linux
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get -qq update && sudo apt-get -qq install meson libssl-dev zlib1g-dev libbrotli-dev libgtest-dev
- name: Install dependencies on MacOS
if: matrix.os == 'macos-latest'
run: brew install meson openssl brotli googletest
- name: Setup MSVC on Windows
if: matrix.os == 'windows-latest'
uses: ilammy/msvc-dev-cmd@v1
# It is necessary to remove MinGW and StrawberryPerl as they both provide
# GCC. This causes issues because CMake prefers to use MSVC, while Meson
# uses GCC, if found, causing linking errors.
- name: Install dependencies on Windows
if: matrix.os == 'windows-latest'
run: |
choco uninstall mingw strawberryperl --yes --all-versions --remove-dependencies --skip-autouninstaller --no-color
Remove-Item -Path C:\Strawberry -Recurse
choco install pkgconfiglite --yes --skip-virus-check --no-color
pip install meson ninja
Invoke-WebRequest -Uri https://github.com/google/googletest/archive/refs/heads/master.zip -OutFile googletest-master.zip
Expand-Archive -Path googletest-master.zip
cd googletest-master\googletest-master
cmake -S . -B build -DINSTALL_GTEST=ON -DBUILD_GMOCK=OFF -Dgtest_hide_internal_symbols=ON -DCMAKE_INSTALL_PREFIX=C:/googletest
cmake --build build --config=Release
cmake --install build --config=Release
cd ..\..
- name: Build and test
run: |
meson setup build -Dcpp-httplib_test=true -Dpkg_config_path=C:\googletest\lib\pkgconfig -Db_vscrt=static_from_buildtype
meson test --no-stdsplit --print-errorlogs -C build
+2
View File
@@ -787,6 +787,8 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
#include <httplib.h>
```
Note: cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
Note: Windows 8 or lower and Cygwin on Windows are not supported.
License
+137 -51
View File
@@ -259,7 +259,7 @@ namespace detail {
template <class T, class... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args &&... args) {
make_unique(Args &&...args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
@@ -492,7 +492,7 @@ public:
virtual socket_t socket() const = 0;
template <typename... Args>
ssize_t write_format(const char *fmt, const Args &... args);
ssize_t write_format(const char *fmt, const Args &...args);
ssize_t write(const char *ptr);
ssize_t write(const std::string &s);
};
@@ -622,7 +622,7 @@ public:
Server &Options(const std::string &pattern, Handler handler);
bool set_base_dir(const std::string &dir,
const std::string &mount_point = nullptr);
const std::string &mount_point = std::string());
bool set_mount_point(const std::string &mount_point, const std::string &dir,
Headers headers = Headers());
bool remove_mount_point(const std::string &mount_point);
@@ -1161,6 +1161,8 @@ public:
const std::string &client_cert_path,
const std::string &client_key_path);
Client(Client &&) = default;
~Client();
bool is_valid() const;
@@ -1473,7 +1475,7 @@ inline T Response::get_header_value(const char *key, size_t id) const {
}
template <typename... Args>
inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
inline ssize_t Stream::write_format(const char *fmt, const Args &...args) {
const auto bufsiz = 2048;
std::array<char, bufsiz> buf;
@@ -1671,6 +1673,10 @@ bool parse_range_header(const std::string &s, Ranges &ranges);
int close_socket(socket_t sock);
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
enum class EncodingType { None = 0, Gzip, Brotli };
EncodingType encoding_type(const Request &req, const Response &res);
@@ -2189,6 +2195,34 @@ template <typename T> inline ssize_t handle_EINTR(T fn) {
return res;
}
inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {
return handle_EINTR([&]() {
return recv(sock,
#ifdef _WIN32
static_cast<char *>(ptr),
static_cast<int>(size),
#else
ptr,
size,
#endif
flags);
});
}
inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags) {
return handle_EINTR([&]() {
return send(sock,
#ifdef _WIN32
static_cast<const char *>(ptr),
static_cast<int>(size),
#else
ptr,
size,
#endif
flags);
});
}
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
@@ -2313,6 +2347,12 @@ private:
time_t read_timeout_usec_;
time_t write_timeout_sec_;
time_t write_timeout_usec_;
std::vector<char> read_buff_;
size_t read_buff_off_ = 0;
size_t read_buff_content_size_ = 0;
static const size_t read_buff_size_ = 1024 * 4;
};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -2860,10 +2900,10 @@ inline bool gzip_compressor::compress(const char *data, size_t data_length,
do {
constexpr size_t max_avail_in =
std::numeric_limits<decltype(strm_.avail_in)>::max();
(std::numeric_limits<decltype(strm_.avail_in)>::max)();
strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
std::min(data_length, max_avail_in));
(std::min)(data_length, max_avail_in));
strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
data_length -= strm_.avail_in;
@@ -2919,10 +2959,10 @@ inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
do {
constexpr size_t max_avail_in =
std::numeric_limits<decltype(strm_.avail_in)>::max();
(std::numeric_limits<decltype(strm_.avail_in)>::max)();
strm_.avail_in = static_cast<decltype(strm_.avail_in)>(
std::min(data_length, max_avail_in));
(std::min)(data_length, max_avail_in));
strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
data_length -= strm_.avail_in;
@@ -2933,7 +2973,14 @@ inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
strm_.avail_out = static_cast<uInt>(buff.size());
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
auto prev_avail_in = strm_.avail_in;
ret = inflate(&strm_, Z_NO_FLUSH);
if (prev_avail_in - strm_.avail_in == 0) {
return false;
}
assert(ret != Z_STREAM_ERROR);
switch (ret) {
case Z_NEED_DICT:
@@ -3957,17 +4004,15 @@ 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) {
using namespace std;
std::vector<unsigned char> md(digest_length, 0);
CTX ctx;
init(&ctx);
update(&ctx, s.data(), s.size());
final(md.data(), &ctx);
stringstream ss;
std::stringstream ss;
for (auto c : md) {
ss << setfill('0') << setw(2) << hex << (unsigned int)c;
ss << std::setfill('0') << std::setw(2) << std::hex << (unsigned int)c;
}
return ss.str();
}
@@ -4035,45 +4080,55 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
const Request &req, const std::map<std::string, std::string> &auth,
size_t cnonce_count, const std::string &cnonce, const std::string &username,
const std::string &password, bool is_proxy = false) {
using namespace std;
string nc;
std::string nc;
{
stringstream ss;
ss << setfill('0') << setw(8) << hex << cnonce_count;
std::stringstream ss;
ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
nc = ss.str();
}
auto qop = auth.at("qop");
if (qop.find("auth-int") != std::string::npos) {
qop = "auth-int";
} else {
qop = "auth";
std::string qop;
if (auth.find("qop") != auth.end()) {
qop = auth.at("qop");
if (qop.find("auth-int") != std::string::npos) {
qop = "auth-int";
} else if (qop.find("auth") != std::string::npos) {
qop = "auth";
} else {
qop.clear();
}
}
std::string algo = "MD5";
if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
string response;
std::string response;
{
auto H = algo == "SHA-256"
? detail::SHA_256
: algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
auto H = algo == "SHA-256" ? detail::SHA_256
: algo == "SHA-512" ? detail::SHA_512
: detail::MD5;
auto A1 = username + ":" + auth.at("realm") + ":" + password;
auto A2 = req.method + ":" + req.path;
if (qop == "auth-int") { A2 += ":" + H(req.body); }
response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
":" + qop + ":" + H(A2));
if (qop.empty()) {
response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
} else {
response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
":" + qop + ":" + H(A2));
}
}
auto field = "Digest username=\"" + username + "\", realm=\"" +
auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
"\", uri=\"" + req.path + "\", algorithm=" + algo +
", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce +
"\", response=\"" + response + "\"";
auto field =
"Digest username=\"" + username + "\", realm=\"" + auth.at("realm") +
"\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
"\", algorithm=" + algo +
(qop.empty() ? ", response=\""
: ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" +
cnonce + "\", response=\"") +
response + "\"";
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
@@ -4360,7 +4415,8 @@ inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
: sock_(sock), read_timeout_sec_(read_timeout_sec),
read_timeout_usec_(read_timeout_usec),
write_timeout_sec_(write_timeout_sec),
write_timeout_usec_(write_timeout_usec) {}
write_timeout_usec_(write_timeout_usec),
read_buff_(read_buff_size_, 0) {}
inline SocketStream::~SocketStream() {}
@@ -4373,31 +4429,56 @@ inline bool SocketStream::is_writable() const {
}
inline ssize_t SocketStream::read(char *ptr, size_t size) {
#ifdef _WIN32
size = (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
#else
size = (std::min)(size, static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));
#endif
if (read_buff_off_ < read_buff_content_size_) {
auto remaining_size = read_buff_content_size_ - read_buff_off_;
if (size <= remaining_size) {
memcpy(ptr, read_buff_.data() + read_buff_off_, size);
read_buff_off_ += size;
return static_cast<ssize_t>(size);
} else {
memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);
read_buff_off_ += remaining_size;
return static_cast<ssize_t>(remaining_size);
}
}
if (!is_readable()) { return -1; }
#ifdef _WIN32
if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
return -1;
read_buff_off_ = 0;
read_buff_content_size_ = 0;
if (size < read_buff_size_) {
auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, CPPHTTPLIB_RECV_FLAGS);
if (n <= 0) {
return n;
} else if (n <= static_cast<ssize_t>(size)) {
memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));
return n;
} else {
memcpy(ptr, read_buff_.data(), size);
read_buff_off_ = size;
read_buff_content_size_ = static_cast<size_t>(n);
return static_cast<ssize_t>(size);
}
} else {
return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);
}
return recv(sock_, ptr, static_cast<int>(size), CPPHTTPLIB_RECV_FLAGS);
#else
return handle_EINTR(
[&]() { return recv(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); });
#endif
}
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
if (!is_writable()) { return -1; }
#ifdef _WIN32
if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
return -1;
}
return send(sock_, ptr, static_cast<int>(size), CPPHTTPLIB_SEND_FLAGS);
#else
return handle_EINTR(
[&]() { return send(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); });
size = (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
#endif
return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);
}
inline void SocketStream::get_remote_ip_and_port(std::string &ip,
@@ -4922,7 +5003,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
/* For debug
size_t pos = 0;
while (pos < n) {
auto read_size = std::min<size_t>(1, n - pos);
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);
if (!ret) { return false; }
@@ -5941,7 +6022,12 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
return write_content_with_provider(strm, req, error);
}
return detail::write_data(strm, req.body.data(), req.body.size());
if (!detail::write_data(strm, req.body.data(), req.body.size())) {
error = Error::Write;
return false;
}
return true;
}
inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
+108 -2
View File
@@ -1,7 +1,113 @@
project('cpp-httplib', 'cpp', license: 'MIT')
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
#
# SPDX-License-Identifier: MIT
cpp_httplib_dep = declare_dependency(include_directories: include_directories('.'))
project(
'cpp-httplib',
'cpp',
license: 'MIT',
default_options: [
'cpp_std=c++11',
'buildtype=release',
'b_ndebug=if-release',
'b_lto=true',
'warning_level=3'
],
meson_version: '>=0.47.0'
)
# Check just in case downstream decides to edit the source
# and add a project version
version = meson.project_version()
if version == 'undefined'
git = find_program('git', required: false)
if git.found()
result = run_command(git, 'describe', '--tags', '--abbrev=0')
if result.returncode() == 0
version = result.stdout().strip('v\n')
endif
endif
endif
python = import('python').find_installation('python3')
# If version is still undefined it means that the git method failed
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))'
).stdout().strip()
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'))
if openssl_dep.found()
deps += openssl_dep
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
endif
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
if zlib_dep.found()
deps += zlib_dep
args += '-DCPPHTTPLIB_ZLIB_SUPPORT'
endif
brotli_deps = [dependency('libbrotlicommon', required: get_option('cpp-httplib_brotli'))]
brotli_deps += dependency('libbrotlidec', required: get_option('cpp-httplib_brotli'))
brotli_deps += dependency('libbrotlienc', required: get_option('cpp-httplib_brotli'))
brotli_found_all = true
foreach brotli_dep : brotli_deps
if not brotli_dep.found()
brotli_found_all = false
endif
endforeach
if brotli_found_all
deps += brotli_deps
args += '-DCPPHTTPLIB_BROTLI_SUPPORT'
endif
cpp_httplib_dep = dependency('', required: false)
if get_option('cpp-httplib_compile')
httplib_ch = custom_target(
'split',
input: 'httplib.h',
output: ['httplib.cc', 'httplib.h'],
command: [python, files('split.py'), '--out', meson.current_build_dir()],
install: true,
install_dir: [false, get_option('includedir')]
)
lib = library(
'cpp-httplib',
sources: httplib_ch,
dependencies: deps,
cpp_args: args,
version: version,
install: true
)
cpp_httplib_dep = declare_dependency(link_with: lib, sources: httplib_ch[1])
import('pkgconfig').generate(
lib,
description: 'A C++ HTTP/HTTPS server and client library',
url: 'https://github.com/yhirose/cpp-httplib',
version: version
)
else
install_headers('httplib.h')
cpp_httplib_dep = declare_dependency(include_directories: include_directories('.'), dependencies: deps)
endif
if meson.version().version_compare('>=0.54.0')
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
endif
if get_option('cpp-httplib_test')
subdir('test')
endif
+9
View File
@@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
#
# SPDX-License-Identifier: MIT
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_test', type: 'boolean', value: false, description: 'Build tests')
+44 -24
View File
@@ -19,29 +19,49 @@ args_parser.add_argument(
args = args_parser.parse_args()
cur_dir = os.path.dirname(sys.argv[0])
with open(cur_dir + '/httplib.h') as f:
lines = f.readlines()
lib_name = 'httplib'
header_name = '/' + lib_name + '.h'
source_name = '/' + lib_name + '.' + args.extension
# get the input file
in_file = cur_dir + header_name
# get the output file
h_out = args.out + header_name
cc_out = args.out + source_name
python_version = sys.version_info[0]
if python_version < 3:
os.makedirs(args.out)
# if the modification time of the out file is after the in file,
# don't split (as it is already finished)
do_split = True
if os.path.exists(h_out):
in_time = os.path.getmtime(in_file)
out_time = os.path.getmtime(h_out)
do_split = in_time > out_time
if do_split:
with open(in_file) as f:
lines = f.readlines()
python_version = sys.version_info[0]
if python_version < 3:
os.makedirs(args.out)
else:
os.makedirs(args.out, exist_ok=True)
in_implementation = False
cc_out = args.out + source_name
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
fc.write('#include "httplib.h"\n')
fc.write('namespace httplib {\n')
for line in lines:
is_border_line = border in line
if is_border_line:
in_implementation = not in_implementation
elif in_implementation:
fc.write(line.replace('inline ', ''))
else:
fh.write(line)
fc.write('} // namespace httplib\n')
print("Wrote {} and {}".format(h_out, cc_out))
else:
os.makedirs(args.out, exist_ok=True)
in_implementation = False
h_out = args.out + '/httplib.h'
cc_out = args.out + '/httplib.' + args.extension
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
fc.write('#include "httplib.h"\n')
fc.write('namespace httplib {\n')
for line in lines:
is_border_line = border in line
if is_border_line:
in_implementation = not in_implementation
elif in_implementation:
fc.write(line.replace('inline ', ''))
else:
fh.write(line)
fc.write('} // namespace httplib\n')
print("Wrote {} and {}".format(h_out, cc_out))
print("{} and {} are up to date".format(h_out, cc_out))
+97
View File
@@ -0,0 +1,97 @@
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
#
# SPDX-License-Identifier: MIT
gtest_dep = dependency('gtest', main: true)
openssl = find_program('openssl')
test_conf = files('test.conf')
key_pem = custom_target(
'key_pem',
output: 'key.pem',
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
)
temp_req = custom_target(
'temp_req',
input: key_pem,
output: 'temp_req',
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
)
cert_pem = custom_target(
'cert_pem',
input: [temp_req, key_pem],
output: 'cert.pem',
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '3650', '-req', '-signkey', '@INPUT1@', '-out', '@OUTPUT@']
)
cert2_pem = custom_target(
'cert2_pem',
input: key_pem,
output: 'cert2.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',
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
)
rootca_cert_pem = custom_target(
'rootca_cert_pem',
input: rootca_key_pem,
output: 'rootCA.cert.pem',
command: [openssl, 'req', '-x509', '-new', '-batch', '-config', files('test.rootCA.conf'), '-key', '@INPUT@', '-days', '1024', '-out', '@OUTPUT@']
)
client_key_pem = custom_target(
'client_key_pem',
output: 'client.key.pem',
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
)
client_temp_req = custom_target(
'client_temp_req',
input: client_key_pem,
output: 'client_temp_req',
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
)
client_cert_pem = custom_target(
'client_cert_pem',
input: [client_temp_req, rootca_cert_pem, rootca_key_pem],
output: 'client.cert.pem',
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@']
)
# Copy test files to the build directory
configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true)
configure_file(input: 'image.jpg', output: 'image.jpg', copy: true)
subdir(join_paths('www', 'dir'))
subdir(join_paths('www2', 'dir'))
subdir(join_paths('www3', 'dir'))
test(
'main',
executable(
'main',
'test.cc',
dependencies: [
cpp_httplib_dep,
gtest_dep
]
),
depends: [
key_pem,
cert_pem,
cert2_pem,
rootca_key_pem,
rootca_cert_pem,
client_key_pem,
client_cert_pem
],
workdir: meson.current_build_dir(),
timeout: 300
)
+22 -3
View File
@@ -8,6 +8,7 @@
#include <sstream>
#include <stdexcept>
#include <thread>
#include <type_traits>
#define SERVER_CERT_FILE "./cert.pem"
#define SERVER_CERT2_FILE "./cert2.pem"
@@ -40,6 +41,11 @@ MultipartFormData &get_file_value(MultipartFormDataItems &files,
throw std::runtime_error("invalid mulitpart form data name error");
}
TEST(ConstructorTest, MoveConstructible) {
EXPECT_FALSE(std::is_copy_constructible<Client>::value);
EXPECT_TRUE(std::is_nothrow_move_constructible<Client>::value);
}
#ifdef _WIN32
TEST(StartupTest, WSAStartup) {
WSADATA wsaData;
@@ -971,8 +977,15 @@ TEST(RedirectFromPageWithContentIP6, Redirect) {
auto th = std::thread([&]() { svr.listen("::1", 1234); });
while (!svr.is_running()) {
// When IPV6 support isn't available svr.listen("::1", 1234) never
// actually starts anything, so the condition !svr.is_running() will
// always remain true, and the loop never stops.
// This basically counts how many milliseconds have passed since the
// call to svr.listen(), and if after 5 seconds nothing started yet
// aborts the test.
for (unsigned int milliseconds = 0; !svr.is_running(); milliseconds++) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
ASSERT_LT(milliseconds, 5000U);
}
// Give GET time to get a few messages.
@@ -1349,11 +1362,13 @@ protected:
std::this_thread::sleep_for(std::chrono::seconds(2));
res.set_content("slow", "text/plain");
})
#if 0
.Post("/slowpost",
[&](const Request & /*req*/, Response &res) {
std::this_thread::sleep_for(std::chrono::seconds(2));
res.set_content("slow", "text/plain");
})
#endif
.Get("/remote_addr",
[&](const Request &req, Response &res) {
auto remote_addr = req.headers.find("REMOTE_ADDR")->second;
@@ -2623,6 +2638,7 @@ TEST_F(ServerTest, SlowRequest) {
std::thread([=]() { auto res = cli_.Get("/slow"); }));
}
#if 0
TEST_F(ServerTest, SlowPost) {
char buffer[64 * 1024];
memset(buffer, 0x42, sizeof(buffer));
@@ -2656,6 +2672,7 @@ TEST_F(ServerTest, SlowPostFail) {
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Write, res.error());
}
#endif
TEST_F(ServerTest, Put) {
auto res = cli_.Put("/put", "PUT", "text/plain");
@@ -3562,10 +3579,12 @@ TEST(StreamingTest, NoContentLengthStreaming) {
Client client(HOST, PORT);
auto get_thread = std::thread([&client]() {
auto res = client.Get("/stream", [](const char *data, size_t len) -> bool {
EXPECT_EQ("aaabbb", std::string(data, len));
std::string s;
auto res = client.Get("/stream", [&s](const char *data, size_t len) -> bool {
s += std::string(data, len);
return true;
});
EXPECT_EQ("aaabbb", s);
});
// Give GET time to get a few messages.
+7
View File
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
#
# SPDX-License-Identifier: MIT
configure_file(input: 'index.html', output: 'index.html', copy: true)
configure_file(input: 'test.abcde', output: 'test.abcde', copy: true)
configure_file(input: 'test.html', output: 'test.html', copy: true)
+6
View File
@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
#
# SPDX-License-Identifier: MIT
configure_file(input: 'index.html', output: 'index.html', copy: true)
configure_file(input: 'test.html', output: 'test.html', copy: true)
+6
View File
@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
#
# SPDX-License-Identifier: MIT
configure_file(input: 'index.html', output: 'index.html', copy: true)
configure_file(input: 'test.html', output: 'test.html', copy: true)