3#include <xrpl/basics/safe_cast.h>
4#include <xrpl/beast/utility/Journal.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/beast/utility/rngfill.h>
7#include <xrpl/crypto/csprng.h>
8#include <xrpl/protocol/BuildInfo.h>
9#include <xrpl/server/Handoff.h>
10#include <xrpl/server/Port.h>
11#include <xrpl/server/WSSession.h>
12#include <xrpl/server/detail/BasePeer.h>
13#include <xrpl/server/detail/LowestLayer.h>
15#include <boost/asio/error.hpp>
16#include <boost/beast/core/multi_buffer.hpp>
17#include <boost/beast/http/message.hpp>
18#include <boost/beast/websocket.hpp>
19#include <boost/logic/tribool.hpp>
37template <
class Handler,
class Impl>
48 friend class BasePeer<Handler, Impl>;
51 boost::beast::multi_buffer
rb_;
52 boost::beast::multi_buffer
wb_;
60 boost::beast::websocket::close_reason
cr_;
69 template <
class Body,
class Headers>
73 boost::asio::executor
const& executor,
76 boost::beast::http::request<Body, Headers>&&
request,
86 [[nodiscard]]
Port const&
95 return this->request_;
98 [[nodiscard]] boost::asio::ip::tcp::endpoint
const&
111 close(boost::beast::websocket::close_reason
const& reason)
override;
120 return *
static_cast<Impl*
>(
this);
159 template <
class String>
166template <
class Handler,
class Impl>
167template <
class Body,
class Headers>
171 boost::asio::executor
const& executor,
174 boost::beast::http::request<Body, Headers>&&
request,
176 :
BasePeer<Handler, Impl>(
port, handler, executor, remoteAddress, journal)
183template <
class Handler,
class Impl>
187 if (!
strand_.running_in_this_thread())
188 return post(
strand_, [self =
impl().shared_from_this()] { self->run(); });
189 impl().ws_.set_option(
port().pmdOptions);
198 impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](
auto& res) {
201 impl().ws_.async_accept(
203 self->onWsHandshake(ec);
207template <
class Handler,
class Impl>
211 if (!
strand_.running_in_this_thread())
214 strand_, [self =
impl().shared_from_this(), w = std::move(w)] { self->send(w); });
218 if (
wq_.size() >
port().wsQueueLimit)
220 cr_.code =
safeCast<
decltype(
cr_.code)>(boost::beast::websocket::close_code::policy_error);
221 cr_.reason =
"Policy error: client is too slow.";
222 JLOG(this->
j_.info()) <<
cr_.reason;
227 wq_.emplace_back(std::move(w));
232template <
class Handler,
class Impl>
236 close(boost::beast::websocket::close_reason{});
239template <
class Handler,
class Impl>
243 if (!
strand_.running_in_this_thread())
244 return post(
strand_, [self =
impl().shared_from_this(), reason] { self->close(reason); });
250 impl().ws_.async_close(
253 strand_, [self =
impl().shared_from_this()](boost::beast::error_code
const& ec) {
263template <
class Handler,
class Impl>
267 if (!
strand_.running_in_this_thread())
268 return post(
strand_, [self =
impl().shared_from_this()] { self->complete(); });
272template <
class Handler,
class Impl>
277 return fail(ec,
"on_ws_handshake");
282template <
class Handler,
class Impl>
286 if (!
strand_.running_in_this_thread())
287 return post(
strand_, [self =
impl().shared_from_this()] { self->doWrite(); });
291template <
class Handler,
class Impl>
296 return fail(ec,
"write");
297 auto& w = *
wq_.front();
298 auto const result = w.prepare(65536, [self =
impl().shared_from_this()] { self->doWrite(); });
299 if (boost::indeterminate(result.first))
304 impl().ws_.async_write_some(
305 static_cast<bool>(result.first),
314 impl().ws_.async_write_some(
315 static_cast<bool>(result.first),
319 self->onWriteFin(ec);
324template <
class Handler,
class Impl>
329 return fail(ec,
"write_fin");
333 impl().ws_.async_close(
338 else if (!
wq_.empty())
344template <
class Handler,
class Impl>
348 if (!
strand_.running_in_this_thread())
349 return post(
strand_, [self =
impl().shared_from_this()] { self->doRead(); });
350 impl().ws_.async_read(
358template <
class Handler,
class Impl>
362 if (ec == boost::beast::websocket::error::closed)
365 return fail(ec,
"read");
366 auto const& data =
rb_.data();
370 this->
handler_.onWSMessage(
impl().shared_from_this(), b);
374template <
class Handler,
class Impl>
381template <
class Handler,
class Impl>
393 catch (boost::system::system_error
const& e)
395 return fail(e.code(),
"start_timer");
398 timer_.async_wait(bind_executor(
403template <
class Handler,
class Impl>
411 catch (boost::system::system_error
const&)
417template <
class Handler,
class Impl>
421 if (ec == boost::asio::error::operation_aborted)
429template <
class Handler,
class Impl>
432 boost::beast::websocket::frame_type kind,
435 if (kind == boost::beast::websocket::frame_type::pong)
441 JLOG(this->
j_.trace()) <<
"got matching pong";
445 JLOG(this->
j_.trace()) <<
"got pong";
450template <
class Handler,
class Impl>
454 if (ec == boost::asio::error::operation_aborted)
465 impl().ws_.async_ping(
470 JLOG(this->
j_.trace()) <<
"sent ping";
473 ec = boost::system::errc::make_error_code(boost::system::errc::timed_out);
478template <
class Handler,
class Impl>
479template <
class String>
483 XRPL_ASSERT(
strand_.running_in_this_thread(),
"xrpl::BaseWSPeer::fail : strand in this thread");
486 if (!
ec_ && ec != boost::asio::error::operation_aborted)
489 JLOG(this->
j_.trace()) << what <<
": " << ec.message();
T back_inserter(T... args)
A generic endpoint for log messages.
boost::asio::strand< boost::asio::executor > strand_
BasePeer(Port const &port, Handler &handler, boost::asio::executor const &executor, EndpointType remoteAddress, beast::Journal journal)
EndpointType remoteAddress_
void onPingPong(boost::beast::websocket::frame_type kind, std::string_view payload)
boost::beast::websocket::close_reason cr_
boost::beast::multi_buffer rb_
void onRead(ErrorCode const &ec)
void onClose(ErrorCode const &ec)
boost::beast::multi_buffer wb_
void fail(ErrorCode ec, String const &what)
boost::system::error_code ErrorCode
std::chrono::system_clock ClockType
Port const & port() const override
boost::asio::ip::tcp::endpoint EndpointType
HttpRequestType const & request() const override
boost::asio::basic_waitable_timer< ClockType > WaitableTimer
boost::beast::websocket::ping_data payload_
void onWriteFin(ErrorCode const &ec)
void send(std::shared_ptr< WSMsg > w) override
Send a WebSockets message.
void onWsHandshake(ErrorCode const &ec)
void onWrite(ErrorCode const &ec)
void onPing(ErrorCode const &ec)
bool doClose_
The socket has been closed, or will close after the next write finishes.
boost::asio::ip::tcp::endpoint const & remoteEndpoint() const override
std::list< std::shared_ptr< WSMsg > > wq_
BaseWSPeer(Port const &port, Handler &handler, boost::asio::executor const &executor, WaitableTimer timer, EndpointType remoteAddress, boost::beast::http::request< Body, Headers > &&request, beast::Journal journal)
void complete() override
Indicate that the response is complete.
void onTimer(ErrorCode ec)
std::function< void(boost::beast::websocket::frame_type, std::string_view)> controlCallback_
void rngfill(void *const buffer, std::size_t const bytes, Generator &g)
std::string const & getFullVersionString()
Full server version string.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
CsprngEngine & cryptoPrng()
The default cryptographically secure PRNG.
constexpr Dest safeCast(Src s) noexcept
boost::beast::http::request< boost::beast::http::dynamic_body > HttpRequestType
decltype(auto) getLowestLayer(T &t) noexcept
Configuration information for a Server listening port.