From f23645fc583eb7b40ef4e674d19ed5356c0847dc Mon Sep 17 00:00:00 2001 From: Qing Zhang Date: Thu, 20 Aug 2020 15:48:47 -0700 Subject: [PATCH 1/2] introduce an option to restrict the maximum number of open HTTP RPC requests, resolve a number of merge conflicts --- plugins/http_plugin/http_plugin.cpp | 82 ++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/plugins/http_plugin/http_plugin.cpp b/plugins/http_plugin/http_plugin.cpp index 4337520ff8..25c652c54c 100644 --- a/plugins/http_plugin/http_plugin.cpp +++ b/plugins/http_plugin/http_plugin.cpp @@ -130,8 +130,12 @@ namespace eosio { struct abstract_conn { virtual ~abstract_conn() {} virtual bool verify_max_bytes_in_flight() = 0; + virtual bool verify_max_requests_in_flight() = 0; virtual void handle_exception() = 0; virtual void send_response(std::string, int) = 0; + + virtual const void* operator* () const = 0; + virtual void* operator* () = 0; }; using abstract_conn_ptr = std::shared_ptr; @@ -200,7 +204,9 @@ class http_plugin_impl : public std::enable_shared_from_this { uint16_t thread_pool_size = 2; std::optional thread_pool; std::atomic bytes_in_flight{0}; + std::atomic requests_in_flight{0}; size_t max_bytes_in_flight = 0; + int32_t max_requests_in_flight = -1; fc::microseconds max_response_time{30*1000}; std::optional https_listen_endpoint; @@ -325,25 +331,46 @@ class http_plugin_impl : public std::enable_shared_from_this { return true; } + template + void report_429_error( const T& con, string what) { + error_results::error_info ei; + ei.code = websocketpp::http::status_code::too_many_requests; + ei.name = "Busy"; + ei.what = what; + error_results results{websocketpp::http::status_code::too_many_requests, "Busy", ei}; + con->set_body( fc::json::to_string( results, fc::time_point::maximum() )); + con->set_status( websocketpp::http::status_code::too_many_requests ); + con->send_http_response(); + } + template bool verify_max_bytes_in_flight( const T& con ) { auto bytes_in_flight_size = bytes_in_flight.load(); if( bytes_in_flight_size > max_bytes_in_flight ) { fc_dlog( logger, "429 - too many bytes in flight: ${bytes}", ("bytes", bytes_in_flight_size) ); - error_results::error_info ei; - ei.code = websocketpp::http::status_code::too_many_requests; - ei.name = "Busy"; - ei.what = "Too many bytes in flight: " + std::to_string( bytes_in_flight_size ); - error_results results{websocketpp::http::status_code::too_many_requests, "Busy", ei}; - con->set_body( fc::json::to_string( results, fc::time_point::maximum() )); - con->set_status( websocketpp::http::status_code::too_many_requests ); - con->send_http_response(); + string what = "Too many bytes in flight: " + std::to_string( bytes_in_flight_size ) + ". Try again later.";; + report_429_error(con, what); return false; } return true; } + template + bool verify_max_requests_in_flight( const T& con ) { + if (max_requests_in_flight < 0) + return true; + + auto requests_in_flight_num = requests_in_flight.load(); + if( requests_in_flight_num > max_requests_in_flight ) { + fc_dlog( logger, "429 - too many requests in flight: ${requests}", ("requests", requests_in_flight_num) ); + string what = "Too many requests in flight: " + std::to_string( requests_in_flight_num ) + ". Try again later."; + report_429_error(con, what); + return false; + } + return true; + } + /** * child struct, implementing abstract connection for various underlying connection types * that ties it to an http_plugin_impl @@ -355,7 +382,13 @@ class http_plugin_impl : public std::enable_shared_from_this { abstract_conn_impl(detail::connection_ptr conn, http_plugin_impl_ptr impl) :_conn(std::move(conn)) ,_impl(std::move(impl)) - {} + { + _impl->requests_in_flight += 1; + } + + ~abstract_conn_impl() { + _impl->requests_in_flight -= 1; + } // No copy constructor and no move abstract_conn_impl(const abstract_conn_impl&) = delete; @@ -363,12 +396,14 @@ class http_plugin_impl : public std::enable_shared_from_this { abstract_conn_impl& operator=(const abstract_conn_impl&) = delete; abstract_conn_impl& operator=(abstract_conn_impl&&) noexcept = default; - ~abstract_conn_impl() = default; - bool verify_max_bytes_in_flight() override { return _impl->verify_max_bytes_in_flight(_conn); } + bool verify_max_requests_in_flight() override { + return _impl->verify_max_requests_in_flight(_conn); + } + void handle_exception()override { http_plugin_impl::handle_exception(_conn); } @@ -379,6 +414,22 @@ class http_plugin_impl : public std::enable_shared_from_this { _conn->send_http_response(); } + /** + * const accessor + * @return const reference to the contained _conn + */ + const void* operator* () const override { + return (const void *) &_conn; + } + + /** + * mutable accessor (can be moved frmo) + * @return mutable reference to the contained _conn + */ + void* operator* () override { + return (void *) &_conn; + } + detail::connection_ptr _conn; http_plugin_impl_ptr _impl; }; @@ -480,7 +531,7 @@ class http_plugin_impl : public std::enable_shared_from_this { return [my=std::move(my), priority, next_ptr=std::move(next_ptr)] ( detail::abstract_conn_ptr conn, string r, string b, url_response_callback then ) { auto tracked_b = make_in_flight(std::move(b), my); - if (!conn->verify_max_bytes_in_flight()) { + if (!conn->verify_max_bytes_in_flight() || !conn->verify_max_requests_in_flight()) { return; } @@ -529,7 +580,7 @@ class http_plugin_impl : public std::enable_shared_from_this { auto make_http_response_handler( detail::abstract_conn_ptr abstract_conn_ptr) { return [my=shared_from_this(), abstract_conn_ptr]( int code, fc::variant response ) { auto tracked_response = make_in_flight(std::move(response), my); - if (!abstract_conn_ptr->verify_max_bytes_in_flight()) { + if (!abstract_conn_ptr->verify_max_bytes_in_flight() || !abstract_conn_ptr->verify_max_requests_in_flight()) { return; } @@ -577,7 +628,7 @@ class http_plugin_impl : public std::enable_shared_from_this { con->defer_http_response(); auto abstract_conn_ptr = make_abstract_conn_ptr(con, shared_from_this()); - if( !verify_max_bytes_in_flight( con )) return; + if( !verify_max_bytes_in_flight( con ) || !verify_max_requests_in_flight( con ) ) return; std::string resource = con->get_uri()->get_resource(); auto handler_itr = url_handlers.find( resource ); @@ -699,6 +750,8 @@ class http_plugin_impl : public std::enable_shared_from_this { "The maximum body size in bytes allowed for incoming RPC requests") ("http-max-bytes-in-flight-mb", bpo::value()->default_value(500), "Maximum size in megabytes http_plugin should use for processing http requests. 503 error response when exceeded." ) + ("http-max-in-flight-requests", bpo::value()->default_value(-1), + "Maximum number of requests http_plugin should use for processing http requests. 503 error response when exceeded." ) ("http-max-response-time-ms", bpo::value()->default_value(30), "Maximum time for processing a request.") ("verbose-http-errors", bpo::bool_switch()->default_value(false), @@ -722,6 +775,7 @@ class http_plugin_impl : public std::enable_shared_from_this { "http-threads ${num} must be greater than 0", ("num", my->thread_pool_size)); my->max_bytes_in_flight = options.at( "http-max-bytes-in-flight-mb" ).as() * 1024 * 1024; + my->max_requests_in_flight = options.at( "http-max-in-flight-requests" ).as(); my->max_response_time = fc::microseconds( options.at("http-max-response-time-ms").as() * 1000 ); my->validate_host = options.at("http-validate-host").as(); From 234ed11b3680a4bdfe3c4d231a9f5842338f9fea Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 28 Jun 2022 18:49:39 -0400 Subject: [PATCH 2/2] manually pulled in the remaining of max requests in flight changes --- plugins/http_plugin/http_plugin.cpp | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/plugins/http_plugin/http_plugin.cpp b/plugins/http_plugin/http_plugin.cpp index 25c652c54c..e4d5f05e11 100644 --- a/plugins/http_plugin/http_plugin.cpp +++ b/plugins/http_plugin/http_plugin.cpp @@ -133,9 +133,6 @@ namespace eosio { virtual bool verify_max_requests_in_flight() = 0; virtual void handle_exception() = 0; virtual void send_response(std::string, int) = 0; - - virtual const void* operator* () const = 0; - virtual void* operator* () = 0; }; using abstract_conn_ptr = std::shared_ptr; @@ -332,7 +329,7 @@ class http_plugin_impl : public std::enable_shared_from_this { } template - void report_429_error( const T& con, string what) { + void report_429_error( const T& con, const std::string& what) { error_results::error_info ei; ei.code = websocketpp::http::status_code::too_many_requests; ei.name = "Busy"; @@ -414,22 +411,6 @@ class http_plugin_impl : public std::enable_shared_from_this { _conn->send_http_response(); } - /** - * const accessor - * @return const reference to the contained _conn - */ - const void* operator* () const override { - return (const void *) &_conn; - } - - /** - * mutable accessor (can be moved frmo) - * @return mutable reference to the contained _conn - */ - void* operator* () override { - return (void *) &_conn; - } - detail::connection_ptr _conn; http_plugin_impl_ptr _impl; }; @@ -531,7 +512,7 @@ class http_plugin_impl : public std::enable_shared_from_this { return [my=std::move(my), priority, next_ptr=std::move(next_ptr)] ( detail::abstract_conn_ptr conn, string r, string b, url_response_callback then ) { auto tracked_b = make_in_flight(std::move(b), my); - if (!conn->verify_max_bytes_in_flight() || !conn->verify_max_requests_in_flight()) { + if (!conn->verify_max_bytes_in_flight()) { return; } @@ -580,7 +561,7 @@ class http_plugin_impl : public std::enable_shared_from_this { auto make_http_response_handler( detail::abstract_conn_ptr abstract_conn_ptr) { return [my=shared_from_this(), abstract_conn_ptr]( int code, fc::variant response ) { auto tracked_response = make_in_flight(std::move(response), my); - if (!abstract_conn_ptr->verify_max_bytes_in_flight() || !abstract_conn_ptr->verify_max_requests_in_flight()) { + if (!abstract_conn_ptr->verify_max_bytes_in_flight()) { return; } @@ -749,9 +730,9 @@ class http_plugin_impl : public std::enable_shared_from_this { ("max-body-size", bpo::value()->default_value(my->max_body_size), "The maximum body size in bytes allowed for incoming RPC requests") ("http-max-bytes-in-flight-mb", bpo::value()->default_value(500), - "Maximum size in megabytes http_plugin should use for processing http requests. 503 error response when exceeded." ) + "Maximum size in megabytes http_plugin should use for processing http requests. 429 error response when exceeded." ) ("http-max-in-flight-requests", bpo::value()->default_value(-1), - "Maximum number of requests http_plugin should use for processing http requests. 503 error response when exceeded." ) + "Maximum number of requests http_plugin should use for processing http requests. 429 error response when exceeded." ) ("http-max-response-time-ms", bpo::value()->default_value(30), "Maximum time for processing a request.") ("verbose-http-errors", bpo::bool_switch()->default_value(false),