20#ifndef RIPPLE_SERVER_BASEWSPEER_H_INCLUDED
21#define RIPPLE_SERVER_BASEWSPEER_H_INCLUDED
23#include <xrpl/basics/safe_cast.h>
24#include <xrpl/beast/utility/instrumentation.h>
25#include <xrpl/beast/utility/rngfill.h>
26#include <xrpl/crypto/csprng.h>
27#include <xrpl/protocol/BuildInfo.h>
28#include <xrpl/server/WSSession.h>
29#include <xrpl/server/detail/BasePeer.h>
30#include <xrpl/server/detail/LowestLayer.h>
32#include <boost/beast/core/multi_buffer.hpp>
33#include <boost/beast/http/message.hpp>
34#include <boost/beast/websocket.hpp>
35#include <boost/logic/tribool.hpp>
43template <
class Handler,
class Impl>
54 friend class BasePeer<Handler, Impl>;
57 boost::beast::multi_buffer
rb_;
58 boost::beast::multi_buffer
wb_;
64 boost::beast::websocket::close_reason
cr_;
71 void(boost::beast::websocket::frame_type, boost::beast::string_view)>
75 template <
class Body,
class Headers>
79 boost::asio::executor
const& executor,
82 boost::beast::http::request<Body, Headers>&&
request,
104 boost::asio::ip::tcp::endpoint
const&
117 close(boost::beast::websocket::close_reason
const& reason)
override;
126 return *
static_cast<Impl*
>(
this);
161 boost::beast::websocket::frame_type kind,
162 boost::beast::string_view payload);
167 template <
class String>
174template <
class Handler,
class Impl>
175template <
class Body,
class Headers>
179 boost::asio::executor
const& executor,
182 boost::beast::http::request<Body, Headers>&& request,
184 :
BasePeer<Handler, Impl>(port, handler, executor, remote_address, journal)
185 , request_(
std::move(request))
186 , timer_(
std::move(timer))
187 , payload_(
"12345678")
191template <
class Handler,
class Impl>
195 if (!strand_.running_in_this_thread())
198 impl().ws_.set_option(port().pmd_options);
204 std::placeholders::_1,
205 std::placeholders::_2);
206 impl().ws_.control_callback(control_callback_);
208 close_on_timer_ =
true;
209 impl().ws_.set_option(
210 boost::beast::websocket::stream_base::decorator([](
auto& res) {
212 boost::beast::http::field::server,
215 impl().ws_.async_accept(
221 impl().shared_from_this(),
222 std::placeholders::_1)));
225template <
class Handler,
class Impl>
229 if (!strand_.running_in_this_thread())
236 if (wq_.size() > port().ws_queue_limit)
238 cr_.code =
safe_cast<
decltype(cr_.code)>(
239 boost::beast::websocket::close_code::policy_error);
240 cr_.reason =
"Policy error: client is too slow.";
241 JLOG(this->j_.
info()) << cr_.reason;
242 wq_.erase(
std::next(wq_.begin()), wq_.end());
246 wq_.emplace_back(std::move(w));
251template <
class Handler,
class Impl>
255 close(boost::beast::websocket::close_reason{});
258template <
class Handler,
class Impl>
261 boost::beast::websocket::close_reason
const& reason)
263 if (!strand_.running_in_this_thread())
264 return post(strand_, [self = impl().shared_from_this(), reason] {
272 impl().ws_.async_close(
276 [self = impl().shared_from_this()](
277 boost::beast::error_code
const& ec) {
287template <
class Handler,
class Impl>
291 if (!strand_.running_in_this_thread())
298template <
class Handler,
class Impl>
303 return fail(ec,
"on_ws_handshake");
304 close_on_timer_ =
false;
308template <
class Handler,
class Impl>
312 if (!strand_.running_in_this_thread())
319template <
class Handler,
class Impl>
324 return fail(ec,
"write");
325 auto& w = *wq_.front();
326 auto const result = w.prepare(
328 if (boost::indeterminate(result.first))
332 impl().ws_.async_write_some(
333 static_cast<bool>(result.first),
339 impl().shared_from_this(),
340 std::placeholders::_1)));
342 impl().ws_.async_write_some(
343 static_cast<bool>(result.first),
349 impl().shared_from_this(),
350 std::placeholders::_1)));
353template <
class Handler,
class Impl>
358 return fail(ec,
"write_fin");
362 impl().ws_.async_close(
368 impl().shared_from_this(),
369 std::placeholders::_1)));
371 else if (!wq_.empty())
375template <
class Handler,
class Impl>
379 if (!strand_.running_in_this_thread())
383 impl().ws_.async_read(
389 impl().shared_from_this(),
390 std::placeholders::_1)));
393template <
class Handler,
class Impl>
397 if (ec == boost::beast::websocket::error::closed)
400 return fail(ec,
"read");
401 auto const& data = rb_.data();
405 this->handler_.onWSMessage(impl().shared_from_this(), b);
406 rb_.consume(rb_.size());
409template <
class Handler,
class Impl>
416template <
class Handler,
class Impl>
424 timer_.expires_from_now(
425 remote_endpoint().address().is_loopback() ? timeoutLocal : timeout, ec);
427 return fail(ec,
"start_timer");
428 timer_.async_wait(bind_executor(
432 impl().shared_from_this(),
433 std::placeholders::_1)));
437template <
class Handler,
class Impl>
445template <
class Handler,
class Impl>
449 if (ec == boost::asio::error::operation_aborted)
451 ping_active_ =
false;
457template <
class Handler,
class Impl>
460 boost::beast::websocket::frame_type kind,
461 boost::beast::string_view payload)
463 if (kind == boost::beast::websocket::frame_type::pong)
465 boost::beast::string_view p(payload_.begin());
468 close_on_timer_ =
false;
469 JLOG(this->j_.
trace()) <<
"got matching pong";
473 JLOG(this->j_.
trace()) <<
"got pong";
478template <
class Handler,
class Impl>
482 if (ec == boost::asio::error::operation_aborted)
486 if (!close_on_timer_ || !ping_active_)
489 close_on_timer_ =
true;
493 impl().ws_.async_ping(
499 impl().shared_from_this(),
500 std::placeholders::_1)));
501 JLOG(this->j_.
trace()) <<
"sent ping";
504 ec = boost::system::errc::make_error_code(
505 boost::system::errc::timed_out);
510template <
class Handler,
class Impl>
511template <
class String>
516 strand_.running_in_this_thread(),
517 "ripple::BaseWSPeer::fail : strand in this thread");
520 if (!ec_ && ec != boost::asio::error::operation_aborted)
523 JLOG(this->j_.
trace()) << what <<
": " << ec.message();
T back_inserter(T... args)
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
endpoint_type remote_address_
boost::asio::strand< boost::asio::executor > strand_
Represents an active WebSocket connection.
BaseWSPeer(Port const &port, Handler &handler, boost::asio::executor const &executor, waitable_timer timer, endpoint_type remote_address, boost::beast::http::request< Body, Headers > &&request, beast::Journal journal)
boost::asio::ip::tcp::endpoint endpoint_type
boost::system::error_code error_code
http_request_type const & request() const override
std::function< void(boost::beast::websocket::frame_type, boost::beast::string_view)> control_callback_
void fail(error_code ec, String const &what)
bool do_close_
The socket has been closed, or will close after the next write finishes.
void close(boost::beast::websocket::close_reason const &reason) override
http_request_type request_
void on_read(error_code const &ec)
void on_ws_handshake(error_code const &ec)
boost::beast::multi_buffer wb_
void on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload)
boost::asio::basic_waitable_timer< clock_type > waitable_timer
void on_close(error_code const &ec)
void on_write_fin(error_code const &ec)
boost::asio::ip::tcp::endpoint const & remote_endpoint() const override
Port const & port() const override
void complete() override
Indicate that the response is complete.
boost::beast::websocket::close_reason cr_
void send(std::shared_ptr< WSMsg > w) override
Send a WebSockets message.
std::list< std::shared_ptr< WSMsg > > wq_
void on_timer(error_code ec)
void on_ping(error_code const &ec)
boost::beast::multi_buffer rb_
void on_write(error_code const &ec)
boost::beast::websocket::ping_data payload_
void rngfill(void *buffer, std::size_t 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.
constexpr std::enable_if_t< std::is_integral_v< Dest > &&std::is_integral_v< Src >, Dest > safe_cast(Src s) noexcept
csprng_engine & crypto_prng()
The default cryptographically secure PRNG.
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
decltype(auto) get_lowest_layer(T &t) noexcept
Configuration information for a Server listening port.