Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
WsConnectionImpl.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2024, the clio developers.
5
6 Permission to use, copy, modify, and distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#pragma once
21
22#include "util/WithTimeout.hpp"
23#include "util/requests/Types.hpp"
24#include "util/requests/WsConnection.hpp"
25
26#include <boost/asio/associated_executor.hpp>
27#include <boost/asio/bind_cancellation_slot.hpp>
28#include <boost/asio/buffer.hpp>
29#include <boost/asio/cancellation_signal.hpp>
30#include <boost/asio/cancellation_type.hpp>
31#include <boost/asio/spawn.hpp>
32#include <boost/asio/steady_timer.hpp>
33#include <boost/beast/core/buffers_to_string.hpp>
34#include <boost/beast/core/error.hpp>
35#include <boost/beast/core/flat_buffer.hpp>
36#include <boost/beast/core/tcp_stream.hpp>
37#include <boost/beast/ssl/ssl_stream.hpp>
38#include <boost/beast/websocket/rfc6455.hpp>
39#include <boost/beast/websocket/stream.hpp>
40#include <boost/beast/websocket/stream_base.hpp>
41#include <boost/system/errc.hpp>
42
43#include <atomic>
44#include <chrono>
45#include <expected>
46#include <memory>
47#include <optional>
48#include <string>
49#include <utility>
50
51namespace util::requests::impl {
52
53template <typename StreamType>
55 StreamType ws_;
56
57public:
58 explicit WsConnectionImpl(StreamType ws) : ws_(std::move(ws))
59 {
60 }
61
62 std::expected<std::string, RequestError>
64 boost::asio::yield_context yield,
65 std::optional<std::chrono::steady_clock::duration> timeout = std::nullopt
66 ) override
67 {
68 boost::beast::error_code errorCode;
69 boost::beast::flat_buffer buffer;
70
71 auto operation = [&](auto&& token) { ws_.async_read(buffer, token); };
72 if (timeout) {
73 errorCode = util::withTimeout(operation, yield[errorCode], *timeout);
74 } else {
75 operation(yield[errorCode]);
76 }
77
78 if (errorCode)
79 return std::unexpected{RequestError{"Read error", errorCode}};
80
81 return boost::beast::buffers_to_string(std::move(buffer).data());
82 }
83
84 std::optional<RequestError>
86 std::string const& message,
87 boost::asio::yield_context yield,
88 std::optional<std::chrono::steady_clock::duration> timeout = std::nullopt
89 ) override
90 {
91 boost::beast::error_code errorCode;
92 auto operation = [&](auto&& token) { ws_.async_write(boost::asio::buffer(message), token); };
93 if (timeout) {
94 errorCode = util::withTimeout(operation, yield, *timeout);
95 } else {
96 operation(yield[errorCode]);
97 }
98
99 if (errorCode)
100 return RequestError{"Write error", errorCode};
101
102 return std::nullopt;
103 }
104
105 std::optional<RequestError>
107 boost::asio::yield_context yield,
108 std::chrono::steady_clock::duration const timeout = kDEFAULT_TIMEOUT
109 ) override
110 {
111 // Set the timeout for closing the connection
112 boost::beast::websocket::stream_base::timeout wsTimeout{};
113 ws_.get_option(wsTimeout);
114 wsTimeout.handshake_timeout = timeout;
115 ws_.set_option(wsTimeout);
116
117 boost::beast::error_code errorCode;
118 ws_.async_close(boost::beast::websocket::close_code::normal, yield[errorCode]);
119 if (errorCode)
120 return RequestError{"Close error", errorCode};
121 return std::nullopt;
122 }
123};
124
125using PlainWsConnection = WsConnectionImpl<boost::beast::websocket::stream<boost::beast::tcp_stream>>;
126using SslWsConnection =
127 WsConnectionImpl<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>>;
128
129} // namespace util::requests::impl
Error type for HTTP requests.
Definition Types.hpp:34
Interface for WebSocket connections. It is used to hide SSL and plain connections behind the same int...
Definition WsConnection.hpp:48
static constexpr std::chrono::seconds kDEFAULT_TIMEOUT
Definition WsConnection.hpp:90
Definition WsConnectionImpl.hpp:54
std::optional< RequestError > close(boost::asio::yield_context yield, std::chrono::steady_clock::duration const timeout=kDEFAULT_TIMEOUT) override
Close the WebSocket.
Definition WsConnectionImpl.hpp:106
std::optional< RequestError > write(std::string const &message, boost::asio::yield_context yield, std::optional< std::chrono::steady_clock::duration > timeout=std::nullopt) override
Write a message to the WebSocket.
Definition WsConnectionImpl.hpp:85
std::expected< std::string, RequestError > read(boost::asio::yield_context yield, std::optional< std::chrono::steady_clock::duration > timeout=std::nullopt) override
Read a message from the WebSocket.
Definition WsConnectionImpl.hpp:63
This namespace implements the data access layer and related components.
Definition AmendmentCenter.cpp:70
boost::system::error_code withTimeout(Operation &&operation, boost::asio::yield_context yield, std::chrono::steady_clock::duration timeout)
Perform a coroutine operation with a timeout.
Definition WithTimeout.hpp:49