diff --git a/Makefile.am b/Makefile.am index c363389a3..ed4717a2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -343,6 +343,10 @@ include_bitcoin_network_impl_messages_rpc_HEADERS = \ include/bitcoin/network/impl/messages/rpc/broadcaster.ipp \ include/bitcoin/network/impl/messages/rpc/dispatcher.ipp +include_bitcoin_network_impl_protocolsdir = ${includedir}/bitcoin/network/impl/protocols +include_bitcoin_network_impl_protocols_HEADERS = \ + include/bitcoin/network/impl/protocols/protocol_rpc.ipp + include_bitcoin_network_interfacesdir = ${includedir}/bitcoin/network/interfaces include_bitcoin_network_interfaces_HEADERS = \ include/bitcoin/network/interfaces/http.hpp \ diff --git a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj index 3d395caff..88dc1d7c2 100644 --- a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj @@ -381,6 +381,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters index 742e702ee..9e7c2d1f3 100644 --- a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters @@ -35,7 +35,7 @@ {564EB540-D6B6-425C-0000-0000000000C1} - {564EB540-D6B6-425C-0000-0000000000F1} + {564EB540-D6B6-425C-0000-000000000002} {564EB540-D6B6-425C-0000-0000000000D1} @@ -44,7 +44,10 @@ {564EB540-D6B6-425C-0000-0000000000E1} - {564EB540-D6B6-425C-0000-000000000002} + {564EB540-D6B6-425C-0000-000000000003} + + + {564EB540-D6B6-425C-0000-0000000000F1} {564EB540-D6B6-425C-0000-000000000006} @@ -56,25 +59,25 @@ {564EB540-D6B6-425C-0000-000000000008} - {564EB540-D6B6-425C-0000-000000000003} + {564EB540-D6B6-425C-0000-000000000004} - {564EB540-D6B6-425C-0000-000000000006} + {564EB540-D6B6-425C-0000-000000000007} - {564EB540-D6B6-425C-0000-000000000004} + {564EB540-D6B6-425C-0000-000000000005} - {564EB540-D6B6-425C-0000-000000000007} + {564EB540-D6B6-425C-0000-000000000008} - {564EB540-D6B6-425C-0000-000000000008} + {564EB540-D6B6-425C-0000-000000000009} - {564EB540-D6B6-425C-0000-000000000005} + {564EB540-D6B6-425C-0000-000000000006} - {564EB540-D6B6-425C-0000-000000000009} + {564EB540-D6B6-425C-0000-000000000010} {564EB540-D6B6-425C-0000-000000000009} @@ -86,7 +89,7 @@ {564EB540-D6B6-425C-0000-0000000000A1} - {564EB540-D6B6-425C-0000-000000000010} + {564EB540-D6B6-425C-0000-000000000011} {564EB540-D6B6-425C-0000-000000000000} @@ -892,6 +895,9 @@ include\bitcoin\network\impl\messages\rpc + + include\bitcoin\network\impl\protocols + diff --git a/include/bitcoin/network/channels/channel_rpc.hpp b/include/bitcoin/network/channels/channel_rpc.hpp index 93509b9a2..921c8a17e 100644 --- a/include/bitcoin/network/channels/channel_rpc.hpp +++ b/include/bitcoin/network/channels/channel_rpc.hpp @@ -57,18 +57,20 @@ class channel_rpc { } - /// Serialize and write response to client (requires strand). - /// Completion handler is always invoked on the channel strand. - inline void send(rpc::response_t&& message, size_t size_hint, - result_handler&& handler) NOEXCEPT; + /// Public senders, rpc version and identity added to responses. + inline void send_code(const code& ec) NOEXCEPT; + inline void send_error(rpc::result_t&& error) NOEXCEPT; + inline void send_result(rpc::value_t&& result, size_t size_hint) NOEXCEPT; /// Resume reading from the socket (requires strand). inline void resume() NOEXCEPT override; - /// Must call after successful message handling if no stop. - virtual inline void receive() NOEXCEPT; - protected: + /// Serialize and write response to client (requires strand). + /// Completion handler is always invoked on the channel strand. + inline void send(rpc::response_t&& message, size_t size_hint, + result_handler&& handler) NOEXCEPT; + /// Stranded handler invoked from stop(). inline void stopping(const code& ec) NOEXCEPT override; @@ -82,13 +84,21 @@ class channel_rpc virtual inline rpc::response_ptr assign_message(rpc::response_t&& message, size_t size_hint) NOEXCEPT; - /// Handlers. + /// Must call after successful message handling if no stop. + virtual inline void receive() NOEXCEPT; + + /// Handle incoming messages. virtual inline void handle_receive(const code& ec, size_t bytes, const rpc::request_cptr& request) NOEXCEPT; + + /// Handle send complation, handler must invoke receive() unless stopping. virtual inline void handle_send(const code& ec, size_t bytes, const rpc::response_cptr& response, const result_handler& handler) NOEXCEPT; + /// Invoked upon handle_send completion to restart receive(). + virtual void handle_complete(const code& ec) NOEXCEPT; + private: void log_message(const rpc::request& request, size_t bytes) const NOEXCEPT; @@ -96,6 +106,8 @@ class channel_rpc size_t bytes) const NOEXCEPT; // These are protected by strand. + rpc::version version_; + rpc::id_option identity_; http::flat_buffer_ptr response_buffer_; http::flat_buffer request_buffer_; dispatcher dispatcher_{}; diff --git a/include/bitcoin/network/impl/channels/channel_rpc.ipp b/include/bitcoin/network/impl/channels/channel_rpc.ipp index 89d9290d0..d0d807f0f 100644 --- a/include/bitcoin/network/impl/channels/channel_rpc.ipp +++ b/include/bitcoin/network/impl/channels/channel_rpc.ipp @@ -101,6 +101,10 @@ inline void CLASS::handle_receive(const code& ec, size_t bytes, return; } + // Save response state. + identity_ = request->message.id; + version_ = request->message.jsonrpc; + reading_ = false; log_message(*request, bytes); dispatch(request); @@ -109,6 +113,7 @@ inline void CLASS::handle_receive(const code& ec, size_t bytes, TEMPLATE inline void CLASS::dispatch(const rpc::request_cptr& request) NOEXCEPT { + BC_ASSERT(stranded()); if (const auto code = dispatcher_.notify(request->message)) stop(code); } @@ -116,22 +121,50 @@ inline void CLASS::dispatch(const rpc::request_cptr& request) NOEXCEPT TEMPLATE inline http::flat_buffer& CLASS::request_buffer() NOEXCEPT { + BC_ASSERT(stranded()); return request_buffer_; } // Send. // ---------------------------------------------------------------------------- +TEMPLATE +void CLASS::send_code(const code& ec) NOEXCEPT +{ + BC_ASSERT(stranded()); + send_error({ .code = ec.value(), .message = ec.message() }); +} + +TEMPLATE +void CLASS::send_error(rpc::result_t&& error) NOEXCEPT +{ + BC_ASSERT(stranded()); + using namespace std::placeholders; + send({ .jsonrpc = version_, .id = identity_, .error = std::move(error) }, + two * error.message.size(), std::bind(&CLASS::handle_complete, + shared_from_base(), _1)); +} + +TEMPLATE +void CLASS::send_result(rpc::value_t&& result, size_t size_hint) NOEXCEPT +{ + BC_ASSERT(stranded()); + using namespace std::placeholders; + send({ .jsonrpc = version_, .id = identity_, .result = std::move(result) }, + size_hint, std::bind(&CLASS::handle_complete, + shared_from_base(), _1)); +} + +// protected TEMPLATE inline void CLASS::send(rpc::response_t&& model, size_t size_hint, result_handler&& handler) NOEXCEPT { BC_ASSERT(stranded()); - using namespace std::placeholders; const auto out = assign_message(std::move(model), size_hint); - count_handler complete = std::bind(&channel_rpc::handle_send, - shared_from_base(), _1, _2, out, std::move(handler)); + count_handler complete = std::bind(&CLASS::handle_send, + shared_from_base(), _1, _2, out, std::move(handler)); if (!out) { @@ -142,22 +175,35 @@ inline void CLASS::send(rpc::response_t&& model, size_t size_hint, write(*out, std::move(complete)); } +// protected TEMPLATE inline void CLASS::handle_send(const code& ec, size_t bytes, const rpc::response_cptr& response, const result_handler& handler) NOEXCEPT { - if (ec) - stop(ec); - + BC_ASSERT(stranded()); + if (ec) stop(ec); log_message(*response, bytes); handler(ec); } +// protected +TEMPLATE +void CLASS::handle_complete(const code&) NOEXCEPT +{ + BC_ASSERT(stranded()); + if (stopped()) + return; + + // Continue read loop. + receive(); +} + // private TEMPLATE inline rpc::response_ptr CLASS::assign_message(rpc::response_t&& message, size_t size_hint) NOEXCEPT { + BC_ASSERT(stranded()); response_buffer_->max_size(size_hint); const auto ptr = system::to_shared(); ptr->message = std::move(message); diff --git a/include/bitcoin/network/impl/protocols/protocol_rpc.ipp b/include/bitcoin/network/impl/protocols/protocol_rpc.ipp new file mode 100644 index 000000000..7d92f2b21 --- /dev/null +++ b/include/bitcoin/network/impl/protocols/protocol_rpc.ipp @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_NETWORK_PROTOCOL_RPC_IPP +#define LIBBITCOIN_NETWORK_PROTOCOL_RPC_IPP + +#include +#include + +namespace libbitcoin { +namespace network { + +TEMPLATE +inline void CLASS::send_code(const code& ec) NOEXCEPT +{ + channel_->send_code(ec); +} + +TEMPLATE +inline void CLASS::send_error(rpc::result_t&& error) NOEXCEPT +{ + channel_->send_error(std::move(error)); +} + +TEMPLATE +inline void CLASS::send_result(rpc::value_t&& result, + size_t size_hint) NOEXCEPT +{ + channel_->send_result(std::move(result), size_hint); +} + +} // namespace network +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/network/protocols/protocol_rpc.hpp b/include/bitcoin/network/protocols/protocol_rpc.hpp index 8d0ee94d3..b9e9a7778 100644 --- a/include/bitcoin/network/protocols/protocol_rpc.hpp +++ b/include/bitcoin/network/protocols/protocol_rpc.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace libbitcoin { @@ -33,8 +34,7 @@ class protocol_rpc : public protocol { public: - typedef std::shared_ptr ptr; - using protocol_t = protocol_rpc; + typedef std::shared_ptr> ptr; using channel_t = channel_rpc; using options_t = channel_t::options_t; @@ -48,67 +48,11 @@ class protocol_rpc DECLARE_SUBSCRIBE_CHANNEL() - template - inline void send(network::rpc::response_t&& message, size_t size_hint, - Method&& method, Args&&... args) NOEXCEPT - { - channel_->send(std::move(message), size_hint, - std::bind(std::forward(method), - shared_from_base(), std::forward(args)...)); - } - - // TODO: capture and correlate version/id. - inline void send_result(network::rpc::value_t&& value, - size_t size_hint) NOEXCEPT - { - using namespace network::rpc; - using namespace std::placeholders; - send( - { - .jsonrpc = version::v2, - .id = 42, - ////.error = {}, - .result = std::move(value) - }, - size_hint, &protocol_t::handle_complete, _1, error::success); - } - - // TODO: capture and correlate version/id. - inline void send_error(const code& reason) NOEXCEPT - { - using namespace network::rpc; - using namespace std::placeholders; - const auto size_hint = two * reason.message().size(); - send( - { - .jsonrpc = version::v2, - .id = 42, - .error = result_t - { - .code = reason.value(), - .message = reason.message() - } - ////.result = {} - }, - size_hint, &protocol_t::handle_complete, _1, reason); - } - - inline void handle_complete(const code& ec, const code& reason) NOEXCEPT - { - BC_ASSERT(stranded()); - - if (stopped(ec)) - return; - - if (reason) - { - stop(reason); - return; - } - - // Continue read loop. - channel_->receive(); - } + /// Senders (requires strand). + virtual inline void send_code(const code& ec) NOEXCEPT; + virtual inline void send_error(rpc::result_t&& error) NOEXCEPT; + virtual inline void send_result(rpc::value_t&& result, + size_t size_hint) NOEXCEPT; private: // This is mostly thread safe, and used in a thread safe manner. @@ -119,4 +63,12 @@ class protocol_rpc } // namespace network } // namespace libbitcoin +#define TEMPLATE template +#define CLASS protocol_rpc + +#include + +#undef CLASS +#undef TEMPLATE + #endif