diff --git a/include/bitcoin/network/channels/channel.hpp b/include/bitcoin/network/channels/channel.hpp index de8cbf041..b03d2ffa0 100644 --- a/include/bitcoin/network/channels/channel.hpp +++ b/include/bitcoin/network/channels/channel.hpp @@ -89,6 +89,9 @@ class BCT_API channel /// Configuration settings. const settings_t& settings() const NOEXCEPT; + /// TCP server options. + const options_t& options() const NOEXCEPT; + protected: /// Construct a channel to encapsulated and communicate on the socket. channel(const logger& log, const socket::ptr& socket, uint64_t identifier, @@ -112,6 +115,7 @@ class BCT_API channel void handle_monitor(const code& ec) NOEXCEPT; // These are thread safe (const). + const options_t& options_; const settings_t& settings_; const uint64_t identifier_; const uint64_t nonce_ diff --git a/include/bitcoin/network/channels/channel_http.hpp b/include/bitcoin/network/channels/channel_http.hpp index e36ec6d93..b53424b12 100644 --- a/include/bitcoin/network/channels/channel_http.hpp +++ b/include/bitcoin/network/channels/channel_http.hpp @@ -52,28 +52,27 @@ class BCT_API channel_http dispatcher_.subscribe(std::forward(handler)); } - // TODO: network.minimum_buffer is being overloaded here. /// Construct http channel to encapsulate and communicate on the socket. inline channel_http(const logger& log, const socket::ptr& socket, uint64_t identifier, const settings_t& settings, const options_t& options) NOEXCEPT : channel(log, socket, identifier, settings, options), response_buffer_(system::to_shared()), - request_buffer_(settings.minimum_buffer) + request_buffer_(options.minimum_buffer) { } + /// Serialize and write http message to client (requires strand). + /// Completion handler is always invoked on the channel strand. + void send(http::response&& response, + result_handler&& handler) NOEXCEPT; + /// Resume reading from the socket (requires strand). void resume() NOEXCEPT override; /// Must call after successful message handling if no stop. virtual void receive() NOEXCEPT; - /// Serialize and write http message to client (requires strand). - /// Completion handler is always invoked on the channel strand. - virtual void send(http::response&& response, - result_handler&& handler) NOEXCEPT; - protected: /// Stranded handler invoked from stop(). void stopping(const code& ec) NOEXCEPT override; diff --git a/include/bitcoin/network/channels/channel_rpc.hpp b/include/bitcoin/network/channels/channel_rpc.hpp index 169fcd29b..55d055e47 100644 --- a/include/bitcoin/network/channels/channel_rpc.hpp +++ b/include/bitcoin/network/channels/channel_rpc.hpp @@ -50,14 +50,13 @@ class BCT_API channel_rpc dispatcher_.subscribe(std::forward(handler)); } - // TODO: network.minimum_buffer is being overloaded here. /// Construct rpc channel to encapsulate and communicate on the socket. inline channel_rpc(const logger& log, const socket::ptr& socket, uint64_t identifier, const settings_t& settings, const options_t& options) NOEXCEPT : channel(log, socket, identifier, settings, options), response_buffer_(system::to_shared()), - request_buffer_(settings.minimum_buffer) + request_buffer_(options.minimum_buffer) { } diff --git a/include/bitcoin/network/net/socket.hpp b/include/bitcoin/network/net/socket.hpp index 35b2f7a97..1ae02a5b0 100644 --- a/include/bitcoin/network/net/socket.hpp +++ b/include/bitcoin/network/net/socket.hpp @@ -289,6 +289,7 @@ class BCT_API socket std::atomic_bool stopped_{}; // These are protected by strand (see also handle_accept). + size_t maximum_; asio::socket socket_; config::address address_; config::authority authority_{}; diff --git a/include/bitcoin/network/settings.hpp b/include/bitcoin/network/settings.hpp index 0b49d9d3e..699b7d1d3 100644 --- a/include/bitcoin/network/settings.hpp +++ b/include/bitcoin/network/settings.hpp @@ -28,6 +28,14 @@ namespace libbitcoin { namespace network { +/// The largest p2p payload request when configured for witness blocks. +constexpr uint32_t maximum_request_ +{ + system::possible_narrow_cast( + messages::peer::heading::maximum_payload( + messages::peer::level::canonical, true)) +}; + /// Common network configuration settings, properties not thread safe. struct BCT_API settings { @@ -44,6 +52,8 @@ struct BCT_API settings uint16_t connections{ 0 }; uint32_t inactivity_minutes{ 10 }; uint32_t expiration_minutes{ 60 }; + uint32_t maximum_request{ maximum_request_ }; + uint32_t minimum_buffer{ maximum_request_ }; /// Helpers. virtual bool enabled() const NOEXCEPT; @@ -220,12 +230,6 @@ struct BCT_API settings uint32_t handshake_timeout_seconds{ 15 }; uint32_t channel_heartbeat_minutes{ 5 }; uint32_t maximum_skew_minutes{ 120 }; - uint32_t minimum_buffer - { - system::possible_narrow_cast( - messages::peer::heading::maximum_payload( - messages::peer::level::canonical, true)) - }; uint32_t rate_limit{ 1024 }; std::string user_agent{ BC_USER_AGENT }; std::filesystem::path path{}; diff --git a/src/channels/channel.cpp b/src/channels/channel.cpp index 0b6cd9f89..c00bc5455 100644 --- a/src/channels/channel.cpp +++ b/src/channels/channel.cpp @@ -47,7 +47,10 @@ inline deadline::ptr make_timer(const logger& log, asio::strand& strand, channel::channel(const logger& log, const socket::ptr& socket, uint64_t identifier, const settings_t& settings, const options_t& options) NOEXCEPT - : proxy(socket), settings_(settings), identifier_(identifier), + : proxy(socket), + options_(options), + settings_(settings), + identifier_(identifier), inactivity_(make_timer(log, socket->strand(), options.inactivity())), expiration_(make_timer(log, socket->strand(), options.expiration())) { @@ -221,6 +224,11 @@ const network::settings& channel::settings() const NOEXCEPT return settings_; } +const channel::options_t& channel::options() const NOEXCEPT +{ + return options_; +} + BC_POP_WARNING() } // namespace network diff --git a/src/channels/channel_peer.cpp b/src/channels/channel_peer.cpp index 856614bd0..7c4f54076 100644 --- a/src/channels/channel_peer.cpp +++ b/src/channels/channel_peer.cpp @@ -304,9 +304,9 @@ void channel_peer::handle_read_payload(const code& ec, size_t payload_size, /////////////////////////////////////////////////////////////////////////// // Don't retain larger than configured (time-space tradeoff). - if (settings().minimum_buffer < payload_buffer_.capacity()) + if (options().minimum_buffer < payload_buffer_.capacity()) { - payload_buffer_.resize(settings().minimum_buffer); + payload_buffer_.resize(options().minimum_buffer); payload_buffer_.shrink_to_fit(); } diff --git a/src/net/socket.cpp b/src/net/socket.cpp index baa8623b1..82ac1457d 100644 --- a/src/net/socket.cpp +++ b/src/net/socket.cpp @@ -34,9 +34,6 @@ using namespace network::rpc; using namespace std::placeholders; namespace beast = boost::beast; -// TODO: move to config. -constexpr size_t client_request_limit = 5u * 1024u * 1024u; - // 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) @@ -59,6 +56,7 @@ socket::socket(const logger& log, asio::io_context& service) NOEXCEPT socket::socket(const logger& log, asio::io_context& service, const config::address& address) NOEXCEPT : strand_(service.get_executor()), + maximum_(5u * 1024u * 1024u), socket_(strand_), service_(service), address_(address), @@ -551,10 +549,10 @@ void socket::do_http_read(std::reference_wrapper buffer, auto parser = to_shared(); // Causes http::error::body_limit on completion. - parser->body_limit(client_request_limit); + parser->body_limit(maximum_); // Causes http::error::header_limit on completion. - parser->header_limit(client_request_limit); + parser->header_limit(limit(maximum_)); // This operation posts handler to the strand. beast::http::async_read(socket_, buffer.get(), *parser, @@ -675,7 +673,7 @@ void socket::handle_rpc_read(boost_code ec, size_t size, size_t total, return; } - if (total > client_request_limit) + if (total > maximum_) { handler(error::message_overflow, total); return; @@ -894,7 +892,7 @@ code socket::set_websocket(const http::request& request) NOEXCEPT websocket_.emplace(std::move(socket_)); // Causes websocket::error::message_too_big on completion. - websocket_->read_message_max(client_request_limit); + websocket_->read_message_max(maximum_); websocket_->set_option(ws::decorator { [](http::fields& header) NOEXCEPT diff --git a/test/channels/channel_peer.cpp b/test/channels/channel_peer.cpp index ea18d5165..86e1f25ee 100644 --- a/test/channels/channel_peer.cpp +++ b/test/channels/channel_peer.cpp @@ -102,7 +102,6 @@ BOOST_AUTO_TEST_CASE(channel_peer__properties__default__expected) BOOST_REQUIRE_EQUAL(channel_ptr->settings().maximum_payload(), payload_maximum(set)); BOOST_REQUIRE_EQUAL(channel_ptr->settings().identifier, set.identifier); BOOST_REQUIRE_EQUAL(channel_ptr->settings().validate_checksum, set.validate_checksum); - BOOST_REQUIRE_EQUAL(channel_ptr->settings().minimum_buffer, set.minimum_buffer); // Stop is asynchronous, threadpool destruct blocks until all complete. // Calling stop here sets channel.stopped and prevents destructor assertion. diff --git a/test/settings.cpp b/test/settings.cpp index f0d4841f1..617b59ce4 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -53,7 +53,6 @@ BOOST_AUTO_TEST_CASE(settings__construct__default__expected) BOOST_REQUIRE_EQUAL(instance.handshake_timeout_seconds, 15u); BOOST_REQUIRE_EQUAL(instance.channel_heartbeat_minutes, 5u); BOOST_REQUIRE_EQUAL(instance.maximum_skew_minutes, 120u); - BOOST_REQUIRE_EQUAL(instance.minimum_buffer, 4'000'000u); BOOST_REQUIRE_EQUAL(instance.rate_limit, 1024u); BOOST_REQUIRE_EQUAL(instance.user_agent, BC_USER_AGENT); BOOST_REQUIRE(instance.path.empty()); @@ -344,6 +343,7 @@ BOOST_AUTO_TEST_CASE(settings__excluded__default__true) // services // ---------------------------------------------------------------------------- +constexpr auto maximum_request = system::chain::max_block_weight; BOOST_AUTO_TEST_CASE(settings__tcp_server__defaults__expected) { @@ -357,6 +357,7 @@ BOOST_AUTO_TEST_CASE(settings__tcp_server__defaults__expected) BOOST_REQUIRE_EQUAL(instance.connections, 0u); BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE_EQUAL(instance.maximum_request, maximum_request); BOOST_REQUIRE(!instance.enabled()); BOOST_REQUIRE(instance.inactivity() == minutes(10)); BOOST_REQUIRE(instance.expiration() == minutes(60)); @@ -374,6 +375,7 @@ BOOST_AUTO_TEST_CASE(settings__http_server__defaults__expected) BOOST_REQUIRE_EQUAL(instance.connections, 0u); BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE_EQUAL(instance.maximum_request, maximum_request); BOOST_REQUIRE(!instance.enabled()); BOOST_REQUIRE(instance.inactivity() == minutes(10)); BOOST_REQUIRE(instance.expiration() == minutes(60)); @@ -399,6 +401,7 @@ BOOST_AUTO_TEST_CASE(settings__websocket_server__defaults__expected) BOOST_REQUIRE_EQUAL(instance.connections, 0u); BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE_EQUAL(instance.maximum_request, maximum_request); BOOST_REQUIRE(!instance.enabled()); BOOST_REQUIRE(instance.inactivity() == minutes(10)); BOOST_REQUIRE(instance.expiration() == minutes(60)); @@ -425,6 +428,7 @@ BOOST_AUTO_TEST_CASE(settings__peer_outbound__mainnet__expected) BOOST_REQUIRE_EQUAL(instance.connections, 10u); BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE_EQUAL(instance.maximum_request, maximum_request); BOOST_REQUIRE(!instance.enabled()); BOOST_REQUIRE(instance.inactivity() == minutes(10)); BOOST_REQUIRE(instance.expiration() == minutes(60)); @@ -545,6 +549,7 @@ BOOST_AUTO_TEST_CASE(settings__peer_inbound__mainnet__expected) BOOST_REQUIRE_EQUAL(instance.connections, 0u); BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE_EQUAL(instance.maximum_request, maximum_request); BOOST_REQUIRE(!instance.enabled()); BOOST_REQUIRE(instance.inactivity() == minutes(10)); BOOST_REQUIRE(instance.expiration() == minutes(60)); @@ -691,6 +696,7 @@ BOOST_AUTO_TEST_CASE(settings__peer_manual__mainnet__expected) BOOST_REQUIRE_EQUAL(instance.connections, 0u); BOOST_REQUIRE_EQUAL(instance.inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.expiration_minutes, 60u); + BOOST_REQUIRE_EQUAL(instance.maximum_request, maximum_request); BOOST_REQUIRE(!instance.enabled()); BOOST_REQUIRE(instance.inactivity() == minutes(10)); BOOST_REQUIRE(instance.expiration() == minutes(60));