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>
63 read(boost::asio::yield_context yield, std::optional<std::chrono::steady_clock::duration> timeout = std::nullopt)
64 override
65 {
66 boost::beast::error_code errorCode;
67 boost::beast::flat_buffer buffer;
68
69 auto operation = [&](auto&& token) { ws_.async_read(buffer, token); };
70 if (timeout) {
71 errorCode = util::withTimeout(operation, yield[errorCode], *timeout);
72 } else {
73 operation(yield[errorCode]);
74 }
75
76 if (errorCode)
77 return std::unexpected{RequestError{"Read error", errorCode}};
78
79 return boost::beast::buffers_to_string(std::move(buffer).data());
80 }
81
82 std::optional<RequestError>
84 std::string const& message,
85 boost::asio::yield_context yield,
86 std::optional<std::chrono::steady_clock::duration> timeout = std::nullopt
87 ) override
88 {
89 boost::beast::error_code errorCode;
90 auto operation = [&](auto&& token) { ws_.async_write(boost::asio::buffer(message), token); };
91 if (timeout) {
92 errorCode = util::withTimeout(operation, yield, *timeout);
93 } else {
94 operation(yield[errorCode]);
95 }
96
97 if (errorCode)
98 return RequestError{"Write error", errorCode};
99
100 return std::nullopt;
101 }
102
103 std::optional<RequestError>
104 close(boost::asio::yield_context yield, std::chrono::steady_clock::duration const timeout = kDEFAULT_TIMEOUT)
105 override
106 {
107 // Set the timeout for closing the connection
108 boost::beast::websocket::stream_base::timeout wsTimeout{};
109 ws_.get_option(wsTimeout);
110 wsTimeout.handshake_timeout = timeout;
111 ws_.set_option(wsTimeout);
112
113 boost::beast::error_code errorCode;
114 ws_.async_close(boost::beast::websocket::close_code::normal, yield[errorCode]);
115 if (errorCode)
116 return RequestError{"Close error", errorCode};
117 return std::nullopt;
118 }
119};
120
121using PlainWsConnection = WsConnectionImpl<boost::beast::websocket::stream<boost::beast::tcp_stream>>;
122using SslWsConnection =
123 WsConnectionImpl<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>>;
124
125} // 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:104
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:83
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