websocket read tests

This commit is contained in:
Vinnie Falco
2017-08-24 07:41:27 -07:00
parent 142b785119
commit df7b068fa7
9 changed files with 552 additions and 357 deletions

View File

@@ -11,6 +11,7 @@ WebSocket:
* Fix websocket close_op resume state * Fix websocket close_op resume state
* websocket write tests * websocket write tests
* split up websocket tests * split up websocket tests
* websocket read tests
API Changes: API Changes:

View File

@@ -30,7 +30,7 @@ public:
const char* const char*
name() const noexcept override name() const noexcept override
{ {
return "beast.websocket"; return "boost.beast.websocket";
} }
std::string std::string
@@ -38,13 +38,11 @@ public:
{ {
switch(static_cast<error>(ev)) switch(static_cast<error>(ev))
{ {
case error::closed: return "WebSocket connection closed normally";
case error::failed: return "WebSocket connection failed due to a protocol violation";
case error::handshake_failed: return "WebSocket Upgrade handshake failed";
case error::buffer_overflow: return "buffer overflow";
default: default:
return "beast.websocket error"; case error::failed: return "WebSocket connection failed due to a protocol violation";
case error::closed: return "WebSocket connection closed normally";
case error::handshake_failed: return "WebSocket upgrade handshake failed";
case error::buffer_overflow: return "WebSocket dynamic buffer overflow";
} }
} }

View File

