20#ifndef RIPPLE_SERVER_DOOR_H_INCLUDED
21#define RIPPLE_SERVER_DOOR_H_INCLUDED
23#include <xrpl/basics/Log.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/server/detail/PlainHTTPPeer.h>
26#include <xrpl/server/detail/SSLHTTPPeer.h>
27#include <xrpl/server/detail/io_list.h>
29#include <boost/asio/basic_waitable_timer.hpp>
30#include <boost/asio/buffer.hpp>
31#include <boost/asio/io_context.hpp>
32#include <boost/asio/ip/tcp.hpp>
33#include <boost/asio/post.hpp>
34#include <boost/asio/spawn.hpp>
35#include <boost/asio/steady_timer.hpp>
36#include <boost/beast/core/detect_ssl.hpp>
37#include <boost/beast/core/multi_buffer.hpp>
38#include <boost/beast/core/tcp_stream.hpp>
39#include <boost/container/flat_map.hpp>
40#include <boost/predef.h>
43#include <sys/resource.h>
60template <
class Handler>
66 using timer_type = boost::asio::basic_waitable_timer<clock_type>;
82 boost::asio::io_context&
ioc_;
86 boost::asio::strand<boost::asio::io_context::executor_type>
strand_;
93 boost::asio::io_context& ioc,
112 boost::asio::strand<boost::asio::io_context::executor_type>
strand_;
139 boost::asio::io_context& io_context,
163 template <
class ConstBufferSequence>
167 ConstBufferSequence
const& buffers,
175template <
class Handler>
179 boost::asio::io_context& ioc,
186 , stream_(
std::move(stream))
187 , socket_(stream_.socket())
188 , remote_address_(remote_address)
189 , strand_(
boost::asio::make_strand(ioc_))
194template <
class Handler>
203 std::placeholders::_1));
206template <
class Handler>
213template <
class Handler>
217 boost::beast::multi_buffer buf(16);
219 boost::system::error_code ec;
220 bool const ssl = async_detect_ssl(stream_, buf, do_yield[ec]);
221 stream_.expires_never();
248 if (ec != boost::asio::error::operation_aborted)
250 JLOG(
j_.
trace()) <<
"Error detecting ssl: " << ec.message() <<
" from "
257template <
class Handler>
269 ss <<
"Can't close acceptor: " <<
port_.
name <<
", "
272 Throw<std::runtime_error>(ss.
str());
278 acceptor_.open(local_address.protocol(), ec);
282 <<
"' failed:" << ec.message();
283 Throw<std::exception>();
287 boost::asio::ip::tcp::acceptor::reuse_address(
true), ec);
291 <<
"' failed:" << ec.message();
292 Throw<std::exception>();
299 <<
"' failed:" << ec.message();
300 Throw<std::exception>();
303 acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
307 <<
"' failed:" << ec.message();
308 Throw<std::exception>();
314template <
class Handler>
317 boost::asio::io_context& io_context,
338template <
class Handler>
346 this->shared_from_this(),
347 std::placeholders::_1));
350template <
class Handler>
354 if (!strand_.running_in_this_thread())
355 return boost::asio::post(
358 backoff_timer_.cancel();
365template <
class Handler>
366template <
class ConstBufferSequence>
370 ConstBufferSequence
const& buffers,
398template <
class Handler>
402 while (acceptor_.is_open())
404 if (should_throttle_for_fds())
406 backoff_timer_.expires_after(accept_delay_);
407 boost::system::error_code tec;
408 backoff_timer_.async_wait(do_yield[tec]);
409 accept_delay_ =
std::min(accept_delay_ * 2, MAX_ACCEPT_DELAY);
410 JLOG(j_.
warn()) <<
"Throttling do_accept for "
411 << accept_delay_.count() <<
"ms.";
419 acceptor_.async_accept(socket, remote_address, do_yield[ec]);
422 if (ec == boost::asio::error::operation_aborted)
425 if (ec == boost::asio::error::no_descriptors ||
426 ec == boost::asio::error::no_buffer_space)
428 JLOG(j_.
warn()) <<
"accept: Too many open files. Pausing for "
429 << accept_delay_.count() <<
"ms.";
431 backoff_timer_.expires_after(accept_delay_);
432 boost::system::error_code tec;
433 backoff_timer_.async_wait(do_yield[tec]);
435 accept_delay_ =
std::min(accept_delay_ * 2, MAX_ACCEPT_DELAY);
439 JLOG(j_.
error()) <<
"accept error: " << ec.message();
444 accept_delay_ = INITIAL_ACCEPT_DELAY;
448 if (
auto sp = ios().
template emplace<Detector>(
457 else if (ssl_ || plain_)
461 boost::asio::null_buffers{},
468template <
class Handler>
477 if (getrlimit(RLIMIT_NOFILE, &rl) != 0 || rl.rlim_cur == RLIM_INFINITY)
481 constexpr char const* kFdDir =
"/proc/self/fd";
483 constexpr char const* kFdDir =
"/dev/fd";
485 if (DIR* d = ::opendir(kFdDir))
488 while (::readdir(d) !=
nullptr)
492 s.
used = (cnt >= 3) ? (cnt - 3) : 0;
499template <
class Handler>
506 auto const stats = query_fd_stats();
507 if (!stats || stats->limit == 0)
510 auto const& s = *stats;
511 auto const free = (s.limit > s.used) ? (s.limit - s.used) : 0ull;
512 double const free_ratio =
513 static_cast<double>(free) /
static_cast<double>(s.limit);
514 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