22#include "util/OverloadSet.hpp"
23#include "util/Taggable.hpp"
24#include "util/build/Build.hpp"
25#include "web/ng/Connection.hpp"
26#include "web/ng/Error.hpp"
27#include "web/ng/Request.hpp"
28#include "web/ng/Response.hpp"
29#include "web/ng/impl/Concepts.hpp"
30#include "web/ng/impl/SendingQueue.hpp"
32#include <boost/asio/buffer.hpp>
33#include <boost/asio/ip/tcp.hpp>
34#include <boost/asio/spawn.hpp>
35#include <boost/asio/ssl/context.hpp>
36#include <boost/asio/ssl/stream.hpp>
37#include <boost/beast/core/buffers_to_string.hpp>
38#include <boost/beast/core/flat_buffer.hpp>
39#include <boost/beast/core/role.hpp>
40#include <boost/beast/core/tcp_stream.hpp>
41#include <boost/beast/http/field.hpp>
42#include <boost/beast/http/message.hpp>
43#include <boost/beast/http/string_body.hpp>
44#include <boost/beast/ssl.hpp>
45#include <boost/beast/websocket/rfc6455.hpp>
46#include <boost/beast/websocket/stream.hpp>
47#include <boost/beast/websocket/stream_base.hpp>
56namespace web::ng::impl {
62 virtual std::expected<void, Error>
63 sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield) = 0;
66template <
typename StreamType>
68 boost::beast::websocket::stream<StreamType> stream_;
69 boost::beast::http::request<boost::beast::http::string_body> initialRequest_;
71 using MessageType = std::variant<Response, std::shared_ptr<std::string>>;
80 boost::beast::flat_buffer buffer,
81 boost::beast::http::request<boost::beast::http::string_body> initialRequest,
85 , stream_(std::move(stream))
86 , initialRequest_(std::move(initialRequest))
87 , sendingQueue_{[
this](MessageType
const& message,
auto&& yield) {
88 boost::asio::const_buffer
const buffer = std::visit(
90 [](
Response const& r) -> boost::asio::const_buffer {
return r.asWsResponse(); },
91 [](std::shared_ptr<std::string>
const& m) -> boost::asio::const_buffer {
92 return boost::asio::buffer(*m);
97 stream_.async_write(buffer, yield);
103 ~WsConnection()
override =
default;
104 WsConnection(WsConnection&&) =
delete;
106 operator=(WsConnection&&) =
delete;
107 WsConnection(WsConnection
const&) =
delete;
109 operator=(WsConnection
const&) =
delete;
111 std::expected<void, Error>
112 performHandshake(boost::asio::yield_context yield)
115 stream_.async_accept(initialRequest_, yield[error]);
117 return std::unexpected{error};
127 std::expected<void, Error>
128 sendShared(std::shared_ptr<std::string> message, boost::asio::yield_context yield)
override
130 return sendingQueue_.send(std::move(message), yield);
134 setTimeout(std::chrono::steady_clock::duration newTimeout)
override
136 boost::beast::websocket::stream_base::timeout wsTimeout =
137 boost::beast::websocket::stream_base::timeout::suggested(
138 boost::beast::role_type::server
140 wsTimeout.idle_timeout = newTimeout;
141 wsTimeout.handshake_timeout = newTimeout;
142 stream_.set_option(wsTimeout);
145 std::expected<void, Error>
148 return sendingQueue_.send(std::move(response), yield);
151 std::expected<Request, Error>
152 receive(boost::asio::yield_context yield)
override
155 stream_.async_read(buffer_, yield[error]);
157 return std::unexpected{error};
159 auto request = boost::beast::buffers_to_string(buffer_.data());
160 buffer_.consume(buffer_.size());
162 return Request{std::move(request), initialRequest_};
166 close(boost::asio::yield_context yield)
override
176 boost::system::error_code error;
177 stream_.async_close(boost::beast::websocket::close_code::normal, yield[error]);
185 boost::beast::get_lowest_layer(stream_).expires_never();
188 boost::beast::websocket::stream_base::decorator(
189 [](boost::beast::websocket::response_type& res) {
191 boost::beast::http::field::server, util::build::getClioFullVersionString()
199using PlainWsConnection = WsConnection<boost::beast::tcp_stream>;
200using SslWsConnection = WsConnection<boost::asio::ssl::stream<boost::beast::tcp_stream>>;
202template <
typename StreamType>
203std::expected<std::unique_ptr<WsConnection<StreamType>>, Error>
207 boost::beast::flat_buffer buffer,
208 boost::beast::http::request<boost::beast::http::string_body> request,
210 boost::asio::yield_context yield
213 auto connection = std::make_unique<WsConnection<StreamType>>(
214 std::forward<StreamType>(stream),
220 auto const expectedSuccess = connection->performHandshake(yield);
221 if (not expectedSuccess.has_value())
222 return std::unexpected{expectedSuccess.error()};
A factory for TagDecorator instantiation.
Definition Taggable.hpp:184
Connection(std::string ip, boost::beast::flat_buffer buffer, util::TagDecoratorFactory const &tagDecoratorFactory)
Construct a new Connection object.
Definition Connection.cpp:51
static constexpr std::chrono::steady_clock::duration kDEFAULT_TIMEOUT
The default timeout for send, receive, and close operations.
Definition Connection.hpp:122
Represents an HTTP or WebSocket request.
Definition Request.hpp:37
Represents an HTTP or Websocket response.
Definition Response.hpp:40
Definition SendingQueue.hpp:35
Definition WsConnection.hpp:58
Connection(std::string ip, boost::beast::flat_buffer buffer, util::TagDecoratorFactory const &tagDecoratorFactory)
Construct a new Connection object.
Definition Connection.cpp:51
std::expected< Request, Error > receive(boost::asio::yield_context yield) override
Receive a request from the client.
Definition WsConnection.hpp:152
void close(boost::asio::yield_context yield) override
Gracefully close the connection.
Definition WsConnection.hpp:166
std::expected< void, Error > send(Response response, boost::asio::yield_context yield) override
Send a response to the client.
Definition WsConnection.hpp:146
void setTimeout(std::chrono::steady_clock::duration newTimeout) override
Get the timeout for send, receive, and close operations. For WebSocket connections,...
Definition WsConnection.hpp:134
bool wasUpgraded() const override
Whether the connection was upgraded. Upgraded connections are websocket connections.
Definition WsConnection.hpp:122
Overload set for lambdas.
Definition OverloadSet.hpp:30