xrpld
Loading...
Searching...
No Matches
JSONRPCClient.cpp
1#include <test/jtx/JSONRPCClient.h>
2
3#include <test/jtx/AbstractClient.h>
4
5#include <xrpld/core/Config.h>
6
7#include <xrpl/basics/contract.h>
8#include <xrpl/config/BasicConfig.h>
9#include <xrpl/config/Constants.h>
10#include <xrpl/json/json_reader.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/json/to_string.h>
13#include <xrpl/protocol/jss.h>
14#include <xrpl/server/Port.h>
15
16#include <boost/asio/buffer.hpp>
17#include <boost/asio/io_context.hpp>
18#include <boost/asio/ip/address_v4.hpp>
19#include <boost/asio/ip/address_v6.hpp>
20#include <boost/asio/ip/tcp.hpp>
21#include <boost/beast/core/multi_buffer.hpp>
22#include <boost/beast/http/dynamic_body.hpp>
23#include <boost/beast/http/message.hpp>
24#include <boost/beast/http/read.hpp>
25#include <boost/beast/http/string_body.hpp>
26#include <boost/beast/http/verb.hpp>
27#include <boost/beast/http/write.hpp>
28
29#include <iostream>
30#include <memory>
31#include <sstream>
32#include <stdexcept>
33#include <string>
34
35namespace xrpl::test {
36
38{
39 static boost::asio::ip::tcp::endpoint
41 {
42 auto& log = std::cerr;
43 ParsedPort common;
44 parsePort(common, cfg[Sections::kServer], log);
45 for (auto const& name : cfg.section(Sections::kServer).values())
46 {
47 if (!cfg.exists(name))
48 continue;
49 ParsedPort pp;
50 parsePort(pp, cfg[name], log);
51 if (not pp.protocol.contains("http"))
52 continue;
53 using namespace boost::asio::ip;
54 if (pp.ip && pp.ip->is_unspecified())
55 {
56 *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()}
57 : address{address_v4::loopback()};
58 }
59
60 if (!pp.port)
61 Throw<std::runtime_error>("Use fixConfigPorts with auto ports");
62
63 return {*pp.ip, *pp.port}; // NOLINT(bugprone-unchecked-optional-access)
64 }
65 Throw<std::runtime_error>("Missing HTTP port");
66 return {}; // Silence compiler control paths return value warning
67 }
68
69 template <class ConstBufferSequence>
70 static std::string
71 bufferString(ConstBufferSequence const& b)
72 {
73 using namespace boost::asio;
75 s.resize(buffer_size(b));
76 buffer_copy(buffer(&s[0], s.size()), b);
77 return s;
78 }
79
80 boost::asio::ip::tcp::endpoint ep_;
81 boost::asio::io_context ios_;
82 boost::asio::ip::tcp::socket stream_;
83 boost::beast::multi_buffer bin_;
84 boost::beast::multi_buffer bout_;
85 unsigned rpcVersion_;
86
87public:
88 explicit JSONRPCClient(Config const& cfg, unsigned rpcVersion)
89 : ep_(getEndpoint(cfg)), stream_(ios_), rpcVersion_(rpcVersion)
90 {
91 stream_.connect(ep_);
92 }
93
94 /*
95 Return value is an Object type with up to three keys:
96 status
97 error
98 result
99 */
101 invoke(std::string const& cmd, json::Value const& params) override
102 {
103 using namespace boost::beast::http;
104 using namespace boost::asio;
105 using namespace std::string_literals;
106
107 request<string_body> req;
108 req.method(boost::beast::http::verb::post);
109 req.target("/");
110 req.version(11);
111 req.insert("Content-Type", "application/json; charset=UTF-8");
112 {
114 ostr << ep_;
115 req.insert("Host", ostr.str());
116 }
117 {
118 json::Value jr;
119 jr[jss::method] = cmd;
120 if (rpcVersion_ == 2)
121 {
122 jr[jss::jsonrpc] = "2.0";
123 jr[jss::ripplerpc] = "2.0";
124 jr[jss::id] = 5;
125 }
126 if (params)
127 {
128 json::Value& ja = jr[jss::params] = json::ValueType::Array;
129 ja.append(params);
130 }
131 req.body() = to_string(jr);
132 }
133 req.prepare_payload();
134 write(stream_, req);
135
136 response<dynamic_body> res;
137 read(stream_, bin_, res);
138
139 json::Reader jr;
140 json::Value jv;
141 jr.parse(bufferString(res.body().data()), jv);
142 if (jv["result"].isMember("error"))
143 jv["error"] = jv["result"]["error"];
144 if (jv["result"].isMember("status"))
145 jv["status"] = jv["result"]["status"];
146 return jv;
147 }
148
149 [[nodiscard]] unsigned
150 version() const override
151 {
152 return rpcVersion_;
153 }
154};
155
157makeJSONRPCClient(Config const& cfg, unsigned rpcVersion)
158{
159 return std::make_unique<JSONRPCClient>(cfg, rpcVersion);
160}
161
162} // namespace xrpl::test
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
Holds unparsed configuration information.
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
Section & section(std::string const &name)
Returns the section with the given name.
std::vector< std::string > const & values() const
Returns all the values in the section.
Definition BasicConfig.h:58
JSONRPCClient(Config const &cfg, unsigned rpcVersion)
static std::string bufferString(ConstBufferSequence const &b)
unsigned version() const override
Get RPC 1.0 or RPC 2.0.
boost::asio::ip::tcp::endpoint ep_
boost::asio::ip::tcp::socket stream_
boost::beast::multi_buffer bout_
boost::beast::multi_buffer bin_
boost::asio::io_context ios_
static boost::asio::ip::tcp::endpoint getEndpoint(BasicConfig const &cfg)
json::Value invoke(std::string const &cmd, json::Value const &params) override
Submit a command synchronously.
T contains(T... args)
T make_unique(T... args)
@ Array
array value (ordered list)
Definition json_value.h:25
std::unique_ptr< AbstractClient > makeJSONRPCClient(Config const &cfg, unsigned rpcVersion)
Returns a client using JSON-RPC over HTTP/S.
void parsePort(ParsedPort &port, Section const &section, std::ostream &log)
Definition Port.cpp:195
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
T resize(T... args)
T size(T... args)
T str(T... args)
std::set< std::string, boost::beast::iless > protocol
Definition Port.h:80
std::optional< boost::asio::ip::address > ip
Definition Port.h:93
std::optional< std::uint16_t > port
Definition Port.h:94
static constexpr auto kServer
Definition Constants.h:55