Compare commits

..

10 Commits

Author SHA1 Message Date
yhirose
66f698fab6 Fixed build errors with some examples 2020-05-16 00:50:52 -04:00
yhirose
b9a9df4d73 Fixed problem with writing large data 2020-05-15 22:21:58 -04:00
yhirose
25aa3ca982 Added std::ostream os in DataSink. 2020-05-15 21:26:13 -04:00
yhirose
2d67211183 Added more unit tests for the simple interface 2020-05-14 18:25:18 -04:00
yhirose
f4c5d94d74 Updated version in the User Agent string 2020-05-14 18:07:02 -04:00
yhirose
63a96aeb20 Improved Client2 interface 2020-05-14 12:51:34 -04:00
yhirose
bbb83d12c1 Removed default parameter values in Client and SSLClient constructors 2020-05-14 08:51:32 -04:00
yhirose
2d4b42b70b Removed url 2020-05-14 01:43:06 -04:00
yhirose
1919d08f71 Added Client2 2020-05-14 01:36:56 -04:00
yhirose
824c02fcd3 Code cleanup 2020-05-14 01:08:36 -04:00
4 changed files with 532 additions and 173 deletions

View File

@@ -14,13 +14,12 @@ using namespace std;
int main(void) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
httplib::url::Options options;
options.ca_cert_file_path = CA_CERT_FILE;
// options.server_certificate_verification = true;
auto res = httplib::url::Get("https://localhost:8080/hi", options);
auto res = httplib::Client2("https://localhost:8080")
.set_ca_cert_path(CA_CERT_FILE)
// .enable_server_certificate_verification(true)
.Get("/hi");
#else
auto res = httplib::url::Get("http://localhost:8080/hi");
auto res = httplib::Client2("http://localhost:8080").Get("/hi");
#endif
if (res) {

View File

@@ -80,15 +80,19 @@ int main(void) {
svr.Get("/event1", [&](const Request & /*req*/, Response &res) {
cout << "connected to event1..." << endl;
res.set_header("Content-Type", "text/event-stream");
res.set_chunked_content_provider(
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
ed.wait_event(&sink);
return true;
});
});
svr.Get("/event2", [&](const Request & /*req*/, Response &res) {
cout << "connected to event2..." << endl;
res.set_header("Content-Type", "text/event-stream");
res.set_chunked_content_provider(
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
ed.wait_event(&sink);
return true;
});
});
thread t([&] {

580
httplib.h
View File

@@ -215,7 +215,8 @@ using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
class DataSink {
public:
DataSink() = default;
DataSink() : os(&sb_), sb_(*this) {}
DataSink(const DataSink &) = delete;
DataSink &operator=(const DataSink &) = delete;
DataSink(DataSink &&) = delete;
@@ -224,6 +225,24 @@ public:
std::function<void(const char *data, size_t data_len)> write;
std::function<void()> done;
std::function<bool()> is_writable;
std::ostream os;
private:
class data_sink_streambuf : public std::streambuf {
public:
data_sink_streambuf(DataSink &sink) : sink_(sink) {}
protected:
std::streamsize xsputn(const char *s, std::streamsize n) {
sink_.write(s, static_cast<size_t>(n));
return n;
}
private:
DataSink &sink_;
};
data_sink_streambuf sb_;
};
using ContentProvider =
@@ -280,9 +299,10 @@ struct Request {
// for client
size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
size_t authorization_count = 1;
ResponseHandler response_handler;
ContentReceiver content_receiver;
size_t content_length = 0;
ContentProvider content_provider;
Progress progress;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -305,8 +325,7 @@ struct Request {
MultipartFormData get_file_value(const char *key) const;
// private members...
size_t content_length;
ContentProvider content_provider;
size_t authorization_count_ = 1;
};
struct Response {
@@ -339,15 +358,15 @@ struct Response {
Response(Response &&) = default;
Response &operator=(Response &&) = default;
~Response() {
if (content_provider_resource_releaser) {
content_provider_resource_releaser();
if (content_provider_resource_releaser_) {
content_provider_resource_releaser_();
}
}
// private members...
size_t content_length = 0;
ContentProvider content_provider;
std::function<void()> content_provider_resource_releaser;
size_t content_length_ = 0;
ContentProvider content_provider_;
std::function<void()> content_provider_resource_releaser_;
};
class Stream {
@@ -571,9 +590,13 @@ private:
class Client {
public:
explicit Client(const std::string &host, int port = 80,
const std::string &client_cert_path = std::string(),
const std::string &client_key_path = std::string());
explicit Client(const std::string &host);
explicit Client(const std::string &host, int port);
explicit Client(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path);
virtual ~Client();
@@ -897,18 +920,22 @@ private:
class SSLClient : public Client {
public:
explicit SSLClient(const std::string &host, int port = 443,
const std::string &client_cert_path = std::string(),
const std::string &client_key_path = std::string());
explicit SSLClient(const std::string &host);
SSLClient(const std::string &host, int port, X509 *client_cert,
EVP_PKEY *client_key);
explicit SSLClient(const std::string &host, int port);
explicit SSLClient(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path);
explicit SSLClient(const std::string &host, int port, X509 *client_cert,
EVP_PKEY *client_key);
~SSLClient() override;
bool is_valid() const override;
void set_ca_cert_path(const char *ca_ceert_file_path,
void set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path = nullptr);
void set_ca_cert_store(X509_STORE *ca_cert_store);
@@ -2071,27 +2098,37 @@ inline ssize_t write_headers(Stream &strm, const T &info,
return write_len;
}
inline bool write_data(Stream &strm, const char *d, size_t l) {
size_t offset = 0;
while (offset < l) {
auto length = strm.write(d + offset, l - offset);
if (length < 0) { return false; }
offset += static_cast<size_t>(length);
}
return true;
}
inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
size_t offset, size_t length) {
size_t begin_offset = offset;
size_t end_offset = offset + length;
ssize_t written_length = 0;
auto ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
offset += l;
written_length = strm.write(d, l);
};
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
if (ok) {
offset += l;
if (!write_data(strm, d, l)) { ok = false; }
}
};
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
while (offset < end_offset) {
while (ok && offset < end_offset) {
if (!content_provider(offset, end_offset - offset, data_sink)) {
return -1;
}
if (written_length < 0) { return written_length; }
if (!ok) { return -1; }
}
return static_cast<ssize_t>(offset - begin_offset);
@@ -2105,29 +2142,41 @@ inline ssize_t write_content_chunked(Stream &strm,
auto data_available = true;
ssize_t total_written_length = 0;
ssize_t written_length = 0;
auto ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
data_available = l > 0;
offset += l;
if (ok) {
data_available = l > 0;
offset += l;
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
written_length = strm.write(chunk);
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
if (write_data(strm, chunk.data(), chunk.size())) {
total_written_length += chunk.size();
} else {
ok = false;
}
}
};
data_sink.done = [&](void) {
data_available = false;
written_length = strm.write("0\r\n\r\n");
if (ok) {
static const std::string done_marker("0\r\n\r\n");
if (write_data(strm, done_marker.data(), done_marker.size())) {
total_written_length += done_marker.size();
} else {
ok = false;
}
}
};
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
return ok && strm.is_writable();
};
while (data_available && !is_shutting_down()) {
if (!content_provider(offset, 0, data_sink)) { return -1; }
if (written_length < 0) { return written_length; }
total_written_length += written_length;
if (!ok) { return -1; }
}
return total_written_length;
@@ -2597,7 +2646,7 @@ inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
[&](const std::string &token) { strm.write(token); },
[&](const char *token) { strm.write(token); },
[&](size_t offset, size_t length) {
return write_content(strm, res.content_provider, offset, length) >= 0;
return write_content(strm, res.content_provider_, offset, length) >= 0;
});
}
@@ -2607,7 +2656,7 @@ get_range_offset_and_length(const Request &req, const Response &res,
auto r = req.ranges[index];
if (r.second == -1) {
r.second = static_cast<ssize_t>(res.content_length) - 1;
r.second = static_cast<ssize_t>(res.content_length_) - 1;
}
return std::make_pair(r.first, r.second - r.first + 1);
@@ -2913,21 +2962,20 @@ inline void
Response::set_content_provider(size_t in_length, ContentProvider provider,
std::function<void()> resource_releaser) {
assert(in_length > 0);
content_length = in_length;
content_provider = [provider](size_t offset, size_t length, DataSink &sink) {
content_length_ = in_length;
content_provider_ = [provider](size_t offset, size_t length, DataSink &sink) {
return provider(offset, length, sink);
};
content_provider_resource_releaser = resource_releaser;
content_provider_resource_releaser_ = resource_releaser;
}
inline void Response::set_chunked_content_provider(
ChunkedContentProvider provider,
std::function<void()> resource_releaser) {
content_length = 0;
content_provider = [provider](size_t offset, size_t, DataSink &sink) {
ChunkedContentProvider provider, std::function<void()> resource_releaser) {
content_length_ = 0;
content_provider_ = [provider](size_t offset, size_t, DataSink &sink) {
return provider(offset, sink);
};
content_provider_resource_releaser = resource_releaser;
content_provider_resource_releaser_ = resource_releaser;
}
// Rstream implementation
@@ -3250,7 +3298,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
}
if (!res.has_header("Content-Type") &&
(!res.body.empty() || res.content_length > 0)) {
(!res.body.empty() || res.content_length_ > 0)) {
res.set_header("Content-Type", "text/plain");
}
@@ -3275,17 +3323,17 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
}
if (res.body.empty()) {
if (res.content_length > 0) {
if (res.content_length_ > 0) {
size_t length = 0;
if (req.ranges.empty()) {
length = res.content_length;
length = res.content_length_;
} else if (req.ranges.size() == 1) {
auto offsets =
detail::get_range_offset_and_length(req, res.content_length, 0);
detail::get_range_offset_and_length(req, res.content_length_, 0);
auto offset = offsets.first;
length = offsets.second;
auto content_range = detail::make_content_range_header_field(
offset, length, res.content_length);
offset, length, res.content_length_);
res.set_header("Content-Range", content_range);
} else {
length = detail::get_multipart_ranges_data_length(req, res, boundary,
@@ -3293,7 +3341,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
}
res.set_header("Content-Length", std::to_string(length));
} else {
if (res.content_provider) {
if (res.content_provider_) {
res.set_header("Transfer-Encoding", "chunked");
} else {
res.set_header("Content-Length", "0");
@@ -3341,7 +3389,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
if (req.method != "HEAD") {
if (!res.body.empty()) {
if (!strm.write(res.body)) { return false; }
} else if (res.content_provider) {
} else if (res.content_provider_) {
if (!write_content_with_provider(strm, req, res, boundary,
content_type)) {
return false;
@@ -3359,18 +3407,18 @@ inline bool
Server::write_content_with_provider(Stream &strm, const Request &req,
Response &res, const std::string &boundary,
const std::string &content_type) {
if (res.content_length) {
if (res.content_length_) {
if (req.ranges.empty()) {
if (detail::write_content(strm, res.content_provider, 0,
res.content_length) < 0) {
if (detail::write_content(strm, res.content_provider_, 0,
res.content_length_) < 0) {
return false;
}
} else if (req.ranges.size() == 1) {
auto offsets =
detail::get_range_offset_and_length(req, res.content_length, 0);
detail::get_range_offset_and_length(req, res.content_length_, 0);
auto offset = offsets.first;
auto length = offsets.second;
if (detail::write_content(strm, res.content_provider, offset, length) <
if (detail::write_content(strm, res.content_provider_, offset, length) <
0) {
return false;
}
@@ -3384,7 +3432,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
auto is_shutting_down = [this]() {
return this->svr_sock_ == INVALID_SOCKET;
};
if (detail::write_content_chunked(strm, res.content_provider,
if (detail::write_content_chunked(strm, res.content_provider_,
is_shutting_down) < 0) {
return false;
}
@@ -3787,6 +3835,12 @@ inline bool Server::process_and_close_socket(socket_t sock) {
}
// HTTP client implementation
inline Client::Client(const std::string &host)
: Client(host, 80, std::string(), std::string()) {}
inline Client::Client(const std::string &host, int port)
: Client(host, port, std::string(), std::string()) {}
inline Client::Client(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
@@ -3902,7 +3956,7 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if ((res.status == 401 || res.status == 407) &&
req.authorization_count == 1) {
req.authorization_count_ == 1) {
auto is_proxy = res.status == 407;
const auto &username =
is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
@@ -3913,12 +3967,12 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
std::map<std::string, std::string> auth;
if (parse_www_authenticate(res, auth, is_proxy)) {
Request new_req = req;
new_req.authorization_count += 1;
new_req.authorization_count_ += 1;
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
new_req.headers.erase(key);
new_req.headers.insert(make_digest_authentication_header(
req, auth, new_req.authorization_count, random_string(10), username,
password, is_proxy));
req, auth, new_req.authorization_count_, random_string(10),
username, password, is_proxy));
Response new_res;
@@ -4066,7 +4120,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
if (!req.has_header("User-Agent")) {
headers.emplace("User-Agent", "cpp-httplib/0.5");
headers.emplace("User-Agent", "cpp-httplib/0.6");
}
if (req.body.empty()) {
@@ -4102,7 +4156,9 @@ inline bool Client::write_request(Stream &strm, const Request &req,
// Flush buffer
auto &data = bstrm.get_buffer();
strm.write(data.data(), data.size());
if (!detail::write_data(strm, data.data(), data.size())) {
return false;
}
// Body
if (req.body.empty()) {
@@ -4110,26 +4166,31 @@ inline bool Client::write_request(Stream &strm, const Request &req,
size_t offset = 0;
size_t end_offset = req.content_length;
ssize_t written_length = 0;
bool ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
written_length = strm.write(d, l);
offset += static_cast<size_t>(written_length);
if (ok) {
if (detail::write_data(strm, d, l)) {
offset += l;
} else {
ok = false;
}
}
};
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
return ok && strm.is_writable();
};
while (offset < end_offset) {
if (!req.content_provider(offset, end_offset - offset, data_sink)) {
return false;
}
if (written_length < 0) { return false; }
if (!ok) { return false; }
}
}
} else {
strm.write(req.body);
return detail::write_data(strm, req.body.data(), req.body.size());
}
return true;
@@ -4846,6 +4907,12 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
}
// SSL HTTP client implementation
inline SSLClient::SSLClient(const std::string &host)
: SSLClient(host, 443, std::string(), std::string()) {}
inline SSLClient::SSLClient(const std::string &host, int port)
: SSLClient(host, port, std::string(), std::string()) {}
inline SSLClient::SSLClient(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
@@ -5100,63 +5167,338 @@ inline bool SSLClient::check_host_name(const char *pattern,
}
#endif
namespace url {
class Client2 {
public:
explicit Client2(const char *scheme_host_port)
: Client2(scheme_host_port, std::string(), std::string()) {}
struct Options {
// TODO: support more options...
bool follow_location = false;
std::string client_cert_path;
std::string client_key_path;
explicit Client2(const char *scheme_host_port,
const std::string &client_cert_path,
const std::string &client_key_path) {
const static std::regex re(R"(^(https?)://([^:/?#]+)(?::(\d+))?)");
std::string ca_cert_file_path;
std::string ca_cert_dir_path;
bool server_certificate_verification = false;
};
std::cmatch m;
if (std::regex_match(scheme_host_port, m, re)) {
auto scheme = m[1].str();
auto host = m[2].str();
auto port_str = m[3].str();
inline std::shared_ptr<Response> Get(const char *url, Options &options) {
const static std::regex re(
R"(^(https?)://([^:/?#]+)(?::(\d+))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
auto port = !port_str.empty() ? std::stoi(port_str)
: (scheme == "https" ? 443 : 80);
std::cmatch m;
if (!std::regex_match(url, m, re)) { return nullptr; }
auto next_scheme = m[1].str();
auto next_host = m[2].str();
auto port_str = m[3].str();
auto next_path = m[4].str();
auto next_port = !port_str.empty() ? std::stoi(port_str)
: (next_scheme == "https" ? 443 : 80);
if (next_path.empty()) { next_path = "/"; }
if (next_scheme == "https") {
if (scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port, options.client_cert_path,
options.client_key_path);
cli.set_follow_location(options.follow_location);
cli.set_ca_cert_path(options.ca_cert_file_path.c_str(),
options.ca_cert_dir_path.c_str());
cli.enable_server_certificate_verification(
options.server_certificate_verification);
return cli.Get(next_path.c_str());
#else
return nullptr;
is_ssl_ = true;
cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path,
client_key_path);
#endif
} else {
Client cli(next_host.c_str(), next_port, options.client_cert_path,
options.client_key_path);
cli.set_follow_location(options.follow_location);
return cli.Get(next_path.c_str());
} else {
cli_ = std::make_shared<Client>(host.c_str(), port, client_cert_path,
client_key_path);
}
}
}
}
inline std::shared_ptr<Response> Get(const char *url) {
Options options;
return Get(url, options);
}
~Client2() {}
} // namespace url
bool is_valid() const { return cli_ != nullptr; }
std::shared_ptr<Response> Get(const char *path) { return cli_->Get(path); }
std::shared_ptr<Response> Get(const char *path, const Headers &headers) {
return cli_->Get(path, headers);
}
std::shared_ptr<Response> Get(const char *path, Progress progress) {
return cli_->Get(path, progress);
}
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
Progress progress) {
return cli_->Get(path, headers, progress);
}
std::shared_ptr<Response> Get(const char *path,
ContentReceiver content_receiver) {
return cli_->Get(path, content_receiver);
}
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ContentReceiver content_receiver) {
return cli_->Get(path, headers, content_receiver);
}
std::shared_ptr<Response>
Get(const char *path, ContentReceiver content_receiver, Progress progress) {
return cli_->Get(path, content_receiver, progress);
}
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ContentReceiver content_receiver,
Progress progress) {
return cli_->Get(path, headers, content_receiver, progress);
}
std::shared_ptr<Response> Get(const char *path, const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return cli_->Get(path, headers, response_handler, content_receiver);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> Head(const char *path) { return cli_->Head(path); }
std::shared_ptr<Response> Head(const char *path, const Headers &headers) {
return cli_->Head(path, headers);
}
std::shared_ptr<Response> Post(const char *path) { return cli_->Post(path); }
std::shared_ptr<Response> Post(const char *path, const std::string &body,
const char *content_type) {
return cli_->Post(path, body, content_type);
}
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
return cli_->Post(path, headers, body, content_type);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> Post(const char *path, const Params &params) {
return cli_->Post(path, params);
}
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const Params &params) {
return cli_->Post(path, headers, params);
}
std::shared_ptr<Response> Post(const char *path,
const MultipartFormDataItems &items) {
return cli_->Post(path, items);
}
std::shared_ptr<Response> Post(const char *path, const Headers &headers,
const MultipartFormDataItems &items) {
return cli_->Post(path, headers, items);
}
std::shared_ptr<Response> Put(const char *path) { return cli_->Put(path); }
std::shared_ptr<Response> Put(const char *path, const std::string &body,
const char *content_type) {
return cli_->Put(path, body, content_type);
}
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
return cli_->Put(path, headers, body, content_type);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> Put(const char *path, const Params &params) {
return cli_->Put(path, params);
}
std::shared_ptr<Response> Put(const char *path, const Headers &headers,
const Params &params) {
return cli_->Put(path, headers, params);
}
std::shared_ptr<Response> Patch(const char *path, const std::string &body,
const char *content_type) {
return cli_->Patch(path, body, content_type);
}
std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
return cli_->Patch(path, headers, body, content_type);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> 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);
}
std::shared_ptr<Response> Delete(const char *path) {
return cli_->Delete(path);
}
std::shared_ptr<Response> Delete(const char *path, const std::string &body,
const char *content_type) {
return cli_->Delete(path, body, content_type);
}
std::shared_ptr<Response> Delete(const char *path, const Headers &headers) {
return cli_->Delete(path, headers);
}
std::shared_ptr<Response> Delete(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
return cli_->Delete(path, headers, body, content_type);
}
std::shared_ptr<Response> Options(const char *path) {
return cli_->Options(path);
}
std::shared_ptr<Response> Options(const char *path, const Headers &headers) {
return cli_->Options(path, headers);
}
bool send(const Request &req, Response &res) { return cli_->send(req, res); }
bool send(const std::vector<Request> &requests,
std::vector<Response> &responses) {
return cli_->send(requests, responses);
}
void stop() { cli_->stop(); }
Client2 &set_timeout_sec(time_t timeout_sec) {
cli_->set_timeout_sec(timeout_sec);
return *this;
}
Client2 &set_read_timeout(time_t sec, time_t usec) {
cli_->set_read_timeout(sec, usec);
return *this;
}
Client2 &set_keep_alive_max_count(size_t count) {
cli_->set_keep_alive_max_count(count);
return *this;
}
Client2 &set_basic_auth(const char *username, const char *password) {
cli_->set_basic_auth(username, password);
return *this;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
Client2 &set_digest_auth(const char *username, const char *password) {
cli_->set_digest_auth(username, password);
return *this;
}
#endif
Client2 &set_follow_location(bool on) {
cli_->set_follow_location(on);
return *this;
}
Client2 &set_compress(bool on) {
cli_->set_compress(on);
return *this;
}
Client2 &set_interface(const char *intf) {
cli_->set_interface(intf);
return *this;
}
Client2 &set_proxy(const char *host, int port) {
cli_->set_proxy(host, port);
return *this;
}
Client2 &set_proxy_basic_auth(const char *username, const char *password) {
cli_->set_proxy_basic_auth(username, password);
return *this;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
Client2 &set_proxy_digest_auth(const char *username, const char *password) {
cli_->set_proxy_digest_auth(username, password);
return *this;
}
#endif
Client2 &set_logger(Logger logger) {
cli_->set_logger(logger);
return *this;
}
// SSL
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
Client2 &set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path = nullptr) {
dynamic_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path,
ca_cert_dir_path);
return *this;
}
Client2 &set_ca_cert_store(X509_STORE *ca_cert_store) {
dynamic_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
return *this;
}
Client2 &enable_server_certificate_verification(bool enabled) {
dynamic_cast<SSLClient &>(*cli_).enable_server_certificate_verification(
enabled);
return *this;
}
long get_openssl_verify_result() const {
return dynamic_cast<SSLClient &>(*cli_).get_openssl_verify_result();
}
SSL_CTX *ssl_context() const {
return dynamic_cast<SSLClient &>(*cli_).ssl_context();
}
#endif
private:
bool is_ssl_ = false;
std::shared_ptr<Client> cli_;
};
namespace detail {

View File

@@ -651,19 +651,6 @@ TEST(YahooRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(YahooRedirectTestWithURL, Redirect) {
auto res = httplib::url::Get("http://yahoo.com");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(301, res->status);
httplib::url::Options options;
options.follow_location = true;
res = httplib::url::Get("http://yahoo.com", options);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest, Redirect) {
httplib::SSLClient cli("httpbin.org");
cli.set_follow_location(true);
@@ -673,19 +660,6 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTestWithURL, Redirect) {
httplib::url::Options options;
options.follow_location = true;
auto res = httplib::url::Get(
"https://httpbin.org/"
"redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302",
options);
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(RedirectToDifferentPort, Redirect) {
Server svr8080;
Server svr8081;
@@ -901,9 +875,9 @@ protected:
res.set_chunked_content_provider(
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.write("123", 3);
sink.write("456", 3);
sink.write("789", 3);
sink.os << "123";
sink.os << "456";
sink.os << "789";
sink.done();
return true;
});
@@ -915,9 +889,9 @@ protected:
[i](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
switch (*i) {
case 0: sink.write("123", 3); break;
case 1: sink.write("456", 3); break;
case 2: sink.write("789", 3); break;
case 0: sink.os << "123"; break;
case 1: sink.os << "456"; break;
case 2: sink.os << "789"; break;
case 3: sink.done(); break;
}
(*i)++;
@@ -929,7 +903,7 @@ protected:
[&](const Request & /*req*/, Response &res) {
res.set_content_provider(
6, [](size_t offset, size_t /*length*/, DataSink &sink) {
sink.write(offset < 3 ? "a" : "b", 1);
sink.os << (offset < 3 ? "a" : "b");
return true;
});
})
@@ -955,8 +929,7 @@ protected:
size_t(-1),
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
std::string data = "data_chunk";
sink.write(data.data(), data.size());
sink.os << "data_chunk";
return true;
});
})
@@ -1924,7 +1897,7 @@ TEST_F(ServerTest, PutWithContentProvider) {
"/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.write("PUT", 3);
sink.os << "PUT";
return true;
},
"text/plain");
@@ -1952,7 +1925,7 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
"/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.write("PUT", 3);
sink.os << "PUT";
return true;
},
"text/plain");
@@ -2890,13 +2863,6 @@ TEST(SSLClientServerTest, TrustDirOptional) {
t.join();
}
/* Cannot test this case as there is no external access to SSL object to check
SSL_get_peer_certificate() == NULL TEST(SSLClientServerTest,
ClientCAPathRequired) { SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
nullptr, CLIENT_CA_CERT_DIR);
}
*/
#endif
#ifdef _WIN32
@@ -2905,3 +2871,51 @@ TEST(CleanupTest, WSACleanup) {
ASSERT_EQ(0, ret);
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(InvalidScheme, SimpleInterface) {
httplib::Client2 cli("scheme://yahoo.com");
ASSERT_FALSE(cli.is_valid());
}
TEST(NoScheme, SimpleInterface) {
httplib::Client2 cli("yahoo.com");
ASSERT_FALSE(cli.is_valid());
}
TEST(YahooRedirectTest2, SimpleInterface) {
httplib::Client2 cli("http://yahoo.com");
auto res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(301, res->status);
cli.set_follow_location(true);
res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(YahooRedirectTest3, SimpleInterface) {
httplib::Client2 cli("https://yahoo.com");
auto res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(301, res->status);
cli.set_follow_location(true);
res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
auto res =
httplib::Client2("https://httpbin.org")
.set_follow_location(true)
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
#endif