@@ -351,8 +351,7 @@ operator()(
} }
if(! ws_.pmd_ || ! ws_.pmd_->rd_set) if(! ws_.pmd_ || ! ws_.pmd_->rd_set)
{ {
// Check for empty final frame if(ws_.rd_.remain > 0)
if(ws_.rd_.remain > 0 || ! ws_.rd_.fh.fin)
{ {
if(ws_.rd_.buf.size() == 0 && ws_.rd_.buf.max_size() > if(ws_.rd_.buf.size() == 0 && ws_.rd_.buf.max_size() >
(std::min)(clamp(ws_.rd_.remain), (std::min)(clamp(ws_.rd_.remain),
@@ -589,13 +588,13 @@ template<
class DynamicBuffer, class DynamicBuffer,
class Handler> class Handler>
class stream<NextLayer>::read_op class stream<NextLayer>::read_op
: public boost::asio::coroutine
{ {
Handler h_; Handler h_;
stream<NextLayer>& ws_; stream<NextLayer>& ws_;
DynamicBuffer& b_; DynamicBuffer& b_;
std::size_t limit_; std::size_t limit_;
std::size_t bytes_written_ = 0; std::size_t bytes_written_ = 0;
int step_ = 0;
bool some_; bool some_;
public: public:
@@ -644,8 +643,8 @@ public:
bool asio_handler_is_continuation(read_op* op) bool asio_handler_is_continuation(read_op* op)
{ {
using boost::asio::asio_handler_is_continuation; using boost::asio::asio_handler_is_continuation;
return op->step_ >= 2 || return asio_handler_is_continuation(
asio_handler_is_continuation(std::addressof(op->h_)); std::addressof(op->h_));
} }
template<class Function> template<class Function>
@@ -668,54 +667,41 @@ operator()(
std::size_t bytes_transferred) std::size_t bytes_transferred)
{ {
using beast::detail::clamp; using beast::detail::clamp;
switch(ec ? 3 : step_) using buffers_type = typename
DynamicBuffer::mutable_buffers_type;
boost::optional<buffers_type> mb;
BOOST_ASIO_CORO_REENTER(*this)
{ {
case 0: do
{
if(ws_.failed_)
{ {
// Reads after failure are aborted try
ec = boost::asio::error::operation_aborted; {
break; mb.emplace(b_.prepare(clamp(
ws_.read_size_hint(b_), limit_)));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
}
if(ec)
{
BOOST_ASIO_CORO_YIELD
ws_.get_io_service().post(
bind_handler(std::move(*this),
error::buffer_overflow, 0));
break;
}
BOOST_ASIO_CORO_YIELD
read_some_op<buffers_type, read_op>{
std::move(*this), ws_, *mb}();
if(ec)
break;
b_.commit(bytes_transferred);
bytes_written_ += bytes_transferred;
} }
step_ = 1; while(! some_ && ! ws_.is_message_done());
do_read:
using buffers_type = typename
DynamicBuffer::mutable_buffers_type;
auto const size = clamp(
ws_.read_size_hint(b_), limit_);
boost::optional<buffers_type> mb;
try
{
mb.emplace(b_.prepare(size));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
break;
}
return read_some_op<buffers_type, read_op>{
std::move(*this), ws_, *mb}();
}
case 1:
case 2:
b_.commit(bytes_transferred);
bytes_written_ += bytes_transferred;
if(some_ || ws_.is_message_done())
break;
step_ = 2;
goto do_read;
case 3:
break;
}
if(step_ == 0)
return ws_.get_io_service().post(
bind_handler(std::move(h_),
ec, bytes_written_));
else
h_(ec, bytes_written_); h_(ec, bytes_written_);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -747,12 +733,6 @@ read(DynamicBuffer& buffer, error_code& ec)
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(beast::is_dynamic_buffer<DynamicBuffer>::value, static_assert(beast::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met"); "DynamicBuffer requirements not met");
// Make sure the stream is open
if(failed_)
{
ec = boost::asio::error::operation_aborted;
return 0;
}
std::size_t bytes_written = 0; std::size_t bytes_written = 0;
do do
{ {
@@ -822,12 +802,6 @@ read_some(
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value, static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met"); "DynamicBuffer requirements not met");
// Make sure the stream is open
if(failed_)
{
ec = boost::asio::error::operation_aborted;
return 0;
}
using beast::detail::clamp; using beast::detail::clamp;
if(! limit) if(! limit)
limit = (std::numeric_limits<std::size_t>::max)(); limit = (std::numeric_limits<std::size_t>::max)();
@@ -1017,17 +991,12 @@ loop:
rd_.buf.consume(len); rd_.buf.consume(len);
if(ctrl_cb_) if(ctrl_cb_)
ctrl_cb_(frame_type::close, cr_.reason); ctrl_cb_(frame_type::close, cr_.reason);
if(! wr_close_) BOOST_ASSERT(! wr_close_);
{ // _Start the WebSocket Closing Handshake_
// _Start the WebSocket Closing Handshake_ do_fail(
do_fail( cr.code == close_code::none ?
cr.code == close_code::none ? close_code::normal : cr.code,
close_code::normal : cr.code, error::closed, ec);
error::closed, ec);
return bytes_written;
}
// _Close the WebSocket Connection_
do_fail(close_code::none, error::closed, ec);
return bytes_written; return bytes_written;
} }
} }
@@ -1044,8 +1013,7 @@ loop:
} }
if(! pmd_ || ! pmd_->rd_set) if(! pmd_ || ! pmd_->rd_set)
{ {
// Check for empty final frame if(rd_.remain > 0)
if(rd_.remain > 0 || ! rd_.fh.fin)
{ {
if(rd_.buf.size() == 0 && rd_.buf.max_size() > if(rd_.buf.size() == 0 && rd_.buf.max_size() >
(std::min)(clamp(rd_.remain), (std::min)(clamp(rd_.remain),
@@ -1116,10 +1084,8 @@ loop:
! rd_.utf8.finish())) ! rd_.utf8.finish()))
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail( do_fail(close_code::bad_payload,
close_code::bad_payload, error::failed, ec);
error::failed,
ec);
return bytes_written; return bytes_written;
} }
} }
@@ -1226,10 +1192,8 @@ loop:
if(rd_msg_max_ && beast::detail::sum_exceeds( if(rd_msg_max_ && beast::detail::sum_exceeds(
rd_.size, zs.total_out, rd_msg_max_)) rd_.size, zs.total_out, rd_msg_max_))
{ {
do_fail( do_fail(close_code::too_big,
close_code::too_big, error::failed, ec);
error::failed,
ec);
return bytes_written; return bytes_written;
} }
cb.consume(zs.total_out); cb.consume(zs.total_out);
@@ -1246,10 +1210,8 @@ loop:
rd_.done && ! rd_.utf8.finish())) rd_.done && ! rd_.utf8.finish()))
{ {
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
do_fail( do_fail(close_code::bad_payload,
close_code::bad_payload, error::failed, ec);
error::failed,
ec);
return bytes_written; return bytes_written;
} }
} }

View File

@@ -27,12 +27,18 @@ public:
pmd.client_enable = false; pmd.client_enable = false;
pmd.server_enable = false; pmd.server_enable = false;
// normal close // close
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
{ {
w.close(ws, {}); w.close(ws, {});
}); });
// close with code
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, close_code::going_away);
});
// double close // double close
{ {
echo_server es{log}; echo_server es{log};

View File

@@ -37,10 +37,10 @@ public:
void run() override void run() override
{ {
check("beast.websocket", error::closed); check("boost.beast.websocket", error::closed);
check("beast.websocket", error::failed); check("boost.beast.websocket", error::failed);
check("beast.websocket", error::handshake_failed); check("boost.beast.websocket", error::handshake_failed);
check("beast.websocket", error::buffer_overflow); check("boost.beast.websocket", error::buffer_overflow);
} }
}; };

