Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
ErrorHandling.hpp
1#pragma once
2
3#include "rpc/Errors.hpp"
4#include "rpc/JS.hpp"
5#include "util/Assert.hpp"
6#include "web/interface/ConnectionBase.hpp"
7
8#include <boost/beast/http/status.hpp>
9#include <boost/json/object.hpp>
10#include <boost/json/serialize.hpp>
11#include <fmt/format.h>
12#include <xrpl/protocol/ErrorCodes.h>
13#include <xrpl/protocol/jss.h>
14
15#include <memory>
16#include <optional>
17#include <string>
18#include <utility>
19#include <variant>
20
21namespace web::impl {
22
26class ErrorHelper {
27 std::shared_ptr<web::ConnectionBase> connection_;
28 std::optional<boost::json::object> request_;
29
30public:
31 ErrorHelper(
32 std::shared_ptr<web::ConnectionBase> const& connection,
33 std::optional<boost::json::object> request = std::nullopt
34 )
35 : connection_{connection}, request_{std::move(request)}
36 {
37 }
38
39 void
40 sendError(rpc::Status const& err) const
41 {
42 if (connection_->upgraded) {
43 connection_->send(boost::json::serialize(composeError(err)));
44 } else {
45 // Note: a collection of crutches to match rippled output follows
46 if (auto const clioCode = std::get_if<rpc::ClioError>(&err.code)) {
47 switch (*clioCode) {
48 case rpc::ClioError::RpcInvalidApiVersion:
49 connection_->send(
50 std::string{rpc::getErrorInfo(*clioCode).error},
51 boost::beast::http::status::bad_request
52 );
53 break;
54 case rpc::ClioError::RpcCommandIsMissing:
55 connection_->send("Null method", boost::beast::http::status::bad_request);
56 break;
57 case rpc::ClioError::RpcCommandIsEmpty:
58 connection_->send(
59 "method is empty", boost::beast::http::status::bad_request
60 );
61 break;
62 case rpc::ClioError::RpcCommandNotString:
63 connection_->send(
64 "method is not string", boost::beast::http::status::bad_request
65 );
66 break;
67 case rpc::ClioError::RpcParamsUnparsable:
68 connection_->send(
69 "params unparsable", boost::beast::http::status::bad_request
70 );
71 break;
72
73 // others are not applicable but we want a compilation error next time we add
74 // one
75 case rpc::ClioError::RpcUnknownOption:
76 case rpc::ClioError::RpcMalformedCurrency:
77 case rpc::ClioError::RpcMalformedRequest:
78 case rpc::ClioError::RpcMalformedOwner:
79 case rpc::ClioError::RpcMalformedAddress:
80 case rpc::ClioError::RpcFieldNotFoundTransaction:
81 case rpc::ClioError::RpcMalformedOracleDocumentId:
82 case rpc::ClioError::RpcMalformedAuthorizedCredentials:
83 case rpc::ClioError::EtlConnectionError:
84 case rpc::ClioError::EtlRequestError:
85 case rpc::ClioError::EtlRequestTimeout:
86 case rpc::ClioError::EtlInvalidResponse:
87 ASSERT(
88 false, "Unknown rpc error code {}", static_cast<int>(*clioCode)
89 ); // this should never happen
90 break;
91 }
92 } else {
93 connection_->send(
94 boost::json::serialize(composeError(err)),
95 boost::beast::http::status::bad_request
96 );
97 }
98 }
99 }
100
101 void
102 sendInternalError() const
103 {
104 connection_->send(
105 boost::json::serialize(composeError(rpc::RippledError::rpcINTERNAL)),
106 boost::beast::http::status::internal_server_error
107 );
108 }
109
110 void
111 sendNotReadyError() const
112 {
113 connection_->send(
114 boost::json::serialize(composeError(rpc::RippledError::rpcNOT_READY)),
115 boost::beast::http::status::ok
116 );
117 }
118
119 void
120 sendTooBusyError() const
121 {
122 if (connection_->upgraded) {
123 connection_->send(
124 boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)),
125 boost::beast::http::status::ok
126 );
127 } else {
128 connection_->send(
129 boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)),
130 boost::beast::http::status::service_unavailable
131 );
132 }
133 }
134
135 void
136 sendJsonParsingError() const
137 {
138 if (connection_->upgraded) {
139 connection_->send(
140 boost::json::serialize(rpc::makeError(rpc::RippledError::rpcBAD_SYNTAX))
141 );
142 } else {
143 connection_->send(
144 fmt::format("Unable to parse JSON from the request"),
145 boost::beast::http::status::bad_request
146 );
147 }
148 }
149
150 boost::json::object
151 composeError(auto const& error) const
152 {
153 auto e = rpc::makeError(error);
154
155 if (request_) {
156 auto const appendFieldIfExist = [&](auto const& field) {
157 if (request_->contains(field) and not request_->at(field).is_null())
158 e[field] = request_->at(field);
159 };
160
161 appendFieldIfExist(JS(id));
162
163 if (connection_->upgraded)
164 appendFieldIfExist(JS(api_version));
165
166 e[JS(request)] = request_.value();
167 }
168
169 if (connection_->upgraded) {
170 return e;
171 }
172 return {{JS(result), e}};
173 }
174};
175
176} // namespace web::impl
ClioErrorInfo const & getErrorInfo(ClioError code)
Get the error info object from an clio-specific error code.
Definition Errors.cpp:94
boost::json::object makeError(RippledError err, std::optional< std::string_view > customError, std::optional< std::string_view > customMessage)
Generate JSON from a rpc::RippledError.
Definition Errors.cpp:160
A status returned from any RPC handler.
Definition Errors.hpp:65