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,
138 boost::asio::streambuf& sb,
std::string const& strHost) {
139 self->makeGet(strPath, sb, strHost);
155 boost::asio::ip::resolver_query_base::numeric_service);
162 catch (boost::system::system_error
const& e)
166 JLOG(
j_.trace()) <<
"expires_after: " <<
shutdown_.message();
168 self->handleDeadline(ec);
181 boost::system::error_code
const& ecResult,
182 boost::asio::ip::tcp::resolver::results_type results) {
183 self->handleResolve(ecResult, results);
194 if (ecResult == boost::asio::error::operation_aborted)
197 JLOG(
j_.trace()) <<
"Deadline cancelled.";
203 JLOG(
j_.trace()) <<
"Deadline error: " <<
deqSites_[0] <<
": " << ecResult.message();
210 JLOG(
j_.trace()) <<
"Deadline arrived.";
215 boost::system::errc::bad_address, boost::system::system_category()};
222 self->handleShutdown(ec);
232 JLOG(
j_.trace()) <<
"Shutdown error: " <<
deqSites_[0] <<
": " << ecResult.message();
238 boost::system::error_code
const& ecResult,
239 boost::asio::ip::tcp::resolver::results_type result)
258 JLOG(
j_.trace()) <<
"Resolve complete.";
260 boost::asio::async_connect(
264 boost::system::error_code
const& ecResult,
265 boost::asio::ip::tcp::endpoint
const&) { self->handleConnect(ecResult); });
277 JLOG(
j_.trace()) <<
"Connect error: " <<
shutdown_.message();
282 JLOG(
j_.trace()) <<
"Connected.";
290 JLOG(
j_.trace()) <<
"postConnectVerify: " <<
deqSites_[0] <<
": "
302 AutoSocket::ssl_socket::client,
304 self->handleRequest(ec);
321 JLOG(
j_.trace()) <<
"Handshake error:" <<
shutdown_.message();
327 JLOG(
j_.trace()) <<
"Session started.";
334 boost::system::error_code
const& ecResult,
std::size_t bytesTransferred) {
335 self->handleWrite(ecResult, bytesTransferred);
348 JLOG(
j_.trace()) <<
"Write error: " <<
shutdown_.message();
354 JLOG(
j_.trace()) <<
"Wrote.";
360 boost::system::error_code
const& ecResult,
std::size_t bytesTransferred) {
361 self->handleHeader(ecResult, bytesTransferred);
371 JLOG(
j_.trace()) <<
"Header: \"" << strHeader <<
"\"";
373 static boost::regex
const kReStatus{R
"(\`HTTP/1\S+ (\d{3}) .*\')"};
374 static boost::regex
const kReSize{
375 R
"(\`.*\r\nContent-Length:\s+([0-9]+).*\')", boost::regex::icase};
376 static boost::regex
const kReBody{R
"(\`.*\r\n\r\n(.*)\')"};
378 boost::smatch smMatch;
380 if (!boost::regex_match(strHeader, smMatch, kReStatus))
383 JLOG(
j_.trace()) <<
"No status code";
385 boost::system::error_code{
386 boost::system::errc::bad_address, boost::system::system_category()});
392 if (boost::regex_match(strHeader, smMatch, kReBody))
396 if (boost::regex_match(strHeader, smMatch, kReSize))
403 JLOG(
j_.trace()) <<
"Response field too large";
405 boost::system::error_code{
406 boost::system::errc::value_too_large, boost::system::system_category()});
410 if (responseSize == 0)
415 else if (
body_.size() >= responseSize)
424 boost::asio::transfer_all(),
426 boost::system::error_code
const& ecResult,
std::size_t bytesTransferred) {
427 self->handleData(ecResult, bytesTransferred);
440 JLOG(
j_.trace()) <<
"Read error: " <<
shutdown_.message();
448 JLOG(
j_.trace()) <<
"Complete.";
463 boost::system::error_code
const& ecResult,
467 boost::system::error_code ecCancel;
472 catch (boost::system::system_error
const& e)
474 JLOG(
j_.trace()) <<
"invokeComplete: Deadline cancel error: " << e.what();
478 JLOG(
j_.debug()) <<
"invokeComplete: Deadline popping: " <<
deqSites_.size();
512 boost::asio::ip::resolver_query_base::flags
flags;
525 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
528 boost::asio::basic_waitable_timer<std::chrono::steady_clock>
deadline_;
543 boost::asio::io_context& ioContext,
545 unsigned short const port,
550 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
555 client->get(bSSL, deqSites, strPath, timeout, complete);
561 boost::asio::io_context& ioContext,
563 unsigned short const port,
568 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
575 client->get(bSSL, deqSites, strPath, timeout, complete);
581 boost::asio::io_context& ioContext,
583 unsigned short const port,
588 bool(boost::system::error_code
const& ecResult,
int iStatus,
std::string const& strData)>
595 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)
constexpr Out lexicalCastThrow(In in)
Convert from one type to another, throw on error.
constexpr 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