22#include "data/BackendInterface.hpp"
23#include "etlng/ETLServiceInterface.hpp"
25#include "rpc/Factories.hpp"
28#include "rpc/common/impl/APIVersionParser.hpp"
29#include "util/JsonUtils.hpp"
30#include "util/Profiler.hpp"
31#include "util/Taggable.hpp"
32#include "util/config/ConfigDefinition.hpp"
33#include "util/log/Logger.hpp"
34#include "web/dosguard/DOSGuardInterface.hpp"
35#include "web/impl/ErrorHandling.hpp"
36#include "web/interface/ConnectionBase.hpp"
38#include <boost/asio/spawn.hpp>
39#include <boost/beast/core/error.hpp>
40#include <boost/json/array.hpp>
41#include <boost/json/object.hpp>
42#include <boost/json/parse.hpp>
43#include <boost/json/serialize.hpp>
44#include <boost/system/system_error.hpp>
45#include <xrpl/protocol/jss.h>
63template <
typename RPCEngineType>
65 std::shared_ptr<BackendInterface const>
const backend_;
66 std::shared_ptr<RPCEngineType>
const rpcEngine_;
67 std::shared_ptr<etlng::ETLServiceInterface const>
const etl_;
70 std::reference_wrapper<web::dosguard::DOSGuardInterface> dosguard_;
87 std::shared_ptr<BackendInterface const>
const& backend,
88 std::shared_ptr<RPCEngineType>
const& rpcEngine,
89 std::shared_ptr<etlng::ETLServiceInterface const>
const&
etl,
93 , rpcEngine_(rpcEngine)
96 , apiVersionParser_(config.getObject(
"api_version"))
108 operator()(std::string
const& request, std::shared_ptr<web::ConnectionBase>
const& connection)
110 if (not dosguard_.get().isOk(connection->clientIp)) {
111 connection->sendSlowDown(request);
116 auto req = boost::json::parse(request).as_object();
117 LOG(perfLog_.
debug()) << connection->tag() <<
"Adding to work queue";
119 if (not connection->upgraded and shouldReplaceParams(req))
120 req[JS(params)] = boost::json::array({boost::json::object{}});
122 if (not dosguard_.get().request(connection->clientIp, req)) {
123 connection->sendSlowDown(request);
127 if (!rpcEngine_->post(
128 [
this, request = std::move(req), connection](boost::asio::yield_context yield)
mutable {
129 handleRequest(yield, std::move(request), connection);
133 rpcEngine_->notifyTooBusy();
136 }
catch (boost::system::system_error
const& ex) {
138 rpcEngine_->notifyBadSyntax();
140 LOG(log_.
warn()) <<
"Error parsing JSON: " << ex.what() <<
". For request: " << request;
141 }
catch (std::invalid_argument
const& ex) {
143 rpcEngine_->notifyBadSyntax();
144 LOG(log_.
warn()) <<
"Invalid argument error: " << ex.what() <<
". For request: " << request;
146 }
catch (std::exception
const& ex) {
147 LOG(perfLog_.
error()) << connection->tag() <<
"Caught exception: " << ex.what();
148 rpcEngine_->notifyInternalError();
156 boost::asio::yield_context yield,
157 boost::json::object&& request,
158 std::shared_ptr<web::ConnectionBase>
const& connection
161 LOG(log_.
info()) << connection->tag() << (connection->upgraded ?
"ws" :
"http")
163 <<
" ip = " << connection->clientIp;
166 auto const range = backend_->fetchLedgerRange();
169 rpcEngine_->notifyNotReady();
175 auto const context = [&] {
176 if (connection->upgraded) {
180 connection->makeSubscriptionContext(tagFactory_),
181 tagFactory_.
with(connection->tag()),
183 connection->clientIp,
184 std::cref(apiVersionParser_),
185 connection->isAdmin()
191 tagFactory_.
with(connection->tag()),
193 connection->clientIp,
194 std::cref(apiVersionParser_),
195 connection->isAdmin()
200 auto const err = context.error();
201 LOG(perfLog_.
warn()) << connection->tag() <<
"Could not create Web context: " << err;
202 LOG(log_.
warn()) << connection->tag() <<
"Could not create Web context: " << err;
206 rpcEngine_->notifyBadSyntax();
212 auto [result, timeDiff] =
util::timed([&]() {
return rpcEngine_->buildResponse(*context); });
214 auto const us = std::chrono::duration<int, std::milli>(timeDiff);
217 boost::json::object response;
219 if (!result.response.has_value()) {
222 auto const responseStr = boost::json::serialize(response);
224 LOG(perfLog_.
debug()) << context->tag() <<
"Encountered error: " << responseStr;
225 LOG(log_.
debug()) << context->tag() <<
"Encountered error: " << responseStr;
228 rpcEngine_->notifyComplete(context->method, us);
230 auto& json = result.response.value();
231 auto const isForwarded =
232 json.contains(
"forwarded") && json.at(
"forwarded").is_bool() && json.at(
"forwarded").as_bool();
235 json.erase(
"forwarded");
240 if (isForwarded && (json.contains(JS(result)) || connection->upgraded)) {
241 for (
auto const& [k, v] : json)
242 response.insert_or_assign(k, v);
244 response[JS(result)] = json;
248 response[
"forwarded"] =
true;
252 if (connection->upgraded) {
253 auto const appendFieldIfExist = [&](
auto const& field) {
254 if (request.contains(field) and not request.at(field).is_null())
255 response[field] = request.at(field);
258 appendFieldIfExist(JS(
id));
259 appendFieldIfExist(JS(api_version));
261 if (!response.contains(JS(error)))
262 response[JS(status)] = JS(success);
264 response[JS(type)] = JS(response);
266 if (response.contains(JS(result)) && !response[JS(result)].as_object().contains(JS(error)))
267 response[JS(result)].as_object()[JS(status)] = JS(success);
271 boost::json::array warnings = std::move(result.warnings);
274 if (etl_->lastCloseAgeSeconds() >= 60)
277 response[
"warnings"] = warnings;
278 connection->send(boost::json::serialize(response));
279 }
catch (std::exception
const& ex) {
282 LOG(perfLog_.
error()) << connection->tag() <<
"Caught exception: " << ex.what();
283 LOG(log_.
error()) << connection->tag() <<
"Caught exception: " << ex.what();
285 rpcEngine_->notifyInternalError();
293 shouldReplaceParams(boost::json::object
const& req)
const
295 auto const hasParams = req.contains(JS(params));
296 auto const paramsIsArray = hasParams and req.at(JS(params)).is_array();
297 auto const paramsIsEmptyString =
298 hasParams and req.at(JS(params)).is_string() and req.at(JS(params)).as_string().empty();
299 auto const paramsIsEmptyObject =
300 hasParams and req.at(JS(params)).is_object() and req.at(JS(params)).as_object().empty();
301 auto const paramsIsNull = hasParams and req.at(JS(params)).is_null();
302 auto const arrayIsEmpty = paramsIsArray and req.at(JS(params)).as_array().empty();
303 auto const arrayIsNotEmpty = paramsIsArray and not req.at(JS(params)).as_array().empty();
304 auto const firstArgIsNull = arrayIsNotEmpty and req.at(JS(params)).as_array().at(0).is_null();
305 auto const firstArgIsEmptyString = arrayIsNotEmpty and req.at(JS(params)).as_array().at(0).is_string() and
306 req.at(JS(params)).as_array().at(0).as_string().empty();
309 return not hasParams or paramsIsEmptyString or paramsIsNull or paramsIsEmptyObject or arrayIsEmpty or
310 firstArgIsEmptyString or firstArgIsNull;
Definition APIVersionParser.hpp:34
A simple thread-safe logger for the channel specified in the constructor.
Definition Logger.hpp:111
Pump warn(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::WRN severity.
Definition Logger.cpp:224
Pump error(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::ERR severity.
Definition Logger.cpp:229
Pump debug(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::DBG severity.
Definition Logger.cpp:214
Pump info(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::NFO severity.
Definition Logger.cpp:219
A factory for TagDecorator instantiation.
Definition Taggable.hpp:182
TagDecoratorFactory with(ParentType parent) const noexcept
Creates a new tag decorator factory with a bound parent tag decorator.
Definition Taggable.cpp:66
All the config data will be stored and extracted from this class.
Definition ConfigDefinition.hpp:54
The server handler for RPC requests called by web server.
Definition RPCServerHandler.hpp:64
RPCServerHandler(util::config::ClioConfigDefinition const &config, std::shared_ptr< BackendInterface const > const &backend, std::shared_ptr< RPCEngineType > const &rpcEngine, std::shared_ptr< etlng::ETLServiceInterface const > const &etl, web::dosguard::DOSGuardInterface &dosguard)
Create a new server handler.
Definition RPCServerHandler.hpp:85
void operator()(std::string const &request, std::shared_ptr< web::ConnectionBase > const &connection)
The callback when server receives a request.
Definition RPCServerHandler.hpp:108
The interface of a denial of service guard.
Definition DOSGuardInterface.hpp:46
A helper that attempts to match rippled reporting mode HTTP errors as close as possible.
Definition ErrorHandling.hpp:45
This namespace contains everything to do with the ETL and ETL sources.
Definition CacheLoader.hpp:39
std::expected< web::Context, Status > makeWsContext(boost::asio::yield_context yc, boost::json::object const &request, web::SubscriptionContextPtr session, util::TagDecoratorFactory const &tagFactory, data::LedgerRange const &range, std::string const &clientIp, std::reference_wrapper< APIVersionParser const > apiVersionParser, bool isAdmin)
A factory function that creates a Websocket context.
Definition Factories.cpp:47
void logDuration(boost::json::object const &request, util::BaseTagDecorator const &tag, DurationType const &dur)
Log the duration of the request processing.
Definition RPCHelpers.hpp:760
boost::json::object makeWarning(WarningCode code)
Generate JSON from a rpc::WarningCode.
Definition Errors.cpp:65
std::expected< web::Context, Status > makeHttpContext(boost::asio::yield_context yc, boost::json::object const &request, util::TagDecoratorFactory const &tagFactory, data::LedgerRange const &range, std::string const &clientIp, std::reference_wrapper< APIVersionParser const > apiVersionParser, bool const isAdmin)
A factory function that creates a HTTP context.
Definition Factories.cpp:77
boost::json::object removeSecret(boost::json::object const &object)
Removes any detected secret information from a response JSON object.
Definition JsonUtils.hpp:67
auto timed(FnType &&func)
Profiler function to measure the time a function execution consumes.
Definition Profiler.hpp:40
This namespace implements the web server and related components.
Definition Types.hpp:43