Skip to content

Commit

Permalink
[coro_http][unit test]Test upload (#812)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos authored Nov 19, 2024
1 parent 19917f9 commit fba32ec
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 27 deletions.
45 changes: 30 additions & 15 deletions include/ylt/standalone/cinatra/coro_http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,14 +413,14 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
}

#ifdef CINATRA_ENABLE_GZIP
void gzip_decompress(std::string_view source, std::string &dest_buf,
std::span<char> &span, resp_data &data) {
void gzip_compress(std::string_view source, std::string &dest_buf,
std::span<char> &span, resp_data &data) {
if (enable_ws_deflate_ && is_server_support_ws_deflate_) {
if (cinatra::gzip_codec::deflate(source, dest_buf)) {
span = dest_buf;
}
else {
CINATRA_LOG_ERROR << "compuress data error, data: " << source;
CINATRA_LOG_ERROR << "compress data error, data: " << source;
data.net_err = std::make_error_code(std::errc::protocol_error);
data.status = 404;
}
Expand Down Expand Up @@ -448,7 +448,7 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
span = {source.data(), source.size()};
#ifdef CINATRA_ENABLE_GZIP
std::string dest_buf;
gzip_decompress({source.data(), source.size()}, dest_buf, span, data);
gzip_compress({source.data(), source.size()}, dest_buf, span, data);
#endif
co_await write_ws_frame(span, ws, op, data);
}
Expand All @@ -458,8 +458,8 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
span = {result.buf.data(), result.buf.size()};
#ifdef CINATRA_ENABLE_GZIP
std::string dest_buf;
gzip_decompress({result.buf.data(), result.buf.size()}, dest_buf, span,
data);
gzip_compress({result.buf.data(), result.buf.size()}, dest_buf, span,
data);
#endif
co_await write_ws_frame(span, ws, op, data, result.eof);

Expand Down Expand Up @@ -740,11 +740,11 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
detail::resize(file_data, (std::min)(max_single_part_size_, length));
coro_io::coro_file file{};
file.open(source, std::ios::in);
file.seek(offset, std::ios::cur);
if (!file.is_open()) {
ec = std::make_error_code(std::errc::bad_file_descriptor);
co_return;
}
file.seek(offset, std::ios::cur);
std::size_t size;
while (length > 0) {
if (std::tie(ec, size) = co_await file.async_read(
Expand Down Expand Up @@ -1065,6 +1065,17 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
co_return true;
}

void handle_upload_timeout_error(std::error_code &ec) {
#ifdef INJECT_FOR_HTTP_CLIENT_TEST
if (write_header_timeout_ || write_payload_timeout_ || read_timeout_) {
socket_->is_timeout_ = true;
}
#endif
if (socket_->is_timeout_) {
ec = std::make_error_code(std::errc::timed_out);
}
}

template <upload_type_t upload_type, typename S, typename Source>
async_simple::coro::Lazy<resp_data> async_upload_impl(
S uri, http_method method, Source source /* file */,
Expand Down Expand Up @@ -1122,9 +1133,7 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
auto time_guard = timer_guard(this, req_timeout_duration_, "request timer");
std::tie(ec, size) = co_await async_write(asio::buffer(header_str));
if (ec) {
if (socket_->is_timeout_) {
ec = std::make_error_code(std::errc::timed_out);
}
handle_upload_timeout_error(ec);
co_return resp_data{ec, 404};
}

Expand Down Expand Up @@ -1184,16 +1193,14 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
}
}
if (ec) {
if (socket_->is_timeout_) {
ec = std::make_error_code(std::errc::timed_out);
}
handle_upload_timeout_error(ec);
co_return resp_data{ec, 404};
}

data = co_await handle_read(ec, size, is_keep_alive, std::move(ctx),
http_method::POST);
if (ec && socket_->is_timeout_) {
ec = std::make_error_code(std::errc::timed_out);
if (ec) {
handle_upload_timeout_error(ec);
}
handle_result(data, ec, is_keep_alive);
co_return data;
Expand Down Expand Up @@ -2253,6 +2260,11 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
template <typename AsioBuffer>
async_simple::coro::Lazy<std::pair<std::error_code, size_t>> async_read_until(
AsioBuffer &buffer, asio::string_view delim) noexcept {
#ifdef INJECT_FOR_HTTP_CLIENT_TEST
if (read_failed_forever_) {
return async_read_failed();
}
#endif
#ifdef CINATRA_ENABLE_SSL
if (has_init_ssl_) {
return coro_io::async_read_until(*socket_->ssl_stream_, buffer, delim);
Expand Down Expand Up @@ -2370,6 +2382,9 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
bool connect_timeout_forever_ = false;
bool parse_failed_forever_ = false;
bool read_failed_forever_ = false;
bool write_header_timeout_ = false;
bool write_payload_timeout_ = false;
bool read_timeout_ = false;
#endif
};

Expand Down
182 changes: 170 additions & 12 deletions src/coro_http/tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,123 @@ TEST_CASE("test coro_http_client multipart upload") {
CHECK(result.status == 200);
}

#ifdef CINATRA_ENABLE_SSL
TEST_CASE("test ssl upload") {
coro_http_server server(1, 8091);
#ifdef CINATRA_ENABLE_SSL
server.init_ssl("../openssl_files/server.crt", "../openssl_files/server.key",
"test");
#endif
server.set_http_handler<cinatra::PUT>(
"/upload",
[](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
std::string_view filename = req.get_header_value("filename");
uint64_t sz;
auto oldpath = fs::current_path().append(filename);
std::string newpath = fs::current_path()
.append("server_" + std::string{filename})
.string();
std::ofstream file(newpath, std::ios::binary);
CHECK(file.is_open());
file.write(req.get_body().data(), req.get_body().size());
file.flush();
file.close();

size_t offset = 0;
std::string offset_s = std::string{req.get_header_value("offset")};
if (!offset_s.empty()) {
offset = stoull(offset_s);
}

std::string filesize = std::string{req.get_header_value("filesize")};

if (!filesize.empty()) {
sz = stoull(filesize);
}
else {
sz = std::filesystem::file_size(oldpath);
sz -= offset;
}

CHECK(!filename.empty());
CHECK(sz == std::filesystem::file_size(newpath));
std::ifstream ifs(oldpath);
ifs.seekg(offset, std::ios::cur);
std::string str;
str.resize(sz);
ifs.read(str.data(), sz);
CHECK(str == req.get_body());
resp.set_status_and_content(status_type::ok, std::string(filename));
co_return;
});
server.async_start();

std::string filename = "test_ssl_upload.txt";
create_file(filename, 10);
std::string uri = "https://127.0.0.1:8091/upload";

{
coro_http_client client{};
bool r = client.init_ssl();
CHECK(r);
client.add_header("filename", filename);
auto lazy = client.async_upload(uri, http_method::PUT, filename);
auto result = async_simple::coro::syncAwait(lazy);
CHECK(result.status == 200);
}

{
coro_http_client client{};
client.add_header("filename", filename);
auto lazy = client.async_upload(uri, http_method::PUT, filename);
auto result = async_simple::coro::syncAwait(lazy);
CHECK(result.status == 200);
}

cinatra::coro_http_server server1(1, 9002);
server1.init_ssl("../openssl_files/server.crt", "../openssl_files/server.key",
"test");
server1.set_http_handler<cinatra::GET, cinatra::PUT>(
"/chunked",
[](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
assert(req.get_content_type() == content_type::chunked);
chunked_result result{};
std::string content;

while (true) {
result = co_await req.get_conn()->read_chunked();
if (result.ec) {
co_return;
}
if (result.eof) {
break;
}

content.append(result.data);
}

std::cout << "content size: " << content.size() << "\n";
std::cout << content << "\n";
resp.set_format_type(format_type::chunked);
resp.set_status_and_content(status_type::ok, "chunked ok");
});
server1.async_start();

uri = "https://127.0.0.1:9002/chunked";
{
coro_http_client client{};
bool r = client.init_ssl();
CHECK(r);
client.add_header("filename", filename);
auto lazy = client.async_upload_chunked(uri, http_method::PUT, filename);
auto result = async_simple::coro::syncAwait(lazy);
CHECK(result.status == 200);
}
}
#endif

TEST_CASE("test coro_http_client upload") {
auto test_upload_by_file_path = [](std::string filename,
std::size_t offset = 0,
Expand Down Expand Up @@ -1698,6 +1815,14 @@ TEST_CASE("test coro_http_client upload") {
test_upload_by_stream(filename, offset, r_size, true);
}
}
{
filename = "some_test_file.txt";
bool r = create_file(filename, 10);
CHECK(r);
test_upload_by_file_path(filename, 20, SIZE_MAX, true);
std::error_code ec{};
fs::remove(filename, ec);
}
}

TEST_CASE("test coro_http_client chunked upload and download") {
Expand Down Expand Up @@ -1996,20 +2121,53 @@ TEST_CASE("test inject failed") {
server.async_start();

std::string uri = "http://127.0.0.1:8090";
{
coro_http_client client1{};
client1.read_failed_forever_ = true;
ret = client1.get(uri);
CHECK(ret.status != 200);

coro_http_client client1{};
client1.read_failed_forever_ = true;
ret = client1.get(uri);
CHECK(ret.status != 200);
client1.close();
std::string out;
out.resize(2024);
ret = async_simple::coro::syncAwait(
client1.async_request(uri, http_method::GET, req_context<>{}, {},
std::span<char>{out.data(), out.size()}));
CHECK(ret.status != 200);
client1.read_failed_forever_ = false;
}

client1.close();
std::string out;
out.resize(2024);
ret = async_simple::coro::syncAwait(
client1.async_request(uri, http_method::GET, req_context<>{}, {},
std::span<char>{out.data(), out.size()}));
CHECK(ret.status != 200);
client1.read_failed_forever_ = false;
{
coro_http_client client1{};
client1.add_str_part("hello", "test");
client1.write_failed_forever_ = true;
client1.write_header_timeout_ = true;
ret = async_simple::coro::syncAwait(
client1.async_upload_multipart("http://baidu.com"));
CHECK(ret.status != 200);
client1.write_failed_forever_ = false;
client1.write_header_timeout_ = false;
}

{
coro_http_client client1{};
client1.add_str_part("hello", "test");
client1.write_failed_forever_ = true;
client1.write_payload_timeout_ = true;
ret = async_simple::coro::syncAwait(
client1.async_upload_multipart("http://baidu.com"));
CHECK(ret.status != 200);
}

{
coro_http_client client1{};
client1.add_str_part("hello", "test");
client1.read_failed_forever_ = true;
client1.read_timeout_ = true;
ret = async_simple::coro::syncAwait(
client1.async_upload_multipart("http://baidu.com"));
CHECK(ret.status != 200);
}
}
#endif

Expand Down

0 comments on commit fba32ec

Please sign in to comment.