From dc5f9ba1641e29d38d46e88c45db655b75298105 Mon Sep 17 00:00:00 2001 From: yhirose Date: Sat, 8 Aug 2020 20:50:24 -0400 Subject: [PATCH] Better error handling on client (#601) --- README.md | 23 +- example/client.cc | 4 +- example/simplecli.cc | 6 +- httplib.h | 943 ++++++++++++++++++++++--------------------- test/test.cc | 408 +++++++++++-------- 5 files changed, 727 insertions(+), 657 deletions(-) diff --git a/README.md b/README.md index b823da4..aef6cf2 100644 --- a/README.md +++ b/README.md @@ -286,9 +286,13 @@ int main(void) { httplib::Client cli("localhost", 1234); - auto res = cli.Get("/hi"); - if (res && res->status == 200) { - std::cout << res->body << std::endl; + if (auto res = cli.Get("/hi")) { + if (res->status == 200) { + std::cout << res->body << std::endl; + } + } else { + auto err = res.error(); + ... } } ``` @@ -434,13 +438,12 @@ auto res = cli_.Post( httplib::Client client(url, port); // prints: 0 / 000 bytes => 50% complete -std::shared_ptr res = - cli.Get("/", [](uint64_t len, uint64_t total) { - printf("%lld / %lld bytes => %d%% complete\n", - len, total, - (int)(len*100/total)); - return true; // return 'false' if you want to cancel the request. - } +auto res = cli.Get("/", [](uint64_t len, uint64_t total) { + printf("%lld / %lld bytes => %d%% complete\n", + len, total, + (int)(len*100/total)); + return true; // return 'false' if you want to cancel the request. +} ); ``` diff --git a/example/client.cc b/example/client.cc index 926f210..a9b0fc0 100644 --- a/example/client.cc +++ b/example/client.cc @@ -23,12 +23,12 @@ int main(void) { httplib::Client cli("localhost", 8080); #endif - auto res = cli.Get("/hi"); - if (res) { + if (auto res = cli.Get("/hi")) { cout << res->status << endl; cout << res->get_header_value("Content-Type") << endl; cout << res->body << endl; } else { + cout << "error code: " << res.error() << std::endl; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT auto result = cli.get_openssl_verify_result(); if (result) { diff --git a/example/simplecli.cc b/example/simplecli.cc index 52d1b43..b005e40 100644 --- a/example/simplecli.cc +++ b/example/simplecli.cc @@ -17,12 +17,12 @@ int main(void) { auto scheme_host_port = "http://localhost:8080"; #endif - auto res = httplib::Client(scheme_host_port).Get("/hi"); - - if (res) { + if (auto res = httplib::Client(scheme_host_port).Get("/hi")) { cout << res->status << endl; cout << res->get_header_value("Content-Type") << endl; cout << res->body << endl; + } else { + cout << res.error() << endl; } return 0; diff --git a/httplib.h b/httplib.h index 571a230..aefde7a 100644 --- a/httplib.h +++ b/httplib.h @@ -396,6 +396,7 @@ struct Response { void set_header(const char *key, const std::string &val); void set_redirect(const char *url, int status = 302); + void set_redirect(const std::string &url, int status = 302); void set_content(const char *s, size_t n, const char *content_type); void set_content(std::string s, const char *content_type); @@ -674,6 +675,36 @@ private: SocketOptions socket_options_ = default_socket_options; }; +enum Error { + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification +}; + +class Result { +public: + Result(std::shared_ptr res, Error err) : res_(res), err_(err) {} + operator bool() { return res_ != nullptr; } + bool operator==(nullptr_t) const { return res_ == nullptr; } + bool operator!=(nullptr_t) const { return res_ != nullptr; } + const Response &value() { return *res_; } + const Response &operator*() { return *res_; } + const Response *operator->() { return res_.get(); } + Error error() { return err_; } + +private: + std::shared_ptr res_; + Error err_; +}; + class ClientImpl { public: explicit ClientImpl(const std::string &host); @@ -688,99 +719,76 @@ public: virtual bool is_valid() const; - std::shared_ptr Get(const char *path); - std::shared_ptr Get(const char *path, const Headers &headers); - std::shared_ptr Get(const char *path, Progress progress); - std::shared_ptr Get(const char *path, const Headers &headers, - Progress progress); - std::shared_ptr Get(const char *path, - ContentReceiver content_receiver); - std::shared_ptr Get(const char *path, const Headers &headers, - ContentReceiver content_receiver); - std::shared_ptr - Get(const char *path, ContentReceiver content_receiver, Progress progress); - std::shared_ptr Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, - Progress progress); - std::shared_ptr Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver); - std::shared_ptr Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - std::shared_ptr Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress); - std::shared_ptr Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress); + Result Get(const char *path); + Result Get(const char *path, const Headers &headers); + Result Get(const char *path, Progress progress); + Result Get(const char *path, const Headers &headers, Progress progress); + Result Get(const char *path, ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const char *path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); - std::shared_ptr Head(const char *path); - std::shared_ptr Head(const char *path, const Headers &headers); + Result Head(const char *path); + Result Head(const char *path, const Headers &headers); - std::shared_ptr Post(const char *path); - std::shared_ptr Post(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Post(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); - std::shared_ptr Post(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Post(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Post(const char *path, const Params ¶ms); - std::shared_ptr Post(const char *path, const Headers &headers, - const Params ¶ms); - std::shared_ptr Post(const char *path, - const MultipartFormDataItems &items); - std::shared_ptr Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items); + Result Post(const char *path); + Result Post(const char *path, const std::string &body, + const char *content_type); + Result Post(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Post(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Params ¶ms); + Result Post(const char *path, const Headers &headers, const Params ¶ms); + Result Post(const char *path, const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); - std::shared_ptr Put(const char *path); - std::shared_ptr Put(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Put(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); - std::shared_ptr Put(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Put(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Put(const char *path, const Params ¶ms); - std::shared_ptr Put(const char *path, const Headers &headers, - const Params ¶ms); + Result Put(const char *path); + Result Put(const char *path, const std::string &body, + const char *content_type); + Result Put(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Put(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Params ¶ms); + Result Put(const char *path, const Headers &headers, const Params ¶ms); - std::shared_ptr Patch(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Patch(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); - std::shared_ptr Patch(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Patch(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type); + Result Patch(const char *path, const std::string &body, + const char *content_type); + Result Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + Result Patch(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); - std::shared_ptr Delete(const char *path); - std::shared_ptr Delete(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Delete(const char *path, const Headers &headers); - std::shared_ptr Delete(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); + Result Delete(const char *path); + Result Delete(const char *path, const std::string &body, + const char *content_type); + Result Delete(const char *path, const Headers &headers); + Result Delete(const char *path, const Headers &headers, + const std::string &body, const char *content_type); - std::shared_ptr Options(const char *path); - std::shared_ptr Options(const char *path, const Headers &headers); + Result Options(const char *path); + Result Options(const char *path, const Headers &headers); bool send(const Request &req, Response &res); @@ -837,6 +845,11 @@ protected: bool process_request(Stream &strm, const Request &req, Response &res, bool close_connection); + Error get_last_error() const; + + // Error state + mutable Error error_ = Error::Success; + // Socket endoint information const std::string host_; const int port_; @@ -934,7 +947,7 @@ private: bool redirect(const Request &req, Response &res); bool handle_request(Stream &strm, const Request &req, Response &res, bool close_connection); - + void stop_core(); std::shared_ptr send_with_content_provider( const char *method, const char *path, const Headers &headers, const std::string &body, size_t content_length, @@ -961,101 +974,78 @@ public: const std::string &client_cert_path, const std::string &client_key_path); - virtual ~Client(); + ~Client(); - virtual bool is_valid() const; + bool is_valid() const; - std::shared_ptr Get(const char *path); - std::shared_ptr Get(const char *path, const Headers &headers); - std::shared_ptr Get(const char *path, Progress progress); - std::shared_ptr Get(const char *path, const Headers &headers, - Progress progress); - std::shared_ptr Get(const char *path, - ContentReceiver content_receiver); - std::shared_ptr Get(const char *path, const Headers &headers, - ContentReceiver content_receiver); - std::shared_ptr - Get(const char *path, ContentReceiver content_receiver, Progress progress); - std::shared_ptr Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, - Progress progress); - std::shared_ptr Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver); - std::shared_ptr Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver); - std::shared_ptr Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress); - std::shared_ptr Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress); + Result Get(const char *path); + Result Get(const char *path, const Headers &headers); + Result Get(const char *path, Progress progress); + Result Get(const char *path, const Headers &headers, Progress progress); + Result Get(const char *path, ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const char *path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); - std::shared_ptr Head(const char *path); - std::shared_ptr Head(const char *path, const Headers &headers); + Result Head(const char *path); + Result Head(const char *path, const Headers &headers); - std::shared_ptr Post(const char *path); - std::shared_ptr Post(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Post(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); - std::shared_ptr Post(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Post(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Post(const char *path, const Params ¶ms); - std::shared_ptr Post(const char *path, const Headers &headers, - const Params ¶ms); - std::shared_ptr Post(const char *path, - const MultipartFormDataItems &items); - std::shared_ptr Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items); - std::shared_ptr Put(const char *path); - std::shared_ptr Put(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Put(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); - std::shared_ptr Put(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Put(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Put(const char *path, const Params ¶ms); - std::shared_ptr Put(const char *path, const Headers &headers, - const Params ¶ms); - std::shared_ptr Patch(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Patch(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); - std::shared_ptr Patch(const char *path, size_t content_length, - ContentProvider content_provider, - const char *content_type); - std::shared_ptr Patch(const char *path, const Headers &headers, - size_t content_length, - ContentProvider content_provider, - const char *content_type); + Result Post(const char *path); + Result Post(const char *path, const std::string &body, + const char *content_type); + Result Post(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Post(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Params ¶ms); + Result Post(const char *path, const Headers &headers, const Params ¶ms); + Result Post(const char *path, const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const char *path); + Result Put(const char *path, const std::string &body, + const char *content_type); + Result Put(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Put(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Params ¶ms); + Result Put(const char *path, const Headers &headers, const Params ¶ms); + Result Patch(const char *path, const std::string &body, + const char *content_type); + Result Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + Result Patch(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); - std::shared_ptr Delete(const char *path); - std::shared_ptr Delete(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Delete(const char *path, const Headers &headers); - std::shared_ptr Delete(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); + Result Delete(const char *path); + Result Delete(const char *path, const std::string &body, + const char *content_type); + Result Delete(const char *path, const Headers &headers); + Result Delete(const char *path, const Headers &headers, + const std::string &body, const char *content_type); - std::shared_ptr Options(const char *path); - std::shared_ptr Options(const char *path, const Headers &headers); + Result Options(const char *path); + Result Options(const char *path, const Headers &headers); bool send(const Request &req, Response &res); @@ -1941,15 +1931,18 @@ 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) { - return create_socket( + const std::string &intf, Error &error) { + auto sock = create_socket( host, port, 0, tcp_nodelay, socket_options, [&](socket_t sock, struct addrinfo &ai) -> bool { if (!intf.empty()) { #ifndef _WIN32 auto ip = if2ip(intf); if (ip.empty()) { ip = intf; } - if (!bind_ip_address(sock, ip.c_str())) { return false; } + if (!bind_ip_address(sock, ip.c_str())) { + error = Error::BindIPAddress; + return false; + } #endif } @@ -1957,17 +1950,28 @@ inline socket_t create_client_socket(const char *host, int port, auto ret = ::connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + if (ret < 0) { if (is_connection_error() || !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) { close_socket(sock); + error = Error::Connection; return false; } } set_nonblocking(sock, false); + error = Error::Success; return true; }); + + if (sock != INVALID_SOCKET) { + if (error != Error::Success) { error = Error::Success; } + } else { + if (error == Error::Success) { error = Error::Connection; } + } + + return sock; } inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, @@ -3515,6 +3519,10 @@ inline void Response::set_redirect(const char *url, int stat) { } } +inline void Response::set_redirect(const std::string &url, int stat) { + set_redirect(url.c_str(), stat); +} + inline void Response::set_content(const char *s, size_t n, const char *content_type) { body.assign(s, n); @@ -4145,8 +4153,8 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, out = receiver; } - if (!detail::read_content(strm, req, payload_max_length_, res.status, - Progress(), out, true)) { + if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, + out, true)) { return false; } @@ -4496,19 +4504,21 @@ inline ClientImpl::ClientImpl(const std::string &host, int port, host_and_port_(host_ + ":" + std::to_string(port_)), client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} -inline ClientImpl::~ClientImpl() { stop(); } +inline ClientImpl::~ClientImpl() { stop_core(); } inline bool ClientImpl::is_valid() const { return true; } +inline Error ClientImpl::get_last_error() const { return error_; } + inline socket_t ClientImpl::create_client_socket() const { if (!proxy_host_.empty()) { return detail::create_client_socket( proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_, - connection_timeout_sec_, connection_timeout_usec_, interface_); + 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_); + return detail::create_client_socket( + host_.c_str(), port_, tcp_nodelay_, socket_options_, + connection_timeout_sec_, connection_timeout_usec_, interface_, error_); } inline bool ClientImpl::create_and_connect_socket(Socket &socket) { @@ -4583,14 +4593,21 @@ inline bool ClientImpl::send(const Request &req, Response &res) { return handle_request(strm, req, res, close_connection); }); - if (close_connection || !ret) { stop(); } + if (close_connection || !ret) { stop_core(); } + + if (!ret) { + if (error_ == Error::Success) { error_ = Error::Unknown; } + } return ret; } inline bool ClientImpl::handle_request(Stream &strm, const Request &req, Response &res, bool close_connection) { - if (req.path.empty()) { return false; } + if (req.path.empty()) { + error_ = Error::Connection; + return false; + } bool ret; @@ -4641,7 +4658,10 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req, } inline bool ClientImpl::redirect(const Request &req, Response &res) { - if (req.redirect_count == 0) { return false; } + if (req.redirect_count == 0) { + error_ = Error::ExceedRedirectCount; + return false; + } auto location = res.get_header_value("location"); if (location.empty()) { return false; } @@ -4677,14 +4697,18 @@ inline bool ClientImpl::redirect(const Request &req, Response &res) { #ifdef CPPHTTPLIB_OPENSSL_SUPPORT SSLClient cli(next_host.c_str(), next_port); cli.copy_settings(*this); - return detail::redirect(cli, req, res, next_path); + auto ret = detail::redirect(cli, req, res, next_path); + if (!ret) { error_ = cli.get_last_error(); } + return ret; #else return false; #endif } else { ClientImpl cli(next_host.c_str(), next_port); cli.copy_settings(*this); - return detail::redirect(cli, req, res, next_path); + auto ret = detail::redirect(cli, req, res, next_path); + if (!ret) { error_ = cli.get_last_error(); } + return ret; } } } @@ -4767,7 +4791,10 @@ 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())) { return false; } + if (!detail::write_data(strm, data.data(), data.size())) { + error_ = Error::Write; + return false; + } // Body if (req.body.empty()) { @@ -4791,9 +4818,13 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req, while (offset < end_offset) { if (!req.content_provider(offset, end_offset - offset, data_sink)) { + error_ = Error::Canceled; + return false; + } + if (!ok) { + error_ = Error::Write; return false; } - if (!ok) { return false; } } } } else { @@ -4807,6 +4838,7 @@ inline std::shared_ptr 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, const char *content_type) { + Request req; req.method = method; req.headers = default_headers_; @@ -4845,6 +4877,7 @@ inline std::shared_ptr ClientImpl::send_with_content_provider( while (ok && offset < content_length) { if (!content_provider(offset, content_length - offset, data_sink)) { + error_ = Error::Canceled; return nullptr; } } @@ -4883,11 +4916,15 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req, // Receive response and headers if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + error_ = Error::Read; return false; } if (req.response_handler) { - if (!req.response_handler(res)) { return false; } + if (!req.response_handler(res)) { + error_ = Error::Canceled; + return false; + } } // Body @@ -4895,7 +4932,9 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req, auto out = req.content_receiver ? static_cast([&](const char *buf, size_t n) { - return req.content_receiver(buf, n); + auto ret = req.content_receiver(buf, n); + if (!ret) { error_ = Error::Canceled; } + return ret; }) : static_cast([&](const char *buf, size_t n) { if (res.body.size() + n > res.body.max_size()) { return false; } @@ -4903,16 +4942,24 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req, return true; }); + auto progress = [&](uint64_t current, uint64_t total) { + if (!req.progress) { return true; } + auto ret = req.progress(current, total); + if (!ret) { error_ = Error::Canceled; } + return ret; + }; + int dummy_status; if (!detail::read_content(strm, res, (std::numeric_limits::max)(), - dummy_status, req.progress, out, decompress_)) { + dummy_status, progress, out, decompress_)) { + if (error_ != Error::Canceled) { error_ = Error::Read; } return false; } } if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") { - stop(); + stop_core(); } // Log @@ -4931,22 +4978,20 @@ ClientImpl::process_socket(Socket &socket, inline bool ClientImpl::is_ssl() const { return false; } -inline std::shared_ptr ClientImpl::Get(const char *path) { +inline Result ClientImpl::Get(const char *path) { return Get(path, Headers(), Progress()); } -inline std::shared_ptr ClientImpl::Get(const char *path, - Progress progress) { +inline Result ClientImpl::Get(const char *path, Progress progress) { return Get(path, Headers(), std::move(progress)); } -inline std::shared_ptr ClientImpl::Get(const char *path, - const Headers &headers) { +inline Result ClientImpl::Get(const char *path, const Headers &headers) { return Get(path, headers, Progress()); } -inline std::shared_ptr -ClientImpl::Get(const char *path, const Headers &headers, Progress progress) { +inline Result ClientImpl::Get(const char *path, const Headers &headers, + Progress progress) { Request req; req.method = "GET"; req.path = path; @@ -4955,60 +5000,59 @@ ClientImpl::Get(const char *path, const Headers &headers, Progress progress) { req.progress = std::move(progress); auto res = std::make_shared(); - return send(req, *res) ? res : nullptr; + return Result{send(req, *res) ? res : nullptr, get_last_error()}; } -inline std::shared_ptr -ClientImpl::Get(const char *path, ContentReceiver content_receiver) { - return Get(path, Headers(), nullptr, std::move(content_receiver), Progress()); +inline Result ClientImpl::Get(const char *path, + ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); } -inline std::shared_ptr -ClientImpl::Get(const char *path, ContentReceiver content_receiver, - Progress progress) { +inline Result ClientImpl::Get(const char *path, + ContentReceiver content_receiver, + Progress progress) { return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress)); } -inline std::shared_ptr -ClientImpl::Get(const char *path, const Headers &headers, - ContentReceiver content_receiver) { - return Get(path, headers, nullptr, std::move(content_receiver), Progress()); +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), nullptr); } -inline std::shared_ptr -ClientImpl::Get(const char *path, const Headers &headers, - ContentReceiver content_receiver, Progress progress) { +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress)); } -inline std::shared_ptr -ClientImpl::Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver) { +inline Result ClientImpl::Get(const char *path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { return Get(path, Headers(), std::move(response_handler), content_receiver, - Progress()); + nullptr); } -inline std::shared_ptr -ClientImpl::Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver) { +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { return Get(path, headers, std::move(response_handler), content_receiver, - Progress()); + nullptr); } -inline std::shared_ptr -ClientImpl::Get(const char *path, ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { +inline Result ClientImpl::Get(const char *path, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { return Get(path, Headers(), std::move(response_handler), content_receiver, progress); } -inline std::shared_ptr -ClientImpl::Get(const char *path, const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, Progress progress) { +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { Request req; req.method = "GET"; req.path = path; @@ -5019,15 +5063,14 @@ ClientImpl::Get(const char *path, const Headers &headers, req.progress = std::move(progress); auto res = std::make_shared(); - return send(req, *res) ? res : nullptr; + return Result{send(req, *res) ? res : nullptr, get_last_error()}; } -inline std::shared_ptr ClientImpl::Head(const char *path) { +inline Result ClientImpl::Head(const char *path) { return Head(path, Headers()); } -inline std::shared_ptr ClientImpl::Head(const char *path, - const Headers &headers) { +inline Result ClientImpl::Head(const char *path, const Headers &headers) { Request req; req.method = "HEAD"; req.headers = default_headers_; @@ -5035,63 +5078,59 @@ inline std::shared_ptr ClientImpl::Head(const char *path, req.path = path; auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + return Result{send(req, *res) ? res : nullptr, get_last_error()}; } -inline std::shared_ptr ClientImpl::Post(const char *path) { +inline Result ClientImpl::Post(const char *path) { return Post(path, std::string(), nullptr); } -inline std::shared_ptr ClientImpl::Post(const char *path, - const std::string &body, - const char *content_type) { +inline Result ClientImpl::Post(const char *path, const std::string &body, + const char *content_type) { return Post(path, Headers(), body, content_type); } -inline std::shared_ptr ClientImpl::Post(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { - return send_with_content_provider("POST", path, headers, body, 0, nullptr, - content_type); +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return Result{send_with_content_provider("POST", path, headers, body, 0, + nullptr, content_type), + get_last_error()}; } -inline std::shared_ptr ClientImpl::Post(const char *path, - const Params ¶ms) { +inline Result ClientImpl::Post(const char *path, const Params ¶ms) { return Post(path, Headers(), params); } -inline std::shared_ptr -ClientImpl::Post(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type) { +inline Result ClientImpl::Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { return Post(path, Headers(), content_length, content_provider, content_type); } -inline std::shared_ptr -ClientImpl::Post(const char *path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const char *content_type) { - return send_with_content_provider("POST", path, headers, std::string(), - content_length, content_provider, - content_type); +inline Result ClientImpl::Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Result{send_with_content_provider("POST", path, headers, std::string(), + content_length, content_provider, + content_type), + get_last_error()}; } -inline std::shared_ptr ClientImpl::Post(const char *path, - const Headers &headers, - const Params ¶ms) { +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const Params ¶ms) { auto query = detail::params_to_query_str(params); return Post(path, headers, query, "application/x-www-form-urlencoded"); } -inline std::shared_ptr -ClientImpl::Post(const char *path, const MultipartFormDataItems &items) { +inline Result ClientImpl::Post(const char *path, + const MultipartFormDataItems &items) { return Post(path, Headers(), items); } -inline std::shared_ptr -ClientImpl::Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items) { +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { auto boundary = detail::make_multipart_data_boundary(); std::string body; @@ -5116,98 +5155,94 @@ ClientImpl::Post(const char *path, const Headers &headers, return Post(path, headers, body, content_type.c_str()); } -inline std::shared_ptr ClientImpl::Put(const char *path) { +inline Result ClientImpl::Put(const char *path) { return Put(path, std::string(), nullptr); } -inline std::shared_ptr ClientImpl::Put(const char *path, - const std::string &body, - const char *content_type) { +inline Result ClientImpl::Put(const char *path, const std::string &body, + const char *content_type) { return Put(path, Headers(), body, content_type); } -inline std::shared_ptr ClientImpl::Put(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { - return send_with_content_provider("PUT", path, headers, body, 0, nullptr, - content_type); +inline Result ClientImpl::Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return Result{send_with_content_provider("PUT", path, headers, body, 0, + nullptr, content_type), + get_last_error()}; } -inline std::shared_ptr -ClientImpl::Put(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type) { +inline Result ClientImpl::Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { return Put(path, Headers(), content_length, content_provider, content_type); } -inline std::shared_ptr -ClientImpl::Put(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type) { - return send_with_content_provider("PUT", path, headers, std::string(), - content_length, content_provider, - content_type); +inline Result ClientImpl::Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Result{send_with_content_provider("PUT", path, headers, std::string(), + content_length, content_provider, + content_type), + get_last_error()}; } -inline std::shared_ptr ClientImpl::Put(const char *path, - const Params ¶ms) { +inline Result ClientImpl::Put(const char *path, const Params ¶ms) { return Put(path, Headers(), params); } -inline std::shared_ptr ClientImpl::Put(const char *path, - const Headers &headers, - const Params ¶ms) { +inline Result ClientImpl::Put(const char *path, const Headers &headers, + const Params ¶ms) { auto query = detail::params_to_query_str(params); return Put(path, headers, query, "application/x-www-form-urlencoded"); } -inline std::shared_ptr ClientImpl::Patch(const char *path, - const std::string &body, - const char *content_type) { +inline Result ClientImpl::Patch(const char *path, const std::string &body, + const char *content_type) { return Patch(path, Headers(), body, content_type); } -inline std::shared_ptr ClientImpl::Patch(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { - return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, - content_type); +inline Result ClientImpl::Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return Result{send_with_content_provider("PATCH", path, headers, body, 0, + nullptr, content_type), + get_last_error()}; } -inline std::shared_ptr -ClientImpl::Patch(const char *path, size_t content_length, - ContentProvider content_provider, const char *content_type) { +inline Result ClientImpl::Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { return Patch(path, Headers(), content_length, content_provider, content_type); } -inline std::shared_ptr -ClientImpl::Patch(const char *path, const Headers &headers, - size_t content_length, ContentProvider content_provider, - const char *content_type) { - return send_with_content_provider("PATCH", path, headers, std::string(), - content_length, content_provider, - content_type); +inline Result ClientImpl::Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Result{send_with_content_provider("PATCH", path, headers, + std::string(), content_length, + content_provider, content_type), + get_last_error()}; } -inline std::shared_ptr ClientImpl::Delete(const char *path) { +inline Result ClientImpl::Delete(const char *path) { return Delete(path, Headers(), std::string(), nullptr); } -inline std::shared_ptr ClientImpl::Delete(const char *path, - const std::string &body, - const char *content_type) { +inline Result ClientImpl::Delete(const char *path, const std::string &body, + const char *content_type) { return Delete(path, Headers(), body, content_type); } -inline std::shared_ptr ClientImpl::Delete(const char *path, - const Headers &headers) { +inline Result ClientImpl::Delete(const char *path, const Headers &headers) { return Delete(path, headers, std::string(), nullptr); } -inline std::shared_ptr ClientImpl::Delete(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { +inline Result ClientImpl::Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { Request req; req.method = "DELETE"; req.headers = default_headers_; @@ -5218,16 +5253,14 @@ inline std::shared_ptr ClientImpl::Delete(const char *path, req.body = body; auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + return Result{send(req, *res) ? res : nullptr, get_last_error()}; } -inline std::shared_ptr ClientImpl::Options(const char *path) { +inline Result ClientImpl::Options(const char *path) { return Options(path, Headers()); } -inline std::shared_ptr ClientImpl::Options(const char *path, - const Headers &headers) { +inline Result ClientImpl::Options(const char *path, const Headers &headers) { Request req; req.method = "OPTIONS"; req.headers = default_headers_; @@ -5235,8 +5268,7 @@ inline std::shared_ptr ClientImpl::Options(const char *path, req.path = path; auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + return Result{send(req, *res) ? res : nullptr, get_last_error()}; } inline size_t ClientImpl::is_socket_open() const { @@ -5245,6 +5277,11 @@ inline size_t ClientImpl::is_socket_open() const { } inline void ClientImpl::stop() { + stop_core(); + error_ = Error::Canceled; +} + +inline void ClientImpl::stop_core() { std::lock_guard guard(socket_mutex_); if (socket_.is_open()) { detail::shutdown_socket(socket_.sock); @@ -5768,23 +5805,36 @@ inline bool SSLClient::initialize_ssl(Socket &socket) { socket.sock, ctx_, ctx_mutex_, [&](SSL *ssl) { if (server_certificate_verification_) { - if (!load_certs()) { return false; } + if (!load_certs()) { + error_ = Error::SSLLoadingCerts; + return false; + } SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr); } - if (SSL_connect(ssl) != 1) { return false; } + if (SSL_connect(ssl) != 1) { + error_ = Error::SSLConnection; + return false; + } if (server_certificate_verification_) { verify_result_ = SSL_get_verify_result(ssl); - if (verify_result_ != X509_V_OK) { return false; } + if (verify_result_ != X509_V_OK) { + error_ = Error::SSLServerVerification; + return false; + } auto server_cert = SSL_get_peer_certificate(ssl); - if (server_cert == nullptr) { return false; } + if (server_cert == nullptr) { + error_ = Error::SSLServerVerification; + return false; + } if (!verify_host(server_cert)) { X509_free(server_cert); + error_ = Error::SSLServerVerification; return false; } X509_free(server_cert); @@ -5968,10 +6018,12 @@ inline Client::Client(const char *scheme_host_port, auto scheme = m[1].str(); #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - if (!scheme.empty() && (scheme != "http" && scheme != "https")) { return; } + if (!scheme.empty() && (scheme != "http" && scheme != "https")) { #else - if (!scheme.empty() && scheme != "http") { return; } + if (!scheme.empty() && scheme != "http") { #endif + return; + } auto is_ssl = scheme == "https"; @@ -6011,198 +6063,159 @@ inline bool Client::is_valid() const { return cli_ != nullptr && cli_->is_valid(); } -inline std::shared_ptr Client::Get(const char *path) { - return cli_->Get(path); -} -inline std::shared_ptr Client::Get(const char *path, - const Headers &headers) { +inline Result Client::Get(const char *path) { return cli_->Get(path); } +inline Result Client::Get(const char *path, const Headers &headers) { return cli_->Get(path, headers); } -inline std::shared_ptr Client::Get(const char *path, - Progress progress) { +inline Result Client::Get(const char *path, Progress progress) { return cli_->Get(path, progress); } -inline std::shared_ptr -Client::Get(const char *path, const Headers &headers, Progress progress) { +inline Result Client::Get(const char *path, const Headers &headers, + Progress progress) { return cli_->Get(path, headers, progress); } -inline std::shared_ptr Client::Get(const char *path, - ContentReceiver content_receiver) { - return cli_->Get(path, content_receiver); +inline Result Client::Get(const char *path, ContentReceiver content_receiver) { + return cli_->Get(path, std::move(content_receiver)); } -inline std::shared_ptr Client::Get(const char *path, - const Headers &headers, - ContentReceiver content_receiver) { - return cli_->Get(path, headers, content_receiver); +inline Result Client::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(content_receiver)); } -inline std::shared_ptr Client::Get(const char *path, - ContentReceiver content_receiver, - Progress progress) { - return cli_->Get(path, content_receiver, progress); +inline Result Client::Get(const char *path, ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, std::move(content_receiver), std::move(progress)); } -inline std::shared_ptr Client::Get(const char *path, - const Headers &headers, - ContentReceiver content_receiver, - Progress progress) { - return cli_->Get(path, headers, content_receiver, progress); +inline Result Client::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(content_receiver), + std::move(progress)); } -inline std::shared_ptr Client::Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return cli_->Get(path, Headers(), response_handler, content_receiver); +inline Result Client::Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver)); } -inline std::shared_ptr Client::Get(const char *path, - const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver) { - return cli_->Get(path, headers, response_handler, content_receiver); +inline Result Client::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver)); } -inline std::shared_ptr Client::Get(const char *path, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { - return cli_->Get(path, Headers(), response_handler, content_receiver, - progress); +inline Result Client::Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver), std::move(progress)); } -inline std::shared_ptr Client::Get(const char *path, - const Headers &headers, - ResponseHandler response_handler, - ContentReceiver content_receiver, - Progress progress) { +inline Result Client::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { return cli_->Get(path, headers, response_handler, content_receiver, progress); } -inline std::shared_ptr Client::Head(const char *path) { - return cli_->Head(path); -} -inline std::shared_ptr Client::Head(const char *path, - const Headers &headers) { +inline Result Client::Head(const char *path) { return cli_->Head(path); } +inline Result Client::Head(const char *path, const Headers &headers) { return cli_->Head(path, headers); } -inline std::shared_ptr Client::Post(const char *path) { - return cli_->Post(path); -} -inline std::shared_ptr Client::Post(const char *path, - const std::string &body, - const char *content_type) { +inline Result Client::Post(const char *path) { return cli_->Post(path); } +inline Result Client::Post(const char *path, const std::string &body, + const char *content_type) { return cli_->Post(path, body, content_type); } -inline std::shared_ptr Client::Post(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { +inline Result Client::Post(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { return cli_->Post(path, headers, body, content_type); } -inline std::shared_ptr Client::Post(const char *path, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { +inline Result Client::Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { return cli_->Post(path, content_length, content_provider, content_type); } -inline std::shared_ptr -Client::Post(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type) { +inline Result Client::Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { return cli_->Post(path, headers, content_length, content_provider, content_type); } -inline std::shared_ptr Client::Post(const char *path, - const Params ¶ms) { +inline Result Client::Post(const char *path, const Params ¶ms) { return cli_->Post(path, params); } -inline std::shared_ptr -Client::Post(const char *path, const Headers &headers, const Params ¶ms) { +inline Result Client::Post(const char *path, const Headers &headers, + const Params ¶ms) { return cli_->Post(path, headers, params); } -inline std::shared_ptr -Client::Post(const char *path, const MultipartFormDataItems &items) { +inline Result Client::Post(const char *path, + const MultipartFormDataItems &items) { return cli_->Post(path, items); } -inline std::shared_ptr -Client::Post(const char *path, const Headers &headers, - const MultipartFormDataItems &items) { +inline Result Client::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { return cli_->Post(path, headers, items); } -inline std::shared_ptr Client::Put(const char *path) { - return cli_->Put(path); -} -inline std::shared_ptr Client::Put(const char *path, - const std::string &body, - const char *content_type) { +inline Result Client::Put(const char *path) { return cli_->Put(path); } +inline Result Client::Put(const char *path, const std::string &body, + const char *content_type) { return cli_->Put(path, body, content_type); } -inline std::shared_ptr Client::Put(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { +inline Result Client::Put(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { return cli_->Put(path, headers, body, content_type); } -inline std::shared_ptr Client::Put(const char *path, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { +inline Result Client::Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { return cli_->Put(path, content_length, content_provider, content_type); } -inline std::shared_ptr -Client::Put(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type) { +inline Result Client::Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { return cli_->Put(path, headers, content_length, content_provider, content_type); } -inline std::shared_ptr Client::Put(const char *path, - const Params ¶ms) { +inline Result Client::Put(const char *path, const Params ¶ms) { return cli_->Put(path, params); } -inline std::shared_ptr -Client::Put(const char *path, const Headers &headers, const Params ¶ms) { +inline Result Client::Put(const char *path, const Headers &headers, + const Params ¶ms) { return cli_->Put(path, headers, params); } -inline std::shared_ptr Client::Patch(const char *path, - const std::string &body, - const char *content_type) { +inline Result Client::Patch(const char *path, const std::string &body, + const char *content_type) { return cli_->Patch(path, body, content_type); } -inline std::shared_ptr Client::Patch(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { +inline Result Client::Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { return cli_->Patch(path, headers, body, content_type); } -inline std::shared_ptr Client::Patch(const char *path, - size_t content_length, - ContentProvider content_provider, - const char *content_type) { +inline Result Client::Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { return cli_->Patch(path, content_length, content_provider, content_type); } -inline std::shared_ptr -Client::Patch(const char *path, const Headers &headers, size_t content_length, - ContentProvider content_provider, const char *content_type) { +inline Result Client::Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { return cli_->Patch(path, headers, content_length, content_provider, content_type); } -inline std::shared_ptr Client::Delete(const char *path) { - return cli_->Delete(path); -} -inline std::shared_ptr Client::Delete(const char *path, - const std::string &body, - const char *content_type) { +inline Result Client::Delete(const char *path) { return cli_->Delete(path); } +inline Result Client::Delete(const char *path, const std::string &body, + const char *content_type) { return cli_->Delete(path, body, content_type); } -inline std::shared_ptr Client::Delete(const char *path, - const Headers &headers) { +inline Result Client::Delete(const char *path, const Headers &headers) { return cli_->Delete(path, headers); } -inline std::shared_ptr Client::Delete(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { +inline Result Client::Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { return cli_->Delete(path, headers, body, content_type); } -inline std::shared_ptr Client::Options(const char *path) { - return cli_->Options(path); -} -inline std::shared_ptr Client::Options(const char *path, - const Headers &headers) { +inline Result Client::Options(const char *path) { return cli_->Options(path); } +inline Result Client::Options(const char *path, const Headers &headers) { return cli_->Options(path, headers); } diff --git a/test/test.cc b/test/test.cc index 5fe6bd9..b603acd 100644 --- a/test/test.cc +++ b/test/test.cc @@ -303,7 +303,7 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) { auto res = cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); std::string out; detail::read_file("./image.jpg", out); @@ -331,7 +331,7 @@ TEST(ChunkedEncodingTest, WithContentReceiver) { body.append(data, data_length); return true; }); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); std::string out; detail::read_file("./image.jpg", out); @@ -363,7 +363,7 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) { body.append(data, data_length); return true; }); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); std::string out; detail::read_file("./image.jpg", out); @@ -379,14 +379,14 @@ TEST(DefaultHeadersTest, FromHTTPBin) { { auto res = cli.Get("/range/32"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("bcdefghijk", res->body); EXPECT_EQ(206, res->status); } { auto res = cli.Get("/range/32"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("bcdefghijk", res->body); EXPECT_EQ(206, res->status); } @@ -406,7 +406,7 @@ TEST(RangeTest, FromHTTPBin) { { auto res = cli.Get("/range/32"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); EXPECT_EQ(200, res->status); } @@ -414,7 +414,7 @@ TEST(RangeTest, FromHTTPBin) { { Headers headers = {make_range_header({{1, -1}})}; auto res = cli.Get("/range/32", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("bcdefghijklmnopqrstuvwxyzabcdef", res->body); EXPECT_EQ(206, res->status); } @@ -422,7 +422,7 @@ TEST(RangeTest, FromHTTPBin) { { Headers headers = {make_range_header({{1, 10}})}; auto res = cli.Get("/range/32", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("bcdefghijk", res->body); EXPECT_EQ(206, res->status); } @@ -430,7 +430,7 @@ TEST(RangeTest, FromHTTPBin) { { Headers headers = {make_range_header({{0, 31}})}; auto res = cli.Get("/range/32", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); EXPECT_EQ(200, res->status); } @@ -438,7 +438,7 @@ TEST(RangeTest, FromHTTPBin) { { Headers headers = {make_range_header({{0, -1}})}; auto res = cli.Get("/range/32", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); EXPECT_EQ(200, res->status); } @@ -446,7 +446,7 @@ TEST(RangeTest, FromHTTPBin) { { Headers headers = {make_range_header({{0, 32}})}; auto res = cli.Get("/range/32", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(416, res->status); } } @@ -464,7 +464,8 @@ TEST(ConnectionErrorTest, InvalidHost) { cli.set_connection_timeout(2); auto res = cli.Get("/"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); } TEST(ConnectionErrorTest, InvalidHost2) { @@ -478,7 +479,8 @@ TEST(ConnectionErrorTest, InvalidHost2) { cli.set_connection_timeout(2); auto res = cli.Get("/"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); } TEST(ConnectionErrorTest, InvalidPort) { @@ -494,7 +496,8 @@ TEST(ConnectionErrorTest, InvalidPort) { cli.set_connection_timeout(2); auto res = cli.Get("/"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); } TEST(ConnectionErrorTest, Timeout) { @@ -510,7 +513,8 @@ TEST(ConnectionErrorTest, Timeout) { cli.set_connection_timeout(2); auto res = cli.Get("/"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_TRUE(res.error() == Error::Connection); } TEST(CancelTest, NoCancel) { @@ -526,7 +530,7 @@ TEST(CancelTest, NoCancel) { cli.set_connection_timeout(5); auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; }); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("abcdefghijklmnopqrstuvwxyzabcdef", res->body); EXPECT_EQ(200, res->status); } @@ -544,7 +548,8 @@ TEST(CancelTest, WithCancelSmallPayload) { auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; }); cli.set_connection_timeout(5); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); } TEST(CancelTest, WithCancelLargePayload) { @@ -562,7 +567,8 @@ TEST(CancelTest, WithCancelLargePayload) { uint32_t count = 0; auto res = cli.Get("/range/65536", [&count](uint64_t, uint64_t) { return (count++ == 0); }); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); } TEST(BaseAuthTest, FromHTTPWatch) { @@ -578,14 +584,14 @@ TEST(BaseAuthTest, FromHTTPWatch) { { auto res = cli.Get("/basic-auth/hello/world"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(401, res->status); } { auto res = cli.Get("/basic-auth/hello/world", {make_basic_authentication_header("hello", "world")}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); EXPECT_EQ(200, res->status); @@ -594,7 +600,7 @@ TEST(BaseAuthTest, FromHTTPWatch) { { cli.set_basic_auth("hello", "world"); auto res = cli.Get("/basic-auth/hello/world"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); EXPECT_EQ(200, res->status); @@ -603,14 +609,14 @@ TEST(BaseAuthTest, FromHTTPWatch) { { cli.set_basic_auth("hello", "bad"); auto res = cli.Get("/basic-auth/hello/world"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(401, res->status); } { cli.set_basic_auth("bad", "world"); auto res = cli.Get("/basic-auth/hello/world"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(401, res->status); } } @@ -623,7 +629,7 @@ TEST(DigestAuthTest, FromHTTPWatch) { { auto res = cli.Get("/digest-auth/auth/hello/world"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(401, res->status); } @@ -638,7 +644,7 @@ TEST(DigestAuthTest, FromHTTPWatch) { cli.set_digest_auth("hello", "world"); for (auto path : paths) { auto res = cli.Get(path.c_str()); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n", res->body); EXPECT_EQ(200, res->status); @@ -647,7 +653,7 @@ TEST(DigestAuthTest, FromHTTPWatch) { cli.set_digest_auth("hello", "bad"); for (auto path : paths) { auto res = cli.Get(path.c_str()); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(401, res->status); } @@ -656,7 +662,7 @@ TEST(DigestAuthTest, FromHTTPWatch) { // cli.set_digest_auth("bad", "world"); // for (auto path : paths) { // auto res = cli.Get(path.c_str()); - // ASSERT_TRUE(res != nullptr); + // ASSERT_TRUE(res); // EXPECT_EQ(400, res->status); // } } @@ -675,7 +681,7 @@ TEST(AbsoluteRedirectTest, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/absolute-redirect/3"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -690,7 +696,7 @@ TEST(RedirectTest, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/redirect/3"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -705,7 +711,7 @@ TEST(RelativeRedirectTest, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/relative-redirect/3"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -720,7 +726,8 @@ TEST(TooManyRedirectTest, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/redirect/21"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::ExceedRedirectCount, res.error()); } #endif @@ -729,12 +736,12 @@ TEST(YahooRedirectTest, Redirect) { Client cli("yahoo.com"); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(301, res->status); cli.set_follow_location(true); res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -744,7 +751,7 @@ TEST(HttpsToHttpRedirectTest, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } #endif @@ -776,7 +783,7 @@ TEST(RedirectToDifferentPort, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/1"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("Hello World!", res->body); @@ -793,7 +800,7 @@ TEST(UrlWithSpace, Redirect) { cli.set_follow_location(true); auto res = cli.Get("/files/2595/310/Neat 1.4-17.jar"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(18527, res->get_header_value("Content-Length")); } @@ -815,7 +822,7 @@ TEST(Server, BindDualStack) { Client cli("127.0.0.1", PORT); auto res = cli.Get("/1"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("Hello World!", res->body); } @@ -823,7 +830,7 @@ TEST(Server, BindDualStack) { Client cli("::1", PORT); auto res = cli.Get("/1"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("Hello World!", res->body); } @@ -1244,6 +1251,12 @@ protected: [&](const Request &req, Response & /*res*/) { EXPECT_EQ("close", req.get_header_value("Connection")); }) + .Get(R"(/redirect/(\d+))", + [&](const Request &req, Response &res) { + auto num = std::stoi(req.matches[1]) + 1; + std::string url = "/redirect/" + std::to_string(num); + res.set_redirect(url); + }) #if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT) .Get("/compress", [&](const Request & /*req*/, Response &res) { @@ -1310,7 +1323,7 @@ protected: TEST_F(ServerTest, GetMethod200) { auto res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("HTTP/1.1", res->version); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); @@ -1320,7 +1333,7 @@ TEST_F(ServerTest, GetMethod200) { TEST_F(ServerTest, GetMethod200withPercentEncoding) { auto res = cli_.Get("/%68%69"); // auto res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("HTTP/1.1", res->version); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); @@ -1330,7 +1343,7 @@ TEST_F(ServerTest, GetMethod200withPercentEncoding) { TEST_F(ServerTest, GetMethod302) { auto res = cli_.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(302, res->status); EXPECT_EQ("/hi", res->get_header_value("Location")); } @@ -1338,20 +1351,20 @@ TEST_F(ServerTest, GetMethod302) { TEST_F(ServerTest, GetMethod302Redirect) { cli_.set_follow_location(true); auto res = cli_.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("Hello World!", res->body); } TEST_F(ServerTest, GetMethod404) { auto res = cli_.Get("/invalid"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, HeadMethod200) { auto res = cli_.Head("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_TRUE(res->body.empty()); @@ -1359,7 +1372,7 @@ TEST_F(ServerTest, HeadMethod200) { TEST_F(ServerTest, HeadMethod200Static) { auto res = cli_.Head("/mount/dir/index.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/html", res->get_header_value("Content-Type")); EXPECT_EQ(104, std::stoi(res->get_header_value("Content-Length"))); @@ -1368,14 +1381,14 @@ TEST_F(ServerTest, HeadMethod200Static) { TEST_F(ServerTest, HeadMethod404) { auto res = cli_.Head("/invalid"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); EXPECT_TRUE(res->body.empty()); } TEST_F(ServerTest, GetMethodPersonJohn) { auto res = cli_.Get("/person/john"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("programmer", res->body); @@ -1383,16 +1396,16 @@ TEST_F(ServerTest, GetMethodPersonJohn) { TEST_F(ServerTest, PostMethod1) { auto res = cli_.Get("/person/john1"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(404, res->status); res = cli_.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); res = cli_.Get("/person/john1"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); ASSERT_EQ("coder", res->body); @@ -1400,7 +1413,7 @@ TEST_F(ServerTest, PostMethod1) { TEST_F(ServerTest, PostMethod2) { auto res = cli_.Get("/person/john2"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(404, res->status); Params params; @@ -1408,11 +1421,11 @@ TEST_F(ServerTest, PostMethod2) { params.emplace("note", "coder"); res = cli_.Post("/person", params); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); res = cli_.Get("/person/john2"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); ASSERT_EQ("coder", res->body); @@ -1420,7 +1433,7 @@ TEST_F(ServerTest, PostMethod2) { TEST_F(ServerTest, PutMethod3) { auto res = cli_.Get("/person/john3"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(404, res->status); Params params; @@ -1428,11 +1441,11 @@ TEST_F(ServerTest, PutMethod3) { params.emplace("note", "coder"); res = cli_.Put("/person", params); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); res = cli_.Get("/person/john3"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); ASSERT_EQ("coder", res->body); @@ -1444,35 +1457,35 @@ TEST_F(ServerTest, PostWwwFormUrlEncodedJson) { auto res = cli_.Post("/x-www-form-urlencoded-json", params); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ(JSON_DATA, res->body); } TEST_F(ServerTest, PostEmptyContent) { auto res = cli_.Post("/empty", "", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("empty", res->body); } TEST_F(ServerTest, PostEmptyContentWithNoContentType) { auto res = cli_.Post("/empty-no-content-type"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("empty-no-content-type", res->body); } TEST_F(ServerTest, PutEmptyContentWithNoContentType) { auto res = cli_.Put("/empty-no-content-type"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("empty-no-content-type", res->body); } TEST_F(ServerTest, GetMethodDir) { auto res = cli_.Get("/dir/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/html", res->get_header_value("Content-Type")); @@ -1490,7 +1503,7 @@ TEST_F(ServerTest, GetMethodDir) { TEST_F(ServerTest, GetMethodDirTest) { auto res = cli_.Get("/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/html", res->get_header_value("Content-Type")); EXPECT_EQ("test.html", res->body); @@ -1498,7 +1511,7 @@ TEST_F(ServerTest, GetMethodDirTest) { TEST_F(ServerTest, GetMethodDirTestWithDoubleDots) { auto res = cli_.Get("/dir/../dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/html", res->get_header_value("Content-Type")); EXPECT_EQ("test.html", res->body); @@ -1506,25 +1519,25 @@ TEST_F(ServerTest, GetMethodDirTestWithDoubleDots) { TEST_F(ServerTest, GetMethodInvalidPath) { auto res = cli_.Get("/dir/../test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, GetMethodOutOfBaseDir) { auto res = cli_.Get("/../www/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, GetMethodOutOfBaseDir2) { auto res = cli_.Get("/dir/../../www/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, GetMethodDirMountTest) { auto res = cli_.Get("/mount/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/html", res->get_header_value("Content-Type")); EXPECT_EQ("test.html", res->body); @@ -1532,7 +1545,7 @@ TEST_F(ServerTest, GetMethodDirMountTest) { TEST_F(ServerTest, GetMethodDirMountTestWithDoubleDots) { auto res = cli_.Get("/mount/dir/../dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/html", res->get_header_value("Content-Type")); EXPECT_EQ("test.html", res->body); @@ -1540,25 +1553,25 @@ TEST_F(ServerTest, GetMethodDirMountTestWithDoubleDots) { TEST_F(ServerTest, GetMethodInvalidMountPath) { auto res = cli_.Get("/mount/dir/../test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, GetMethodOutOfBaseDirMount) { auto res = cli_.Get("/mount/../www2/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) { auto res = cli_.Get("/mount/dir/../../www2/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, PostMethod303) { auto res = cli_.Post("/1", "body", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(303, res->status); EXPECT_EQ("/2", res->get_header_value("Location")); } @@ -1566,14 +1579,14 @@ TEST_F(ServerTest, PostMethod303) { TEST_F(ServerTest, PostMethod303Redirect) { cli_.set_follow_location(true); auto res = cli_.Post("/1", "body", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("redirected.", res->body); } TEST_F(ServerTest, UserDefinedMIMETypeMapping) { auto res = cli_.Get("/dir/test.abcde"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/abcde", res->get_header_value("Content-Type")); EXPECT_EQ("abcde", res->body); @@ -1585,7 +1598,8 @@ TEST_F(ServerTest, InvalidBaseDirMount) { TEST_F(ServerTest, EmptyRequest) { auto res = cli_.Get(""); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Connection, res.error()); } TEST_F(ServerTest, LongRequest) { @@ -1597,7 +1611,7 @@ TEST_F(ServerTest, LongRequest) { auto res = cli_.Get(request.c_str()); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } @@ -1610,7 +1624,7 @@ TEST_F(ServerTest, TooLongRequest) { auto res = cli_.Get(request.c_str()); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(414, res->status); } @@ -1670,7 +1684,7 @@ TEST_F(ServerTest, LongHeader) { TEST_F(ServerTest, LongQueryValue) { auto res = cli_.Get(LONG_QUERY_URL.c_str()); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(414, res->status); } @@ -1729,37 +1743,37 @@ TEST_F(ServerTest, TooLongHeader) { TEST_F(ServerTest, PercentEncoding) { auto res = cli_.Get("/e%6edwith%"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } TEST_F(ServerTest, PercentEncodingUnicode) { auto res = cli_.Get("/e%u006edwith%"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } TEST_F(ServerTest, InvalidPercentEncoding) { auto res = cli_.Get("/%endwith%"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, InvalidPercentEncodingUnicode) { auto res = cli_.Get("/%uendwith%"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, EndWithPercentCharacterInQuery) { auto res = cli_.Get("/hello?aaa=bbb%"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); } TEST_F(ServerTest, PlusSignEncoding) { auto res = cli_.Get("/a+%2Bb?a %2bb=a %2Bb"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("a +b", res->body); } @@ -1775,13 +1789,13 @@ TEST_F(ServerTest, MultipartFormData) { auto res = cli_.Post("/multipart", items); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } TEST_F(ServerTest, CaseInsensitiveHeaderName) { auto res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("content-type")); EXPECT_EQ("Hello World!", res->body); @@ -1818,7 +1832,7 @@ TEST_F(ServerTest, CaseInsensitiveTransferEncoding) { TEST_F(ServerTest, GetStreamed2) { auto res = cli_.Get("/streamed", {{make_range_header({{2, 3}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("2", res->get_header_value("Content-Length")); EXPECT_EQ(std::string("ab"), res->body); @@ -1826,7 +1840,7 @@ TEST_F(ServerTest, GetStreamed2) { TEST_F(ServerTest, GetStreamed) { auto res = cli_.Get("/streamed"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("6", res->get_header_value("Content-Length")); EXPECT_EQ(std::string("aaabbb"), res->body); @@ -1834,7 +1848,7 @@ TEST_F(ServerTest, GetStreamed) { TEST_F(ServerTest, GetStreamedWithRange1) { auto res = cli_.Get("/streamed-with-range", {{make_range_header({{3, 5}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("3", res->get_header_value("Content-Length")); EXPECT_EQ(true, res->has_header("Content-Range")); @@ -1843,7 +1857,7 @@ TEST_F(ServerTest, GetStreamedWithRange1) { TEST_F(ServerTest, GetStreamedWithRange2) { auto res = cli_.Get("/streamed-with-range", {{make_range_header({{1, -1}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("6", res->get_header_value("Content-Length")); EXPECT_EQ(true, res->has_header("Content-Range")); @@ -1853,7 +1867,7 @@ TEST_F(ServerTest, GetStreamedWithRange2) { TEST_F(ServerTest, GetStreamedWithRangeMultipart) { auto res = cli_.Get("/streamed-with-range", {{make_range_header({{1, 2}, {4, 5}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("269", res->get_header_value("Content-Length")); EXPECT_EQ(false, res->has_header("Content-Range")); @@ -1870,7 +1884,8 @@ TEST_F(ServerTest, GetStreamedEndless) { } return false; }); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); } TEST_F(ServerTest, ClientStop) { @@ -1879,7 +1894,9 @@ TEST_F(ServerTest, ClientStop) { threads.emplace_back(thread([&]() { auto res = cli_.Get("/streamed-cancel", [&](const char *, uint64_t) { return true; }); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_TRUE(res.error() == Error::Canceled || + res.error() == Error::Read); })); } @@ -1896,7 +1913,7 @@ TEST_F(ServerTest, ClientStop) { TEST_F(ServerTest, GetWithRange1) { auto res = cli_.Get("/with-range", {{make_range_header({{3, 5}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("3", res->get_header_value("Content-Length")); EXPECT_EQ(true, res->has_header("Content-Range")); @@ -1905,7 +1922,7 @@ TEST_F(ServerTest, GetWithRange1) { TEST_F(ServerTest, GetWithRange2) { auto res = cli_.Get("/with-range", {{make_range_header({{1, -1}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("6", res->get_header_value("Content-Length")); EXPECT_EQ(true, res->has_header("Content-Range")); @@ -1914,7 +1931,7 @@ TEST_F(ServerTest, GetWithRange2) { TEST_F(ServerTest, GetWithRange3) { auto res = cli_.Get("/with-range", {{make_range_header({{0, 0}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("1", res->get_header_value("Content-Length")); EXPECT_EQ(true, res->has_header("Content-Range")); @@ -1923,7 +1940,7 @@ TEST_F(ServerTest, GetWithRange3) { TEST_F(ServerTest, GetWithRange4) { auto res = cli_.Get("/with-range", {{make_range_header({{-1, 2}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("2", res->get_header_value("Content-Length")); EXPECT_EQ(true, res->has_header("Content-Range")); @@ -1932,7 +1949,7 @@ TEST_F(ServerTest, GetWithRange4) { TEST_F(ServerTest, GetWithRangeMultipart) { auto res = cli_.Get("/with-range", {{make_range_header({{1, 2}, {4, 5}})}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(206, res->status); EXPECT_EQ("269", res->get_header_value("Content-Length")); EXPECT_EQ(false, res->has_header("Content-Range")); @@ -1941,14 +1958,14 @@ TEST_F(ServerTest, GetWithRangeMultipart) { TEST_F(ServerTest, GetStreamedChunked) { auto res = cli_.Get("/streamed-chunked"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(std::string("123456789"), res->body); } TEST_F(ServerTest, GetStreamedChunked2) { auto res = cli_.Get("/streamed-chunked2"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(std::string("123456789"), res->body); } @@ -1986,7 +2003,7 @@ TEST_F(ServerTest, LargeChunkedPost) { TEST_F(ServerTest, GetMethodRemoteAddr) { auto res = cli_.Get("/remote_addr"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1"); @@ -1994,7 +2011,7 @@ TEST_F(ServerTest, GetMethodRemoteAddr) { TEST_F(ServerTest, HTTPResponseSplitting) { auto res = cli_.Get("/http_response_splitting"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -2020,7 +2037,7 @@ TEST_F(ServerTest, SlowPost) { }, "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); cli_.set_write_timeout(0, 0); @@ -2032,12 +2049,13 @@ TEST_F(ServerTest, SlowPost) { }, "text/plain"); - ASSERT_FALSE(res != nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Write, res.error()); } TEST_F(ServerTest, Put) { auto res = cli_.Put("/put", "PUT", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("PUT", res->body); } @@ -2052,7 +2070,7 @@ TEST_F(ServerTest, PutWithContentProvider) { }, "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("PUT", res->body); } @@ -2065,7 +2083,8 @@ TEST_F(ServerTest, PostWithContentProviderAbort) { }, "text/plain"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); } #ifdef CPPHTTPLIB_ZLIB_SUPPORT @@ -2080,7 +2099,7 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) { }, "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("PUT", res->body); } @@ -2094,14 +2113,15 @@ TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) { }, "text/plain"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::Canceled, res.error()); } TEST_F(ServerTest, PutLargeFileWithGzip) { cli_.set_compress(true); auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(LARGE_DATA, res->body); } @@ -2114,7 +2134,7 @@ TEST_F(ServerTest, PutContentWithDeflate) { auto res = cli_.Put("/put", headers, "\170\234\013\010\015\001\0\001\361\0\372", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("PUT", res->body); } @@ -2124,7 +2144,7 @@ TEST_F(ServerTest, GetStreamedChunkedWithGzip) { headers.emplace("Accept-Encoding", "gzip, deflate"); auto res = cli_.Get("/streamed-chunked", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(std::string("123456789"), res->body); } @@ -2134,7 +2154,7 @@ TEST_F(ServerTest, GetStreamedChunkedWithGzip2) { headers.emplace("Accept-Encoding", "gzip, deflate"); auto res = cli_.Get("/streamed-chunked2", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(std::string("123456789"), res->body); } @@ -2146,7 +2166,7 @@ TEST_F(ServerTest, GetStreamedChunkedWithBrotli) { headers.emplace("Accept-Encoding", "brotli"); auto res = cli_.Get("/streamed-chunked", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(std::string("123456789"), res->body); } @@ -2156,7 +2176,7 @@ TEST_F(ServerTest, GetStreamedChunkedWithBrotli2) { headers.emplace("Accept-Encoding", "brotli"); auto res = cli_.Get("/streamed-chunked2", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(std::string("123456789"), res->body); } @@ -2164,28 +2184,28 @@ TEST_F(ServerTest, GetStreamedChunkedWithBrotli2) { TEST_F(ServerTest, Patch) { auto res = cli_.Patch("/patch", "PATCH", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("PATCH", res->body); } TEST_F(ServerTest, Delete) { auto res = cli_.Delete("/delete"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("DELETE", res->body); } TEST_F(ServerTest, DeleteContentReceiver) { auto res = cli_.Delete("/delete-body", "content", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("content", res->body); } TEST_F(ServerTest, Options) { auto res = cli_.Options("*"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("GET, POST, HEAD, OPTIONS", res->get_header_value("Allow")); EXPECT_TRUE(res->body.empty()); @@ -2193,13 +2213,13 @@ TEST_F(ServerTest, Options) { TEST_F(ServerTest, URL) { auto res = cli_.Get("/request-target?aaa=bbb&ccc=ddd"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } TEST_F(ServerTest, ArrayParam) { auto res = cli_.Get("/array-param?array=value1&array=value2&array=value3"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -2207,13 +2227,13 @@ TEST_F(ServerTest, NoMultipleHeaders) { Headers headers = {{"Content-Length", "5"}}; auto res = cli_.Post("/validate-no-multiple-headers", headers, "hello", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } TEST_F(ServerTest, PostContentReceiver) { auto res = cli_.Post("/content_receiver", "content", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("content", res->body); } @@ -2229,28 +2249,28 @@ TEST_F(ServerTest, PostMulitpartFilsContentReceiver) { auto res = cli_.Post("/content_receiver", items); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } TEST_F(ServerTest, PostContentReceiverGzip) { cli_.set_compress(true); auto res = cli_.Post("/content_receiver", "content", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("content", res->body); } TEST_F(ServerTest, PutContentReceiver) { auto res = cli_.Put("/content_receiver", "content", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("content", res->body); } TEST_F(ServerTest, PatchContentReceiver) { auto res = cli_.Patch("/content_receiver", "content", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); ASSERT_EQ("content", res->body); } @@ -2258,7 +2278,7 @@ TEST_F(ServerTest, PatchContentReceiver) { TEST_F(ServerTest, PostQueryStringAndBody) { auto res = cli_.Post("/query-string-and-body?key=value", "content", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); } @@ -2277,29 +2297,29 @@ TEST_F(ServerTest, HTTP2Magic) { TEST_F(ServerTest, KeepAlive) { auto res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("Hello World!", res->body); res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("Hello World!", res->body); res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("Hello World!", res->body); res = cli_.Get("/not-exist"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); res = cli_.Post("/empty", "", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("empty", res->body); @@ -2308,25 +2328,32 @@ TEST_F(ServerTest, KeepAlive) { res = cli_.Post( "/empty", 0, [&](size_t, size_t, DataSink &) { return true; }, "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("empty", res->body); cli_.set_keep_alive(false); res = cli_.Get("/last-request"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("close", res->get_header_value("Connection")); } +TEST_F(ServerTest, TooManyRedirect) { + cli_.set_follow_location(true); + auto res = cli_.Get("/redirect/0"); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::ExceedRedirectCount, res.error()); +} + #ifdef CPPHTTPLIB_ZLIB_SUPPORT TEST_F(ServerTest, Gzip) { Headers headers; headers.emplace("Accept-Encoding", "gzip, deflate"); auto res = cli_.Get("/compress", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("gzip", res->get_header_value("Content-Encoding")); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("33", res->get_header_value("Content-Length")); @@ -2339,7 +2366,7 @@ TEST_F(ServerTest, Gzip) { TEST_F(ServerTest, GzipWithoutAcceptEncoding) { auto res = cli_.Get("/compress"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_TRUE(res->get_header_value("Content-Encoding").empty()); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("100", res->get_header_value("Content-Length")); @@ -2360,7 +2387,7 @@ TEST_F(ServerTest, GzipWithContentReceiver) { return true; }); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("gzip", res->get_header_value("Content-Encoding")); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("33", res->get_header_value("Content-Length")); @@ -2377,7 +2404,7 @@ TEST_F(ServerTest, GzipWithoutDecompressing) { cli_.set_decompress(false); auto res = cli_.Get("/compress", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("gzip", res->get_header_value("Content-Encoding")); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("33", res->get_header_value("Content-Length")); @@ -2387,14 +2414,13 @@ TEST_F(ServerTest, GzipWithoutDecompressing) { TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) { std::string body; - auto res = cli_.Get("/compress", - [&](const char *data, uint64_t data_length) { - EXPECT_EQ(data_length, 100); - body.append(data, data_length); - return true; - }); + auto res = cli_.Get("/compress", [&](const char *data, uint64_t data_length) { + EXPECT_EQ(data_length, 100); + body.append(data, data_length); + return true; + }); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_TRUE(res->get_header_value("Content-Encoding").empty()); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("100", res->get_header_value("Content-Length")); @@ -2409,7 +2435,7 @@ TEST_F(ServerTest, NoGzip) { headers.emplace("Accept-Encoding", "gzip, deflate"); auto res = cli_.Get("/nocompress", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(false, res->has_header("Content-Encoding")); EXPECT_EQ("application/octet-stream", res->get_header_value("Content-Type")); EXPECT_EQ("100", res->get_header_value("Content-Length")); @@ -2430,7 +2456,7 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) { return true; }); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(false, res->has_header("Content-Encoding")); EXPECT_EQ("application/octet-stream", res->get_header_value("Content-Type")); EXPECT_EQ("100", res->get_header_value("Content-Length")); @@ -2449,7 +2475,7 @@ TEST_F(ServerTest, MultipartFormDataGzip) { cli_.set_compress(true); auto res = cli_.Post("/compress-multipart", items); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } #endif @@ -2460,7 +2486,7 @@ TEST_F(ServerTest, Brotli) { headers.emplace("Accept-Encoding", "br"); auto res = cli_.Get("/compress", headers); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ("brotli", res->get_header_value("Content-Encoding")); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("19", res->get_header_value("Content-Length")); @@ -2474,9 +2500,11 @@ 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) { + Error error = Error::Success; + auto client_sock = detail::create_client_socket(HOST, PORT, false, nullptr, - /*timeout_sec=*/5, 0, std::string()); + /*timeout_sec=*/5, 0, std::string(), error); if (client_sock == INVALID_SOCKET) { return false; } @@ -2686,7 +2714,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) { const Headers headers = {{"Accept", "text/event-stream"}}; auto get_thread = std::thread([&client, &headers]() { - std::shared_ptr res = client.Get( + auto res = client.Get( "/events", headers, [](const char * /*data*/, size_t /*len*/) -> bool { return true; }); }); @@ -2718,27 +2746,27 @@ TEST(MountTest, Unmount) { svr.set_mount_point("/mount2", "./www2"); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); res = cli.Get("/mount2/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); svr.set_mount_point("/", "./www"); res = cli.Get("/dir/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); svr.remove_mount_point("/"); res = cli.Get("/dir/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); svr.remove_mount_point("/mount2"); res = cli.Get("/mount2/dir/test.html"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(404, res->status); svr.stop(); @@ -2765,7 +2793,7 @@ TEST(ExceptionTest, ThrowExceptionInHandler) { Client cli("localhost", PORT); auto res = cli.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(500, res->status); ASSERT_FALSE(res->has_header("EXCEPTION_WHAT")); @@ -2799,10 +2827,11 @@ TEST(KeepAliveTest, ReadTimeout) { cli.set_read_timeout(1); auto resa = cli.Get("/a"); - ASSERT_TRUE(resa == nullptr); + ASSERT_TRUE(!resa); + EXPECT_EQ(Error::Read, resa.error()); auto resb = cli.Get("/b"); - ASSERT_TRUE(resb != nullptr); + ASSERT_TRUE(resb); EXPECT_EQ(200, resb->status); EXPECT_EQ("b", resb->body); @@ -2854,7 +2883,7 @@ protected: TEST_F(ServerTestWithAI_PASSIVE, GetMethod200) { auto res = cli_.Get("/hi"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); EXPECT_EQ("Hello World!", res->body); @@ -2936,11 +2965,11 @@ protected: TEST_F(PayloadMaxLengthTest, ExceedLimit) { auto res = cli_.Post("/test", "123456789", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(413, res->status); res = cli_.Post("/test", "12345678", "text/plain"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -2948,14 +2977,14 @@ TEST_F(PayloadMaxLengthTest, ExceedLimit) { TEST(SSLClientTest, ServerNameIndication) { SSLClient cli("httpbin.org", 443); auto res = cli.Get("/get"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); } TEST(SSLClientTest, ServerCertificateVerification1) { SSLClient cli("google.com"); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(301, res->status); } @@ -2964,14 +2993,15 @@ TEST(SSLClientTest, ServerCertificateVerification2) { cli.enable_server_certificate_verification(true); cli.set_ca_cert_path("hello"); auto res = cli.Get("/"); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::SSLLoadingCerts, res.error()); } TEST(SSLClientTest, ServerCertificateVerification3) { SSLClient cli("google.com"); cli.set_ca_cert_path(CA_CERT_FILE); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(301, res->status); } @@ -2982,7 +3012,7 @@ TEST(SSLClientTest, WildcardHostNameMatch) { cli.enable_server_certificate_verification(true); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); } @@ -3023,7 +3053,7 @@ TEST(SSLClientServerTest, ClientCertPresent) { cli.set_connection_timeout(30); auto res = cli.Get("/test"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); t.join(); @@ -3094,7 +3124,7 @@ TEST(SSLClientServerTest, MemoryClientCertPresent) { cli.set_connection_timeout(30); auto res = cli.Get("/test"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); X509_free(server_cert); @@ -3118,7 +3148,8 @@ TEST(SSLClientServerTest, ClientCertMissing) { SSLClient cli(HOST, PORT); auto res = cli.Get("/test"); cli.set_connection_timeout(30); - ASSERT_TRUE(res == nullptr); + ASSERT_TRUE(!res); + EXPECT_EQ(Error::SSLServerVerification, res.error()); svr.stop(); @@ -3142,7 +3173,7 @@ TEST(SSLClientServerTest, TrustDirOptional) { cli.set_connection_timeout(30); auto res = cli.Get("/test"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); ASSERT_EQ(200, res->status); t.join(); @@ -3178,12 +3209,12 @@ TEST(YahooRedirectTest2, SimpleInterface) { Client cli("http://yahoo.com"); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(301, res->status); cli.set_follow_location(true); res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } @@ -3191,12 +3222,35 @@ TEST(YahooRedirectTest3, SimpleInterface) { Client cli("https://yahoo.com"); auto res = cli.Get("/"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(301, res->status); cli.set_follow_location(true); res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(200, res->status); +} + +TEST(YahooRedirectTest3, NewResultInterface) { + Client cli("https://yahoo.com"); + + auto res = cli.Get("/"); + ASSERT_TRUE(res); + ASSERT_FALSE(!res); + ASSERT_TRUE(res); + ASSERT_FALSE(res == nullptr); ASSERT_TRUE(res != nullptr); + EXPECT_EQ(Error::Success, res.error()); + EXPECT_EQ(301, res.value().status); + EXPECT_EQ(301, (*res).status); + EXPECT_EQ(301, res->status); + + cli.set_follow_location(true); + res = cli.Get("/"); + ASSERT_TRUE(res); + EXPECT_EQ(Error::Success, res.error()); + EXPECT_EQ(200, res.value().status); + EXPECT_EQ(200, (*res).status); EXPECT_EQ(200, res->status); } @@ -3206,7 +3260,7 @@ TEST(DecodeWithChunkedEncoding, BrotliEncoding) { auto res = cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "brotli"}}); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); EXPECT_EQ(287630, res->body.size()); EXPECT_EQ("application/javascript; charset=utf-8", @@ -3221,7 +3275,7 @@ TEST(HttpsToHttpRedirectTest2, SimpleInterface) { .set_follow_location(true) .Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302"); - ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(res); EXPECT_EQ(200, res->status); } #endif