mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 06:26:02 -07:00
Added chunked content provider support on client
This commit is contained in:
parent
cee062d4c9
commit
c2afc5ca44
2 changed files with 318 additions and 129 deletions
391
httplib.h
391
httplib.h
|
@ -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 ¶ms);
|
||||
Result Post(const char *path, const Headers &headers, const Params ¶ms);
|
||||
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 ¶ms);
|
||||
Result Put(const char *path, const Headers &headers, const Params ¶ms);
|
||||
|
||||
|
@ -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 ¶ms);
|
||||
Result Post(const char *path, const Headers &headers, const Params ¶ms);
|
||||
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 ¶ms);
|
||||
Result Put(const char *path, const Headers &headers, const Params ¶ms);
|
||||
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 ¶ms) {
|
||||
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 ¶ms) {
|
||||
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) {
|
||||
|
|
56
test/test.cc
56
test/test.cc
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue