JSON-RPC 2.0
JSON-RPC 2.0 Modern C++ Library
Loading...
Searching...
No Matches
jsonrpc::transport::SocketTransport Class Reference

#include <socket_transport.hpp>

Inheritance diagram for jsonrpc::transport::SocketTransport:
Collaboration diagram for jsonrpc::transport::SocketTransport:

Public Member Functions

 SocketTransport (asio::any_io_executor executor, std::string address, uint16_t port, bool is_server, std::shared_ptr< spdlog::logger > logger=nullptr)
 
 ~SocketTransport () override
 
 SocketTransport (const SocketTransport &)=delete
 
auto operator= (const SocketTransport &) -> SocketTransport &=delete
 
 SocketTransport (SocketTransport &&)=delete
 
auto operator= (SocketTransport &&) -> SocketTransport &=delete
 
auto Start () -> asio::awaitable< std::expected< void, error::RpcError > > override
 
auto Close () -> asio::awaitable< std::expected< void, error::RpcError > > override
 
auto CloseNow () -> void override
 
auto SendMessage (std::string message) -> asio::awaitable< std::expected< void, error::RpcError > > override
 
auto ReceiveMessage () -> asio::awaitable< std::expected< std::string, error::RpcError > > override
 
- Public Member Functions inherited from jsonrpc::transport::Transport
 Transport (asio::any_io_executor executor, std::shared_ptr< spdlog::logger > logger=nullptr)
 
 Transport (const Transport &)=delete
 
 Transport (Transport &&)=delete
 
auto operator= (const Transport &) -> Transport &=delete
 
auto operator= (Transport &&) -> Transport &=delete
 
virtual ~Transport ()=default
 
auto GetExecutor () const -> asio::any_io_executor
 
auto GetStrand () -> asio::strand< asio::any_io_executor > &
 

Additional Inherited Members

- Protected Member Functions inherited from jsonrpc::transport::Transport
auto Logger () -> std::shared_ptr< spdlog::logger >
 

Detailed Description

Definition at line 14 of file socket_transport.hpp.

Constructor & Destructor Documentation

◆ SocketTransport() [1/3]

jsonrpc::transport::SocketTransport::SocketTransport ( asio::any_io_executor executor,
std::string address,
uint16_t port,
bool is_server,
std::shared_ptr< spdlog::logger > logger = nullptr )

Definition at line 11 of file socket_transport.cpp.

14 : Transport(std::move(executor), logger),
15 socket_(GetExecutor()),
16 address_(std::move(address)),
17 port_(port),
18 is_server_(is_server),
19 read_buffer_() {
20}
Transport(asio::any_io_executor executor, std::shared_ptr< spdlog::logger > logger=nullptr)
Definition transport.hpp:15
auto GetExecutor() const -> asio::any_io_executor
Definition transport.hpp:45

◆ ~SocketTransport()

jsonrpc::transport::SocketTransport::~SocketTransport ( )
override

Definition at line 22 of file socket_transport.cpp.

22 {
23 if (!is_closed_) {
24 Logger()->debug("SocketTransport destructor triggering CloseNow()");
25 try {
26 CloseNow();
27 } catch (const std::exception &e) {
28 Logger()->error("SocketTransport destructor error: {}", e.what());
29 }
30 }
31}
auto Logger() -> std::shared_ptr< spdlog::logger >
Definition transport.hpp:54

References CloseNow(), and jsonrpc::transport::Transport::Logger().

Here is the call graph for this function:

◆ SocketTransport() [2/3]

jsonrpc::transport::SocketTransport::SocketTransport ( const SocketTransport & )
delete

◆ SocketTransport() [3/3]

jsonrpc::transport::SocketTransport::SocketTransport ( SocketTransport && )
delete

Member Function Documentation

◆ Close()

auto jsonrpc::transport::SocketTransport::Close ( ) -> asio::awaitable<std::expected<void, error::RpcError>>
overridevirtual

Implements jsonrpc::transport::Transport.

Definition at line 81 of file socket_transport.cpp.