View File

@@ -19,18 +19,287 @@ namespace websocket {
class stream_read_test : public websocket_test_suite class stream_read_test : public websocket_test_suite
{ {
public: public:
template<class Wrap>
void
doReadTest(
Wrap const& w,
ws_type& ws,
close_code code)
{
try
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != error::closed)
throw;
BEAST_EXPECT(
ws.reason().code == code);
}
}
template<class Wrap>
void
doFailTest(
Wrap const& w,
ws_type& ws,
error_code ev)
{
try
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != ev)
throw;
}
}
template<class Wrap> template<class Wrap>
void void
doTestRead(Wrap const& w) doTestRead(Wrap const& w)
{ {
using boost::asio::buffer;
permessage_deflate pmd; permessage_deflate pmd;
pmd.client_enable = false; pmd.client_enable = false;
pmd.server_enable = false; pmd.server_enable = false;
// Read close frames // empty, fragmented message
doTest(pmd, [&](ws_type& ws)
{ {
// VFALCO What about asynchronous?? ws.next_layer().append(
string_view(
"\x01\x00" "\x80\x00", 4));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(b.size() == 0);
});
// two part message
// this triggers "fill the read buffer first"
doTest(pmd, [&](ws_type& ws)
{
w.write_raw(ws, sbuf(
"\x01\x81\xff\xff\xff\xff"));
w.write_raw(ws, sbuf(
"\xd5"));
w.write_raw(ws, sbuf(
"\x80\x81\xff\xff\xff\xff\xd5"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == "**");
});
// ping
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x89, 0x00));
bool invoked = false;
auto cb = [&](frame_type kind, string_view)
{
BEAST_EXPECT(! invoked);
BEAST_EXPECT(kind == frame_type::ping);
invoked = true;
};
ws.control_callback(cb);
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(invoked);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(to_string(b.data()) == "Hello");
});
// ping
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x88, 0x00));
bool invoked = false;
auto cb = [&](frame_type kind, string_view)
{
BEAST_EXPECT(! invoked);
BEAST_EXPECT(kind == frame_type::close);
invoked = true;
};
ws.control_callback(cb);
w.write(ws, sbuf("Hello"));
doReadTest(w, ws, close_code::none);
});
// ping then message
doTest(pmd, [&](ws_type& ws)
{
bool once = false;
auto cb =
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "");
};
ws.control_callback(cb);
w.ping(ws, "");
ws.binary(true);
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(once);
BEAST_EXPECT(ws.got_binary());
BEAST_EXPECT(to_string(b.data()) == "Hello");
});
// ping then fragmented message
doTest(pmd, [&](ws_type& ws)
{
bool once = false;
auto cb =
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "payload");
};
ws.control_callback(cb);
ws.ping("payload");
w.write_some(ws, false, sbuf("Hello, "));
w.write_some(ws, false, sbuf(""));
w.write_some(ws, true, sbuf("World!"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(once);
BEAST_EXPECT(to_string(b.data()) == "Hello, World!");
});
// already closed
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, {});
multi_buffer b;
doFailTest(w, ws,
boost::asio::error::operation_aborted);
});
// buffer overflow
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello, world!";
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, buffer(s));
try
{
multi_buffer b(3);
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != error::buffer_overflow)
throw;
}
});
// bad utf8, big
doTest(pmd, [&](ws_type& ws)
{
auto const s = std::string(2000, '*') +
random_string();
ws.text(true);
w.write(ws, buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
// invalid fixed frame header
doTest(pmd, [&](ws_type& ws)
{
w.write_raw(ws, cbuf(
0x8f, 0x80, 0xff, 0xff, 0xff, 0xff));
doReadTest(w, ws, close_code::protocol_error);
});
// receive bad close
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x88, 0x02, 0x03, 0xed));
doFailTest(w, ws, error::failed);
});
// expected cont
doTest(pmd, [&](ws_type& ws)
{
w.write_some(ws, false, boost::asio::null_buffers{});
w.write_raw(ws, cbuf(
0x81, 0x80, 0xff, 0xff, 0xff, 0xff));
doReadTest(w, ws, close_code::protocol_error);
});
// message size above 2^64
doTest(pmd, [&](ws_type& ws)
{
w.write_some(ws, false, sbuf("*"));
w.write_raw(ws, cbuf(
0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
doReadTest(w, ws, close_code::too_big);
});
// message size exceeds max
doTest(pmd, [&](ws_type& ws)
{
ws.read_message_max(1);
w.write(ws, sbuf("**"));
doFailTest(w, ws, error::failed);
});
// unexpected cont
doTest(pmd, [&](ws_type& ws)
{
w.write_raw(ws, cbuf(
0x80, 0x80, 0xff, 0xff, 0xff, 0xff));
doReadTest(w, ws, close_code::protocol_error);
});
// bad utf8
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
doFailTest(w, ws, error::failed);
});
// incomplete utf8
doTest(pmd, [&](ws_type& ws)
{
std::string const s =
"Hello, world!" "\xc0";
w.write(ws, buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
// incomplete utf8, big
doTest(pmd, [&](ws_type& ws)
{
std::string const s =
random_string() +
"Hello, world!" "\xc0";
w.write(ws, buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
// close frames
{
auto const check = auto const check =
[&](error_code ev, string_view s) [&](error_code ev, string_view s)
{ {
@@ -71,8 +340,24 @@ public:
"\x88\x06\xfc\x15utf8"); "\x88\x06\xfc\x15utf8");
} }
//
// permessage-deflate
//
pmd.client_enable = true; pmd.client_enable = true;
pmd.server_enable = true; pmd.server_enable = true;
pmd.client_max_window_bits = 9;
pmd.server_max_window_bits = 9;
pmd.compLevel = 1;
// message size limit
doTest(pmd, [&](ws_type& ws)
{
std::string const s = std::string(128, '*');
w.write(ws, buffer(s));
ws.read_message_max(32);
doFailTest(w, ws, error::failed);
});
// invalid inflate block // invalid inflate block
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
@@ -99,18 +384,136 @@ public:
throw; throw;
} }
}); });
// no_context_takeover
pmd.server_no_context_takeover = true;
doTest(pmd, [&](ws_type& ws)
{
auto const& s = random_string();
ws.binary(true);
w.write(ws, buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s);
});
pmd.client_no_context_takeover = false;
}
template<class Wrap>
void
doTestRead(
permessage_deflate const& pmd,
Wrap const& w)
{
using boost::asio::buffer;
// message
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello, world!";
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(to_string(b.data()) == s);
});
// empty message
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "";
ws.text(true);
w.write(ws, buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(to_string(b.data()) == s);
});
// partial message
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello";
w.write(ws, buffer(s));
char buf[3];
auto const bytes_written =
w.read_some(ws, buffer(buf, sizeof(buf)));
BEAST_EXPECT(bytes_written > 0);
BEAST_EXPECT(
string_view(buf, 3).substr(0, bytes_written) ==
s.substr(0, bytes_written));
});
// partial message, dynamic buffer
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "Hello, world!";
w.write(ws, buffer(s));
multi_buffer b;
auto bytes_written =
w.read_some(ws, 3, b);
BEAST_EXPECT(bytes_written > 0);
BEAST_EXPECT(to_string(b.data()) ==
s.substr(0, b.size()));
w.read_some(ws, 256, b);
BEAST_EXPECT(to_string(b.data()) == s);
});
// big message
doTest(pmd, [&](ws_type& ws)
{
auto const& s = random_string();
ws.binary(true);
w.write(ws, buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s);
});
// message, bad utf8
doTest(pmd, [&](ws_type& ws)
{
std::string const s = "\x03\xea\xf0\x28\x8c\xbc";
ws.auto_fragment(false);
ws.text(true);
w.write(ws, buffer(s));
doReadTest(w, ws, close_code::bad_payload);
});
} }
void void
testRead() testRead()
{ {
doTestRead(SyncClient{}); using boost::asio::buffer;
doTestRead(SyncClient{});
yield_to([&](yield_context yield) yield_to([&](yield_context yield)
{ {
doTestRead(AsyncClient{yield}); doTestRead(AsyncClient{yield});
}); });
permessage_deflate pmd;
pmd.client_enable = false;
pmd.server_enable = false;
doTestRead(pmd, SyncClient{});
yield_to([&](yield_context yield)
{
doTestRead(pmd, AsyncClient{yield});
});
pmd.client_enable = true;
pmd.server_enable = true;
pmd.client_max_window_bits = 9;
pmd.server_max_window_bits = 9;
pmd.compLevel = 1;
doTestRead(pmd, SyncClient{});
yield_to([&](yield_context yield)
{
doTestRead(pmd, AsyncClient{yield});
});
// Read close frames // Read close frames
{ {
auto const check = auto const check =
@@ -144,6 +547,39 @@ public:
check(error::closed, check(error::closed,
"\x88\x06\xfc\x15utf8"); "\x88\x06\xfc\x15utf8");
} }
// suspend on write
{
echo_server es{log};
error_code ec;
boost::asio::io_service ios;
stream<test::stream> ws{ios};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/", ec);
BEAST_EXPECTS(! ec, ec.message());
// insert a ping
ws.next_layer().append(string_view(
"\x89\x00", 2));
std::size_t count = 0;
multi_buffer b;
std::string const s = "Hello, world";
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(to_string(b.data()) == s);
});
ws.async_write(buffer(s),
[&](error_code ec)
{
++count;
BEAST_EXPECTS(! ec, ec.message());
});
BEAST_EXPECT(ws.wr_block_);
ios.run();
BEAST_EXPECT(count == 2);
}
} }
void void

