Skip to content

Commit

Permalink
feat: add compress api for coro client (qicosmos#597)
Browse files Browse the repository at this point in the history
  • Loading branch information
helintongh authored Jun 12, 2024
1 parent 314c475 commit 2b0b5d8
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 12 deletions.
36 changes: 35 additions & 1 deletion include/cinatra/coro_http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,20 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
if (is_redirect)
redirect_uri_ = parser_.get_header_value("Location");

#ifdef CINATRA_ENABLE_GZIP
if (!parser_.get_header_value("Content-Encoding").empty()) {
if (parser_.get_header_value("Content-Encoding").find("gzip") !=
std::string_view::npos)
encoding_type_ = content_encoding::gzip;
else if (parser_.get_header_value("Content-Encoding").find("deflate") !=
std::string_view::npos)
encoding_type_ = content_encoding::deflate;
}
else {
encoding_type_ = content_encoding::none;
}
#endif

size_t content_len = (size_t)parser_.body_len();
#ifdef BENCHMARK_TEST
total_len_ = parser_.total_len();
Expand Down Expand Up @@ -1541,7 +1555,26 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
}

std::string_view reply(data_ptr, content_len);
data.resp_body = reply;
#ifdef CINATRA_ENABLE_GZIP
if (encoding_type_ == content_encoding::gzip) {
std::string unziped_str;
bool r = gzip_codec::uncompress(reply, unziped_str);
if (r)
data.resp_body = unziped_str;
else
data.resp_body = reply;
}
else if (encoding_type_ == content_encoding::deflate) {
std::string inflate_str;
bool r = gzip_codec::inflate(reply, inflate_str);
if (r)
data.resp_body = inflate_str;
else
data.resp_body = reply;
}
else
#endif
data.resp_body = reply;

