Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[coro_http][unit test]Test upload #812

Merged
merged 7 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading