Skip to content

Commit

Permalink
Merge pull request #96 from AntelopeIO/http-max-response
Browse files Browse the repository at this point in the history
[3.2] Remove hardcoded 10ms limit for chain_api_plugin calls
  • Loading branch information
heifner authored Sep 12, 2022
2 parents 7175728 + 30736e9 commit 0b1c8ab
Show file tree
Hide file tree
Showing 21 changed files with 329 additions and 228 deletions.
21 changes: 13 additions & 8 deletions plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ parse_params<chain_apis::read_only::get_transaction_status_params, http_params_t
#define CALL_WITH_400(api_name, api_handle, api_namespace, call_name, http_response_code, params_type) \
{std::string("/v1/" #api_name "/" #call_name), \
[api_handle](string, string body, url_response_callback cb) mutable { \
api_handle.validate(); \
auto deadline = api_handle.start(); \
try { \
auto params = parse_params<api_namespace::call_name ## _params, params_type>(body);\
fc::variant result( api_handle.call_name( std::move(params) ) ); \
cb(http_response_code, std::move(result)); \
FC_CHECK_DEADLINE(deadline);\
fc::variant result( api_handle.call_name( std::move(params), deadline ) ); \
cb(http_response_code, deadline, std::move(result)); \
} catch (...) { \
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
} \
Expand All @@ -64,10 +65,11 @@ parse_params<chain_apis::read_only::get_transaction_status_params, http_params_t
#define CALL_ASYNC_WITH_400(api_name, api_handle, api_namespace, call_name, call_result, http_response_code, params_type) \
{std::string("/v1/" #api_name "/" #call_name), \
[api_handle](string, string body, url_response_callback cb) mutable { \
api_handle.validate(); \
auto deadline = api_handle.start(); \
try { \
auto params = parse_params<api_namespace::call_name ## _params, params_type>(body);\
api_handle.call_name( std::move(params),\
FC_CHECK_DEADLINE(deadline);\
api_handle.call_name( std::move(params), \
[cb, body](const std::variant<fc::exception_ptr, call_result>& result){\
if (std::holds_alternative<fc::exception_ptr>(result)) {\
try {\
Expand All @@ -76,7 +78,7 @@ parse_params<chain_apis::read_only::get_transaction_status_params, http_params_t
http_plugin::handle_exception(#api_name, #call_name, body, cb);\
}\
} else {\
cb(http_response_code, std::visit(async_result_visitor(), result));\
cb(http_response_code, fc::time_point::maximum(), std::visit(async_result_visitor(), result));\
}\
});\
} catch (...) { \
Expand All @@ -96,8 +98,11 @@ void chain_api_plugin::plugin_startup() {
ilog( "starting chain_api_plugin" );
my.reset(new chain_api_plugin_impl(app().get_plugin<chain_plugin>().chain()));
auto& chain = app().get_plugin<chain_plugin>();
auto ro_api = chain.get_read_only_api();
auto rw_api = chain.get_read_write_api();
auto& http = app().get_plugin<http_plugin>();
fc::microseconds max_response_time = http.get_max_response_time();

auto ro_api = chain.get_read_only_api(max_response_time);
auto rw_api = chain.get_read_write_api(max_response_time);

auto& _http_plugin = app().get_plugin<http_plugin>();
ro_api.set_shorten_abi_errors( !_http_plugin.verbose_errors() );
Expand Down
132 changes: 78 additions & 54 deletions plugins/chain_plugin/chain_plugin.cpp

Large diffs are not rendered by default.

127 changes: 85 additions & 42 deletions plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions plugins/chain_plugin/test/test_chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ class chain_plugin_tester : public TESTER {
read_only::get_account_results get_account_info(const account_name acct){
auto account_object = control->get_account(acct);
read_only::get_account_params params = { account_object.name };
chain_apis::read_only plugin(*(control.get()), {}, fc::microseconds::maximum(), {}, {});
return plugin.get_account(params);
chain_apis::read_only plugin(*(control.get()), {}, fc::microseconds::maximum(), fc::microseconds::maximum(), {}, {});
return plugin.get_account(params, fc::time_point::maximum());
}

transaction_trace_ptr setup_producer_accounts( const std::vector<account_name>& accounts ) {
Expand Down
2 changes: 1 addition & 1 deletion plugins/db_size_api_plugin/db_size_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ using namespace eosio;
try { \
body = parse_params<std::string, http_params_types::no_params>(body); \
INVOKE \
cb(http_response_code, fc::variant(result)); \
cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \
} catch (...) { \
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
} \
Expand Down
35 changes: 20 additions & 15 deletions plugins/http_plugin/http_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
return;
}

url_response_callback wrapped_then = [tracked_b, then=std::move(then)](int code, std::optional<fc::variant> resp) {
then(code, std::move(resp));
url_response_callback wrapped_then = [tracked_b, then=std::move(then)](int code, const fc::time_point& deadline, std::optional<fc::variant> resp) {
then(code, deadline, std::move(resp));
};

// post to the app thread taking shared ownership of next (via std::shared_ptr),
Expand Down Expand Up @@ -253,8 +253,8 @@ class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
"Maximum size in megabytes http_plugin should use for processing http requests. -1 for unlimited. 429 error response when exceeded." )
("http-max-in-flight-requests", bpo::value<int32_t>()->default_value(-1),
"Maximum number of requests http_plugin should use for processing http requests. 429 error response when exceeded." )
("http-max-response-time-ms", bpo::value<uint32_t>()->default_value(30),
"Maximum time for processing a request.")
("http-max-response-time-ms", bpo::value<int64_t>()->default_value(30),
"Maximum time for processing a request, -1 for unlimited")
("verbose-http-errors", bpo::bool_switch()->default_value(false),
"Append the error log to HTTP responses")
("http-validate-host", boost::program_options::value<bool>()->default_value(true),
Expand Down Expand Up @@ -286,8 +286,13 @@ class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
my->plugin_state->max_bytes_in_flight = max_bytes_mb * 1024 * 1024;
}
my->plugin_state->max_requests_in_flight = options.at( "http-max-in-flight-requests" ).as<int32_t>();
my->plugin_state->max_response_time = fc::microseconds( options.at("http-max-response-time-ms").as<uint32_t>() * 1000 );

int64_t max_reponse_time_ms = options.at("http-max-response-time-ms").as<int64_t>();
EOS_ASSERT( max_reponse_time_ms == -1 || max_reponse_time_ms >= 0, chain::plugin_config_exception,
"http-max-response-time-ms must be -1, or non-negative: ${m}", ("m", max_reponse_time_ms) );
// set to one year for -1, unlimited, since this is added to fc::time_point::now() for a deadline
my->plugin_state->max_response_time = max_reponse_time_ms == -1 ?
fc::days(365) : fc::microseconds( max_reponse_time_ms * 1000 );

my->plugin_state->validate_host = options.at("http-validate-host").as<bool>();
if( options.count( "http-alias" )) {
const auto& aliases = options["http-alias"].as<vector<string>>();
Expand Down Expand Up @@ -430,7 +435,7 @@ class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
try {
if (body.empty()) body = "{}";
auto result = (*this).get_supported_apis();
cb(200, fc::variant(result));
cb(200, fc::time_point::maximum(), fc::variant(result));
} catch (...) {
handle_exception("node", "get_supported_apis", body, cb);
}
Expand Down Expand Up @@ -487,36 +492,36 @@ class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
throw;
} catch (chain::unknown_block_exception& e) {
error_results results{400, "Unknown Block", error_results::error_info(e, verbose_http_errors)};
cb( 400, fc::variant( results ));
cb( 400, fc::time_point::maximum(), fc::variant( results ));
} catch (chain::invalid_http_request& e) {
error_results results{400, "Invalid Request", error_results::error_info(e, verbose_http_errors)};
cb( 400, fc::variant( results ));
cb( 400, fc::time_point::maximum(), fc::variant( results ));
} catch (chain::unsatisfied_authorization& e) {
error_results results{401, "UnAuthorized", error_results::error_info(e, verbose_http_errors)};
cb( 401, fc::variant( results ));
cb( 401, fc::time_point::maximum(), fc::variant( results ));
} catch (chain::tx_duplicate& e) {
error_results results{409, "Conflict", error_results::error_info(e, verbose_http_errors)};
cb( 409, fc::variant( results ));
cb( 409, fc::time_point::maximum(), fc::variant( results ));
} catch (fc::eof_exception& e) {
error_results results{422, "Unprocessable Entity", error_results::error_info(e, verbose_http_errors)};
cb( 422, fc::variant( results ));
cb( 422, fc::time_point::maximum(), fc::variant( results ));
fc_elog( logger(), "Unable to parse arguments to ${api}.${call}", ("api", api_name)( "call", call_name ) );
fc_dlog( logger(), "Bad arguments: ${args}", ("args", body) );
} catch (fc::exception& e) {
error_results results{500, "Internal Service Error", error_results::error_info(e, verbose_http_errors)};
cb( 500, fc::variant( results ));
cb( 500, fc::time_point::maximum(), fc::variant( results ));
fc_dlog( logger(), "Exception while processing ${api}.${call}: ${e}",
("api", api_name)( "call", call_name )("e", e.to_detail_string()) );
} catch (std::exception& e) {
error_results results{500, "Internal Service Error", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, e.what())), verbose_http_errors)};
cb( 500, fc::variant( results ));
cb( 500, fc::time_point::maximum(), fc::variant( results ));
fc_elog( logger(), "STD Exception encountered while processing ${api}.${call}",
("api", api_name)( "call", call_name ) );
fc_dlog( logger(), "Exception Details: ${e}", ("e", e.what()) );
} catch (...) {
error_results results{500, "Internal Service Error",
error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, "Unknown Exception" )), verbose_http_errors)};
cb( 500, fc::variant( results ));
cb( 500, fc::time_point::maximum(), fc::variant( results ));
fc_elog( logger(), "Unknown Exception encountered while processing ${api}.${call}",
("api", api_name)( "call", call_name ) );
}
Expand Down
14 changes: 10 additions & 4 deletions plugins/http_plugin/include/eosio/http_plugin/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,18 +212,24 @@ auto make_in_flight(T&& object, std::shared_ptr<http_plugin_state> plugin_state)
*/
auto make_http_response_handler(std::shared_ptr<http_plugin_state> plugin_state, detail::abstract_conn_ptr session_ptr) {
return [plugin_state{std::move(plugin_state)},
session_ptr{std::move(session_ptr)}](int code, std::optional<fc::variant> response) {
session_ptr{std::move(session_ptr)}](int code, fc::time_point deadline, std::optional<fc::variant> response) {
auto tracked_response = make_in_flight(std::move(response), plugin_state);
if(!session_ptr->verify_max_bytes_in_flight()) {
return;
}

// post back to an HTTP thread to to allow the response handler to be called from any thread
auto start = fc::time_point::now();
if( deadline == fc::time_point::maximum() ) { // no caller supplied deadline so use http configured deadline
deadline = start + plugin_state->max_response_time;
}

// post back to an HTTP thread to allow the response handler to be called from any thread
boost::asio::post(plugin_state->thread_pool->get_executor(),
[plugin_state, session_ptr, code, tracked_response = std::move(tracked_response)]() {
[plugin_state, session_ptr, code, deadline, start,
tracked_response = std::move(tracked_response)]() {
try {
if(tracked_response->obj().has_value()) {
std::string json = fc::json::to_string(*tracked_response->obj(), fc::time_point::now() + plugin_state->max_response_time);
std::string json = fc::json::to_string(*tracked_response->obj(), deadline + (fc::time_point::now() - start));
auto tracked_json = make_in_flight(std::move(json), plugin_state);
session_ptr->send_response(std::move(tracked_json->obj()), code);
} else {
Expand Down
4 changes: 2 additions & 2 deletions plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ namespace eosio {
* @brief A callback function provided to a URL handler to
* allow it to specify the HTTP response code and body
*
* Arguments: response_code, response_body
* Arguments: response_code, deadline, response_body
*/
using url_response_callback = std::function<void(int,std::optional<fc::variant>)>;
using url_response_callback = std::function<void(int,fc::time_point,std::optional<fc::variant>)>;

/**
* @brief Callback type for a URL handler
Expand Down
2 changes: 1 addition & 1 deletion plugins/login_plugin/login_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ void login_plugin::plugin_initialize(const variables_map& options) {
if (body.empty()) \
body = "{}"; \
fc::variant result( call_name(fc::json::from_string(body).as<login_plugin::call_name##_params>()) ); \
cb(http_response_code, std::move(result)); \
cb(http_response_code, fc::time_point::maximum(), std::move(result)); \
} catch (...) { \
http_plugin::handle_exception("login", #call_name, body, cb); \
} \
Expand Down
2 changes: 1 addition & 1 deletion plugins/net_api_plugin/net_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ using namespace eosio;
[&api_handle](string, string body, url_response_callback cb) mutable { \
try { \
INVOKE \
cb(http_response_code, fc::variant(result)); \
cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \
} catch (...) { \
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
} \
Expand Down
4 changes: 2 additions & 2 deletions plugins/producer_api_plugin/producer_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct async_result_visitor : public fc::visitor<fc::variant> {
[&api_handle](string, string body, url_response_callback cb) mutable { \
try { \
INVOKE \
cb(http_response_code, fc::variant(result)); \
cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \
} catch (...) { \
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
} \
Expand All @@ -50,7 +50,7 @@ struct async_result_visitor : public fc::visitor<fc::variant> {
http_plugin::handle_exception(#api_name, #call_name, body, cb);\
}\
} else {\
cb(http_response_code, std::visit(async_result_visitor(), result));\
cb(http_response_code, fc::time_point::maximum(), std::visit(async_result_visitor(), result));\
}\
};\
INVOKE\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct async_result_visitor : public fc::visitor<std::string> {
try { \
auto params = parse_params<api_namespace::call_name ## _params, params_type>(body);\
fc::variant result( api_handle.call_name( std::move(params) ) ); \
cb(http_response_code, std::move(result)); \
cb(http_response_code, fc::time_point::maximum(), std::move(result)); \
} catch (...) { \
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
} \
Expand Down
20 changes: 11 additions & 9 deletions plugins/trace_api_plugin/trace_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this<trace_api
return;
}

const auto deadline = that->calc_deadline( max_response_time );

auto block_number = ([&body]() -> std::optional<uint32_t> {
if (body.empty()) {
return {};
Expand All @@ -274,19 +276,18 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this<trace_api

if (!block_number) {
error_results results{400, "Bad or missing block_num"};
cb( 400, fc::variant( results ));
cb( 400, deadline, fc::variant( results ));
return;
}

try {

const auto deadline = that->calc_deadline( max_response_time );
auto resp = that->req_handler->get_block_trace(*block_number, [deadline]() { FC_CHECK_DEADLINE(deadline); });
if (resp.is_null()) {
error_results results{404, "Trace API: block trace missing"};
cb( 404, fc::variant( results ));
cb( 404, deadline, fc::variant( results ));
} else {
cb( 200, std::move(resp) );
cb( 200, deadline, std::move(resp) );
}
} catch (...) {
http_plugin::handle_exception("trace_api", "get_block", body, cb);
Expand All @@ -302,6 +303,8 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this<trace_api
return;
}

const auto deadline = that->calc_deadline( max_response_time );

auto trx_id = ([&body]() -> std::optional<transaction_id_type> {
if (body.empty()) {
return {};
Expand All @@ -320,24 +323,23 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this<trace_api

if (!trx_id) {
error_results results{400, "Bad or missing transaction ID"};
cb( 400, fc::variant( results ));
cb( 400, deadline, fc::variant( results ));
return;
}

try {
const auto deadline = that->calc_deadline( max_response_time );
// search for the block that contains the transaction
get_block_n blk_num = common->store->get_trx_block_number(*trx_id, common->minimum_irreversible_history_blocks, [deadline]() { FC_CHECK_DEADLINE(deadline); });
if (!blk_num.has_value()){
error_results results{404, "Trace API: transaction id missing in the transaction id log files"};
cb( 404, fc::variant( results ));
cb( 404, deadline, fc::variant( results ));
} else {
auto resp = that->req_handler->get_transaction_trace(*trx_id, *blk_num, [deadline]() { FC_CHECK_DEADLINE(deadline); });
if (resp.is_null()) {
error_results results{404, "Trace API: transaction trace missing"};
cb( 404, fc::variant( results ));
cb( 404, deadline, fc::variant( results ));
} else {
cb( 200, std::move(resp) );
cb( 200, deadline, std::move(resp) );
}
}
} catch (...) {
Expand Down
Loading

0 comments on commit 0b1c8ab

Please sign in to comment.