#pragma once #include #include #include #include /// @brief Abstraction over plain-text and TLS streams. class Stream : private std::variant< boost::asio::ip::tcp::socket, boost::asio::ssl::stream> { public: using tcp_socket = boost::asio::ip::tcp::socket; using tls_stream = boost::asio::ssl::stream; /// @brief The type of the executor associated with the stream. using executor_type = boost::asio::any_io_executor; /// @brief Type of the lowest layer of this stream using lowest_layer_type = tcp_socket::lowest_layer_type; private: using base_type = std::variant; auto base() -> base_type& { return *this; } auto base() const -> base_type const& { return *this; } public: /// @brief Initialize stream with a plain TCP socket /// @param ioc IO context of stream template Stream(T&& executor) : base_type{std::in_place_type, std::forward(executor)} {} /// @brief Reset stream to a plain TCP socket /// @return Reference to internal socket object auto reset() -> tcp_socket& { return base().emplace(get_executor()); } /// @brief Upgrade a plain TCP socket into a TLS stream. /// @param ctx TLS context used for handshake /// @return Reference to internal stream object auto upgrade(boost::asio::ssl::context& ctx) -> tls_stream& { auto socket = std::move(std::get(base())); return base().emplace(std::move(socket), ctx); } /// @brief Get underlying basic socket /// @return Reference to underlying socket auto lowest_layer() -> lowest_layer_type& { return std::visit([](auto&& x) -> decltype(auto) { return x.lowest_layer(); }, base()); } /// @brief Get underlying basic socket /// @return Reference to underlying socket auto lowest_layer() const -> lowest_layer_type const& { return std::visit([](auto&& x) -> decltype(auto) { return x.lowest_layer(); }, base()); } /// @brief Get the executor associated with this stream. /// @return The executor associated with the stream. auto get_executor() -> executor_type const& { return lowest_layer().get_executor(); } /// @brief Initiates an asynchronous read operation. /// @tparam MutableBufferSequence Type of the buffer sequence. /// @tparam Token Type of the completion token. /// @param buffers The buffer sequence into which data will be read. /// @param token The completion token for the read operation. /// @return The result determined by the completion token. template < typename MutableBufferSequence, boost::asio::completion_token_for Token> auto async_read_some(MutableBufferSequence&& buffers, Token&& token) -> decltype(auto) { return std::visit([&buffers, &token](auto&& x) -> decltype(auto) { return x.async_read_some(std::forward(buffers), std::forward(token)); }, base()); } /// @brief Initiates an asynchronous write operation. /// @tparam ConstBufferSequence Type of the buffer sequence. /// @tparam Token Type of the completion token. /// @param buffers The buffer sequence from which data will be written. /// @param token The completion token for the write operation. /// @return The result determined by the completion token. template < typename ConstBufferSequence, boost::asio::completion_token_for Token> auto async_write_some(ConstBufferSequence&& buffers, Token&& token) -> decltype(auto) { return std::visit([&buffers, &token](auto&& x) -> decltype(auto) { return x.async_write_some(std::forward(buffers), std::forward(token)); }, base()); } /// @brief Tear down the network stream auto close() -> void { boost::system::error_code err; auto& socket = lowest_layer(); socket.shutdown(socket.shutdown_both, err); socket.lowest_layer().close(err); } };