rippled
DatabaseDownloader_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright 2019 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/net/DatabaseDownloader.h>
21 #include <boost/filesystem/operations.hpp>
22 #include <condition_variable>
23 #include <mutex>
24 #include <test/jtx.h>
25 #include <test/jtx/TrustedPublisherServer.h>
26 #include <test/unit_test/FileDirGuard.h>
27 
28 namespace ripple {
29 namespace test {
30 
31 #define REPORT_FAILURE(D) reportFailure(D, __FILE__, __LINE__)
32 
33 class DatabaseDownloader_test : public beast::unit_test::suite
34 {
36  createServer(jtx::Env& env, bool ssl = true)
37  {
41  env.app().getIOService(),
42  list,
43  env.timeKeeper().now() + std::chrono::seconds{3600},
44  // No future VLs
45  {},
46  ssl);
47  }
48 
50  {
53  bool called = false;
54  boost::filesystem::path dest;
55 
56  void
57  operator()(boost::filesystem::path dst)
58  {
60  called = true;
61  dest = std::move(dst);
62  cv.notify_one();
63  };
64 
65  bool
67  {
69 
70  auto stat = cv.wait_for(
71  lk, std::chrono::seconds(10), [this] { return called; });
72 
73  called = false;
74  return stat;
75  };
76  };
78 
79  struct Downloader
80  {
84 
86  : journal_{sink_}
88  env.app().getIOService(),
89  env.app().config(),
90  journal_)}
91  {
92  }
93 
95  {
96  ptr_->stop();
97  }
98 
101  {
102  return ptr_.get();
103  }
104 
105  DatabaseDownloader const*
106  operator->() const
107  {
108  return ptr_.get();
109  }
110  };
111 
112  void
113  reportFailure(Downloader const& dl, char const* file, int line)
114  {
116  ss << "Failed. LOGS:\n"
117  << dl.sink_.messages().str()
118  << "\nDownloadCompleter failure."
119  "\nDatabaseDownloader session active? "
120  << std::boolalpha << dl->sessionIsActive()
121  << "\nDatabaseDownloader is stopping? " << std::boolalpha
122  << dl->isStopping();
123 
124  fail(ss.str(), file, line);
125  }
126 
127  void
129  {
130  testcase << std::string("Basic download - SSL ") +
131  (verify ? "Verify" : "No Verify");
132 
133  using namespace jtx;
134 
136  *this, "_cert", "ca.pem", TrustedPublisherServer::ca_cert()};
137 
138  Env env{*this, envconfig([&cert, &verify](std::unique_ptr<Config> cfg) {
139  if ((cfg->SSL_VERIFY = verify)) // yes, this is assignment
140  cfg->SSL_VERIFY_FILE = cert.file().string();
141  return cfg;
142  })};
143 
144  Downloader dl{env};
145 
146  // create a TrustedPublisherServer as a simple HTTP
147  // server to request from. Use the /textfile endpoint
148  // to get a simple text file sent as response.
149  auto server = createServer(env);
150 
152  *this, "downloads", "data", "", false, false};
153  // initiate the download and wait for the callback
154  // to be invoked
155  auto stat = dl->download(
156  server->local_endpoint().address().to_string(),
157  std::to_string(server->local_endpoint().port()),
158  "/textfile",
159  11,
160  data.file(),
161  std::function<void(boost::filesystem::path)>{std::ref(cb)});
162  if (!BEAST_EXPECT(stat))
163  {
164  REPORT_FAILURE(dl);
165  return;
166  }
167  if (!BEAST_EXPECT(cb.waitComplete()))
168  {
169  REPORT_FAILURE(dl);
170  return;
171  }
172  BEAST_EXPECT(cb.dest == data.file());
173  if (!BEAST_EXPECT(boost::filesystem::exists(data.file())))
174  return;
175  BEAST_EXPECT(boost::filesystem::file_size(data.file()) > 0);
176  }
177 
178  void
180  {
181  testcase("Error conditions");
182 
183  using namespace jtx;
184 
185  Env env{*this};
186 
187  {
188  // bad hostname
189  boost::system::error_code ec;
190  boost::asio::ip::tcp::resolver resolver{env.app().getIOService()};
191  auto const results = resolver.resolve("badhostname", "443", ec);
192  // we require an error in resolving this name in order
193  // for this test to pass. Some networks might have DNS hijacking
194  // that prevent NXDOMAIN, in which case the failure is not
195  // possible, so we skip the test.
196  if (ec)
197  {
198  Downloader dl{env};
199  ripple::test::detail::FileDirGuard const datafile{
200  *this, "downloads", "data", "", false, false};
201  BEAST_EXPECT(dl->download(
202  "badhostname",
203  "443",
204  "",
205  11,
206  datafile.file(),
207  std::function<void(boost::filesystem::path)>{
208  std::ref(cb)}));
209  if (!BEAST_EXPECT(cb.waitComplete()))
210  {
211  REPORT_FAILURE(dl);
212  }
213  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
214  BEAST_EXPECTS(
215  dl.sink_.messages().str().find("async_resolve") !=
216  std::string::npos,
217  dl.sink_.messages().str());
218  }
219  }
220  {
221  // can't connect
222  Downloader dl{env};
223  ripple::test::detail::FileDirGuard const datafile{
224  *this, "downloads", "data", "", false, false};
225  auto server = createServer(env);
226  auto host = server->local_endpoint().address().to_string();
227  auto port = std::to_string(server->local_endpoint().port());
228  server->stop();
229  BEAST_EXPECT(dl->download(
230  host,
231  port,
232  "",
233  11,
234  datafile.file(),
235  std::function<void(boost::filesystem::path)>{std::ref(cb)}));
236  if (!BEAST_EXPECT(cb.waitComplete()))
237  {
238  REPORT_FAILURE(dl);
239  }
240  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
241  BEAST_EXPECTS(
242  dl.sink_.messages().str().find("async_connect") !=
243  std::string::npos,
244  dl.sink_.messages().str());
245  }
246  {
247  // not ssl (failed handlshake)
248  Downloader dl{env};
249  ripple::test::detail::FileDirGuard const datafile{
250  *this, "downloads", "data", "", false, false};
251  auto server = createServer(env, false);
252  BEAST_EXPECT(dl->download(
253  server->local_endpoint().address().to_string(),
254  std::to_string(server->local_endpoint().port()),
255  "",
256  11,
257  datafile.file(),
258  std::function<void(boost::filesystem::path)>{std::ref(cb)}));
259  if (!BEAST_EXPECT(cb.waitComplete()))
260  {
261  REPORT_FAILURE(dl);
262  }
263  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
264  BEAST_EXPECTS(
265  dl.sink_.messages().str().find("async_handshake") !=
266  std::string::npos,
267  dl.sink_.messages().str());
268  }
269  {
270  // huge file (content length)
271  Downloader dl{env};
272  ripple::test::detail::FileDirGuard const datafile{
273  *this, "downloads", "data", "", false, false};
274  auto server = createServer(env);
275  BEAST_EXPECT(dl->download(
276  server->local_endpoint().address().to_string(),
277  std::to_string(server->local_endpoint().port()),
278  "/textfile/huge",
279  11,
280  datafile.file(),
281  std::function<void(boost::filesystem::path)>{std::ref(cb)}));
282  if (!BEAST_EXPECT(cb.waitComplete()))
283  {
284  REPORT_FAILURE(dl);
285  }
286  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
287  BEAST_EXPECTS(
288  dl.sink_.messages().str().find("Insufficient disk space") !=
289  std::string::npos,
290  dl.sink_.messages().str());
291  }
292  }
293 
294 public:
295  void
296  run() override
297  {
298  testDownload(true);
299  testDownload(false);
300  testFailures();
301  }
302 };
303 
304 #undef REPORT_FAILURE
305 
307 } // namespace test
308 } // namespace ripple
ripple::test::DatabaseDownloader_test::testDownload
void testDownload(bool verify)
Definition: DatabaseDownloader_test.cpp:128
std::string
STL class.
std::shared_ptr
STL class.
ripple::test::DatabaseDownloader_test::Downloader::Downloader
Downloader(jtx::Env &env)
Definition: DatabaseDownloader_test.cpp:85
ripple::test::DatabaseDownloader_test::Downloader::~Downloader
~Downloader()
Definition: DatabaseDownloader_test.cpp:94
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical) noexcept
Verify a signature on a message.
Definition: PublicKey.cpp:272
std::vector
STL class.
std::chrono::seconds
std::stringstream
STL class.
ripple::test::jtx::Env::timeKeeper
ManualTimeKeeper & timeKeeper()
Definition: Env.h:253
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
std::function
ripple::test::DatabaseDownloader_test::DownloadCompleter::cv
std::condition_variable cv
Definition: DatabaseDownloader_test.cpp:52
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::test::TrustedPublisherServer::ca_cert
static std::string const & ca_cert()
Definition: TrustedPublisherServer.h:400
ripple::test::DatabaseDownloader_test::cb
DownloadCompleter cb
Definition: DatabaseDownloader_test.cpp:77
std::vector::push_back
T push_back(T... args)
ripple::test::DatabaseDownloader_test::Downloader::ptr_
std::shared_ptr< DatabaseDownloader > ptr_
Definition: DatabaseDownloader_test.cpp:83
ripple::test::DatabaseDownloader_test::Downloader::sink_
test::StreamSink sink_
Definition: DatabaseDownloader_test.cpp:81
ripple::test::DatabaseDownloader_test::DownloadCompleter::operator()
void operator()(boost::filesystem::path dst)
Definition: DatabaseDownloader_test.cpp:57
ripple::test::DatabaseDownloader_test::DownloadCompleter::waitComplete
bool waitComplete()
Definition: DatabaseDownloader_test.cpp:66
std::unique_lock
STL class.
std::to_string
T to_string(T... args)
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::test::DatabaseDownloader_test::reportFailure
void reportFailure(Downloader const &dl, char const *file, int line)
Definition: DatabaseDownloader_test.cpp:113
ripple::test::DatabaseDownloader_test::run
void run() override
Definition: DatabaseDownloader_test.cpp:296
ripple::test::TrustedPublisherServer::randomValidator
static Validator randomValidator()
Definition: TrustedPublisherServer.h:148
ripple::test::DatabaseDownloader_test::Downloader::journal_
beast::Journal journal_
Definition: DatabaseDownloader_test.cpp:82
std::condition_variable::wait_for
T wait_for(T... args)
ripple::Application::getIOService
virtual boost::asio::io_service & getIOService()=0
ripple::test::StreamSink
Definition: SuiteJournal.h:114
ripple::HTTPDownloader::sessionIsActive
bool sessionIsActive() const
Definition: HTTPDownloader.cpp:297
std::condition_variable::notify_one
T notify_one(T... args)
ripple::test::StreamSink::messages
std::stringstream const & messages() const
Definition: SuiteJournal.h:134
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::HTTPDownloader::isStopping
bool isStopping() const
Definition: HTTPDownloader.cpp:304
ripple::test::DatabaseDownloader_test::Downloader::operator->
DatabaseDownloader * operator->()
Definition: DatabaseDownloader_test.cpp:100
ripple::test::DatabaseDownloader_test::Downloader::operator->
DatabaseDownloader const * operator->() const
Definition: DatabaseDownloader_test.cpp:106
ripple::test::DatabaseDownloader_test::DownloadCompleter::m
std::mutex m
Definition: DatabaseDownloader_test.cpp:51
std::boolalpha
T boolalpha(T... args)
ripple::test::DatabaseDownloader_test::DownloadCompleter
Definition: DatabaseDownloader_test.cpp:49
ripple::test::DatabaseDownloader_test::Downloader
Definition: DatabaseDownloader_test.cpp:79
condition_variable
ripple::test::make_TrustedPublisherServer
std::shared_ptr< TrustedPublisherServer > make_TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< TrustedPublisherServer::Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point >> const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
Definition: TrustedPublisherServer.h:714
ripple::test::DatabaseDownloader_test::testFailures
void testFailures()
Definition: DatabaseDownloader_test.cpp:179
mutex
std::stringstream::str
T str(T... args)
ripple::test::DatabaseDownloader_test::DownloadCompleter::dest
boost::filesystem::path dest
Definition: DatabaseDownloader_test.cpp:54
ripple::test::DatabaseDownloader_test::createServer
std::shared_ptr< TrustedPublisherServer > createServer(jtx::Env &env, bool ssl=true)
Definition: DatabaseDownloader_test.cpp:36
ripple::test::detail::FileDirGuard
Write a file in a directory and remove when done.
Definition: FileDirGuard.h:107
ripple::DatabaseDownloader
Definition: DatabaseDownloader.h:28
ripple::test::ManualTimeKeeper::now
time_point now() const override
Returns the estimate of wall time, in network time.
Definition: ManualTimeKeeper.cpp:37
std::unique_ptr
STL class.
ripple::make_DatabaseDownloader
std::shared_ptr< DatabaseDownloader > make_DatabaseDownloader(boost::asio::io_service &io_service, Config const &config, beast::Journal j)
Definition: DatabaseDownloader.cpp:25
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::test::DatabaseDownloader_test
Definition: DatabaseDownloader_test.cpp:33
ripple::test::DatabaseDownloader_test::DownloadCompleter::called
bool called
Definition: DatabaseDownloader_test.cpp:53
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)