diff --git a/CHANGELOG.md b/CHANGELOG.md index 2347a31f..c6e87f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 212: * dynamic_buffer_ref tests and tidy +* flat_stream tests and tidy -------------------------------------------------------------------------------- diff --git a/include/boost/beast/_experimental/test/impl/stream.hpp b/include/boost/beast/_experimental/test/impl/stream.hpp index bba5574f..69044a20 100644 --- a/include/boost/beast/_experimental/test/impl/stream.hpp +++ b/include/boost/beast/_experimental/test/impl/stream.hpp @@ -332,7 +332,7 @@ read_some(MutableBufferSequence const& buffers, if(in_->fc && in_->fc->fail(ec)) return 0; - // 0-byte reads are no-ops + // A request to read 0 bytes from a stream is a no-op. if(buffer_size(buffers) == 0) { ec.clear(); @@ -398,7 +398,7 @@ async_read_some( return init.result.get(); } - // 0-byte reads are no-ops + // A request to read 0 bytes from a stream is a no-op. if(buffer_size(buffers) == 0) { lock.unlock(); @@ -474,6 +474,17 @@ write_some( ++in_->nwrite; + // test failure + if(in_->fc && in_->fc->fail(ec)) + return 0; + + // A request to write 0 bytes to a stream is a no-op. + if(buffer_size(buffers) == 0) + { + ec.clear(); + return 0; + } + // connection closed auto out = out_.lock(); if(! out) @@ -482,10 +493,6 @@ write_some( return 0; } - // test failure - if(in_->fc && in_->fc->fail(ec)) - return 0; - // copy buffers auto n = std::min( buffer_size(buffers), in_->write_max); @@ -513,19 +520,6 @@ async_write_some(ConstBufferSequence const& buffers, ++in_->nwrite; - // connection closed - auto out = out_.lock(); - if(! out) - { - net::post( - in_->ioc.get_executor(), - beast::bind_front_handler( - std::move(init.completion_handler), - net::error::connection_reset, - std::size_t{0})); - return init.result.get(); - } - // test failure error_code ec; if(in_->fc && in_->fc->fail(ec)) @@ -539,6 +533,30 @@ async_write_some(ConstBufferSequence const& buffers, return init.result.get(); } + // A request to read 0 bytes from a stream is a no-op. + if(buffer_size(buffers) == 0) + { + net::post( + in_->ioc.get_executor(), + beast::bind_front_handler( + std::move(init.completion_handler), + ec, std::size_t{0})); + return init.result.get(); + } + + // connection closed + auto out = out_.lock(); + if(! out) + { + net::post( + in_->ioc.get_executor(), + beast::bind_front_handler( + std::move(init.completion_handler), + net::error::connection_reset, + std::size_t{0})); + return init.result.get(); + } + // copy buffers auto n = std::min( buffer_size(buffers), in_->write_max); diff --git a/include/boost/beast/core/detail/flat_stream.hpp b/include/boost/beast/core/detail/flat_stream.hpp index 36e67195..f00ccb98 100644 --- a/include/boost/beast/core/detail/flat_stream.hpp +++ b/include/boost/beast/core/detail/flat_stream.hpp @@ -23,22 +23,25 @@ class flat_stream_base public: // Largest buffer size we will flatten. // 16KB is the upper limit on reasonably sized HTTP messages. - static std::size_t constexpr coalesce_limit = 16 * 1024; + static std::size_t constexpr max_size = 16 * 1024; - struct coalesce_result + // Largest stack we will use to flatten + static std::size_t constexpr max_stack = 8 * 1024; + + struct flatten_result { std::size_t size; - bool needs_coalescing; + bool flatten; }; - // calculates the coalesce settings for a buffer sequence + // calculates the flatten settings for a buffer sequence template static - coalesce_result - coalesce( + flatten_result + flatten( BufferSequence const& buffers, std::size_t limit) { - coalesce_result result{0, false}; + flatten_result result{0, false}; auto first = net::buffer_sequence_begin(buffers); auto last = net::buffer_sequence_end(buffers); if(first != last) @@ -56,7 +59,7 @@ public: result.size += n; prev = it; } - result.needs_coalescing = prev != first; + result.flatten = prev != first; } } return result; diff --git a/include/boost/beast/core/flat_stream.hpp b/include/boost/beast/core/flat_stream.hpp index 4dea1354..f0bc30db 100644 --- a/include/boost/beast/core/flat_stream.hpp +++ b/include/boost/beast/core/flat_stream.hpp @@ -90,13 +90,6 @@ class flat_stream : private detail::flat_stream_base #endif { - // Largest buffer size we will flatten. - // 16KB is the upper limit on reasonably sized HTTP messages. - static std::size_t constexpr max_size = 16 * 1024; - - // Largest stack we will use to flatten - static std::size_t constexpr max_stack = 16 * 1024; - template class write_op; NextLayer stream_; diff --git a/include/boost/beast/core/impl/flat_stream.hpp b/include/boost/beast/core/impl/flat_stream.hpp index f1f6cd71..e9868bfd 100644 --- a/include/boost/beast/core/impl/flat_stream.hpp +++ b/include/boost/beast/core/impl/flat_stream.hpp @@ -43,8 +43,8 @@ public: s.get_executor()) { auto const result = - coalesce(b, coalesce_limit); - if(result.needs_coalescing) + flatten(b, max_size); + if(result.flatten) { s.buffer_.clear(); s.buffer_.commit(net::buffer_copy( @@ -178,10 +178,10 @@ write_some(ConstBufferSequence const& buffers, error_code& ec) static_assert(net::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); - auto const result = coalesce(buffers, coalesce_limit); - if(result.needs_coalescing) + auto const result = flatten(buffers, max_size); + if(result.flatten) { - if(result.size > max_stack) + if(result.size <= max_stack) return stack_write_some(result.size, buffers, ec); buffer_.clear(); diff --git a/test/beast/core/flat_stream.cpp b/test/beast/core/flat_stream.cpp index 8687c5e4..e19bdaff 100644 --- a/test/beast/core/flat_stream.cpp +++ b/test/beast/core/flat_stream.cpp @@ -12,26 +12,168 @@ #include "stream_tests.hpp" -#include -#include #include #include +#include #include #include namespace boost { namespace beast { -class flat_stream_test - : public unit_test::suite - , public test::enable_yield_to +class flat_stream_test : public unit_test::suite { public: void - testStream() + testMembers() { + net::io_context ioc; + test_sync_stream>(); + test_async_stream>(); + + // read/write + + { + error_code ec; + flat_stream s(ioc); + { + // VFALCO Hack to make test stream code = eof + test::stream ts(ioc); + s.next_layer().connect(ts); + } + char buf[1]; + net::mutable_buffer m1 = net::buffer(buf); + + BEAST_EXPECT(s.read_some(net::mutable_buffer{}) == 0); + BEAST_EXPECT(s.read_some(net::mutable_buffer{}, ec) == 0); + BEAST_EXPECTS(! ec, ec.message()); + + try + { + s.read_some(m1); + BEAST_FAIL(); + } + catch(std::exception const&) + { + BEAST_PASS(); + } + catch(...) + { + BEAST_FAIL(); + } + + BEAST_EXPECT(s.write_some(net::const_buffer{}) == 0); + BEAST_EXPECT(s.write_some(net::const_buffer{}, ec) == 0); + BEAST_EXPECTS(! ec, ec.message()); + + try + { + s.write_some(m1); + BEAST_FAIL(); + } + catch(std::exception const&) + { + BEAST_PASS(); + } + catch(...) + { + BEAST_FAIL(); + } + + bool invoked; + + invoked = false; + s.async_read_some(net::mutable_buffer{}, + [&](error_code ec, std::size_t) + { + invoked = true; + BEAST_EXPECTS(! ec, ec.message()); + }); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(invoked); + + invoked = false; + s.async_write_some(net::const_buffer{}, + [&](error_code ec, std::size_t) + { + invoked = true; + BEAST_EXPECTS(! ec, ec.message()); + }); + ioc.run(); + ioc.restart(); + BEAST_EXPECT(invoked); + } + + // stack_write_some + + { + char b[detail::flat_stream_base::max_size]; + std::array bs; + bs[0] = net::const_buffer(b, 100); + bs[1] = net::const_buffer(b + 100, 200); + bs[2] = net::const_buffer(b + 100 + 200, 300); + BEAST_EXPECT(buffer_size(bs) <= + detail::flat_stream_base::max_stack); + flat_stream s(ioc); + error_code ec; + s.write_some(bs, ec); + } + + // write_some + + { + char b[detail::flat_stream_base::max_size]; + std::array bs; + bs[0] = net::const_buffer(b, + detail::flat_stream_base::max_stack); + bs[1] = net::const_buffer(b + bs[0].size(), 1024); + BEAST_EXPECT(buffer_size(bs) <= + detail::flat_stream_base::max_size); + flat_stream s(ioc); + error_code ec; + s.write_some(bs, ec); + } + + // async_write_some + + { + char b[detail::flat_stream_base::max_size]; + std::array bs; + bs[0] = net::const_buffer(b, + detail::flat_stream_base::max_stack); + bs[1] = net::const_buffer(b + bs[0].size(), 1024); + BEAST_EXPECT(buffer_size(bs) <= + detail::flat_stream_base::max_size); + flat_stream s(ioc); + error_code ec; + s.async_write_some(bs, + [](error_code, std::size_t) + { + }); + } + + // teardown + + { + test::stream ts(ioc); + flat_stream s(ioc); + ts.connect(s.next_layer()); + error_code ec; + teardown(websocket::role_type::client, s, ec); + } + + { + test::stream ts(ioc); + flat_stream s(ioc); + ts.connect(s.next_layer()); + async_teardown(websocket::role_type::client, s, + [](error_code) + { + }); + } } void @@ -49,9 +191,9 @@ public: for(auto const n : v0) v.emplace_back("", n); auto const result = - boost::beast::detail::flat_stream_base::coalesce(v, limit); + boost::beast::detail::flat_stream_base::flatten(v, limit); BEAST_EXPECT(result.size == count); - BEAST_EXPECT(result.needs_coalescing == copy); + BEAST_EXPECT(result.flatten == copy); return result; }; check({}, 1, 0, false); @@ -67,50 +209,11 @@ public: check({1,2,3,4}, 3, 3, true); } - void - testHttp() - { - pass(); - } - - void - testWebsocket() - { - { - error_code ec; - test::ws_echo_server es{log}; - net::io_context ioc; - websocket::stream> ws{ioc}; - ws.next_layer().next_layer().connect(es.stream()); - ws.handshake("localhost", "/", ec); - BEAST_EXPECTS(! ec, ec.message()); - ws.close({}, ec); - BEAST_EXPECTS(! ec, ec.message()); - } - { - test::ws_echo_server es{log}; - net::io_context ioc; - websocket::stream> ws{ioc}; - get_lowest_layer(ws).connect(es.stream()); - ws.async_handshake("localhost", "/", - [&](error_code) - { - ws.async_close({}, - [&](error_code) - { - }); - }); - ioc.run(); - } - } - void run() override { - testStream(); + testMembers(); testSplit(); - testHttp(); - testWebsocket(); } };