Compare commits

...

4 Commits

Author SHA1 Message Date
yhirose 88c961f37e Removed std::atomic<Error> error_ 2020-12-01 15:17:34 +00:00
yhirose b952376968 Fixed warning 2020-12-01 03:50:55 +00:00
yhirose 5dd605d3a2 Fix #762 2020-11-30 21:49:31 -05:00
Seunghwan Hong 9c0c98b1ed Add keep_alive_timeout guide on README.md (#778) 2020-11-30 07:11:22 -05:00
6 changed files with 252 additions and 213 deletions
+1
View File
@@ -275,6 +275,7 @@ svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
```cpp
svr.set_keep_alive_max_count(2); // Default is 5
svr.set_keep_alive_timeout(10); // Default is 5
```
### Timeout
+193 -160
View File
@@ -753,7 +753,8 @@ enum Error {
SSLConnection,
SSLLoadingCerts,
SSLServerVerification,
UnsupportedMultipartBoundaryChars
UnsupportedMultipartBoundaryChars,
Compression,
};
class Result {
@@ -878,7 +879,8 @@ public:
Result Options(const char *path);
Result Options(const char *path, const Headers &headers);
bool send(const Request &req, Response &res);
bool send(const Request &req, Response &res, Error &error);
Result send(const Request &req);
size_t is_socket_open() const;
@@ -931,7 +933,7 @@ protected:
bool is_open() const { return sock != INVALID_SOCKET; }
};
virtual bool create_and_connect_socket(Socket &socket);
virtual bool create_and_connect_socket(Socket &socket, Error &error);
// All of:
// shutdown_ssl
@@ -949,16 +951,13 @@ protected:
void lock_socket_and_shutdown_and_close();
bool process_request(Stream &strm, const Request &req, Response &res,
bool close_connection);
bool close_connection, Error &error);
bool write_content_with_provider(Stream &strm, const Request &req);
Error get_last_error() const;
bool write_content_with_provider(Stream &strm, const Request &req,
Error &error);
void copy_settings(const ClientImpl &rhs);
// Error state
mutable std::atomic<Error> error_;
// Socket endoint information
const std::string host_;
const int port_;
@@ -970,7 +969,7 @@ protected:
std::recursive_mutex request_mutex_;
// These are all protected under socket_mutex
int socket_requests_in_flight_ = 0;
size_t socket_requests_in_flight_ = 0;
std::thread::id socket_requests_are_from_thread_ = std::thread::id();
bool socket_should_be_closed_when_request_is_done_ = false;
@@ -1025,13 +1024,20 @@ protected:
Logger logger_;
private:
socket_t create_client_socket() const;
socket_t create_client_socket(Error &error) const;
bool read_response_line(Stream &strm, Response &res);
bool write_request(Stream &strm, const Request &req, bool close_connection);
bool redirect(const Request &req, Response &res);
bool write_request(Stream &strm, const Request &req, bool close_connection,
Error &error);
bool redirect(const Request &req, Response &res, Error &error);
bool handle_request(Stream &strm, const Request &req, Response &res,
bool close_connection);
bool close_connection, Error &error);
std::unique_ptr<Response> send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const char *content_type, Error &error);
Result send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider,
@@ -1149,7 +1155,8 @@ public:
Result Options(const char *path);
Result Options(const char *path, const Headers &headers);
bool send(const Request &req, Response &res);
bool send(const Request &req, Response &res, Error &error);
Result send(const Request &req);
size_t is_socket_open() const;
@@ -1260,15 +1267,16 @@ public:
SSL_CTX *ssl_context() const;
private:
bool create_and_connect_socket(Socket &socket) override;
bool create_and_connect_socket(Socket &socket, Error &error) override;
void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
bool process_socket(const Socket &socket,
std::function<bool(Stream &strm)> callback) override;
bool is_ssl() const override;
bool connect_with_proxy(Socket &sock, Response &res, bool &success);
bool initialize_ssl(Socket &socket);
bool connect_with_proxy(Socket &sock, Response &res, bool &success,
Error &error);
bool initialize_ssl(Socket &socket, Error &error);
bool load_certs();
@@ -1340,14 +1348,6 @@ inline std::string from_i_to_hex(size_t n) {
return ret;
}
inline bool start_with(const std::string &a, const std::string &b) {
if (a.size() < b.size()) { return false; }
for (size_t i = 0; i < b.size(); i++) {
if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
}
return true;
}
inline size_t to_utf8(int code, char *buff) {
if (code < 0x0080) {
buff[0] = (code & 0x7F);
@@ -2064,8 +2064,7 @@ 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,
std::atomic<Error> &error) {
const std::string &intf, Error &error) {
auto sock = create_socket(
host, port, 0, tcp_nodelay, std::move(socket_options),
[&](socket_t sock, struct addrinfo &ai) -> bool {
@@ -2812,7 +2811,7 @@ inline bool write_data(Stream &strm, const char *d, size_t l) {
template <typename T>
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
size_t offset, size_t length, T is_shutting_down,
std::atomic<Error> &error) {
Error &error) {
size_t end_offset = offset + length;
auto ok = true;
DataSink data_sink;
@@ -2848,7 +2847,7 @@ template <typename T>
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
size_t offset, size_t length,
const T &is_shutting_down) {
std::atomic<Error> error;
auto error = Error::Success;
return write_content(strm, content_provider, offset, length, is_shutting_down,
error);
}
@@ -2882,10 +2881,9 @@ write_content_without_length(Stream &strm,
}
template <typename T, typename U>
inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down, U &compressor,
std::atomic<Error> &error) {
inline bool
write_content_chunked(Stream &strm, const ContentProvider &content_provider,
const T &is_shutting_down, U &compressor, Error &error) {
size_t offset = 0;
auto data_available = true;
auto ok = true;
@@ -2968,14 +2966,15 @@ template <typename T, typename U>
inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down, U &compressor) {
std::atomic<Error> error;
auto error = Error::Success;
return write_content_chunked(strm, content_provider, is_shutting_down,
compressor, error);
}
template <typename T>
inline bool redirect(T &cli, const Request &req, Response &res,
const std::string &path, const std::string &location) {
const std::string &path, const std::string &location,
Error &error) {
Request new_req = req;
new_req.path = path;
new_req.redirect_count_ -= 1;
@@ -2988,7 +2987,7 @@ inline bool redirect(T &cli, const Request &req, Response &res,
Response new_res;
auto ret = cli.send(new_req, new_res);
auto ret = cli.send(new_req, new_res, error);
if (ret) {
new_res.location = location;
res = new_res;
@@ -3126,7 +3125,7 @@ public:
static const std::string header_name = "content-type:";
const auto header = buf_.substr(0, pos);
if (start_with(header, header_name)) {
if (start_with_case_ignore(header, header_name)) {
file_.content_type = trim_copy(header.substr(header_name.size()));
} else {
std::smatch m;
@@ -3148,15 +3147,7 @@ public:
auto pattern = crlf_ + dash_;
if (pattern.size() > buf_.size()) { return true; }
auto pos = buf_.find(pattern);
if (pos == std::string::npos) {
pos = buf_.size();
while (pos > 0) {
auto c = buf_[pos - 1];
if (c != '\r' && c != '\n' && c != '-') { break; }
pos--;
}
}
auto pos = find_string(buf_, pattern);
if (!content_callback(buf_.data(), pos)) {
is_valid_ = false;
@@ -3166,7 +3157,6 @@ public:
off_ += pos;
buf_.erase(0, pos);
}
{
auto pattern = crlf_ + dash_ + boundary_;
if (pattern.size() > buf_.size()) { return true; }
@@ -3230,6 +3220,43 @@ private:
file_.content_type.clear();
}
bool start_with_case_ignore(const std::string &a,
const std::string &b) const {
if (a.size() < b.size()) { return false; }
for (size_t i = 0; i < b.size(); i++) {
if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
}
return true;
}
bool start_with(const std::string &a, size_t off,
const std::string &b) const {
if (a.size() - off < b.size()) { return false; }
for (size_t i = 0; i < b.size(); i++) {
if (a[i + off] != b[i]) { return false; }
}
return true;
}
size_t find_string(const std::string &s, const std::string &pattern) const {
auto c = pattern.front();
size_t off = 0;
while (off < s.size()) {
auto pos = s.find(c, off);
if (pos == std::string::npos) { return s.size(); }
auto rem = s.size() - pos;
if (pattern.size() > rem) { return pos; }
if (start_with(s, pos, pattern)) { return pos; }
off = pos + 1;
}
return s.size();
}
std::string boundary_;
std::string buf_;
@@ -4812,7 +4839,8 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
inline ClientImpl::ClientImpl(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
: error_(Error::Success), host_(host), port_(port),
// : (Error::Success), host_(host), port_(port),
: host_(host), port_(port),
host_and_port_(host_ + ":" + std::to_string(port_)),
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
@@ -4820,8 +4848,6 @@ inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
inline bool ClientImpl::is_valid() const { return true; }
inline Error ClientImpl::get_last_error() const { return error_; }
inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
client_cert_path_ = rhs.client_cert_path_;
client_key_path_ = rhs.client_key_path_;
@@ -4859,19 +4885,20 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
logger_ = rhs.logger_;
}
inline socket_t ClientImpl::create_client_socket() const {
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_);
connection_timeout_sec_, connection_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_);
connection_timeout_sec_, connection_timeout_usec_, interface_, error);
}
inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
auto sock = create_client_socket();
inline bool ClientImpl::create_and_connect_socket(Socket &socket,
Error &error) {
auto sock = create_client_socket(error);
if (sock == INVALID_SOCKET) { return false; }
socket.sock = sock;
return true;
@@ -4899,6 +4926,7 @@ inline void ClientImpl::close_socket(Socket &socket) {
// than the one they intended!
assert(socket_requests_in_flight_ == 0 ||
socket_requests_are_from_thread_ == std::this_thread::get_id());
// It is also a bug if this happens while SSL is still active
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
assert(socket.ssl == nullptr);
@@ -4944,7 +4972,7 @@ inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
return true;
}
inline bool ClientImpl::send(const Request &req, Response &res) {
inline bool ClientImpl::send(const Request &req, Response &res, Error &error) {
std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
{
@@ -4969,7 +4997,7 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
}
if (!is_alive) {
if (!create_and_connect_socket(socket_)) { return false; }
if (!create_and_connect_socket(socket_, error)) { return false; }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
// TODO: refactoring
@@ -4977,12 +5005,12 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
auto &scli = static_cast<SSLClient &>(*this);
if (!proxy_host_.empty() && proxy_port_ != -1) {
bool success = false;
if (!scli.connect_with_proxy(socket_, res, success)) {
if (!scli.connect_with_proxy(socket_, res, success, error)) {
return success;
}
}
if (!scli.initialize_ssl(socket_)) { return false; }
if (!scli.initialize_ssl(socket_, error)) { return false; }
}
#endif
}
@@ -4999,7 +5027,7 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
auto close_connection = !keep_alive_;
auto ret = process_socket(socket_, [&](Stream &strm) {
return handle_request(strm, req, res, close_connection);
return handle_request(strm, req, res, close_connection, error);
});
// Briefly lock mutex in order to mark that a request is no longer ongoing
@@ -5020,16 +5048,24 @@ inline bool ClientImpl::send(const Request &req, Response &res) {
}
if (!ret) {
if (error_ == Error::Success) { error_ = Error::Unknown; }
if (error == Error::Success) { error = Error::Unknown; }
}
return ret;
}
inline Result ClientImpl::send(const Request &req) {
auto res = detail::make_unique<Response>();
auto error = Error::Success;
auto ret = send(req, *res, error);
return Result{ret ? std::move(res) : nullptr, error};
}
inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
Response &res, bool close_connection) {
Response &res, bool close_connection,
Error &error) {
if (req.path.empty()) {
error_ = Error::Connection;
error = Error::Connection;
return false;
}
@@ -5038,15 +5074,15 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
auto req2 = req;
req2.path = "http://" + host_and_port_ + req.path;
ret = process_request(strm, req2, res, close_connection);
ret = process_request(strm, req2, res, close_connection, error);
} else {
ret = process_request(strm, req, res, close_connection);
ret = process_request(strm, req, res, close_connection, error);
}
if (!ret) { return false; }
if (300 < res.status && res.status < 400 && follow_location_) {
ret = redirect(req, res);
ret = redirect(req, res, error);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -5071,7 +5107,7 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
Response new_res;
ret = send(new_req, new_res);
ret = send(new_req, new_res, error);
if (ret) { res = new_res; }
}
}
@@ -5081,9 +5117,10 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
return ret;
}
inline bool ClientImpl::redirect(const Request &req, Response &res) {
inline bool ClientImpl::redirect(const Request &req, Response &res,
Error &error) {
if (req.redirect_count_ == 0) {
error_ = Error::ExceedRedirectCount;
error = Error::ExceedRedirectCount;
return false;
}
@@ -5115,30 +5152,27 @@ inline bool ClientImpl::redirect(const Request &req, Response &res) {
if (next_path.empty()) { next_path = "/"; }
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
return detail::redirect(*this, req, res, next_path, location);
return detail::redirect(*this, req, res, next_path, location, error);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
auto ret = detail::redirect(cli, req, res, next_path, location);
if (!ret) { error_ = cli.get_last_error(); }
return ret;
return detail::redirect(cli, req, res, next_path, location, error);
#else
return false;
#endif
} else {
ClientImpl cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
auto ret = detail::redirect(cli, req, res, next_path, location);
if (!ret) { error_ = cli.get_last_error(); }
return ret;
return detail::redirect(cli, req, res, next_path, location, error);
}
}
}
inline bool ClientImpl::write_content_with_provider(Stream &strm,
const Request &req) {
const Request &req,
Error &error) {
auto is_shutting_down = []() { return false; };
if (req.is_chunked_content_provider_) {
@@ -5154,15 +5188,15 @@ inline bool ClientImpl::write_content_with_provider(Stream &strm,
}
return detail::write_content_chunked(strm, req.content_provider_,
is_shutting_down, *compressor, error_);
is_shutting_down, *compressor, error);
} else {
return detail::write_content(strm, req.content_provider_, 0,
req.content_length_, is_shutting_down, error_);
req.content_length_, is_shutting_down, error);
}
} // namespace httplib
inline bool ClientImpl::write_request(Stream &strm, const Request &req,
bool close_connection) {
bool close_connection, Error &error) {
detail::BufferStream bstrm;
// Request line
@@ -5245,13 +5279,13 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
// Flush buffer
auto &data = bstrm.get_buffer();
if (!detail::write_data(strm, data.data(), data.size())) {
error_ = Error::Write;
error = Error::Write;
return false;
}
// Body
if (req.body.empty()) {
return write_content_with_provider(strm, req);
return write_content_with_provider(strm, req, error);
} else {
return detail::write_data(strm, req.body.data(), req.body.size());
}
@@ -5264,7 +5298,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
const std::string &body, size_t content_length,
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const char *content_type) {
const char *content_type, Error &error) {
Request req;
req.method = method;
@@ -5310,7 +5344,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
while (ok && offset < content_length) {
if (!content_provider(offset, content_length - offset, data_sink)) {
error_ = Error::Canceled;
error = Error::Canceled;
return nullptr;
}
}
@@ -5320,6 +5354,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
req.body.append(data, data_len);
return true;
})) {
error = Error::Compression;
return nullptr;
}
}
@@ -5342,25 +5377,38 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
}
auto res = detail::make_unique<Response>();
return send(req, *res, error) ? std::move(res) : nullptr;
}
return send(req, *res) ? std::move(res) : nullptr;
inline Result ClientImpl::send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const char *content_type) {
auto error = Error::Success;
auto res = send_with_content_provider(
method, path, headers, body, content_length, std::move(content_provider),
std::move(content_provider_without_length), content_type, error);
return Result{std::move(res), error};
}
inline bool ClientImpl::process_request(Stream &strm, const Request &req,
Response &res, bool close_connection) {
Response &res, bool close_connection,
Error &error) {
// Send request
if (!write_request(strm, req, close_connection)) { return false; }
if (!write_request(strm, req, close_connection, error)) { return false; }
// Receive response and headers
if (!read_response_line(strm, res) ||
!detail::read_headers(strm, res.headers)) {
error_ = Error::Read;
error = Error::Read;
return false;
}
if (req.response_handler_) {
if (!req.response_handler_(res)) {
error_ = Error::Canceled;
error = Error::Canceled;
return false;
}
}
@@ -5372,7 +5420,7 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
? static_cast<ContentReceiverWithProgress>(
[&](const char *buf, size_t n, uint64_t off, uint64_t len) {
auto ret = req.content_receiver_(buf, n, off, len);
if (!ret) { error_ = Error::Canceled; }
if (!ret) { error = Error::Canceled; }
return ret;
})
: static_cast<ContentReceiverWithProgress>(
@@ -5388,17 +5436,23 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
auto progress = [&](uint64_t current, uint64_t total) {
if (!req.progress_) { return true; }
auto ret = req.progress_(current, total);
if (!ret) { error_ = Error::Canceled; }
if (!ret) { error = Error::Canceled; }
return ret;
};
int dummy_status;
// std::cout << "A" << std::endl;
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
dummy_status, std::move(progress), std::move(out),
decompress_)) {
if (error_ != Error::Canceled) { error_ = Error::Read; }
// std::cout << "B" << std::endl;
if (error != Error::Canceled) {
// std::cout << "C" << std::endl;
error = Error::Read;
}
return false;
}
// std::cout << "D" << std::endl;
}
if (res.get_header_value("Connection") == "close" ||
@@ -5453,9 +5507,7 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
req.headers.insert(headers.begin(), headers.end());
req.progress_ = std::move(progress);
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? std::move(res) : nullptr, get_last_error()};
return send(req);
}
inline Result ClientImpl::Get(const char *path,
@@ -5521,9 +5573,7 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
};
req.progress_ = std::move(progress);
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? std::move(res) : nullptr, get_last_error()};
return send(req);
}
inline Result ClientImpl::Head(const char *path) {
@@ -5537,9 +5587,7 @@ inline Result ClientImpl::Head(const char *path, const Headers &headers) {
req.headers.insert(headers.begin(), headers.end());
req.path = path;
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? std::move(res) : nullptr, get_last_error()};
return send(req);
}
inline Result ClientImpl::Post(const char *path) {
@@ -5554,9 +5602,8 @@ inline Result ClientImpl::Post(const char *path, const std::string &body,
inline Result ClientImpl::Post(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
nullptr, content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("POST", path, headers, body, 0, nullptr,
nullptr, content_type);
}
inline Result ClientImpl::Post(const char *path, const Params &params) {
@@ -5580,19 +5627,17 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
const char *content_type) {
auto ret = send_with_content_provider(
"POST", path, headers, std::string(), content_length,
std::move(content_provider), nullptr, content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("POST", path, headers, std::string(),
content_length, std::move(content_provider),
nullptr, content_type);
}
inline Result ClientImpl::Post(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
auto ret = send_with_content_provider("POST", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("POST", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
}
inline Result ClientImpl::Post(const char *path, const Headers &headers,
@@ -5616,8 +5661,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
for (size_t i = 0; i < boundary.size(); i++) {
char c = boundary[i];
if (!std::isalnum(c) && c != '-' && c != '_') {
error_ = Error::UnsupportedMultipartBoundaryChars;
return Result{nullptr, error_};
return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};
}
}
@@ -5655,9 +5699,8 @@ inline Result ClientImpl::Put(const char *path, const std::string &body,
inline Result ClientImpl::Put(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
nullptr, content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
nullptr, content_type);
}
inline Result ClientImpl::Put(const char *path, size_t content_length,
@@ -5677,19 +5720,17 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
const char *content_type) {
auto ret = send_with_content_provider(
"PUT", path, headers, std::string(), content_length,
std::move(content_provider), nullptr, content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("PUT", path, headers, std::string(),
content_length, std::move(content_provider),
nullptr, content_type);
}
inline Result ClientImpl::Put(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
auto ret = send_with_content_provider("PUT", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("PUT", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
}
inline Result ClientImpl::Put(const char *path, const Params &params) {
@@ -5710,9 +5751,8 @@ inline Result ClientImpl::Patch(const char *path, const std::string &body,
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
nullptr, nullptr, content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
nullptr, content_type);
}
inline Result ClientImpl::Patch(const char *path, size_t content_length,
@@ -5732,19 +5772,17 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
const char *content_type) {
auto ret = send_with_content_provider(
"PATCH", path, headers, std::string(), content_length,
std::move(content_provider), nullptr, content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("PATCH", path, headers, std::string(),
content_length, std::move(content_provider),
nullptr, content_type);
}
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
auto ret = send_with_content_provider("PATCH", path, headers, std::string(),
0, nullptr, std::move(content_provider),
content_type);
return Result{std::move(ret), get_last_error()};
return send_with_content_provider("PATCH", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
}
inline Result ClientImpl::Delete(const char *path) {
@@ -5772,9 +5810,7 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
if (content_type) { req.headers.emplace("Content-Type", content_type); }
req.body = body;
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? std::move(res) : nullptr, get_last_error()};
return send(req);
}
inline Result ClientImpl::Options(const char *path) {
@@ -5788,9 +5824,7 @@ inline Result ClientImpl::Options(const char *path, const Headers &headers) {
req.headers.insert(headers.begin(), headers.end());
req.path = path;
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
return Result{ret ? std::move(res) : nullptr, get_last_error()};
return send(req);
}
inline size_t ClientImpl::is_socket_open() const {
@@ -5800,10 +5834,6 @@ inline size_t ClientImpl::is_socket_open() const {
inline void ClientImpl::stop() {
std::lock_guard<std::mutex> guard(socket_mutex_);
// There is no guarantee that this doesn't get overwritten later, but set it
// so that there is a good chance that any threads stopping as a result pick
// up this error.
error_ = Error::Canceled;
// If there is anything ongoing right now, the ONLY thread-safe thing we can
// do is to shutdown_socket, so that threads using this socket suddenly
@@ -5812,6 +5842,7 @@ inline void ClientImpl::stop() {
// not thread-safe.
if (socket_requests_in_flight_ > 0) {
shutdown_socket(socket_);
// Aside from that, we set a flag for the socket to be closed when we're
// done.
socket_should_be_closed_when_request_is_done_ = true;
@@ -6307,13 +6338,13 @@ inline long SSLClient::get_openssl_verify_result() const {
inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
inline bool SSLClient::create_and_connect_socket(Socket &socket) {
return is_valid() && ClientImpl::create_and_connect_socket(socket);
inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
return is_valid() && ClientImpl::create_and_connect_socket(socket, error);
}
// Assumes that socket_mutex_ is locked and that there are no requests in flight
inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
bool &success) {
bool &success, Error &error) {
success = true;
Response res2;
if (!detail::process_client_socket(
@@ -6322,7 +6353,7 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
Request req2;
req2.method = "CONNECT";
req2.path = host_and_port_;
return process_request(strm, req2, res2, false);
return process_request(strm, req2, res2, false, error);
})) {
// Thread-safe to close everything because we are assuming there are no
// requests in flight
@@ -6349,7 +6380,7 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
req3, auth, 1, detail::random_string(10),
proxy_digest_auth_username_, proxy_digest_auth_password_,
true));
return process_request(strm, req3, res3, false);
return process_request(strm, req3, res3, false, error);
})) {
// Thread-safe to close everything because we are assuming there are
// no requests in flight
@@ -6396,13 +6427,13 @@ inline bool SSLClient::load_certs() {
return ret;
}
inline bool SSLClient::initialize_ssl(Socket &socket) {
inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
auto ssl = detail::ssl_new(
socket.sock, ctx_, ctx_mutex_,
[&](SSL *ssl) {
if (server_certificate_verification_) {
if (!load_certs()) {
error_ = Error::SSLLoadingCerts;
error = Error::SSLLoadingCerts;
return false;
}
SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
@@ -6411,7 +6442,7 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
if (!detail::ssl_connect_or_accept_nonblocking(
socket.sock, ssl, SSL_connect, connection_timeout_sec_,
connection_timeout_usec_)) {
error_ = Error::SSLConnection;
error = Error::SSLConnection;
return false;
}
@@ -6419,20 +6450,20 @@ inline bool SSLClient::initialize_ssl(Socket &socket) {
verify_result_ = SSL_get_verify_result(ssl);
if (verify_result_ != X509_V_OK) {
error_ = Error::SSLServerVerification;
error = Error::SSLServerVerification;
return false;
}
auto server_cert = SSL_get_peer_certificate(ssl);
if (server_cert == nullptr) {
error_ = Error::SSLServerVerification;
error = Error::SSLServerVerification;
return false;
}
if (!verify_host(server_cert)) {
X509_free(server_cert);
error_ = Error::SSLServerVerification;
error = Error::SSLServerVerification;
return false;
}
X509_free(server_cert);
@@ -6860,10 +6891,12 @@ inline Result Client::Options(const char *path, const Headers &headers) {
return cli_->Options(path, headers);
}
inline bool Client::send(const Request &req, Response &res) {
return cli_->send(req, res);
inline bool Client::send(const Request &req, Response &res, Error &error) {
return cli_->send(req, res, error);
}
inline Result Client::send(const Request &req) { return cli_->send(req); }
inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
inline void Client::stop() { cli_->stop(); }
+35 -37
View File
@@ -1,28 +1,26 @@
#include <memory>
#include <httplib.h>
#include <memory>
class FuzzedStream : public httplib::Stream {
public:
FuzzedStream(const uint8_t* data, size_t size)
public:
FuzzedStream(const uint8_t *data, size_t size)
: data_(data), size_(size), read_pos_(0) {}
ssize_t read(char* ptr, size_t size) override {
if (size + read_pos_ > size_) {
size = size_ - read_pos_;
}
ssize_t read(char *ptr, size_t size) override {
if (size + read_pos_ > size_) { size = size_ - read_pos_; }
memcpy(ptr, data_ + read_pos_, size);
read_pos_ += size;
return size;
return static_cast<ssize_t>(size);
}
ssize_t write(const char* ptr, size_t size) override {
ssize_t write(const char *ptr, size_t size) override {
response_.append(ptr, size);
return static_cast<int>(size);
}
int write(const char* ptr) { return write(ptr, strlen(ptr)); }
ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); }
int write(const std::string& s) { return write(s.data(), s.size()); }
ssize_t write(const std::string &s) { return write(s.data(), s.size()); }
std::string get_remote_addr() const { return ""; }
@@ -37,16 +35,16 @@ class FuzzedStream : public httplib::Stream {
socket_t socket() const override { return 0; }
private:
const uint8_t* data_;
private:
const uint8_t *data_;
size_t size_;
size_t read_pos_;
std::string response_;
};
class FuzzableServer : public httplib::Server {
public:
void ProcessFuzzedRequest(FuzzedStream& stream) {
public:
void ProcessFuzzedRequest(FuzzedStream &stream) {
bool connection_close = false;
process_request(stream, /*last_connection=*/false, connection_close,
nullptr);
@@ -55,36 +53,36 @@ class FuzzableServer : public httplib::Server {
static FuzzableServer g_server;
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) {
g_server.Get(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
[&](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("response content", "text/plain");
});
g_server.Post(R"(.*)",
[&](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("response content", "text/plain");
});
g_server.Post(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
g_server.Put(R"(.*)",
[&](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("response content", "text/plain");
});
g_server.Patch(R"(.*)",
[&](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("response content", "text/plain");
});
g_server.Put(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Patch(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Delete(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Options(R"(.*)",
[&](const httplib::Request& req, httplib::Response& res) {
res.set_content("response content", "text/plain");
});
g_server.Delete(
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("response content", "text/plain");
});
g_server.Options(
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
res.set_content("response content", "text/plain");
});
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedStream stream{data, size};
g_server.ProcessFuzzedRequest(stream);
return 0;
}
}
+4 -3
View File
@@ -42,8 +42,9 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion"
#elif __GNUC__
#pragma gcc diagnostic push
#pragma gcc diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wconversion"
#endif
// The following lines pull in the real gtest *.cc files.
@@ -9127,5 +9128,5 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
#if __clang__
#pragma clang diagnostic pop
#elif __GNUC__
#pragma gcc diagnostic pop
#pragma GCC diagnostic pop
#endif
+7 -6
View File
@@ -63,8 +63,9 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#elif __GNUC__
#pragma gcc diagnostic push
#pragma gcc diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wconversion"
#endif
// Copyright 2005, Google Inc.
@@ -18353,8 +18354,8 @@ AssertionResult CmpHelperEQ(const char* expected_expression,
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#elif __GNUC__
#pragma gcc diagnostic push
#pragma gcc diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif
if (expected == actual) {
@@ -18366,7 +18367,7 @@ AssertionResult CmpHelperEQ(const char* expected_expression,
#elif __clang__
#pragma clang diagnostic pop
#elif __GNUC__
#pragma gcc diagnostic pop
#pragma GCC diagnostic pop
#endif
return EqFailure(expected_expression,
@@ -19564,7 +19565,7 @@ bool StaticAssertTypeEq() {
#if __clang__
#pragma clang diagnostic pop
#elif __GNUC__
#pragma gcc diagnostic pop
#pragma GCC diagnostic pop
#endif
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
+12 -7
View File
@@ -2,10 +2,10 @@
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <future>
#include <thread>
#include <atomic>
#define SERVER_CERT_FILE "./cert.pem"
#define SERVER_CERT2_FILE "./cert2.pem"
@@ -1759,7 +1759,8 @@ TEST_F(ServerTest, LongHeader) {
"@@@@@@@@@@@@@@@@");
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
auto error = Error::Success;
auto ret = cli_.send(req, *res, error);
ASSERT_TRUE(ret);
EXPECT_EQ(200, res->status);
@@ -1819,7 +1820,8 @@ TEST_F(ServerTest, TooLongHeader) {
"@@@@@@@@@@@@@@@@@");
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
auto error = Error::Success;
auto ret = cli_.send(req, *res, error);
ASSERT_TRUE(ret);
EXPECT_EQ(200, res->status);
@@ -1908,7 +1910,8 @@ TEST_F(ServerTest, CaseInsensitiveTransferEncoding) {
req.body = "4\r\ndech\r\nf\r\nunked post body\r\n0\r\n\r\n";
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
auto error = Error::Success;
auto ret = cli_.send(req, *res, error);
ASSERT_TRUE(ret);
EXPECT_EQ(200, res->status);
@@ -2125,7 +2128,8 @@ TEST_F(ServerTest, LargeChunkedPost) {
req.body = chunk + chunk + chunk + chunk + chunk + chunk + "0\r\n\r\n";
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
auto error = Error::Success;
auto ret = cli_.send(req, *res, error);
ASSERT_TRUE(ret);
EXPECT_EQ(200, res->status);
@@ -2551,7 +2555,8 @@ TEST_F(ServerTest, HTTP2Magic) {
req.body = "SM";
auto res = std::make_shared<Response>();
auto ret = cli_.send(req, *res);
auto error = Error::Success;
auto ret = cli_.send(req, *res, error);
ASSERT_TRUE(ret);
EXPECT_EQ(400, res->status);
@@ -2762,7 +2767,7 @@ TEST_F(ServerTest, Brotli) {
// Sends a raw request to a server listening at HOST:PORT.
static bool send_request(time_t read_timeout_sec, const std::string &req,
std::string *resp = nullptr) {
std::atomic<Error> error(Error::Success);
auto error = Error::Success;
auto client_sock =
detail::create_client_socket(HOST, PORT, false, nullptr,