Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
RPCEngine.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2022, the clio developers.
5
6 Permission to use, copy, modify, and distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#pragma once
21
22#include "data/BackendInterface.hpp"
23#include "rpc/Errors.hpp"
24#include "rpc/RPCHelpers.hpp"
25#include "rpc/WorkQueue.hpp"
26#include "rpc/common/HandlerProvider.hpp"
27#include "rpc/common/Types.hpp"
28#include "rpc/common/impl/ForwardingProxy.hpp"
29#include "util/ResponseExpirationCache.hpp"
30#include "util/log/Logger.hpp"
31#include "web/Context.hpp"
32#include "web/dosguard/DOSGuardInterface.hpp"
33
34#include <boost/asio/spawn.hpp>
35#include <boost/iterator/transform_iterator.hpp>
36#include <boost/json.hpp>
37#include <fmt/core.h>
38#include <fmt/format.h>
39#include <xrpl/protocol/ErrorCodes.h>
40
41#include <chrono>
42#include <exception>
43#include <functional>
44#include <memory>
45#include <optional>
46#include <string>
47#include <unordered_set>
48#include <utility>
49
53namespace rpc {
54
58template <typename LoadBalancerType, typename CountersType>
59class RPCEngine {
60 util::Logger perfLog_{"Performance"};
61 util::Logger log_{"RPC"};
62
63 std::shared_ptr<BackendInterface> backend_;
64 std::reference_wrapper<web::dosguard::DOSGuardInterface const> dosGuard_;
65 std::reference_wrapper<WorkQueue> workQueue_;
66 std::reference_wrapper<CountersType> counters_;
67
68 std::shared_ptr<HandlerProvider const> handlerProvider_;
69
71
72 std::optional<util::ResponseExpirationCache> responseCache_;
73
74public:
88 std::shared_ptr<BackendInterface> const& backend,
89 std::shared_ptr<LoadBalancerType> const& balancer,
91 WorkQueue& workQueue,
92 CountersType& counters,
93 std::shared_ptr<HandlerProvider const> const& handlerProvider
94 )
95 : backend_{backend}
96 , dosGuard_{std::cref(dosGuard)}
97 , workQueue_{std::ref(workQueue)}
98 , counters_{std::ref(counters)}
99 , handlerProvider_{handlerProvider}
100 , forwardingProxy_{balancer, counters, handlerProvider}
101 {
102 // Let main thread catch the exception if config type is wrong
103 auto const cacheTimeout = config.get<float>("rpc.cache_timeout");
104
105 if (cacheTimeout > 0.f) {
106 LOG(log_.info()) << fmt::format("Init RPC Cache, timeout: {} seconds", cacheTimeout);
107
108 responseCache_.emplace(
110 std::unordered_set<std::string>{"server_info"}
111 );
112 }
113 }
114
127 static std::shared_ptr<RPCEngine>
130 std::shared_ptr<BackendInterface> const& backend,
131 std::shared_ptr<LoadBalancerType> const& balancer,
132 web::dosguard::DOSGuardInterface const& dosGuard,
133 WorkQueue& workQueue,
134 CountersType& counters,
135 std::shared_ptr<HandlerProvider const> const& handlerProvider
136 )
137 {
138 return std::make_shared<RPCEngine>(config, backend, balancer, dosGuard, workQueue, counters, handlerProvider);
139 }
140
147 Result
149 {
150 if (forwardingProxy_.shouldForward(ctx)) {
151 // Disallow forwarding of the admin api, only user api is allowed for security reasons.
152 if (isAdminCmd(ctx.method, ctx.params))
153 return Result{Status{RippledError::rpcNO_PERMISSION}};
154
155 return forwardingProxy_.forward(ctx);
156 }
157
158 if (not ctx.isAdmin and responseCache_) {
159 if (auto res = responseCache_->get(ctx.method); res.has_value())
160 return Result{std::move(res).value()};
161 }
162
163 if (backend_->isTooBusy()) {
164 LOG(log_.error()) << "Database is too busy. Rejecting request";
165 notifyTooBusy(); // TODO: should we add ctx.method if we have it?
166 return Result{Status{RippledError::rpcTOO_BUSY}};
167 }
168
169 auto const method = handlerProvider_->getHandler(ctx.method);
170 if (!method) {
172 return Result{Status{RippledError::rpcUNKNOWN_COMMAND}};
173 }
174
175 try {
176 LOG(perfLog_.debug()) << ctx.tag() << " start executing rpc `" << ctx.method << '`';
177
178 auto const context = Context{
179 .yield = ctx.yield,
180 .session = ctx.session,
181 .isAdmin = ctx.isAdmin,
182 .clientIp = ctx.clientIp,
183 .apiVersion = ctx.apiVersion
184 };
185 auto v = (*method).process(ctx.params, context);
186
187 LOG(perfLog_.debug()) << ctx.tag() << " finish executing rpc `" << ctx.method << '`';
188
189 if (not v) {
190 notifyErrored(ctx.method);
191 } else if (not ctx.isAdmin and responseCache_) {
192 responseCache_->put(ctx.method, v.result->as_object());
193 }
194
195 return Result{std::move(v)};
196 } catch (data::DatabaseTimeout const& t) {
197 LOG(log_.error()) << "Database timeout";
199
200 return Result{Status{RippledError::rpcTOO_BUSY}};
201 } catch (std::exception const& ex) {
202 LOG(log_.error()) << ctx.tag() << "Caught exception: " << ex.what();
204
205 return Result{Status{RippledError::rpcINTERNAL}};
206 }
207 }
208
217 template <typename FnType>
218 bool
219 post(FnType&& func, std::string const& ip)
220 {
221 return workQueue_.get().postCoro(std::forward<FnType>(func), dosGuard_.get().isWhiteListed(ip));
222 }
223
230 void
231 notifyComplete(std::string const& method, std::chrono::microseconds const& duration)
232 {
233 if (validHandler(method))
234 counters_.get().rpcComplete(method, duration);
235 }
236
244 void
245 notifyFailed(std::string const& method)
246 {
247 // FIXME: seems like this is not used?
248 if (validHandler(method))
249 counters_.get().rpcFailed(method);
250 }
251
259 void
260 notifyErrored(std::string const& method)
261 {
262 if (validHandler(method))
263 counters_.get().rpcErrored(method);
264 }
265
269 void
271 {
272 counters_.get().onTooBusy();
273 }
274
280 void
282 {
283 counters_.get().onNotReady();
284 }
285
289 void
291 {
292 counters_.get().onBadSyntax();
293 }
294
298 void
300 {
301 counters_.get().onUnknownCommand();
302 }
303
307 void
309 {
310 counters_.get().onInternalError();
311 }
312
313private:
314 bool
315 validHandler(std::string const& method) const
316 {
317 return handlerProvider_->contains(method) || forwardingProxy_.isProxied(method);
318 }
319};
320
321} // namespace rpc
Represents a database timeout error.
Definition BackendInterface.hpp:56
The RPC engine that ties all RPC-related functionality together.
Definition RPCEngine.hpp:59
RPCEngine(util::config::ClioConfigDefinition const &config, std::shared_ptr< BackendInterface > const &backend, std::shared_ptr< LoadBalancerType > const &balancer, web::dosguard::DOSGuardInterface const &dosGuard, WorkQueue &workQueue, CountersType &counters, std::shared_ptr< HandlerProvider const > const &handlerProvider)
Construct a new RPCEngine object.
Definition RPCEngine.hpp:86
void notifyComplete(std::string const &method, std::chrono::microseconds const &duration)
Notify the system that specified method was executed.
Definition RPCEngine.hpp:231
void notifyTooBusy()
Notify the system that the RPC system is too busy to handle an incoming request.
Definition RPCEngine.hpp:270
void notifyErrored(std::string const &method)
Notify the system that specified method failed due to some unrecoverable error.
Definition RPCEngine.hpp:260
void notifyUnknownCommand()
Notify the system that the incoming request specified an unknown/unsupported method/command.
Definition RPCEngine.hpp:299
void notifyNotReady()
Notify the system that the RPC system was not ready to handle an incoming request.
Definition RPCEngine.hpp:281
void notifyFailed(std::string const &method)
Notify the system that specified method failed to execute due to a recoverable user error.
Definition RPCEngine.hpp:245
void notifyInternalError()
Notify the system that the incoming request lead to an internal error (unrecoverable).
Definition RPCEngine.hpp:308
bool post(FnType &&func, std::string const &ip)
Used to schedule request processing onto the work queue.
Definition RPCEngine.hpp:219
static std::shared_ptr< RPCEngine > makeRPCEngine(util::config::ClioConfigDefinition const &config, std::shared_ptr< BackendInterface > const &backend, std::shared_ptr< LoadBalancerType > const &balancer, web::dosguard::DOSGuardInterface const &dosGuard, WorkQueue &workQueue, CountersType &counters, std::shared_ptr< HandlerProvider const > const &handlerProvider)
Factory function to create a new instance of the RPC engine.
Definition RPCEngine.hpp:128
void notifyBadSyntax()
Notify the system that the incoming request did not specify the RPC method/command.
Definition RPCEngine.hpp:290
Result buildResponse(web::Context const &ctx)
Main request processor routine.
Definition RPCEngine.hpp:148
An asynchronous, thread-safe queue for RPC requests.
Definition WorkQueue.hpp:46
Definition ForwardingProxy.hpp:38
A simple thread-safe logger for the channel specified in the constructor.
Definition Logger.hpp:110
Pump error(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::ERR severity.
Definition Logger.cpp:215
Pump debug(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::DBG severity.
Definition Logger.cpp:200
Pump info(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::NFO severity.
Definition Logger.cpp:205
BaseTagDecorator const & tag() const
Getter for tag decorator.
Definition Taggable.hpp:267
All the config data will be stored and extracted from this class.
Definition ConfigDefinition.hpp:54
static std::chrono::milliseconds toMilliseconds(float value)
Method to convert a float seconds value to milliseconds.
Definition ConfigDefinition.cpp:122
T get(std::string_view fullKey) const
Returns the specified value of given string if value exists.
Definition ConfigDefinition.hpp:108
The interface of a denial of service guard.
Definition DOSGuardInterface.hpp:44
This namespace contains all the RPC logic and handlers.
Definition AMMHelpers.cpp:36
bool isAdminCmd(std::string const &method, boost::json::object const &request)
Check whether a request requires administrative privileges on rippled side.
Definition RPCHelpers.cpp:1345
Context of an RPC call.
Definition Types.hpp:118
Result type used to return responses or error statuses to the Webserver subsystem.
Definition Types.hpp:129
A status returned from any RPC handler.
Definition Errors.hpp:82
Context that is used by the Webserver to pass around information about an incoming request.
Definition Context.hpp:40