rippled
Server_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/basics/make_SSLContext.h>
21 #include <ripple/beast/rfc2616.h>
22 #include <ripple/beast/unit_test.h>
23 #include <ripple/core/ConfigSections.h>
24 #include <ripple/server/Server.h>
25 #include <ripple/server/Session.h>
26 
27 #include <test/jtx.h>
28 #include <test/jtx/CaptureLogs.h>
29 #include <test/jtx/envconfig.h>
30 #include <test/unit_test/SuiteJournal.h>
31 
32 #include <boost/asio.hpp>
33 #include <boost/beast/core/tcp_stream.hpp>
34 #include <boost/beast/ssl/ssl_stream.hpp>
35 #include <boost/utility/in_place_factory.hpp>
36 
37 #include <chrono>
38 #include <optional>
39 #include <stdexcept>
40 #include <thread>
41 
42 namespace ripple {
43 namespace test {
44 
45 using socket_type = boost::beast::tcp_stream;
46 using stream_type = boost::beast::ssl_stream<socket_type>;
47 
48 class Server_test : public beast::unit_test::suite
49 {
50 public:
51  class TestThread
52  {
53  private:
54  boost::asio::io_service io_service_;
57 
58  public:
60  : work_(std::in_place, std::ref(io_service_))
61  , thread_([&]() { this->io_service_.run(); })
62  {
63  }
64 
66  {
67  work_.reset();
68  thread_.join();
69  }
70 
71  boost::asio::io_service&
73  {
74  return io_service_;
75  }
76  };
77 
78  //--------------------------------------------------------------------------
79 
81  {
82  beast::unit_test::suite& suite_;
83 
84  public:
85  explicit TestSink(beast::unit_test::suite& suite)
86  : Sink(beast::severities::kWarning, false), suite_(suite)
87  {
88  }
89 
90  void
92  override
93  {
94  if (level < threshold())
95  return;
96 
97  suite_.log << text << std::endl;
98  }
99  };
100 
101  //--------------------------------------------------------------------------
102 
103  struct TestHandler
104  {
105  bool
106  onAccept(Session& session, boost::asio::ip::tcp::endpoint endpoint)
107  {
108  return true;
109  }
110 
111  Handoff
113  Session& session,
115  http_request_type&& request,
116  boost::asio::ip::tcp::endpoint remote_address)
117  {
118  return Handoff{};
119  }
120 
121  Handoff
123  Session& session,
124  http_request_type&& request,
125  boost::asio::ip::tcp::endpoint remote_address)
126  {
127  return Handoff{};
128  }
129 
130  void
131  onRequest(Session& session)
132  {
133  session.write(std::string("Hello, world!\n"));
134  if (beast::rfc2616::is_keep_alive(session.request()))
135  session.complete();
136  else
137  session.close(true);
138  }
139 
140  void
144  {
145  }
146 
147  void
148  onClose(Session& session, boost::system::error_code const&)
149  {
150  }
151 
152  void
153  onStopped(Server& server)
154  {
155  }
156  };
157 
158  //--------------------------------------------------------------------------
159 
160  // Connect to an address
161  template <class Socket>
162  bool
163  connect(Socket& s, typename Socket::endpoint_type const& ep)
164  {
165  try
166  {
167  s.connect(ep);
168  pass();
169  return true;
170  }
171  catch (std::exception const& e)
172  {
173  fail(e.what());
174  }
175 
176  return false;
177  }
178 
179  // Write a string to the stream
180  template <class SyncWriteStream>
181  bool
182  write(SyncWriteStream& s, std::string const& text)
183  {
184  try
185  {
186  boost::asio::write(s, boost::asio::buffer(text));
187  pass();
188  return true;
189  }
190  catch (std::exception const& e)
191  {
192  fail(e.what());
193  }
194  return false;
195  }
196 
197  // Expect that reading the stream produces a matching string
198  template <class SyncReadStream>
199  bool
200  expect_read(SyncReadStream& s, std::string const& match)
201  {
202  boost::asio::streambuf b(1000); // limit on read
203  try
204  {
205  auto const n = boost::asio::read_until(s, b, '\n');
206  if (BEAST_EXPECT(n == match.size()))
207  {
208  std::string got;
209  got.resize(n);
210  boost::asio::buffer_copy(
211  boost::asio::buffer(&got[0], n), b.data());
212  return BEAST_EXPECT(got == match);
213  }
214  }
215  catch (std::length_error const& e)
216  {
217  fail(e.what());
218  }
219  catch (std::exception const& e)
220  {
221  fail(e.what());
222  }
223  return false;
224  }
225 
226  void
227  test_request(boost::asio::ip::tcp::endpoint const& ep)
228  {
229  boost::asio::io_service ios;
230  using socket = boost::asio::ip::tcp::socket;
231  socket s(ios);
232 
233  if (!connect(s, ep))
234  return;
235 
236  if (!write(
237  s,
238  "GET / HTTP/1.1\r\n"
239  "Connection: close\r\n"
240  "\r\n"))
241  return;
242 
243  if (!expect_read(s, "Hello, world!\n"))
244  return;
245 
246  boost::system::error_code ec;
247  s.shutdown(socket::shutdown_both, ec);
248 
250  }
251 
252  void
253  test_keepalive(boost::asio::ip::tcp::endpoint const& ep)
254  {
255  boost::asio::io_service ios;
256  using socket = boost::asio::ip::tcp::socket;
257  socket s(ios);
258 
259  if (!connect(s, ep))
260  return;
261 
262  if (!write(
263  s,
264  "GET / HTTP/1.1\r\n"
265  "Connection: Keep-Alive\r\n"
266  "\r\n"))
267  return;
268 
269  if (!expect_read(s, "Hello, world!\n"))
270  return;
271 
272  if (!write(
273  s,
274  "GET / HTTP/1.1\r\n"
275  "Connection: close\r\n"
276  "\r\n"))
277  return;
278 
279  if (!expect_read(s, "Hello, world!\n"))
280  return;
281 
282  boost::system::error_code ec;
283  s.shutdown(socket::shutdown_both, ec);
284  }
285 
286  void
288  {
289  testcase("Basic client/server");
290  TestSink sink{*this};
291  TestThread thread;
292  sink.threshold(beast::severities::Severity::kAll);
293  beast::Journal journal{sink};
294  TestHandler handler;
295  auto s = make_Server(handler, thread.get_io_service(), journal);
296  std::vector<Port> serverPort(1);
297  serverPort.back().ip =
298  beast::IP::Address::from_string(getEnvLocalhostAddr()),
299  serverPort.back().port = 0;
300  serverPort.back().protocol.insert("http");
301  auto eps = s->ports(serverPort);
302  test_request(eps[0]);
303  test_keepalive(eps[0]);
304  // s->close();
305  s = nullptr;
306  pass();
307  }
308 
309  void
311  {
312  testcase("stress test");
313  struct NullHandler
314  {
315  bool
316  onAccept(Session& session, boost::asio::ip::tcp::endpoint endpoint)
317  {
318  return true;
319  }
320 
321  Handoff
322  onHandoff(
323  Session& session,
325  http_request_type&& request,
326  boost::asio::ip::tcp::endpoint remote_address)
327  {
328  return Handoff{};
329  }
330 
331  Handoff
332  onHandoff(
333  Session& session,
334  http_request_type&& request,
335  boost::asio::ip::tcp::endpoint remote_address)
336  {
337  return Handoff{};
338  }
339 
340  void
341  onRequest(Session& session)
342  {
343  }
344 
345  void
346  onWSMessage(
349  {
350  }
351 
352  void
353  onClose(Session& session, boost::system::error_code const&)
354  {
355  }
356 
357  void
358  onStopped(Server& server)
359  {
360  }
361  };
362 
363  using namespace beast::severities;
364  SuiteJournal journal("Server_test", *this);
365 
366  NullHandler h;
367  for (int i = 0; i < 1000; ++i)
368  {
369  TestThread thread;
370  auto s = make_Server(h, thread.get_io_service(), journal);
371  std::vector<Port> serverPort(1);
372  serverPort.back().ip =
373  beast::IP::Address::from_string(getEnvLocalhostAddr()),
374  serverPort.back().port = 0;
375  serverPort.back().protocol.insert("http");
376  s->ports(serverPort);
377  }
378  pass();
379  }
380 
381  void
383  {
384  testcase("Server config - invalid options");
385  using namespace test::jtx;
386 
387  std::string messages;
388 
389  except([&] {
390  Env env{
391  *this,
393  (*cfg).deprecatedClearSection("port_rpc");
394  return cfg;
395  }),
396  std::make_unique<CaptureLogs>(&messages)};
397  });
398  BEAST_EXPECT(
399  messages.find("Missing 'ip' in [port_rpc]") != std::string::npos);
400 
401  except([&] {
402  Env env{
403  *this,
405  (*cfg).deprecatedClearSection("port_rpc");
406  (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
407  return cfg;
408  }),
409  std::make_unique<CaptureLogs>(&messages)};
410  });
411  BEAST_EXPECT(
412  messages.find("Missing 'port' in [port_rpc]") != std::string::npos);
413 
414  except([&] {
415  Env env{
416  *this,
418  (*cfg).deprecatedClearSection("port_rpc");
419  (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
420  (*cfg)["port_rpc"].set("port", "0");
421  return cfg;
422  }),
423  std::make_unique<CaptureLogs>(&messages)};
424  });
425  BEAST_EXPECT(
426  messages.find("Invalid value '0' for key 'port' in [port_rpc]") !=
427  std::string::npos);
428 
429  except([&] {
430  Env env{
431  *this,
433  (*cfg).deprecatedClearSection("port_rpc");
434  (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
435  (*cfg)["port_rpc"].set("port", "8081");
436  (*cfg)["port_rpc"].set("protocol", "");
437  return cfg;
438  }),
439  std::make_unique<CaptureLogs>(&messages)};
440  });
441  BEAST_EXPECT(
442  messages.find("Missing 'protocol' in [port_rpc]") !=
443  std::string::npos);
444 
445  except(
446  [&] // this creates a standard test config without the server
447  // section
448  {
449  Env env{
450  *this,
452  cfg = std::make_unique<Config>();
453  cfg->overwrite(
454  ConfigSection::nodeDatabase(), "type", "memory");
455  cfg->overwrite(
456  ConfigSection::nodeDatabase(), "path", "main");
457  cfg->deprecatedClearSection(
459  cfg->legacy("database_path", "");
460  cfg->setupControl(true, true, true);
461  (*cfg)["port_peer"].set("ip", getEnvLocalhostAddr());
462  (*cfg)["port_peer"].set("port", "8080");
463  (*cfg)["port_peer"].set("protocol", "peer");
464  (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr());
465  (*cfg)["port_rpc"].set("port", "8081");
466  (*cfg)["port_rpc"].set("protocol", "http,ws2");
467  (*cfg)["port_rpc"].set("admin", getEnvLocalhostAddr());
468  (*cfg)["port_ws"].set("ip", getEnvLocalhostAddr());
469  (*cfg)["port_ws"].set("port", "8082");
470  (*cfg)["port_ws"].set("protocol", "ws");
471  (*cfg)["port_ws"].set("admin", getEnvLocalhostAddr());
472  return cfg;
473  }),
474  std::make_unique<CaptureLogs>(&messages)};
475  });
476  BEAST_EXPECT(
477  messages.find("Required section [server] is missing") !=
478  std::string::npos);
479 
480  except([&] // this creates a standard test config without some of the
481  // port sections
482  {
483  Env env{
484  *this,
486  cfg = std::make_unique<Config>();
487  cfg->overwrite(
488  ConfigSection::nodeDatabase(), "type", "memory");
489  cfg->overwrite(
490  ConfigSection::nodeDatabase(), "path", "main");
491  cfg->deprecatedClearSection(
493  cfg->legacy("database_path", "");
494  cfg->setupControl(true, true, true);
495  (*cfg)["server"].append("port_peer");
496  (*cfg)["server"].append("port_rpc");
497  (*cfg)["server"].append("port_ws");
498  return cfg;
499  }),
500  std::make_unique<CaptureLogs>(&messages)};
501  });
502  BEAST_EXPECT(
503  messages.find("Missing section: [port_peer]") != std::string::npos);
504  }
505 
506  void
507  run() override
508  {
509  basicTests();
510  stressTest();
511  testBadConfig();
512  }
513 };
514 
516 
517 } // namespace test
518 } // namespace ripple
ripple::test::Server_test::TestThread::TestThread
TestThread()
Definition: Server_test.cpp:59
ripple::test::Server_test::stressTest
void stressTest()
Definition: Server_test.cpp:310
beast::Journal::Sink
Abstraction for the underlying message destination.
Definition: Journal.h:74
ripple::test::Server_test::TestThread::~TestThread
~TestThread()
Definition: Server_test.cpp:65
std::string::resize
T resize(T... args)
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::make_Server
std::unique_ptr< Server > make_Server(Handler &handler, boost::asio::io_service &io_service, beast::Journal journal)
Create the HTTP server using the specified handler.
Definition: Server.h:34
std::string
STL class.
std::shared_ptr
STL class.
ripple::test::Server_test::TestHandler
Definition: Server_test.cpp:103
std::exception
STL class.
ripple::test::Server_test::basicTests
void basicTests()
Definition: Server_test.cpp:287
ripple::test::Server_test::TestHandler::onHandoff
Handoff onHandoff(Session &session, http_request_type &&request, boost::asio::ip::tcp::endpoint remote_address)
Definition: Server_test.cpp:122
ripple::test::socket_type
boost::beast::tcp_stream socket_type
Definition: Server_test.cpp:45
std::vector
STL class.
std::string::find
T find(T... args)
ripple::ConfigSection::importNodeDatabase
static std::string importNodeDatabase()
Definition: ConfigSections.h:43
std::chrono::seconds
beast::severities
A namespace for easy access to logging severity values.
Definition: Journal.h:29
ripple::test::Server_test::TestThread::get_io_service
boost::asio::io_service & get_io_service()
Definition: Server_test.cpp:72
ripple::test::Server_test::TestHandler::onRequest
void onRequest(Session &session)
Definition: Server_test.cpp:131
std::vector::back
T back(T... args)
ripple::test::Server_test::TestHandler::onAccept
bool onAccept(Session &session, boost::asio::ip::tcp::endpoint endpoint)
Definition: Server_test.cpp:106
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::Session
Persistent state information for a connection session.
Definition: Session.h:40
ripple::Session::write
void write(std::string const &s)
Send a copy of data asynchronously.
Definition: Session.h:74
ripple::test::Server_test::TestSink::write
void write(beast::severities::Severity level, std::string const &text) override
Write text to the sink at the specified severity.
Definition: Server_test.cpp:91
std::optional::reset
T reset(T... args)
ripple::test::Server_test::run
void run() override
Definition: Server_test.cpp:507
ripple::test::Server_test::TestSink::TestSink
TestSink(beast::unit_test::suite &suite)
Definition: Server_test.cpp:85
stdexcept
ripple::test::getEnvLocalhostAddr
const char * getEnvLocalhostAddr()
Definition: envconfig.h:31
ripple::test::Server_test::TestSink::suite_
beast::unit_test::suite & suite_
Definition: Server_test.cpp:82
beast::rfc2616::is_keep_alive
bool is_keep_alive(boost::beast::http::message< isRequest, Body, Fields > const &m)
Definition: rfc2616.h:386
thread
chrono
ripple::test::Server_test::expect_read
bool expect_read(SyncReadStream &s, std::string const &match)
Definition: Server_test.cpp:200
std::length_error
STL class.
ripple::test::stream_type
boost::beast::ssl_stream< socket_type > stream_type
Definition: Server_test.cpp:46
beast::Journal::Sink::Sink
Sink()=delete
ripple::test::Server_test::connect
bool connect(Socket &s, typename Socket::endpoint_type const &ep)
Definition: Server_test.cpp:163
ripple::Server
A multi-protocol server.
Definition: ServerImpl.h:44
ripple::Session::request
virtual http_request_type & request()=0
Returns the current HTTP request.
ripple::test::Server_test::test_keepalive
void test_keepalive(boost::asio::ip::tcp::endpoint const &ep)
Definition: Server_test.cpp:253
ripple::test::Server_test::write
bool write(SyncWriteStream &s, std::string const &text)
Definition: Server_test.cpp:182
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::Session::complete
virtual void complete()=0
Indicate that the response is complete.
ripple::test::Server_test::TestThread::io_service_
boost::asio::io_service io_service_
Definition: Server_test.cpp:54
ripple::test::SuiteJournal
Definition: SuiteJournal.h:88
ripple::test::Server_test::TestThread
Definition: Server_test.cpp:51
ripple::test::Server_test::TestHandler::onClose
void onClose(Session &session, boost::system::error_code const &)
Definition: Server_test.cpp:148
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
std::endl
T endl(T... args)
beast::Journal::Sink::threshold
virtual Severity threshold() const
Returns the minimum severity level this sink will report.
Definition: beast_Journal.cpp:106
beast::severities::Severity
Severity
Severity level / threshold of a Journal message.
Definition: Journal.h:31
std
STL namespace.
beast::severities::kWarning
@ kWarning
Definition: Journal.h:37
ripple::test::Server_test::TestThread::work_
std::optional< boost::asio::io_service::work > work_
Definition: Server_test.cpp:55
ripple::Session::close
virtual void close(bool graceful)=0
Close the session.
ripple::Handoff
Used to indicate the result of a server connection handoff.
Definition: Handoff.h:37
ripple::test::Server_test
Definition: Server_test.cpp:48
optional
ripple::test::Server_test::TestHandler::onWSMessage
void onWSMessage(std::shared_ptr< WSSession > session, std::vector< boost::asio::const_buffer > const &)
Definition: Server_test.cpp:141
ripple::test::Server_test::TestSink
Definition: Server_test.cpp:80
ripple::test::Server_test::TestHandler::onHandoff
Handoff onHandoff(Session &session, std::unique_ptr< stream_type > &&bundle, http_request_type &&request, boost::asio::ip::tcp::endpoint remote_address)
Definition: Server_test.cpp:112
ripple::test::Server_test::TestThread::thread_
std::thread thread_
Definition: Server_test.cpp:56
ripple::test::Server_test::test_request
void test_request(boost::asio::ip::tcp::endpoint const &ep)
Definition: Server_test.cpp:227
ripple::http_request_type
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition: Handshake.h:47
std::unique_ptr< stream_type >
ripple::test::Server_test::TestHandler::onStopped
void onStopped(Server &server)
Definition: Server_test.cpp:153
ripple::test::Server_test::testBadConfig
void testBadConfig()
Definition: Server_test.cpp:382
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::ConfigSection::nodeDatabase
static std::string nodeDatabase()
Definition: ConfigSections.h:33
std::thread::join
T join(T... args)
std::exception::what
T what(T... args)
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)
beast
Definition: base_uint.h:641