rippled
SSLHTTPDownloader_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 <boost/predef.h>
23 #include <condition_variable>
24 #include <mutex>
25 #include <test/jtx.h>
26 #include <test/jtx/TrustedPublisherServer.h>
27 #include <test/unit_test/FileDirGuard.h>
28 
29 namespace ripple {
30 namespace test {
31 
32 class SSLHTTPDownloader_test : public beast::unit_test::suite
33 {
35  createServer(jtx::Env& env, bool ssl = true)
36  {
40  env.app().getIOService(),
41  list,
42  env.timeKeeper().now() + std::chrono::seconds{3600},
43  ssl};
44  }
45 
47  {
50  bool called = false;
51  boost::filesystem::path dest;
52 
53  void
54  operator()(boost::filesystem::path dst)
55  {
57  called = true;
58  dest = std::move(dst);
59  cv.notify_one();
60  };
61 
62  bool
64  {
66  using namespace std::chrono_literals;
67 #if BOOST_OS_WINDOWS
68  auto constexpr timeout = 4s;
69 #else
70  auto constexpr timeout = 2s;
71 #endif
72  auto stat = cv.wait_for(lk, timeout, [this] { return called; });
73  called = false;
74  return stat;
75  };
76  };
78 
79  struct Downloader
80  {
83  // The SSLHTTPDownloader must be created as shared_ptr
84  // because it uses shared_from_this
86 
88  : journal_{sink_}
89  , ptr_{std::make_shared<DatabaseDownloader>(
90  env.app().getIOService(),
91  journal_,
92  env.app().config())}
93  {
94  }
95 
98  {
99  return ptr_.get();
100  }
101  };
102 
103  void
105  {
106  testcase << std::string("Basic download - SSL ") +
107  (verify ? "Verify" : "No Verify");
108 
109  using namespace jtx;
110 
112  *this, "_cert", "ca.pem", TrustedPublisherServer::ca_cert()};
113 
114  Env env{*this, envconfig([&cert, &verify](std::unique_ptr<Config> cfg) {
115  if ((cfg->SSL_VERIFY = verify)) // yes, this is assignment
116  cfg->SSL_VERIFY_FILE = cert.file().string();
117  return cfg;
118  })};
119 
120  Downloader downloader{env};
121 
122  // create a TrustedPublisherServer as a simple HTTP
123  // server to request from. Use the /textfile endpoint
124  // to get a simple text file sent as response.
125  auto server = createServer(env);
126 
128  *this, "downloads", "data", "", false, false};
129  // initiate the download and wait for the callback
130  // to be invoked
131  auto stat = downloader->download(
132  server.local_endpoint().address().to_string(),
133  std::to_string(server.local_endpoint().port()),
134  "/textfile",
135  11,
136  data.file(),
137  std::function<void(boost::filesystem::path)>{std::ref(cb)});
138  if (!BEAST_EXPECT(stat))
139  {
140  log << "Failed. LOGS:\n" + downloader.sink_.messages().str();
141  return;
142  }
143  if (!BEAST_EXPECT(cb.waitComplete()))
144  {
145  log << "Failed. LOGS:\n" + downloader.sink_.messages().str();
146  return;
147  }
148  BEAST_EXPECT(cb.dest == data.file());
149  if (!BEAST_EXPECT(boost::filesystem::exists(data.file())))
150  return;
151  BEAST_EXPECT(boost::filesystem::file_size(data.file()) > 0);
152  }
153 
154  void
156  {
157  testcase("Error conditions");
158 
159  using namespace jtx;
160 
161  Env env{*this};
162 
163  {
164  // bad hostname
165  boost::system::error_code ec;
166  boost::asio::ip::tcp::resolver resolver{env.app().getIOService()};
167  auto const results = resolver.resolve("badhostname", "443", ec);
168  // we require an error in resolving this name in order
169  // for this test to pass. Some networks might have DNS hijacking
170  // that prevent NXDOMAIN, in which case the failure is not
171  // possible, so we skip the test.
172  if (ec)
173  {
174  Downloader dl{env};
175  ripple::test::detail::FileDirGuard const datafile{
176  *this, "downloads", "data", "", false, false};
177  BEAST_EXPECT(dl->download(
178  "badhostname",
179  "443",
180  "",
181  11,
182  datafile.file(),
183  std::function<void(boost::filesystem::path)>{
184  std::ref(cb)}));
185  BEAST_EXPECT(cb.waitComplete());
186  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
187  BEAST_EXPECTS(
188  dl.sink_.messages().str().find("async_resolve") !=
189  std::string::npos,
190  dl.sink_.messages().str());
191  }
192  }
193  {
194  // can't connect
195  Downloader dl{env};
196  ripple::test::detail::FileDirGuard const datafile{
197  *this, "downloads", "data", "", false, false};
198  auto server = createServer(env);
199  auto host = server.local_endpoint().address().to_string();
200  auto port = std::to_string(server.local_endpoint().port());
201  server.stop();
202  BEAST_EXPECT(dl->download(
203  host,
204  port,
205  "",
206  11,
207  datafile.file(),
208  std::function<void(boost::filesystem::path)>{std::ref(cb)}));
209  BEAST_EXPECT(cb.waitComplete());
210  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
211  BEAST_EXPECTS(
212  dl.sink_.messages().str().find("async_connect") !=
213  std::string::npos,
214  dl.sink_.messages().str());
215  }
216  {
217  // not ssl (failed handlshake)
218  Downloader dl{env};
219  ripple::test::detail::FileDirGuard const datafile{
220  *this, "downloads", "data", "", false, false};
221  auto server = createServer(env, false);
222  BEAST_EXPECT(dl->download(
223  server.local_endpoint().address().to_string(),
224  std::to_string(server.local_endpoint().port()),
225  "",
226  11,
227  datafile.file(),
228  std::function<void(boost::filesystem::path)>{std::ref(cb)}));
229  BEAST_EXPECT(cb.waitComplete());
230  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
231  BEAST_EXPECTS(
232  dl.sink_.messages().str().find("async_handshake") !=
233  std::string::npos,
234  dl.sink_.messages().str());
235  }
236  {
237  // huge file (content length)
238  Downloader dl{env};
239  ripple::test::detail::FileDirGuard const datafile{
240  *this, "downloads", "data", "", false, false};
241  auto server = createServer(env);
242  BEAST_EXPECT(dl->download(
243  server.local_endpoint().address().to_string(),
244  std::to_string(server.local_endpoint().port()),
245  "/textfile/huge",
246  11,
247  datafile.file(),
248  std::function<void(boost::filesystem::path)>{std::ref(cb)}));
249  BEAST_EXPECT(cb.waitComplete());
250  BEAST_EXPECT(!boost::filesystem::exists(datafile.file()));
251  BEAST_EXPECTS(
252  dl.sink_.messages().str().find("Insufficient disk space") !=
253  std::string::npos,
254  dl.sink_.messages().str());
255  }
256  }
257 
258 public:
259  void
260  run() override
261  {
262  testDownload(true);
263  testDownload(false);
264  testFailures();
265  }
266 };
267 
269 } // namespace test
270 } // namespace ripple
ripple::test::SSLHTTPDownloader_test
Definition: SSLHTTPDownloader_test.cpp:32
ripple::test::SSLHTTPDownloader_test::DownloadCompleter::m
std::mutex m
Definition: SSLHTTPDownloader_test.cpp:48
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountDelete, app, ripple)
std::string
STL class.
std::shared_ptr
STL class.
ripple::test::SSLHTTPDownloader_test::DownloadCompleter::called
bool called
Definition: SSLHTTPDownloader_test.cpp:50
ripple::test::SSLHTTPDownloader_test::testDownload
void testDownload(bool verify)
Definition: SSLHTTPDownloader_test.cpp:104
std::vector
STL class.
std::chrono::seconds
ripple::test::SSLHTTPDownloader_test::DownloadCompleter::waitComplete
bool waitComplete()
Definition: SSLHTTPDownloader_test.cpp:63
std::shared_ptr::get
T get(T... args)
ripple::test::jtx::Env::timeKeeper
ManualTimeKeeper & timeKeeper()
Definition: Env.h:250
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:238
std::function
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::test::SSLHTTPDownloader_test::Downloader::journal_
beast::Journal journal_
Definition: SSLHTTPDownloader_test.cpp:82
ripple::test::TrustedPublisherServer::ca_cert
static std::string const & ca_cert()
Definition: TrustedPublisherServer.h:320
ripple::test::SSLHTTPDownloader_test::Downloader::Downloader
Downloader(jtx::Env &env)
Definition: SSLHTTPDownloader_test.cpp:87
std::vector::push_back
T push_back(T... args)
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical)
Verify a signature on a message.
Definition: PublicKey.cpp:268
ripple::test::SSLHTTPDownloader_test::Downloader::sink_
test::StreamSink sink_
Definition: SSLHTTPDownloader_test.cpp:81
ripple::test::SSLHTTPDownloader_test::DownloadCompleter::dest
boost::filesystem::path dest
Definition: SSLHTTPDownloader_test.cpp:51
std::unique_lock
STL class.
std::to_string
T to_string(T... args)
ripple::test::SSLHTTPDownloader_test::cb
DownloadCompleter cb
Definition: SSLHTTPDownloader_test.cpp:77
ripple::test::TrustedPublisherServer
Definition: TrustedPublisherServer.h:42
ripple::test::SSLHTTPDownloader_test::Downloader
Definition: SSLHTTPDownloader_test.cpp:79
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::test::TrustedPublisherServer::randomValidator
static Validator randomValidator()
Definition: TrustedPublisherServer.h:128
std::condition_variable::wait_for
T wait_for(T... args)
ripple::test::SSLHTTPDownloader_test::run
void run() override
Definition: SSLHTTPDownloader_test.cpp:260
ripple::Application::getIOService
virtual boost::asio::io_service & getIOService()=0
ripple::test::StreamSink
Definition: SuiteJournal.h:110
std::condition_variable::notify_one
T notify_one(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::SSLHTTPDownloader_test::DownloadCompleter::cv
std::condition_variable cv
Definition: SSLHTTPDownloader_test.cpp:49
ripple::test::SSLHTTPDownloader_test::testFailures
void testFailures()
Definition: SSLHTTPDownloader_test.cpp:155
condition_variable
ripple::test::SSLHTTPDownloader_test::DownloadCompleter::operator()
void operator()(boost::filesystem::path dst)
Definition: SSLHTTPDownloader_test.cpp:54
ripple::test::SSLHTTPDownloader_test::Downloader::operator->
DatabaseDownloader * operator->()
Definition: SSLHTTPDownloader_test.cpp:97
mutex
ripple::test::detail::FileDirGuard
Write a file in a directory and remove when done.
Definition: FileDirGuard.h:110
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
ripple::test::SSLHTTPDownloader_test::Downloader::ptr_
std::shared_ptr< DatabaseDownloader > ptr_
Definition: SSLHTTPDownloader_test.cpp:85
std::unique_ptr
STL class.
ripple::SSLHTTPDownloader
Provides an asynchronous HTTPS file downloader.
Definition: SSLHTTPDownloader.h:44
ripple::test::SSLHTTPDownloader_test::createServer
TrustedPublisherServer createServer(jtx::Env &env, bool ssl=true)
Definition: SSLHTTPDownloader_test.cpp:35
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:114
ripple::test::SSLHTTPDownloader_test::DownloadCompleter
Definition: SSLHTTPDownloader_test.cpp:46