flat_stream tests and tidy

This commit is contained in:
Vinnie Falco
2019-02-07 21:43:02 -08:00
parent 98834967c3
commit f15bbf10b4
6 changed files with 205 additions and 87 deletions

View File

@ -1,6 +1,7 @@
Version 212: Version 212:
* dynamic_buffer_ref tests and tidy * dynamic_buffer_ref tests and tidy
* flat_stream tests and tidy
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -332,7 +332,7 @@ read_some(MutableBufferSequence const& buffers,
if(in_->fc && in_->fc->fail(ec)) if(in_->fc && in_->fc->fail(ec))
return 0; 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) if(buffer_size(buffers) == 0)
{ {
ec.clear(); ec.clear();
@ -398,7 +398,7 @@ async_read_some(
return init.result.get(); 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) if(buffer_size(buffers) == 0)
{ {
lock.unlock(); lock.unlock();
@ -474,6 +474,17 @@ write_some(
++in_->nwrite; ++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 // connection closed
auto out = out_.lock(); auto out = out_.lock();
if(! out) if(! out)
@ -482,10 +493,6 @@ write_some(
return 0; return 0;
} }
// test failure
if(in_->fc && in_->fc->fail(ec))
return 0;
// copy buffers // copy buffers
auto n = std::min<std::size_t>( auto n = std::min<std::size_t>(
buffer_size(buffers), in_->write_max); buffer_size(buffers), in_->write_max);
@ -513,19 +520,6 @@ async_write_some(ConstBufferSequence const& buffers,
++in_->nwrite; ++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 // test failure
error_code ec; error_code ec;
if(in_->fc && in_->fc->fail(ec)) if(in_->fc && in_->fc->fail(ec))
@ -539,6 +533,30 @@ async_write_some(ConstBufferSequence const& buffers,
return init.result.get(); 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 // copy buffers
auto n = std::min<std::size_t>( auto n = std::min<std::size_t>(
buffer_size(buffers), in_->write_max); buffer_size(buffers), in_->write_max);

View File

@ -23,22 +23,25 @@ class flat_stream_base
public: public:
// Largest buffer size we will flatten. // Largest buffer size we will flatten.
// 16KB is the upper limit on reasonably sized HTTP messages. // 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; 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<class BufferSequence> template<class BufferSequence>
static static
coalesce_result flatten_result
coalesce( flatten(
BufferSequence const& buffers, std::size_t limit) 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 first = net::buffer_sequence_begin(buffers);
auto last = net::buffer_sequence_end(buffers); auto last = net::buffer_sequence_end(buffers);
if(first != last) if(first != last)
@ -56,7 +59,7 @@ public:
result.size += n; result.size += n;
prev = it; prev = it;
} }
result.needs_coalescing = prev != first; result.flatten = prev != first;
} }
} }
return result; return result;

View File

@ -90,13 +90,6 @@ class flat_stream
: private detail::flat_stream_base : private detail::flat_stream_base
#endif #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> class write_op; template<class> class write_op;
NextLayer stream_; NextLayer stream_;

View File

@ -43,8 +43,8 @@ public:
s.get_executor()) s.get_executor())
{ {
auto const result = auto const result =
coalesce(b, coalesce_limit); flatten(b, max_size);
if(result.needs_coalescing) if(result.flatten)
{ {
s.buffer_.clear(); s.buffer_.clear();
s.buffer_.commit(net::buffer_copy( 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< static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value, ConstBufferSequence>::value,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
auto const result = coalesce(buffers, coalesce_limit); auto const result = flatten(buffers, max_size);
if(result.needs_coalescing) if(result.flatten)
{ {
if(result.size > max_stack) if(result.size <= max_stack)
return stack_write_some(result.size, buffers, ec); return stack_write_some(result.size, buffers, ec);
buffer_.clear(); buffer_.clear();

View File

@ -12,26 +12,168 @@
#include "stream_tests.hpp" #include "stream_tests.hpp"
#include <boost/beast/test/websocket.hpp>
#include <boost/beast/test/yield_to.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp> #include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/_experimental/test/stream.hpp> #include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/websocket/role.hpp>
#include <initializer_list> #include <initializer_list>
#include <vector> #include <vector>
namespace boost { namespace boost {
namespace beast { namespace beast {
class flat_stream_test class flat_stream_test : public unit_test::suite
: public unit_test::suite
, public test::enable_yield_to
{ {
public: public:
void void
testStream() testMembers()
{ {
net::io_context ioc;
test_sync_stream<flat_stream<test::stream>>(); test_sync_stream<flat_stream<test::stream>>();
test_async_stream<flat_stream<test::stream>>(); test_async_stream<flat_stream<test::stream>>();
// read/write
{
error_code ec;
flat_stream<test::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<net::const_buffer, 3> 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<test::stream> s(ioc);
error_code ec;
s.write_some(bs, ec);
}
// write_some
{
char b[detail::flat_stream_base::max_size];
std::array<net::const_buffer, 2> 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<test::stream> s(ioc);
error_code ec;
s.write_some(bs, ec);
}
// async_write_some
{
char b[detail::flat_stream_base::max_size];
std::array<net::const_buffer, 2> 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<test::stream> s(ioc);
error_code ec;
s.async_write_some(bs,
[](error_code, std::size_t)
{
});
}
// teardown
{
test::stream ts(ioc);
flat_stream<test::stream> s(ioc);
ts.connect(s.next_layer());
error_code ec;
teardown(websocket::role_type::client, s, ec);
}
{
test::stream ts(ioc);
flat_stream<test::stream> s(ioc);
ts.connect(s.next_layer());
async_teardown(websocket::role_type::client, s,
[](error_code)
{
});
}
} }
void void
@ -49,9 +191,9 @@ public:
for(auto const n : v0) for(auto const n : v0)
v.emplace_back("", n); v.emplace_back("", n);
auto const result = 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.size == count);
BEAST_EXPECT(result.needs_coalescing == copy); BEAST_EXPECT(result.flatten == copy);
return result; return result;
}; };
check({}, 1, 0, false); check({}, 1, 0, false);
@ -67,50 +209,11 @@ public:
check({1,2,3,4}, 3, 3, true); 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<flat_stream<test::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<flat_stream<test::stream>> ws{ioc};
get_lowest_layer(ws).connect(es.stream());
ws.async_handshake("localhost", "/",
[&](error_code)
{
ws.async_close({},
[&](error_code)
{
});
});
ioc.run();
}
}
void void
run() override run() override
{ {
testStream(); testMembers();
testSplit(); testSplit();
testHttp();
testWebsocket();
} }
}; };