diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b0ee67..dd2bb89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Version 211: * close_socket is in stream_traits.hpp * Improvements to test::stream * Add stranded_stream +* Add flat_stream -------------------------------------------------------------------------------- diff --git a/doc/qbk/03_core/2_streams.qbk b/doc/qbk/03_core/2_streams.qbk index 6b2683a5..6221b4af 100644 --- a/doc/qbk/03_core/2_streams.qbk +++ b/doc/qbk/03_core/2_streams.qbk @@ -7,6 +7,8 @@ Official repository: https://github.com/boostorg/beast ] + + [section Stream Types] A __Stream__ is a communication channel where data is reliably transferred as @@ -111,4 +113,30 @@ synchronous stream may check its argument: [snippet_core_3] + + +[heading Stream Interfaces] + +To facilitiate stream algorithms, these types provide enhanced functionality +above what is offered by networking: + +[table Stream Interfaces +[[Name][Description]] +[[ + [link beast.ref.boost__beast__basic_timeout_stream `basic_timeout_stream`] + [link beast.ref.boost__beast__timeout_stream `timeout_stream`] +][ + Objects of this type are designed to act as a replacement for the + traditional networking socket. They +]] +[[ + [link beast.ref.boost__beast__flat_stream `flat_stream`] +][ + This stream wrapper improves performance by working around a limitation + of networking's `ssl::stream` to improve performance. +]] +] + + + [endsect] diff --git a/doc/qbk/quickref.xml b/doc/qbk/quickref.xml index 178bf619..0548c6bd 100644 --- a/doc/qbk/quickref.xml +++ b/doc/qbk/quickref.xml @@ -33,6 +33,7 @@ flat_buffer flat_static_buffer flat_static_buffer_base + flat_stream 🞲 handler_ptr iequal iless @@ -354,7 +355,6 @@ Classes - flat_stream ssl_stream http::icy_stream test::fail_count diff --git a/include/boost/beast/_experimental/core/ssl_stream.hpp b/include/boost/beast/_experimental/core/ssl_stream.hpp index 9cbc5af3..7a2a06a5 100644 --- a/include/boost/beast/_experimental/core/ssl_stream.hpp +++ b/include/boost/beast/_experimental/core/ssl_stream.hpp @@ -15,7 +15,7 @@ // This include is necessary to work with `ssl::stream` and `boost::beast::websocket::stream` #include -#include +#include #include #include #include diff --git a/include/boost/beast/core.hpp b/include/boost/beast/core.hpp index 27e18b2a..5bf8d46c 100644 --- a/include/boost/beast/core.hpp +++ b/include/boost/beast/core.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/beast/_experimental/core/detail/flat_stream.hpp b/include/boost/beast/core/detail/flat_stream.hpp similarity index 100% rename from include/boost/beast/_experimental/core/detail/flat_stream.hpp rename to include/boost/beast/core/detail/flat_stream.hpp diff --git a/include/boost/beast/_experimental/core/flat_stream.hpp b/include/boost/beast/core/flat_stream.hpp similarity index 94% rename from include/boost/beast/_experimental/core/flat_stream.hpp rename to include/boost/beast/core/flat_stream.hpp index 0b8f27f8..4dea1354 100644 --- a/include/boost/beast/_experimental/core/flat_stream.hpp +++ b/include/boost/beast/core/flat_stream.hpp @@ -12,7 +12,9 @@ #include #include -#include +#include +#include +#include #include #include #include @@ -73,12 +75,12 @@ namespace beast { `net::ssl::stream`. @par Concepts - @b AsyncStream - @b SyncStream + @li SyncStream + @li AsyncStream @see - @li https://github.com/boostorg/beast/issues/1108 @li https://github.com/boostorg/asio/issues/100 + @li https://github.com/boostorg/beast/issues/1108 @li https://stackoverflow.com/questions/38198638/openssl-ssl-write-from-multiple-buffers-ssl-writev @li https://stackoverflow.com/questions/50026167/performance-drop-on-port-from-beast-1-0-0-b66-to-boost-1-67-0-beast */ @@ -92,9 +94,22 @@ class flat_stream // 16KB is the upper limit on reasonably sized HTTP messages. static std::size_t constexpr max_size = 16 * 1024; - template class write_op; + // Largest stack we will use to flatten + static std::size_t constexpr max_stack = 16 * 1024; + + template class write_op; NextLayer stream_; + flat_buffer buffer_; + + BOOST_STATIC_ASSERT(has_get_executor::value); + + template + std::size_t + stack_write_some( + std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec); public: /// The type of the next layer. @@ -102,7 +117,7 @@ public: typename std::remove_reference::type; /// The type of the executor associated with the object. - using executor_type = typename next_layer_type::executor_type; + using executor_type = beast::executor_type; flat_stream(flat_stream&&) = default; flat_stream(flat_stream const&) = default; @@ -322,6 +337,6 @@ public: } // beast } // boost -#include +#include #endif diff --git a/include/boost/beast/_experimental/core/impl/flat_stream.hpp b/include/boost/beast/core/impl/flat_stream.hpp similarity index 65% rename from include/boost/beast/_experimental/core/impl/flat_stream.hpp rename to include/boost/beast/core/impl/flat_stream.hpp index 93ea5c5a..f1f6cd71 100644 --- a/include/boost/beast/_experimental/core/impl/flat_stream.hpp +++ b/include/boost/beast/core/impl/flat_stream.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -22,39 +23,16 @@ namespace boost { namespace beast { template -template +template class flat_stream::write_op : public async_op_base> - , public net::coroutine { - using alloc_type = std::allocator; - - struct deleter - { - alloc_type alloc; - std::size_t size = 0; - - explicit - deleter(alloc_type const& alloc_) - : alloc(alloc_) - { - } - - void - operator()(char* p) - { - alloc.deallocate(p, size); - } - }; - - flat_stream& s_; - ConstBufferSequence b_; - std::unique_ptr p_; - public: - template + template< + class ConstBufferSequence, + class Handler_> write_op( flat_stream& s, ConstBufferSequence const& b, @@ -63,11 +41,26 @@ public: beast::executor_type>( std::forward(h), s.get_executor()) - , s_(s) - , b_(b) - , p_(nullptr, deleter{alloc_type{}}) { - (*this)({}, 0); + auto const result = + coalesce(b, coalesce_limit); + if(result.needs_coalescing) + { + s.buffer_.clear(); + s.buffer_.commit(net::buffer_copy( + s.buffer_.prepare(result.size), + b, result.size)); + s.stream_.async_write_some( + s.buffer_.data(), std::move(*this)); + } + else + { + s.buffer_.clear(); + s.buffer_.shrink_to_fit(); + s.stream_.async_write_some( + beast::buffers_prefix( + result.size, b), std::move(*this)); + } } void @@ -75,36 +68,7 @@ public: boost::system::error_code ec, std::size_t bytes_transferred) { - BOOST_ASIO_CORO_REENTER(*this) - { - BOOST_ASIO_CORO_YIELD - { - auto const result = - coalesce(b_, coalesce_limit); - if(result.needs_coalescing) - { - p_.get_deleter().size = result.size; - p_.reset(p_.get_deleter().alloc.allocate( - p_.get_deleter().size)); - net::buffer_copy( - net::buffer( - p_.get(), p_.get_deleter().size), - b_, result.size); - s_.stream_.async_write_some( - net::buffer( - p_.get(), p_.get_deleter().size), - std::move(*this)); - } - else - { - s_.stream_.async_write_some( - boost::beast::buffers_prefix( - result.size, b_), std::move(*this)); - } - } - p_.reset(); - this->invoke(ec, bytes_transferred); - } + this->invoke(ec, bytes_transferred); } }; @@ -128,7 +92,7 @@ read_some(MutableBufferSequence const& buffers) "SyncReadStream requirements not met"); static_assert(net::is_mutable_buffer_sequence< MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); + "MutableBufferSequence requirements not met"); error_code ec; auto n = read_some(buffers, ec); if(ec) @@ -142,6 +106,11 @@ std::size_t flat_stream:: read_some(MutableBufferSequence const& buffers, error_code& ec) { + static_assert(boost::beast::is_sync_read_stream::value, + "SyncReadStream requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); return stream_.read_some(buffers, ec); } @@ -159,7 +128,7 @@ async_read_some( static_assert(boost::beast::is_async_read_stream::value, "AsyncReadStream requirements not met"); static_assert(net::is_mutable_buffer_sequence< - MutableBufferSequence >::value, + MutableBufferSequence >::value, "MutableBufferSequence requirements not met"); return stream_.async_read_some( buffers, std::forward(handler)); @@ -176,16 +145,26 @@ write_some(ConstBufferSequence const& buffers) static_assert(net::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); - auto const result = coalesce(buffers, coalesce_limit); - if(result.needs_coalescing) - { - std::unique_ptr p{new char[result.size]}; - auto const b = net::buffer(p.get(), result.size); - net::buffer_copy(b, buffers); - return stream_.write_some(b); - } - return stream_.write_some( - boost::beast::buffers_prefix(result.size, buffers)); + error_code ec; + auto n = write_some(buffers, ec); + if(ec) + BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); + return n; +} + +template +template +std::size_t +flat_stream:: +stack_write_some( + std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec) +{ + static_buffer b; + b.commit(net::buffer_copy( + b.prepare(size), buffers)); + return stream_.write_some(b.data(), ec); } template @@ -198,15 +177,21 @@ write_some(ConstBufferSequence const& buffers, error_code& ec) "SyncWriteStream requirements not met"); static_assert(net::is_const_buffer_sequence< ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); + "ConstBufferSequence requirements not met"); auto const result = coalesce(buffers, coalesce_limit); if(result.needs_coalescing) { - std::unique_ptr p{new char[result.size]}; - auto const b = net::buffer(p.get(), result.size); - net::buffer_copy(b, buffers); - return stream_.write_some(b, ec); + if(result.size > max_stack) + return stack_write_some(result.size, buffers, ec); + + buffer_.clear(); + buffer_.commit(net::buffer_copy( + buffer_.prepare(result.size), + buffers)); + return stream_.write_some(buffer_.data(), ec); } + buffer_.clear(); + buffer_.shrink_to_fit(); return stream_.write_some( boost::beast::buffers_prefix(result.size, buffers), ec); } @@ -225,13 +210,13 @@ async_write_some( static_assert(boost::beast::is_async_write_stream::value, "AsyncWriteStream requirements not met"); static_assert(net::is_const_buffer_sequence< - ConstBufferSequence>::value, + ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); BOOST_BEAST_HANDLER_INIT( WriteHandler, void(error_code, std::size_t)); - write_op{ - *this, buffers, std::move(init.completion_handler)}; + write_op( + *this, buffers, std::move(init.completion_handler)); return init.result.get(); } diff --git a/test/beast/core/CMakeLists.txt b/test/beast/core/CMakeLists.txt index d9890450..d481211c 100644 --- a/test/beast/core/CMakeLists.txt +++ b/test/beast/core/CMakeLists.txt @@ -51,6 +51,7 @@ add_executable (tests-beast-core file_win32.cpp flat_buffer.cpp flat_static_buffer.cpp + flat_stream.cpp handler_ptr.cpp make_printable.cpp multi_buffer.cpp diff --git a/test/beast/core/Jamfile b/test/beast/core/Jamfile index c53ce101..c63b3ff8 100644 --- a/test/beast/core/Jamfile +++ b/test/beast/core/Jamfile @@ -39,6 +39,7 @@ local SOURCES = file_win32.cpp flat_buffer.cpp flat_static_buffer.cpp + flat_stream.cpp handler_ptr.cpp make_printable.cpp multi_buffer.cpp diff --git a/test/beast/experimental/flat_stream.cpp b/test/beast/core/flat_stream.cpp similarity index 89% rename from test/beast/experimental/flat_stream.cpp rename to test/beast/core/flat_stream.cpp index 1220e639..8687c5e4 100644 --- a/test/beast/experimental/flat_stream.cpp +++ b/test/beast/core/flat_stream.cpp @@ -8,11 +8,14 @@ // // Test that header file is self-contained. -#include +#include + +#include "stream_tests.hpp" #include #include #include +#include #include #include @@ -24,6 +27,13 @@ class flat_stream_test , public test::enable_yield_to { public: + void + testStream() + { + test_sync_stream>(); + test_async_stream>(); + } + void testSplit() { @@ -81,7 +91,7 @@ public: test::ws_echo_server es{log}; net::io_context ioc; websocket::stream> ws{ioc}; - ws.next_layer().next_layer().connect(es.stream()); + get_lowest_layer(ws).connect(es.stream()); ws.async_handshake("localhost", "/", [&](error_code) { @@ -97,6 +107,7 @@ public: void run() override { + testStream(); testSplit(); testHttp(); testWebsocket(); diff --git a/test/beast/experimental/CMakeLists.txt b/test/beast/experimental/CMakeLists.txt index 2397c684..5b870f5a 100644 --- a/test/beast/experimental/CMakeLists.txt +++ b/test/beast/experimental/CMakeLists.txt @@ -17,7 +17,6 @@ add_executable (tests-beast-experimental ${TEST_MAIN} Jamfile error.cpp - flat_stream.cpp icy_stream.cpp ssl_stream.cpp stream.cpp diff --git a/test/beast/experimental/Jamfile b/test/beast/experimental/Jamfile index a4d05381..ff7d2e4d 100644 --- a/test/beast/experimental/Jamfile +++ b/test/beast/experimental/Jamfile @@ -9,7 +9,6 @@ local SOURCES = error.cpp - flat_stream.cpp icy_stream.cpp ssl_stream.cpp stream.cpp