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