Files
clio/src/web/SslHttpSession.hpp
2026-03-24 15:25:32 +00:00

180 lines
5.6 KiB
C++

#pragma once
#include "data/LedgerCacheInterface.hpp"
#include "util/Taggable.hpp"
#include "web/AdminVerificationStrategy.hpp"
#include "web/ProxyIpResolver.hpp"
#include "web/SslWsSession.hpp"
#include "web/dosguard/DOSGuardInterface.hpp"
#include "web/impl/HttpBase.hpp"
#include "web/interface/Concepts.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/stream_base.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/beast/ssl/ssl_stream.hpp>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
namespace web {
using tcp = boost::asio::ip::tcp;
/**
* @brief Represents a HTTPS connection established by a client.
*
* It will handle the upgrade to secure websocket, pass the ownership of the socket to the upgrade
* session. Otherwise, it will pass control to the base class.
*
* @tparam HandlerType The type of the server handler to use
*/
template <SomeServerHandler HandlerType>
class SslHttpSession : public impl::HttpBase<SslHttpSession, HandlerType>,
public std::enable_shared_from_this<SslHttpSession<HandlerType>> {
boost::asio::ssl::stream<boost::beast::tcp_stream> stream_;
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
std::uint32_t maxWsSendingQueueSize_;
public:
/**
* @brief Create a new SSL session.
*
* @param socket The socket. Ownership is transferred to HttpSession
* @param ip Client's IP address
* @param adminVerification The admin verification strategy to use
* @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy
* @param ctx The SSL context
* @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 cache The ledger cache to use
* @param buffer Buffer with initial data received from the peer
* @param maxWsSendingQueueSize The maximum size of the sending queue for websocket
*/
explicit SslHttpSession(
tcp::socket&& socket,
std::string const& ip,
std::shared_ptr<AdminVerificationStrategy> const& adminVerification,
std::shared_ptr<ProxyIpResolver> proxyIpResolver,
boost::asio::ssl::context& ctx,
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
std::shared_ptr<HandlerType> const& handler,
std::reference_wrapper<data::LedgerCacheInterface const> cache,
boost::beast::flat_buffer buffer,
std::uint32_t maxWsSendingQueueSize
)
: impl::HttpBase<SslHttpSession, HandlerType>(
ip,
tagFactory,
adminVerification,
std::move(proxyIpResolver),
dosGuard,
handler,
cache,
std::move(buffer)
)
, stream_(std::move(socket), ctx)
, tagFactory_(tagFactory)
, maxWsSendingQueueSize_(maxWsSendingQueueSize)
{
}
~SslHttpSession() override = default;
/** @return The SSL stream. */
boost::asio::ssl::stream<boost::beast::tcp_stream>&
stream()
{
return stream_;
}
/** @brief Initiates the handshake. */
void
run()
{
auto self = this->shared_from_this();
boost::asio::dispatch(stream_.get_executor(), [self]() {
// Set the timeout.
boost::beast::get_lowest_layer(self->stream()).expires_after(std::chrono::seconds(30));
// Perform the SSL handshake
// Note, this is the buffered version of the handshake.
self->stream_.async_handshake(
boost::asio::ssl::stream_base::server,
self->buffer_.data(),
boost::beast::bind_front_handler(&SslHttpSession<HandlerType>::onHandshake, self)
);
});
}
/**
* @brief Handles the handshake.
*
* @param ec Error code if any
* @param bytesUsed The total amount of data read from the stream
*/
void
onHandshake(boost::beast::error_code ec, std::size_t bytesUsed)
{
if (ec)
return this->httpFail(ec, "handshake");
this->buffer_.consume(bytesUsed);
this->doRead();
}
/** @brief Closes the underlying connection. */
void
doClose()
{
boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
stream_.async_shutdown(
boost::beast::bind_front_handler(&SslHttpSession::onShutdown, this->shared_from_this())
);
}
/**
* @brief Handles a connection shutdown.
*
* @param ec Error code if any
*/
void
onShutdown(boost::beast::error_code ec)
{
if (ec)
return this->httpFail(ec, "shutdown");
// At this point the connection is closed gracefully
}
/** @brief Upgrades connection to secure websocket. */
void
upgrade()
{
std::make_shared<SslWsUpgrader<HandlerType>>(
std::move(stream_),
this->clientIp_,
tagFactory_,
this->dosGuard_,
this->handler_,
std::move(this->buffer_),
std::move(this->req_),
ConnectionBase::isAdmin(),
maxWsSendingQueueSize_
)
->run();
}
};
} // namespace web