1#ifndef XRPL_SERVER_DOOR_H_INCLUDED 
    2#define XRPL_SERVER_DOOR_H_INCLUDED 
    4#include <xrpl/basics/Log.h> 
    5#include <xrpl/basics/contract.h> 
    6#include <xrpl/server/detail/PlainHTTPPeer.h> 
    7#include <xrpl/server/detail/SSLHTTPPeer.h> 
    8#include <xrpl/server/detail/io_list.h> 
   10#include <boost/asio/basic_waitable_timer.hpp> 
   11#include <boost/asio/buffer.hpp> 
   12#include <boost/asio/io_context.hpp> 
   13#include <boost/asio/ip/tcp.hpp> 
   14#include <boost/asio/post.hpp> 
   15#include <boost/asio/spawn.hpp> 
   16#include <boost/asio/steady_timer.hpp> 
   17#include <boost/beast/core/detect_ssl.hpp> 
   18#include <boost/beast/core/multi_buffer.hpp> 
   19#include <boost/beast/core/tcp_stream.hpp> 
   20#include <boost/container/flat_map.hpp> 
   21#include <boost/predef.h> 
   24#include <sys/resource.h> 
   41template <
class Handler>
 
   47    using timer_type = boost::asio::basic_waitable_timer<clock_type>;
 
   63        boost::asio::io_context& 
ioc_;
 
   67        boost::asio::strand<boost::asio::io_context::executor_type> 
strand_;
 
   74            boost::asio::io_context& ioc,
 
 
   91    boost::asio::io_context& 
ioc_;
 
   93    boost::asio::strand<boost::asio::io_context::executor_type> 
strand_;
 
  120        boost::asio::io_context& io_context,
 
  144    template <
class ConstBufferSequence>
 
  148        ConstBufferSequence 
const& buffers,
 
 
  156template <
class Handler>
 
  160    boost::asio::io_context& ioc,
 
  167    , stream_(
std::move(stream))
 
  168    , socket_(stream_.socket())
 
  169    , remote_address_(remote_address)
 
  170    , strand_(
boost::asio::make_strand(ioc_))
 
 
  175template <
class Handler>
 
  184            std::placeholders::_1));
 
 
  187template <
class Handler>
 
  194template <
class Handler>
 
  198    boost::beast::multi_buffer buf(16);
 
  200    boost::system::error_code ec;
 
  201    bool const ssl = async_detect_ssl(stream_, buf, do_yield[ec]);
 
  202    stream_.expires_never();
 
  229    if (ec != boost::asio::error::operation_aborted)
 
  231        JLOG(
j_.
trace()) << 
"Error detecting ssl: " << ec.message() << 
" from " 
 
  238template <
class Handler>
 
  250            ss << 
"Can't close acceptor: " << 
port_.
name << 
", " 
  253            Throw<std::runtime_error>(ss.
str());
 
  259    acceptor_.open(local_address.protocol(), ec);
 
  263                         << 
"' failed:" << ec.message();
 
  264        Throw<std::exception>();
 
  268        boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
 
  272                         << 
"' failed:" << ec.message();
 
  273        Throw<std::exception>();
 
  280                         << 
"' failed:" << ec.message();
 
  281        Throw<std::exception>();
 
  284    acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
 
  288                         << 
"' failed:" << ec.message();
 
  289        Throw<std::exception>();
 
 
  295template <
class Handler>
 
  298    boost::asio::io_context& io_context,
 
 
  319template <
class Handler>
 
  327            this->shared_from_this(),
 
  328            std::placeholders::_1));
 
 
  331template <
