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/asio/error.hpp>
33#include <boost/beast/core/multi_buffer.hpp>
34#include <boost/beast/http/message.hpp>
35#include <boost/beast/websocket.hpp>
36#include <boost/logic/tribool.hpp>
44template <
class Handler,
class Impl>
55 friend class BasePeer<Handler, Impl>;
58 boost::beast::multi_buffer
rb_;
59 boost::beast::multi_buffer
wb_;
65 boost::beast::websocket::close_reason
cr_;
72 void(boost::beast::websocket::frame_type, boost::beast::string_view)>
76 template <
class Body,
class Headers>
80 boost::asio::executor
const& executor,
83 boost::beast::http::request<Body, Headers>&&
request,
105 boost::asio::ip::tcp::endpoint
const&
118 close(boost::beast::websocket::close_reason
const& reason)
override;
127 return *
static_cast<Impl*
>(
this);
162 boost::beast::websocket::frame_type kind,
163 boost::beast::string_view payload);
168 template <
class String>
175template <
class Handler,
class Impl>
176template <
class Body,
class Headers>
180 boost::asio::executor
const& executor,
183 boost::beast::http::request<Body, Headers>&& request,
185 :
BasePeer<Handler, Impl>(port, handler, executor, remote_address, journal)
186 , request_(
std::move(request))
187 , timer_(
std::move(timer))
188 , payload_(
"12345678")
192template <
class Handler,
class Impl>
196 if (!strand_.running_in_this_thread())
199 impl().ws_.set_option(port().pmd_options);
205 std::placeholders::_1,
206 std::placeholders::_2);
207 impl().ws_.control_callback(control_callback_);
209 close_on_timer_ =
true;
210 impl().ws_.set_option(
211 boost::beast::websocket::stream_base::decorator([](
auto& res) {
213 boost::beast::http::field::server,
216 impl().ws_.async_accept(
222 impl().shared_from_this(),
223 std::placeholders::_1)));
226template <
class Handler,
class Impl>
230 if (!strand_.running_in_this_thread())
237 if (wq_.size() > port().ws_queue_limit)
239 cr_.code =
safe_cast<
decltype(cr_.code)>(
240 boost::beast::websocket::close_code::policy_error);
241 cr_.reason =
"Policy error: client is too slow.";
242 JLOG(this->j_.
info()) << cr_.reason;
243 wq_.erase(
std::next(wq_.begin()), wq_.end());
247 wq_.emplace_back(std::move(w));
252template <
class Handler,
class Impl>
256 close(boost::beast::websocket::close_reason{});
259template <
class Handler,
class Impl>
262 boost::beast::websocket::close_reason
const& reason)
264 if (!strand_.running_in_this_thread())
265 return post(strand_, [self = impl().shared_from_this(), reason] {
273 impl().ws_.async_close(
277 [self = impl().shared_from_this()](
278 boost::beast::error_code
const& ec) {
288template <
class Handler,
class Impl>
292 if (!strand_.running_in_this_thread())
299template <
class Handler,
class Impl>
304 return fail(ec,
"on_ws_handshake");
305 close_on_timer_ =
false;
309template <
class Handler,
class Impl>
313 if (!strand_.running_in_this_thread())
320template <
class Handler,
class Impl>
325 return fail(ec,
"write");
326 auto& w = *wq_.front();
327 auto const result = w.prepare(
329 if (boost::indeterminate(result.first))
333 impl().ws_.async_write_some(
334 static_cast<bool>(result.first),
340 impl().shared_from_this(),
341 std::placeholders::_1)));
343 impl().ws_.async_write_some(
344 static_cast<bool>(result.first),
350 impl().shared_from_this(),
351 std::placeholders::_1)));
354template <
class Handler,
class Impl>
359 return fail(ec,
"write_fin");
363 impl().ws_.async_close(
369 impl().shared_from_this(),
370 std::placeholders::_1)));
372 else if (!wq_.empty())
376template <
class Handler,
class Impl>
380 if (!strand_.running_in_this_thread())
384 impl().ws_.async_read(
390 impl().shared_from_this(),
391 std::placeholders::_1)));
394template <
class Handler,
class Impl>
398 if (ec == boost::beast::websocket::error::closed)
401 return fail(ec,
"read");
402 auto const& data = rb_.data();
406 this->handler_.onWSMessage(impl().shared_from_this(), b);
407 rb_.consume(rb_.size());
410template <
class Handler,
class Impl>
417template <
class Handler,
class Impl>
427 timer_.expires_after(
428 remote_endpoint().address().is_loopback() ? timeoutLocal : timeout);
430 catch (boost::system::system_error
const& e)
432 return fail(e.code(),
"start_timer");
435 timer_.async_wait(bind_executor(
439 impl().shared_from_this(),
440 std::placeholders::_1)));
444template <
class Handler,
class Impl>
452 catch (boost::system::system_error
const&)
458template <
class Handler,
class Impl>
462 if (ec == boost::asio::error::operation_aborted)
464 ping_active_ =
false;
470template <
class Handler,
class Impl>
473 boost::beast::websocket::frame_type kind,
474 boost::beast::string_view payload)
476 if (kind == boost::beast::websocket::frame_type::pong)
478 boost::beast::string_view p(payload_.begin());
481 close_on_timer_ =
false;
482 JLOG(this->j_.
trace()) <<
"got matching pong";
486 JLOG(this->j_.
trace()) <<
"got pong";
491template <
class Handler,
class Impl>
495 if (ec == boost::asio::error::operation_aborted)
499 if (!close_on_timer_ || !ping_active_)
502 close_on_timer_ =
true;
506 impl().ws_.async_ping(
512 impl().shared_from_this(),
513 std::placeholders::_1)));
514 JLOG(this->j_.
trace()) <<
"sent ping";
517 ec = boost::system::errc::make_error_code(
518 boost::system::errc::timed_out);
523template <
class Handler,
class Impl>
524template <
class String>
529 strand_.running_in_this_thread(),
530 "ripple::BaseWSPeer::fail : strand in this thread");
533 if (!ec_ && ec != boost::asio::error::operation_aborted)
536 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 *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.
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.