20 #include <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/misc/LoadFeeTrack.h>
22 #include <ripple/app/misc/NetworkOPs.h>
23 #include <ripple/basics/base64.h>
24 #include <ripple/beast/test/yield_to.hpp>
25 #include <ripple/json/json_reader.h>
26 #include <ripple/rpc/ServerHandler.h>
27 #include <boost/algorithm/string/predicate.hpp>
28 #include <boost/asio.hpp>
29 #include <boost/asio/ssl.hpp>
30 #include <boost/beast/core/multi_buffer.hpp>
31 #include <boost/beast/http.hpp>
37 #include <test/jtx/JSONRPCClient.h>
38 #include <test/jtx/WSClient.h>
39 #include <test/jtx/envconfig.h>
45 public beast::test::enable_yield_to
47 class myFields :
public boost::beast::http::fields
55 bool credentials =
false)
57 auto const section_name =
58 boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
61 p->overwrite(section_name,
"protocol", proto);
63 p->overwrite(section_name,
"admin",
"");
67 (*p)[section_name].set(
"admin_password",
"p");
68 (*p)[section_name].set(
"admin_user",
"u");
72 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
74 boost::starts_with(proto,
"h") ?
"ws" :
"http");
81 (*p)[
"server"].append(
"port_alt");
83 (*p)[
"port_alt"].set(
"port",
"7099");
84 (*p)[
"port_alt"].set(
"protocol",
"http");
95 using namespace boost::beast::http;
96 request<string_body> req;
101 req.insert(
"User-Agent",
"test");
102 req.method(boost::beast::http::verb::get);
103 req.insert(
"Upgrade",
"websocket");
115 req.insert(
"Sec-WebSocket-Version",
"13");
116 req.insert(boost::beast::http::field::connection,
"upgrade");
128 using namespace boost::beast::http;
129 request<string_body> req;
133 for (
auto const& f : fields)
134 req.insert(f.name(), f.value());
136 req.insert(
"User-Agent",
"test");
139 req.method(boost::beast::http::verb::get);
143 req.method(boost::beast::http::verb::post);
144 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
147 req.prepare_payload();
154 boost::asio::yield_context& yield,
155 boost::beast::http::request<boost::beast::http::string_body>&& req,
159 boost::beast::http::response<boost::beast::http::string_body>& resp,
160 boost::system::error_code& ec)
163 using namespace boost::beast::http;
164 io_service& ios = get_io_service();
165 ip::tcp::resolver r{ios};
166 boost::beast::multi_buffer sb;
168 auto it = r.async_resolve(
176 ssl::context ctx{ssl::context::sslv23};
177 ctx.set_verify_mode(ssl::verify_none);
178 ssl::stream<ip::tcp::socket> ss{ios, ctx};
179 async_connect(ss.next_layer(), it, yield[ec]);
182 ss.async_handshake(ssl::stream_base::client, yield[ec]);
185 boost::beast::http::async_write(ss, req, yield[ec]);
188 async_read(ss, sb, resp, yield[ec]);
194 ip::tcp::socket sock{ios};
195 async_connect(sock, it, yield[ec]);
198 boost::beast::http::async_write(sock, req, yield[ec]);
201 async_read(sock, sb, resp, yield[ec]);
212 boost::asio::yield_context& yield,
214 boost::beast::http::response<boost::beast::http::string_body>& resp,
215 boost::system::error_code& ec)
221 yield,
makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec);
228 boost::asio::yield_context& yield,
230 boost::beast::http::response<boost::beast::http::string_body>& resp,
231 boost::system::error_code& ec,
255 bool subobject =
false)
262 jp[
"admin_user"] = user;
267 jpi[
"admin_password"] = password;
268 jp[
"admin_password"] = jpi;
272 jp[
"admin_password"] = password;
276 if (boost::starts_with(proto,
"h"))
279 jrr = jrc->invoke(
"ledger_accept", jp);
284 jrr = wsc->invoke(
"ledger_accept", jp);
297 testcase <<
"Admin request over " << proto <<
", config "
298 << (admin ?
"enabled" :
"disabled") <<
", credentials "
299 << (credentials ?
"" :
"not ") <<
"set";
304 auto const proto_ws = boost::starts_with(proto,
"w");
309 if (admin && credentials)
311 auto const user = env.
app()
312 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
315 auto const password =
317 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
322 env, proto, *user, *password +
"_")[jss::result];
324 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
326 jrr[
"error_message"] == proto_ws
328 :
"You don't have permission for this command.");
332 env, proto, *user, *password,
true)[jss::result];
334 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
336 jrr[
"error_message"] == proto_ws
338 :
"You don't have permission for this command.");
342 env, proto, *user +
"_", *password)[jss::result];
344 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
346 jrr[
"error_message"] == proto_ws
348 :
"You don't have permission for this command.");
353 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
355 jrr[
"error_message"] == proto_ws
357 :
"You don't have permission for this command.");
361 BEAST_EXPECT(jrr[
"status"] ==
"success");
367 BEAST_EXPECT(jrr[
"status"] ==
"success");
371 BEAST_EXPECT(jrr[
"status"] ==
"success");
378 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
380 jrr[
"error_message"] == proto_ws
382 :
"You don't have permission for this command.");
389 testcase(
"WS client to http server fails");
392 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
398 boost::system::error_code ec;
399 boost::beast::http::response<boost::beast::http::string_body> resp;
401 if (!BEAST_EXPECTS(!ec, ec.message()))
404 resp.result() == boost::beast::http::status::unauthorized);
409 boost::system::error_code ec;
410 boost::beast::http::response<boost::beast::http::string_body> resp;
412 if (!BEAST_EXPECTS(!ec, ec.message()))
415 resp.result() == boost::beast::http::status::unauthorized);
422 testcase(
"Status request");
425 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
426 cfg->section(
"port_ws").
set(
"protocol",
"http");
432 boost::system::error_code ec;
433 boost::beast::http::response<boost::beast::http::string_body> resp;
435 if (!BEAST_EXPECTS(!ec, ec.message()))
437 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
442 boost::system::error_code ec;
443 boost::beast::http::response<boost::beast::http::string_body> resp;
445 if (!BEAST_EXPECTS(!ec, ec.message()))
447 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
454 testcase(
"Partial WS upgrade request");
457 using namespace boost::beast::http;
459 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
467 boost::system::error_code ec;
468 response<string_body> resp;
472 auto req_string = boost::lexical_cast<std::string>(req);
473 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
475 io_service& ios = get_io_service();
476 ip::tcp::resolver r{ios};
477 boost::beast::multi_buffer sb;
479 auto it = r.async_resolve(
481 if (!BEAST_EXPECTS(!ec, ec.message()))
484 ip::tcp::socket sock{ios};
485 async_connect(sock, it, yield[ec]);
486 if (!BEAST_EXPECTS(!ec, ec.message()))
488 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
489 if (!BEAST_EXPECTS(!ec, ec.message()))
493 async_read(sock, sb, resp, yield[ec]);
501 boost::asio::yield_context& yield)
506 testcase <<
"Connect fails: " << client_protocol <<
" client to "
507 << server_protocol <<
" server";
511 boost::beast::http::response<boost::beast::http::string_body> resp;
512 boost::system::error_code ec;
513 if (boost::starts_with(client_protocol,
"h"))
515 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
523 client_protocol ==
"wss" || client_protocol ==
"wss2",
531 testAuth(
bool secure, boost::asio::yield_context& yield)
533 testcase <<
"Server with authorization, "
534 << (secure ?
"secure" :
"non-secure");
536 using namespace test::jtx;
538 (*cfg)[
"port_rpc"].set(
"user",
"me");
539 (*cfg)[
"port_rpc"].set(
"password",
"secret");
540 (*cfg)[
"port_rpc"].set(
541 "protocol", secure ?
"https" :
"http");
543 (*cfg)[
"port_ws"].
set(
"protocol",
"http,ws");
548 jr[jss::method] =
"server_info";
549 boost::beast::http::response<boost::beast::http::string_body> resp;
550 boost::system::error_code ec;
552 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
555 auth.insert(
"Authorization",
"");
557 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
559 auth.set(
"Authorization",
"Basic NOT-VALID");
561 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
563 auth.set(
"Authorization",
"Basic " +
base64_encode(
"me:badpass"));
565 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
567 auto const user = env.
app()
572 auto const pass = env.
app()
579 auth.set(
"Authorization",
"Basic " + user +
":" + pass);
581 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
584 auth.set(
"Authorization",
"Basic " +
base64_encode(user +
":" + pass));
586 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
587 BEAST_EXPECT(!resp.body().empty());
593 testcase <<
"Server with connection limit of " << limit;
595 using namespace test::jtx;
597 using namespace boost::beast::http;
608 boost::system::error_code ec;
609 io_service& ios = get_io_service();
610 ip::tcp::resolver r{ios};
613 jr[jss::method] =
"server_info";
615 auto it = r.async_resolve(
621 int connectionCount{1};
629 int testTo = (limit == 0) ? 50 : limit + 1;
630 while (connectionCount < testTo)
633 ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
634 async_connect(clients.
back().first, it, yield[ec]);
637 async_write(clients.
back().first, req, yield[ec]);
643 for (
auto& [soc, buf] : clients)
645 boost::beast::http::response<boost::beast::http::string_body> resp;
646 async_read(soc, buf, resp, yield[ec]);
651 (limit == 0 || readCount < limit - 1) ? (!ec) :
bool(ec));
658 testcase(
"Connection with WS handoff");
660 using namespace test::jtx;
662 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
670 boost::beast::http::response<boost::beast::http::string_body> resp;
671 boost::system::error_code ec;
674 resp.result() == boost::beast::http::status::switching_protocols);
676 resp.find(
"Upgrade") != resp.end() &&
677 resp[
"Upgrade"] ==
"websocket");
679 resp.find(
"Connection") != resp.end() &&
680 resp[
"Connection"] ==
"upgrade");
686 testcase(
"Connection to port with no RPC enabled");
688 using namespace test::jtx;
695 boost::beast::http::response<boost::beast::http::string_body> resp;
696 boost::system::error_code ec;
707 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
708 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
714 testcase(
"WS client sends assorted input");
716 using namespace test::jtx;
718 using namespace boost::beast::http;
725 boost::system::error_code ec;
727 io_service& ios = get_io_service();
728 ip::tcp::resolver r{ios};
730 auto it = r.async_resolve(
732 if (!BEAST_EXPECT(!ec))
735 ip::tcp::socket sock{ios};
736 async_connect(sock, it, yield[ec]);
737 if (!BEAST_EXPECT(!ec))
740 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
745 ws.async_write_some(
true, buffer(req), yield[ec]);
746 if (!BEAST_EXPECT(!ec))
749 boost::beast::multi_buffer sb;
750 ws.async_read(sb, yield[ec]);
751 if (!BEAST_EXPECT(!ec))
756 if (!BEAST_EXPECT(jr.
parse(
757 boost::lexical_cast<std::string>(
758 boost::beast::make_printable(sb.data())),
761 sb.consume(sb.size());
766 auto resp = sendAndParse(
"NOT JSON");
768 resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
769 BEAST_EXPECT(!resp.isMember(jss::status));
774 jv[jss::command] =
"foo";
775 jv[jss::method] =
"bar";
778 resp.isMember(jss::error) &&
779 resp[jss::error] ==
"missingCommand");
781 resp.isMember(jss::status) && resp[jss::status] ==
"error");
786 jv[jss::command] =
"ping";
789 resp.isMember(jss::status) && resp[jss::status] ==
"success");
791 resp.isMember(jss::result) &&
792 resp[jss::result].isMember(jss::role) &&
793 resp[jss::result][jss::role] ==
"admin");
801 "Status request over WS and RPC with/without Amendment Warning");
804 using namespace boost::beast::http;
809 cfg->section(
"port_rpc").
set(
"protocol",
"http");
823 auto si = env.
rpc(
"server_info")[jss::result];
824 BEAST_EXPECT(si.isMember(jss::info));
825 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
828 BEAST_EXPECT(!si.isMember(jss::warnings));
832 si = env.
rpc(
"server_state")[jss::result];
833 BEAST_EXPECT(si.isMember(jss::state));
834 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
837 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
843 boost::system::error_code ec;
844 response<string_body> resp;
855 if (!BEAST_EXPECTS(!ec, ec.message()))
857 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
859 resp.body().find(
"connectivity is working.") != std::string::npos);
871 si = env.
rpc(
"server_info")[jss::result];
872 BEAST_EXPECT(si.isMember(jss::info));
873 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
875 si[jss::info].isMember(jss::warnings) &&
876 si[jss::info][jss::warnings].isArray() &&
877 si[jss::info][jss::warnings].size() == 1 &&
878 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
883 si = env.
rpc(
"server_state")[jss::result];
884 BEAST_EXPECT(si.isMember(jss::state));
885 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
887 si[jss::state].isMember(jss::warnings) &&
888 si[jss::state][jss::warnings].isArray() &&
889 si[jss::state][jss::warnings].size() == 1 &&
890 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
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);
921 if (!BEAST_EXPECTS(!ec, ec.message()))
923 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
925 resp.body().find(
"connectivity is working.") != std::string::npos);
931 testcase(
"Status request over WS and RPC with/without Amendment Block");
934 using namespace boost::beast::http;
939 cfg->section(
"port_rpc").
set(
"protocol",
"http");
953 auto si = env.
rpc(
"server_info")[jss::result];
954 BEAST_EXPECT(si.isMember(jss::info));
955 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
958 BEAST_EXPECT(!si.isMember(jss::warnings));
962 si = env.
rpc(
"server_state")[jss::result];
963 BEAST_EXPECT(si.isMember(jss::state));
964 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
967 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
973 boost::system::error_code ec;
974 response<string_body> resp;
985 if (!BEAST_EXPECTS(!ec, ec.message()))
987 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
989 resp.body().find(
"connectivity is working.") != std::string::npos);
1001 si = env.
rpc(
"server_info")[jss::result];
1002 BEAST_EXPECT(si.isMember(jss::info));
1004 si[jss::info].isMember(jss::amendment_blocked) &&
1005 si[jss::info][jss::amendment_blocked] ==
true);
1007 si[jss::info].isMember(jss::warnings) &&
1008 si[jss::info][jss::warnings].isArray() &&
1009 si[jss::info][jss::warnings].size() == 1 &&
1010 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
1014 si = env.
rpc(
"server_state")[jss::result];
1016 si[jss::state].isMember(jss::amendment_blocked) &&
1017 si[jss::state][jss::amendment_blocked] ==
true);
1019 si[jss::state].isMember(jss::warnings) &&
1020 si[jss::state][jss::warnings].isArray() &&
1021 si[jss::state][jss::warnings].size() == 1 &&
1022 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
1036 if (!BEAST_EXPECTS(!ec, ec.message()))
1038 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
1040 resp.body().find(
"connectivity is working.") != std::string::npos);
1053 if (!BEAST_EXPECTS(!ec, ec.message()))
1056 resp.result() == boost::beast::http::status::internal_server_error);
1058 resp.body().find(
"cannot accept clients:") != std::string::npos);
1060 resp.body().find(
"Server version too old") != std::string::npos);
1066 testcase(
"RPC client sends assorted input");
1068 using namespace test::jtx;
1071 boost::system::error_code ec;
1073 boost::beast::http::response<boost::beast::http::string_body> resp;
1076 resp.result() == boost::beast::http::status::bad_request);
1077 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1081 boost::beast::http::response<boost::beast::http::string_body> resp;
1086 resp.result() == boost::beast::http::status::bad_request);
1087 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1091 boost::beast::http::response<boost::beast::http::string_body> resp;
1096 resp.result() == boost::beast::http::status::bad_request);
1097 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1101 boost::beast::http::response<boost::beast::http::string_body> resp;
1108 resp.result() == boost::beast::http::status::bad_request);
1109 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1113 boost::beast::http::response<boost::beast::http::string_body> resp;
1115 jv[jss::method] =
"batch";
1116 jv[jss::params] = 2;
1119 resp.result() == boost::beast::http::status::bad_request);
1120 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1124 boost::beast::http::response<boost::beast::http::string_body> resp;
1126 jv[jss::method] =
"batch";
1128 jv[jss::params][
"invalid"] = 3;
1131 resp.result() == boost::beast::http::status::bad_request);
1132 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1137 boost::beast::http::response<boost::beast::http::string_body> resp;
1141 resp.result() == boost::beast::http::status::bad_request);
1142 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1146 boost::beast::http::response<boost::beast::http::string_body> resp;
1147 jv[jss::method] = 1;
1150 resp.result() == boost::beast::http::status::bad_request);
1151 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
1155 boost::beast::http::response<boost::beast::http::string_body> resp;
1156 jv[jss::method] =
"";
1159 resp.result() == boost::beast::http::status::bad_request);
1160 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
1164 boost::beast::http::response<boost::beast::http::string_body> resp;
1165 jv[jss::method] =
"some_method";
1166 jv[jss::params] =
"params";
1169 resp.result() == boost::beast::http::status::bad_request);
1170 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1174 boost::beast::http::response<boost::beast::http::string_body> resp;
1176 jv[jss::params][0u] =
"not an object";
1179 resp.result() == boost::beast::http::status::bad_request);
1180 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1187 testcase(
"Server status not okay");
1189 using namespace test::jtx;
1191 cfg->ELB_SUPPORT =
true;
1198 boost::beast::http::response<boost::beast::http::string_body> resp;
1199 boost::system::error_code ec;
1202 resp.result() == boost::beast::http::status::internal_server_error);
1203 std::regex body{
"Server cannot accept clients"};
1211 for (
auto it : {
"http",
"ws",
"ws2"})
1218 yield_to([&](boost::asio::yield_context& yield) {