Compare commits

..

12 Commits

Author SHA1 Message Date
Ken Schalk 7c60e69c33 Remove redunant call to close_socket (#911) 2021-04-23 17:07:19 -04:00
yhirose 33e94891ee Updated test.cc 2021-04-22 08:04:46 -04:00
yhirose 73e0729f63 Change sink.write() to return boolean 2021-04-22 07:14:08 -04:00
yhirose 21c529229c Fixed timeout issues 2021-04-22 07:14:08 -04:00
yhirose 63643e6386 Code format 2021-04-13 20:52:49 -04:00
yhirose 6cc2edce99 Added set_address_family 2021-04-13 20:49:52 -04:00
yhirose d122ff3ca8 Code formatting 2021-04-13 12:38:45 -04:00
James Young 14c6d526b4 Use newer version-flexible TLS/SSL method (#904) 2021-04-13 09:11:38 -04:00
Philipp Hasper 28e07bca16 Fixed minor code smells (#901) 2021-04-09 14:55:21 -04:00
yhirose faa5f1d802 Additional changes for #889 2021-04-05 16:13:41 -04:00
yhirose 9d3365df54 Fix #889 2021-04-05 11:40:53 -04:00
yhirose 6ff84d34d1 Another simpler implementation of #890 (#891) 2021-04-02 18:25:04 -04:00
3 changed files with 380 additions and 160 deletions
+2 -4
View File
@@ -20,8 +20,6 @@ using namespace std;
class EventDispatcher {
public:
EventDispatcher() {
id_ = 0;
cid_ = -1;
}
void wait_event(DataSink *sink) {
@@ -41,8 +39,8 @@ public:
private:
mutex m_;
condition_variable cv_;
atomic_int id_;
atomic_int cid_;
atomic_int id_ = 0;
atomic_int cid_ = -1;
string message_;
};
+220 -71
View File
@@ -200,11 +200,11 @@ using socket_t = int;
#include <mutex>
#include <random>
#include <regex>
#include <set>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <thread>
#include <set>
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#include <openssl/err.h>
@@ -257,7 +257,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)...));
}
@@ -308,7 +308,7 @@ public:
DataSink(DataSink &&) = delete;
DataSink &operator=(DataSink &&) = delete;
std::function<void(const char *data, size_t data_len)> write;
std::function<bool(const char *data, size_t data_len)> write;
std::function<void()> done;
std::function<bool()> is_writable;
std::ostream os;
@@ -395,7 +395,7 @@ struct Request {
ContentReceiverWithProgress content_receiver;
Progress progress;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
const SSL *ssl;
const SSL *ssl = nullptr;
#endif
bool has_header(const char *key) const;
@@ -487,7 +487,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);
};
@@ -663,14 +663,24 @@ public:
Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
Server &set_logger(Logger logger);
Server &set_address_family(int family);
Server &set_tcp_nodelay(bool on);
Server &set_socket_options(SocketOptions socket_options);
Server &set_keep_alive_max_count(size_t count);
Server &set_keep_alive_timeout(time_t sec);
Server &set_read_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
Server &set_write_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
Server &set_idle_interval(time_t sec, time_t usec = 0);
template <class Rep, class Period>
Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
Server &set_payload_max_length(size_t length);
@@ -773,11 +783,12 @@ private:
Logger logger_;
Expect100ContinueHandler expect_100_continue_handler_;
int address_family_ = AF_UNSPEC;
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
SocketOptions socket_options_ = default_socket_options;
};
enum Error {
enum class Error {
Success = 0,
Unknown,
Connection,
@@ -962,12 +973,22 @@ public:
void set_default_headers(Headers headers);
void set_address_family(int family);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
void set_connection_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
void
set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
void set_read_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
void set_write_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
void set_basic_auth(const char *username, const char *password);
void set_bearer_token_auth(const char *token);
@@ -1074,6 +1095,7 @@ protected:
bool keep_alive_ = false;
bool follow_location_ = false;
int address_family_ = AF_UNSPEC;
bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
SocketOptions socket_options_ = nullptr;
@@ -1264,12 +1286,22 @@ public:
void set_default_headers(Headers headers);
void set_address_family(int family);
void set_tcp_nodelay(bool on);
void set_socket_options(SocketOptions socket_options);
void set_connection_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
void
set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
void set_read_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
void set_write_timeout(time_t sec, time_t usec = 0);
template <class Rep, class Period>
void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
void set_basic_auth(const char *username, const char *password);
void set_bearer_token_auth(const char *token);
@@ -2033,15 +2065,16 @@ inline int shutdown_socket(socket_t sock) {
}
template <typename BindOrConnect>
socket_t create_socket(const char *host, int port, int socket_flags,
bool tcp_nodelay, SocketOptions socket_options,
socket_t create_socket(const char *host, int port, int address_family,
int socket_flags, bool tcp_nodelay,
SocketOptions socket_options,
BindOrConnect bind_or_connect) {
// Get address info
struct addrinfo hints;
struct addrinfo *result;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_family = address_family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = socket_flags;
hints.ai_protocol = 0;
@@ -2058,8 +2091,9 @@ socket_t create_socket(const char *host, int port, int socket_flags,
for (auto rp = result; rp; rp = rp->ai_next) {
// Create a socket
#ifdef _WIN32
auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);
auto sock =
WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,
WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
/**
* Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
* and above the socket creation fails on older Windows Systems.
@@ -2181,13 +2215,14 @@ inline std::string if2ip(const std::string &ifn) {
}
#endif
inline socket_t create_client_socket(const char *host, int port,
bool tcp_nodelay,
SocketOptions socket_options,
time_t timeout_sec, time_t timeout_usec,
const std::string &intf, Error &error) {
inline socket_t create_client_socket(
const char *host, int port, int address_family, bool tcp_nodelay,
SocketOptions socket_options, time_t connection_timeout_sec,
time_t connection_timeout_usec, time_t read_timeout_sec,
time_t read_timeout_usec, time_t write_timeout_sec,
time_t write_timeout_usec, const std::string &intf, Error &error) {
auto sock = create_socket(
host, port, 0, tcp_nodelay, std::move(socket_options),
host, port, address_family, 0, tcp_nodelay, std::move(socket_options),
[&](socket_t sock, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
#ifdef USE_IF2IP
@@ -2207,14 +2242,28 @@ inline socket_t create_client_socket(const char *host, int port,
if (ret < 0) {
if (is_connection_error() ||
!wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) {
close_socket(sock);
!wait_until_socket_is_ready(sock, connection_timeout_sec,
connection_timeout_usec)) {
error = Error::Connection;
return false;
}
}
set_nonblocking(sock, false);
{
timeval tv;
tv.tv_sec = static_cast<long>(read_timeout_sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
}
{
timeval tv;
tv.tv_sec = static_cast<long>(write_timeout_sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
}
error = Error::Success;
return true;
});
@@ -2971,7 +3020,7 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
auto ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) {
if (write_data(strm, d, l)) {
offset += l;
@@ -2979,6 +3028,7 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
ok = false;
}
}
return ok;
};
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
@@ -3017,11 +3067,12 @@ write_content_without_length(Stream &strm,
auto ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) {
offset += l;
if (!write_data(strm, d, l)) { ok = false; }
}
return ok;
};
data_sink.done = [&](void) { data_available = false; };
@@ -3044,30 +3095,30 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
auto ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
if (!ok) { return; }
data_sink.write = [&](const char *d, size_t l) -> bool {
if (ok) {
data_available = l > 0;
offset += l;
data_available = l > 0;
offset += l;
std::string payload;
if (!compressor.compress(d, l, false,
[&](const char *data, size_t data_len) {
payload.append(data, data_len);
return true;
})) {
ok = false;
return;
}
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (!write_data(strm, chunk.data(), chunk.size())) {
std::string payload;
if (compressor.compress(d, l, false,
[&](const char *data, size_t data_len) {
payload.append(data, data_len);
return true;
})) {
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk =
from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (!write_data(strm, chunk.data(), chunk.size())) {
ok = false;
}
}
} else {
ok = false;
return;
}
}
return ok;
};
data_sink.done = [&](void) {
@@ -3175,9 +3226,7 @@ inline void parse_query_text(const std::string &s, Params &params) {
std::set<std::string> cache;
split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
std::string kv(b, e);
if (cache.find(kv) != cache.end()) {
return;
}
if (cache.find(kv) != cache.end()) { return; }
cache.insert(kv);
std::string key;
@@ -3719,9 +3768,9 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
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;
@@ -3804,6 +3853,15 @@ private:
ContentProviderWithoutLength content_provider_;
};
template <typename T, typename U>
inline void duration_to_sec_and_usec(const T &duration, U callback) {
auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
duration - std::chrono::seconds(sec))
.count();
callback(sec, usec);
}
} // namespace detail
// Header utilities
@@ -4024,7 +4082,7 @@ inline ssize_t Stream::write(const std::string &s) {
}
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;
@@ -4299,13 +4357,11 @@ inline Server &
Server::set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime) {
file_extension_and_mimetype_map_[ext] = mime;
return *this;
}
inline Server &Server::set_file_request_handler(Handler handler) {
file_request_handler_ = std::move(handler);
return *this;
}
@@ -4339,7 +4395,6 @@ inline Server &Server::set_post_routing_handler(Handler handler) {
inline Server &Server::set_logger(Logger logger) {
logger_ = std::move(logger);
return *this;
}
@@ -4350,54 +4405,75 @@ Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
return *this;
}
inline Server &Server::set_address_family(int family) {
address_family_ = family;
return *this;
}
inline Server &Server::set_tcp_nodelay(bool on) {
tcp_nodelay_ = on;
return *this;
}
inline Server &Server::set_socket_options(SocketOptions socket_options) {
socket_options_ = std::move(socket_options);
return *this;
}
inline Server &Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
return *this;
}
inline Server &Server::set_keep_alive_timeout(time_t sec) {
keep_alive_timeout_sec_ = sec;
return *this;
}
inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
return *this;
}
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
return *this;
}
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
idle_interval_sec_ = sec;
idle_interval_usec_ = usec;
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
return *this;
}
inline Server &Server::set_payload_max_length(size_t length) {
payload_max_length_ = length;
return *this;
}
@@ -4713,7 +4789,8 @@ inline socket_t
Server::create_server_socket(const char *host, int port, int socket_flags,
SocketOptions socket_options) const {
return detail::create_socket(
host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
host, port, address_family_, socket_flags, tcp_nodelay_,
std::move(socket_options),
[](socket_t sock, struct addrinfo &ai) -> bool {
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
return false;
@@ -4788,6 +4865,19 @@ inline bool Server::listen_internal() {
break;
}
{
timeval tv;
tv.tv_sec = static_cast<long>(read_timeout_sec_);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
}
{
timeval tv;
tv.tv_sec = static_cast<long>(write_timeout_sec_);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
}
#if __cplusplus > 201703L
task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
#else
@@ -5202,12 +5292,16 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
inline socket_t ClientImpl::create_client_socket(Error &error) const {
if (!proxy_host_.empty() && proxy_port_ != -1) {
return detail::create_client_socket(
proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_,
connection_timeout_sec_, connection_timeout_usec_, interface_, error);
proxy_host_.c_str(), proxy_port_, address_family_, tcp_nodelay_,
socket_options_, connection_timeout_sec_, connection_timeout_usec_,
read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
write_timeout_usec_, interface_, error);
}
return detail::create_client_socket(
host_.c_str(), port_, tcp_nodelay_, socket_options_,
connection_timeout_sec_, connection_timeout_usec_, interface_, error);
host_.c_str(), port_, address_family_, tcp_nodelay_, socket_options_,
connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_,
error);
}
inline bool ClientImpl::create_and_connect_socket(Socket &socket,
@@ -5653,7 +5747,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
size_t offset = 0;
DataSink data_sink;
data_sink.write = [&](const char *data, size_t data_len) {
data_sink.write = [&](const char *data, size_t data_len) -> bool {
if (ok) {
auto last = offset + data_len == content_length;
@@ -5669,6 +5763,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
ok = false;
}
}
return ok;
};
data_sink.is_writable = [&](void) { return ok && true; };
@@ -5746,19 +5841,22 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
return false;
}
if (req.response_handler) {
if (!req.response_handler(res)) {
error = Error::Canceled;
return false;
}
}
// Body
if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") {
auto redirect = 300 < res.status && res.status < 400 && follow_location_;
if (req.response_handler && !redirect) {
if (!req.response_handler(res)) {
error = Error::Canceled;
return false;
}
}
auto out =
req.content_receiver
? static_cast<ContentReceiverWithProgress>(
[&](const char *buf, size_t n, uint64_t off, uint64_t len) {
if (redirect) { return true; }
auto ret = req.content_receiver(buf, n, off, len);
if (!ret) { error = Error::Canceled; }
return ret;
@@ -5774,7 +5872,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
});
auto progress = [&](uint64_t current, uint64_t total) {
if (!req.progress) { return true; }
if (!req.progress || redirect) { return true; }
auto ret = req.progress(current, total);
if (!ret) { error = Error::Canceled; }
return ret;
@@ -6273,16 +6371,38 @@ inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
connection_timeout_usec_ = usec;
}
template <class Rep, class Period>
inline void ClientImpl::set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
set_connection_timeout(sec, usec);
});
}
inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
}
template <class Rep, class Period>
inline void ClientImpl::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
}
inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
}
template <class Rep, class Period>
inline void ClientImpl::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
}
inline void ClientImpl::set_basic_auth(const char *username,
const char *password) {
basic_auth_username_ = username;
@@ -6309,6 +6429,10 @@ inline void ClientImpl::set_default_headers(Headers headers) {
default_headers_ = std::move(headers);
}
inline void ClientImpl::set_address_family(int family) {
address_family_ = family;
}
inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
@@ -6570,7 +6694,7 @@ static SSLInit sslinit_;
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) {
ctx_ = SSL_CTX_new(SSLv23_server_method());
ctx_ = SSL_CTX_new(TLS_method());
if (ctx_) {
SSL_CTX_set_options(ctx_,
@@ -7371,7 +7495,12 @@ inline void Client::set_default_headers(Headers headers) {
cli_->set_default_headers(std::move(headers));
}
inline void Client::set_address_family(int family) {
cli_->set_address_family(family);
}
inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
inline void Client::set_socket_options(SocketOptions socket_options) {
cli_->set_socket_options(std::move(socket_options));
}
@@ -7379,13 +7508,33 @@ inline void Client::set_socket_options(SocketOptions socket_options) {
inline void Client::set_connection_timeout(time_t sec, time_t usec) {
cli_->set_connection_timeout(sec, usec);
}
template <class Rep, class Period>
inline void Client::set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration) {
cli_->set_connection_timeout(duration);
}
inline void Client::set_read_timeout(time_t sec, time_t usec) {
cli_->set_read_timeout(sec, usec);
}
template <class Rep, class Period>
inline void
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
cli_->set_read_timeout(duration);
}
inline void Client::set_write_timeout(time_t sec, time_t usec) {
cli_->set_write_timeout(sec, usec);
}
template <class Rep, class Period>
inline void
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
cli_->set_write_timeout(duration);
}
inline void Client::set_basic_auth(const char *username, const char *password) {
cli_->set_basic_auth(username, password);
}
+158 -85
View File
@@ -417,16 +417,16 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
cli.set_connection_timeout(2);
std::string body;
auto res =
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
[&](const Response &response) {
EXPECT_EQ(200, response.status);
return true;
},
[&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
auto res = cli.Get(
"/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
[&](const Response &response) {
EXPECT_EQ(200, response.status);
return true;
},
[&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res);
std::string out;
@@ -525,7 +525,7 @@ TEST(ConnectionErrorTest, InvalidHost) {
auto port = 80;
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
cli.set_connection_timeout(std::chrono::seconds(2));
auto res = cli.Get("/");
ASSERT_TRUE(!res);
@@ -540,7 +540,7 @@ TEST(ConnectionErrorTest, InvalidHost2) {
#else
Client cli(host);
#endif
cli.set_connection_timeout(2);
cli.set_connection_timeout(std::chrono::seconds(2));
auto res = cli.Get("/");
ASSERT_TRUE(!res);
@@ -556,7 +556,7 @@ TEST(ConnectionErrorTest, InvalidPort) {
#else
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
cli.set_connection_timeout(std::chrono::seconds(2));
auto res = cli.Get("/");
ASSERT_TRUE(!res);
@@ -573,7 +573,7 @@ TEST(ConnectionErrorTest, Timeout) {
auto port = 8080;
Client cli(host, port);
#endif
cli.set_connection_timeout(2);
cli.set_connection_timeout(std::chrono::seconds(2));
auto res = cli.Get("/");
ASSERT_TRUE(!res);
@@ -590,7 +590,7 @@ TEST(CancelTest, NoCancel) {
auto port = 80;
Client cli(host, port);
#endif
cli.set_connection_timeout(5);
cli.set_connection_timeout(std::chrono::seconds(5));
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
ASSERT_TRUE(res);
@@ -610,7 +610,7 @@ TEST(CancelTest, WithCancelSmallPayload) {
#endif
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
cli.set_connection_timeout(5);
cli.set_connection_timeout(std::chrono::seconds(5));
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Canceled, res.error());
}
@@ -625,7 +625,7 @@ TEST(CancelTest, WithCancelLargePayload) {
auto port = 80;
Client cli(host, port);
#endif
cli.set_connection_timeout(5);
cli.set_connection_timeout(std::chrono::seconds(5));
uint32_t count = 0;
auto res = cli.Get("/range/65536",
@@ -889,6 +889,62 @@ TEST(UrlWithSpace, Redirect) {
EXPECT_EQ(200, res->status);
EXPECT_EQ(18527, res->get_header_value<uint64_t>("Content-Length"));
}
TEST(RedirectFromPageWithContent, Redirect) {
Server svr;
svr.Get("/1", [&](const Request & /*req*/, Response &res) {
res.set_content("___", "text/plain");
res.set_redirect("/2");
});
svr.Get("/2", [&](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
});
auto th = std::thread([&]() { svr.listen("localhost", PORT); });
while (!svr.is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
// Give GET time to get a few messages.
std::this_thread::sleep_for(std::chrono::seconds(1));
{
Client cli("localhost", PORT);
cli.set_follow_location(true);
std::string body;
auto res = cli.Get("/1", [&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("Hello World!", body);
}
{
Client cli("localhost", PORT);
std::string body;
auto res = cli.Get("/1", [&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res);
EXPECT_EQ(302, res->status);
EXPECT_EQ("___", body);
}
svr.stop();
th.join();
ASSERT_FALSE(svr.is_running());
}
#endif
TEST(BindServerTest, BindDualStack) {
@@ -1310,7 +1366,8 @@ protected:
const auto &d = *data;
auto out_len =
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
sink.write(&d[static_cast<size_t>(offset)], out_len);
auto ret = sink.write(&d[static_cast<size_t>(offset)], out_len);
EXPECT_TRUE(ret);
return true;
},
[data] { delete data; });
@@ -2462,13 +2519,14 @@ TEST_F(ServerTest, SlowPost) {
char buffer[64 * 1024];
memset(buffer, 0x42, sizeof(buffer));
auto res =
cli_.Post("/slowpost", 64 * 1024 * 1024,
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
sink.write(buffer, sizeof(buffer));
return true;
},
"text/plain");
auto res = cli_.Post(
"/slowpost", 64 * 1024 * 1024,
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
auto ret = sink.write(buffer, sizeof(buffer));
EXPECT_TRUE(ret);
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
@@ -2478,14 +2536,14 @@ TEST_F(ServerTest, SlowPostFail) {
char buffer[64 * 1024];
memset(buffer, 0x42, sizeof(buffer));
cli_.set_write_timeout(0, 0);
auto res =
cli_.Post("/slowpost", 64 * 1024 * 1024,
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
sink.write(buffer, sizeof(buffer));
return true;
},
"text/plain");
cli_.set_write_timeout(std::chrono::seconds(0));
auto res = cli_.Post(
"/slowpost", 64 * 1024 * 1024,
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
sink.write(buffer, sizeof(buffer));
return true;
},
"text/plain");
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Write, res.error());
@@ -2499,13 +2557,14 @@ TEST_F(ServerTest, Put) {
}
TEST_F(ServerTest, PutWithContentProvider) {
auto res = cli_.Put("/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
return true;
},
"text/plain");
auto res = cli_.Put(
"/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
@@ -2513,24 +2572,27 @@ TEST_F(ServerTest, PutWithContentProvider) {
}
TEST_F(ServerTest, PostWithContentProviderAbort) {
auto res = cli_.Post("/post", 42,
[](size_t /*offset*/, size_t /*length*/,
DataSink & /*sink*/) { return false; },
"text/plain");
auto res = cli_.Post(
"/post", 42,
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
return false;
},
"text/plain");
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Canceled, res.error());
}
TEST_F(ServerTest, PutWithContentProviderWithoutLength) {
auto res = cli_.Put("/put",
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
sink.done();
return true;
},
"text/plain");
auto res = cli_.Put(
"/put",
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
sink.done();
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
@@ -2549,13 +2611,14 @@ TEST_F(ServerTest, PostWithContentProviderWithoutLengthAbort) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
cli_.set_compress(true);
auto res = cli_.Put("/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
return true;
},
"text/plain");
auto res = cli_.Put(
"/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
@@ -2564,10 +2627,12 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
cli_.set_compress(true);
auto res = cli_.Post("/post", 42,
[](size_t /*offset*/, size_t /*length*/,
DataSink & /*sink*/) { return false; },
"text/plain");
auto res = cli_.Post(
"/post", 42,
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
return false;
},
"text/plain");
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Canceled, res.error());
@@ -2575,14 +2640,15 @@ TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
TEST_F(ServerTest, PutWithContentProviderWithoutLengthWithGzip) {
cli_.set_compress(true);
auto res = cli_.Put("/put",
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
sink.done();
return true;
},
"text/plain");
auto res = cli_.Put(
"/put",
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
sink.done();
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
@@ -2902,8 +2968,9 @@ TEST_F(ServerTest, KeepAlive) {
EXPECT_EQ("empty", res->body);
EXPECT_EQ("close", res->get_header_value("Connection"));
res = cli_.Post("/empty", 0, [&](size_t, size_t, DataSink &) { return true; },
"text/plain");
res = cli_.Post(
"/empty", 0, [&](size_t, size_t, DataSink &) { return true; },
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
@@ -3079,8 +3146,11 @@ static bool send_request(time_t read_timeout_sec, const std::string &req,
auto error = Error::Success;
auto client_sock =
detail::create_client_socket(HOST, PORT, false, nullptr,
/*timeout_sec=*/5, 0, std::string(), error);
detail::create_client_socket(HOST, PORT, AF_UNSPEC, false, nullptr,
/*connection_timeout_sec=*/5, 0,
/*read_timeout_sec=*/5, 0,
/*write_timeout_sec=*/5, 0,
std::string(), error);
if (client_sock == INVALID_SOCKET) { return false; }
@@ -3146,7 +3216,7 @@ static void test_raw_request(const std::string &req,
// bug to reproduce, probably to force the server to process a request
// without a trailing blank line.
const time_t client_read_timeout_sec = 1;
svr.set_read_timeout(client_read_timeout_sec + 1, 0);
svr.set_read_timeout(std::chrono::seconds(client_read_timeout_sec + 1));
bool listen_thread_ok = false;
thread t = thread([&] { listen_thread_ok = svr.listen(HOST, PORT); });
while (!svr.is_running()) {
@@ -3281,7 +3351,8 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
DataSink &sink) {
char buffer[27];
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
sink.write(buffer, size);
auto ret = sink.write(buffer, size);
EXPECT_TRUE(ret);
std::this_thread::sleep_for(std::chrono::seconds(1));
return true;
});
@@ -3446,7 +3517,7 @@ TEST(KeepAliveTest, ReadTimeout) {
Client cli("localhost", PORT);
cli.set_keep_alive(true);
cli.set_read_timeout(1);
cli.set_read_timeout(std::chrono::seconds(1));
auto resa = cli.Get("/a");
ASSERT_TRUE(!resa);
@@ -3498,7 +3569,7 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
TEST(GetWithParametersTest, GetWithParameters) {
Server svr;
svr.Get("/", [&](const Request & req, Response &res) {
svr.Get("/", [&](const Request &req, Response &res) {
auto text = req.get_param_value("hello");
res.set_content(text, "text/plain");
});
@@ -3527,7 +3598,7 @@ TEST(GetWithParametersTest, GetWithParameters) {
TEST(GetWithParametersTest, GetWithParameters2) {
Server svr;
svr.Get("/", [&](const Request & req, Response &res) {
svr.Get("/", [&](const Request &req, Response &res) {
auto text = req.get_param_value("hello");
res.set_content(text, "text/plain");
});
@@ -3544,10 +3615,11 @@ TEST(GetWithParametersTest, GetWithParameters2) {
params.emplace("hello", "world");
std::string body;
auto res = cli.Get("/", params, Headers{}, [&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
auto res = cli.Get("/", params, Headers{},
[&](const char *data, size_t data_length) {
body.append(data, data_length);
return true;
});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
@@ -3583,7 +3655,7 @@ TEST(KeepAliveTest, ReadTimeoutSSL) {
SSLClient cli("localhost", PORT);
cli.enable_server_certificate_verification(false);
cli.set_keep_alive(true);
cli.set_read_timeout(1);
cli.set_read_timeout(std::chrono::seconds(1));
auto resa = cli.Get("/a");
ASSERT_TRUE(!resa);
@@ -3808,6 +3880,7 @@ TEST(SSLClientTest, WildcardHostNameMatch) {
cli.set_ca_cert_path(CA_CERT_FILE);
cli.enable_server_certificate_verification(true);
cli.set_follow_location(true);
auto res = cli.Get("/");
ASSERT_TRUE(res);