Compare commits

..

9 Commits

Author SHA1 Message Date
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
yhirose 2c0613f211 Fix #472 2020-05-13 21:48:14 -04:00
Saika Fatih be45ff1ff1 A detail about Gzip support (#475)
* Typos fixed

* README.md edited.libz should be linked for GZIP support.
2020-05-12 17:38:51 -04:00
Saika Fatih 803ebe1e20 Typos fixed (#474) 2020-05-12 13:18:58 -04:00
3 changed files with 549 additions and 183 deletions
+36 -5
View File
@@ -181,6 +181,7 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
[data](size_t offset, size_t length, DataSink &sink) {
const auto &d = *data;
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
return true; // return 'false' if you want to cancel the process.
},
[data] { delete data; });
});
@@ -192,10 +193,11 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
svr.Get("/chunked", [&](const Request& req, Response& res) {
res.set_chunked_content_provider(
[](size_t offset, DataSink &sink) {
sink.write("123", 3);
sink.write("345", 3);
sink.write("789", 3);
sink.done();
sink.write("123", 3);
sink.write("345", 3);
sink.write("789", 3);
sink.done();
return true; // return 'false' if you want to cancel the process.
}
);
});
@@ -363,6 +365,35 @@ res = cli.Options("/resource/foo");
```c++
cli.set_timeout_sec(5); // timeouts in 5 seconds
```
### Receive content with Content receiver
```cpp
std::string body;
auto res = cli.Get(
"/stream", Headers(),
[&](const Response &response) {
EXPECT_EQ(200, response.status);
return true; // return 'false' if you want to cancel the request.
},
[&](const char *data, size_t data_length) {
body.append(data, data_length);
return true; // return 'false' if you want to cancel the request.
});
```
### Send content with Content provider
```cpp
std::string body = ...;
auto res = cli_.Post(
"/stream", body.size(),
[](size_t offset, size_t length, DataSink &sink) {
sink.write(body.data() + offset, length);
return true; // return 'false' if you want to cancel the request.
},
"text/plain");
```
### With Progress Callback
```cpp
@@ -494,7 +525,7 @@ cli.enable_server_certificate_verification(true);
Zlib Support
------------
'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`.
'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. `libz` should be linked.
The server applies gzip compression to the following MIME type contents:
+453 -146
View File
@@ -227,7 +227,10 @@ public:
};
using ContentProvider =
std::function<void(size_t offset, size_t length, DataSink &sink)>;
std::function<bool(size_t offset, size_t length, DataSink &sink)>;
using ChunkedContentProvider =
std::function<bool(size_t offset, DataSink &sink)>;
using ContentReceiver =
std::function<bool(const char *data, size_t data_length)>;
@@ -241,18 +244,18 @@ public:
using MultipartReader = std::function<bool(MultipartContentHeader header,
ContentReceiver receiver)>;
ContentReader(Reader reader, MultipartReader muitlpart_reader)
: reader_(reader), muitlpart_reader_(muitlpart_reader) {}
ContentReader(Reader reader, MultipartReader multipart_reader)
: reader_(reader), multipart_reader_(multipart_reader) {}
bool operator()(MultipartContentHeader header,
ContentReceiver receiver) const {
return muitlpart_reader_(header, receiver);
return multipart_reader_(header, receiver);
}
bool operator()(ContentReceiver receiver) const { return reader_(receiver); }
Reader reader_;
MultipartReader muitlpart_reader_;
MultipartReader multipart_reader_;
};
using Range = std::pair<ssize_t, ssize_t>;
@@ -277,9 +280,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
@@ -302,8 +306,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 {
@@ -323,13 +326,11 @@ struct Response {
void set_content(std::string s, const char *content_type);
void set_content_provider(
size_t length,
std::function<void(size_t offset, size_t length, DataSink &sink)>
provider,
size_t length, ContentProvider provider,
std::function<void()> resource_releaser = [] {});
void set_chunked_content_provider(
std::function<void(size_t offset, DataSink &sink)> provider,
ChunkedContentProvider provider,
std::function<void()> resource_releaser = [] {});
Response() = default;
@@ -338,15 +339,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 {
@@ -570,9 +571,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();
@@ -896,18 +901,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);
@@ -2074,22 +2083,25 @@ 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;
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;
};
while (offset < end_offset) {
ssize_t written_length = 0;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
offset += l;
written_length = strm.write(d, l);
};
data_sink.done = [&](void) { written_length = -1; };
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
};
content_provider(offset, end_offset - offset, data_sink);
if (!content_provider(offset, end_offset - offset, data_sink)) {
return -1;
}
if (written_length < 0) { return written_length; }
}
return static_cast<ssize_t>(offset - begin_offset);
}
@@ -2100,31 +2112,32 @@ inline ssize_t write_content_chunked(Stream &strm,
size_t offset = 0;
auto data_available = true;
ssize_t total_written_length = 0;
ssize_t written_length = 0;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
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);
};
data_sink.done = [&](void) {
data_available = false;
written_length = strm.write("0\r\n\r\n");
};
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
};
while (data_available && !is_shutting_down()) {
ssize_t written_length = 0;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
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);
};
data_sink.done = [&](void) {
data_available = false;
written_length = strm.write("0\r\n\r\n");
};
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
};
content_provider(offset, 0, data_sink);
if (!content_provider(offset, 0, data_sink)) { return -1; }
if (written_length < 0) { return written_length; }
total_written_length += written_length;
}
return total_written_length;
}
@@ -2592,7 +2605,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;
});
}
@@ -2602,7 +2615,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);
@@ -2904,26 +2917,24 @@ inline void Response::set_content(std::string s, const char *content_type) {
set_header("Content-Type", content_type);
}
inline void Response::set_content_provider(
size_t in_length,
std::function<void(size_t offset, size_t length, DataSink &sink)> provider,
std::function<void()> resource_releaser) {
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) {
provider(offset, length, 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(
std::function<void(size_t offset, DataSink &sink)> provider,
std::function<void()> resource_releaser) {
content_length = 0;
content_provider = [provider](size_t offset, size_t, DataSink &sink) {
provider(offset, 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
@@ -3246,7 +3257,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");
}
@@ -3271,17 +3282,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,
@@ -3289,7 +3300,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");
@@ -3337,7 +3348,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;
@@ -3355,18 +3366,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;
}
@@ -3380,7 +3391,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;
}
@@ -3783,6 +3794,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)
@@ -3898,7 +3915,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_;
@@ -3909,12 +3926,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;
@@ -4062,7 +4079,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()) {
@@ -4106,15 +4123,22 @@ 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;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
auto written_length = strm.write(d, l);
written_length = strm.write(d, l);
offset += static_cast<size_t>(written_length);
};
data_sink.is_writable = [&](void) { return strm.is_writable(); };
data_sink.is_writable = [&](void) {
return strm.is_writable() && written_length >= 0;
};
while (offset < end_offset) {
req.content_provider(offset, end_offset - offset, data_sink);
if (!req.content_provider(offset, end_offset - offset, data_sink)) {
return false;
}
if (written_length < 0) { return false; }
}
}
} else {
@@ -4148,7 +4172,9 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
data_sink.is_writable = [&](void) { return true; };
while (offset < content_length) {
content_provider(offset, content_length - offset, data_sink);
if (!content_provider(offset, content_length - offset, data_sink)) {
return nullptr;
}
}
} else {
req.body = body;
@@ -4833,6 +4859,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)
@@ -5087,63 +5119,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 {
+60 -32
View File
@@ -651,15 +651,15 @@ TEST(YahooRedirectTest, Redirect) {
EXPECT_EQ(200, res->status);
}
TEST(YahooRedirectTestWithURL, Redirect) {
auto res = httplib::url::Get("http://yahoo.com");
TEST(YahooRedirectTest2, Redirect) {
httplib::Client2 cli("http://yahoo.com");
auto res = cli.Get("/");
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);
cli.set_follow_location(true);
res = cli.Get("/");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
}
@@ -673,14 +673,11 @@ 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);
TEST(HttpsToHttpRedirectTest2, Redirect) {
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);
@@ -899,20 +896,21 @@ protected:
.Get("/streamed-chunked",
[&](const Request & /*req*/, Response &res) {
res.set_chunked_content_provider(
[](uint64_t /*offset*/, DataSink &sink) {
ASSERT_TRUE(sink.is_writable());
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.write("123", 3);
sink.write("456", 3);
sink.write("789", 3);
sink.done();
return true;
});
})
.Get("/streamed-chunked2",
[&](const Request & /*req*/, Response &res) {
auto i = new int(0);
res.set_chunked_content_provider(
[i](uint64_t /*offset*/, DataSink &sink) {
ASSERT_TRUE(sink.is_writable());
[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;
@@ -920,14 +918,16 @@ protected:
case 3: sink.done(); break;
}
(*i)++;
return true;
},
[i] { delete i; });
})
.Get("/streamed",
[&](const Request & /*req*/, Response &res) {
res.set_content_provider(
6, [](uint64_t offset, uint64_t /*length*/, DataSink &sink) {
6, [](size_t offset, size_t /*length*/, DataSink &sink) {
sink.write(offset < 3 ? "a" : "b", 1);
return true;
});
})
.Get("/streamed-with-range",
@@ -935,25 +935,27 @@ protected:
auto data = new std::string("abcdefg");
res.set_content_provider(
data->size(),
[data](uint64_t offset, uint64_t length, DataSink &sink) {
ASSERT_TRUE(sink.is_writable());
[data](size_t offset, size_t length, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
size_t DATA_CHUNK_SIZE = 4;
const auto &d = *data;
auto out_len =
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
sink.write(&d[static_cast<size_t>(offset)], out_len);
return true;
},
[data] { delete data; });
})
.Get("/streamed-cancel",
[&](const Request & /*req*/, Response &res) {
res.set_content_provider(size_t(-1), [](uint64_t /*offset*/,
uint64_t /*length*/,
DataSink &sink) {
ASSERT_TRUE(sink.is_writable());
std::string data = "data_chunk";
sink.write(data.data(), data.size());
});
res.set_content_provider(
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());
return true;
});
})
.Get("/with-range",
[&](const Request & /*req*/, Response &res) {
@@ -1918,8 +1920,9 @@ TEST_F(ServerTest, PutWithContentProvider) {
auto res = cli_.Put(
"/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
ASSERT_TRUE(sink.is_writable());
EXPECT_TRUE(sink.is_writable());
sink.write("PUT", 3);
return true;
},
"text/plain");
@@ -1928,14 +1931,26 @@ TEST_F(ServerTest, PutWithContentProvider) {
EXPECT_EQ("PUT", res->body);
}
TEST_F(ServerTest, PostWithContentProviderAbort) {
auto res = cli_.Post(
"/post", 42,
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
return false;
},
"text/plain");
ASSERT_TRUE(res == nullptr);
}
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
cli_.set_compress(true);
auto res = cli_.Put(
"/put", 3,
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
ASSERT_TRUE(sink.is_writable());
EXPECT_TRUE(sink.is_writable());
sink.write("PUT", 3);
return true;
},
"text/plain");
@@ -1944,6 +1959,18 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
EXPECT_EQ("PUT", res->body);
}
TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
cli_.set_compress(true);
auto res = cli_.Post(
"/post", 42,
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
return false;
},
"text/plain");
ASSERT_TRUE(res == nullptr);
}
TEST_F(ServerTest, PutLargeFileWithGzip) {
cli_.set_compress(true);
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
@@ -2091,8 +2118,8 @@ TEST_F(ServerTest, KeepAlive) {
Get(requests, "/not-exist");
Post(requests, "/empty", "", "text/plain");
Post(
requests, "/empty", 0, [&](size_t, size_t, httplib::DataSink &) {},
"text/plain");
requests, "/empty", 0,
[&](size_t, size_t, httplib::DataSink &) { return true; }, "text/plain");
std::vector<Response> responses;
auto ret = cli_.send(requests, responses);
@@ -2440,6 +2467,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
sink.write(buffer, size);
std::this_thread::sleep_for(std::chrono::seconds(1));
return true;
});
});