View File

@@ -47,102 +47,6 @@ public:
{ {
using boost::asio::buffer; using boost::asio::buffer;
// send empty message
doTest(pmd, [&](ws_type& ws)
{
ws.text(true);
w.write(ws, boost::asio::null_buffers{});
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(b.size() == 0);
});
// send message
doTest(pmd, [&](ws_type& ws)
{
ws.auto_fragment(false);
ws.binary(false);
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(to_string(b.data()) == "Hello");
});
// read_some
doTest(pmd, [&](ws_type& ws)
{
w.write(ws, sbuf("Hello"));
char buf[10];
auto const bytes_written =
w.read_some(ws, buffer(buf, sizeof(buf)));
BEAST_EXPECT(bytes_written > 0);
buf[bytes_written] = 0;
BEAST_EXPECT(
string_view(buf).substr(0, bytes_written) ==
string_view("Hello").substr(0, bytes_written));
});
// close, no payload
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, {});
});
// close with code
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, close_code::going_away);
});
// send ping and message
doTest(pmd, [&](ws_type& ws)
{
bool once = false;
auto cb =
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "");
};
ws.control_callback(cb);
w.ping(ws, "");
ws.binary(true);
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(once);
BEAST_EXPECT(ws.got_binary());
BEAST_EXPECT(to_string(b.data()) == "Hello");
});
// send ping and fragmented message
doTest(pmd, [&](ws_type& ws)
{
bool once = false;
auto cb =
[&](frame_type kind, string_view s)
{
BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once);
once = true;
BEAST_EXPECT(s == "payload");
};
ws.control_callback(cb);
ws.ping("payload");
w.write_some(ws, false, sbuf("Hello, "));
w.write_some(ws, false, sbuf(""));
w.write_some(ws, true, sbuf("World!"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(once);
BEAST_EXPECT(to_string(b.data()) == "Hello, World!");
ws.control_callback();
});
// send pong // send pong
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
{ {
@@ -170,109 +74,6 @@ public:
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
}); });
// unexpected cont
doTest(pmd, [&](ws_type& ws)
{
w.write_raw(ws, cbuf(
0x80, 0x80, 0xff, 0xff, 0xff, 0xff));
doCloseTest(w, ws, close_code::protocol_error);
});
// invalid fixed frame header
doTest(pmd, [&](ws_type& ws)
{
w.write_raw(ws, cbuf(
0x8f, 0x80, 0xff, 0xff, 0xff, 0xff));
doCloseTest(w, ws, close_code::protocol_error);
});
if(! pmd.client_enable)
{
// expected cont
doTest(pmd, [&](ws_type& ws)
{
w.write_some(ws, false, boost::asio::null_buffers{});
w.write_raw(ws, cbuf(
0x81, 0x80, 0xff, 0xff, 0xff, 0xff));
doCloseTest(w, ws, close_code::protocol_error);
});
// message size above 2^64
doTest(pmd, [&](ws_type& ws)
{
w.write_some(ws, false, cbuf(0x00));
w.write_raw(ws, cbuf(
0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
doCloseTest(w, ws, close_code::too_big);
});
/*
// message size exceeds max
doTest(pmd, [&](ws_type& ws)
{
// VFALCO This was never implemented correctly
ws.read_message_max(1);
w.write(ws, cbuf(0x81, 0x02, '*', '*'));
doCloseTest(w, ws, close_code::too_big);
});
*/
}
// receive ping
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x89, 0x00));
bool invoked = false;
auto cb = [&](frame_type kind, string_view)
{
BEAST_EXPECT(! invoked);
BEAST_EXPECT(kind == frame_type::ping);
invoked = true;
};
ws.control_callback(cb);
w.write(ws, sbuf("Hello"));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(invoked);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(to_string(b.data()) == "Hello");
});
// receive ping
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x88, 0x00));
bool invoked = false;
auto cb = [&](frame_type kind, string_view)
{
BEAST_EXPECT(! invoked);
BEAST_EXPECT(kind == frame_type::close);
invoked = true;
};
ws.control_callback(cb);
w.write(ws, sbuf("Hello"));
doCloseTest(w, ws, close_code::none);
});
// receive bad utf8
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
doFailTest(w, ws, error::failed);
});
// receive bad close
doTest(pmd, [&](ws_type& ws)
{
put(ws.next_layer().buffer(), cbuf(
0x88, 0x02, 0x03, 0xed));
doFailTest(w, ws, error::failed);
});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -303,6 +104,7 @@ public:
testOptions(); testOptions();
#if 0
auto const testStream = auto const testStream =
[this](permessage_deflate const& pmd) [this](permessage_deflate const& pmd)
{ {
@@ -336,6 +138,7 @@ public:
pmd.compLevel = 1; pmd.compLevel = 1;
pmd.memLevel = 1; pmd.memLevel = 1;
testStream(pmd); testStream(pmd);
#endif
} }
}; };

