diff --git a/silkworm/rpc/http/connection.cpp b/silkworm/rpc/http/connection.cpp index 1c2d38ba31..a10ac547e5 100644 --- a/silkworm/rpc/http/connection.cpp +++ b/silkworm/rpc/http/connection.cpp @@ -19,7 +19,6 @@ #ifndef ZLIB_CONST #define ZLIB_CONST #endif -#include #include #include @@ -31,6 +30,9 @@ #include #include #include +#include +#include +#include #include #include @@ -42,6 +44,7 @@ namespace silkworm::rpc::http { static constexpr std::string_view kMaxAge{"600"}; static constexpr auto kMaxPayloadSize{30 * kMebi}; // 30MiB static constexpr std::array kAcceptedContentTypes{"application/json", "application/jsonrequest", "application/json-rpc"}; +static std::vector kSupportedCompressionList{"gzip"}; // specify the compression algo in priority level Connection::Connection(boost::asio::io_context& io_context, commands::RpcApi& api, @@ -174,6 +177,31 @@ Task Connection::handle_actual_request(const boost::beast::http::request Connection::handle_actual_request(const boost::beast::http::requestappend("\n"), boost::beast::http::status::ok, !encoding.empty()); + co_await do_write(rsp_content->append("\n"), boost::beast::http::status::ok, selected_compression); } } @@ -262,7 +289,7 @@ Task Connection::write(std::string_view content, bool /*last*/) { co_return bytes_transferred; } -Task Connection::do_write(const std::string& content, boost::beast::http::status http_status, bool compress) { +Task Connection::do_write(const std::string& content, boost::beast::http::status http_status, const std::string& content_encoding) { try { SILK_TRACE << "Connection::do_write response: " << http_status << " content: " << content; boost::beast::http::response res{http_status, request_http_version_}; @@ -276,19 +303,23 @@ Task Connection::do_write(const std::string& content, boost::beast::http:: res.set(boost::beast::http::field::date, get_date_time()); res.erase(boost::beast::http::field::host); res.keep_alive(request_keep_alive_); - if (compress) { - const std::string compression_type = "gzip"; - res.set(boost::beast::http::field::content_encoding, compression_type); + if (http_status == boost::beast::http::status::ok && !content_encoding.empty()) { + // Positive response w/ compression required + res.set(boost::beast::http::field::content_encoding, content_encoding); std::string compressed_data; try { compress_data(content, compressed_data); } catch (const std::exception& e) { - SILK_ERROR << "Connection::compress_data exception: " << e.what(); + SILK_ERROR << "Connection::do_write cannot compress exception: " << e.what(); throw; } res.content_length(compressed_data.length()); res.body() = std::move(compressed_data); } else { + // Any negative response or positive response w/o compression + if (!content_encoding.empty()) { + res.set(boost::beast::http::field::accept_encoding, content_encoding); // Indicate the supported encoding + } res.content_length(content.size()); res.body() = content; } @@ -310,33 +341,10 @@ Task Connection::do_write(const std::string& content, boost::beast::http:: } void Connection::compress_data(const std::string& clear_data, std::string& compressed_data) { - z_stream strm; - - std::memset(&strm, 0, sizeof(strm)); - int ret = Z_OK; - ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); - if (ret != Z_OK) { - throw std::runtime_error("deflateInit2 fail"); - } - strm.avail_in = static_cast(clear_data.size()); - strm.next_in = reinterpret_cast(clear_data.c_str()); - - do { - strm.next_out = reinterpret_cast(temp_compressed_buffer_); - strm.avail_out = sizeof(temp_compressed_buffer_); - - ret = deflate(&strm, Z_FINISH); - if (ret < 0) { - deflateEnd(&strm); - throw std::runtime_error("deflate fail"); - } - if (compressed_data.size() < strm.total_out) { - // append the block to the output string - compressed_data.append(temp_compressed_buffer_, strm.total_out - compressed_data.size()); - } - } while (ret != Z_STREAM_END); - - deflateEnd(&strm); + boost::iostreams::filtering_ostream out; + out.push(boost::iostreams::gzip_compressor()); + out.push(boost::iostreams::back_inserter(compressed_data)); + boost::iostreams::copy(boost::make_iterator_range(clear_data), out); } Connection::AuthorizationResult Connection::is_request_authorized(const boost::beast::http::request& req) { @@ -407,6 +415,15 @@ void Connection::set_cors(boost::beast::http::response& res) { } } +std::string Connection::select_compression_algo(const std::string& requested_compression) { + for (std::string curr_compression : kSupportedCompressionList) { + if (requested_compression.find(curr_compression) != std::string::npos) { + return curr_compression; + } + } + return ""; +} + bool Connection::is_origin_allowed(const std::vector& allowed_origins, const std::string& origin) { if (allowed_origins.size() == 1 && allowed_origins[0] == "*") { return true; diff --git a/silkworm/rpc/http/connection.hpp b/silkworm/rpc/http/connection.hpp index ec104f8bf5..fecd7f5419 100644 --- a/silkworm/rpc/http/connection.hpp +++ b/silkworm/rpc/http/connection.hpp @@ -87,10 +87,12 @@ class Connection : public StreamWriter { Task do_read(); //! Perform an asynchronous write operation. - Task do_write(const std::string& content, boost::beast::http::status http_status, bool compress = false); + Task do_write(const std::string& content, boost::beast::http::status http_status, const std::string& content_encoding = ""); static std::string get_date_time(); + std::string select_compression_algo(const std::string& request_compression); + void compress_data(const std::string& clear_data, std::string& compressed_data); //! Socket for the connection. @@ -119,8 +121,6 @@ class Connection : public StreamWriter { std::string vary_; std::string origin_; boost::beast::http::verb method_{boost::beast::http::verb::unknown}; - - char temp_compressed_buffer_[10 * kMebi]{}; }; } // namespace silkworm::rpc::http