Compare commits

..

22 Commits

Author SHA1 Message Date
yhirose f14accb7b6 Release v0.14.2 2023-12-04 22:31:12 -05:00
yhirose c5c704cb3b Fix #1724 2023-12-04 21:34:55 -05:00
Jean-Francois Simoneau 115a786581 Fix readability warnings (#1722)
* Fix readability warnings

Did not fix readbility-qualified-auto, will do a separate pull request

* Revert changes where meaning is lost

* Revert some style changes
2023-11-24 09:55:04 -05:00
Jean-Francois Simoneau 5ef4cfd263 Fix bugprone warnings (#1721) 2023-11-20 22:14:00 -05:00
Jean-Francois Simoneau 03fecb2f78 Fix modernize warnings (#1720) 2023-11-20 22:10:04 -05:00
Jean-Francois Simoneau 7fc8682a0a Fix performance-noexcept-move-constructor (#1715) 2023-11-20 13:13:59 -05:00
Jiwoo Park f1431311a4 Minor fixes on test cases (#1709)
* Fix data race

* Replace sleep_for() to wait_until_ready()
2023-11-11 21:28:50 -05:00
Jiwoo Park 1d14e051a5 Remove cryptui on Windows (#1710) 2023-11-11 21:26:57 -05:00
Jiwoo Park 97ae6733ed Run fuzz test in CTest (#1707) 2023-11-09 19:35:15 -05:00
Jiwoo Park 1d6b22b5f0 Fix C6001 (#1701) 2023-10-30 07:13:40 -04:00
yhirose 1a49076b5b Removed unnecessary exception 2023-10-29 19:36:40 -04:00
Jiwoo Park d0e4cb3f07 Include missing stdint.h on fuzz test (#1700)
* Include missing stdint.h

* Remove std:: from uint8_t
2023-10-29 19:26:06 -04:00
yhirose e2813d9d4d Code cleanup. (Removed unnecessary .c_str() calls) 2023-10-23 16:43:12 -04:00
Andrea Pappacoda 20a7f088ce build(meson): copy 1MB.txt test file (#1695)
Since tests are run in the build directory, the 1MB.txt file has to be
copied there.
2023-10-20 17:58:06 -04:00
yhirose f63ba7d013 Fix #1685 2023-10-03 09:59:27 -04:00
yhirose 0a629d7391 Release v0.14.1 2023-09-30 22:26:23 -04:00
PabloMK7 a609330e4c Add optional user defined header writer (#1683)
* Add optional user defined header writer

* Fix errors and add test
2023-09-30 22:13:14 -04:00
Jiwoo Park c029597a5a Update the remote address of www.httpwatch.com (#1664) 2023-09-13 10:33:33 -04:00
yhirose 30b7732565 Release v0.14.0 2023-08-22 20:19:07 -04:00
yhirose 6650632e7f Fix #1638 2023-08-22 19:36:10 -04:00
Sven Panne afe627e7af Avoid a -Warray-bounds false positive in GCC 13. (#1639)
The exact circumstances when this false positive is triggered are quite
tricky to reproduce, but it happened reproducibly with g++ 13.1 and 13.2 in
a close-source SW I'm working on.  The fix even improves performance by a
very tiny bit: There is no need to copy the std::smatch, having a const
reference is enough.

Just as a side note: -Warray-bounds seems to cause trouble in other
projects, too, so e.g. the Linux kernel has disabled since June 2022.
2023-08-14 10:26:54 -04:00
Duncan Ogilvie 67f6ff7fa9 Fix CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR (#1634) 2023-08-03 17:01:40 -04:00
8 changed files with 282 additions and 226 deletions
-1
View File
@@ -219,7 +219,6 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
# Needed for Windows libs on Mingw, as the pragma comment(lib, "xyz") aren't triggered.
$<$<PLATFORM_ID:Windows>:ws2_32>
$<$<PLATFORM_ID:Windows>:crypt32>
$<$<PLATFORM_ID:Windows>:cryptui>
# Needed for API from MacOS Security framework
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}>>:-framework CoreFoundation -framework Security>"
# Can't put multiple targets in a single generator expression or it bugs out.
+148 -122
View File
@@ -8,7 +8,7 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.13.3"
#define CPPHTTPLIB_VERSION "0.14.2"
/*
* Configuration
@@ -247,7 +247,6 @@ using socket_t = int;
#ifdef _MSC_VER
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
#include <TargetConditionals.h>
@@ -331,7 +330,7 @@ struct scope_exit {
explicit scope_exit(std::function<void(void)> &&f)
: exit_function(std::move(f)), execute_on_destruction{true} {}
scope_exit(scope_exit &&rhs)
scope_exit(scope_exit &&rhs) noexcept
: exit_function(std::move(rhs.exit_function)),
execute_on_destruction{rhs.execute_on_destruction} {
rhs.release();
@@ -383,6 +382,7 @@ public:
DataSink &operator=(DataSink &&) = delete;
std::function<bool(const char *data, size_t data_len)> write;
std::function<bool()> is_writable;
std::function<void()> done;
std::function<void(const Headers &trailer)> done_with_trailer;
std::ostream os;
@@ -393,7 +393,7 @@ private:
explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
protected:
std::streamsize xsputn(const char *s, std::streamsize n) {
std::streamsize xsputn(const char *s, std::streamsize n) override {
sink_.write(s, static_cast<size_t>(n));
return n;
}
@@ -487,8 +487,7 @@ struct Request {
bool has_header(const std::string &key) const;
std::string get_header_value(const std::string &key, size_t id = 0) const;
template <typename T>
T get_header_value(const std::string &key, size_t id = 0) const;
uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const;
size_t get_header_value_count(const std::string &key) const;
void set_header(const std::string &key, const std::string &val);
@@ -520,8 +519,7 @@ struct Response {
bool has_header(const std::string &key) const;
std::string get_header_value(const std::string &key, size_t id = 0) const;
template <typename T>
T get_header_value(const std::string &key, size_t id = 0) const;
uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const;
size_t get_header_value_count(const std::string &key) const;
void set_header(const std::string &key, const std::string &val);
@@ -739,6 +737,8 @@ private:
std::regex regex_;
};
ssize_t write_headers(Stream &strm, const Headers &headers);
} // namespace detail
class Server {
@@ -802,6 +802,8 @@ public:
Server &set_socket_options(SocketOptions socket_options);
Server &set_default_headers(Headers headers);
Server &
set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
Server &set_keep_alive_max_count(size_t count);
Server &set_keep_alive_timeout(time_t sec);
@@ -867,15 +869,15 @@ private:
bool routing(Request &req, Response &res, Stream &strm);
bool handle_file_request(const Request &req, Response &res,
bool head = false);
bool dispatch_request(Request &req, Response &res, const Handlers &handlers);
bool
dispatch_request_for_content_reader(Request &req, Response &res,
ContentReader content_reader,
const HandlersForContentReader &handlers);
bool dispatch_request(Request &req, Response &res,
const Handlers &handlers) const;
bool dispatch_request_for_content_reader(
Request &req, Response &res, ContentReader content_reader,
const HandlersForContentReader &handlers) const;
bool parse_request_line(const char *s, Request &req);
bool parse_request_line(const char *s, Request &req) const;
void apply_ranges(const Request &req, Response &res,
std::string &content_type, std::string &boundary);
std::string &content_type, std::string &boundary) const;
bool write_response(Stream &strm, bool close_connection, const Request &req,
Response &res);
bool write_response_with_content(Stream &strm, bool close_connection,
@@ -895,7 +897,7 @@ private:
bool read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver);
ContentReceiver multipart_receiver) const;
virtual bool process_and_close_socket(socket_t sock);
@@ -936,6 +938,8 @@ private:
SocketOptions socket_options_ = default_socket_options;
Headers default_headers_;
std::function<ssize_t(Stream &, Headers &)> header_writer_ =
detail::write_headers;
};
enum class Error {
@@ -959,7 +963,7 @@ enum class Error {
SSLPeerCouldBeClosed_,
};
std::string to_string(const Error error);
std::string to_string(Error error);
std::ostream &operator<<(std::ostream &os, const Error &obj);
@@ -988,8 +992,8 @@ public:
bool has_request_header(const std::string &key) const;
std::string get_request_header_value(const std::string &key,
size_t id = 0) const;
template <typename T>
T get_request_header_value(const std::string &key, size_t id = 0) const;
uint64_t get_request_header_value_u64(const std::string &key,
size_t id = 0) const;
size_t get_request_header_value_count(const std::string &key) const;
private:
@@ -1166,6 +1170,9 @@ public:
void set_default_headers(Headers headers);
void
set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
void set_address_family(int family);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
@@ -1214,7 +1221,7 @@ public:
void set_ca_cert_path(const std::string &ca_cert_file_path,
const std::string &ca_cert_dir_path = std::string());
void set_ca_cert_store(X509_STORE *ca_cert_store);
X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size);
X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -1243,14 +1250,14 @@ protected:
// Also, shutdown_ssl and close_socket should also NOT be called concurrently
// with a DIFFERENT thread sending requests using that socket.
virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
void shutdown_socket(Socket &socket);
void shutdown_socket(Socket &socket) const;
void close_socket(Socket &socket);
bool process_request(Stream &strm, Request &req, Response &res,
bool close_connection, Error &error);
bool write_content_with_provider(Stream &strm, const Request &req,
Error &error);
Error &error) const;
void copy_settings(const ClientImpl &rhs);
@@ -1275,6 +1282,10 @@ protected:
// Default headers
Headers default_headers_;
// Header writer
std::function<ssize_t(Stream &, Headers &)> header_writer_ =
detail::write_headers;
// Settings
std::string client_cert_path_;
std::string client_key_path_;
@@ -1337,7 +1348,8 @@ private:
Result send_(Request &&req);
socket_t create_client_socket(Error &error) const;
bool read_response_line(Stream &strm, const Request &req, Response &res);
bool read_response_line(Stream &strm, const Request &req,
Response &res) const;
bool write_request(Stream &strm, Request &req, bool close_connection,
Error &error);
bool redirect(Request &req, Response &res, Error &error);
@@ -1356,7 +1368,7 @@ private:
const std::string &content_type);
ContentProviderWithoutLength get_multipart_content_provider(
const std::string &boundary, const MultipartFormDataItems &items,
const MultipartFormDataProviderItems &provider_items);
const MultipartFormDataProviderItems &provider_items) const;
std::string adjust_host_string(const std::string &host) const;
@@ -1541,6 +1553,9 @@ public:
void set_default_headers(Headers headers);
void
set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
void set_address_family(int family);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
@@ -1666,7 +1681,7 @@ public:
private:
bool create_and_connect_socket(Socket &socket, Error &error) override;
void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
void shutdown_ssl_impl(Socket &socket, bool shutdown_socket);
void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
bool process_socket(const Socket &socket,
std::function<bool(Stream &strm)> callback) override;
@@ -1710,15 +1725,9 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) {
callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
}
template <typename T>
inline T get_header_value(const Headers & /*headers*/,
const std::string & /*key*/, size_t /*id*/ = 0,
uint64_t /*def*/ = 0) {}
template <>
inline uint64_t get_header_value<uint64_t>(const Headers &headers,
const std::string &key, size_t id,
uint64_t def) {
inline uint64_t get_header_value_u64(const Headers &headers,
const std::string &key, size_t id,
uint64_t def) {
auto rng = headers.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
@@ -1730,14 +1739,14 @@ inline uint64_t get_header_value<uint64_t>(const Headers &headers,
} // namespace detail
template <typename T>
inline T Request::get_header_value(const std::string &key, size_t id) const {
return detail::get_header_value<T>(headers, key, id, 0);
inline uint64_t Request::get_header_value_u64(const std::string &key,
size_t id) const {
return detail::get_header_value_u64(headers, key, id, 0);
}
template <typename T>
inline T Response::get_header_value(const std::string &key, size_t id) const {
return detail::get_header_value<T>(headers, key, id, 0);
inline uint64_t Response::get_header_value_u64(const std::string &key,
size_t id) const {
return detail::get_header_value_u64(headers, key, id, 0);
}
template <typename... Args>
@@ -1906,10 +1915,9 @@ inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
return os;
}
template <typename T>
inline T Result::get_request_header_value(const std::string &key,
size_t id) const {
return detail::get_header_value<T>(request_headers_, key, id, 0);
inline uint64_t Result::get_request_header_value_u64(const std::string &key,
size_t id) const {
return detail::get_header_value_u64(request_headers_, key, id, 0);
}
template <class Rep, class Period>
@@ -2059,7 +2067,7 @@ public:
class nocompressor : public compressor {
public:
virtual ~nocompressor() = default;
~nocompressor() override = default;
bool compress(const char *data, size_t data_length, bool /*last*/,
Callback callback) override;
@@ -2069,7 +2077,7 @@ public:
class gzip_compressor : public compressor {
public:
gzip_compressor();
~gzip_compressor();
~gzip_compressor() override;
bool compress(const char *data, size_t data_length, bool last,
Callback callback) override;
@@ -2082,7 +2090,7 @@ private:
class gzip_decompressor : public decompressor {
public:
gzip_decompressor();
~gzip_decompressor();
~gzip_decompressor() override;
bool is_valid() const override;
@@ -2221,7 +2229,7 @@ inline std::string from_i_to_hex(size_t n) {
inline size_t to_utf8(int code, char *buff) {
if (code < 0x0080) {
buff[0] = (code & 0x7F);
buff[0] = static_cast<char>(code & 0x7F);
return 1;
} else if (code < 0x0800) {
buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
@@ -2556,7 +2564,7 @@ inline mmap::mmap(const char *path)
#endif
,
size_(0), addr_(nullptr) {
if (!open(path)) { std::runtime_error(""); }
open(path);
}
inline mmap::~mmap() { close(); }
@@ -2829,7 +2837,7 @@ private:
size_t read_buff_off_ = 0;
size_t read_buff_content_size_ = 0;
static const size_t read_buff_size_ = 1024 * 4;
static const size_t read_buff_size_ = 1024l * 4;
};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -2958,7 +2966,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
#ifndef _WIN32
if (hints.ai_family == AF_UNIX) {
const auto addrlen = host.length();
if (addrlen > sizeof(sockaddr_un::sun_path)) return INVALID_SOCKET;
if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (sock != INVALID_SOCKET) {
@@ -3162,7 +3170,7 @@ inline socket_t create_client_socket(
#ifdef USE_IF2IP
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())) {
if (!bind_ip_address(sock2, ip_from_if)) {
error = Error::BindIPAddress;
return false;
}
@@ -3319,7 +3327,7 @@ find_content_type(const std::string &path,
auto ext = file_extension(path);
auto it = user_data.find(ext);
if (it != user_data.end()) { return it->second.c_str(); }
if (it != user_data.end()) { return it->second; }
using udl::operator""_t;
@@ -3530,7 +3538,7 @@ inline bool gzip_decompressor::decompress(const char *data, size_t data_length,
}
}
if (ret != Z_OK && ret != Z_STREAM_END) return false;
if (ret != Z_OK && ret != Z_STREAM_END) { return false; }
} while (data_length > 0);
@@ -3798,7 +3806,7 @@ inline bool read_content_chunked(Stream &strm, T &x,
if (!line_reader.getline()) { return false; }
if (strcmp(line_reader.ptr(), "\r\n")) { return false; }
if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; }
if (!line_reader.getline()) { return false; }
}
@@ -3808,7 +3816,7 @@ inline bool read_content_chunked(Stream &strm, T &x,
// Trailer
if (!line_reader.getline()) { return false; }
while (strcmp(line_reader.ptr(), "\r\n")) {
while (strcmp(line_reader.ptr(), "\r\n") != 0) {
if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
// Exclude line terminator
@@ -3894,7 +3902,7 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
} else if (!has_header(x.headers, "Content-Length")) {
ret = read_content_without_length(strm, out);
} else {
auto len = get_header_value<uint64_t>(x.headers, "Content-Length");
auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0);
if (len > payload_max_length) {
exceed_payload_max_length = true;
skip_content_with_length(strm, len);
@@ -3952,6 +3960,8 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
return ok;
};
data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
while (offset < end_offset && !is_shutting_down()) {
if (!strm.is_writable()) {
error = Error::Write;
@@ -3996,6 +4006,8 @@ write_content_without_length(Stream &strm,
return ok;
};
data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
data_sink.done = [&](void) { data_available = false; };
while (data_available && !is_shutting_down()) {
@@ -4046,6 +4058,8 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
return ok;
};
data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
auto done_with_trailer = [&](const Headers *trailer) {
if (!ok) { return; }
@@ -4143,7 +4157,7 @@ inline bool redirect(T &cli, Request &req, Response &res,
req = new_req;
res = new_res;
if (res.location.empty()) res.location = location;
if (res.location.empty()) { res.location = location; }
}
return ret;
}
@@ -4230,7 +4244,7 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
auto len = static_cast<size_t>(m.length(1));
auto all_valid_ranges = true;
split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
if (!all_valid_ranges) return;
if (!all_valid_ranges) { return; }
static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
std::cmatch cm;
if (std::regex_match(b, e, cm, re_another_range)) {
@@ -4581,7 +4595,7 @@ serialize_multipart_formdata(const MultipartFormDataItems &items,
body += item.content + serialize_multipart_formdata_item_end();
}
if (finish) body += serialize_multipart_formdata_finish(boundary);
if (finish) { body += serialize_multipart_formdata_finish(boundary); }
return body;
}
@@ -4986,7 +5000,7 @@ inline bool parse_www_authenticate(const Response &res,
s = s.substr(pos + 1);
auto beg = std::sregex_iterator(s.begin(), s.end(), re);
for (auto i = beg; i != std::sregex_iterator(); ++i) {
auto m = *i;
const auto &m = *i;
auto key = s.substr(static_cast<size_t>(m.position(1)),
static_cast<size_t>(m.length(1)));
auto val = m.length(2) > 0
@@ -5289,7 +5303,7 @@ inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
write_timeout_sec_(write_timeout_sec),
write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
inline SocketStream::~SocketStream() {}
inline SocketStream::~SocketStream() = default;
inline bool SocketStream::is_readable() const {
return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
@@ -5503,7 +5517,7 @@ inline Server::Server()
#endif
}
inline Server::~Server() {}
inline Server::~Server() = default;
inline std::unique_ptr<detail::MatcherBase>
Server::make_matcher(const std::string &pattern) {
@@ -5515,66 +5529,60 @@ Server::make_matcher(const std::string &pattern) {
}
inline Server &Server::Get(const std::string &pattern, Handler handler) {
get_handlers_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
inline Server &Server::Post(const std::string &pattern, Handler handler) {
post_handlers_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
inline Server &Server::Post(const std::string &pattern,
HandlerWithContentReader handler) {
post_handlers_for_content_reader_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
std::move(handler));
return *this;
}
inline Server &Server::Put(const std::string &pattern, Handler handler) {
put_handlers_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
inline Server &Server::Put(const std::string &pattern,
HandlerWithContentReader handler) {
put_handlers_for_content_reader_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
std::move(handler));
return *this;
}
inline Server &Server::Patch(const std::string &pattern, Handler handler) {
patch_handlers_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
inline Server &Server::Patch(const std::string &pattern,
HandlerWithContentReader handler) {
patch_handlers_for_content_reader_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
std::move(handler));
return *this;
}
inline Server &Server::Delete(const std::string &pattern, Handler handler) {
delete_handlers_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
inline Server &Server::Delete(const std::string &pattern,
HandlerWithContentReader handler) {
delete_handlers_for_content_reader_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),
std::move(handler));
return *this;
}
inline Server &Server::Options(const std::string &pattern, Handler handler) {
options_handlers_.push_back(
std::make_pair(make_matcher(pattern), std::move(handler)));
options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));
return *this;
}
@@ -5681,6 +5689,12 @@ inline Server &Server::set_default_headers(Headers headers) {
return *this;
}
inline Server &Server::set_header_writer(
std::function<ssize_t(Stream &, Headers &)> const &writer) {
header_writer_ = writer;
return *this;
}
inline Server &Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
return *this;
@@ -5716,8 +5730,7 @@ inline Server &Server::set_payload_max_length(size_t length) {
inline bool Server::bind_to_port(const std::string &host, int port,
int socket_flags) {
if (bind_internal(host, port, socket_flags) < 0) return false;
return true;
return bind_internal(host, port, socket_flags) >= 0;
}
inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
return bind_internal(host, 0, socket_flags);
@@ -5751,7 +5764,7 @@ inline void Server::stop() {
}
}
inline bool Server::parse_request_line(const char *s, Request &req) {
inline bool Server::parse_request_line(const char *s, Request &req) const {
auto len = strlen(s);
if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
len -= 2;
@@ -5875,7 +5888,7 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
return false;
}
if (!detail::write_headers(bstrm, res.headers)) { return false; }
if (!header_writer_(bstrm, res.headers)) { return false; }
// Flush buffer
auto &data = bstrm.get_buffer();
@@ -6002,10 +6015,11 @@ inline bool Server::read_content_with_content_receiver(
std::move(multipart_receiver));
}
inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) {
inline bool
Server::read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) const {
detail::MultipartFormDataParser multipart_form_data_parser;
ContentReceiverWithProgress out;
@@ -6069,7 +6083,7 @@ inline bool Server::handle_file_request(const Request &req, Response &res,
if (detail::is_file(path)) {
for (const auto &kv : entry.headers) {
res.set_header(kv.first.c_str(), kv.second);
res.set_header(kv.first, kv.second);
}
auto mm = std::make_shared<detail::mmap>(path.c_str());
@@ -6296,7 +6310,7 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
}
inline bool Server::dispatch_request(Request &req, Response &res,
const Handlers &handlers) {
const Handlers &handlers) const {
for (const auto &x : handlers) {
const auto &matcher = x.first;
const auto &handler = x.second;
@@ -6311,7 +6325,7 @@ inline bool Server::dispatch_request(Request &req, Response &res,
inline void Server::apply_ranges(const Request &req, Response &res,
std::string &content_type,
std::string &boundary) {
std::string &boundary) const {
if (req.ranges.size() > 1) {
boundary = detail::make_multipart_data_boundary();
@@ -6423,7 +6437,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
inline bool Server::dispatch_request_for_content_reader(
Request &req, Response &res, ContentReader content_reader,
const HandlersForContentReader &handlers) {
const HandlersForContentReader &handlers) const {
for (const auto &x : handlers) {
const auto &matcher = x.first;
const auto &handler = x.second;
@@ -6665,7 +6679,7 @@ inline socket_t ClientImpl::create_client_socket(Error &error) const {
// Check is custom IP specified for host_
std::string ip;
auto it = addr_map_.find(host_);
if (it != addr_map_.end()) ip = it->second;
if (it != addr_map_.end()) { ip = it->second; }
return detail::create_client_socket(
host_, ip, port_, address_family_, tcp_nodelay_, socket_options_,
@@ -6690,7 +6704,7 @@ inline void ClientImpl::shutdown_ssl(Socket & /*socket*/,
socket_requests_are_from_thread_ == std::this_thread::get_id());
}
inline void ClientImpl::shutdown_socket(Socket &socket) {
inline void ClientImpl::shutdown_socket(Socket &socket) const {
if (socket.sock == INVALID_SOCKET) { return; }
detail::shutdown_socket(socket.sock);
}
@@ -6715,7 +6729,7 @@ inline void ClientImpl::close_socket(Socket &socket) {
}
inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
Response &res) {
Response &res) const {
std::array<char, 2048> buf{};
detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
@@ -6723,9 +6737,9 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
if (!line_reader.getline()) { return false; }
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
#else
const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
#else
const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
#endif
std::cmatch m;
@@ -6979,7 +6993,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port);
SSLClient cli(next_host, next_port);
cli.copy_settings(*this);
if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
return detail::redirect(cli, req, res, path, location, error);
@@ -6987,7 +7001,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
return false;
#endif
} else {
ClientImpl cli(next_host.c_str(), next_port);
ClientImpl cli(next_host, next_port);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, path, location, error);
}
@@ -6996,7 +7010,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
inline bool ClientImpl::write_content_with_provider(Stream &strm,
const Request &req,
Error &error) {
Error &error) const {
auto is_shutting_down = []() { return false; };
if (req.is_chunked_content_provider_) {
@@ -7114,7 +7128,7 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;
bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
detail::write_headers(bstrm, req.headers);
header_writer_(bstrm, req.headers);
// Flush buffer
auto &data = bstrm.get_buffer();
@@ -7322,13 +7336,14 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
const std::string &boundary, const MultipartFormDataItems &items,
const MultipartFormDataProviderItems &provider_items) {
size_t cur_item = 0, cur_start = 0;
const MultipartFormDataProviderItems &provider_items) const {
size_t cur_item = 0;
size_t cur_start = 0;
// cur_item and cur_start are copied to within the std::function and maintain
// state between successive calls
return [&, cur_item, cur_start](size_t offset,
DataSink &sink) mutable -> bool {
if (!offset && items.size()) {
if (!offset && !items.empty()) {
sink.os << detail::serialize_multipart_formdata(items, boundary, false);
return true;
} else if (cur_item < provider_items.size()) {
@@ -7345,8 +7360,9 @@ inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
cur_sink.write = sink.write;
cur_sink.done = [&]() { has_data = false; };
if (!provider_items[cur_item].provider(offset - cur_start, cur_sink))
if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {
return false;
}
if (!has_data) {
sink.os << detail::serialize_multipart_formdata_item_end();
@@ -7465,7 +7481,7 @@ inline Result ClientImpl::Get(const std::string &path, const Params &params,
if (params.empty()) { return Get(path, headers); }
std::string path_with_query = append_query_params(path, params);
return Get(path_with_query.c_str(), headers, progress);
return Get(path_with_query, headers, progress);
}
inline Result ClientImpl::Get(const std::string &path, const Params &params,
@@ -7485,8 +7501,8 @@ inline Result ClientImpl::Get(const std::string &path, const Params &params,
}
std::string path_with_query = append_query_params(path, params);
return Get(path_with_query.c_str(), headers, response_handler,
content_receiver, progress);
return Get(path_with_query, headers, response_handler, content_receiver,
progress);
}
inline Result ClientImpl::Head(const std::string &path) {
@@ -7588,7 +7604,7 @@ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
const auto &content_type =
detail::serialize_multipart_formdata_get_content_type(boundary);
const auto &body = detail::serialize_multipart_formdata(items, boundary);
return Post(path, headers, body, content_type.c_str());
return Post(path, headers, body, content_type);
}
inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
@@ -7601,7 +7617,7 @@ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
const auto &content_type =
detail::serialize_multipart_formdata_get_content_type(boundary);
const auto &body = detail::serialize_multipart_formdata(items, boundary);
return Post(path, headers, body, content_type.c_str());
return Post(path, headers, body, content_type);
}
inline Result
@@ -7925,6 +7941,11 @@ inline void ClientImpl::set_default_headers(Headers headers) {
default_headers_ = std::move(headers);
}
inline void ClientImpl::set_header_writer(
std::function<ssize_t(Stream &, Headers &)> const &writer) {
header_writer_ = writer;
}
inline void ClientImpl::set_address_family(int family) {
address_family_ = family;
}
@@ -7978,9 +7999,9 @@ inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
}
inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
std::size_t size) {
std::size_t size) const {
auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
if (!mem) return nullptr;
if (!mem) { return nullptr; }
auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
if (!inf) {
@@ -8130,7 +8151,7 @@ inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
}
inline SSLSocketStream::~SSLSocketStream() {}
inline SSLSocketStream::~SSLSocketStream() = default;
inline bool SSLSocketStream::is_readable() const {
return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
@@ -8346,7 +8367,7 @@ inline SSLClient::SSLClient(const std::string &host, int port,
detail::split(&host_[0], &host_[host_.size()], '.',
[&](const char *b, const char *e) {
host_components_.emplace_back(std::string(b, e));
host_components_.emplace_back(b, e);
});
if (!client_cert_path.empty() && !client_key_path.empty()) {
@@ -8367,7 +8388,7 @@ inline SSLClient::SSLClient(const std::string &host, int port,
detail::split(&host_[0], &host_[host_.size()], '.',
[&](const char *b, const char *e) {
host_components_.emplace_back(std::string(b, e));
host_components_.emplace_back(b, e);
});
if (client_cert != nullptr && client_key != nullptr) {
@@ -8641,8 +8662,8 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
auto type = GEN_DNS;
struct in6_addr addr6;
struct in_addr addr;
struct in6_addr addr6 {};
struct in_addr addr {};
size_t addr_len = 0;
#ifndef __MINGW32__
@@ -8717,7 +8738,7 @@ inline bool SSLClient::check_host_name(const char *pattern,
std::vector<std::string> pattern_components;
detail::split(&pattern[0], &pattern[pattern_len], '.',
[&](const char *b, const char *e) {
pattern_components.emplace_back(std::string(b, e));
pattern_components.emplace_back(b, e);
});
if (host_components_.size() != pattern_components.size()) { return false; }
@@ -8796,7 +8817,7 @@ inline Client::Client(const std::string &host, int port,
: cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
client_key_path)) {}
inline Client::~Client() {}
inline Client::~Client() = default;
inline bool Client::is_valid() const {
return cli_ != nullptr && cli_->is_valid();
@@ -9119,6 +9140,11 @@ inline void Client::set_default_headers(Headers headers) {
cli_->set_default_headers(std::move(headers));
}
inline void Client::set_header_writer(
std::function<ssize_t(Stream &, Headers &)> const &writer) {
cli_->set_header_writer(writer);
}
inline void Client::set_address_family(int family) {
cli_->set_address_family(family);
}
+2
View File
@@ -102,3 +102,5 @@ if(HTTPLIB_IS_USING_OPENSSL)
COMMAND_ERROR_IS_FATAL ANY
)
endif()
add_subdirectory(fuzzing)
+10
View File
@@ -0,0 +1,10 @@
file(GLOB HTTPLIB_CORPUS corpus/*)
add_executable(httplib-test-fuzz
server_fuzzer.cc
standalone_fuzz_target_runner.cpp
)
target_link_libraries(httplib-test-fuzz PRIVATE httplib)
add_test(
NAME httplib-test-fuzz
COMMAND httplib-test-fuzz ${HTTPLIB_CORPUS}
)
+2 -1
View File
@@ -1,5 +1,6 @@
#include <cstdint>
#include <httplib.h>
#include <memory>
class FuzzedStream : public httplib::Stream {
public:
@@ -5,9 +5,9 @@
// on the test corpus or on a single file,
// e.g. the one that comes from a bug report.
#include <cassert>
#include <iostream>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <vector>
// Forward declare the "fuzz target" interface.
@@ -21,7 +21,7 @@ int main(int argc, char **argv) {
std::ifstream in(argv[i]);
in.seekg(0, in.end);
size_t length = static_cast<size_t>(in.tellg());
in.seekg (0, in.beg);
in.seekg(0, in.beg);
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
// Allocate exactly length bytes so that we reliably catch buffer overflows.
std::vector<char> bytes(length);
+116 -99
View File
@@ -211,8 +211,7 @@ TEST(GetHeaderValueTest, DefaultValue) {
TEST(GetHeaderValueTest, DefaultValueInt) {
Headers headers = {{"Dummy", "Dummy"}};
auto val =
detail::get_header_value<uint64_t>(headers, "Content-Length", 0, 100);
auto val = detail::get_header_value_u64(headers, "Content-Length", 0, 100);
EXPECT_EQ(100ull, val);
}
@@ -241,8 +240,7 @@ TEST(GetHeaderValueTest, SetContent) {
TEST(GetHeaderValueTest, RegularValueInt) {
Headers headers = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
auto val =
detail::get_header_value<uint64_t>(headers, "Content-Length", 0, 0);
auto val = detail::get_header_value_u64(headers, "Content-Length", 0, 0);
EXPECT_EQ(100ull, val);
}
@@ -444,7 +442,7 @@ TEST(HostnameToIPConversionTest, HTTPWatch_Online) {
auto host = "www.httpwatch.com";
auto ip = hosted_at(host);
EXPECT_EQ("191.236.16.12", ip);
EXPECT_EQ("23.96.13.243", ip);
std::vector<std::string> addrs;
hosted_at(host, addrs);
@@ -1016,7 +1014,7 @@ TEST(UrlWithSpace, Redirect_Online) {
auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ(18527U, res->get_header_value<uint64_t>("Content-Length"));
EXPECT_EQ(18527U, res->get_header_value_u64("Content-Length"));
}
#endif
@@ -1090,12 +1088,8 @@ TEST(RedirectToDifferentPort, Redirect) {
ASSERT_FALSE(svr1.is_running());
});
while (!svr1.is_running() || !svr2.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));
svr1.wait_until_ready();
svr2.wait_until_ready();
Client cli("localhost", svr2_port);
cli.set_follow_location(true);
@@ -1127,9 +1121,6 @@ TEST(RedirectFromPageWithContent, Redirect) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli("localhost", PORT);
cli.set_follow_location(true);
@@ -1195,9 +1186,6 @@ TEST(RedirectFromPageWithContentIP6, Redirect) {
ASSERT_LT(milliseconds, 5000U);
}
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli("http://[::1]:1234");
cli.set_follow_location(true);
@@ -1248,8 +1236,7 @@ TEST(PathUrlEncodeTest, PathUrlEncode) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
@@ -1279,8 +1266,7 @@ TEST(BindServerTest, DISABLED_BindDualStack) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli("127.0.0.1", PORT);
@@ -1351,8 +1337,7 @@ TEST(ErrorHandlerTest, ContentLength) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
@@ -1393,8 +1378,7 @@ TEST(ExceptionHandlerTest, ContentLength) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
for (size_t i = 0; i < 10; i++) {
Client cli(HOST, PORT);
@@ -1434,8 +1418,7 @@ TEST(NoContentTest, ContentLength) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
@@ -1485,8 +1468,7 @@ TEST(RoutingHandlerTest, PreRoutingHandler) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -1554,8 +1536,7 @@ TEST(InvalidFormatTest, StatusCode) {
ASSERT_FALSE(svr.is_running());
});
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
@@ -1579,7 +1560,7 @@ TEST(URLFragmentTest, WithFragment) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
@@ -1594,6 +1575,46 @@ TEST(URLFragmentTest, WithFragment) {
}
}
TEST(HeaderWriter, SetHeaderWriter) {
Server svr;
svr.set_header_writer([](Stream &strm, Headers &hdrs) {
hdrs.emplace("CustomServerHeader", "CustomServerValue");
return detail::write_headers(strm, hdrs);
});
svr.Get("/hi", [](const Request &req, Response &res) {
auto it = req.headers.find("CustomClientHeader");
EXPECT_TRUE(it != req.headers.end());
EXPECT_EQ(it->second, "CustomClientValue");
res.set_content("Hello World!\n", "text/plain");
});
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
auto se = detail::scope_exit([&] {
svr.stop();
thread.join();
ASSERT_FALSE(svr.is_running());
});
svr.wait_until_ready();
{
Client cli(HOST, PORT);
cli.set_header_writer([](Stream &strm, Headers &hdrs) {
hdrs.emplace("CustomClientHeader", "CustomClientValue");
return detail::write_headers(strm, hdrs);
});
auto res = cli.Get("/hi");
EXPECT_TRUE(res);
EXPECT_EQ(200, res->status);
auto it = res->headers.find("CustomServerHeader");
EXPECT_TRUE(it != res->headers.end());
EXPECT_EQ(it->second, "CustomServerValue");
}
}
class ServerTest : public ::testing::Test {
protected:
ServerTest()
@@ -2167,9 +2188,7 @@ protected:
t_ = thread([&]() { ASSERT_TRUE(svr_.listen(HOST, PORT)); });
while (!svr_.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
svr_.wait_until_ready();
}
virtual void TearDown() {
@@ -3296,7 +3315,7 @@ TEST_F(ServerTest, PutLargeFileWithGzip2) {
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ(LARGE_DATA, res->body);
EXPECT_EQ(101942u, res.get_request_header_value<uint64_t>("Content-Length"));
EXPECT_EQ(101942u, res.get_request_header_value_u64("Content-Length"));
EXPECT_EQ("gzip", res.get_request_header_value("Content-Encoding"));
}
@@ -4184,9 +4203,6 @@ TEST(MountTest, Unmount) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
svr.set_mount_point("/mount2", "./www2");
@@ -4237,9 +4253,6 @@ TEST(ExceptionTest, ThrowExceptionInHandler) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
{
@@ -4281,9 +4294,6 @@ TEST(KeepAliveTest, ReadTimeout) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
cli.set_keep_alive(true);
cli.set_read_timeout(std::chrono::seconds(1));
@@ -4313,7 +4323,7 @@ TEST(KeepAliveTest, Issue1041) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(200));
svr.wait_until_ready();
Client cli(HOST, PORT);
cli.set_keep_alive(true);
@@ -4346,7 +4356,7 @@ TEST(KeepAliveTest, SSLClientReconnection) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(200));
svr.wait_until_ready();
SSLClient cli(HOST, PORT);
cli.enable_server_certificate_verification(false);
@@ -4409,9 +4419,6 @@ TEST(ClientProblemDetectionTest, ContentProvider) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
{
@@ -4449,9 +4456,6 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
auto res = cli.Get("/");
@@ -4460,6 +4464,43 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
EXPECT_EQ("helloworld", res->body);
}
TEST(LongPollingTest, ClientCloseDetection) {
Server svr;
svr.Get("/events", [&](const Request & /*req*/, Response &res) {
res.set_chunked_content_provider(
"text/plain", [](std::size_t const, DataSink &sink) -> bool {
EXPECT_TRUE(sink.is_writable()); // the socket is alive
sink.os << "hello";
auto count = 10;
while (count > 0 && sink.is_writable()) {
this_thread::sleep_for(chrono::milliseconds(10));
}
EXPECT_FALSE(sink.is_writable()); // the socket is closed
return true;
});
});
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
auto se = detail::scope_exit([&] {
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
});
svr.wait_until_ready();
Client cli("localhost", PORT);
auto res = cli.Get("/events", [&](const char *data, size_t data_length) {
EXPECT_EQ("hello", string(data, data_length));
return false; // close the socket immediately.
});
ASSERT_FALSE(res);
}
TEST(GetWithParametersTest, GetWithParameters) {
Server svr;
@@ -4495,7 +4536,6 @@ TEST(GetWithParametersTest, GetWithParameters) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli(HOST, PORT);
@@ -4554,7 +4594,6 @@ TEST(GetWithParametersTest, GetWithParameters2) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
@@ -4622,7 +4661,6 @@ TEST(ServerDefaultHeadersTest, DefaultHeaders) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
Client cli("localhost", PORT);
@@ -4657,9 +4695,6 @@ TEST(KeepAliveTest, ReadTimeoutSSL) {
svr.wait_until_ready();
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
SSLClient cli("localhost", PORT);
cli.enable_server_certificate_verification(false);
cli.set_keep_alive(true);
@@ -4698,9 +4733,7 @@ protected:
t_ = thread(
[&]() { ASSERT_TRUE(svr_.listen(std::string(), PORT, AI_PASSIVE)); });
while (!svr_.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
svr_.wait_until_ready();
}
virtual void TearDown() {
@@ -4737,9 +4770,7 @@ protected:
ASSERT_TRUE(svr_.listen_after_bind());
});
while (!svr_.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
svr_.wait_until_ready();
}
virtual void TearDown() {
@@ -4780,9 +4811,7 @@ protected:
t_ = thread([&]() { ASSERT_TRUE(svr_.listen(HOST, PORT)); });
while (!svr_.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
svr_.wait_until_ready();
}
virtual void TearDown() {
@@ -4899,7 +4928,7 @@ TEST(SSLClientTest, ServerCertificateVerification4) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli("127.0.0.1", PORT);
cli.set_ca_cert_path(SERVER_CERT2_FILE);
@@ -5015,7 +5044,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
cli.enable_server_certificate_verification(false);
@@ -5090,7 +5119,7 @@ TEST(SSLClientServerTest, MemoryClientCertPresent) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli(HOST, PORT, client_cert, client_private_key);
cli.enable_server_certificate_verification(false);
@@ -5121,7 +5150,7 @@ TEST(SSLClientServerTest, ClientCertMissing) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli(HOST, PORT);
auto res = cli.Get("/test");
@@ -5145,7 +5174,7 @@ TEST(SSLClientServerTest, TrustDirOptional) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
cli.enable_server_certificate_verification(false);
@@ -5166,12 +5195,12 @@ TEST(SSLClientServerTest, SSLConnectTimeout) {
client_ca_cert_dir_path),
stop_(false) {}
bool stop_;
std::atomic_bool stop_;
private:
bool process_and_close_socket(socket_t /*sock*/) override {
// Don't create SSL context
while (!stop_) {
while (!stop_.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return true;
@@ -5193,7 +5222,7 @@ TEST(SSLClientServerTest, SSLConnectTimeout) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
cli.enable_server_certificate_verification(false);
@@ -5265,7 +5294,7 @@ TEST(SSLClientServerTest, CustomizeServerSSLCtx) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
SSLClient cli(HOST, PORT, CLIENT_CERT_FILE, CLIENT_PRIVATE_KEY_FILE);
cli.enable_server_certificate_verification(false);
@@ -5401,9 +5430,10 @@ TEST(ServerLargeContentTest, DISABLED_SendLargeContent) {
ASSERT_TRUE(content);
Server svr;
svr.Get("/foo", [=](const httplib::Request &req, httplib::Response &resp) {
resp.set_content(content, content_size, "application/octet-stream");
});
svr.Get("/foo",
[=](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content(content, content_size, "application/octet-stream");
});
auto listen_thread = std::thread([&svr]() { svr.listen(HOST, PORT); });
auto se = detail::scope_exit([&] {
@@ -5415,9 +5445,6 @@ TEST(ServerLargeContentTest, DISABLED_SendLargeContent) {
svr.wait_until_ready();
// 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);
@@ -5552,7 +5579,8 @@ TEST(HttpToHttpsRedirectTest, CertFile) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::milliseconds(1));
svr.wait_until_ready();
ssl_svr.wait_until_ready();
Client cli("127.0.0.1", PORT);
cli.set_ca_cert_path(SERVER_CERT2_FILE);
@@ -5608,7 +5636,6 @@ TEST(MultipartFormDataTest, LargeData) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '.');
@@ -5756,7 +5783,6 @@ TEST(MultipartFormDataTest, DataProviderItems) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli("https://localhost:8080");
@@ -5958,7 +5984,6 @@ TEST(MultipartFormDataTest, PostCustomBoundary) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '.');
@@ -5980,9 +6005,6 @@ TEST(MultipartFormDataTest, PostCustomBoundary) {
}
TEST(MultipartFormDataTest, PostInvalidBoundaryChars) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::string data(1024 * 1024 * 2, '&');
std::stringstream buffer;
buffer << data;
@@ -6045,7 +6067,6 @@ TEST(MultipartFormDataTest, PutFormData) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '&');
@@ -6110,7 +6131,6 @@ TEST(MultipartFormDataTest, PutFormDataCustomBoundary) {
});
svr.wait_until_ready();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::string data(1024 * 1024 * 2, '&');
@@ -6132,9 +6152,6 @@ TEST(MultipartFormDataTest, PutFormDataCustomBoundary) {
}
TEST(MultipartFormDataTest, PutInvalidBoundaryChars) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::string data(1024 * 1024 * 2, '&');
std::stringstream buffer;
buffer << data;
@@ -6204,7 +6221,7 @@ TEST(MultipartFormDataTest, AlternateFilename) {
"\r\n"
"text default\r\n"
"----------\r\n"
"Content-Disposition: form-data; filename*=\"UTF-8''\%41.txt\"; "
"Content-Disposition: form-data; filename*=\"UTF-8''%41.txt\"; "
"filename=\"a.txt\"; name=\"file1\"\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
@@ -6463,7 +6480,7 @@ TEST(RedirectTest, RedirectToUrlWithQueryParameters) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
@@ -6509,7 +6526,7 @@ TEST(VulnerabilityTest, CRLFInjection) {
ASSERT_FALSE(svr.is_running());
});
std::this_thread::sleep_for(std::chrono::seconds(1));
svr.wait_until_ready();
{
Client cli(HOST, PORT);
+1
View File
@@ -5,3 +5,4 @@
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)
configure_file(input: '1MB.txt', output: '1MB.txt', copy: true)