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
46 std::shared_ptr<web::ConnectionBase> connection_;
47 std::optional<boost::json::object> request_;
48
49public:
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}, boost::beast::http::status::bad_request
70 );
71 break;
72 case rpc::ClioError::RpcCommandIsMissing:
73 connection_->send("Null method", boost::beast::http::status::bad_request);
74 break;
75 case rpc::ClioError::RpcCommandIsEmpty:
76 connection_->send("method is empty", boost::beast::http::status::bad_request);
77 break;
78 case rpc::ClioError::RpcCommandNotString:
79 connection_->send("method is not string", boost::beast::http::status::bad_request);
80 break;
81 case rpc::ClioError::RpcParamsUnparsable:
82 connection_->send("params unparsable", boost::beast::http::status::bad_request);
83 break;
84
85 // others are not applicable but we want a compilation error next time we add one
86 case rpc::ClioError::RpcUnknownOption:
87 case rpc::ClioError::RpcMalformedCurrency:
88 case rpc::ClioError::RpcMalformedRequest:
89 case rpc::ClioError::RpcMalformedOwner:
90 case rpc::ClioError::RpcMalformedAddress:
91 case rpc::ClioError::RpcFieldNotFoundTransaction:
92 case rpc::ClioError::RpcMalformedOracleDocumentId:
93 case rpc::ClioError::RpcMalformedAuthorizedCredentials:
94 case rpc::ClioError::RpcEntryNotFound:
95 case rpc::ClioError::EtlConnectionError:
96 case rpc::ClioError::EtlRequestError:
97 case rpc::ClioError::EtlRequestTimeout:
98 case rpc::ClioError::EtlInvalidResponse:
99 ASSERT(
100 false, "Unknown rpc error code {}", static_cast<int>(*clioCode)
101 ); // this should never happen
102 break;
103 }
104 } else {
105 connection_->send(boost::json::serialize(composeError(err)), boost::beast::http::status::bad_request);
106 }
107 }
108 }
109
110 void
111 sendInternalError() const
112 {
113 connection_->send(
114 boost::json::serialize(composeError(rpc::RippledError::rpcINTERNAL)),
115 boost::beast::http::status::internal_server_error
116 );
117 }
118
119 void
120 sendNotReadyError() const
121 {
122 connection_->send(
123 boost::json::serialize(composeError(rpc::RippledError::rpcNOT_READY)), boost::beast::http::status::ok
124 );
125 }
126
127 void
128 sendTooBusyError() const
129 {
130 if (connection_->upgraded) {
131 connection_->send(
132 boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)), boost::beast::http::status::ok
133 );
134 } else {
135 connection_->send(
136 boost::json::serialize(rpc::makeError(rpc::RippledError::rpcTOO_BUSY)),
137 boost::beast::http::status::service_unavailable
138 );
139 }
140 }
141
142 void
143 sendJsonParsingError() const
144 {
145 if (connection_->upgraded) {
146 connection_->send(boost::json::serialize(rpc::makeError(rpc::RippledError::rpcBAD_SYNTAX)));
147 } else {
148 connection_->send(
149 fmt::format("Unable to parse JSON from the request"), boost::beast::http::status::bad_request
150 );
151 }
152 }
153
154 boost::json::object
155 composeError(auto const& error) const
156 {
157 auto e = rpc::makeError(error);
158
159 if (request_) {
160 auto const appendFieldIfExist = [&](auto const& field) {
161 if (request_->contains(field) and not request_->at(field).is_null())
162 e[field] = request_->at(field);
163 };
164
165 appendFieldIfExist(JS(id));
166
167 if (connection_->upgraded)
168 appendFieldIfExist(JS(api_version));
169
170 e[JS(request)] = request_.value();
171 }
172
173 if (connection_->upgraded) {
174 return e;
175 }
176 return {{JS(result), e}};
177 }
178};
179
180} // namespace web::impl
A helper that attempts to match rippled reporting mode HTTP errors as close as possible.
Definition ErrorHandling.hpp:45
ClioErrorInfo const & getErrorInfo(ClioError code)
Get the error info object from an clio-specific error code.
Definition Errors.cpp:75
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:120
A status returned from any RPC handler.
Definition Errors.hpp:83