3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/server/Port.h>
7#include <xrpl/server/detail/PlainHTTPPeer.h>
8#include <xrpl/server/detail/SSLHTTPPeer.h>
9#include <xrpl/server/detail/io_list.h>
11#include <boost/asio/basic_waitable_timer.hpp>
12#include <boost/asio/buffer.hpp>
13#include <boost/asio/io_context.hpp>
14#include <boost/asio/ip/tcp.hpp>
15#include <boost/asio/post.hpp>
16#include <boost/asio/spawn.hpp>
17#include <boost/asio/steady_timer.hpp>
18#include <boost/beast/core/detect_ssl.hpp>
19#include <boost/beast/core/multi_buffer.hpp>
20#include <boost/beast/core/tcp_stream.hpp>
21#include <boost/container/flat_map.hpp>
22#include <boost/predef.h>
28#include <sys/resource.h>
46template <
class Handler>
51 using timer_type = boost::asio::basic_waitable_timer<clock_type>;
66 boost::asio::io_context&
ioc_;
70 boost::asio::strand<boost::asio::io_context::executor_type>
strand_;
77 boost::asio::io_context& ioc,
94 boost::asio::io_context&
ioc_;
96 boost::asio::strand<boost::asio::io_context::executor_type>
strand_;
98 port_.protocol.contains(
"https") ||
port_.protocol.contains(
"wss") ||
99 port_.protocol.contains(
"wss2") ||
port_.protocol.contains(
"peer")};
101 port_.protocol.contains(
"http") ||
port_.protocol.contains(
"ws") ||
102 (
port_.protocol.contains(
"ws2"))};
151 template <
class ConstBufferSequence>
155 ConstBufferSequence
const& buffers,
163template <
class Handler>
167 boost::asio::io_context& ioc,
182template <
class Handler>
190template <
class Handler>
197template <
class Handler>
201 boost::beast::multi_buffer buf(16);
203 boost::system::error_code ec;
204 bool const ssl = async_detect_ssl(
stream_, buf, doYield[ec]);
220 if (ec != boost::asio::error::operation_aborted)
222 JLOG(
j_.trace()) <<
"Error detecting ssl: " << ec.message() <<
" from " <<
remoteAddress_;
228template <
class Handler>
240 ss <<
"Can't close acceptor: " <<
port_.name <<
", " << ec.message();
241 JLOG(
j_.error()) << ss.
str();
248 acceptor_.open(localAddress.protocol(), ec);
251 JLOG(
j_.error()) <<
"Open port '" <<
port_.name <<
"' failed:" << ec.message();
255 acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
258 JLOG(
j_.error()) <<
"Option for port '" <<
port_.name <<
"' failed:" << ec.message();
265 JLOG(
j_.error()) <<
"Bind port '" <<
port_.name <<
"' failed:" << ec.message();
269 acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
272 JLOG(
j_.error()) <<
"Listen on port '" <<
port_.name <<
"' failed:" << ec.message();
276 JLOG(
j_.info()) <<
"Opened " <<
port_;
279template <
class Handler>
282 boost::asio::io_context& ioContext,
297template <
class Handler>
305template <
class Handler>
309 if (!
strand_.running_in_this_thread())
320template <
class Handler>
321template <
class ConstBufferSequence>
325 ConstBufferSequence
const& buffers,
341template <
class Handler>
349 JLOG(
j_.warn()) <<
"Throttling do_accept for " <<
acceptDelay_.count() <<
"ms.";
351 boost::system::error_code tec;
361 acceptor_.async_accept(socket, remoteAddress, doYield[ec]);
364 if (ec == boost::asio::error::operation_aborted)
367 if (ec == boost::asio::error::no_descriptors ||
368 ec == boost::asio::error::no_buffer_space)
370 char const*
const cause = (ec == boost::asio::error::no_descriptors)
371 ?
"too many open files"
372 :
"kernel buffer space exhausted";
373 JLOG(
j_.warn()) <<
"accept: " << cause <<
". Pausing for " <<
acceptDelay_.count()
377 boost::system::error_code tec;
384 JLOG(
j_.error()) <<
"accept error: " << ec.message();
393 if (
auto sp =
ios().
template emplace<Detector>(
399 create(
ssl_, boost::asio::null_buffers{}, std::move(stream), remoteAddress);
404template <
class Handler>
413 if (getrlimit(RLIMIT_NOFILE, &rl) != 0 || rl.rlim_cur == RLIM_INFINITY)
417 static constexpr char const* kFdDir =
"/proc/self/fd";
419 static constexpr char const* kFdDir =
"/dev/fd";
421 if (DIR* d = ::opendir(kFdDir))
424 while (::readdir(d) !=
nullptr)
428 s.
used = (cnt >= 3) ? (cnt - 3) : 0;
435template <
class Handler>
449 stats && stats->limit > 0 && stats->used * 100 > stats->limit *
kMaxUsedFdPercent;
A generic endpoint for log messages.
boost::asio::strand< boost::asio::io_context::executor_type > strand_
void doDetect(yield_context yield)
boost::asio::io_context & ioc_
endpoint_type remoteAddress_
Detector(Port const &port, Handler &handler, boost::asio::io_context &ioc, stream_type &&stream, endpoint_type remoteAddress, beast::Journal j)
protocol_type::acceptor acceptor_type
boost::asio::ip::tcp::socket socket_type
boost::asio::io_context & ioc_
static constexpr std::chrono::milliseconds kFdSampleInterval
boost::asio::strand< boost::asio::io_context::executor_type > strand_
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remoteAddress)
endpoint_type getEndpoint() const
boost::asio::yield_context yield_context
boost::beast::tcp_stream stream_type
static constexpr std::uint64_t kMaxUsedFdPercent
protocol_type::endpoint endpoint_type
void doAccept(yield_context yield)
static constexpr std::chrono::milliseconds kMaxAcceptDelay
boost::system::error_code error_code
std::chrono::milliseconds acceptDelay_
boost::asio::basic_waitable_timer< clock_type > timer_type
void close() override
Close the Door listening socket and connections.
clock_type::time_point fdSampleAt_
Door(Handler &handler, boost::asio::io_context &ioContext, Port const &port, beast::Journal j)
bool shouldThrottleForFds()
boost::asio::ip::tcp protocol_type
std::optional< FDStats > queryFdStats() const
std::chrono::steady_clock clock_type
static constexpr std::chrono::milliseconds kInitialAcceptDelay
boost::asio::steady_timer backoffTimer_
IOList & ios()
Return the IOList 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
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
T shared_from_this(T... args)
Configuration information for a Server listening port.