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