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

[3.2 -> main] Return application/json for all http responses including errors #337

Merged
merged 4 commits into from
Oct 17, 2022
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
2 changes: 1 addition & 1 deletion plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void chain_api_plugin::plugin_startup() {
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() );
ro_api.set_shorten_abi_errors( !http_plugin::verbose_errors() );

_http_plugin.add_api( {
CHAIN_RO_CALL(get_info, 200, http_params_types::no_params)}, appbase::priority::medium_high);
Expand Down
2 changes: 1 addition & 1 deletion plugins/http_plugin/http_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
return (!my->listen_endpoint || my->listen_endpoint->address().is_loopback());
}

bool http_plugin::verbose_errors()const {
bool http_plugin::verbose_errors() {
return verbose_http_errors;
}

Expand Down
114 changes: 65 additions & 49 deletions plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <eosio/http_plugin/common.hpp>
#include <fc/io/json.hpp>

#include <memory>
#include <string>
Expand Down Expand Up @@ -107,25 +108,13 @@ class beast_http_session : public detail::abstract_conn {
res_->keep_alive(req.keep_alive());
res_->set(http::field::server, BOOST_BEAST_VERSION_STRING);

// Returns a bad request response
auto const bad_request =
[](const beast::string_view& why, detail::abstract_conn& conn) {
conn.send_response(std::string(why),
static_cast<int>(http::status::bad_request));
};

// Returns a not found response
auto const not_found =
[](const std::string& target, detail::abstract_conn& conn) {
conn.send_response("The resource '" + target + "' was not found.",
static_cast<int>(http::status::not_found));
};

// Request path must be absolute and not contain "..".
if(req.target().empty() ||
req.target()[0] != '/' ||
req.target().find("..") != beast::string_view::npos)
return bad_request("Illegal request-target", *this);
if(req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos) {
error_results results{static_cast<uint16_t>(http::status::bad_request), "Illegal request-target"};
send_response( fc::json::to_string( results, fc::time_point::maximum() ),
static_cast<unsigned int>(http::status::bad_request) );
return;
}

try {
if(!derived().allow_host(req))
Expand All @@ -146,15 +135,15 @@ class beast_http_session : public detail::abstract_conn {

// Respond to options request
if(req.method() == http::verb::options) {
send_response("", static_cast<int>(http::status::ok));
send_response("{}", static_cast<unsigned int>(http::status::ok));
return;
}

// verfiy bytes in flight/requests in flight
if(!verify_max_bytes_in_flight()) return;

std::string resource = std::string(req.target());
// look for the URL handler to handle this reosouce
// look for the URL handler to handle this resource
auto handler_itr = plugin_state_->url_handlers.find(resource);
if(handler_itr != plugin_state_->url_handlers.end()) {
if(plugin_state_->logger.is_enabled(fc::log_level::all))
Expand All @@ -165,27 +154,29 @@ class beast_http_session : public detail::abstract_conn {
std::move(body),
make_http_response_handler(plugin_state_, derived().shared_from_this()));
} else {
fc_dlog(plugin_state_->logger, "404 - not found: ${ep}", ("ep", resource));
not_found(resource, *this);
fc_dlog( plugin_state_->logger, "404 - not found: ${ep}", ("ep", resource) );
error_results results{static_cast<uint16_t>(http::status::not_found), "Not Found",
error_results::error_info( fc::exception( FC_LOG_MESSAGE( error, "Unknown Endpoint" ) ),
http_plugin::verbose_errors() )};
send_response( fc::json::to_string( results, fc::time_point::now() + plugin_state_->max_response_time ),
static_cast<unsigned int>(http::status::not_found) );
}
} catch(...) {
handle_exception();
}
}

void report_429_error(std::string what) {
send_response(std::move(what),
static_cast<int>(http::status::too_many_requests));
}

public:
virtual bool verify_max_bytes_in_flight() override {
auto bytes_in_flight_size = plugin_state_->bytes_in_flight.load();
if(bytes_in_flight_size > plugin_state_->max_bytes_in_flight) {
fc_dlog(plugin_state_->logger, "429 - too many bytes in flight: ${bytes}", ("bytes", bytes_in_flight_size));
std::string what = "Too many bytes in flight: " + std::to_string(bytes_in_flight_size) + ". Try again later.";
;
report_429_error(std::move(what));
error_results::error_info ei;
ei.code = static_cast<int64_t>(http::status::too_many_requests);
ei.name = "Busy";
ei.what = "Too many bytes in flight: " + std::to_string( bytes_in_flight_size );
error_results results{static_cast<uint16_t>(http::status::too_many_requests), "Busy", ei};
send_response( fc::json::to_string( results, fc::time_point::maximum() ), static_cast<unsigned int>(http::status::too_many_requests) );
return false;
}
return true;
Expand All @@ -198,8 +189,12 @@ class beast_http_session : public detail::abstract_conn {
auto requests_in_flight_num = plugin_state_->requests_in_flight.load();
if(requests_in_flight_num > plugin_state_->max_requests_in_flight) {
fc_dlog(plugin_state_->logger, "429 - too many requests in flight: ${requests}", ("requests", requests_in_flight_num));
std::string what = "Too many requests in flight: " + std::to_string(requests_in_flight_num) + ". Try again later.";
report_429_error(std::move(what));
error_results::error_info ei;
ei.code = static_cast<int64_t>(http::status::too_many_requests);
ei.name = "Busy";
ei.what = "Too many requests in flight: " + std::to_string( requests_in_flight_num );
error_results results{static_cast<uint16_t>(http::status::too_many_requests), "Busy", ei};
send_response( fc::json::to_string( results, fc::time_point::maximum() ), static_cast<unsigned int>(http::status::too_many_requests) );
return false;
}
return true;
Expand Down Expand Up @@ -310,38 +305,59 @@ class beast_http_session : public detail::abstract_conn {
virtual void handle_exception() override {
std::string err_str;
try {
throw;
} catch(const fc::exception& e) {
err_str = e.to_detail_string();
fc_elog(plugin_state_->logger, "fc::exception: ${w}", ("w", err_str));
} catch(std::exception& e) {
err_str = e.what();
fc_elog(plugin_state_->logger, "std::exception: ${w}", ("w", err_str));
} catch(...) {
err_str = "unknown";
fc_elog(plugin_state_->logger, "unkonwn exception");
try {
throw;
} catch(const fc::exception& e) {
err_str = e.to_detail_string();
fc_elog(plugin_state_->logger, "fc::exception: ${w}", ("w", err_str));
error_results results{static_cast<uint16_t>(http::status::internal_server_error),
"Internal Service Error",
error_results::error_info( e, http_plugin::verbose_errors() )};
err_str = fc::json::to_string( results, fc::time_point::now() + plugin_state_->max_response_time );
} catch(std::exception& e) {
err_str = e.what();
fc_elog(plugin_state_->logger, "std::exception: ${w}", ("w", err_str));
error_results results{static_cast<uint16_t>(http::status::internal_server_error),
"Internal Service Error",
error_results::error_info( fc::exception( FC_LOG_MESSAGE( error, err_str )),
http_plugin::verbose_errors() )};
err_str = fc::json::to_string( results, fc::time_point::now() + plugin_state_->max_response_time );
} catch(...) {
err_str = "Unknown exception";
fc_elog(plugin_state_->logger, err_str);
error_results results{static_cast<uint16_t>(http::status::internal_server_error),
"Internal Service Error",
error_results::error_info(
fc::exception( FC_LOG_MESSAGE( error, err_str )),
http_plugin::verbose_errors() )};
err_str = fc::json::to_string( results, fc::time_point::now() + plugin_state_->max_response_time );
}
} catch (fc::timeout_exception& e) {
fc_elog( plugin_state_->logger, "Timeout exception ${te} attempting to handle exception: ${e}", ("te", e.to_detail_string())("e", err_str) );
err_str = R"xxx({"message": "Internal Server Error"})xxx";
} catch (...) {
fc_elog( plugin_state_->logger, "Exception attempting to handle exception: ${e}", ("e", err_str) );
err_str = R"xxx({"message": "Internal Server Error"})xxx";
}


if(is_send_exception_response_) {
res_->set(http::field::content_type, "text/plain");
res_->set(http::field::content_type, "application/json");
res_->keep_alive(false);
res_->set(http::field::server, BOOST_BEAST_VERSION_STRING);

http::status stat = http::status::internal_server_error;
auto resp_str = "Internal Server Error\n\nUnhandled Exception: " + err_str;
send_response(resp_str, static_cast<int>(stat));
send_response(std::move(err_str), static_cast<unsigned int>(http::status::internal_server_error));
derived().do_eof();
}
}

virtual void send_response(std::optional<std::string> body, int code) override {
virtual void send_response(std::string json_body, unsigned int code) override {
write_begin_ = steady_clock::now();
auto dt = write_begin_ - handle_begin_;
handle_time_us_ += std::chrono::duration_cast<std::chrono::microseconds>(dt).count();

res_->result(code);
if(body.has_value())
res_->body() = *body;
res_->body() = std::move(json_body);

res_->prepare_payload();

Expand Down
4 changes: 2 additions & 2 deletions plugins/http_plugin/include/eosio/http_plugin/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ struct abstract_conn {
virtual bool verify_max_requests_in_flight() = 0;
virtual void handle_exception() = 0;

virtual void send_response(std::optional<std::string> body, int code) = 0;
virtual void send_response(std::string json_body, unsigned int code) = 0;
};

using abstract_conn_ptr = std::shared_ptr<abstract_conn>;
Expand Down Expand Up @@ -233,7 +233,7 @@ auto make_http_response_handler(std::shared_ptr<http_plugin_state> plugin_state,
auto tracked_json = make_in_flight(std::move(json), plugin_state);
session_ptr->send_response(std::move(tracked_json->obj()), code);
} else {
session_ptr->send_response({}, code);
session_ptr->send_response("{}", code);
}
} catch(...) {
session_ptr->handle_exception();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ namespace eosio {
bool is_on_loopback() const;
bool is_secure() const;

bool verbose_errors()const;
static bool verbose_errors();

struct get_supported_apis_result {
vector<string> apis;
Expand Down