View File

@@ -70,8 +70,9 @@ public:
, ws_(ts_) , ws_(ts_)
{ {
permessage_deflate pmd; permessage_deflate pmd;
pmd.client_enable = true;
pmd.server_enable = true; pmd.server_enable = true;
pmd.server_max_window_bits = 9;
pmd.compLevel = 1;
ws_.set_option(pmd); ws_.set_option(pmd);
switch(k) switch(k)
@@ -238,48 +239,6 @@ public:
} }
}; };
template<class Wrap>
void
doCloseTest(
Wrap const& w,
ws_type& ws,
close_code code)
{
try
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != error::closed)
throw;
BEAST_EXPECT(
ws.reason().code == code);
}
}
template<class Wrap>
void
doFailTest(
Wrap const& w,
ws_type& ws,
error_code ev)
{
try
{
multi_buffer b;
w.read(ws, b);
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
if(se.code() != ev)
throw;
}
}
template<class Test> template<class Test>
void void
doTestLoop(Test const& f) doTestLoop(Test const& f)

View File

@@ -23,10 +23,36 @@ public:
void void
doTestWrite(Wrap const& w) doTestWrite(Wrap const& w)
{ {
using boost::asio::buffer;
permessage_deflate pmd; permessage_deflate pmd;
pmd.client_enable = false; pmd.client_enable = false;
pmd.server_enable = false; pmd.server_enable = false;
// message
doTest(pmd, [&](ws_type& ws)
{
ws.auto_fragment(false);
ws.binary(false);
std::string const s = "Hello, world!";
w.write(ws, buffer(s));
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(to_string(b.data()) == s);
});
// empty message
doTest(pmd, [&](ws_type& ws)
{
ws.text(true);
w.write(ws, boost::asio::null_buffers{});
multi_buffer b;
w.read(ws, b);
BEAST_EXPECT(ws.got_text());
BEAST_EXPECT(b.size() == 0);
});
// continuation // continuation
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
{ {
@@ -34,8 +60,8 @@ public:
std::size_t const chop = 3; std::size_t const chop = 3;
BOOST_ASSERT(chop < s.size()); BOOST_ASSERT(chop < s.size());
w.write_some(ws, false, w.write_some(ws, false,
boost::asio::buffer(s.data(), chop)); buffer(s.data(), chop));
w.write_some(ws, true, boost::asio::buffer( w.write_some(ws, true, buffer(
s.data() + chop, s.size() - chop)); s.data() + chop, s.size() - chop));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
@@ -47,7 +73,7 @@ public:
{ {
ws.auto_fragment(false); ws.auto_fragment(false);
std::string const s = "Hello"; std::string const s = "Hello";
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -59,7 +85,7 @@ public:
ws.auto_fragment(false); ws.auto_fragment(false);
ws.write_buffer_size(16); ws.write_buffer_size(16);
std::string const s(32, '*'); std::string const s(32, '*');
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -70,7 +96,7 @@ public:
{ {
ws.auto_fragment(true); ws.auto_fragment(true);
std::string const s(16384, '*'); std::string const s(16384, '*');
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -88,7 +114,7 @@ public:
w.accept(ws); w.accept(ws);
ws.auto_fragment(false); ws.auto_fragment(false);
std::string const s = "Hello"; std::string const s = "Hello";
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -114,7 +140,7 @@ public:
w.accept(ws); w.accept(ws);
ws.auto_fragment(true); ws.auto_fragment(true);
std::string const s(16384, '*'); std::string const s(16384, '*');
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -130,13 +156,14 @@ public:
pmd.client_enable = true; pmd.client_enable = true;
pmd.server_enable = true; pmd.server_enable = true;
pmd.compLevel = 1;
// deflate // deflate
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
{ {
auto const& s = random_string(); auto const& s = random_string();
ws.binary(true); ws.binary(true);
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -151,8 +178,8 @@ public:
// This call should produce no // This call should produce no
// output due to compression latency. // output due to compression latency.
w.write_some(ws, false, w.write_some(ws, false,
boost::asio::buffer(s.data(), chop)); buffer(s.data(), chop));
w.write_some(ws, true, boost::asio::buffer( w.write_some(ws, true, buffer(
s.data() + chop, s.size() - chop)); s.data() + chop, s.size() - chop));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
@@ -165,7 +192,7 @@ public:
{ {
auto const& s = random_string(); auto const& s = random_string();
ws.binary(true); ws.binary(true);
w.write(ws, boost::asio::buffer(s)); w.write(ws, buffer(s));
flat_buffer b; flat_buffer b;
w.read(ws, b); w.read(ws, b);
BEAST_EXPECT(to_string(b.data()) == s); BEAST_EXPECT(to_string(b.data()) == s);
@@ -175,6 +202,8 @@ public:
void void
testWrite() testWrite()
{ {
using boost::asio::buffer;
doTestWrite(SyncClient{}); doTestWrite(SyncClient{});
yield_to([&](yield_context yield) yield_to([&](yield_context yield)
@@ -249,7 +278,7 @@ public:
std::size_t count = 0; std::size_t count = 0;
std::string const s(16384, '*'); std::string const s(16384, '*');
ws.auto_fragment(true); ws.auto_fragment(true);
ws.async_write(boost::asio::buffer(s), ws.async_write(buffer(s),
[&](error_code ec) [&](error_code ec)
{ {
++count; ++count;
@@ -295,7 +324,7 @@ public:
std::size_t count = 0; std::size_t count = 0;
std::string const s(16384, '*'); std::string const s(16384, '*');
ws.auto_fragment(true); ws.auto_fragment(true);
ws.async_write(boost::asio::buffer(s), ws.async_write(buffer(s),
[&](error_code ec) [&](error_code ec)
{ {
++count; ++count;
@@ -338,6 +367,7 @@ public:
{ {
permessage_deflate pmd; permessage_deflate pmd;
pmd.client_enable = true; pmd.client_enable = true;
pmd.compLevel = 1;
ws.set_option(pmd); ws.set_option(pmd);
} }
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
@@ -346,7 +376,7 @@ public:
std::size_t count = 0; std::size_t count = 0;
auto const& s = random_string(); auto const& s = random_string();
ws.binary(true); ws.binary(true);
ws.async_write(boost::asio::buffer(s), ws.async_write(buffer(s),
[&](error_code ec) [&](error_code ec)
{ {
++count; ++count;