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