3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/server/detail/PlainHTTPPeer.h>
6#include <xrpl/server/detail/SSLHTTPPeer.h>
7#include <xrpl/server/detail/io_list.h>
9#include <boost/asio/basic_waitable_timer.hpp>
10#include <boost/asio/buffer.hpp>
11#include <boost/asio/io_context.hpp>
12#include <boost/asio/ip/tcp.hpp>
13#include <boost/asio/post.hpp>
14#include <boost/asio/spawn.hpp>
15#include <boost/asio/steady_timer.hpp>
16#include <boost/beast/core/detect_ssl.hpp>
17#include <boost/beast/core/multi_buffer.hpp>
18#include <boost/beast/core/tcp_stream.hpp>
19#include <boost/container/flat_map.hpp>
20#include <boost/predef.h>
23#include <sys/resource.h>
40template <
class Handler>
45 using timer_type = boost::asio::basic_waitable_timer<clock_type>;
60 boost::asio::io_context&
ioc_;
64 boost::asio::strand<boost::asio::io_context::executor_type>
strand_;
71 boost::asio::io_context& ioc,
88 boost::asio::io_context&
ioc_;
90 boost::asio::strand<boost::asio::io_context::executor_type>
strand_;
92 port_.protocol.contains(
"https") ||
port_.protocol.contains(
"wss") ||
93 port_.protocol.contains(
"wss2") ||
port_.protocol.contains(
"peer")};
95 port_.protocol.contains(
"http") ||
port_.protocol.contains(
"ws") ||
96 (
port_.protocol.contains(
"ws2"))};
144 template <
class ConstBufferSequence>
148 ConstBufferSequence
const& buffers,
156template <
class Handler>
160 boost::asio::io_context& ioc,
175template <
class Handler>
183template <
class Handler>
190template <
class Handler>
194 boost::beast::multi_buffer buf(16);
196 boost::system::error_code ec;
197 bool const ssl = async_detect_ssl(
stream_, buf, doYield[ec]);
213 if (ec != boost::asio::error::operation_aborted)
215 JLOG(
j_.trace()) <<
"Error detecting ssl: " << ec.message() <<
" from " <<
remoteAddress_;
221template <
class Handler>
233 ss <<
"Can't close acceptor: " <<
port_.name <<
", " << ec.message();
234 JLOG(
j_.error()) << ss.
str();
241 acceptor_.open(localAddress.protocol(), ec);
244 JLOG(
j_.error()) <<
"Open port '" <<
port_.name <<
"' failed:" << ec.message();
248 acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
251 JLOG(
j_.error()) <<
"Option for port '" <<
port_.name <<
"' failed:" << ec.message();
258 JLOG(
j_.error()) <<
"Bind port '" <<
port_.name <<
"' failed:" << ec.message();
262 acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
265 JLOG(
j_.error()) <<
"Listen on port '" <<
port_.name <<
"' failed:" << ec.message();
269 JLOG(
j_.info()) <<
"Opened " <<
port_;
272template <
class Handler>
275 boost::asio::io_context& ioContext,
290template <
class Handler>
299template <
class Handler>
303 if (!
strand_.running_in_this_thread())
305 return boost::asio::post(
315template <
class Handler>
316template <
class ConstBufferSequence>
320 ConstBufferSequence
const& buffers,
336template <
class Handler>
344 JLOG(
j_.warn()) <<
"Throttling do_accept for " <<
acceptDelay_.count() <<
"ms.";
346 boost::system::error_code tec;
356 acceptor_.async_accept(socket, remoteAddress, doYield[ec]);
359 if (ec == boost::asio::error::operation_aborted)
362 if (ec == boost::asio::error::no_descriptors ||
363 ec == boost::asio::error::no_buffer_space)
365 char const*
const cause = (ec == boost::asio::error::no_descriptors)
366 ?
"too many open files"
367 :
"kernel buffer space exhausted";
368 JLOG(
j_.warn()) <<
"accept: " << cause <<
". Pausing for " <<
acceptDelay_.count()
372 boost::system::error_code tec;
379 JLOG(
j_.error()) <<
"accept error: " << ec.message();
388 if (
auto sp =
ios().
template emplace<Detector>(
394 create(
ssl_, boost::asio::null_buffers{}, std::move(stream), remoteAddress);
399template <
class Handler>
408 if (getrlimit(RLIMIT_NOFILE, &rl) != 0 || rl.rlim_cur == RLIM_INFINITY)
412 static constexpr char const* kFdDir =
"/proc/self/fd";
414 static constexpr char const* kFdDir =
"/dev/fd";
416 if (DIR* d = ::opendir(kFdDir))
419 while (::readdir(d) !=
nullptr)
423 s.
used = (cnt >= 3) ? (cnt - 3) : 0;
430template <
class Handler>
444 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.