Skip to content

Commit

Permalink
support iostream (qicosmos#407)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos authored Sep 8, 2023
1 parent b6a6bfe commit de6e9e4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 17 deletions.
75 changes: 61 additions & 14 deletions include/cinatra/coro_http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ inline ClientInjectAction inject_write_failed = ClientInjectAction::none;
inline ClientInjectAction inject_read_failed = ClientInjectAction::none;
#endif

template <class, class = void>
struct is_stream : std::false_type {};

template <class T>
struct is_stream<
T, std::void_t<decltype(std::declval<T>().read(nullptr, 0),
std::declval<T>().async_read(nullptr, 0))>>
: std::true_type {};

template <class T>
constexpr bool is_stream_v = is_stream<T>::value;

template <class, class = void>
struct is_smart_ptr : std::false_type {};

template <class T>
struct is_smart_ptr<
T, std::void_t<decltype(std::declval<T>().get(), *std::declval<T>(),
is_stream_v<typename T::element_type>)>>
: std::true_type {};

template <class T>
constexpr bool is_stream_ptr_v = is_smart_ptr<T>::value;

struct http_header;

struct resp_data {
Expand Down Expand Up @@ -703,9 +727,9 @@ class coro_http_client {

std::string_view get_port() { return port_; }

template <typename S, typename String>
template <typename S, typename File>
async_simple::coro::Lazy<resp_data> async_upload_chunked(
S uri, http_method method, String filename,
S uri, http_method method, File file,
req_content_type content_type = req_content_type::text,
std::unordered_map<std::string, std::string> headers = {}) {
std::shared_ptr<int> guard(nullptr, [this](auto) {
Expand All @@ -721,9 +745,18 @@ class coro_http_client {
co_return resp_data{{}, 404};
}

if (!std::filesystem::exists(filename)) {
co_return resp_data{
std::make_error_code(std::errc::no_such_file_or_directory), 404};
constexpr bool is_stream_file = is_stream_ptr_v<File>;
if constexpr (is_stream_file) {
if (!file) {
co_return resp_data{
std::make_error_code(std::errc::no_such_file_or_directory), 404};
}
}
else {
if (!std::filesystem::exists(file)) {
co_return resp_data{
std::make_error_code(std::errc::no_such_file_or_directory), 404};
}
}

add_header("Transfer-Encoding", "chunked");
Expand All @@ -750,17 +783,31 @@ class coro_http_client {
co_return resp_data{ec, 404};
}

coro_io::coro_file file(filename, coro_io::open_mode::read);
std::string file_data;
file_data.resize(max_single_part_size_);
detail::resize(file_data, max_single_part_size_);
std::string chunk_size_str;
while (!file.eof()) {
auto [rd_ec, rd_size] =
co_await file.async_read(file_data.data(), file_data.size());
auto bufs = cinatra::to_chunked_buffers<asio::const_buffer>(
file_data.data(), rd_size, chunk_size_str, file.eof());
if (std::tie(ec, size) = co_await async_write(bufs); ec) {
co_return resp_data{ec, 404};

if constexpr (is_stream_file) {
while (!file->eof()) {
size_t rd_size =
file->read(file_data.data(), file_data.size()).gcount();
auto bufs = cinatra::to_chunked_buffers<asio::const_buffer>(
file_data.data(), rd_size, chunk_size_str, file->eof());
if (std::tie(ec, size) = co_await async_write(bufs); ec) {
co_return resp_data{ec, 404};
}
}
}
else {
coro_io::coro_file coro_file(file, coro_io::open_mode::read);
while (!coro_file.eof()) {
auto [rd_ec, rd_size] =
co_await coro_file.async_read(file_data.data(), file_data.size());
auto bufs = cinatra::to_chunked_buffers<asio::const_buffer>(
file_data.data(), rd_size, chunk_size_str, coro_file.eof());
if (std::tie(ec, size) = co_await async_write(bufs); ec) {
co_return resp_data{ec, 404};
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions lang/coro_http_client_introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,13 +306,13 @@ async_simple::coro::Lazy<resp_data> async_trace(std::string uri);
## chunked 格式上传
```c++
template <typename S, typename String>
template <typename S, typename File>
async_simple::coro::Lazy<resp_data> async_upload_chunked(
S uri, http_method method, String filename,
S uri, http_method method, File file,
req_content_type content_type = req_content_type::text,
std::unordered_map<std::string, std::string> headers = {});
```
method 一般是POST 或者PUT,filename 是带路径的文件名,content_type 文件的类型,headers 是请求头,这些参数填好之后,coro_http_client 会自动将文件分块上传到服务器,直到全部上传完成之后才co_return,中间上传出错也会返回。
method 一般是POST 或者PUT,file 可以是带路径的文件名,也可以是一个iostream 流,content_type 文件的类型,headers 是请求头,这些参数填好之后,coro_http_client 会自动将文件分块上传到服务器,直到全部上传完成之后才co_return,中间上传出错也会返回。

chunked 每块的大小默认为1MB,如果希望修改分块大小可以通过set_max_single_part_size 接口去设置大小,或者通过config 里面的max_single_part_size配置项去设置。

Expand Down

0 comments on commit de6e9e4

Please sign in to comment.