diff --git a/src/webserver/DOSGuard.h b/src/webserver/DOSGuard.h index 90fcf6c09..4a1b8a803 100644 --- a/src/webserver/DOSGuard.h +++ b/src/webserver/DOSGuard.h @@ -15,6 +15,7 @@ class DOSGuard std::uint32_t const maxFetches_; std::uint32_t const sweepInterval_; + // Load config setting for DOSGuard std::optional getConfig(boost::json::object const& config) const { diff --git a/src/webserver/HttpBase.h b/src/webserver/HttpBase.h index 8e0d170c7..953cab1e3 100644 --- a/src/webserver/HttpBase.h +++ b/src/webserver/HttpBase.h @@ -242,12 +242,16 @@ public: }, dosGuard_.isWhiteListed(*ip))) { + // Non-whitelist connection rejected due to full connection queue http::response res{ http::status::ok, req_.version()}; - res.set(http::field::server, "clio-server-v0.0.0"); + res.set( + http::field::server, + "clio-server-" + Build::getClioVersionString()); res.set(http::field::content_type, "application/json"); res.keep_alive(req_.keep_alive()); - res.body() = "Server overloaded"; + res.body() = boost::json::serialize( + RPC::make_error(RPC::Error::rpcTOO_BUSY)); res.prepare_payload(); lambda_(std::move(res)); } @@ -304,7 +308,9 @@ handle_request( std::string content_type, std::string message) { http::response res{status, req.version()}; - res.set(http::field::server, "xrpl-reporting-server-v0.0.0"); + res.set( + http::field::server, + "clio-server-" + Build::getClioVersionString()); res.set(http::field::content_type, content_type); res.keep_alive(req.keep_alive()); res.body() = std::string(message); @@ -324,9 +330,9 @@ handle_request( if (!dosGuard.isOk(ip)) return send(httpResponse( - http::status::ok, - "application/json", - boost::json::serialize(RPC::make_error(RPC::Error::rpcSLOW_DOWN)))); + http::status::service_unavailable, + "text/plain", + "Server is overloaded")); try { @@ -350,13 +356,6 @@ handle_request( RPC::make_error(RPC::Error::rpcBAD_SYNTAX)))); } - if (!dosGuard.isOk(ip)) - return send(httpResponse( - http::status::ok, - "application/json", - boost::json::serialize( - RPC::make_error(RPC::Error::rpcSLOW_DOWN)))); - auto range = backend->fetchLedgerRange(); if (!range) return send(httpResponse( @@ -429,8 +428,7 @@ handle_request( responseStr = boost::json::serialize(response); if (!dosGuard.add(ip, responseStr.size())) { - warnings.emplace_back("Too many requests"); - response["warnings"] = warnings; + response["warning"] = "load"; // reserialize when we need to include this warning responseStr = boost::json::serialize(response); } diff --git a/src/webserver/WsBase.h b/src/webserver/WsBase.h index 5e537b3c5..a058ecaa0 100644 --- a/src/webserver/WsBase.h +++ b/src/webserver/WsBase.h @@ -245,40 +245,24 @@ public: } void - handle_request(std::string const&& msg, boost::asio::yield_context& yc) + handle_request( + boost::json::object const&& request, + boost::json::value const& id, + boost::asio::yield_context& yield) { auto ip = derived().ip(); if (!ip) return; boost::json::object response = {}; - auto sendError = [this](auto error, boost::json::value id) { + auto sendError = [this, &request, id](auto error) { auto e = RPC::make_error(error); - if (!id.is_null()) e["id"] = id; - + e["request"] = request; send(boost::json::serialize(e)); }; - boost::json::value raw = [](std::string const&& msg) { - try - { - return boost::json::parse(msg); - } - catch (std::exception&) - { - return boost::json::value{nullptr}; - } - }(std::move(msg)); - - if (!raw.is_object()) - return sendError(RPC::Error::rpcINVALID_PARAMS, nullptr); - - boost::json::object request = raw.as_object(); - - auto id = request.contains("id") ? request.at("id") : nullptr; - try { BOOST_LOG_TRIVIAL(debug) << " received request : " << request; @@ -286,10 +270,10 @@ public: { auto range = backend_->fetchLedgerRange(); if (!range) - return sendError(RPC::Error::rpcNOT_READY, id); + return sendError(RPC::Error::rpcNOT_READY); std::optional context = RPC::make_WsContext( - yc, + yield, request, backend_, subscriptions_.lock(), @@ -301,7 +285,7 @@ public: *ip); if (!context) - return sendError(RPC::Error::rpcBAD_SYNTAX, id); + return sendError(RPC::Error::rpcBAD_SYNTAX); response = getDefaultWsResponse(id); @@ -334,7 +318,7 @@ public: catch (Backend::DatabaseTimeout const& t) { BOOST_LOG_TRIVIAL(error) << __func__ << " Database timeout"; - return sendError(RPC::Error::rpcNOT_READY, id); + return sendError(RPC::Error::rpcNOT_READY); } } catch (std::exception const& e) @@ -342,7 +326,7 @@ public: BOOST_LOG_TRIVIAL(error) << __func__ << " caught exception : " << e.what(); - return sendError(RPC::Error::rpcINTERNAL, id); + return sendError(RPC::Error::rpcINTERNAL); } boost::json::array warnings; @@ -358,8 +342,7 @@ public: std::string responseStr = boost::json::serialize(response); if (!dosGuard_.add(*ip, responseStr.size())) { - warnings.emplace_back("Too many requests"); - response["warnings"] = warnings; + response["warning"] = "load"; // reserialize if we need to include this warning responseStr = boost::json::serialize(response); } @@ -383,33 +366,59 @@ public: BOOST_LOG_TRIVIAL(debug) << __func__ << " received request from ip = " << *ip; - auto sendError = [&](auto&& msg) { - boost::json::object response; - response["error"] = std::move(msg); - std::string responseStr = boost::json::serialize(response); + auto sendError = [this, ip]( + auto error, + boost::json::value const& id, + boost::json::object const& request) { + auto e = RPC::make_error(error); + + if (!id.is_null()) + e["id"] = id; + e["request"] = request; + + auto responseStr = boost::json::serialize(e); BOOST_LOG_TRIVIAL(trace) << __func__ << " : " << responseStr; - dosGuard_.add(*ip, responseStr.size()); send(std::move(responseStr)); }; + + boost::json::value raw = [](std::string const&& msg) { + try + { + return boost::json::parse(msg); + } + catch (std::exception&) + { + return boost::json::value{nullptr}; + } + }(std::move(msg)); + + boost::json::object request; + if (!raw.is_object()) + return sendError(RPC::Error::rpcINVALID_PARAMS, nullptr, request); + request = raw.as_object(); + + auto id = request.contains("id") ? request.at("id") : nullptr; + if (!dosGuard_.isOk(*ip)) { - sendError("Too many requests. Slow down"); + sendError(RPC::Error::rpcSLOW_DOWN, id, request); } else { if (!queue_.postCoro( - [m = std::move(msg), shared_this = shared_from_this()]( - boost::asio::yield_context yield) { - shared_this->handle_request(std::move(m), yield); + [shared_this = shared_from_this(), + r = std::move(request), + id](boost::asio::yield_context yield) { + shared_this->handle_request(std::move(r), id, yield); }, dosGuard_.isWhiteListed(*ip))) - sendError("Server overloaded"); + sendError(RPC::Error::rpcTOO_BUSY, id, request); } do_read(); } }; -#endif // RIPPLE_REPORTING_WS_BASE_SESSION_H +#endif // RIPPLE_REPORTING_WS_BASE_SESSION_H \ No newline at end of file