rippled
ServerStatus_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
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>
32 #include <algorithm>
33 #include <array>
34 #include <random>
35 #include <regex>
36 #include <test/jtx.h>
37 #include <test/jtx/JSONRPCClient.h>
38 #include <test/jtx/WSClient.h>
39 #include <test/jtx/envconfig.h>
40 
41 namespace ripple {
42 namespace test {
43 
44 class ServerStatus_test : public beast::unit_test::suite,
45  public beast::test::enable_yield_to
46 {
47  class myFields : public boost::beast::http::fields
48  {
49  };
50 
51  auto
53  std::string const& proto,
54  bool admin = true,
55  bool credentials = false)
56  {
57  auto const section_name =
58  boost::starts_with(proto, "h") ? "port_rpc" : "port_ws";
59  auto p = jtx::envconfig();
60 
61  p->overwrite(section_name, "protocol", proto);
62  if (!admin)
63  p->overwrite(section_name, "admin", "");
64 
65  if (credentials)
66  {
67  (*p)[section_name].set("admin_password", "p");
68  (*p)[section_name].set("admin_user", "u");
69  }
70 
71  p->overwrite(
72  boost::starts_with(proto, "h") ? "port_ws" : "port_rpc",
73  "protocol",
74  boost::starts_with(proto, "h") ? "ws" : "http");
75 
76  if (proto == "https")
77  {
78  // this port is here to allow the env to create its internal client,
79  // which requires an http endpoint to talk to. In the connection
80  // failure test, this endpoint should never be used
81  (*p)["server"].append("port_alt");
82  (*p)["port_alt"].set("ip", getEnvLocalhostAddr());
83  (*p)["port_alt"].set("port", "7099");
84  (*p)["port_alt"].set("protocol", "http");
85  (*p)["port_alt"].set("admin", getEnvLocalhostAddr());
86  }
87 
88  return p;
89  }
90 
91  auto
92  makeWSUpgrade(std::string const& host, uint16_t port)
93  {
94  using namespace boost::asio;
95  using namespace boost::beast::http;
96  request<string_body> req;
97 
98  req.target("/");
99  req.version(11);
100  req.insert("Host", host + ":" + std::to_string(port));
101  req.insert("User-Agent", "test");
102  req.method(boost::beast::http::verb::get);
103  req.insert("Upgrade", "websocket");
104  {
105  // not secure, but OK for a testing
107  std::mt19937 e{rd()};
110  for (auto& v : key)
111  v = d(e);
112  req.insert(
113  "Sec-WebSocket-Key", base64_encode(key.data(), key.size()));
114  };
115  req.insert("Sec-WebSocket-Version", "13");
116  req.insert(boost::beast::http::field::connection, "upgrade");
117  return req;
118  }
119 
120  auto
122  std::string const& host,
123  uint16_t port,
124  std::string const& body,
125  myFields const& fields)
126  {
127  using namespace boost::asio;
128  using namespace boost::beast::http;
129  request<string_body> req;
130 
131  req.target("/");
132  req.version(11);
133  for (auto const& f : fields)
134  req.insert(f.name(), f.value());
135  req.insert("Host", host + ":" + std::to_string(port));
136  req.insert("User-Agent", "test");
137  if (body.empty())
138  {
139  req.method(boost::beast::http::verb::get);
140  }
141  else
142  {
143  req.method(boost::beast::http::verb::post);
144  req.insert("Content-Type", "application/json; charset=UTF-8");
145  req.body() = body;
146  }
147  req.prepare_payload();
148 
149  return req;
150  }
151 
152  void
154  boost::asio::yield_context& yield,
155  boost::beast::http::request<boost::beast::http::string_body>&& req,
156  std::string const& host,
157  uint16_t port,
158  bool secure,
159  boost::beast::http::response<boost::beast::http::string_body>& resp,
160  boost::system::error_code& ec)
161  {
162  using namespace boost::asio;
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;
167 
168  auto it = r.async_resolve(
169  ip::tcp::resolver::query{host, std::to_string(port)}, yield[ec]);
170  if (ec)
171  return;
172 
173  resp.body().clear();
174  if (secure)
175  {
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]);
180  if (ec)
181  return;
182  ss.async_handshake(ssl::stream_base::client, yield[ec]);
183  if (ec)
184  return;
185  boost::beast::http::async_write(ss, req, yield[ec]);
186  if (ec)
187  return;
188  async_read(ss, sb, resp, yield[ec]);
189  if (ec)
190  return;
191  }
192  else
193  {
194  ip::tcp::socket sock{ios};
195  async_connect(sock, it, yield[ec]);
196  if (ec)
197  return;
198  boost::beast::http::async_write(sock, req, yield[ec]);
199  if (ec)
200  return;
201  async_read(sock, sb, resp, yield[ec]);
202  if (ec)
203  return;
204  }
205 
206  return;
207  }
208 
209  void
211  test::jtx::Env& env,
212  boost::asio::yield_context& yield,
213  bool secure,
214  boost::beast::http::response<boost::beast::http::string_body>& resp,
215  boost::system::error_code& ec)
216  {
217  auto const port =
218  env.app().config()["port_ws"].get<std::uint16_t>("port");
219  auto ip = env.app().config()["port_ws"].get<std::string>("ip");
220  doRequest(
221  yield, makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec);
222  return;
223  }
224 
225  void
227  test::jtx::Env& env,
228  boost::asio::yield_context& yield,
229  bool secure,
230  boost::beast::http::response<boost::beast::http::string_body>& resp,
231  boost::system::error_code& ec,
232  std::string const& body = "",
233  myFields const& fields = {})
234  {
235  auto const port =
236  env.app().config()["port_rpc"].get<std::uint16_t>("port");
237  auto const ip = env.app().config()["port_rpc"].get<std::string>("ip");
238  doRequest(
239  yield,
240  makeHTTPRequest(*ip, *port, body, fields),
241  *ip,
242  *port,
243  secure,
244  resp,
245  ec);
246  return;
247  }
248 
249  auto
251  jtx::Env& env,
252  std::string const& proto,
253  std::string const& user,
254  std::string const& password,
255  bool subobject = false)
256  {
257  Json::Value jrr;
258 
260  if (!user.empty())
261  {
262  jp["admin_user"] = user;
263  if (subobject)
264  {
265  // special case of bad password..passed as object
267  jpi["admin_password"] = password;
268  jp["admin_password"] = jpi;
269  }
270  else
271  {
272  jp["admin_password"] = password;
273  }
274  }
275 
276  if (boost::starts_with(proto, "h"))
277  {
278  auto jrc = makeJSONRPCClient(env.app().config());
279  jrr = jrc->invoke("ledger_accept", jp);
280  }
281  else
282  {
283  auto wsc = makeWSClient(env.app().config(), proto == "ws2");
284  jrr = wsc->invoke("ledger_accept", jp);
285  }
286 
287  return jrr;
288  }
289 
290  // ------------
291  // Test Cases
292  // ------------
293 
294  void
295  testAdminRequest(std::string const& proto, bool admin, bool credentials)
296  {
297  testcase << "Admin request over " << proto << ", config "
298  << (admin ? "enabled" : "disabled") << ", credentials "
299  << (credentials ? "" : "not ") << "set";
300  using namespace jtx;
301  Env env{*this, makeConfig(proto, admin, credentials)};
302 
303  Json::Value jrr;
304  auto const proto_ws = boost::starts_with(proto, "w");
305 
306  // the set of checks we do are different depending
307  // on how the admin config options are set
308 
309  if (admin && credentials)
310  {
311  auto const user = env.app()
312  .config()[proto_ws ? "port_ws" : "port_rpc"]
313  .get<std::string>("admin_user");
314 
315  auto const password =
316  env.app()
317  .config()[proto_ws ? "port_ws" : "port_rpc"]
318  .get<std::string>("admin_password");
319 
320  // 1 - FAILS with wrong pass
321  jrr = makeAdminRequest(
322  env, proto, *user, *password + "_")[jss::result];
323  BEAST_EXPECT(
324  jrr["error"] == proto_ws ? "forbidden" : "noPermission");
325  BEAST_EXPECT(
326  jrr["error_message"] == proto_ws
327  ? "Bad credentials."
328  : "You don't have permission for this command.");
329 
330  // 2 - FAILS with password in an object
331  jrr = makeAdminRequest(
332  env, proto, *user, *password, true)[jss::result];
333  BEAST_EXPECT(
334  jrr["error"] == proto_ws ? "forbidden" : "noPermission");
335  BEAST_EXPECT(
336  jrr["error_message"] == proto_ws
337  ? "Bad credentials."
338  : "You don't have permission for this command.");
339 
340  // 3 - FAILS with wrong user
341  jrr = makeAdminRequest(
342  env, proto, *user + "_", *password)[jss::result];
343  BEAST_EXPECT(
344  jrr["error"] == proto_ws ? "forbidden" : "noPermission");
345  BEAST_EXPECT(
346  jrr["error_message"] == proto_ws
347  ? "Bad credentials."
348  : "You don't have permission for this command.");
349 
350  // 4 - FAILS no credentials
351  jrr = makeAdminRequest(env, proto, "", "")[jss::result];
352  BEAST_EXPECT(
353  jrr["error"] == proto_ws ? "forbidden" : "noPermission");
354  BEAST_EXPECT(
355  jrr["error_message"] == proto_ws
356  ? "Bad credentials."
357  : "You don't have permission for this command.");
358 
359  // 5 - SUCCEEDS with proper credentials
360  jrr = makeAdminRequest(env, proto, *user, *password)[jss::result];
361  BEAST_EXPECT(jrr["status"] == "success");
362  }
363  else if (admin)
364  {
365  // 1 - SUCCEEDS with proper credentials
366  jrr = makeAdminRequest(env, proto, "u", "p")[jss::result];
367  BEAST_EXPECT(jrr["status"] == "success");
368 
369  // 2 - SUCCEEDS without proper credentials
370  jrr = makeAdminRequest(env, proto, "", "")[jss::result];
371  BEAST_EXPECT(jrr["status"] == "success");
372  }
373  else
374  {
375  // 1 - FAILS - admin disabled
376  jrr = makeAdminRequest(env, proto, "", "")[jss::result];
377  BEAST_EXPECT(
378  jrr["error"] == proto_ws ? "forbidden" : "noPermission");
379  BEAST_EXPECT(
380  jrr["error_message"] == proto_ws
381  ? "Bad credentials."
382  : "You don't have permission for this command.");
383  }
384  }
385 
386  void
387  testWSClientToHttpServer(boost::asio::yield_context& yield)
388  {
389  testcase("WS client to http server fails");
390  using namespace jtx;
391  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
392  cfg->section("port_ws").set("protocol", "http,https");
393  return cfg;
394  })};
395 
396  // non-secure request
397  {
398  boost::system::error_code ec;
399  boost::beast::http::response<boost::beast::http::string_body> resp;
400  doWSRequest(env, yield, false, resp, ec);
401  if (!BEAST_EXPECTS(!ec, ec.message()))
402  return;
403  BEAST_EXPECT(
404  resp.result() == boost::beast::http::status::unauthorized);
405  }
406 
407  // secure request
408  {
409  boost::system::error_code ec;
410  boost::beast::http::response<boost::beast::http::string_body> resp;
411  doWSRequest(env, yield, true, resp, ec);
412  if (!BEAST_EXPECTS(!ec, ec.message()))
413  return;
414  BEAST_EXPECT(
415  resp.result() == boost::beast::http::status::unauthorized);
416  }
417  }
418 
419  void
420  testStatusRequest(boost::asio::yield_context& yield)
421  {
422  testcase("Status request");
423  using namespace jtx;
424  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
425  cfg->section("port_rpc").set("protocol", "ws2,wss2");
426  cfg->section("port_ws").set("protocol", "http");
427  return cfg;
428  })};
429 
430  // non-secure request
431  {
432  boost::system::error_code ec;
433  boost::beast::http::response<boost::beast::http::string_body> resp;
434  doHTTPRequest(env, yield, false, resp, ec);
435  if (!BEAST_EXPECTS(!ec, ec.message()))
436  return;
437  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
438  }
439 
440  // secure request
441  {
442  boost::system::error_code ec;
443  boost::beast::http::response<boost::beast::http::string_body> resp;
444  doHTTPRequest(env, yield, true, resp, ec);
445  if (!BEAST_EXPECTS(!ec, ec.message()))
446  return;
447  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
448  }
449  }
450 
451  void
452  testTruncatedWSUpgrade(boost::asio::yield_context& yield)
453  {
454  testcase("Partial WS upgrade request");
455  using namespace jtx;
456  using namespace boost::asio;
457  using namespace boost::beast::http;
458  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
459  cfg->section("port_ws").set("protocol", "ws2");
460  return cfg;
461  })};
462 
463  auto const port =
464  env.app().config()["port_ws"].get<std::uint16_t>("port");
465  auto const ip = env.app().config()["port_ws"].get<std::string>("ip");
466 
467  boost::system::error_code ec;
468  response<string_body> resp;
469  auto req = makeWSUpgrade(*ip, *port);
470 
471  // truncate the request message to near the value of the version header
472  auto req_string = boost::lexical_cast<std::string>(req);
473  req_string.erase(req_string.find_last_of("13"), std::string::npos);
474 
475  io_service& ios = get_io_service();
476  ip::tcp::resolver r{ios};
477  boost::beast::multi_buffer sb;
478 
479  auto it = r.async_resolve(
480  ip::tcp::resolver::query{*ip, std::to_string(*port)}, yield[ec]);
481  if (!BEAST_EXPECTS(!ec, ec.message()))
482  return;
483 
484  ip::tcp::socket sock{ios};
485  async_connect(sock, it, yield[ec]);
486  if (!BEAST_EXPECTS(!ec, ec.message()))
487  return;
488  async_write(sock, boost::asio::buffer(req_string), yield[ec]);
489  if (!BEAST_EXPECTS(!ec, ec.message()))
490  return;
491  // since we've sent an incomplete request, the server will
492  // keep trying to read until it gives up (by timeout)
493  async_read(sock, sb, resp, yield[ec]);
494  BEAST_EXPECT(ec);
495  }
496 
497  void
499  std::string const& client_protocol,
500  std::string const& server_protocol,
501  boost::asio::yield_context& yield)
502  {
503  // The essence of this test is to have a client and server configured
504  // out-of-phase with respect to ssl (secure client and insecure server
505  // or vice-versa)
506  testcase << "Connect fails: " << client_protocol << " client to "
507  << server_protocol << " server";
508  using namespace jtx;
509  Env env{*this, makeConfig(server_protocol)};
510 
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"))
514  {
515  doHTTPRequest(env, yield, client_protocol == "https", resp, ec);
516  BEAST_EXPECT(ec);
517  }
518  else
519  {
520  doWSRequest(
521  env,
522  yield,
523  client_protocol == "wss" || client_protocol == "wss2",
524  resp,
525  ec);
526  BEAST_EXPECT(ec);
527  }
528  }
529 
530  void
531  testAuth(bool secure, boost::asio::yield_context& yield)
532  {
533  testcase << "Server with authorization, "
534  << (secure ? "secure" : "non-secure");
535 
536  using namespace test::jtx;
537  Env env{*this, envconfig([secure](std::unique_ptr<Config> cfg) {
538  (*cfg)["port_rpc"].set("user", "me");
539  (*cfg)["port_rpc"].set("password", "secret");
540  (*cfg)["port_rpc"].set(
541  "protocol", secure ? "https" : "http");
542  if (secure)
543  (*cfg)["port_ws"].set("protocol", "http,ws");
544  return cfg;
545  })};
546 
547  Json::Value jr;
548  jr[jss::method] = "server_info";
549  boost::beast::http::response<boost::beast::http::string_body> resp;
550  boost::system::error_code ec;
551  doHTTPRequest(env, yield, secure, resp, ec, to_string(jr));
552  BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
553 
554  myFields auth;
555  auth.insert("Authorization", "");
556  doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth);
557  BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
558 
559  auth.set("Authorization", "Basic NOT-VALID");
560  doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth);
561  BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
562 
563  auth.set("Authorization", "Basic " + base64_encode("me:badpass"));
564  doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth);
565  BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
566 
567  auto const user = env.app()
568  .config()
569  .section("port_rpc")
570  .get<std::string>("user")
571  .value();
572  auto const pass = env.app()
573  .config()
574  .section("port_rpc")
575  .get<std::string>("password")
576  .value();
577 
578  // try with the correct user/pass, but not encoded
579  auth.set("Authorization", "Basic " + user + ":" + pass);
580  doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth);
581  BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
582 
583  // finally if we use the correct user/pass encoded, we should get a 200
584  auth.set("Authorization", "Basic " + base64_encode(user + ":" + pass));
585  doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth);
586  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
587  BEAST_EXPECT(!resp.body().empty());
588  }
589 
590  void
591  testLimit(boost::asio::yield_context& yield, int limit)
592  {
593  testcase << "Server with connection limit of " << limit;
594 
595  using namespace test::jtx;
596  using namespace boost::asio;
597  using namespace boost::beast::http;
598  Env env{*this, envconfig([&](std::unique_ptr<Config> cfg) {
599  (*cfg)["port_rpc"].set("limit", std::to_string(limit));
600  return cfg;
601  })};
602 
603  auto const port =
604  env.app().config()["port_rpc"].get<std::uint16_t>("port").value();
605  auto const ip =
606  env.app().config()["port_rpc"].get<std::string>("ip").value();
607 
608  boost::system::error_code ec;
609  io_service& ios = get_io_service();
610  ip::tcp::resolver r{ios};
611 
612  Json::Value jr;
613  jr[jss::method] = "server_info";
614 
615  auto it = r.async_resolve(
616  ip::tcp::resolver::query{ip, std::to_string(port)}, yield[ec]);
617  BEAST_EXPECT(!ec);
618 
620  clients;
621  int connectionCount{1}; // starts at 1 because the Env already has one
622  // for JSONRPCCLient
623 
624  // for nonzero limits, go one past the limit, although failures happen
625  // at the limit, so this really leads to the last two clients failing.
626  // for zero limit, pick an arbitrary nonzero number of clients - all
627  // should connect fine.
628 
629  int testTo = (limit == 0) ? 50 : limit + 1;
630  while (connectionCount < testTo)
631  {
633  ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
634  async_connect(clients.back().first, it, yield[ec]);
635  BEAST_EXPECT(!ec);
636  auto req = makeHTTPRequest(ip, port, to_string(jr), {});
637  async_write(clients.back().first, req, yield[ec]);
638  BEAST_EXPECT(!ec);
639  ++connectionCount;
640  }
641 
642  int readCount = 0;
643  for (auto& [soc, buf] : clients)
644  {
645  boost::beast::http::response<boost::beast::http::string_body> resp;
646  async_read(soc, buf, resp, yield[ec]);
647  ++readCount;
648  // expect the reads to fail for the clients that connected at or
649  // above the limit. If limit is 0, all reads should succeed
650  BEAST_EXPECT(
651  (limit == 0 || readCount < limit - 1) ? (!ec) : bool(ec));
652  }
653  }
654 
655  void
656  testWSHandoff(boost::asio::yield_context& yield)
657  {
658  testcase("Connection with WS handoff");
659 
660  using namespace test::jtx;
661  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
662  (*cfg)["port_ws"].set("protocol", "wss");
663  return cfg;
664  })};
665 
666  auto const port =
667  env.app().config()["port_ws"].get<std::uint16_t>("port").value();
668  auto const ip =
669  env.app().config()["port_ws"].get<std::string>("ip").value();
670  boost::beast::http::response<boost::beast::http::string_body> resp;
671  boost::system::error_code ec;
672  doRequest(yield, makeWSUpgrade(ip, port), ip, port, true, resp, ec);
673  BEAST_EXPECT(
674  resp.result() == boost::beast::http::status::switching_protocols);
675  BEAST_EXPECT(
676  resp.find("Upgrade") != resp.end() &&
677  resp["Upgrade"] == "websocket");
678  BEAST_EXPECT(
679  resp.find("Connection") != resp.end() &&
680  resp["Connection"] == "upgrade");
681  }
682 
683  void
684  testNoRPC(boost::asio::yield_context& yield)
685  {
686  testcase("Connection to port with no RPC enabled");
687 
688  using namespace test::jtx;
689  Env env{*this};
690 
691  auto const port =
692  env.app().config()["port_ws"].get<std::uint16_t>("port").value();
693  auto const ip =
694  env.app().config()["port_ws"].get<std::string>("ip").value();
695  boost::beast::http::response<boost::beast::http::string_body> resp;
696  boost::system::error_code ec;
697  // body content is required here to avoid being
698  // detected as a status request
699  doRequest(
700  yield,
701  makeHTTPRequest(ip, port, "foo", {}),
702  ip,
703  port,
704  false,
705  resp,
706  ec);
707  BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
708  BEAST_EXPECT(resp.body() == "Forbidden\r\n");
709  }
710 
711  void
712  testWSRequests(boost::asio::yield_context& yield)
713  {
714  testcase("WS client sends assorted input");
715 
716  using namespace test::jtx;
717  using namespace boost::asio;
718  using namespace boost::beast::http;
719  Env env{*this};
720 
721  auto const port =
722  env.app().config()["port_ws"].get<std::uint16_t>("port").value();
723  auto const ip =
724  env.app().config()["port_ws"].get<std::string>("ip").value();
725  boost::system::error_code ec;
726 
727  io_service& ios = get_io_service();
728  ip::tcp::resolver r{ios};
729 
730  auto it = r.async_resolve(
731  ip::tcp::resolver::query{ip, std::to_string(port)}, yield[ec]);
732  if (!BEAST_EXPECT(!ec))
733  return;
734 
735  ip::tcp::socket sock{ios};
736  async_connect(sock, it, yield[ec]);
737  if (!BEAST_EXPECT(!ec))
738  return;
739 
740  boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
741  ws.handshake(ip + ":" + std::to_string(port), "/");
742 
743  // helper lambda, used below
744  auto sendAndParse = [&](std::string const& req) -> Json::Value {
745  ws.async_write_some(true, buffer(req), yield[ec]);
746  if (!BEAST_EXPECT(!ec))
747  return Json::objectValue;
748 
749  boost::beast::multi_buffer sb;
750  ws.async_read(sb, yield[ec]);
751  if (!BEAST_EXPECT(!ec))
752  return Json::objectValue;
753 
754  Json::Value resp;
755  Json::Reader jr;
756  if (!BEAST_EXPECT(jr.parse(
757  boost::lexical_cast<std::string>(
758  boost::beast::make_printable(sb.data())),
759  resp)))
760  return Json::objectValue;
761  sb.consume(sb.size());
762  return resp;
763  };
764 
765  { // send invalid json
766  auto resp = sendAndParse("NOT JSON");
767  BEAST_EXPECT(
768  resp.isMember(jss::error) && resp[jss::error] == "jsonInvalid");
769  BEAST_EXPECT(!resp.isMember(jss::status));
770  }
771 
772  { // send incorrect json (method and command fields differ)
773  Json::Value jv;
774  jv[jss::command] = "foo";
775  jv[jss::method] = "bar";
776  auto resp = sendAndParse(to_string(jv));
777  BEAST_EXPECT(
778  resp.isMember(jss::error) &&
779  resp[jss::error] == "missingCommand");
780  BEAST_EXPECT(
781  resp.isMember(jss::status) && resp[jss::status] == "error");
782  }
783 
784  { // send a ping (not an error)
785  Json::Value jv;
786  jv[jss::command] = "ping";
787  auto resp = sendAndParse(to_string(jv));
788  BEAST_EXPECT(
789  resp.isMember(jss::status) && resp[jss::status] == "success");
790  BEAST_EXPECT(
791  resp.isMember(jss::result) &&
792  resp[jss::result].isMember(jss::role) &&
793  resp[jss::result][jss::role] == "admin");
794  }
795  }
796 
797  void
798  testAmendmentWarning(boost::asio::yield_context& yield)
799  {
800  testcase(
801  "Status request over WS and RPC with/without Amendment Warning");
802  using namespace jtx;
803  using namespace boost::asio;
804  using namespace boost::beast::http;
805  Env env{
806  *this,
807  validator(
809  cfg->section("port_rpc").set("protocol", "http");
810  return cfg;
811  }),
812  "")};
813 
814  env.close();
815 
816  // advance the ledger so that server status
817  // sees a published ledger -- without this, we get a status
818  // failure message about no published ledgers
819  env.app().getLedgerMaster().tryAdvance();
820 
821  // make an RPC server info request and look for
822  // amendment warning status
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));
826  BEAST_EXPECT(
827  env.app().getOPs().getConsensusInfo()["validating"] == true);
828  BEAST_EXPECT(!si.isMember(jss::warnings));
829 
830  // make an RPC server state request and look for
831  // amendment warning status
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));
835  BEAST_EXPECT(
836  env.app().getOPs().getConsensusInfo()["validating"] == true);
837  BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
838 
839  auto const port_ws =
840  env.app().config()["port_ws"].get<std::uint16_t>("port");
841  auto const ip_ws = env.app().config()["port_ws"].get<std::string>("ip");
842 
843  boost::system::error_code ec;
844  response<string_body> resp;
845 
846  doRequest(
847  yield,
848  makeHTTPRequest(*ip_ws, *port_ws, "", {}),
849  *ip_ws,
850  *port_ws,
851  false,
852  resp,
853  ec);
854 
855  if (!BEAST_EXPECTS(!ec, ec.message()))
856  return;
857  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
858  BEAST_EXPECT(
859  resp.body().find("connectivity is working.") != std::string::npos);
860 
861  // mark the Network as having an Amendment Warning, but won't fail
862  env.app().getOPs().setAmendmentWarned();
863  env.app().getOPs().beginConsensus(env.closed()->info().hash);
864 
865  // consensus doesn't change
866  BEAST_EXPECT(
867  env.app().getOPs().getConsensusInfo()["validating"] == true);
868 
869  // RPC request server_info again, now unsupported majority should be
870  // returned
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));
874  BEAST_EXPECT(
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() ==
880 
881  // RPC request server_state again, now unsupported majority should be
882  // returned
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));
886  BEAST_EXPECT(
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() ==
892 
893  // but status does not indicate a problem
894  doRequest(
895  yield,
896  makeHTTPRequest(*ip_ws, *port_ws, "", {}),
897  *ip_ws,
898  *port_ws,
899  false,
900  resp,
901  ec);
902 
903  if (!BEAST_EXPECTS(!ec, ec.message()))
904  return;
905  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
906  BEAST_EXPECT(
907  resp.body().find("connectivity is working.") != std::string::npos);
908 
909  // with ELB_SUPPORT, status still does not indicate a problem
910  env.app().config().ELB_SUPPORT = true;
911 
912  doRequest(
913  yield,
914  makeHTTPRequest(*ip_ws, *port_ws, "", {}),
915  *ip_ws,
916  *port_ws,
917  false,
918  resp,
919  ec);
920 
921  if (!BEAST_EXPECTS(!ec, ec.message()))
922  return;
923  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
924  BEAST_EXPECT(
925  resp.body().find("connectivity is working.") != std::string::npos);
926  }
927 
928  void
929  testAmendmentBlock(boost::asio::yield_context& yield)
930  {
931  testcase("Status request over WS and RPC with/without Amendment Block");
932  using namespace jtx;
933  using namespace boost::asio;
934  using namespace boost::beast::http;
935  Env env{
936  *this,
937  validator(
939  cfg->section("port_rpc").set("protocol", "http");
940  return cfg;
941  }),
942  "")};
943 
944  env.close();
945 
946  // advance the ledger so that server status
947  // sees a published ledger -- without this, we get a status
948  // failure message about no published ledgers
949  env.app().getLedgerMaster().tryAdvance();
950 
951  // make an RPC server info request and look for
952  // amendment_blocked status
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));
956  BEAST_EXPECT(
957  env.app().getOPs().getConsensusInfo()["validating"] == true);
958  BEAST_EXPECT(!si.isMember(jss::warnings));
959 
960  // make an RPC server state request and look for
961  // amendment_blocked status
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));
965  BEAST_EXPECT(
966  env.app().getOPs().getConsensusInfo()["validating"] == true);
967  BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
968 
969  auto const port_ws =
970  env.app().config()["port_ws"].get<std::uint16_t>("port");
971  auto const ip_ws = env.app().config()["port_ws"].get<std::string>("ip");
972 
973  boost::system::error_code ec;
974  response<string_body> resp;
975 
976  doRequest(
977  yield,
978  makeHTTPRequest(*ip_ws, *port_ws, "", {}),
979  *ip_ws,
980  *port_ws,
981  false,
982  resp,
983  ec);
984 
985  if (!BEAST_EXPECTS(!ec, ec.message()))
986  return;
987  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
988  BEAST_EXPECT(
989  resp.body().find("connectivity is working.") != std::string::npos);
990 
991  // mark the Network as Amendment Blocked, but still won't fail until
992  // ELB is enabled (next step)
993  env.app().getOPs().setAmendmentBlocked();
994  env.app().getOPs().beginConsensus(env.closed()->info().hash);
995 
996  // consensus now sees validation disabled
997  BEAST_EXPECT(
998  env.app().getOPs().getConsensusInfo()["validating"] == false);
999 
1000  // RPC request server_info again, now AB should be returned
1001  si = env.rpc("server_info")[jss::result];
1002  BEAST_EXPECT(si.isMember(jss::info));
1003  BEAST_EXPECT(
1004  si[jss::info].isMember(jss::amendment_blocked) &&
1005  si[jss::info][jss::amendment_blocked] == true);
1006  BEAST_EXPECT(
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() ==
1012 
1013  // RPC request server_state again, now AB should be returned
1014  si = env.rpc("server_state")[jss::result];
1015  BEAST_EXPECT(
1016  si[jss::state].isMember(jss::amendment_blocked) &&
1017  si[jss::state][jss::amendment_blocked] == true);
1018  BEAST_EXPECT(
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() ==
1024 
1025  // but status does not indicate because it still relies on ELB
1026  // being enabled
1027  doRequest(
1028  yield,
1029  makeHTTPRequest(*ip_ws, *port_ws, "", {}),
1030  *ip_ws,
1031  *port_ws,
1032  false,
1033  resp,
1034  ec);
1035 
1036  if (!BEAST_EXPECTS(!ec, ec.message()))
1037  return;
1038  BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
1039  BEAST_EXPECT(
1040  resp.body().find("connectivity is working.") != std::string::npos);
1041 
1042  env.app().config().ELB_SUPPORT = true;
1043 
1044  doRequest(
1045  yield,
1046  makeHTTPRequest(*ip_ws, *port_ws, "", {}),
1047  *ip_ws,
1048  *port_ws,
1049  false,
1050  resp,
1051  ec);
1052 
1053  if (!BEAST_EXPECTS(!ec, ec.message()))
1054  return;
1055  BEAST_EXPECT(
1056  resp.result() == boost::beast::http::status::internal_server_error);
1057  BEAST_EXPECT(
1058  resp.body().find("cannot accept clients:") != std::string::npos);
1059  BEAST_EXPECT(
1060  resp.body().find("Server version too old") != std::string::npos);
1061  }
1062 
1063  void
1064  testRPCRequests(boost::asio::yield_context& yield)
1065  {
1066  testcase("RPC client sends assorted input");
1067 
1068  using namespace test::jtx;
1069  Env env{*this};
1070 
1071  boost::system::error_code ec;
1072  {
1073  boost::beast::http::response<boost::beast::http::string_body> resp;
1074  doHTTPRequest(env, yield, false, resp, ec, "{}");
1075  BEAST_EXPECT(
1076  resp.result() == boost::beast::http::status::bad_request);
1077  BEAST_EXPECT(resp.body() == "Unable to parse request: \r\n");
1078  }
1079 
1080  {
1081  boost::beast::http::response<boost::beast::http::string_body> resp;
1082  Json::Value jv;
1083  jv["invalid"] = 1;
1084  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1085  BEAST_EXPECT(
1086  resp.result() == boost::beast::http::status::bad_request);
1087  BEAST_EXPECT(resp.body() == "Null method\r\n");
1088  }
1089 
1090  {
1091  boost::beast::http::response<boost::beast::http::string_body> resp;
1093  jv.append("invalid");
1094  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1095  BEAST_EXPECT(
1096  resp.result() == boost::beast::http::status::bad_request);
1097  BEAST_EXPECT(resp.body() == "Unable to parse request: \r\n");
1098  }
1099 
1100  {
1101  boost::beast::http::response<boost::beast::http::string_body> resp;
1103  Json::Value j;
1104  j["invalid"] = 1;
1105  jv.append(j);
1106  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1107  BEAST_EXPECT(
1108  resp.result() == boost::beast::http::status::bad_request);
1109  BEAST_EXPECT(resp.body() == "Unable to parse request: \r\n");
1110  }
1111 
1112  {
1113  boost::beast::http::response<boost::beast::http::string_body> resp;
1114  Json::Value jv;
1115  jv[jss::method] = "batch";
1116  jv[jss::params] = 2;
1117  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1118  BEAST_EXPECT(
1119  resp.result() == boost::beast::http::status::bad_request);
1120  BEAST_EXPECT(resp.body() == "Malformed batch request\r\n");
1121  }
1122 
1123  {
1124  boost::beast::http::response<boost::beast::http::string_body> resp;
1125  Json::Value jv;
1126  jv[jss::method] = "batch";
1127  jv[jss::params] = Json::objectValue;
1128  jv[jss::params]["invalid"] = 3;
1129  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1130  BEAST_EXPECT(
1131  resp.result() == boost::beast::http::status::bad_request);
1132  BEAST_EXPECT(resp.body() == "Malformed batch request\r\n");
1133  }
1134 
1135  Json::Value jv;
1136  {
1137  boost::beast::http::response<boost::beast::http::string_body> resp;
1138  jv[jss::method] = Json::nullValue;
1139  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1140  BEAST_EXPECT(
1141  resp.result() == boost::beast::http::status::bad_request);
1142  BEAST_EXPECT(resp.body() == "Null method\r\n");
1143  }
1144 
1145  {
1146  boost::beast::http::response<boost::beast::http::string_body> resp;
1147  jv[jss::method] = 1;
1148  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1149  BEAST_EXPECT(
1150  resp.result() == boost::beast::http::status::bad_request);
1151  BEAST_EXPECT(resp.body() == "method is not string\r\n");
1152  }
1153 
1154  {
1155  boost::beast::http::response<boost::beast::http::string_body> resp;
1156  jv[jss::method] = "";
1157  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1158  BEAST_EXPECT(
1159  resp.result() == boost::beast::http::status::bad_request);
1160  BEAST_EXPECT(resp.body() == "method is empty\r\n");
1161  }
1162 
1163  {
1164  boost::beast::http::response<boost::beast::http::string_body> resp;
1165  jv[jss::method] = "some_method";
1166  jv[jss::params] = "params";
1167  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1168  BEAST_EXPECT(
1169  resp.result() == boost::beast::http::status::bad_request);
1170  BEAST_EXPECT(resp.body() == "params unparseable\r\n");
1171  }
1172 
1173  {
1174  boost::beast::http::response<boost::beast::http::string_body> resp;
1175  jv[jss::params] = Json::arrayValue;
1176  jv[jss::params][0u] = "not an object";
1177  doHTTPRequest(env, yield, false, resp, ec, to_string(jv));
1178  BEAST_EXPECT(
1179  resp.result() == boost::beast::http::status::bad_request);
1180  BEAST_EXPECT(resp.body() == "params unparseable\r\n");
1181  }
1182  }
1183 
1184  void
1185  testStatusNotOkay(boost::asio::yield_context& yield)
1186  {
1187  testcase("Server status not okay");
1188 
1189  using namespace test::jtx;
1190  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
1191  cfg->ELB_SUPPORT = true;
1192  return cfg;
1193  })};
1194 
1195  // raise the fee so that the server is considered overloaded
1196  env.app().getFeeTrack().raiseLocalFee();
1197 
1198  boost::beast::http::response<boost::beast::http::string_body> resp;
1199  boost::system::error_code ec;
1200  doHTTPRequest(env, yield, false, resp, ec);
1201  BEAST_EXPECT(
1202  resp.result() == boost::beast::http::status::internal_server_error);
1203  std::regex body{"Server cannot accept clients"};
1204  BEAST_EXPECT(std::regex_search(resp.body(), body));
1205  }
1206 
1207 public:
1208  void
1209  run() override
1210  {
1211  for (auto it : {"http", "ws", "ws2"})
1212  {
1213  testAdminRequest(it, true, true);
1214  testAdminRequest(it, true, false);
1215  testAdminRequest(it, false, false);
1216  }
1217 
1218  yield_to([&](boost::asio::yield_context& yield) {
1219  testWSClientToHttpServer(yield);
1220  testStatusRequest(yield);
1221  testTruncatedWSUpgrade(yield);
1222 
1223  // these are secure/insecure protocol pairs, i.e. for
1224  // each item, the second value is the secure or insecure equivalent
1225  testCantConnect("ws", "wss", yield);
1226  testCantConnect("ws2", "wss2", yield);
1227  testCantConnect("http", "https", yield);
1228  testCantConnect("wss", "ws", yield);
1229  testCantConnect("wss2", "ws2", yield);
1230  testCantConnect("https", "http", yield);
1231 
1232  testAmendmentWarning(yield);
1233  testAmendmentBlock(yield);
1234  testAuth(false, yield);
1235  testAuth(true, yield);
1236  testLimit(yield, 5);
1237  testLimit(yield, 0);
1238  testWSHandoff(yield);
1239  testNoRPC(yield);
1240  testWSRequests(yield);
1241  testRPCRequests(yield);
1242  testStatusNotOkay(yield);
1243  });
1244  }
1245 };
1246 
1247 BEAST_DEFINE_TESTSUITE(ServerStatus, server, ripple);
1248 
1249 } // namespace test
1250 } // namespace ripple
ripple::test::ServerStatus_test::testRPCRequests
void testRPCRequests(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:1064
ripple::test::ServerStatus_test::testWSRequests
void testWSRequests(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:712
regex
std::string
STL class.
ripple::test::ServerStatus_test::doWSRequest
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)
Definition: ServerStatus_test.cpp:210
std::uniform_int_distribution
ripple::test::ServerStatus_test::testLimit
void testLimit(boost::asio::yield_context &yield, int limit)
Definition: ServerStatus_test.cpp:591
ripple::test::jtx::Env::closed
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition: Env.cpp:115
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::test::jtx::validator
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
Definition: envconfig.cpp:116
std::vector
STL class.
ripple::base64_encode
std::string base64_encode(std::uint8_t const *data, std::size_t len)
Definition: base64.cpp:236
ripple::test::ServerStatus_test::makeWSUpgrade
auto makeWSUpgrade(std::string const &host, uint16_t port)
Definition: ServerStatus_test.cpp:92
random
ripple::test::ServerStatus_test::makeConfig
auto makeConfig(std::string const &proto, bool admin=true, bool credentials=false)
Definition: ServerStatus_test.cpp:52
ripple::NetworkOPs::setAmendmentBlocked
virtual void setAmendmentBlocked()=0
ripple::test::makeJSONRPCClient
std::unique_ptr< AbstractClient > makeJSONRPCClient(Config const &cfg, unsigned rpc_version)
Returns a client using JSON-RPC over HTTP/S.
Definition: JSONRPCClient.cpp:155
ripple::test::ServerStatus_test::testWSClientToHttpServer
void testWSClientToHttpServer(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:387
ripple::test::ServerStatus_test::testTruncatedWSUpgrade
void testTruncatedWSUpgrade(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:452
ripple::test::ServerStatus_test::testStatusNotOkay
void testStatusNotOkay(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:1185
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
std::vector::back
T back(T... args)
ripple::test::ServerStatus_test::testNoRPC
void testNoRPC(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:684
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::test::ServerStatus_test::testAdminRequest
void testAdminRequest(std::string const &proto, bool admin, bool credentials)
Definition: ServerStatus_test.cpp:295
ripple::test::ServerStatus_test::makeHTTPRequest
auto makeHTTPRequest(std::string const &host, uint16_t port, std::string const &body, myFields const &fields)
Definition: ServerStatus_test.cpp:121
ripple::test::ServerStatus_test::testCantConnect
void testCantConnect(std::string const &client_protocol, std::string const &server_protocol, boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:498
algorithm
ripple::Application::getOPs
virtual NetworkOPs & getOPs()=0
ripple::test::ServerStatus_test::testStatusRequest
void testStatusRequest(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:420
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
ripple::test::ServerStatus_test::makeAdminRequest
auto makeAdminRequest(jtx::Env &env, std::string const &proto, std::string const &user, std::string const &password, bool subobject=false)
Definition: ServerStatus_test.cpp:250
std::mt19937
ripple::test::getEnvLocalhostAddr
const char * getEnvLocalhostAddr()
Definition: envconfig.h:31
ripple::warnRPC_AMENDMENT_BLOCKED
@ warnRPC_AMENDMENT_BLOCKED
Definition: ErrorCodes.h:155
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
std::random_device
ripple::test::ServerStatus_test::testAmendmentBlock
void testAmendmentBlock(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:929
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::NetworkOPs::setAmendmentWarned
virtual void setAmendmentWarned()=0
ripple::test::ServerStatus_test::testAuth
void testAuth(bool secure, boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:531
ripple::Application::config
virtual Config & config()=0
std::to_string
T to_string(T... args)
boost::asio
Definition: Overlay.h:41
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:313
array
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::warnRPC_UNSUPPORTED_MAJORITY
@ warnRPC_UNSUPPORTED_MAJORITY
Definition: ErrorCodes.h:154
ripple::NetworkOPs::getConsensusInfo
virtual Json::Value getConsensusInfo()=0
std::uint16_t
std::regex
std::regex_search
T regex_search(T... args)
ripple::test::ServerStatus_test::testAmendmentWarning
void testAmendmentWarning(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:798
ripple::test::ServerStatus_test::run
void run() override
Definition: ServerStatus_test.cpp:1209
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::LoadFeeTrack::raiseLocalFee
bool raiseLocalFee()
Definition: LoadFeeTrack.cpp:37
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
ripple::test::ServerStatus_test::testWSHandoff
void testWSHandoff(boost::asio::yield_context &yield)
Definition: ServerStatus_test.cpp:656
ripple::Section::get
std::optional< T > get(std::string const &name) const
Definition: BasicConfig.h:138
ripple::test::makeWSClient
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.
Definition: WSClient.cpp:300
std::string::empty
T empty(T... args)
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
std::make_pair
T make_pair(T... args)
ripple::NetworkOPs::beginConsensus
virtual bool beginConsensus(uint256 const &netLCL)=0
ripple::test::ServerStatus_test::myFields
Definition: ServerStatus_test.cpp:47
std::unique_ptr
STL class.
ripple::LedgerMaster::tryAdvance
void tryAdvance()
Definition: LedgerMaster.cpp:1459
ripple::Config::ELB_SUPPORT
bool ELB_SUPPORT
Definition: Config.h:147
ripple::test::ServerStatus_test::doHTTPRequest
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={})
Definition: ServerStatus_test.cpp:226
ripple::test::ServerStatus_test::doRequest
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)
Definition: ServerStatus_test.cpp:153
ripple::test::ServerStatus_test
Definition: ServerStatus_test.cpp:44
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:687
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:127
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)