82 {
83 Logger()->debug("SocketTransport closing");
84 co_await asio::post(GetStrand(), asio::use_awaitable);
85
86 if (is_closed_) {
87 Logger()->debug("SocketTransport already closed");
88 co_return std::expected<void, error::RpcError>{};
89 }
90
91 is_closed_ = true;
92 is_connected_ = false;
93
94 // Clear the message queue
95 send_queue_.clear();
96
97 Logger()->debug("SocketTransport closing");
98
99 // Cancel and close the socket safely
100 std::error_code ec;
101 if (socket_.is_open()) {
102 socket_.cancel(ec);
103 if (ec) {
104 Logger()->warn(
105 "SocketTransport error canceling socket: {}", ec.message());
106 }
107 socket_.close(ec);
108 if (ec) {
109 Logger()->warn("SocketTransport error closing socket: {}", ec.message());
110 }
111 }
112
113 // Clean up acceptor if this is a server
114 if (is_server_ && acceptor_) {
115 acceptor_->cancel(ec);
116 if (ec) {
117 Logger()->warn(
118 "SocketTransport error canceling acceptor: {}", ec.message());
119 }
120 acceptor_->close(ec);
121 if (ec) {
122 Logger()->warn(
123 "SocketTransport error closing acceptor: {}", ec.message());
124 }
125 }
126
127 Logger()->debug("SocketTransport closed");
128 co_return Ok();
129}
auto GetStrand() -> asio::strand< asio::any_io_executor > &
Definition transport.hpp:49

◆ CloseNow()

void jsonrpc::transport::SocketTransport::CloseNow ( ) -> void
overridevirtual

Implements jsonrpc::transport::Transport.

Definition at line 131 of file socket_transport.cpp.

131 {
132 is_closed_ = true;
133 is_connected_ = false;
134
135 // Clear the message queue
136 send_queue_.clear();
137
138 auto try_close_socket = [&]() {
139 if (!socket_.is_open()) {
140 return;
141 }
142 Logger()->debug("SocketTransport closing socket synchronously");
143
144 std::error_code ec;
145 socket_.cancel();
146 socket_.close(ec);
147 if (ec) {
148 Logger()->warn("SocketTransport error closing socket: {}", ec.message());
149 }
150 };
151
152 auto try_close_acceptor = [&]() {
153 if (!is_server_ || !acceptor_ || !acceptor_->is_open()) {
154 return;
155 }
156 Logger()->debug("SocketTransport closing acceptor synchronously");
157
158 std::error_code ec;
159 acceptor_->cancel();
160 acceptor_->close(ec);
161 if (ec) {
162 Logger()->warn(
163 "SocketTransport error closing acceptor: {}", ec.message());
164 }
165 };
166
167 try {
168 try_close_socket();
169 try_close_acceptor();
170 } catch (const std::exception &e) {
171 Logger()->error("SocketTransport error during CloseNow(): {}", e.what());
172 }
173}

References jsonrpc::transport::Transport::Logger().

Referenced by ~SocketTransport().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ operator=() [1/2]

auto jsonrpc::transport::SocketTransport::operator= ( const SocketTransport & ) -> SocketTransport &=delete
delete

◆ operator=() [2/2]

auto jsonrpc::transport::SocketTransport::operator= ( SocketTransport && ) -> SocketTransport &=delete
delete

◆ ReceiveMessage()

auto jsonrpc::transport::SocketTransport::ReceiveMessage ( ) -> asio::awaitable<std::expected<std::string, error::RpcError>>
overridevirtual

Implements jsonrpc::transport::Transport.

Definition at line 251 of file socket_transport.cpp.