class Handler>
 
  335    if (!strand_.running_in_this_thread())
 
  336        return boost::asio::post(
 
  339    backoff_timer_.cancel();
 
 
  346template <
class Handler>
 
  347template <
class ConstBufferSequence>
 
  351    ConstBufferSequence 
const& buffers,
 
 
  379template <
class Handler>
 
  383    while (acceptor_.is_open())
 
  385        if (should_throttle_for_fds())
 
  387            backoff_timer_.expires_after(accept_delay_);
 
  388            boost::system::error_code tec;
 
  389            backoff_timer_.async_wait(do_yield[tec]);
 
  390            accept_delay_ = 
std::min(accept_delay_ * 2, MAX_ACCEPT_DELAY);
 
  391            JLOG(j_.
warn()) << 
"Throttling do_accept for " 
  392                            << accept_delay_.count() << 
"ms.";
 
  400        acceptor_.async_accept(socket, remote_address, do_yield[ec]);
 
  403            if (ec == boost::asio::error::operation_aborted)
 
  406            if (ec == boost::asio::error::no_descriptors ||
 
  407                ec == boost::asio::error::no_buffer_space)
 
  409                JLOG(j_.
warn()) << 
"accept: Too many open files. Pausing for " 
  410                                << accept_delay_.count() << 
"ms.";
 
  412                backoff_timer_.expires_after(accept_delay_);
 
  413                boost::system::error_code tec;
 
  414                backoff_timer_.async_wait(do_yield[tec]);
 
  416                accept_delay_ = 
std::min(accept_delay_ * 2, MAX_ACCEPT_DELAY);
 
  420                JLOG(j_.
error()) << 
"accept error: " << ec.message();
 
  425        accept_delay_ = INITIAL_ACCEPT_DELAY;
 
  429            if (
auto sp = ios().
template emplace<Detector>(
 
  438        else if (ssl_ || plain_)
 
  442                boost::asio::null_buffers{},
 
 
  449template <
class Handler>
 
  458    if (getrlimit(RLIMIT_NOFILE, &rl) != 0 || rl.rlim_cur == RLIM_INFINITY)
 
  462    constexpr char const* kFdDir = 
"/proc/self/fd";
 
  464    constexpr char const* kFdDir = 
"/dev/fd";
 
  466    if (DIR* d = ::opendir(kFdDir))
 
  469        while (::readdir(d) != 
nullptr)
 
  473        s.
used = (cnt >= 3) ? (cnt - 3) : 0;
 
 
  480template <
class Handler>
 
  487    auto const stats = query_fd_stats();
 
  488    if (!stats || stats->limit == 0)
 
  491    auto const& s = *stats;
 
  492    auto const free = (s.limit > s.used) ? (s.limit - s.used) : 0ull;
 
  493    double const free_ratio =
 
  494        static_cast<double>(free) / 
static_cast<double>(s.limit);
 
  495    if (free_ratio < FREE_FD_THRESHOLD)
 
 
A generic endpoint for log messages.
 
Stream trace() const
Severity stream access functions.
 
boost::asio::strand< boost::asio::io_context::executor_type > strand_
 
endpoint_type remote_address_
 
void do_detect(yield_context yield)
 
boost::asio::io_context & ioc_
 
Detector(Port const &port, Handler &handler, boost::asio::io_context &ioc, stream_type &&stream, endpoint_type remote_address, beast::Journal j)
 
void do_accept(yield_context yield)
 
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remote_address)
 
static constexpr std::chrono::milliseconds INITIAL_ACCEPT_DELAY
 
protocol_type::endpoint endpoint_type
 
boost::asio::io_context & ioc_
 
Door(Handler &handler, boost::asio::io_context &io_context, Port const &port, beast::Journal j)
 
boost::beast::tcp_stream stream_type
 
boost::asio::basic_waitable_timer< clock_type > timer_type
 
static constexpr double FREE_FD_THRESHOLD
 
bool should_throttle_for_fds()
 
std::optional< FDStats > query_fd_stats() const
 
protocol_type::acceptor acceptor_type
 
boost::system::error_code error_code
 
boost::asio::ip::tcp protocol_type
 
boost::asio::strand< boost::asio::io_context::executor_type > strand_
 
boost::asio::steady_timer backoff_timer_
 
std::chrono::milliseconds accept_delay_
 
boost::asio::yield_context yield_context
 
void close() override
Close the Door listening socket and connections.
 
endpoint_type get_endpoint() const
 
boost::asio::ip::tcp::socket socket_type
 
static constexpr std::chrono::milliseconds MAX_ACCEPT_DELAY
 
io_list & ios()
Return the io_list associated with the work.
 
void spawn(Ctx &&ctx, F &&func)
Spawns a coroutine using boost::asio::spawn
 
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
 
boost::beast::ssl_stream< socket_type > stream_type
 
T shared_from_this(T... args)
 
Configuration information for a Server listening port.
 
boost::asio::ip::address ip