From 9f92bbfe22228a450ed42b74b797b336d9581766 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 8 Sep 2023 14:07:28 +0800 Subject: [PATCH] support iostream --- include/cinatra/coro_http_client.hpp | 75 ++++++++++++++++++++++----- lang/coro_http_client_introduction.md | 6 +-- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/include/cinatra/coro_http_client.hpp b/include/cinatra/coro_http_client.hpp index 1efdffc1..66aec59f 100644 --- a/include/cinatra/coro_http_client.hpp +++ b/include/cinatra/coro_http_client.hpp @@ -52,6 +52,30 @@ inline ClientInjectAction inject_write_failed = ClientInjectAction::none; inline ClientInjectAction inject_read_failed = ClientInjectAction::none; #endif +template +struct is_stream : std::false_type {}; + +template +struct is_stream< + T, std::void_t().read(nullptr, 0), + std::declval().async_read(nullptr, 0))>> + : std::true_type {}; + +template +constexpr bool is_stream_v = is_stream::value; + +template +struct is_smart_ptr : std::false_type {}; + +template +struct is_smart_ptr< + T, std::void_t().get(), *std::declval(), + is_stream_v)>> + : std::true_type {}; + +template +constexpr bool is_stream_ptr_v = is_smart_ptr::value; + struct http_header; struct resp_data { @@ -703,9 +727,9 @@ class coro_http_client { std::string_view get_port() { return port_; } - template + template async_simple::coro::Lazy 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 headers = {}) { std::shared_ptr guard(nullptr, [this](auto) { @@ -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; + 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"); @@ -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( - 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( + 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( + 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}; + } } } diff --git a/lang/coro_http_client_introduction.md b/lang/coro_http_client_introduction.md index 7168063d..fb779cc4 100644 --- a/lang/coro_http_client_introduction.md +++ b/lang/coro_http_client_introduction.md @@ -306,13 +306,13 @@ async_simple::coro::Lazy async_trace(std::string uri); ## chunked 格式上传 ```c++ -template +template async_simple::coro::Lazy 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 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配置项去设置。