252 {
253 co_await asio::post(GetStrand(), asio::use_awaitable);
254
255 if (is_closed_) {
256 Logger()->warn(
257 "SocketTransport ReceiveMessage() called after transport was closed");
259 RpcErrorCode::kTransportError,
260 "ReceiveMessage() called after transport was closed");
261 }
262
263 if (!is_started_) {
265 RpcErrorCode::kTransportError,
266 "Transport not started before receiving message");
267 }
268
269 if (!socket_.is_open()) {
270 Logger()->warn("SocketTransport ReceiveMessage() socket not open");
272 RpcErrorCode::kTransportError, "Socket not open in ReceiveMessage()");
273 }
274
275 message_buffer_.clear();
276
277 std::error_code ec;
278 size_t bytes_read = co_await socket_.async_read_some(
279 asio::buffer(read_buffer_),
280 asio::redirect_error(asio::use_awaitable, ec));
281
282 if (ec) {
283 if (ec == asio::error::eof) {
284 Logger()->debug(
285 "SocketTransport EOF received, connection closed by peer");
286 is_connected_ = false;
288 RpcErrorCode::kTransportError, "Connection closed by peer");
289 } else if (ec == asio::error::operation_aborted) {
290 Logger()->debug("SocketTransport Read operation aborted");
292 RpcErrorCode::kTransportError, "Receive operation aborted");
293 } else {
294 Logger()->error(
295 "SocketTransport ASIO error in ReceiveMessage(): {}", ec.message());
297 RpcErrorCode::kTransportError, "Receive error: " + ec.message());
298 }
299 }
300
301 if (bytes_read == 0) {
303 RpcErrorCode::kTransportError, "Connection closed by peer (no data)");
304 }
305
306 message_buffer_.append(read_buffer_.data(), bytes_read);
307 Logger()->debug("SocketTransport received {} bytes", bytes_read);
308 co_return std::move(message_buffer_);
309}
static auto UnexpectedFromCode(RpcErrorCode code, std::string message="") -> std::unexpected< RpcError >
Definition error.hpp:101

References jsonrpc::error::RpcError::UnexpectedFromCode().

Here is the call graph for this function:

◆ SendMessage()

auto jsonrpc::transport::SocketTransport::SendMessage ( std::string message) -> asio::awaitable<std::expected<void, error::RpcError>>
overridevirtual

Implements jsonrpc::transport::Transport.

Definition at line 179 of file socket_transport.cpp.

180 {
181 co_await asio::post(GetStrand(), asio::use_awaitable);
182
183 if (is_closed_) {
185 RpcErrorCode::kTransportError,
186 "SendMessage() called on closed transport");
187 }
188
189 if (!is_started_) {
191 RpcErrorCode::kTransportError,
192 "Transport not started before sending message");
193 }
194
195 if (!socket_.is_open()) {
197 RpcErrorCode::kTransportError, "Socket not open in SendMessage()");
198 }
199
200 Logger()->debug("Queuing {} bytes to send to socket", message.size());
201 send_queue_.push_back(std::move(message));
202
203 // If there's no active sending task, start one
204 if (!sending_.exchange(true)) {
205 asio::co_spawn(GetStrand(), SendMessageLoop(), asio::detached);
206 }
207
208 co_return Ok();
209}

References jsonrpc::error::RpcError::UnexpectedFromCode().

Here is the call graph for this function:

◆ Start()

auto jsonrpc::transport::SocketTransport::Start ( ) -> asio::awaitable<std::expected<void, error::RpcError>>
overridevirtual

Implements jsonrpc::transport::Transport.

Definition at line 33 of file socket_transport.cpp.

34 {
35 Logger()->debug("SocketTransport starting");
36 co_await asio::post(GetStrand(), asio::use_awaitable);
37
38 if (is_started_) {
39 Logger()->debug("SocketTransport already started");
41 RpcErrorCode::kTransportError, "SocketTransport already started");
42 }
43
44 if (is_closed_) {
45 Logger()->error("SocketTransport cannot start a closed transport");
47 RpcErrorCode::kTransportError, "Cannot start a closed transport");
48 }
49
50 std::expected<void, error::RpcError> result;
51
52 if (is_server_) {
53 Logger()->debug(
54 "SocketTransport starting server at {}:{}", address_, port_);
55 result = co_await BindAndListen();
56 if (!result) {
57 Logger()->error(
58 "SocketTransport error starting server: {}",
59 result.error().Message());
60 co_return result;
61 }
62 } else {
63 Logger()->debug(
64 "Connecting SocketTransport client to {}:{}", address_, port_);
65 result = co_await Connect();
66 if (!result) {
67 Logger()->error(
68 "SocketTransport error connecting client: {}",
69 result.error().Message());
70 co_return result;
71 }
72 Logger()->debug(
73 "SocketTransport client connected to {}:{}", address_, port_);
74 }
75
76 is_started_ = true;
77 Logger()->debug("SocketTransport successfully started");
78 co_return Ok();
79}

References jsonrpc::error::RpcError::UnexpectedFromCode().

Here is the call graph for this function:

The documentation for this class was generated from the following files: