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:
* 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))
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<std::size_t>(
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<std::size_t>(
buffer_size(buffers), in_->write_max);

View File

@ -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<class BufferSequence>
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;

View File

@ -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> class write_op;
NextLayer stream_;

View File

@ -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();

View File

@ -12,26 +12,168 @@
#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/test/stream.hpp>
#include <boost/beast/websocket/role.hpp>
#include <initializer_list>
#include <vector>
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<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
@ -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<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
run() override
{
testStream();
testMembers();
testSplit();
testHttp();
testWebsocket();
}
};