1#include <xrpl/net/HTTPClient.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/core/LexicalCast.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/net/AutoSocket.h>
7#include <xrpl/net/HTTPClientSSLContext.h>
9#include <boost/asio/basic_waitable_timer.hpp>
10#include <boost/asio/completion_condition.hpp>
11#include <boost/asio/connect.hpp>
12#include <boost/asio/error.hpp>
13#include <boost/asio/io_context.hpp>
14#include <boost/asio/ip/resolver_query_base.hpp>
15#include <boost/asio/ip/tcp.hpp>
16#include <boost/regex/v5/regex.hpp>
17#include <boost/regex/v5/regex_match.hpp>
18#include <boost/system/detail/errc.hpp>
19#include <boost/system/detail/error_code.hpp>
20#include <boost/system/detail/system_category.hpp>
21#include <boost/system/system_error.hpp>
64 boost::asio::io_context& ioContext,
65 unsigned short const port,
88 osRequest <<
"GET " << strPath
94 "Connection: close\r\n\r\n";
106 boost::system::error_code
const& ecResult,
127 boost::system::error_code
const& ecResult,
141 std::placeholders::_1,
142 std::placeholders::_2),
157 boost::asio::ip::resolver_query_base::numeric_service);
164 catch (boost::system::system_error
const& e)
168 JLOG(
j_.trace()) <<
"expires_after: " <<
shutdown_.message();
185 std::placeholders::_1,
186 std::placeholders::_2));
196 if (ecResult == boost::asio::error::operation_aborted)
199 JLOG(
j_.trace()) <<
"Deadline cancelled.";
205 JLOG(
j_.trace()) <<
"Deadline error: " <<
deqSites_[0] <<
": " << ecResult.message();
212 JLOG(
j_.trace()) <<
"Deadline arrived.";
217 boost::system::errc::bad_address, boost::system::system_category()};
234 JLOG(
j_.trace()) <<
"Shutdown error: " <<
deqSites_[0] <<
": " << ecResult.message();
240 boost::system::error_code
const& ecResult,
241 boost::asio::ip::tcp::resolver::results_type result)
260 JLOG(
j_.trace()) <<
"Resolve complete.";
262 boost::asio::async_connect(
278 JLOG(
j_.trace()) <<
"Connect error: " <<
shutdown_.message();
283 JLOG(
j_.trace()) <<
"Connected.";
291 JLOG(
j_.trace()) <<
"postConnectVerify: " <<
deqSites_[0] <<
": "
303 AutoSocket::ssl_socket::client,
321 JLOG(
j_.trace()) <<
"Handshake error:" <<
shutdown_.message();
327 JLOG(
j_.trace()) <<
"Session started.";
336 std::placeholders::_1,
337 std::placeholders::_2));
349 JLOG(
j_.trace()) <<
"Write error: " <<
shutdown_.message();
355 JLOG(
j_.trace()) <<
"Wrote.";
363 std::placeholders::_1,
364 std::placeholders::_2));
373 JLOG(
j_.trace()) <<
"Header: \"" << strHeader <<
"\"";
375 static boost::regex
const kReStatus{
"\\`HTTP/1\\S+ (\\d{3}) .*\\'"};
376 static boost::regex
const kReSize{
377 "\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'", boost::regex::icase};
378 static boost::regex
const kReBody{
"\\`.*\\r\\n\\r\\n(.*)\\'"};
380 boost::smatch smMatch;
382 if (!boost::regex_match(strHeader, smMatch, kReStatus))
385 JLOG(
j_.trace()) <<
"No status code";
387 boost::system::error_code{
388 boost::system::errc::bad_address, boost::system::system_category()});
394 if (boost::regex_match(strHeader, smMatch, kReBody))
398 if (boost::regex_match(strHeader, smMatch, kReSize))
405 JLOG(
j_.trace()) <<
"Response field too large";
407 boost::system::error_code{
408 boost::system::errc::value_too_large, boost::system::system_category()});
412 if (responseSize == 0)
417 else if (
body_.size() >= responseSize)
426 boost::asio::transfer_all(),
430 std::placeholders::_1,
431 std::placeholders::_2));
443 JLOG(
j_.trace()) <<
"Read error: " <<
shutdown_.message();
451 JLOG(
j_.trace()) <<
"Complete.";
466 boost::system::error_code
const& ecResult,
470 boost::system::error_code ecCancel;
475 catch (boost::system::system_error
const& e)
477 JLOG(
j_.trace()) <<
"invokeComplete: Deadline cancel error: " << e.what();
481 JLOG(
j_.debug()) <<
"invokeComplete: Deadline popping: " <<
deqSites_.size();
515 boost::asio::ip::resolver_query_base::flags
flags;
528 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
531 boost::asio::basic_waitable_timer<std::chrono::steady_clock>
deadline_;
546 boost::asio::io_context& ioContext,
548 unsigned short const port,
553 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
558 client->get(bSSL, deqSites, strPath, timeout, complete);
564 boost::asio::io_context& ioContext,
566 unsigned short const port,
571 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
578 client->get(bSSL, deqSites, strPath, timeout, complete);
584 boost::asio::io_context& ioContext,
586 unsigned short const port,
591 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
598 client->request(bSSL, deqSites, setRequest, timeout, complete);
A generic endpoint for log messages.
boost::asio::streambuf request_
void handleData(boost::system::error_code const &ecResult, std::size_t bytesTransferred)
void handleDeadline(boost::system::error_code const &ecResult)
unsigned short const port_
boost::asio::streambuf response_
std::size_t const maxResponseSize_
std::chrono::seconds timeout_
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(boost::system::error_code const &ecResult, int iStatus, std::string const &strData)> complete)
void handleConnect(boost::system::error_code const &ecResult)
void handleHeader(boost::system::error_code const &ecResult, std::size_t bytesTransferred)
boost::system::error_code shutdown_
HTTPClientImp(boost::asio::io_context &ioContext, unsigned short const port, std::size_t maxResponseSize, beast::Journal const &j)
boost::asio::basic_waitable_timer< std::chrono::steady_clock > deadline_
std::shared_ptr< Query > query_
void makeGet(std::string const &strPath, boost::asio::streambuf &sb, std::string const &strHost)
void handleResolve(boost::system::error_code const &ecResult, boost::asio::ip::tcp::resolver::results_type result)
void handleRequest(boost::system::error_code const &ecResult)
void get(bool bSSL, std::deque< std::string > deqSites, std::string const &strPath, std::chrono::seconds timeout, std::function< bool(boost::system::error_code const &ecResult, int iStatus, std::string const &strData)> complete)
boost::asio::ip::tcp::resolver resolver_
boost::asio::streambuf header_
std::deque< std::string > deqSites_
std::shared_ptr< HTTPClient > pointer
void invokeComplete(boost::system::error_code const &ecResult, int iStatus=0, std::string const &strData="")
void handleWrite(boost::system::error_code const &ecResult, std::size_t bytesTransferred)
std::function< void(boost::asio::streambuf &sb, std::string const &strHost)> build_
std::function< bool(boost::system::error_code const &ecResult, int iStatus, std::string const &strData)> complete_
void handleShutdown(boost::system::error_code const &ecResult)
static void initializeSSLContext(std::string const &sslVerifyDir, std::string const &sslVerifyFile, bool sslVerify, beast::Journal j)
static constexpr auto kMaxClientHeaderBytes
static void cleanupSSLContext()
Destroys the global SSL context created by initializeSSLContext().
static void request(bool bSSL, boost::asio::io_context &ioContext, std::string strSite, unsigned short const port, std::function< void(boost::asio::streambuf &sb, std::string const &strHost)> build, std::size_t responseMax, std::chrono::seconds timeout, std::function< bool(boost::system::error_code const &ecResult, int iStatus, std::string const &strData)> complete, beast::Journal const &j)
static void get(bool bSSL, boost::asio::io_context &ioContext, std::deque< std::string > deqSites, unsigned short const port, std::string const &strPath, std::size_t responseMax, std::chrono::seconds timeout, std::function< bool(boost::system::error_code const &ecResult, int iStatus, std::string const &strData)> complete, beast::Journal const &j)
Out lexicalCastThrow(In in)
Convert from one type to another, throw on error.
Out lexicalCast(In in, Out defaultValue=Out())
Convert from one type to another.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
static std::optional< HTTPClientSSLContext > gHttpClientSslContext
T shared_from_this(T... args)
boost::asio::ip::resolver_query_base::flags flags