//------------------------------------------------------------------------------ /* This file is part of clio: https://github.com/XRPLF/clio Copyright (c) 2023, the clio developers. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #pragma once #include "util/Taggable.hpp" #include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/WsBase.hpp" #include "web/interface/ConnectionBase.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace web { /** * @brief Represents a secure websocket session. * * Majority of the operations are handled by the base class. */ template class SslWsSession : public impl::WsBase { using StreamType = boost::beast::websocket::stream>; StreamType ws_; public: /** * @brief Create a new non-secure websocket session. * * @param stream The SSL stream. Ownership is transferred * @param ip Client's IP address * @param tagFactory A factory that is used to generate tags to track requests and sessions * @param dosGuard The denial of service guard to use * @param handler The server handler to use * @param buffer Buffer with initial data received from the peer * @param isAdmin Whether the connection has admin privileges * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket */ explicit SslWsSession( boost::beast::ssl_stream&& stream, std::string ip, std::reference_wrapper tagFactory, std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer&& buffer, bool isAdmin, std::uint32_t maxWsSendingQueueSize ) : impl::WsBase( ip, tagFactory, dosGuard, handler, std::move(buffer), maxWsSendingQueueSize ) , ws_(std::move(stream)) { ConnectionBase::isAdmin_ = isAdmin; // NOLINT(cppcoreguidelines-prefer-member-initializer) } /** @return The secure websocket stream. */ StreamType& ws() { return ws_; } }; /** * @brief The HTTPS upgrader class, upgrade from an HTTPS session to a secure websocket session. * * Pass the stream to the session class after upgrade. */ template class SslWsUpgrader : public std::enable_shared_from_this> { using std::enable_shared_from_this>::shared_from_this; boost::beast::ssl_stream https_; boost::optional> parser_; boost::beast::flat_buffer buffer_; std::string ip_; std::reference_wrapper tagFactory_; std::reference_wrapper dosGuard_; std::shared_ptr const handler_; http::request req_; bool isAdmin_; std::uint32_t maxWsSendingQueueSize_; public: /** * @brief Create a new upgrader to secure websocket. * * @param stream The SSL stream. Ownership is transferred * @param ip Client's IP address * @param tagFactory A factory that is used to generate tags to track requests and sessions * @param dosGuard The denial of service guard to use * @param handler The server handler to use * @param buffer Buffer with initial data received from the peer. Ownership is transferred * @param request The request. Ownership is transferred * @param isAdmin Whether the connection has admin privileges * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket */ SslWsUpgrader( boost::beast::ssl_stream stream, std::string ip, std::reference_wrapper tagFactory, std::reference_wrapper dosGuard, std::shared_ptr handler, boost::beast::flat_buffer&& buffer, http::request request, bool isAdmin, std::uint32_t maxWsSendingQueueSize ) : https_(std::move(stream)) , buffer_(std::move(buffer)) , ip_(std::move(ip)) , tagFactory_(tagFactory) , dosGuard_(dosGuard) , handler_(std::move(handler)) , req_(std::move(request)) , isAdmin_(isAdmin) , maxWsSendingQueueSize_(maxWsSendingQueueSize) { } ~SslWsUpgrader() = default; /** @brief Initiate the upgrade. */ void run() { boost::beast::get_lowest_layer(https_).expires_after(std::chrono::seconds(30)); boost::asio::dispatch( https_.get_executor(), boost::beast::bind_front_handler(&SslWsUpgrader::doUpgrade, shared_from_this()) ); } private: void doUpgrade() { parser_.emplace(); // Apply a reasonable limit to the allowed size of the body in bytes to prevent abuse. static constexpr auto kMAX_BODY_SIZE = 10000; parser_->body_limit(kMAX_BODY_SIZE); boost::beast::get_lowest_layer(https_).expires_after(std::chrono::seconds(30)); onUpgrade(); } void onUpgrade() { if (!boost::beast::websocket::is_upgrade(req_)) return; // Disable the timeout. The websocket::stream uses its own timeout settings. boost::beast::get_lowest_layer(https_).expires_never(); std::make_shared>( std::move(https_), ip_, tagFactory_, dosGuard_, handler_, std::move(buffer_), isAdmin_, maxWsSendingQueueSize_ ) ->run(std::move(req_)); } }; } // namespace web