rippled
HTTPClient.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/Log.h>
21 #include <ripple/basics/StringUtilities.h>
22 #include <ripple/basics/contract.h>
23 #include <ripple/beast/core/LexicalCast.h>
24 #include <ripple/net/AutoSocket.h>
25 #include <ripple/net/HTTPClient.h>
26 #include <ripple/net/HTTPClientSSLContext.h>
27 #include <boost/asio.hpp>
28 #include <boost/asio/ip/tcp.hpp>
29 #include <boost/asio/ssl.hpp>
30 #include <boost/regex.hpp>
31 #include <optional>
32 
33 namespace ripple {
34 
36 
37 void
39 {
40  httpClientSSLContext.emplace(config, j);
41 }
42 
43 //------------------------------------------------------------------------------
44 //
45 // Fetch a web page via http or https.
46 //
47 //------------------------------------------------------------------------------
48 
49 class HTTPClientImp : public std::enable_shared_from_this<HTTPClientImp>,
50  public HTTPClient
51 {
52 public:
54  boost::asio::io_service& io_service,
55  const unsigned short port,
56  std::size_t maxResponseSize,
57  beast::Journal& j)
58  : mSocket(io_service, httpClientSSLContext->context())
59  , mResolver(io_service)
61  , mPort(port)
62  , maxResponseSize_(maxResponseSize)
63  , mDeadline(io_service)
64  , j_(j)
65  {
66  }
67 
68  //--------------------------------------------------------------------------
69 
70  void
72  std::string const& strPath,
73  boost::asio::streambuf& sb,
74  std::string const& strHost)
75  {
76  std::ostream osRequest(&sb);
77 
78  osRequest << "GET " << strPath
79  << " HTTP/1.0\r\n"
80  "Host: "
81  << strHost
82  << "\r\n"
83  "Accept: */*\r\n" // YYY Do we need this line?
84  "Connection: close\r\n\r\n";
85  }
86 
87  //--------------------------------------------------------------------------
88 
89  void
91  bool bSSL,
92  std::deque<std::string> deqSites,
94  void(boost::asio::streambuf& sb, std::string const& strHost)> build,
95  std::chrono::seconds timeout,
96  std::function<bool(
97  const boost::system::error_code& ecResult,
98  int iStatus,
99  std::string const& strData)> complete)
100  {
101  mSSL = bSSL;
102  mDeqSites = deqSites;
103  mBuild = build;
105  mTimeout = timeout;
106 
107  httpsNext();
108  }
109 
110  //--------------------------------------------------------------------------
111 
112  void
113  get(bool bSSL,
114  std::deque<std::string> deqSites,
115  std::string const& strPath,
116  std::chrono::seconds timeout,
117  std::function<bool(
118  const boost::system::error_code& ecResult,
119  int iStatus,
120  std::string const& strData)> complete)
121  {
123  mTimeout = timeout;
124 
125  request(
126  bSSL,
127  deqSites,
128  std::bind(
131  strPath,
132  std::placeholders::_1,
133  std::placeholders::_2),
134  timeout,
135  complete);
136  }
137 
138  //--------------------------------------------------------------------------
139 
140  void
142  {
143  JLOG(j_.trace()) << "Fetch: " << mDeqSites[0];
144 
145  auto query = std::make_shared<boost::asio::ip::tcp::resolver::query>(
146  mDeqSites[0],
148  boost::asio::ip::resolver_query_base::numeric_service);
149  mQuery = query;
150 
151  mDeadline.expires_from_now(mTimeout, mShutdown);
152 
153  JLOG(j_.trace()) << "expires_from_now: " << mShutdown.message();
154 
155  if (!mShutdown)
156  {
157  mDeadline.async_wait(std::bind(
160  std::placeholders::_1));
161  }
162 
163  if (!mShutdown)
164  {
165  JLOG(j_.trace()) << "Resolving: " << mDeqSites[0];
166 
167  mResolver.async_resolve(
168  *mQuery,
169  std::bind(
172  std::placeholders::_1,
173  std::placeholders::_2));
174  }
175 
176  if (mShutdown)
178  }
179 
180  void
181  handleDeadline(const boost::system::error_code& ecResult)
182  {
183  if (ecResult == boost::asio::error::operation_aborted)
184  {
185  // Timer canceled because deadline no longer needed.
186  JLOG(j_.trace()) << "Deadline cancelled.";
187 
188  // Aborter is done.
189  }
190  else if (ecResult)
191  {
192  JLOG(j_.trace()) << "Deadline error: " << mDeqSites[0] << ": "
193  << ecResult.message();
194 
195  // Can't do anything sound.
196  abort();
197  }
198  else
199  {
200  JLOG(j_.trace()) << "Deadline arrived.";
201 
202  // Mark us as shutting down.
203  // XXX Use our own error code.
204  mShutdown = boost::system::error_code{
205  boost::system::errc::bad_address,
206  boost::system::system_category()};
207 
208  // Cancel any resolving.
209  mResolver.cancel();
210 
211  // Stop the transaction.
215  std::placeholders::_1));
216  }
217  }
218 
219  void
220  handleShutdown(const boost::system::error_code& ecResult)
221  {
222  if (ecResult)
223  {
224  JLOG(j_.trace()) << "Shutdown error: " << mDeqSites[0] << ": "
225  << ecResult.message();
226  }
227  }
228 
229  void
231  const boost::system::error_code& ecResult,
232  boost::asio::ip::tcp::resolver::iterator itrEndpoint)
233  {
234  if (!mShutdown)
235  {
236  mShutdown = ecResult ? ecResult
237  : httpClientSSLContext->preConnectVerify(
238  mSocket.SSLSocket(), mDeqSites[0]);
239  }
240 
241  if (mShutdown)
242  {
243  JLOG(j_.trace()) << "Resolve error: " << mDeqSites[0] << ": "
244  << mShutdown.message();
245 
247  }
248  else
249  {
250  JLOG(j_.trace()) << "Resolve complete.";
251 
252  boost::asio::async_connect(
254  itrEndpoint,
255  std::bind(
258  std::placeholders::_1));
259  }
260  }
261 
262  void
263  handleConnect(const boost::system::error_code& ecResult)
264  {
265  if (!mShutdown)
266  mShutdown = ecResult;
267 
268  if (mShutdown)
269  {
270  JLOG(j_.trace()) << "Connect error: " << mShutdown.message();
271  }
272 
273  if (!mShutdown)
274  {
275  JLOG(j_.trace()) << "Connected.";
276 
277  mShutdown = httpClientSSLContext->postConnectVerify(
278  mSocket.SSLSocket(), mDeqSites[0]);
279 
280  if (mShutdown)
281  {
282  JLOG(j_.trace()) << "postConnectVerify: " << mDeqSites[0]
283  << ": " << mShutdown.message();
284  }
285  }
286 
287  if (mShutdown)
288  {
290  }
291  else if (mSSL)
292  {
294  AutoSocket::ssl_socket::client,
295  std::bind(
298  std::placeholders::_1));
299  }
300  else
301  {
302  handleRequest(ecResult);
303  }
304  }
305 
306  void
307  handleRequest(const boost::system::error_code& ecResult)
308  {
309  if (!mShutdown)
310  mShutdown = ecResult;
311 
312  if (mShutdown)
313  {
314  JLOG(j_.trace()) << "Handshake error:" << mShutdown.message();
315 
317  }
318  else
319  {
320  JLOG(j_.trace()) << "Session started.";
321 
323 
325  mRequest,
326  std::bind(
329  std::placeholders::_1,
330  std::placeholders::_2));
331  }
332  }
333 
334  void
336  const boost::system::error_code& ecResult,
337  std::size_t bytes_transferred)
338  {
339  if (!mShutdown)
340  mShutdown = ecResult;
341 
342  if (mShutdown)
343  {
344  JLOG(j_.trace()) << "Write error: " << mShutdown.message();
345 
347  }
348  else
349  {
350  JLOG(j_.trace()) << "Wrote.";
351 
353  mHeader,
354  "\r\n\r\n",
355  std::bind(
358  std::placeholders::_1,
359  std::placeholders::_2));
360  }
361  }
362 
363  void
365  const boost::system::error_code& ecResult,
366  std::size_t bytes_transferred)
367  {
368  std::string strHeader{
371  JLOG(j_.trace()) << "Header: \"" << strHeader << "\"";
372 
373  static boost::regex reStatus{
374  "\\`HTTP/1\\S+ (\\d{3}) .*\\'"}; // HTTP/1.1 200 OK
375  static boost::regex reSize{
376  "\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'"};
377  static boost::regex reBody{"\\`.*\\r\\n\\r\\n(.*)\\'"};
378 
379  boost::smatch smMatch;
380  // Match status code.
381  if (!boost::regex_match(strHeader, smMatch, reStatus))
382  {
383  // XXX Use our own error code.
384  JLOG(j_.trace()) << "No status code";
385  invokeComplete(boost::system::error_code{
386  boost::system::errc::bad_address,
387  boost::system::system_category()});
388  return;
389  }
390 
391  mStatus = beast::lexicalCastThrow<int>(std::string(smMatch[1]));
392 
393  if (boost::regex_match(strHeader, smMatch, reBody)) // we got some body
394  mBody = smMatch[1];
395 
396  std::size_t const responseSize = [&] {
397  if (boost::regex_match(strHeader, smMatch, reSize))
398  return beast::lexicalCast<std::size_t>(
399  std::string(smMatch[1]), maxResponseSize_);
400  return maxResponseSize_;
401  }();
402 
403  if (responseSize > maxResponseSize_)
404  {
405  JLOG(j_.trace()) << "Response field too large";
406  invokeComplete(boost::system::error_code{
407  boost::system::errc::value_too_large,
408  boost::system::system_category()});
409  return;
410  }
411 
412  if (responseSize == 0)
413  {
414  // no body wanted or available
415  invokeComplete(ecResult, mStatus);
416  }
417  else if (mBody.size() >= responseSize)
418  {
419  // we got the whole thing
420  invokeComplete(ecResult, mStatus, mBody);
421  }
422  else
423  {
425  mResponse.prepare(responseSize - mBody.size()),
426  boost::asio::transfer_all(),
427  std::bind(
430  std::placeholders::_1,
431  std::placeholders::_2));
432  }
433  }
434 
435  void
437  const boost::system::error_code& ecResult,
438  std::size_t bytes_transferred)
439  {
440  if (!mShutdown)
441  mShutdown = ecResult;
442 
443  if (mShutdown && mShutdown != boost::asio::error::eof)
444  {
445  JLOG(j_.trace()) << "Read error: " << mShutdown.message();
446 
448  }
449  else
450  {
451  if (mShutdown)
452  {
453  JLOG(j_.trace()) << "Complete.";
454  }
455  else
456  {
457  mResponse.commit(bytes_transferred);
458  std::string strBody{
461  invokeComplete(ecResult, mStatus, mBody + strBody);
462  }
463  }
464  }
465 
466  // Call cancel the deadline timer and invoke the completion routine.
467  void
469  const boost::system::error_code& ecResult,
470  int iStatus = 0,
471  std::string const& strData = "")
472  {
473  boost::system::error_code ecCancel;
474 
475  (void)mDeadline.cancel(ecCancel);
476 
477  if (ecCancel)
478  {
479  JLOG(j_.trace()) << "invokeComplete: Deadline cancel error: "
480  << ecCancel.message();
481  }
482 
483  JLOG(j_.debug()) << "invokeComplete: Deadline popping: "
484  << mDeqSites.size();
485 
486  if (!mDeqSites.empty())
487  {
489  }
490 
491  bool bAgain = true;
492 
493  if (mDeqSites.empty() || !ecResult)
494  {
495  // ecResult: !0 = had an error, last entry
496  // iStatus: result, if no error
497  // strData: data, if no error
498  bAgain = mComplete &&
499  mComplete(ecResult ? ecResult : ecCancel, iStatus, strData);
500  }
501 
502  if (!mDeqSites.empty() && bAgain)
503  {
504  httpsNext();
505  }
506  }
507 
508 private:
510 
511  bool mSSL;
513  boost::asio::ip::tcp::resolver mResolver;
515  boost::asio::streambuf mRequest;
516  boost::asio::streambuf mHeader;
517  boost::asio::streambuf mResponse;
519  const unsigned short mPort;
521  int mStatus;
522  std::function<void(boost::asio::streambuf& sb, std::string const& strHost)>
524  std::function<bool(
525  const boost::system::error_code& ecResult,
526  int iStatus,
527  std::string const& strData)>
529 
530  boost::asio::basic_waitable_timer<std::chrono::steady_clock> mDeadline;
531 
532  // If not success, we are shutting down.
533  boost::system::error_code mShutdown;
534 
538 };
539 
540 //------------------------------------------------------------------------------
541 
542 void
544  bool bSSL,
545  boost::asio::io_service& io_service,
546  std::deque<std::string> deqSites,
547  const unsigned short port,
548  std::string const& strPath,
549  std::size_t responseMax,
550  std::chrono::seconds timeout,
551  std::function<bool(
552  const boost::system::error_code& ecResult,
553  int iStatus,
554  std::string const& strData)> complete,
555  beast::Journal& j)
556 {
557  auto client =
558  std::make_shared<HTTPClientImp>(io_service, port, responseMax, j);
559  client->get(bSSL, deqSites, strPath, timeout, complete);
560 }
561 
562 void
564  bool bSSL,
565  boost::asio::io_service& io_service,
566  std::string strSite,
567  const unsigned short port,
568  std::string const& strPath,
569  std::size_t responseMax,
570  std::chrono::seconds timeout,
571  std::function<bool(
572  const boost::system::error_code& ecResult,
573  int iStatus,
574  std::string const& strData)> complete,
575  beast::Journal& j)
576 {
577  std::deque<std::string> deqSites(1, strSite);
578 
579  auto client =
580  std::make_shared<HTTPClientImp>(io_service, port, responseMax, j);
581  client->get(bSSL, deqSites, strPath, timeout, complete);
582 }
583 
584 void
586  bool bSSL,
587  boost::asio::io_service& io_service,
588  std::string strSite,
589  const unsigned short port,
590  std::function<void(boost::asio::streambuf& sb, std::string const& strHost)>
591  setRequest,
592  std::size_t responseMax,
593  std::chrono::seconds timeout,
594  std::function<bool(
595  const boost::system::error_code& ecResult,
596  int iStatus,
597  std::string const& strData)> complete,
598  beast::Journal& j)
599 {
600  std::deque<std::string> deqSites(1, strSite);
601 
602  auto client =
603  std::make_shared<HTTPClientImp>(io_service, port, responseMax, j);
604  client->request(bSSL, deqSites, setRequest, timeout, complete);
605 }
606 
607 } // namespace ripple
ripple::HTTPClientImp::mSocket
AutoSocket mSocket
Definition: HTTPClient.cpp:512
AutoSocket::lowest_layer
lowest_layer_type & lowest_layer()
Definition: AutoSocket.h:94
ripple::HTTPClientImp::mPort
const unsigned short mPort
Definition: HTTPClient.cpp:519
std::bind
T bind(T... args)
ripple::ShardState::complete
@ complete
std::string
STL class.
std::shared_ptr
STL class.
ripple::HTTPClientImp::HTTPClientImp
HTTPClientImp(boost::asio::io_service &io_service, const unsigned short port, std::size_t maxResponseSize, beast::Journal &j)
Definition: HTTPClient.cpp:53
ripple::HTTPClientImp::makeGet
void makeGet(std::string const &strPath, boost::asio::streambuf &sb, std::string const &strHost)
Definition: HTTPClient.cpp:71
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::HTTPClientImp::handleConnect
void handleConnect(const boost::system::error_code &ecResult)
Definition: HTTPClient.cpp:263
std::deque::pop_front
T pop_front(T... args)
std::string::size
T size(T... args)
ripple::HTTPClient::get
static void get(bool bSSL, boost::asio::io_service &io_service, std::deque< std::string > deqSites, const unsigned short port, std::string const &strPath, std::size_t responseMax, std::chrono::seconds timeout, std::function< bool(const boost::system::error_code &ecResult, int iStatus, std::string const &strData)> complete, beast::Journal &j)
Definition: HTTPClient.cpp:543
std::chrono::seconds
ripple::HTTPClientImp::handleResolve
void handleResolve(const boost::system::error_code &ecResult, boost::asio::ip::tcp::resolver::iterator itrEndpoint)
Definition: HTTPClient.cpp:230
ripple::HTTPClientImp::get
void get(bool bSSL, std::deque< std::string > deqSites, std::string const &strPath, std::chrono::seconds timeout, std::function< bool(const boost::system::error_code &ecResult, int iStatus, std::string const &strData)> complete)
Definition: HTTPClient.cpp:113
std::function
ripple::HTTPClientImp::request
void request(bool bSSL, std::deque< std::string > deqSites, std::function< void(boost::asio::streambuf &sb, std::string const &strHost)> build, std::chrono::seconds timeout, std::function< bool(const boost::system::error_code &ecResult, int iStatus, std::string const &strData)> complete)
Definition: HTTPClient.cpp:90
AutoSocket::async_read
void async_read(const Buf &buffers, Condition cond, Handler handler)
Definition: AutoSocket.h:242
ripple::HTTPClientImp::mDeadline
boost::asio::basic_waitable_timer< std::chrono::steady_clock > mDeadline
Definition: HTTPClient.cpp:530
ripple::HTTPClientImp::mSSL
bool mSSL
Definition: HTTPClient.cpp:511
ripple::HTTPClientImp::mRequest
boost::asio::streambuf mRequest
Definition: HTTPClient.cpp:515
std::enable_shared_from_this< HTTPClientImp >::shared_from_this
T shared_from_this(T... args)
ripple::HTTPClientImp::mStatus
int mStatus
Definition: HTTPClient.cpp:521
AutoSocket::async_write
void async_write(const Buf &buffers, Handler handler)
Definition: AutoSocket.h:220
ripple::httpClientSSLContext
static std::optional< HTTPClientSSLContext > httpClientSSLContext
Definition: HTTPClient.cpp:35
ripple::HTTPClientImp::mResponse
boost::asio::streambuf mResponse
Definition: HTTPClient.cpp:517
ripple::Config
Definition: Config.h:89
ripple::HTTPClientImp::mBuild
std::function< void(boost::asio::streambuf &sb, std::string const &strHost)> mBuild
Definition: HTTPClient.cpp:523
std::ostream
STL class.
ripple::HTTPClientImp::handleHeader
void handleHeader(const boost::system::error_code &ecResult, std::size_t bytes_transferred)
Definition: HTTPClient.cpp:364
AutoSocket::SSLSocket
ssl_socket & SSLSocket()
Definition: AutoSocket.h:71
ripple::HTTPClientImp::mTimeout
std::chrono::seconds mTimeout
Definition: HTTPClient.cpp:536
ripple::HTTPClient::maxClientHeaderBytes
static constexpr auto maxClientHeaderBytes
Definition: HTTPClient.h:38
ripple::HTTPClientImp::j_
beast::Journal j_
Definition: HTTPClient.cpp:537
std::to_string
T to_string(T... args)
ripple::HTTPClientImp
Definition: HTTPClient.cpp:49
std::enable_shared_from_this
ripple::HTTPClientImp::maxResponseSize_
const std::size_t maxResponseSize_
Definition: HTTPClient.cpp:520
std::deque< std::string >
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::HTTPClientImp::mShutdown
boost::system::error_code mShutdown
Definition: HTTPClient.cpp:533
ripple::HTTPClientImp::invokeComplete
void invokeComplete(const boost::system::error_code &ecResult, int iStatus=0, std::string const &strData="")
Definition: HTTPClient.cpp:468
std::istreambuf_iterator
ripple::HTTPClientImp::mHeader
boost::asio::streambuf mHeader
Definition: HTTPClient.cpp:516
ripple::HTTPClientImp::mComplete
std::function< bool(const boost::system::error_code &ecResult, int iStatus, std::string const &strData)> mComplete
Definition: HTTPClient.cpp:528
ripple::HTTPClientImp::handleData
void handleData(const boost::system::error_code &ecResult, std::size_t bytes_transferred)
Definition: HTTPClient.cpp:436
ripple::HTTPClientImp::httpsNext
void httpsNext()
Definition: HTTPClient.cpp:141
ripple::HTTPClientImp::mResolver
boost::asio::ip::tcp::resolver mResolver
Definition: HTTPClient.cpp:513
AutoSocket::async_handshake
void async_handshake(handshake_type type, callback cbFunc)
Definition: AutoSocket.h:114
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
AutoSocket::async_read_until
void async_read_until(const Seq &buffers, Condition condition, Handler handler)
Definition: AutoSocket.h:180
ripple::HTTPClientImp::handleWrite
void handleWrite(const boost::system::error_code &ecResult, std::size_t bytes_transferred)
Definition: HTTPClient.cpp:335
ripple::HTTPClientImp::handleShutdown
void handleShutdown(const boost::system::error_code &ecResult)
Definition: HTTPClient.cpp:220
ripple::HTTPClientImp::mBody
std::string mBody
Definition: HTTPClient.cpp:518
ripple::HTTPClientImp::handleRequest
void handleRequest(const boost::system::error_code &ecResult)
Definition: HTTPClient.cpp:307
std::deque::empty
T empty(T... args)
ripple::HTTPClient::request
static void request(bool bSSL, boost::asio::io_service &io_service, std::string strSite, const unsigned short port, std::function< void(boost::asio::streambuf &sb, std::string const &strHost)> build, std::size_t responseMax, std::chrono::seconds timeout, std::function< bool(const boost::system::error_code &ecResult, int iStatus, std::string const &strData)> complete, beast::Journal &j)
Definition: HTTPClient.cpp:585
optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::HTTPClient
Provides an asynchronous HTTP client implementation with optional SSL.
Definition: HTTPClient.h:33
AutoSocket
Definition: AutoSocket.h:35
AutoSocket::async_shutdown
void async_shutdown(ShutdownHandler handler)
Definition: AutoSocket.h:147
ripple::HTTPClientImp::mQuery
std::shared_ptr< boost::asio::ip::tcp::resolver::query > mQuery
Definition: HTTPClient.cpp:514
ripple::HTTPClientImp::handleDeadline
void handleDeadline(const boost::system::error_code &ecResult)
Definition: HTTPClient.cpp:181
ripple::HTTPClient::initializeSSLContext
static void initializeSSLContext(Config const &config, beast::Journal j)
Definition: HTTPClient.cpp:38
ripple::HTTPClientImp::mDeqSites
std::deque< std::string > mDeqSites
Definition: HTTPClient.cpp:535