head_buf_.consume(content_len);
}
Expand Down Expand Up @@ -2066,6 +2099,7 @@ class coro_http_client : public std::enable_shared_from_this<coro_http_client> {
bool enable_ws_deflate_ = false;
bool is_server_support_ws_deflate_ = false;
std::string inflate_str_;
content_encoding encoding_type_ = content_encoding::none;
#endif

#ifdef BENCHMARK_TEST
Expand Down
19 changes: 19 additions & 0 deletions include/cinatra/coro_http_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,25 @@ class coro_http_request {

bool is_req_ranges() { return parser_.is_req_ranges(); }

std::string_view get_accept_encoding() {
return get_header_value("Accept-Encoding");
}

content_encoding get_encoding_type() {
auto encoding_type = get_header_value("Content-Encoding");
if (!encoding_type.empty()) {
if (encoding_type.find("gzip") != std::string_view::npos)
return content_encoding::gzip;
else if (encoding_type.find("deflate") != std::string_view::npos)
return content_encoding::deflate;
else
return content_encoding::none;
}
else {
return content_encoding::none;
}
}

content_type get_content_type() {
if (is_chunked())
return content_type::chunked;
Expand Down
57 changes: 47 additions & 10 deletions include/cinatra/coro_http_response.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,63 @@ class coro_http_response {
}
void set_status_and_content(
status_type status, std::string content = "",
content_encoding encoding = content_encoding::none) {
set_status_and_content_view(status, std::move(content), encoding, false);
content_encoding encoding = content_encoding::none,
std::string_view client_encoding_type = "") {
set_status_and_content_view(status, std::move(content), encoding, false,
client_encoding_type);
}

template <typename String>
void set_status_and_content_view(
status_type status, String content = "",
content_encoding encoding = content_encoding::none, bool is_view = true) {
content_encoding encoding = content_encoding::none, bool is_view = true,
std::string_view client_encoding_type = "") {
status_ = status;
#ifdef CINATRA_ENABLE_GZIP
if (encoding == content_encoding::gzip) {
std::string encode_str;
bool r = gzip_codec::compress(content, encode_str, true);
if (!r) {
set_status_and_content(status_type::internal_server_error,
"gzip compress error");
if (client_encoding_type.empty() ||
client_encoding_type.find("gzip") != std::string_view::npos) {
std::string encode_str;
bool r = gzip_codec::compress(content, encode_str, true);
if (!r) {
set_status_and_content(status_type::internal_server_error,
"gzip compress error");
}
else {
add_header("Content-Encoding", "gzip");
set_content(std::move(encode_str));
}
}
else {
if (is_view) {
content_view_ = content;
}
else {
content_ = std::move(content);
}
}
}
else if (encoding == content_encoding::deflate) {
if (client_encoding_type.empty() ||
client_encoding_type.find("deflate") != std::string_view::npos) {
std::string deflate_str;
bool r = gzip_codec::deflate(content, deflate_str);
if (!r) {
set_status_and_content(status_type::internal_server_error,
"deflate compress error");
}
else {
add_header("Content-Encoding", "deflate");
set_content(std::move(deflate_str));
}
}
else {
add_header("Content-Encoding", "gzip");
set_content(std::move(encode_str));
if (is_view) {
content_view_ = content;
}
else {
content_ = std::move(content);
}
}
}
else
Expand Down
1 change: 1 addition & 0 deletions include/cinatra/define.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum class http_method {
OPTIONS,
DEL,
};
enum class content_encoding { gzip, deflate, none };
constexpr inline auto GET = http_method::GET;
constexpr inline auto POST = http_method::POST;
constexpr inline auto DEL = http_method::DEL;
Expand Down
1 change: 0 additions & 1 deletion include/cinatra/response_cv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "define.h"

namespace cinatra {
enum class content_encoding { gzip, none };

enum class status_type {
init,
Expand Down
77 changes: 77 additions & 0 deletions tests/test_cinatra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,83 @@ TEST_CASE("test for gzip") {
CHECK(decompress_data == "hello world");
server.stop();
}

TEST_CASE("test encoding type") {
coro_http_server server(1, 9001);

server.set_http_handler<GET, POST>("/get", [](coro_http_request &req,
coro_http_response &resp) {
auto encoding_type = req.get_encoding_type();

if (encoding_type ==
content_encoding::gzip) { // only post request have this field
std::string decode_str;
bool r = gzip_codec::uncompress(req.get_body(), decode_str);
CHECK(decode_str == "Hello World");
}
resp.set_status_and_content(status_type::ok, "ok", content_encoding::gzip,
req.get_accept_encoding());
CHECK(resp.content() != "ok");
});

server.set_http_handler<GET>(
"/coro",
[](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
resp.set_status_and_content(status_type::ok, "ok",
content_encoding::deflate,
req.get_accept_encoding());
CHECK(resp.content() != "ok");
co_return;
});

server.set_http_handler<GET>(
"/only_gzip",
[](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
resp.set_status_and_content(status_type::ok, "ok",
content_encoding::gzip,
req.get_accept_encoding());
// client4 accept-encoding not allow gzip, response content no
// compression
CHECK(resp.content() == "ok");
co_return;
});

server.async_start();
std::this_thread::sleep_for(std::chrono::milliseconds(100));

coro_http_client client1{};
client1.add_header("Accept-Encoding", "gzip, deflate");
auto result = async_simple::coro::syncAwait(
client1.async_get("http://127.0.0.1:9001/get"));
CHECK(result.resp_body == "ok");

coro_http_client client2{};
client2.add_header("Accept-Encoding", "gzip, deflate");
result = async_simple::coro::syncAwait(
client2.async_get("http://127.0.0.1:9001/coro"));
CHECK(result.resp_body == "ok");

coro_http_client client3{};
std::unordered_map<std::string, std::string> headers = {
{"Content-Encoding", "gzip"},
};
std::string ziped_str;
std::string_view data = "Hello World";
gzip_codec::compress(data, ziped_str);
result = async_simple::coro::syncAwait(client3.async_post(
"http://127.0.0.1:9001/get", ziped_str, req_content_type::none, headers));
CHECK(result.resp_body == "ok");

coro_http_client client4{};
client4.add_header("Accept-Encoding", "deflate");
result = async_simple::coro::syncAwait(
client4.async_get("http://127.0.0.1:9001/only_gzip"));
CHECK(result.resp_body == "ok");

server.stop();
}
#endif

#ifdef CINATRA_ENABLE_SSL
Expand Down

0 comments on commit 2b0b5d8

Please sign in to comment.