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;