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>
32 class myFields :
public boost::beast::http::fields
39 auto const section_name = boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
42 p->overwrite(section_name,
"protocol", proto);
44 p->overwrite(section_name,
"admin",
"");
48 (*p)[section_name].set(
"admin_password",
"p");
49 (*p)[section_name].set(
"admin_user",
"u");
53 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
55 boost::starts_with(proto,
"h") ?
"ws" :
"http");
62 (*p)[
"server"].append(
"port_alt");
64 (*p)[
"port_alt"].set(
"port",
"7099");
65 (*p)[
"port_alt"].set(
"protocol",
"http");
76 using namespace boost::beast::http;
77 request<string_body> req;
82 req.insert(
"User-Agent",
"test");
83 req.method(boost::beast::http::verb::get);
84 req.insert(
"Upgrade",
"websocket");
95 req.insert(
"Sec-WebSocket-Version",
"13");
96 req.insert(boost::beast::http::field::connection,
"upgrade");
104 using namespace boost::beast::http;
105 request<string_body> req;
109 for (
auto const& f : fields)
110 req.insert(f.name(), f.value());
112 req.insert(
"User-Agent",
"test");
115 req.method(boost::beast::http::verb::get);
119 req.method(boost::beast::http::verb::post);
120 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
123 req.prepare_payload();
130 boost::asio::yield_context& yield,
131 boost::beast::http::request<boost::beast::http::string_body>&& req,
135 boost::beast::http::response<boost::beast::http::string_body>& resp,
136 boost::system::error_code& ec)
139 using namespace boost::beast::http;
141 ip::tcp::resolver r{ios};
142 boost::beast::multi_buffer sb;
151 ssl::context ctx{ssl::context::sslv23};
152 ctx.set_verify_mode(ssl::verify_none);
153 ssl::stream<ip::tcp::socket> ss{ios, ctx};
154 async_connect(ss.next_layer(), it, yield[ec]);
157 ss.async_handshake(ssl::stream_base::client, yield[ec]);
160 boost::beast::http::async_write(ss, req, yield[ec]);
163 async_read(ss, sb, resp, yield[ec]);
169 ip::tcp::socket sock{ios};
170 async_connect(sock, it, yield[ec]);
173 boost::beast::http::async_write(sock, req, yield[ec]);
176 async_read(sock, sb, resp, yield[ec]);
187 boost::asio::yield_context& yield,
189 boost::beast::http::response<boost::beast::http::string_body>& resp,
190 boost::system::error_code& ec)
201 boost::asio::yield_context& yield,
203 boost::beast::http::response<boost::beast::http::string_body>& resp,
204 boost::system::error_code& ec,
220 bool subobject =
false)
227 jp[
"admin_user"] = user;
232 jpi[
"admin_password"] = password;
233 jp[
"admin_password"] = jpi;
237 jp[
"admin_password"] = password;
241 if (boost::starts_with(proto,
"h"))
244 jrr = jrc->invoke(
"ledger_accept", jp);
249 jrr = wsc->invoke(
"ledger_accept", jp);
262 testcase <<
"Admin request over " << proto <<
", config " << (admin ?
"enabled" :
"disabled")
263 <<
", credentials " << (credentials ?
"" :
"not ") <<
"set";
268 auto const proto_ws = boost::starts_with(proto,
"w");
273 if (admin && credentials)
275 auto const user = env.
app().
config()[proto_ws ?
"port_ws" :
"port_rpc"].get<
std::string>(
"admin_user");
277 auto const password =
282 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
284 jrr[
"error_message"] == proto_ws ?
"Bad credentials." :
"You don't have permission for this command.");
288 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
290 jrr[
"error_message"] == proto_ws ?
"Bad credentials." :
"You don't have permission for this command.");
294 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
296 jrr[
"error_message"] == proto_ws ?
"Bad credentials." :
"You don't have permission for this command.");
300 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
302 jrr[
"error_message"] == proto_ws ?
"Bad credentials." :
"You don't have permission for this command.");
306 BEAST_EXPECT(jrr[
"status"] ==
"success");
312 BEAST_EXPECT(jrr[
"status"] ==
"success");
316 BEAST_EXPECT(jrr[
"status"] ==
"success");
322 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
324 jrr[
"error_message"] == proto_ws ?
"Bad credentials." :
"You don't have permission for this command.");
331 testcase(
"WS client to http server fails");
334 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
340 boost::system::error_code ec;
341 boost::beast::http::response<boost::beast::http::string_body> resp;
343 if (!BEAST_EXPECTS(!ec, ec.message()))
345 BEAST_EXPECT(resp.result() == boost::beast::http::status::unauthorized);
350 boost::system::error_code ec;
351 boost::beast::http::response<boost::beast::http::string_body> resp;
353 if (!BEAST_EXPECTS(!ec, ec.message()))
355 BEAST_EXPECT(resp.result() == boost::beast::http::status::unauthorized);
365 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
366 cfg->section(
"port_ws").
set(
"protocol",
"http");
372 boost::system::error_code ec;
373 boost::beast::http::response<boost::beast::http::string_body> resp;
375 if (!BEAST_EXPECTS(!ec, ec.message()))
377 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
382 boost::system::error_code ec;
383 boost::beast::http::response<boost::beast::http::string_body> resp;
385 if (!BEAST_EXPECTS(!ec, ec.message()))
387 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
394 testcase(
"Partial WS upgrade request");
397 using namespace boost::beast::http;
399 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
406 boost::system::error_code ec;
407 response<string_body> resp;
411 auto req_string = boost::lexical_cast<std::string>(req);
412 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
415 ip::tcp::resolver r{ios};
416 boost::beast::multi_buffer sb;
419 if (!BEAST_EXPECTS(!ec, ec.message()))
422 ip::tcp::socket sock{ios};
423 async_connect(sock, it, yield[ec]);
424 if (!BEAST_EXPECTS(!ec, ec.message()))
426 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
427 if (!BEAST_EXPECTS(!ec, ec.message()))
431 async_read(sock, sb, resp, yield[ec]);
439 boost::asio::yield_context& yield)
444 testcase <<
"Connect fails: " << client_protocol <<
" client to " << server_protocol <<
" server";
448 boost::beast::http::response<boost::beast::http::string_body> resp;
449 boost::system::error_code ec;
450 if (boost::starts_with(client_protocol,
"h"))
452 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
457 doWSRequest(env, yield, client_protocol ==
"wss" || client_protocol ==
"wss2", resp, ec);
463 testAuth(
bool secure, boost::asio::yield_context& yield)
465 testcase <<
"Server with authorization, " << (secure ?
"secure" :
"non-secure");
467 using namespace test::jtx;
469 (*cfg)[
"port_rpc"].set(
"user",
"me");
470 (*cfg)[
"port_rpc"].set(
"password",
"secret");
471 (*cfg)[
"port_rpc"].set(
"protocol", secure ?
"https" :
"http");
473 (*cfg)[
"port_ws"].set(
"protocol",
"http,ws");
478 jr[jss::method] =
"server_info";
479 boost::beast::http::response<boost::beast::http::string_body> resp;
480 boost::system::error_code ec;
482 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
485 auth.insert(
"Authorization",
"");
487 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
489 auth.set(
"Authorization",
"Basic NOT-VALID");
491 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
495 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
501 auth.set(
"Authorization",
"Basic " + user +
":" +
pass);
503 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
508 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
509 BEAST_EXPECT(!resp.body().empty());
515 testcase <<
"Server with connection limit of " << limit;
517 using namespace test::jtx;
519 using namespace boost::beast::http;
528 boost::system::error_code ec;
530 ip::tcp::resolver r{ios};
533 jr[jss::method] =
"server_info";
539 int connectionCount{1};
547 int testTo = (limit == 0) ? 50 : limit + 1;
548 while (connectionCount < testTo)
551 async_connect(clients.
back().first, it, yield[ec]);
554 async_write(clients.
back().first, req, yield[ec]);
560 for (
auto& [soc, buf] : clients)
562 boost::beast::http::response<boost::beast::http::string_body> resp;
563 async_read(soc, buf, resp, yield[ec]);
567 BEAST_EXPECT((limit == 0 || readCount < limit - 1) ? (!ec) : bool(ec));
574 testcase(
"Connection with WS handoff");
576 using namespace test::jtx;
578 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
584 boost::beast::http::response<boost::beast::http::string_body> resp;
585 boost::system::error_code ec;
587 BEAST_EXPECT(resp.result() == boost::beast::http::status::switching_protocols);
588 BEAST_EXPECT(resp.find(
"Upgrade") != resp.end() && resp[
"Upgrade"] ==
"websocket");
589 BEAST_EXPECT(resp.find(
"Connection") != resp.end() && boost::iequals(resp[
"Connection"],
"upgrade"));
595 testcase(
"Connection to port with no RPC enabled");
597 using namespace test::jtx;
602 boost::beast::http::response<boost::beast::http::string_body> resp;
603 boost::system::error_code ec;
607 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
608 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
614 testcase(
"WS client sends assorted input");
616 using namespace test::jtx;
618 using namespace boost::beast::http;
623 boost::system::error_code ec;
626 ip::tcp::resolver r{ios};
629 if (!BEAST_EXPECT(!ec))
632 ip::tcp::socket sock{ios};
633 async_connect(sock, it, yield[ec]);
634 if (!BEAST_EXPECT(!ec))
637 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
642 ws.async_write_some(
true, buffer(req), yield[ec]);
643 if (!BEAST_EXPECT(!ec))
646 boost::beast::multi_buffer sb;
647 ws.async_read(sb, yield[ec]);
648 if (!BEAST_EXPECT(!ec))
654 jr.
parse(boost::lexical_cast<std::string>(boost::beast::make_printable(sb.data())), resp)))
656 sb.consume(sb.size());
661 auto resp = sendAndParse(
"NOT JSON");
662 BEAST_EXPECT(resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
663 BEAST_EXPECT(!resp.isMember(jss::status));
668 jv[jss::command] =
"foo";
669 jv[jss::method] =
"bar";
671 BEAST_EXPECT(resp.isMember(jss::error) && resp[jss::error] ==
"missingCommand");
672 BEAST_EXPECT(resp.isMember(jss::status) && resp[jss::status] ==
"error");
677 jv[jss::command] =
"ping";
679 BEAST_EXPECT(resp.isMember(jss::status) && resp[jss::status] ==
"success");
681 resp.isMember(jss::result) && resp[jss::result].isMember(jss::role) &&
682 resp[jss::result][jss::role] ==
"admin");
689 testcase(
"Status request over WS and RPC with/without Amendment Warning");
692 using namespace boost::beast::http;
697 cfg->section(
"port_rpc").
set(
"protocol",
"http");
711 auto si = env.
rpc(
"server_info")[jss::result];
712 BEAST_EXPECT(si.isMember(jss::info));
713 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
715 BEAST_EXPECT(!si.isMember(jss::warnings));
719 si = env.
rpc(
"server_state")[jss::result];
720 BEAST_EXPECT(si.isMember(jss::state));
721 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
723 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
728 boost::system::error_code ec;
729 response<string_body> resp;
733 if (!BEAST_EXPECTS(!ec, ec.message()))
735 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
736 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
747 si = env.
rpc(
"server_info")[jss::result];
748 BEAST_EXPECT(si.isMember(jss::info));
749 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
751 si[jss::info].isMember(jss::warnings) && si[jss::info][jss::warnings].isArray() &&
752 si[jss::info][jss::warnings].size() == 1 &&
757 si = env.
rpc(
"server_state")[jss::result];
758 BEAST_EXPECT(si.isMember(jss::state));
759 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
761 si[jss::state].isMember(jss::warnings) && si[jss::state][jss::warnings].isArray() &&
762 si[jss::state][jss::warnings].size() == 1 &&
768 if (!BEAST_EXPECTS(!ec, ec.message()))
770 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
771 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
778 if (!BEAST_EXPECTS(!ec, ec.message()))
780 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
781 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
787 testcase(
"Status request over WS and RPC with/without Amendment Block");
790 using namespace boost::beast::http;
795 cfg->section(
"port_rpc").
set(
"protocol",
"http");
809 auto si = env.
rpc(
"server_info")[jss::result];
810 BEAST_EXPECT(si.isMember(jss::info));
811 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
813 BEAST_EXPECT(!si.isMember(jss::warnings));
817 si = env.
rpc(
"server_state")[jss::result];
818 BEAST_EXPECT(si.isMember(jss::state));
819 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
821 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
826 boost::system::error_code ec;
827 response<string_body> resp;
831 if (!BEAST_EXPECTS(!ec, ec.message()))
833 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
834 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
845 si = env.
rpc(
"server_info")[jss::result];
846 BEAST_EXPECT(si.isMember(jss::info));
847 BEAST_EXPECT(si[jss::info].isMember(jss::amendment_blocked) && si[jss::info][jss::amendment_blocked] ==
true);
849 si[jss::info].isMember(jss::warnings) && si[jss::info][jss::warnings].isArray() &&
850 si[jss::info][jss::warnings].size() == 1 &&
854 si = env.
rpc(
"server_state")[jss::result];
855 BEAST_EXPECT(si[jss::state].isMember(jss::amendment_blocked) && si[jss::state][jss::amendment_blocked] ==
true);
857 si[jss::state].isMember(jss::warnings) && si[jss::state][jss::warnings].isArray() &&
858 si[jss::state][jss::warnings].size() == 1 &&
865 if (!BEAST_EXPECTS(!ec, ec.message()))
867 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
868 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
874 if (!BEAST_EXPECTS(!ec, ec.message()))
876 BEAST_EXPECT(resp.result() == boost::beast::http::status::internal_server_error);
877 BEAST_EXPECT(resp.body().find(
"cannot accept clients:") != std::string::npos);
878 BEAST_EXPECT(resp.body().find(
"Server version too old") != std::string::npos);
884 testcase(
"RPC client sends assorted input");
886 using namespace test::jtx;
889 boost::system::error_code ec;
891 boost::beast::http::response<boost::beast::http::string_body> resp;
893 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
894 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
898 boost::beast::http::response<boost::beast::http::string_body> resp;
902 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
903 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
907 boost::beast::http::response<boost::beast::http::string_body> resp;
911 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
912 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
916 boost::beast::http::response<boost::beast::http::string_body> resp;
922 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
923 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
927 boost::beast::http::response<boost::beast::http::string_body> resp;
929 jv[jss::method] =
"batch";
932 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
933 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
937 boost::beast::http::response<boost::beast::http::string_body> resp;
939 jv[jss::method] =
"batch";
941 jv[jss::params][
"invalid"] = 3;
943 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
944 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
949 boost::beast::http::response<boost::beast::http::string_body> resp;
952 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
953 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
957 boost::beast::http::response<boost::beast::http::string_body> resp;
960 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
961 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
965 boost::beast::http::response<boost::beast::http::string_body> resp;
966 jv[jss::method] =
"";
968 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
969 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
973 boost::beast::http::response<boost::beast::http::string_body> resp;
974 jv[jss::method] =
"some_method";
975 jv[jss::params] =
"params";
977 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
978 BEAST_EXPECT(resp.body() ==
"params unparsable\r\n");
982 boost::beast::http::response<boost::beast::http::string_body> resp;
984 jv[jss::params][0u] =
"not an object";
986 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
987 BEAST_EXPECT(resp.body() ==
"params unparsable\r\n");
996 using namespace test::jtx;
998 cfg->ELB_SUPPORT =
true;
1005 boost::beast::http::response<boost::beast::http::string_body> resp;
1006 boost::system::error_code ec;
1008 BEAST_EXPECT(resp.result() == boost::beast::http::status::internal_server_error);
1009 std::regex body{
"Server cannot accept clients"};
1017 for (
auto it : {
"http",
"ws",
"ws2"})
1024 yield_to([&](boost::asio::yield_context& yield) {
1053BEAST_DEFINE_TESTSUITE(ServerStatus, server,
xrpl);
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
Section & section(std::string const &name)
Returns the section with the given name.
virtual void setAmendmentBlocked()=0
virtual bool beginConsensus(uint256 const &netLCL, std::unique_ptr< std::stringstream > const &clog)=0
virtual Json::Value getConsensusInfo()=0
virtual void setAmendmentWarned()=0
std::optional< T > get(std::string const &name) const
virtual NetworkOPs & getOPs()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual LedgerMaster & getLedgerMaster()=0
void testWSClientToHttpServer(boost::asio::yield_context &yield)
auto makeAdminRequest(jtx::Env &env, std::string const &proto, std::string const &user, std::string const &password, bool subobject=false)
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)
auto makeConfig(std::string const &proto, bool admin=true, bool credentials=false)
void testAmendmentBlock(boost::asio::yield_context &yield)
auto makeHTTPRequest(std::string const &host, uint16_t port, std::string const &body, myFields const &fields)
void testNoRPC(boost::asio::yield_context &yield)
void testWSHandoff(boost::asio::yield_context &yield)
void testTruncatedWSUpgrade(boost::asio::yield_context &yield)
void testAdminRequest(std::string const &proto, bool admin, bool credentials)
void testRPCRequests(boost::asio::yield_context &yield)
void testStatusNotOkay(boost::asio::yield_context &yield)
void run() override
Runs the suite.
void testAmendmentWarning(boost::asio::yield_context &yield)
void testAuth(bool secure, boost::asio::yield_context &yield)
void testLimit(boost::asio::yield_context &yield, int limit)
void testWSRequests(boost::asio::yield_context &yield)
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={})
auto makeWSUpgrade(std::string const &host, uint16_t port)
void testCantConnect(std::string const &client_protocol, std::string const &server_protocol, boost::asio::yield_context &yield)
void testStatusRequest(boost::asio::yield_context &yield)
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)
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
std::shared_ptr< ReadView const > closed()
Returns the last closed 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 > envconfig()
creates and initializes a default configuration for jtx::Env
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
char const * getEnvLocalhostAddr()
std::unique_ptr< AbstractClient > makeJSONRPCClient(Config const &cfg, unsigned rpc_version)
Returns a client using JSON-RPC over HTTP/S.
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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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 to_string(base_uint< Bits, Tag > const &a)
std::string base64_encode(std::uint8_t const *data, std::size_t len)
@ warnRPC_AMENDMENT_BLOCKED
@ warnRPC_UNSUPPORTED_MAJORITY
T regex_search(T... args)