21#include <test/jtx/JSONRPCClient.h>
22#include <test/jtx/WSClient.h>
23#include <test/jtx/envconfig.h>
25#include <xrpld/app/ledger/LedgerMaster.h>
26#include <xrpld/app/misc/LoadFeeTrack.h>
27#include <xrpld/app/misc/NetworkOPs.h>
28#include <xrpld/rpc/ServerHandler.h>
30#include <xrpl/basics/base64.h>
31#include <xrpl/beast/test/yield_to.h>
32#include <xrpl/json/json_reader.h>
34#include <boost/algorithm/string/predicate.hpp>
35#include <boost/asio.hpp>
36#include <boost/asio/io_context.hpp>
37#include <boost/asio/ssl.hpp>
38#include <boost/beast/core/multi_buffer.hpp>
39#include <boost/beast/http.hpp>
52 class myFields :
public boost::beast::http::fields
60 bool credentials =
false)
62 auto const section_name =
63 boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
66 p->overwrite(section_name,
"protocol", proto);
68 p->overwrite(section_name,
"admin",
"");
72 (*p)[section_name].set(
"admin_password",
"p");
73 (*p)[section_name].set(
"admin_user",
"u");
77 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
79 boost::starts_with(proto,
"h") ?
"ws" :
"http");
86 (*p)[
"server"].append(
"port_alt");
88 (*p)[
"port_alt"].set(
"port",
"7099");
89 (*p)[
"port_alt"].set(
"protocol",
"http");
100 using namespace boost::beast::http;
101 request<string_body> req;
106 req.insert(
"User-Agent",
"test");
107 req.method(boost::beast::http::verb::get);
108 req.insert(
"Upgrade",
"websocket");
120 req.insert(
"Sec-WebSocket-Version",
"13");
121 req.insert(boost::beast::http::field::connection,
"upgrade");
133 using namespace boost::beast::http;
134 request<string_body> req;
138 for (
auto const& f : fields)
139 req.insert(f.name(), f.value());
141 req.insert(
"User-Agent",
"test");
144 req.method(boost::beast::http::verb::get);
148 req.method(boost::beast::http::verb::post);
149 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
152 req.prepare_payload();
159 boost::asio::yield_context& yield,
160 boost::beast::http::request<boost::beast::http::string_body>&& req,
164 boost::beast::http::response<boost::beast::http::string_body>& resp,
165 boost::system::error_code& ec)
168 using namespace boost::beast::http;
170 ip::tcp::resolver r{ios};
171 boost::beast::multi_buffer sb;
180 ssl::context ctx{ssl::context::sslv23};
181 ctx.set_verify_mode(ssl::verify_none);
182 ssl::stream<ip::tcp::socket> ss{ios, ctx};
183 async_connect(ss.next_layer(), it, yield[ec]);
186 ss.async_handshake(ssl::stream_base::client, yield[ec]);
189 boost::beast::http::async_write(ss, req, yield[ec]);
192 async_read(ss, sb, resp, yield[ec]);
198 ip::tcp::socket sock{ios};
199 async_connect(sock, it, yield[ec]);
202 boost::beast::http::async_write(sock, req, yield[ec]);
205 async_read(sock, sb, resp, yield[ec]);
216 boost::asio::yield_context& yield,
218 boost::beast::http::response<boost::beast::http::string_body>& resp,
219 boost::system::error_code& ec)
225 yield,
makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec);
232 boost::asio::yield_context& yield,
234 boost::beast::http::response<boost::beast::http::string_body>& resp,
235 boost::system::error_code& ec,
259 bool subobject =
false)
266 jp[
"admin_user"] = user;
271 jpi[
"admin_password"] = password;
272 jp[
"admin_password"] = jpi;
276 jp[
"admin_password"] = password;
280 if (boost::starts_with(proto,
"h"))
283 jrr = jrc->invoke(
"ledger_accept", jp);
288 jrr = wsc->invoke(
"ledger_accept", jp);
301 testcase <<
"Admin request over " << proto <<
", config "
302 << (admin ?
"enabled" :
"disabled") <<
", credentials "
303 << (credentials ?
"" :
"not ") <<
"set";
308 auto const proto_ws = boost::starts_with(proto,
"w");
313 if (admin && credentials)
315 auto const user = env.
app()
316 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
319 auto const password =
321 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
326 env, proto, *user, *password +
"_")[jss::result];
328 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
330 jrr[
"error_message"] == proto_ws
332 :
"You don't have permission for this command.");
336 env, proto, *user, *password,
true)[jss::result];
338 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
340 jrr[
"error_message"] == proto_ws
342 :
"You don't have permission for this command.");
346 env, proto, *user +
"_", *password)[jss::result];
348 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
350 jrr[
"error_message"] == proto_ws
352 :
"You don't have permission for this command.");
357 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
359 jrr[
"error_message"] == proto_ws
361 :
"You don't have permission for this command.");
365 BEAST_EXPECT(jrr[
"status"] ==
"success");
371 BEAST_EXPECT(jrr[
"status"] ==
"success");
375 BEAST_EXPECT(jrr[
"status"] ==
"success");
382 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
384 jrr[
"error_message"] == proto_ws
386 :
"You don't have permission for this command.");
393 testcase(
"WS client to http server fails");
396 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
402 boost::system::error_code ec;
403 boost::beast::http::response<boost::beast::http::string_body> resp;
405 if (!BEAST_EXPECTS(!ec, ec.message()))
408 resp.result() == boost::beast::http::status::unauthorized);
413 boost::system::error_code ec;
414 boost::beast::http::response<boost::beast::http::string_body> resp;
416 if (!BEAST_EXPECTS(!ec, ec.message()))
419 resp.result() == boost::beast::http::status::unauthorized);
429 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
430 cfg->section(
"port_ws").
set(
"protocol",
"http");
436 boost::system::error_code ec;
437 boost::beast::http::response<boost::beast::http::string_body> resp;
439 if (!BEAST_EXPECTS(!ec, ec.message()))
441 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
446 boost::system::error_code ec;
447 boost::beast::http::response<boost::beast::http::string_body> resp;
449 if (!BEAST_EXPECTS(!ec, ec.message()))
451 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
458 testcase(
"Partial WS upgrade request");
461 using namespace boost::beast::http;
463 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
471 boost::system::error_code ec;
472 response<string_body> resp;
476 auto req_string = boost::lexical_cast<std::string>(req);
477 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
480 ip::tcp::resolver r{ios};
481 boost::beast::multi_buffer sb;
484 if (!BEAST_EXPECTS(!ec, ec.message()))
487 ip::tcp::socket sock{ios};
488 async_connect(sock, it, yield[ec]);
489 if (!BEAST_EXPECTS(!ec, ec.message()))
491 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
492 if (!BEAST_EXPECTS(!ec, ec.message()))
496 async_read(sock, sb, resp, yield[ec]);
504 boost::asio::yield_context& yield)
509 testcase <<
"Connect fails: " << client_protocol <<
" client to "
510 << server_protocol <<
" server";
514 boost::beast::http::response<boost::beast::http::string_body> resp;
515 boost::system::error_code ec;
516 if (boost::starts_with(client_protocol,
"h"))
518 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
526 client_protocol ==
"wss" || client_protocol ==
"wss2",
534 testAuth(
bool secure, boost::asio::yield_context& yield)
536 testcase <<
"Server with authorization, "
537 << (secure ?
"secure" :
"non-secure");
539 using namespace test::jtx;
541 (*cfg)[
"port_rpc"].set(
"user",
"me");
542 (*cfg)[
"port_rpc"].set(
"password",
"secret");
543 (*cfg)[
"port_rpc"].set(
544 "protocol", secure ?
"https" :
"http");
546 (*cfg)[
"port_ws"].set(
"protocol",
"http,ws");
551 jr[jss::method] =
"server_info";
552 boost::beast::http::response<boost::beast::http::string_body> resp;
553 boost::system::error_code ec;
555 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
558 auth.insert(
"Authorization",
"");
560 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
562 auth.set(
"Authorization",
"Basic NOT-VALID");
564 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
568 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
570 auto const user = env.
app()
582 auth.set(
"Authorization",
"Basic " + user +
":" +
pass);
584 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
589 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
590 BEAST_EXPECT(!resp.body().empty());
596 testcase <<
"Server with connection limit of " << limit;
598 using namespace test::jtx;
600 using namespace boost::beast::http;
611 boost::system::error_code ec;
613 ip::tcp::resolver r{ios};
616 jr[jss::method] =
"server_info";
623 int connectionCount{1};
631 int testTo = (limit == 0) ? 50 : limit + 1;
632 while (connectionCount < testTo)
635 ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
636 async_connect(clients.
back().first, it, yield[ec]);
639 async_write(clients.
back().first, req, yield[ec]);
645 for (
auto& [soc, buf] : clients)
647 boost::beast::http::response<boost::beast::http::string_body> resp;
648 async_read(soc, buf, resp, yield[ec]);
653 (limit == 0 || readCount < limit - 1) ? (!ec) : bool(ec));
660 testcase(
"Connection with WS handoff");
662 using namespace test::jtx;
664 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
672 boost::beast::http::response<boost::beast::http::string_body> resp;
673 boost::system::error_code ec;
676 resp.result() == boost::beast::http::status::switching_protocols);
678 resp.find(
"Upgrade") != resp.end() &&
679 resp[
"Upgrade"] ==
"websocket");
681 resp.find(
"Connection") != resp.end() &&
682 boost::iequals(resp[
"Connection"],
"upgrade"));
688 testcase(
"Connection to port with no RPC enabled");
690 using namespace test::jtx;
697 boost::beast::http::response<boost::beast::http::string_body> resp;
698 boost::system::error_code ec;
709 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
710 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
716 testcase(
"WS client sends assorted input");
718 using namespace test::jtx;
720 using namespace boost::beast::http;
727 boost::system::error_code ec;
730 ip::tcp::resolver r{ios};
733 if (!BEAST_EXPECT(!ec))
736 ip::tcp::socket sock{ios};
737 async_connect(sock, it, yield[ec]);
738 if (!BEAST_EXPECT(!ec))
741 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
746 ws.async_write_some(
true, buffer(req), yield[ec]);
747 if (!BEAST_EXPECT(!ec))
750 boost::beast::multi_buffer sb;
751 ws.async_read(sb, yield[ec]);
752 if (!BEAST_EXPECT(!ec))
757 if (!BEAST_EXPECT(jr.
parse(
758 boost::lexical_cast<std::string>(
759 boost::beast::make_printable(sb.data())),
762 sb.consume(sb.size());
767 auto resp = sendAndParse(
"NOT JSON");
769 resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
770 BEAST_EXPECT(!resp.isMember(jss::status));
775 jv[jss::command] =
"foo";
776 jv[jss::method] =
"bar";
779 resp.isMember(jss::error) &&
780 resp[jss::error] ==
"missingCommand");
782 resp.isMember(jss::status) && resp[jss::status] ==
"error");
787 jv[jss::command] =
"ping";
790 resp.isMember(jss::status) && resp[jss::status] ==
"success");
792 resp.isMember(jss::result) &&
793 resp[jss::result].isMember(jss::role) &&
794 resp[jss::result][jss::role] ==
"admin");
802 "Status request over WS and RPC with/without Amendment Warning");
805 using namespace boost::beast::http;
810 cfg->section(
"port_rpc").
set(
"protocol",
"http");
824 auto si = env.
rpc(
"server_info")[jss::result];
825 BEAST_EXPECT(si.isMember(jss::info));
826 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
829 BEAST_EXPECT(!si.isMember(jss::warnings));
833 si = env.
rpc(
"server_state")[jss::result];
834 BEAST_EXPECT(si.isMember(jss::state));
835 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
838 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
844 boost::system::error_code ec;
845 response<string_body> resp;
856 if (!BEAST_EXPECTS(!ec, ec.message()))
858 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
860 resp.body().find(
"connectivity is working.") != std::string::npos);
872 si = env.
rpc(
"server_info")[jss::result];
873 BEAST_EXPECT(si.isMember(jss::info));
874 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
876 si[jss::info].isMember(jss::warnings) &&
877 si[jss::info][jss::warnings].isArray() &&
878 si[jss::info][jss::warnings].size() == 1 &&
879 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
884 si = env.
rpc(
"server_state")[jss::result];
885 BEAST_EXPECT(si.isMember(jss::state));
886 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
888 si[jss::state].isMember(jss::warnings) &&
889 si[jss::state][jss::warnings].isArray() &&
890 si[jss::state][jss::warnings].size() == 1 &&
891 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
904 if (!BEAST_EXPECTS(!ec, ec.message()))
906 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
908 resp.body().find(
"connectivity is working.") != std::string::npos);
922 if (!BEAST_EXPECTS(!ec, ec.message()))
924 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
926 resp.body().find(
"connectivity is working.") != std::string::npos);
932 testcase(
"Status request over WS and RPC with/without Amendment Block");
935 using namespace boost::beast::http;
940 cfg->section(
"port_rpc").
set(
"protocol",
"http");
954 auto si = env.
rpc(
"server_info")[jss::result];
955 BEAST_EXPECT(si.isMember(jss::info));
956 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
959 BEAST_EXPECT(!si.isMember(jss::warnings));
963 si = env.
rpc(
"server_state")[jss::result];
964 BEAST_EXPECT(si.isMember(jss::state));
965 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
968 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
974 boost::system::error_code ec;
975 response<string_body> resp;
986 if (!BEAST_EXPECTS(!ec, ec.message()))
988 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
990 resp.body().find(
"connectivity is working.") != std::string::npos);
1002 si = env.
rpc(
"server_info")[jss::result];
1003 BEAST_EXPECT(si.isMember(jss::info));
1005 si[jss::info].isMember(jss::amendment_blocked) &&
1006 si[jss::info][jss::amendment_blocked] ==
true);
1008 si[jss::info].isMember(jss::warnings) &&
1009 si[jss::info][jss::warnings].isArray() &&
1010 si[jss::info][jss::warnings].size() == 1 &&
1011 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
1015 si = env.
rpc(
"server_state")[jss::result];
1017 si[jss::state].isMember(jss::amendment_blocked) &&
1018 si[jss::state][jss::amendment_blocked] ==
true);
1020 si[jss::state].isMember(jss::warnings) &&
1021 si[jss::state][jss::warnings].isArray() &&
1022 si[jss::state][jss::warnings].size() == 1 &&
1023 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
1037 if (!BEAST_EXPECTS(!ec, ec.message()))
1039 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
1041 resp.body().find(
"connectivity is working.") != std::string::npos);
1054 if (!BEAST_EXPECTS(!ec, ec.message()))
1057 resp.result() == boost::beast::http::status::internal_server_error);
1059 resp.body().find(
"cannot accept clients:") != std::string::npos);
1061 resp.body().find(
"Server version too old") != std::string::npos);
1067 testcase(
"RPC client sends assorted input");
1069 using namespace test::jtx;
1072 boost::system::error_code ec;
1074 boost::beast::http::response<boost::beast::http::string_body> resp;
1077 resp.result() == boost::beast::http::status::bad_request);
1078 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1082 boost::beast::http::response<boost::beast::http::string_body> resp;
1087 resp.result() == boost::beast::http::status::bad_request);
1088 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1092 boost::beast::http::response<boost::beast::http::string_body> resp;
1097 resp.result() == boost::beast::http::status::bad_request);
1098 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1102 boost::beast::http::response<boost::beast::http::string_body> resp;
1109 resp.result() == boost::beast::http::status::bad_request);
1110 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1114 boost::beast::http::response<boost::beast::http::string_body> resp;
1116 jv[jss::method] =
"batch";
1117 jv[jss::params] = 2;
1120 resp.result() == boost::beast::http::status::bad_request);
1121 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1125 boost::beast::http::response<boost::beast::http::string_body> resp;
1127 jv[jss::method] =
"batch";
1129 jv[jss::params][
"invalid"] = 3;
1132 resp.result() == boost::beast::http::status::bad_request);
1133 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1138 boost::beast::http::response<boost::beast::http::string_body> resp;
1142 resp.result() == boost::beast::http::status::bad_request);
1143 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1147 boost::beast::http::response<boost::beast::http::string_body> resp;
1148 jv[jss::method] = 1;
1151 resp.result() == boost::beast::http::status::bad_request);
1152 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
1156 boost::beast::http::response<boost::beast::http::string_body> resp;
1157 jv[jss::method] =
"";
1160 resp.result() == boost::beast::http::status::bad_request);
1161 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
1165 boost::beast::http::response<boost::beast::http::string_body> resp;
1166 jv[jss::method] =
"some_method";
1167 jv[jss::params] =
"params";
1170 resp.result() == boost::beast::http::status::bad_request);
1171 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1175 boost::beast::http::response<boost::beast::http::string_body> resp;
1177 jv[jss::params][0u] =
"not an object";
1180 resp.result() == boost::beast::http::status::bad_request);
1181 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1188 testcase(
"Server status not okay");
1190 using namespace test::jtx;
1192 cfg->ELB_SUPPORT =
true;
1199 boost::beast::http::response<boost::beast::http::string_body> resp;
1200 boost::system::error_code ec;
1203 resp.result() == boost::beast::http::status::internal_server_error);
1204 std::regex body{
"Server cannot accept clients"};
1212 for (
auto it : {
"http",
"ws",
"ws2"})
1219 yield_to([&](boost::asio::yield_context& yield) {
1248BEAST_DEFINE_TESTSUITE(ServerStatus, server,
ripple);
Unserialize a JSON document into a Value.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Value & append(Value const &value)
Append value to array at the end.
Mix-in to support tests using asio coroutines.
boost::asio::io_context & get_io_context()
Return the io_context associated with the object.
void yield_to(F0 &&f0, FN &&... fn)
Run one or more functions, each in a coroutine.
void pass()
Record a successful test condition.
testcase_t testcase
Memberspace for declaring test cases.
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual NetworkOPs & getOPs()=0
virtual LedgerMaster & getLedgerMaster()=0
Section & section(std::string const &name)
Returns the section with the given name.
virtual void setAmendmentWarned()=0
virtual Json::Value getConsensusInfo()=0
virtual void setAmendmentBlocked()=0
virtual bool beginConsensus(uint256 const &netLCL, std::unique_ptr< std::stringstream > const &clog)=0
std::optional< T > get(std::string const &name) const
void testAmendmentBlock(boost::asio::yield_context &yield)
void testCantConnect(std::string const &client_protocol, std::string const &server_protocol, boost::asio::yield_context &yield)
void testAuth(bool secure, boost::asio::yield_context &yield)
auto makeHTTPRequest(std::string const &host, uint16_t port, std::string const &body, myFields const &fields)
void testRPCRequests(boost::asio::yield_context &yield)
void testStatusRequest(boost::asio::yield_context &yield)
void testAmendmentWarning(boost::asio::yield_context &yield)
void testTruncatedWSUpgrade(boost::asio::yield_context &yield)
auto makeConfig(std::string const &proto, bool admin=true, bool credentials=false)
void doRequest(boost::asio::yield_context &yield, boost::beast::http::request< boost::beast::http::string_body > &&req, std::string const &host, uint16_t port, bool secure, boost::beast::http::response< boost::beast::http::string_body > &resp, boost::system::error_code &ec)
void doWSRequest(test::jtx::Env &env, boost::asio::yield_context &yield, bool secure, boost::beast::http::response< boost::beast::http::string_body > &resp, boost::system::error_code &ec)
void testWSRequests(boost::asio::yield_context &yield)
void testLimit(boost::asio::yield_context &yield, int limit)
void testWSHandoff(boost::asio::yield_context &yield)
void testNoRPC(boost::asio::yield_context &yield)
void testStatusNotOkay(boost::asio::yield_context &yield)
void testWSClientToHttpServer(boost::asio::yield_context &yield)
void run() override
Runs the suite.
auto makeAdminRequest(jtx::Env &env, std::string const &proto, std::string const &user, std::string const &password, bool subobject=false)
auto makeWSUpgrade(std::string const &host, uint16_t port)
void doHTTPRequest(test::jtx::Env &env, boost::asio::yield_context &yield, bool secure, boost::beast::http::response< boost::beast::http::string_body > &resp, boost::system::error_code &ec, std::string const &body="", myFields const &fields={})
void testAdminRequest(std::string const &proto, bool admin, bool credentials)
A transaction testing environment.
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
T emplace_back(T... args)
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
char const * getEnvLocalhostAddr()
std::unique_ptr< AbstractClient > makeJSONRPCClient(Config const &cfg, unsigned rpc_version)
Returns a client using JSON-RPC over HTTP/S.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
@ warnRPC_UNSUPPORTED_MAJORITY
@ warnRPC_AMENDMENT_BLOCKED
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
std::string base64_encode(std::uint8_t const *data, std::size_t len)
std::string to_string(base_uint< Bits, Tag > const &a)
T regex_search(T... args)