2#include <test/jtx/JSONRPCClient.h>
3#include <test/jtx/WSClient.h>
4#include <test/jtx/envconfig.h>
6#include <xrpld/app/ledger/LedgerMaster.h>
7#include <xrpld/app/misc/LoadFeeTrack.h>
8#include <xrpld/app/misc/NetworkOPs.h>
9#include <xrpld/rpc/ServerHandler.h>
11#include <xrpl/basics/base64.h>
12#include <xrpl/beast/test/yield_to.h>
13#include <xrpl/json/json_reader.h>
15#include <boost/algorithm/string/predicate.hpp>
16#include <boost/asio.hpp>
17#include <boost/asio/io_context.hpp>
18#include <boost/asio/ssl.hpp>
19#include <boost/beast/core/multi_buffer.hpp>
20#include <boost/beast/http.hpp>
33 class myFields :
public boost::beast::http::fields
41 bool credentials =
false)
43 auto const section_name =
44 boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
47 p->overwrite(section_name,
"protocol", proto);
49 p->overwrite(section_name,
"admin",
"");
53 (*p)[section_name].set(
"admin_password",
"p");
54 (*p)[section_name].set(
"admin_user",
"u");
58 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
60 boost::starts_with(proto,
"h") ?
"ws" :
"http");
67 (*p)[
"server"].append(
"port_alt");
69 (*p)[
"port_alt"].set(
"port",
"7099");
70 (*p)[
"port_alt"].set(
"protocol",
"http");
81 using namespace boost::beast::http;
82 request<string_body> req;
87 req.insert(
"User-Agent",
"test");
88 req.method(boost::beast::http::verb::get);
89 req.insert(
"Upgrade",
"websocket");
101 req.insert(
"Sec-WebSocket-Version",
"13");
102 req.insert(boost::beast::http::field::connection,
"upgrade");
114 using namespace boost::beast::http;
115 request<string_body> req;
119 for (
auto const& f : fields)
120 req.insert(f.name(), f.value());
122 req.insert(
"User-Agent",
"test");
125 req.method(boost::beast::http::verb::get);
129 req.method(boost::beast::http::verb::post);
130 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
133 req.prepare_payload();
140 boost::asio::yield_context& yield,
141 boost::beast::http::request<boost::beast::http::string_body>&& req,
145 boost::beast::http::response<boost::beast::http::string_body>& resp,
146 boost::system::error_code& ec)
149 using namespace boost::beast::http;
151 ip::tcp::resolver r{ios};
152 boost::beast::multi_buffer sb;
161 ssl::context ctx{ssl::context::sslv23};
162 ctx.set_verify_mode(ssl::verify_none);
163 ssl::stream<ip::tcp::socket> ss{ios, ctx};
164 async_connect(ss.next_layer(), it, yield[ec]);
167 ss.async_handshake(ssl::stream_base::client, yield[ec]);
170 boost::beast::http::async_write(ss, req, yield[ec]);
173 async_read(ss, sb, resp, yield[ec]);
179 ip::tcp::socket sock{ios};
180 async_connect(sock, it, yield[ec]);
183 boost::beast::http::async_write(sock, req, yield[ec]);
186 async_read(sock, sb, resp, yield[ec]);
197 boost::asio::yield_context& yield,
199 boost::beast::http::response<boost::beast::http::string_body>& resp,
200 boost::system::error_code& ec)
206 yield,
makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec);
213 boost::asio::yield_context& yield,
215 boost::beast::http::response<boost::beast::http::string_body>& resp,
216 boost::system::error_code& ec,
240 bool subobject =
false)
247 jp[
"admin_user"] = user;
252 jpi[
"admin_password"] = password;
253 jp[
"admin_password"] = jpi;
257 jp[
"admin_password"] = password;
261 if (boost::starts_with(proto,
"h"))
264 jrr = jrc->invoke(
"ledger_accept", jp);
269 jrr = wsc->invoke(
"ledger_accept", jp);
282 testcase <<
"Admin request over " << proto <<
", config "
283 << (admin ?
"enabled" :
"disabled") <<
", credentials "
284 << (credentials ?
"" :
"not ") <<
"set";
289 auto const proto_ws = boost::starts_with(proto,
"w");
294 if (admin && credentials)
296 auto const user = env.
app()
297 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
300 auto const password =
302 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
307 env, proto, *user, *password +
"_")[jss::result];
309 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
311 jrr[
"error_message"] == proto_ws
313 :
"You don't have permission for this command.");
317 env, proto, *user, *password,
true)[jss::result];
319 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
321 jrr[
"error_message"] == proto_ws
323 :
"You don't have permission for this command.");
327 env, proto, *user +
"_", *password)[jss::result];
329 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
331 jrr[
"error_message"] == proto_ws
333 :
"You don't have permission for this command.");
338 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
340 jrr[
"error_message"] == proto_ws
342 :
"You don't have permission for this command.");
346 BEAST_EXPECT(jrr[
"status"] ==
"success");
352 BEAST_EXPECT(jrr[
"status"] ==
"success");
356 BEAST_EXPECT(jrr[
"status"] ==
"success");
363 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
365 jrr[
"error_message"] == proto_ws
367 :
"You don't have permission for this command.");
374 testcase(
"WS client to http server fails");
377 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
383 boost::system::error_code ec;
384 boost::beast::http::response<boost::beast::http::string_body> resp;
386 if (!BEAST_EXPECTS(!ec, ec.message()))
389 resp.result() == boost::beast::http::status::unauthorized);
394 boost::system::error_code ec;
395 boost::beast::http::response<boost::beast::http::string_body> resp;
397 if (!BEAST_EXPECTS(!ec, ec.message()))
400 resp.result() == boost::beast::http::status::unauthorized);
410 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
411 cfg->section(
"port_ws").
set(
"protocol",
"http");
417 boost::system::error_code ec;
418 boost::beast::http::response<boost::beast::http::string_body> resp;
420 if (!BEAST_EXPECTS(!ec, ec.message()))
422 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
427 boost::system::error_code ec;
428 boost::beast::http::response<boost::beast::http::string_body> resp;
430 if (!BEAST_EXPECTS(!ec, ec.message()))
432 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
439 testcase(
"Partial WS upgrade request");
442 using namespace boost::beast::http;
444 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
452 boost::system::error_code ec;
453 response<string_body> resp;
457 auto req_string = boost::lexical_cast<std::string>(req);
458 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
461 ip::tcp::resolver r{ios};
462 boost::beast::multi_buffer sb;
465 if (!BEAST_EXPECTS(!ec, ec.message()))
468 ip::tcp::socket sock{ios};
469 async_connect(sock, it, yield[ec]);
470 if (!BEAST_EXPECTS(!ec, ec.message()))
472 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
473 if (!BEAST_EXPECTS(!ec, ec.message()))
477 async_read(sock, sb, resp, yield[ec]);
485 boost::asio::yield_context& yield)
490 testcase <<
"Connect fails: " << client_protocol <<
" client to "
491 << server_protocol <<
" server";
495 boost::beast::http::response<boost::beast::http::string_body> resp;
496 boost::system::error_code ec;
497 if (boost::starts_with(client_protocol,
"h"))
499 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
507 client_protocol ==
"wss" || client_protocol ==
"wss2",
515 testAuth(
bool secure, boost::asio::yield_context& yield)
517 testcase <<
"Server with authorization, "
518 << (secure ?
"secure" :
"non-secure");
520 using namespace test::jtx;
522 (*cfg)[
"port_rpc"].set(
"user",
"me");
523 (*cfg)[
"port_rpc"].set(
"password",
"secret");
524 (*cfg)[
"port_rpc"].set(
525 "protocol", secure ?
"https" :
"http");
527 (*cfg)[
"port_ws"].set(
"protocol",
"http,ws");
532 jr[jss::method] =
"server_info";
533 boost::beast::http::response<boost::beast::http::string_body> resp;
534 boost::system::error_code ec;
536 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
539 auth.insert(
"Authorization",
"");
541 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
543 auth.set(
"Authorization",
"Basic NOT-VALID");
545 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
549 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
551 auto const user = env.
app()
563 auth.set(
"Authorization",
"Basic " + user +
":" +
pass);
565 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
570 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
571 BEAST_EXPECT(!resp.body().empty());
577 testcase <<
"Server with connection limit of " << limit;
579 using namespace test::jtx;
581 using namespace boost::beast::http;
592 boost::system::error_code ec;
594 ip::tcp::resolver r{ios};
597 jr[jss::method] =
"server_info";
604 int connectionCount{1};
612 int testTo = (limit == 0) ? 50 : limit + 1;
613 while (connectionCount < testTo)
616 ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
617 async_connect(clients.
back().first, it, yield[ec]);
620 async_write(clients.
back().first, req, yield[ec]);
626 for (
auto& [soc, buf] : clients)
628 boost::beast::http::response<boost::beast::http::string_body> resp;
629 async_read(soc, buf, resp, yield[ec]);
634 (limit == 0 || readCount < limit - 1) ? (!ec) : bool(ec));
641 testcase(
"Connection with WS handoff");
643 using namespace test::jtx;
645 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
653 boost::beast::http::response<boost::beast::http::string_body> resp;
654 boost::system::error_code ec;
657 resp.result() == boost::beast::http::status::switching_protocols);
659 resp.find(
"Upgrade") != resp.end() &&
660 resp[
"Upgrade"] ==
"websocket");
662 resp.find(
"Connection") != resp.end() &&
663 boost::iequals(resp[
"Connection"],
"upgrade"));
669 testcase(
"Connection to port with no RPC enabled");
671 using namespace test::jtx;
678 boost::beast::http::response<boost::beast::http::string_body> resp;
679 boost::system::error_code ec;
690 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
691 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
697 testcase(
"WS client sends assorted input");
699 using namespace test::jtx;
701 using namespace boost::beast::http;
708 boost::system::error_code ec;
711 ip::tcp::resolver r{ios};
714 if (!BEAST_EXPECT(!ec))
717 ip::tcp::socket sock{ios};
718 async_connect(sock, it, yield[ec]);
719 if (!BEAST_EXPECT(!ec))
722 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
727 ws.async_write_some(
true, buffer(req), yield[ec]);
728 if (!BEAST_EXPECT(!ec))
731 boost::beast::multi_buffer sb;
732 ws.async_read(sb, yield[ec]);
733 if (!BEAST_EXPECT(!ec))
738 if (!BEAST_EXPECT(jr.
parse(
739 boost::lexical_cast<std::string>(
740 boost::beast::make_printable(sb.data())),
743 sb.consume(sb.size());
748 auto resp = sendAndParse(
"NOT JSON");
750 resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
751 BEAST_EXPECT(!resp.isMember(jss::status));
756 jv[jss::command] =
"foo";
757 jv[jss::method] =
"bar";
760 resp.isMember(jss::error) &&
761 resp[jss::error] ==
"missingCommand");
763 resp.isMember(jss::status) && resp[jss::status] ==
"error");
768 jv[jss::command] =
"ping";
771 resp.isMember(jss::status) && resp[jss::status] ==
"success");
773 resp.isMember(jss::result) &&
774 resp[jss::result].isMember(jss::role) &&
775 resp[jss::result][jss::role] ==
"admin");
783 "Status request over WS and RPC with/without Amendment Warning");
786 using namespace boost::beast::http;
791 cfg->section(
"port_rpc").
set(
"protocol",
"http");
805 auto si = env.
rpc(
"server_info")[jss::result];
806 BEAST_EXPECT(si.isMember(jss::info));
807 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
810 BEAST_EXPECT(!si.isMember(jss::warnings));
814 si = env.
rpc(
"server_state")[jss::result];
815 BEAST_EXPECT(si.isMember(jss::state));
816 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
819 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
825 boost::system::error_code ec;
826 response<string_body> resp;
837 if (!BEAST_EXPECTS(!ec, ec.message()))
839 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
841 resp.body().find(
"connectivity is working.") != std::string::npos);
853 si = env.
rpc(
"server_info")[jss::result];
854 BEAST_EXPECT(si.isMember(jss::info));
855 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
857 si[jss::info].isMember(jss::warnings) &&
858 si[jss::info][jss::warnings].isArray() &&
859 si[jss::info][jss::warnings].size() == 1 &&
860 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
865 si = env.
rpc(
"server_state")[jss::result];
866 BEAST_EXPECT(si.isMember(jss::state));
867 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
869 si[jss::state].isMember(jss::warnings) &&
870 si[jss::state][jss::warnings].isArray() &&
871 si[jss::state][jss::warnings].size() == 1 &&
872 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
885 if (!BEAST_EXPECTS(!ec, ec.message()))
887 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
889 resp.body().find(
"connectivity is working.") != std::string::npos);
903 if (!BEAST_EXPECTS(!ec, ec.message()))
905 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
907 resp.body().find(
"connectivity is working.") != std::string::npos);
913 testcase(
"Status request over WS and RPC with/without Amendment Block");
916 using namespace boost::beast::http;
921 cfg->section(
"port_rpc").
set(
"protocol",
"http");
935 auto si = env.
rpc(
"server_info")[jss::result];
936 BEAST_EXPECT(si.isMember(jss::info));
937 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
940 BEAST_EXPECT(!si.isMember(jss::warnings));
944 si = env.
rpc(
"server_state")[jss::result];
945 BEAST_EXPECT(si.isMember(jss::state));
946 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
949 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
955 boost::system::error_code ec;
956 response<string_body> resp;
967 if (!BEAST_EXPECTS(!ec, ec.message()))
969 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
971 resp.body().find(
"connectivity is working.") != std::string::npos);
983 si = env.
rpc(
"server_info")[jss::result];
984 BEAST_EXPECT(si.isMember(jss::info));
986 si[jss::info].isMember(jss::amendment_blocked) &&
987 si[jss::info][jss::amendment_blocked] ==
true);
989 si[jss::info].isMember(jss::warnings) &&
990 si[jss::info][jss::warnings].isArray() &&
991 si[jss::info][jss::warnings].size() == 1 &&
992 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
996 si = env.
rpc(
"server_state")[jss::result];
998 si[jss::state].isMember(jss::amendment_blocked) &&
999 si[jss::state][jss::amendment_blocked] ==
true);
1001 si[jss::state].isMember(jss::warnings) &&
1002 si[jss::state][jss::warnings].isArray() &&
1003 si[jss::state][jss::warnings].size() == 1 &&
1004 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
1018 if (!BEAST_EXPECTS(!ec, ec.message()))
1020 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
1022 resp.body().find(
"connectivity is working.") != std::string::npos);
1035 if (!BEAST_EXPECTS(!ec, ec.message()))
1038 resp.result() == boost::beast::http::status::internal_server_error);
1040 resp.body().find(
"cannot accept clients:") != std::string::npos);
1042 resp.body().find(
"Server version too old") != std::string::npos);
1048 testcase(
"RPC client sends assorted input");
1050 using namespace test::jtx;
1053 boost::system::error_code ec;
1055 boost::beast::http::response<boost::beast::http::string_body> resp;
1058 resp.result() == boost::beast::http::status::bad_request);
1059 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1063 boost::beast::http::response<boost::beast::http::string_body> resp;
1068 resp.result() == boost::beast::http::status::bad_request);
1069 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1073 boost::beast::http::response<boost::beast::http::string_body> resp;
1078 resp.result() == boost::beast::http::status::bad_request);
1079 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1083 boost::beast::http::response<boost::beast::http::string_body> resp;
1090 resp.result() == boost::beast::http::status::bad_request);
1091 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1095 boost::beast::http::response<boost::beast::http::string_body> resp;
1097 jv[jss::method] =
"batch";
1098 jv[jss::params] = 2;
1101 resp.result() == boost::beast::http::status::bad_request);
1102 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1106 boost::beast::http::response<boost::beast::http::string_body> resp;
1108 jv[jss::method] =
"batch";
1110 jv[jss::params][
"invalid"] = 3;
1113 resp.result() == boost::beast::http::status::bad_request);
1114 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1119 boost::beast::http::response<boost::beast::http::string_body> resp;
1123 resp.result() == boost::beast::http::status::bad_request);
1124 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1128 boost::beast::http::response<boost::beast::http::string_body> resp;
1129 jv[jss::method] = 1;
1132 resp.result() == boost::beast::http::status::bad_request);
1133 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
1137 boost::beast::http::response<boost::beast::http::string_body> resp;
1138 jv[jss::method] =
"";
1141 resp.result() == boost::beast::http::status::bad_request);
1142 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
1146 boost::beast::http::response<boost::beast::http::string_body> resp;
1147 jv[jss::method] =
"some_method";
1148 jv[jss::params] =
"params";
1151 resp.result() == boost::beast::http::status::bad_request);
1152 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1156 boost::beast::http::response<boost::beast::http::string_body> resp;
1158 jv[jss::params][0u] =
"not an object";
1161 resp.result() == boost::beast::http::status::bad_request);
1162 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1169 testcase(
"Server status not okay");
1171 using namespace test::jtx;
1173 cfg->ELB_SUPPORT =
true;
1180 boost::beast::http::response<boost::beast::http::string_body> resp;
1181 boost::system::error_code ec;
1184 resp.result() == boost::beast::http::status::internal_server_error);
1185 std::regex body{
"Server cannot accept clients"};
1193 for (
auto it : {
"http",
"ws",
"ws2"})
1200 yield_to([&](boost::asio::yield_context& yield) {
1229BEAST_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)