diff --git a/Makefile.am b/Makefile.am index f7e0a1435..ecf96dd63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -111,9 +111,10 @@ src_libbitcoin_network_la_SOURCES = \ src/net/socket.cpp \ src/net/socket_connect.cpp \ src/net/socket_http.cpp \ + src/net/socket_p2p.cpp \ + src/net/socket_raw.cpp \ src/net/socket_rpc.cpp \ src/net/socket_stop.cpp \ - src/net/socket_tcp.cpp \ src/net/socket_wait.cpp \ src/net/socket_ws.cpp \ src/protocols/protocol.cpp \ diff --git a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj index 6f2a8dea7..f8f5c2729 100644 --- a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj @@ -205,9 +205,10 @@ + + - diff --git a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters index 96ef47f7c..341431d97 100644 --- a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters @@ -396,13 +396,16 @@ src\net - + src\net - + + src\net + + src\net - + src\net diff --git a/include/bitcoin/network/channels/channel_rpc.hpp b/include/bitcoin/network/channels/channel_rpc.hpp index 1f44372a7..a97ee278c 100644 --- a/include/bitcoin/network/channels/channel_rpc.hpp +++ b/include/bitcoin/network/channels/channel_rpc.hpp @@ -108,12 +108,7 @@ class channel_rpc inline void send(Message&& message, size_t size_hint, result_handler&& handler) NOEXCEPT; - /// Size and assign response_buffer_ (value type is json-rpc::json). - template - inline rpc::message_ptr assign_message(Message&& message, - size_t size_hint) NOEXCEPT; - - /// Handle send completion, invokes receive(). + /// Handle send completion, invokes receive() for non-notifications. template inline void handle_send(const code& ec, size_t bytes, const rpc::message_cptr& message, diff --git a/include/bitcoin/network/impl/channels/channel_rpc.ipp b/include/bitcoin/network/impl/channels/channel_rpc.ipp index 6d631c8f5..e5fa484f0 100644 --- a/include/bitcoin/network/impl/channels/channel_rpc.ipp +++ b/include/bitcoin/network/impl/channels/channel_rpc.ipp @@ -28,8 +28,6 @@ namespace libbitcoin { namespace network { -using namespace std::placeholders; - // Shared pointers required in handler parameters so closures control lifetime. BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) @@ -64,16 +62,24 @@ inline void CLASS::receive() NOEXCEPT { BC_ASSERT(stranded()); BC_ASSERT_MSG(!reading_, "already reading"); + using namespace std::placeholders; + using namespace system; if (stopped() || paused() || reading_) return; reading_ = true; - const auto in = system::to_shared(); - using namespace std::placeholders; + const auto in = emplace_shared + ( + // default json model, unused size_hint, unused serialization buffer. + json::json_value{}, + + // default incoming rpc message. + rpc::request_t{}, - // Electrum, allow params singleton to be accepted as array. - in->strict = false; + // !strict allows params singleton to be accepted as array (Electrum). + false + ); // Post handle_read to strand upon stop, error, or buffer full. read(request_buffer(), *in, @@ -146,34 +152,29 @@ inline http::flat_buffer& CLASS::request_buffer() NOEXCEPT // protected TEMPLATE template -inline void CLASS::send(Message&& model, size_t size_hint, +inline void CLASS::send(Message&& message, size_t size_hint, result_handler&& handler) NOEXCEPT { BC_ASSERT(stranded()); - const auto out = assign_message(std::move(model), size_hint); - count_handler complete = std::bind(&CLASS::handle_send, - shared_from_base(), _1, _2, out, std::move(handler)); + using namespace std::placeholders; + using namespace system; - if (!out) - { - complete(error::bad_alloc, {}); - return; - } - - write(*out, std::move(complete)); -} + // Templated message due to notification sending request_t. + const auto out = emplace_shared> + ( + // default json model, buffer size_hint, default serialization buffer. + json::json_value{ {}, size_hint, {} }, -// protected -TEMPLATE -template -inline rpc::message_ptr CLASS::assign_message(Message&& message, - size_t size_hint) NOEXCEPT -{ - BC_ASSERT(stranded()); - const auto ptr = system::to_shared>(); - ptr->message = std::move(message); - ptr->size_hint = size_hint; - return ptr; + // outgoing rpc message (request_t or response_t). + std::forward(message), + + // unused strict json-rpc. + true + ); + + // Write message to socket, capture its pointer for lifetime. + write(*out, std::bind(&CLASS::handle_send, + shared_from_base(), _1, _2, out, std::move(handler))); } // protected @@ -212,8 +213,7 @@ inline void CLASS::send_code(const code& ec, result_handler&& handler) NOEXCEPT { .code = ec.value(), .message = ec.message() - }, - std::move(handler)); + }, std::move(handler)); } TEMPLATE diff --git a/include/bitcoin/network/impl/protocols/protocol_rpc.ipp b/include/bitcoin/network/impl/protocols/protocol_rpc.ipp index ab56b483b..0269f0f5e 100644 --- a/include/bitcoin/network/impl/protocols/protocol_rpc.ipp +++ b/include/bitcoin/network/impl/protocols/protocol_rpc.ipp @@ -57,6 +57,7 @@ TEMPLATE inline void CLASS::send_notification(rpc::string_t&& method, rpc::params_t&& notification, size_t size_hint) NOEXCEPT { + using namespace std::placeholders; channel_->send_notification(std::move(method), std::move(notification), size_hint, std::bind(&CLASS::handle_send, shared_from_base(), _1)); diff --git a/include/bitcoin/network/messages/rpc/model.hpp b/include/bitcoin/network/messages/rpc/model.hpp index 0f5af49cb..b480d2f51 100644 --- a/include/bitcoin/network/messages/rpc/model.hpp +++ b/include/bitcoin/network/messages/rpc/model.hpp @@ -50,7 +50,7 @@ using json_t = boost::json::value; struct value_t { - /// 88 bytes (object_t). + /// 72 bytes (object_t, any_t). using inner_t = std::variant < /// json-rpc diff --git a/include/bitcoin/network/net/proxy.hpp b/include/bitcoin/network/net/proxy.hpp index 8afe68c87..004b612a4 100644 --- a/include/bitcoin/network/net/proxy.hpp +++ b/include/bitcoin/network/net/proxy.hpp @@ -122,26 +122,28 @@ class BCT_API proxy /// Cancel wait or any asynchronous read/write operation, handlers posted. virtual void cancel(result_handler&& handler) NOEXCEPT; - /// TCP (generic, p2p). + /// RAW (generic, variable size). /// ----------------------------------------------------------------------- - /// Read fixed-size TCP message from the remote endpoint into buffer. - virtual void read(const asio::mutable_buffer& buffer, + /// Read complete logical message for websockets (not for tcp). + /// Read available buffer from the socket, handler posted to socket strand. + virtual void read(http::flat_buffer& out, count_handler&& handler) NOEXCEPT; - /// Send a complete TCP message to the remote endpoint. - virtual void write(const asio::const_buffer& buffer, - count_handler&& handler) NOEXCEPT; + /// Binary or text mode applies to websockets (no-op for tcp). + /// Write the provided buffer to socket, handler posted to socket strand. + virtual void write(const asio::const_buffer& in, + count_handler&& handler, bool binary) NOEXCEPT; - /// WS (generic). + /// P2P (generic, fixed size). /// ----------------------------------------------------------------------- - /// Read full buffer from the websocket (post-upgrade). - virtual void ws_read(http::flat_buffer& out, + /// Read fixed-size TCP message from the remote endpoint into buffer. + virtual void read(const asio::mutable_buffer& buffer, count_handler&& handler) NOEXCEPT; - /// Write full buffer to the websocket (post-upgrade), specify binary/text. - virtual void ws_write(const asio::const_buffer& in, bool binary, + /// Write the provided buffer to socket, handler posted to socket strand. + virtual void write(const asio::const_buffer& buffer, count_handler&& handler) NOEXCEPT; /// RPC (TCP: electrum/stratum_v1, WS: btcd). @@ -175,13 +177,13 @@ class BCT_API proxy typedef std::deque queue; // For write buffering. - void do_tcp_write(const asio::const_buffer& payload, + void do_raw_write(const asio::const_buffer& payload, bool binary, const count_handler& handler) NOEXCEPT; - void do_ws_write(const asio::const_buffer& in, bool binary, + void do_p2p_write(const asio::const_buffer& payload, const count_handler& handler) NOEXCEPT; - void do_rpc_write_response(const ref& response, + void do_response_write(const ref& response, const count_handler& handler) NOEXCEPT; - void do_rpc_write_notification(const ref& notification, + void do_notification_write(const ref& notification, const count_handler& handler) NOEXCEPT; void do_subscribe_stop(const result_handler& handler, const result_handler& complete) NOEXCEPT; diff --git a/include/bitcoin/network/net/socket.hpp b/include/bitcoin/network/net/socket.hpp index 53580d217..ed75b1f8f 100644 --- a/include/bitcoin/network/net/socket.hpp +++ b/include/bitcoin/network/net/socket.hpp @@ -110,26 +110,28 @@ class BCT_API socket virtual void connect(const asio::endpoints& range, result_handler&& handler) NOEXCEPT; - /// TCP (generic, p2p). + /// RAW (generic, variable size). /// ----------------------------------------------------------------------- - /// Read full buffer from the socket, handler posted to socket strand. - virtual void tcp_read(const asio::mutable_buffer& out, + /// Read complete logical message for websockets (not for tcp). + /// Read available buffer from the socket, handler posted to socket strand. + virtual void raw_read(http::flat_buffer& out, count_handler&& handler) NOEXCEPT; - /// Write full buffer to the socket, handler posted to socket strand. - virtual void tcp_write(const asio::const_buffer& in, - count_handler&& handler) NOEXCEPT; + /// Binary or text mode applies to websockets (no-op for tcp). + /// Write the provided buffer to socket, handler posted to socket strand. + virtual void raw_write(const asio::const_buffer& in, + count_handler&& handler, bool binary=true) NOEXCEPT; - /// WS (generic). + /// P2P (generic, fixed size). /// ----------------------------------------------------------------------- - /// Read full buffer from the websocket (post-upgrade). - virtual void ws_read(http::flat_buffer& out, + /// Read fixed buffer from the socket, handler posted to socket strand. + virtual void p2p_read(const asio::mutable_buffer& out, count_handler&& handler) NOEXCEPT; - /// Write full buffer to the websocket (post-upgrade), specify binary/text. - virtual void ws_write(const asio::const_buffer& in, bool binary, + /// Write the provided buffer to socket, handler posted to socket strand. + virtual void p2p_write(const asio::const_buffer& in, count_handler&& handler) NOEXCEPT; /// RPC (TCP: electrum/stratum_v1, WS: btcd). @@ -219,10 +221,17 @@ class BCT_API socket tcp_t get_tcp() NOEXCEPT; asio::socket& get_base() NOEXCEPT; asio::ssl::socket& get_ssl() NOEXCEPT; + + /// Variant (ws vs. tcp) helpers (protected by strand). + /// ----------------------------------------------------------------------- + void async_read(http::flat_buffer& buffer, const count_handler& handler, + size_t size=rpc::writer::default_buffer) NOEXCEPT; + void async_read(const asio::mutable_buffer& buffer, + const count_handler& handler) NOEXCEPT; void async_read_some(const asio::mutable_buffer& buffer, - count_handler&& handler) NOEXCEPT; - void async_write(const asio::const_buffer& buffer, - count_handler&& handler) NOEXCEPT; + const count_handler& handler) NOEXCEPT; + void async_write(const asio::const_buffer& buffer, bool binary, + const count_handler& handler) NOEXCEPT; private: using http_parser = boost::beast::http::request_parser; @@ -278,6 +287,9 @@ class BCT_API socket void do_ws_stop() NOEXCEPT; void do_ssl_stop() NOEXCEPT; + // config + void do_ws_event(ws::frame_type kind, const std::string& data) NOEXCEPT; + // wait void do_wait(const result_handler& handler) NOEXCEPT; void do_cancel(const result_handler& handler) NOEXCEPT; @@ -287,19 +299,15 @@ class BCT_API socket const result_handler& handler) NOEXCEPT; void do_handshake(const result_handler& handler) NOEXCEPT; - // tcp (generic) - void do_tcp_read(const asio::mutable_buffer& out, + // raw (tcp/ws) + void do_raw_read(ref out, const count_handler& handler) NOEXCEPT; - void do_tcp_write(const asio::const_buffer& in, + void do_raw_write(const asio::const_buffer& in, bool binary, const count_handler& handler) NOEXCEPT; - // ws (generic) - void do_ws_read(ref out, + // p2p (tcp/ws) + void do_p2p_read(const asio::mutable_buffer& out, const count_handler& handler) NOEXCEPT; - void do_ws_write(const asio::const_buffer& in, bool raw, - const count_handler& handler) NOEXCEPT; - void do_ws_event(ws::frame_type kind, - const std::string_view& data) NOEXCEPT; // rpc (tcp/ws) void do_rpc_read(boost_code ec, size_t total, @@ -309,15 +317,13 @@ class BCT_API socket void do_rpc_notify(boost_code ec, size_t total, const notify_rpc::ptr& out, const count_handler& handler) NOEXCEPT; - // http (generic) + // http void do_http_read(ref buffer, const ref& request, const count_handler& handler) NOEXCEPT; void do_http_write(const ref& response, const count_handler& handler) NOEXCEPT; - code set_websocket(const http::request& request) NOEXCEPT; - // handle // ------------------------------------------------------------------------ @@ -325,6 +331,10 @@ class BCT_API socket void handle_ws_close(const boost_code& ec) NOEXCEPT; void handle_ssl_close(const boost_code& ec) NOEXCEPT; + // config + void handle_ws_event(ws::frame_type kind, + const std::string& data) NOEXCEPT; + // wait void handle_wait(const boost_code& ec, const result_handler& handler) NOEXCEPT; @@ -337,8 +347,11 @@ class BCT_API socket void handle_handshake(const boost_code& ec, const result_handler& handler) NOEXCEPT; - // tcp (generic) - void handle_tcp(const boost_code& ec, size_t size, + // raw/p2p/read/write (tcp/ws) + void handle_async(const boost_code& ec, size_t size, + const count_handler& handler,const std::string& operation) NOEXCEPT; + void handle_async_read(const boost_code& ec, size_t size, + const asio::mutable_buffer& buffer, const http::flat_buffer_ptr& flat, const count_handler& handler) NOEXCEPT; // rpc (tcp/ws) @@ -349,20 +362,17 @@ class BCT_API socket void handle_rpc_notify(boost_code ec, size_t size, size_t total, const notify_rpc::ptr& out, const count_handler& handler) NOEXCEPT; - // ws (generic) - void handle_ws(const boost_code& ec, size_t size, - const count_handler& handler) NOEXCEPT; - void handle_ws_event(ws::frame_type kind, - const std::string& data) NOEXCEPT; - // http (generic/rpc) void handle_http_read(const boost_code& ec, size_t size, - const ref& request, - const http_parser_ptr& parser, const count_handler& handler) NOEXCEPT; + const ref& request, const http_parser_ptr& parser, + const count_handler& handler) NOEXCEPT; void handle_http_write(const boost_code& ec, size_t size, const count_handler& handler) NOEXCEPT; - // logging + // utility + // ------------------------------------------------------------------------ + + code set_websocket(const http::request& request) NOEXCEPT; void logx(const std::string& context, const boost_code& ec) const NOEXCEPT; protected: diff --git a/src/channels/channel_ws.cpp b/src/channels/channel_ws.cpp index 1b30043dc..066fd4327 100644 --- a/src/channels/channel_ws.cpp +++ b/src/channels/channel_ws.cpp @@ -46,7 +46,7 @@ void channel_ws::receive() NOEXCEPT return; } - ws_read(request_buffer(), + read(request_buffer(), std::bind(&channel_ws::handle_receive_ws, shared_from_base(), _1, _2)); } diff --git a/src/net/connector_socks.cpp b/src/net/connector_socks.cpp index 1e07479d8..f41e43e36 100644 --- a/src/net/connector_socks.cpp +++ b/src/net/connector_socks.cpp @@ -177,7 +177,7 @@ void connector_socks::do_socks_greeting_write(const code& ec, // Start of socket strand sequence. // All socket operations are dispatched to its own strand, so this write // will be posted before invocation. This makes socket calls thread safe. - socket->tcp_write({ greeting->data(), greeting->size() }, + socket->p2p_write({ greeting->data(), greeting->size() }, std::bind(&connector_socks::handle_socks_greeting_write, shared_from_base(), _1, _2, socket, greeting)); @@ -202,7 +202,7 @@ void connector_socks::handle_socks_greeting_write(const code& ec, size_t size, const auto response = emplace_shared>(); - socket->tcp_read({ response->data(), response->size() }, + socket->p2p_read({ response->data(), response->size() }, std::bind(&connector_socks::handle_socks_method_read, shared_from_base(), _1, _2, socket, response)); @@ -273,7 +273,7 @@ void connector_socks::handle_socks_method_read(const code& ec, size_t size, *it++ = narrow_cast(password_length); it = std::copy(password.begin(), password.end(), it); - socket->tcp_write({ authenticator->data(), authenticator->size() }, + socket->p2p_write({ authenticator->data(), authenticator->size() }, std::bind(&connector_socks::handle_socks_authentication_write, shared_from_base(), _1, _2, socket, authenticator)); @@ -299,7 +299,7 @@ void connector_socks::handle_socks_authentication_write(const code& ec, const auto auth_res = emplace_shared>(); - socket->tcp_read({ auth_res->data(), auth_res->size() }, + socket->p2p_read({ auth_res->data(), auth_res->size() }, std::bind(&connector_socks::handle_socks_authentication_read, shared_from_base(), _1, _2, socket, auth_res)); @@ -404,7 +404,7 @@ void connector_socks::do_socks_connect_write( it = std::copy(port.begin(), port.end(), it); } - socket->tcp_write({ request->data(), request->size() }, + socket->p2p_write({ request->data(), request->size() }, std::bind(&connector_socks::handle_socks_connect_write, shared_from_base(), _1, _2, socket, request)); @@ -429,7 +429,7 @@ void connector_socks::handle_socks_connect_write(const code& ec, size_t size, const auto response = emplace_shared>(); - socket->tcp_read({ response->data(), response->size() }, + socket->p2p_read({ response->data(), response->size() }, std::bind(&connector_socks::handle_socks_response_read, shared_from_base(), _1, _2, socket, response)); @@ -473,7 +473,7 @@ void connector_socks::handle_socks_response_read(const code& ec, size_t size, // A version-4 IP address with length of 4 octets. const auto address = emplace_shared(4u + port_size); - socket->tcp_read({ address->data(), address->size() }, + socket->p2p_read({ address->data(), address->size() }, std::bind(&connector_socks::handle_socks_address_read, shared_from_base(), _1, _2, socket, address)); @@ -484,7 +484,7 @@ void connector_socks::handle_socks_response_read(const code& ec, size_t size, // A version-6 IP address with length of 16 octets. const auto address = emplace_shared(16u + port_size); - socket->tcp_read({ address->data(), address->size() }, + socket->p2p_read({ address->data(), address->size() }, std::bind(&connector_socks::handle_socks_address_read, shared_from_base(), _1, _2, socket, address)); @@ -497,7 +497,7 @@ void connector_socks::handle_socks_response_read(const code& ec, size_t size, // of name that follow (and excludes two byte length of the port). const auto length = emplace_shared>(); - socket->tcp_read({ length->data(), sizeof(uint8_t) }, + socket->p2p_read({ length->data(), sizeof(uint8_t) }, std::bind(&connector_socks::handle_socks_length_read, shared_from_base(), _1, _2, socket, length)); @@ -532,7 +532,7 @@ void connector_socks::handle_socks_length_read(const code& ec, size_t size, const auto length = host_length->front() + port_size; const auto address = emplace_shared(length); - socket->tcp_read({ address->data(), address->size() }, + socket->p2p_read({ address->data(), address->size() }, std::bind(&connector_socks::handle_socks_address_read, shared_from_base(), _1, _2, socket, address)); diff --git a/src/net/proxy_actions.cpp b/src/net/proxy_actions.cpp index 052cecca0..291c608b1 100644 --- a/src/net/proxy_actions.cpp +++ b/src/net/proxy_actions.cpp @@ -26,10 +26,7 @@ namespace libbitcoin { namespace network { -// Shared pointers required in handler parameters so closures control lifetime. BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) -BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) using namespace std::placeholders; @@ -46,21 +43,20 @@ void proxy::cancel(result_handler&& handler) NOEXCEPT socket_->cancel(std::move(handler)); } -// TCP (generic, p2p). +// RAW (generic, variable size). // ---------------------------------------------------------------------------- -void proxy::read(const asio::mutable_buffer& buffer, - count_handler&& handler) NOEXCEPT +void proxy::read(http::flat_buffer& out, count_handler&& handler) NOEXCEPT { do_reading(); - socket_->tcp_read(buffer, std::move(handler)); + socket_->raw_read(out, std::move(handler)); } -void proxy::write(const asio::const_buffer& buffer, - count_handler&& handler) NOEXCEPT +void proxy::write(const asio::const_buffer& in, count_handler&& handler, + bool binary) NOEXCEPT { - writer call = std::bind(&proxy::do_tcp_write, - shared_from_this(), buffer, std::move(handler)); + writer call = std::bind(&proxy::do_raw_write, + shared_from_this(), in, binary, std::move(handler)); boost::asio::dispatch(strand(), std::bind(&proxy::do_write, @@ -68,34 +64,44 @@ void proxy::write(const asio::const_buffer& buffer, } // private -void proxy::do_tcp_write(const asio::const_buffer& buffer, +void proxy::do_raw_write(const asio::const_buffer& payload, bool binary, const count_handler& handler) NOEXCEPT { - socket_->tcp_write({ buffer.data(), buffer.size() }, + socket_->raw_write({ payload.data(), payload.size() }, std::bind(&proxy::handle_write, - shared_from_this(), _1, _2, handler)); + shared_from_this(), _1, _2, handler), binary); } -// WS (generic). +// P2P (generic, fixed size). // ---------------------------------------------------------------------------- -void proxy::ws_read(http::flat_buffer& out, count_handler&& handler) NOEXCEPT +void proxy::read(const asio::mutable_buffer& out, + count_handler&& handler) NOEXCEPT { do_reading(); - socket_->ws_read(out, std::move(handler)); + socket_->p2p_read(out, std::move(handler)); } -void proxy::ws_write(const asio::const_buffer& in, bool binary, +void proxy::write(const asio::const_buffer& in, count_handler&& handler) NOEXCEPT { - writer call = std::bind(&proxy::do_ws_write, - shared_from_this(), in, binary, std::move(handler)); + writer call = std::bind(&proxy::do_p2p_write, + shared_from_this(), in, std::move(handler)); boost::asio::dispatch(strand(), std::bind(&proxy::do_write, shared_from_this(), std::move(call))); } +// private +void proxy::do_p2p_write(const asio::const_buffer& payload, + const count_handler& handler) NOEXCEPT +{ + socket_->p2p_write({ payload.data(), payload.size() }, + std::bind(&proxy::handle_write, + shared_from_this(), _1, _2, handler)); +} + // RPC (TCP: electrum/stratum_v1, WS: btcd). // ---------------------------------------------------------------------------- @@ -108,7 +114,7 @@ void proxy::read(http::flat_buffer& buffer, rpc::request& request, void proxy::write(rpc::response& response, count_handler&& handler) NOEXCEPT { - writer call = std::bind(&proxy::do_rpc_write_response, + writer call = std::bind(&proxy::do_response_write, shared_from_this(), std::ref(response), std::move(handler)); boost::asio::dispatch(strand(), @@ -118,7 +124,7 @@ void proxy::write(rpc::response& response, count_handler&& handler) NOEXCEPT void proxy::write(rpc::request& notification, count_handler&& handler) NOEXCEPT { - writer call = std::bind(&proxy::do_rpc_write_notification, + writer call = std::bind(&proxy::do_notification_write, shared_from_this(), std::ref(notification), std::move(handler)); boost::asio::dispatch(strand(), @@ -127,7 +133,7 @@ void proxy::write(rpc::request& notification, count_handler&& handler) NOEXCEPT } // private -void proxy::do_rpc_write_response(const ref& response, +void proxy::do_response_write(const ref& response, const count_handler& handler) NOEXCEPT { socket_->rpc_write(response.get(), @@ -136,7 +142,7 @@ void proxy::do_rpc_write_response(const ref& response, } // private -void proxy::do_rpc_write_notification(const ref& notification, +void proxy::do_notification_write(const ref& notification, const count_handler& handler) NOEXCEPT { socket_->rpc_notify(notification.get(), @@ -144,15 +150,6 @@ void proxy::do_rpc_write_notification(const ref& notification, shared_from_this(), _1, _2, handler)); } -// private -void proxy::do_ws_write(const asio::const_buffer& in, bool binary, - const count_handler& handler) NOEXCEPT -{ - socket_->ws_write(in, binary, - std::bind(&proxy::handle_write, - shared_from_this(), _1, _2, handler)); -} - // HTTP (generic/rpc). // ---------------------------------------------------------------------------- @@ -171,8 +168,6 @@ void proxy::write(http::response& response, socket_->http_write(response, std::move(handler)); } -BC_POP_WARNING() -BC_POP_WARNING() BC_POP_WARNING() } // namespace network diff --git a/src/net/socket.cpp b/src/net/socket.cpp index 8913bc96c..27213617a 100644 --- a/src/net/socket.cpp +++ b/src/net/socket.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -37,7 +38,11 @@ namespace libbitcoin { namespace network { using namespace system; +using namespace std::placeholders; +// Shared pointers required in handler parameters so closures control lifetime. +BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) +BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) // Construct. @@ -256,38 +261,164 @@ asio::ssl::socket& socket::get_ssl() NOEXCEPT }, socket_); } -// Allows for full generalization between tcp and websockets. +// Variant (ws vs. tcp) helpers. +// ---------------------------------------------------------------------------- +// protected + +void socket::async_write(const asio::const_buffer& buffer, bool binary, + const count_handler& handler) NOEXCEPT +{ + BC_ASSERT(stranded()); + + try + { + if (is_websocket()) + { + VARIANT_DISPATCH_METHOD(get_ws(), binary(binary)); + VARIANT_DISPATCH_METHOD(get_ws(), + async_write(buffer, std::bind(&socket::handle_async, + shared_from_this(), _1, _2, handler, "async_write"))); + } + else + { + VARIANT_DISPATCH_FUNCTION(boost::asio::async_write, get_tcp(), + buffer, std::bind(&socket::handle_async, + shared_from_this(), _1, _2, handler, "async_write")); + } + } + catch (const std::exception& e) + { + LOGF("Exception @ async_write: " << e.what()); + handler(error::operation_failed, {}); + } +} + + void socket::async_read_some(const asio::mutable_buffer& buffer, - count_handler&& handler) NOEXCEPT + const count_handler& handler) NOEXCEPT { - if (is_websocket()) + try { - VARIANT_DISPATCH_METHOD(get_ws(), - async_read_some(buffer, std::move(handler))); + if (is_websocket()) + { + VARIANT_DISPATCH_METHOD(get_ws(), + async_read_some(buffer, std::bind(&socket::handle_async, + shared_from_this(), _1, _2, handler, "async_read_some"))); + } + else + { + VARIANT_DISPATCH_METHOD(get_tcp(), + async_read_some(buffer, std::bind(&socket::handle_async, + shared_from_this(), _1, _2, handler, "async_read_some"))); + } } - else + catch (const std::exception& e) { - VARIANT_DISPATCH_METHOD(get_tcp(), - async_read_some(buffer, std::move(handler))); + LOGF("Exception @ async_read_some: " << e.what()); + handler(error::operation_failed, {}); } } -// Allows for full generalization between tcp and websockets. -void socket::async_write(const asio::const_buffer& buffer, - count_handler&& handler) NOEXCEPT +// raw +void socket::async_read(http::flat_buffer& buffer, + const count_handler& handler, size_t size) NOEXCEPT +{ + try + { + if (is_websocket()) + { + buffer.consume(buffer.size()); + + // Complete logical message semantics. + VARIANT_DISPATCH_METHOD(get_ws(), + async_read(buffer, std::bind(&socket::handle_async, + shared_from_this(), _1, _2, handler, "async_read_raw"))); + } + else + { + // Any available data semantics (ok). + async_read_some(buffer.prepare(size), handler); + } + } + catch (const std::exception& e) + { + LOGF("Exception @ async_read_raw: " << e.what()); + handler(error::operation_failed, {}); + } +} + +// fixed/p2p +void socket::async_read(const asio::mutable_buffer& buffer, + const count_handler& handler) NOEXCEPT +{ + try + { + if (is_websocket()) + { + const auto flat = std::make_shared(); + count_handler complete = std::bind(&socket::handle_async_read, + shared_from_this(), _1, _2, buffer, flat, handler); + + // Websocket doesn't have fixed-size reads, so simulate it. + async_read(*flat, std::move(complete)); + } + else + { + // Fixed-size semantics. + VARIANT_DISPATCH_FUNCTION(boost::asio::async_read, get_tcp(), + buffer, std::bind(&socket::handle_async, + shared_from_this(), _1, _2, handler, "async_read_fixed")); + } + } + catch (const std::exception& e) + { + LOGF("Exception @ async_read_fixed: " << e.what()); + handler(error::operation_failed, {}); + } +} + +// private +void socket::handle_async_read(const boost_code& ec, size_t size, + const asio::mutable_buffer& buffer, const http::flat_buffer_ptr& flat, + const count_handler& handler) NOEXCEPT { BC_ASSERT(stranded()); - if (is_websocket()) + if (ec) { - VARIANT_DISPATCH_METHOD(get_ws(), - async_write(buffer, std::move(handler))); + handler(error::ws_to_error_code(ec), size); + return; } - else + + if (size != buffer.size()) { - VARIANT_DISPATCH_FUNCTION(boost::asio::async_write, get_tcp(), - buffer, std::move(handler)); + handler(error::bad_size, size); + return; } + + std::memcpy(buffer.data(), flat->data().data(), size); + handler(error::success, size); +} + +// private +void socket::handle_async(const boost_code& ec, size_t size, + const count_handler& handler, const std::string& operation) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (error::asio_is_canceled(ec)) + { + handler(error::channel_stopped, size); + return; + } + + const auto code = is_websocket() ? error::ws_to_error_code(ec) : + error::asio_to_error_code(ec); + + if (code == error::unknown) + logx(operation, ec); + + handler(code, size); } // Logging. @@ -301,6 +432,8 @@ void socket::logx(const std::string& context, << ec.category().name() << " : " << ec.message()); } +BC_POP_WARNING() +BC_POP_WARNING() BC_POP_WARNING() } // namespace network diff --git a/src/net/socket_p2p.cpp b/src/net/socket_p2p.cpp new file mode 100644 index 000000000..442b7b817 --- /dev/null +++ b/src/net/socket_p2p.cpp @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2011-2026 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 . + */ +#include + +#include +#include +#include + +namespace libbitcoin { +namespace network { + +using namespace std::placeholders; + +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +// P2P (read). +// ---------------------------------------------------------------------------- + +void socket::p2p_read(const asio::mutable_buffer& out, + count_handler&& handler) NOEXCEPT +{ + boost::asio::dispatch(strand_, + std::bind(&socket::do_p2p_read, + shared_from_this(), out, std::move(handler))); +} + +// private +void socket::do_p2p_read(const asio::mutable_buffer& out, + const count_handler& handler) NOEXCEPT +{ + BC_ASSERT(stranded()); + async_read(out, handler); +} + +// P2P (write). +// ---------------------------------------------------------------------------- + +void socket::p2p_write(const asio::const_buffer& in, + count_handler&& handler) NOEXCEPT +{ + raw_write(in, std::move(handler), true); +} + +BC_POP_WARNING() + +} // namespace network +} // namespace libbitcoin diff --git a/src/net/socket_raw.cpp b/src/net/socket_raw.cpp new file mode 100644 index 000000000..ffe859fbb --- /dev/null +++ b/src/net/socket_raw.cpp @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2011-2026 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 . + */ +#include + +#include +#include +#include + +namespace libbitcoin { +namespace network { + +using namespace std::placeholders; + +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + +// RAW (read). +// ---------------------------------------------------------------------------- + +void socket::raw_read(http::flat_buffer& out, + count_handler&& handler) NOEXCEPT +{ + boost::asio::dispatch(strand_, + std::bind(&socket::do_raw_read, + shared_from_this(), std::ref(out), std::move(handler))); +} + +// private +void socket::do_raw_read(ref out, + const count_handler& handler) NOEXCEPT +{ + BC_ASSERT(stranded()); + async_read(out.get(), handler); +} + +// RAW (write). +// ---------------------------------------------------------------------------- + +void socket::raw_write(const asio::const_buffer& in, + count_handler&& handler, bool binary) NOEXCEPT +{ + boost::asio::dispatch(strand_, + std::bind(&socket::do_raw_write, + shared_from_this(), in, binary, std::move(handler))); +} + +// private +void socket::do_raw_write(const asio::const_buffer& in, bool binary, + const count_handler& handler) NOEXCEPT +{ + BC_ASSERT(stranded()); + async_write(in, binary, handler); +} + +BC_POP_WARNING() + +} // namespace network +} // namespace libbitcoin diff --git a/src/net/socket_rpc.cpp b/src/net/socket_rpc.cpp index 7f74d2b0a..7ad6f5122 100644 --- a/src/net/socket_rpc.cpp +++ b/src/net/socket_rpc.cpp @@ -148,7 +148,7 @@ void socket::do_rpc_write(boost_code ec, size_t total, BC_ASSERT(buffer.has_value()); - async_write(buffer.value().first, + async_write(buffer.value().first, true, std::bind(&socket::handle_rpc_write, shared_from_this(), _1, _2, total, out, handler)); } @@ -176,7 +176,7 @@ void socket::handle_rpc_write(boost_code ec, size_t size, size_t total, do_rpc_write(ec, total, out, handler); } -/// Unified JSON-RPC (notify). +/// RPC (notify). // ---------------------------------------------------------------------------- void socket::rpc_notify(rpc::request& notification, @@ -210,7 +210,7 @@ void socket::do_rpc_notify(boost_code ec, size_t total, BC_ASSERT(buffer.has_value()); - async_write(buffer.value().first, + async_write(buffer.value().first, true, std::bind(&socket::handle_rpc_notify, shared_from_this(), _1, _2, total, out, handler)); } diff --git a/src/net/socket_tcp.cpp b/src/net/socket_tcp.cpp deleted file mode 100644 index 4b95b60ed..000000000 --- a/src/net/socket_tcp.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) 2011-2026 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 . - */ -#include - -#include -#include -#include -#include - -namespace libbitcoin { -namespace network { - -using namespace network::rpc; -using namespace std::placeholders; - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -// TCP (read). -// ---------------------------------------------------------------------------- - -void socket::tcp_read(const asio::mutable_buffer& out, - count_handler&& handler) NOEXCEPT -{ - // asio::mutable_buffer is essentially a data_slab. - boost::asio::dispatch(strand_, - std::bind(&socket::do_tcp_read, - shared_from_this(), out, std::move(handler))); -} - -// private -void socket::do_tcp_read(const asio::mutable_buffer& out, - const count_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - try - { - VARIANT_DISPATCH_FUNCTION(boost::asio::async_read, - get_tcp(), out, - std::bind(&socket::handle_tcp, - shared_from_this(), _1, _2, handler)); - } - catch (const std::exception& e) - { - LOGF("Exception @ do_read: " << e.what()); - handler(error::operation_failed, {}); - } -} - -// TCP (write). -// ---------------------------------------------------------------------------- - -void socket::tcp_write(const asio::const_buffer& in, - count_handler&& handler) NOEXCEPT -{ - // asio::const_buffer is essentially a data_slice. - boost::asio::dispatch(strand_, - std::bind(&socket::do_tcp_write, - shared_from_this(), in, std::move(handler))); -} - -// private -void socket::do_tcp_write(const asio::const_buffer& in, - const count_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - try - { - VARIANT_DISPATCH_FUNCTION(boost::asio::async_write, - get_tcp(), in, - std::bind(&socket::handle_tcp, - shared_from_this(), _1, _2, handler)); - } - catch (const std::exception& e) - { - LOGF("Exception @ do_write: " << e.what()); - handler(error::operation_failed, {}); - } -} - -// TCP (both). -// ---------------------------------------------------------------------------- - -// private -void socket::handle_tcp(const boost_code& ec, size_t size, - const count_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (error::asio_is_canceled(ec)) - { - handler(error::channel_stopped, size); - return; - } - - const auto code = error::asio_to_error_code(ec); - if (code == error::unknown) logx("tcp", ec); - handler(code, size); -} - -BC_POP_WARNING() - -} // namespace network -} // namespace libbitcoin diff --git a/src/net/socket_ws.cpp b/src/net/socket_ws.cpp index 8d6d56e07..619e6dbeb 100644 --- a/src/net/socket_ws.cpp +++ b/src/net/socket_ws.cpp @@ -32,108 +32,13 @@ using namespace std::placeholders; BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -// WS (read). -// ---------------------------------------------------------------------------- - -void socket::ws_read(http::flat_buffer& out, - count_handler&& handler) NOEXCEPT -{ - boost::asio::dispatch(strand_, - std::bind(&socket::do_ws_read, - shared_from_this(), std::ref(out), std::move(handler))); -} - -// private -// flat_buffer is copied to allow it to be non-const. -void socket::do_ws_read(ref out, - const count_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - BC_ASSERT(is_websocket()); - - // Consume full previous buffer (no bytes are left behind by ws read). - out.get().consume(out.get().size()); - - try - { - VARIANT_DISPATCH_METHOD(get_ws(), - async_read(out.get(), std::bind(&socket::handle_ws, - shared_from_this(), _1, _2, handler))); - } - catch (const std::exception& e) - { - LOGF("Exception @ do_ws_read: " << e.what()); - handler(error::operation_failed, {}); - } -} - -// WS (write). -// ---------------------------------------------------------------------------- - -void socket::ws_write(const asio::const_buffer& in, bool binary, - count_handler&& handler) NOEXCEPT -{ - boost::asio::dispatch(strand_, - std::bind(&socket::do_ws_write, - shared_from_this(), in, binary, std::move(handler))); -} - -// private -void socket::do_ws_write(const asio::const_buffer& in, bool binary, - const count_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - BC_ASSERT(is_websocket()); - - try - { - if (binary) - { - VARIANT_DISPATCH_METHOD(get_ws(), binary(true)); - } - else - { - VARIANT_DISPATCH_METHOD(get_ws(), text(true)); - } - - VARIANT_DISPATCH_METHOD(get_ws(), - async_write(in, std::bind(&socket::handle_ws, - shared_from_this(), _1, _2, handler))); - } - catch (const std::exception& e) - { - LOGF("Exception @ do_ws_write: " << e.what()); - handler(error::operation_failed, {}); - } -} - -// WS (both). -// ---------------------------------------------------------------------------- - -// private -void socket::handle_ws(const boost_code& ec, size_t size, - const count_handler& handler) NOEXCEPT -{ - BC_ASSERT(stranded()); - - if (error::asio_is_canceled(ec)) - { - handler(error::channel_stopped, size); - return; - } - - const auto code = error::ws_to_error_code(ec); - if (code == error::unknown) logx("ws", ec); - handler(code, size); -} - // WS (event). // ---------------------------------------------------------------------------- // This is a unique/internal aspect of websockets. // private void socket::do_ws_event(ws::frame_type kind, - const std::string_view& data) NOEXCEPT + const std::string& data) NOEXCEPT { // Must not post to the iocontext once closed, and this is under control of // the websocket, so must be guarded here. Otherwise the socket will leak. diff --git a/test/net/socket.cpp b/test/net/socket.cpp index 63acc4aca..f641ca9f5 100644 --- a/test/net/socket.cpp +++ b/test/net/socket.cpp @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(socket__read__disconnected__error) const auto instance = std::make_shared(log, pool.service(), std::move(params)); system::data_array<42> data; - instance->tcp_read(asio::mutable_buffer{ data.data(), data.size() }, + instance->p2p_read({ data.data(), data.size() }, [instance](const code& ec, size_t size) { // 10009 (WSAEBADF, invalid file handle) gets mapped to bad_stream. @@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(socket__write__disconnected__bad_stream) const auto instance = std::make_shared(log, pool.service(), std::move(params)); system::data_array<42> data; - instance->tcp_write(asio::const_buffer{ data.data(), data.size() }, + instance->p2p_write({ data.data(), data.size() }, [instance](const code& ec, size_t size) { // 10009 (WSAEBADF, invalid file handle) gets mapped to bad_stream. diff --git a/test/ssl/wolfssl/tests/api.c b/test/ssl/wolfssl/tests/api.c index d56ddc2a1..45c6f1027 100644 --- a/test/ssl/wolfssl/tests/api.c +++ b/test/ssl/wolfssl/tests/api.c @@ -33853,7 +33853,7 @@ static int test_wolfSSL_d2i_and_i2d_PublicKey_ecc(void) const unsigned char* p; unsigned char *der = NULL; unsigned char *tmp = NULL; - int derLen; + int derLen = -1; // HACK: delint. unsigned char pub_buf[65]; unsigned char pub_spki_buf[91]; const int pub_len = 65;