Added chunked content provider support on client

This commit is contained in:
yhirose 2020-11-21 08:17:00 -05:00
parent cee062d4c9
commit c2afc5ca44
2 changed files with 318 additions and 129 deletions

391
httplib.h
View file

@ -269,9 +269,11 @@ make_unique(std::size_t n) {
struct ci {
bool operator()(const std::string &s1, const std::string &s2) const {
return std::lexicographical_compare(
s1.begin(), s1.end(), s2.begin(), s2.end(),
[](unsigned char c1, unsigned char c2) { return ::tolower(c1) < ::tolower(c2); });
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),
s2.end(),
[](unsigned char c1, unsigned char c2) {
return ::tolower(c1) < ::tolower(c2);
});
}
};
@ -388,13 +390,6 @@ struct Request {
Match matches;
// for client
size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
ResponseHandler response_handler;
ContentReceiverWithProgress content_receiver;
size_t content_length = 0;
ContentProvider content_provider;
Progress progress;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
const SSL *ssl;
#endif
@ -417,6 +412,13 @@ struct Request {
MultipartFormData get_file_value(const char *key) const;
// private members...
size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
ResponseHandler response_handler_;
ContentReceiverWithProgress content_receiver_;
size_t content_length_ = 0;
ContentProvider content_provider_;
bool is_chunked_content_provider_ = false;
Progress progress_;
size_t authorization_count_ = 0;
};
@ -467,7 +469,7 @@ struct Response {
size_t content_length_ = 0;
ContentProvider content_provider_;
std::function<void()> content_provider_resource_releaser_;
bool is_chunked_content_provider = false;
bool is_chunked_content_provider_ = false;
};
class Stream {
@ -819,8 +821,13 @@ public:
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, ContentProviderWithoutLength 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 Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type);
Result Post(const char *path, const Params &params);
Result Post(const char *path, const Headers &headers, const Params &params);
Result Post(const char *path, const MultipartFormDataItems &items);
@ -836,8 +843,13 @@ public:
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, ContentProviderWithoutLength 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 Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type);
Result Put(const char *path, const Params &params);
Result Put(const char *path, const Headers &headers, const Params &params);
@ -847,8 +859,13 @@ public:
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, ContentProviderWithoutLength 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);
Result Patch(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type);
Result Delete(const char *path);
Result Delete(const char *path, const std::string &body,
@ -919,6 +936,7 @@ protected:
bool process_request(Stream &strm, const Request &req, Response &res,
bool close_connection);
bool write_content_with_provider(Stream &strm, const Request &req);
Error get_last_error() const;
void copy_settings(const ClientImpl &rhs);
@ -997,7 +1015,9 @@ private:
std::unique_ptr<Response> send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider, const char *content_type);
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const char *content_type);
virtual bool process_socket(Socket &socket,
std::function<bool(Stream &strm)> callback);
@ -1056,8 +1076,13 @@ public:
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, ContentProviderWithoutLength 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 Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type);
Result Post(const char *path, const Params &params);
Result Post(const char *path, const Headers &headers, const Params &params);
Result Post(const char *path, const MultipartFormDataItems &items);
@ -1072,8 +1097,13 @@ public:
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, ContentProviderWithoutLength 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 Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type);
Result Put(const char *path, const Params &params);
Result Put(const char *path, const Headers &headers, const Params &params);
Result Patch(const char *path, const std::string &body,
@ -1082,8 +1112,13 @@ public:
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, ContentProviderWithoutLength 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);
Result Patch(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type);
Result Delete(const char *path);
Result Delete(const char *path, const std::string &body,
@ -2755,17 +2790,20 @@ inline bool write_data(Stream &strm, const char *d, size_t l) {
}
template <typename T>
inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
size_t offset, size_t length, T is_shutting_down) {
size_t begin_offset = offset;
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
size_t offset, size_t length, T is_shutting_down,
Error &error) {
size_t end_offset = offset + length;
auto ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
if (ok) {
offset += l;
if (!write_data(strm, d, l)) { ok = false; }
if (write_data(strm, d, l)) {
offset += l;
} else {
ok = false;
}
}
};
@ -2773,18 +2811,33 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
while (offset < end_offset && !is_shutting_down()) {
if (!content_provider(offset, end_offset - offset, data_sink)) {
return -1;
error = Error::Canceled;
return false;
}
if (!ok) {
error = Error::Write;
return false;
}
if (!ok) { return -1; }
}
return static_cast<ssize_t>(offset - begin_offset);
error = Error::Success;
return true;
}
template <typename T>
inline ssize_t write_content_without_length(Stream &strm,
ContentProvider content_provider,
T is_shutting_down) {
inline bool write_content(Stream &strm, const ContentProvider &content_provider,
size_t offset, size_t length,
const T &is_shutting_down) {
Error error;
return write_content(strm, content_provider, offset, length, is_shutting_down,
error);
}
template <typename T>
inline bool
write_content_without_length(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down) {
size_t offset = 0;
auto data_available = true;
auto ok = true;
@ -2802,20 +2855,18 @@ inline ssize_t write_content_without_length(Stream &strm,
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
while (data_available && !is_shutting_down()) {
if (!content_provider(offset, 0, data_sink)) { return -1; }
if (!ok) { return -1; }
if (!content_provider(offset, 0, data_sink)) { return false; }
if (!ok) { return false; }
}
return static_cast<ssize_t>(offset);
return true;
}
template <typename T, typename U>
inline ssize_t write_content_chunked(Stream &strm,
ContentProvider content_provider,
T is_shutting_down, U &compressor) {
inline bool
write_content_chunked(Stream &strm, const ContentProvider &content_provider,
const T &is_shutting_down, U &compressor, Error &error) {
size_t offset = 0;
auto data_available = true;
ssize_t total_written_length = 0;
auto ok = true;
DataSink data_sink;
@ -2838,9 +2889,7 @@ inline ssize_t write_content_chunked(Stream &strm,
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (write_data(strm, chunk.data(), chunk.size())) {
total_written_length += chunk.size();
} else {
if (!write_data(strm, chunk.data(), chunk.size())) {
ok = false;
return;
}
@ -2865,18 +2914,14 @@ inline ssize_t write_content_chunked(Stream &strm,
if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
if (write_data(strm, chunk.data(), chunk.size())) {
total_written_length += chunk.size();
} else {
if (!write_data(strm, chunk.data(), chunk.size())) {
ok = false;
return;
}
}
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 {
if (!write_data(strm, done_marker.data(), done_marker.size())) {
ok = false;
}
};
@ -2884,11 +2929,27 @@ inline ssize_t write_content_chunked(Stream &strm,
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
while (data_available && !is_shutting_down()) {
if (!content_provider(offset, 0, data_sink)) { return -1; }
if (!ok) { return -1; }
if (!content_provider(offset, 0, data_sink)) {
error = Error::Canceled;
return false;
}
if (!ok) {
error = Error::Write;
return false;
}
}
return total_written_length;
error = Error::Success;
return true;
}
template <typename T, typename U>
inline bool write_content_chunked(Stream &strm,
const ContentProvider &content_provider,
const T &is_shutting_down, U &compressor) {
Error error;
return write_content_chunked(strm, content_provider, is_shutting_down,
compressor, error);
}
template <typename T>
@ -2896,7 +2957,7 @@ inline bool redirect(T &cli, const Request &req, Response &res,
const std::string &path) {
Request new_req = req;
new_req.path = path;
new_req.redirect_count -= 1;
new_req.redirect_count_ -= 1;
if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
new_req.method = "GET";
@ -3291,14 +3352,14 @@ inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
Response &res,
const std::string &boundary,
const std::string &content_type,
T is_shutting_down) {
const T &is_shutting_down) {
return process_multipart_ranges_data(
req, res, boundary, content_type,
[&](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,
is_shutting_down) >= 0;
is_shutting_down);
});
}
@ -3686,7 +3747,7 @@ Response::set_content_provider(size_t in_length, const char *content_type,
content_length_ = in_length;
content_provider_ = std::move(provider);
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider = false;
is_chunked_content_provider_ = false;
}
inline void
@ -3697,7 +3758,7 @@ Response::set_content_provider(const char *content_type,
content_length_ = 0;
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider = false;
is_chunked_content_provider_ = false;
}
inline void Response::set_chunked_content_provider(
@ -3707,7 +3768,7 @@ inline void Response::set_chunked_content_provider(
content_length_ = 0;
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider = true;
is_chunked_content_provider_ = true;
}
// Rstream implementation
@ -4131,27 +4192,21 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
if (res.content_length_ > 0) {
if (req.ranges.empty()) {
if (detail::write_content(strm, res.content_provider_, 0,
res.content_length_, is_shutting_down) < 0) {
return false;
}
return detail::write_content(strm, res.content_provider_, 0,
res.content_length_, is_shutting_down);
} else if (req.ranges.size() == 1) {
auto offsets =
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,
is_shutting_down) < 0) {
return false;
}
return detail::write_content(strm, res.content_provider_, offset, length,
is_shutting_down);
} else {
if (!detail::write_multipart_ranges_data(
strm, req, res, boundary, content_type, is_shutting_down)) {
return false;
}
return detail::write_multipart_ranges_data(
strm, req, res, boundary, content_type, is_shutting_down);
}
} else {
if (res.is_chunked_content_provider) {
if (res.is_chunked_content_provider_) {
auto type = detail::encoding_type(req, res);
std::unique_ptr<detail::compressor> compressor;
@ -4168,15 +4223,11 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
}
assert(compressor != nullptr);
if (detail::write_content_chunked(strm, res.content_provider_,
is_shutting_down, *compressor) < 0) {
return false;
}
return detail::write_content_chunked(strm, res.content_provider_,
is_shutting_down, *compressor);
} else {
if (detail::write_content_without_length(strm, res.content_provider_,
is_shutting_down) < 0) {
return false;
}
return detail::write_content_without_length(strm, res.content_provider_,
is_shutting_down);
}
}
return true;
@ -4531,7 +4582,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
res.set_header("Content-Length", std::to_string(length));
} else {
if (res.content_provider_) {
if (res.is_chunked_content_provider) {
if (res.is_chunked_content_provider_) {
res.set_header("Transfer-Encoding", "chunked");
if (type == detail::EncodingType::Gzip) {
res.set_header("Content-Encoding", "gzip");
@ -4943,7 +4994,7 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
}
inline bool ClientImpl::redirect(const Request &req, Response &res) {
if (req.redirect_count == 0) {
if (req.redirect_count_ == 0) {
error_ = Error::ExceedRedirectCount;
return false;
}
@ -4998,6 +5049,30 @@ inline bool ClientImpl::redirect(const Request &req, Response &res) {
}
}
inline bool ClientImpl::write_content_with_provider(Stream &strm,
const Request &req) {
auto is_shutting_down = []() { return false; };
if (req.is_chunked_content_provider_) {
// TODO: Brotli suport
std::unique_ptr<detail::compressor> compressor;
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) {
compressor = detail::make_unique<detail::gzip_compressor>();
} else
#endif
{
compressor = detail::make_unique<detail::nocompressor>();
}
return detail::write_content_chunked(strm, req.content_provider_,
is_shutting_down, *compressor, error_);
} else {
return detail::write_content(strm, req.content_provider_, 0,
req.content_length_, is_shutting_down, error_);
}
} // namespace httplib
inline bool ClientImpl::write_request(Stream &strm, const Request &req,
bool close_connection) {
detail::BufferStream bstrm;
@ -5034,9 +5109,11 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
}
if (req.body.empty()) {
if (req.content_provider) {
auto length = std::to_string(req.content_length);
headers.emplace("Content-Length", length);
if (req.content_provider_) {
if (!req.is_chunked_content_provider_) {
auto length = std::to_string(req.content_length_);
headers.emplace("Content-Length", length);
}
} else {
if (req.method == "POST" || req.method == "PUT" ||
req.method == "PATCH") {
@ -5086,35 +5163,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
// Body
if (req.body.empty()) {
if (req.content_provider) {
size_t offset = 0;
size_t end_offset = req.content_length;
bool ok = true;
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
if (ok) {
if (detail::write_data(strm, d, l)) {
offset += l;
} else {
ok = false;
}
}
};
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
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;
}
}
}
return write_content_with_provider(strm, req);
} else {
return detail::write_data(strm, req.body.data(), req.body.size());
}
@ -5125,7 +5174,9 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
inline std::unique_ptr<Response> 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) {
ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const char *content_type) {
Request req;
req.method = method;
@ -5136,14 +5187,19 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
if (content_type) { req.headers.emplace("Content-Type", content_type); }
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) {
if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); }
#endif
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_ && !content_provider_without_length) {
// TODO: Brotli support
detail::gzip_compressor compressor;
if (content_provider) {
auto ok = true;
size_t offset = 0;
DataSink data_sink;
data_sink.write = [&](const char *data, size_t data_len) {
if (ok) {
auto last = offset + data_len == content_length;
@ -5161,6 +5217,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
}
}
};
data_sink.is_writable = [&](void) { return ok && true; };
while (ok && offset < content_length) {
@ -5178,14 +5235,19 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
return nullptr;
}
}
req.headers.emplace("Content-Encoding", "gzip");
} else
#endif
{
if (content_provider) {
req.content_length = content_length;
req.content_provider = std::move(content_provider);
req.content_length_ = content_length;
req.content_provider_ = std::move(content_provider);
req.is_chunked_content_provider_ = false;
} else if (content_provider_without_length) {
req.content_length_ = 0;
req.content_provider_ = detail::ContentProviderAdapter(
std::move(content_provider_without_length));
req.is_chunked_content_provider_ = true;
req.headers.emplace("Transfer-Encoding", "chunked");
} else {
req.body = body;
}
@ -5208,8 +5270,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
return false;
}
if (req.response_handler) {
if (!req.response_handler(res)) {
if (req.response_handler_) {
if (!req.response_handler_(res)) {
error_ = Error::Canceled;
return false;
}
@ -5218,10 +5280,10 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
// Body
if (req.method != "HEAD" && req.method != "CONNECT") {
auto out =
req.content_receiver
req.content_receiver_
? static_cast<ContentReceiverWithProgress>(
[&](const char *buf, size_t n, uint64_t off, uint64_t len) {
auto ret = req.content_receiver(buf, n, off, len);
auto ret = req.content_receiver_(buf, n, off, len);
if (!ret) { error_ = Error::Canceled; }
return ret;
})
@ -5236,8 +5298,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
});
auto progress = [&](uint64_t current, uint64_t total) {
if (!req.progress) { return true; }
auto ret = req.progress(current, total);
if (!req.progress_) { return true; }
auto ret = req.progress_(current, total);
if (!ret) { error_ = Error::Canceled; }
return ret;
};
@ -5291,7 +5353,7 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
req.path = path;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.progress = std::move(progress);
req.progress_ = std::move(progress);
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
@ -5353,13 +5415,13 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
req.path = path;
req.headers = default_headers_;
req.headers.insert(headers.begin(), headers.end());
req.response_handler = std::move(response_handler);
req.content_receiver =
req.response_handler_ = std::move(response_handler);
req.content_receiver_ =
[content_receiver](const char *data, size_t data_length,
uint64_t /*offset*/, uint64_t /*total_length*/) {
return content_receiver(data, data_length);
};
req.progress = std::move(progress);
req.progress_ = std::move(progress);
auto res = detail::make_unique<Response>();
auto ret = send(req, *res);
@ -5395,7 +5457,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
content_type);
nullptr, content_type);
return Result{std::move(ret), get_last_error()};
}
@ -5410,13 +5472,28 @@ inline Result ClientImpl::Post(const char *path, size_t content_length,
content_type);
}
inline Result ClientImpl::Post(const char *path,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return Post(path, Headers(), std::move(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) {
auto ret = send_with_content_provider(
"POST", path, headers, std::string(), content_length,
std::move(content_provider), content_type);
std::move(content_provider), nullptr, content_type);
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Post(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
auto ret = send_with_content_provider("POST", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
return Result{std::move(ret), get_last_error()};
}
@ -5481,7 +5558,7 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
content_type);
nullptr, content_type);
return Result{std::move(ret), get_last_error()};
}
@ -5492,13 +5569,28 @@ inline Result ClientImpl::Put(const char *path, size_t content_length,
content_type);
}
inline Result ClientImpl::Put(const char *path,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return Put(path, Headers(), std::move(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) {
auto ret = send_with_content_provider(
"PUT", path, headers, std::string(), content_length,
std::move(content_provider), content_type);
std::move(content_provider), nullptr, content_type);
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Put(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
auto ret = send_with_content_provider("PUT", path, headers, std::string(), 0,
nullptr, std::move(content_provider),
content_type);
return Result{std::move(ret), get_last_error()};
}
@ -5521,7 +5613,7 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
const std::string &body,
const char *content_type) {
auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
nullptr, content_type);
nullptr, nullptr, content_type);
return Result{std::move(ret), get_last_error()};
}
@ -5532,13 +5624,28 @@ inline Result ClientImpl::Patch(const char *path, size_t content_length,
content_type);
}
inline Result ClientImpl::Patch(const char *path,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return Patch(path, Headers(), std::move(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) {
auto ret = send_with_content_provider(
"PATCH", path, headers, std::string(), content_length,
std::move(content_provider), content_type);
std::move(content_provider), nullptr, content_type);
return Result{std::move(ret), get_last_error()};
}
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
auto ret = send_with_content_provider("PATCH", path, headers, std::string(),
0, nullptr, std::move(content_provider),
content_type);
return Result{std::move(ret), get_last_error()};
}
@ -6500,6 +6607,11 @@ inline Result Client::Post(const char *path, size_t content_length,
return cli_->Post(path, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Post(const char *path,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return cli_->Post(path, std::move(content_provider), content_type);
}
inline Result Client::Post(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
@ -6507,6 +6619,11 @@ inline Result Client::Post(const char *path, const Headers &headers,
return cli_->Post(path, headers, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Post(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return cli_->Post(path, headers, std::move(content_provider), content_type);
}
inline Result Client::Post(const char *path, const Params &params) {
return cli_->Post(path, params);
}
@ -6542,6 +6659,11 @@ inline Result Client::Put(const char *path, size_t content_length,
return cli_->Put(path, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Put(const char *path,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return cli_->Put(path, std::move(content_provider), content_type);
}
inline Result Client::Put(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
@ -6549,6 +6671,11 @@ inline Result Client::Put(const char *path, const Headers &headers,
return cli_->Put(path, headers, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Put(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return cli_->Put(path, headers, std::move(content_provider), content_type);
}
inline Result Client::Put(const char *path, const Params &params) {
return cli_->Put(path, params);
}
@ -6570,6 +6697,11 @@ inline Result Client::Patch(const char *path, size_t content_length,
return cli_->Patch(path, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Patch(const char *path,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return cli_->Patch(path, std::move(content_provider), content_type);
}
inline Result Client::Patch(const char *path, const Headers &headers,
size_t content_length,
ContentProvider content_provider,
@ -6577,6 +6709,11 @@ inline Result Client::Patch(const char *path, const Headers &headers,
return cli_->Patch(path, headers, content_length, std::move(content_provider),
content_type);
}
inline Result Client::Patch(const char *path, const Headers &headers,
ContentProviderWithoutLength content_provider,
const char *content_type) {
return cli_->Patch(path, headers, std::move(content_provider), 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) {

View file

@ -2217,6 +2217,31 @@ TEST_F(ServerTest, PostWithContentProviderAbort) {
EXPECT_EQ(Error::Canceled, res.error());
}
TEST_F(ServerTest, PutWithContentProviderWithoutLength) {
auto res = cli_.Put(
"/put",
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
sink.done();
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("PUT", res->body);
}
TEST_F(ServerTest, PostWithContentProviderWithoutLengthAbort) {
auto res = cli_.Post(
"/post", [](size_t /*offset*/, DataSink & /*sink*/) { return false; },
"text/plain");
ASSERT_TRUE(!res);
EXPECT_EQ(Error::Canceled, res.error());
}
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
cli_.set_compress(true);
@ -2247,6 +2272,33 @@ TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
EXPECT_EQ(Error::Canceled, res.error());
}
TEST_F(ServerTest, PutWithContentProviderWithoutLengthWithGzip) {
cli_.set_compress(true);
auto res = cli_.Put(
"/put",
[](size_t /*offset*/, DataSink &sink) {
EXPECT_TRUE(sink.is_writable());
sink.os << "PUT";
sink.done();
return true;
},
"text/plain");
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);
EXPECT_EQ("PUT", res->body);
}
TEST_F(ServerTest, PostWithContentProviderWithoutLengthWithGzipAbort) {
cli_.set_compress(true);
auto res = cli_.Post(
"/post", [](size_t /*offset*/, DataSink & /*sink*/) { return false; },
"text/plain");
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");
@ -3627,8 +3679,8 @@ TEST(YahooRedirectTest3, NewResultInterface) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
Client cli("https://cdnjs.cloudflare.com");
auto res = cli.Get("/ajax/libs/jquery/3.5.1/jquery.js",
{{"Accept-Encoding", "br"}});
auto res =
cli.Get("/ajax/libs/jquery/3.5.1/jquery.js", {{"Accept-Encoding", "br"}});
ASSERT_TRUE(res);
EXPECT_EQ(200, res->status);