Skip to content

Commit

Permalink
Merge pull request #337 from AntelopeIO/GH-324-http-json-main
Browse files Browse the repository at this point in the history
[3.2 -> main] Return application/json for all http responses including errors
  • Loading branch information
heifner authored Oct 17, 2022
2 parents 7db26d8 + 9916ba7 commit 4890d44
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 54 deletions.
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

0 comments on commit 4890d44

Please sign in to comment.