diff --git a/CHANGELOG.md b/CHANGELOG.md index c3ba22bb..49dd1643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ WebSocket: * Fix websocket close_op resume state * websocket write tests * split up websocket tests +* websocket read tests API Changes: diff --git a/include/boost/beast/websocket/impl/error.ipp b/include/boost/beast/websocket/impl/error.ipp index 0c20a615..9acd0c1d 100644 --- a/include/boost/beast/websocket/impl/error.ipp +++ b/include/boost/beast/websocket/impl/error.ipp @@ -30,7 +30,7 @@ public: const char* name() const noexcept override { - return "beast.websocket"; + return "boost.beast.websocket"; } std::string @@ -38,13 +38,11 @@ public: { switch(static_cast(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: - 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"; } } diff --git a/include/boost/beast/websocket/impl/read.ipp b/include/boost/beast/websocket/impl/read.ipp index d70f1fcf..721c9614 100644 --- a/include/boost/beast/websocket/impl/read.ipp +++ b/include/boost/beast/websocket/impl/read.ipp @@ -351,8 +351,7 @@ operator()( } if(! ws_.pmd_ || ! ws_.pmd_->rd_set) { - // Check for empty final frame - if(ws_.rd_.remain > 0 || ! ws_.rd_.fh.fin) + if(ws_.rd_.remain > 0) { if(ws_.rd_.buf.size() == 0 && ws_.rd_.buf.max_size() > (std::min)(clamp(ws_.rd_.remain), @@ -589,13 +588,13 @@ template< class DynamicBuffer, class Handler> class stream::read_op + : public boost::asio::coroutine { Handler h_; stream& ws_; DynamicBuffer& b_; std::size_t limit_; std::size_t bytes_written_ = 0; - int step_ = 0; bool some_; public: @@ -644,8 +643,8 @@ public: bool asio_handler_is_continuation(read_op* op) { using boost::asio::asio_handler_is_continuation; - return op->step_ >= 2 || - asio_handler_is_continuation(std::addressof(op->h_)); + return asio_handler_is_continuation( + std::addressof(op->h_)); } template @@ -668,54 +667,41 @@ operator()( std::size_t bytes_transferred) { using beast::detail::clamp; - switch(ec ? 3 : step_) + using buffers_type = typename + DynamicBuffer::mutable_buffers_type; + boost::optional mb; + BOOST_ASIO_CORO_REENTER(*this) { - case 0: - { - if(ws_.failed_) + do { - // Reads after failure are aborted - ec = boost::asio::error::operation_aborted; - break; + try + { + 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{ + std::move(*this), ws_, *mb}(); + if(ec) + break; + b_.commit(bytes_transferred); + bytes_written_ += bytes_transferred; } - step_ = 1; - do_read: - using buffers_type = typename - DynamicBuffer::mutable_buffers_type; - auto const size = clamp( - ws_.read_size_hint(b_), limit_); - boost::optional mb; - try - { - mb.emplace(b_.prepare(size)); - } - catch(std::length_error const&) - { - ec = error::buffer_overflow; - break; - } - return read_some_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 + while(! some_ && ! ws_.is_message_done()); h_(ec, bytes_written_); + } } //------------------------------------------------------------------------------ @@ -747,12 +733,6 @@ read(DynamicBuffer& buffer, error_code& ec) "SyncStream requirements not met"); static_assert(beast::is_dynamic_buffer::value, "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; do { @@ -822,12 +802,6 @@ read_some( "SyncStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); - // Make sure the stream is open - if(failed_) - { - ec = boost::asio::error::operation_aborted; - return 0; - } using beast::detail::clamp; if(! limit) limit = (std::numeric_limits::max)(); @@ -1017,17 +991,12 @@ loop: rd_.buf.consume(len); if(ctrl_cb_) ctrl_cb_(frame_type::close, cr_.reason); - if(! wr_close_) - { - // _Start the WebSocket Closing Handshake_ - do_fail( - cr.code == close_code::none ? - close_code::normal : cr.code, - error::closed, ec); - return bytes_written; - } - // _Close the WebSocket Connection_ - do_fail(close_code::none, error::closed, ec); + BOOST_ASSERT(! wr_close_); + // _Start the WebSocket Closing Handshake_ + do_fail( + cr.code == close_code::none ? + close_code::normal : cr.code, + error::closed, ec); return bytes_written; } } @@ -1044,8 +1013,7 @@ loop: } if(! pmd_ || ! pmd_->rd_set) { - // Check for empty final frame - if(rd_.remain > 0 || ! rd_.fh.fin) + if(rd_.remain > 0) { if(rd_.buf.size() == 0 && rd_.buf.max_size() > (std::min)(clamp(rd_.remain), @@ -1116,10 +1084,8 @@ loop: ! rd_.utf8.finish())) { // _Fail the WebSocket Connection_ - do_fail( - close_code::bad_payload, - error::failed, - ec); + do_fail(close_code::bad_payload, + error::failed, ec); return bytes_written; } } @@ -1226,10 +1192,8 @@ loop: if(rd_msg_max_ && beast::detail::sum_exceeds( rd_.size, zs.total_out, rd_msg_max_)) { - do_fail( - close_code::too_big, - error::failed, - ec); + do_fail(close_code::too_big, + error::failed, ec); return bytes_written; } cb.consume(zs.total_out); @@ -1246,10 +1210,8 @@ loop: rd_.done && ! rd_.utf8.finish())) { // _Fail the WebSocket Connection_ - do_fail( - close_code::bad_payload, - error::failed, - ec); + do_fail(close_code::bad_payload, + error::failed, ec); return bytes_written; } } diff --git a/test/beast/websocket/close.cpp b/test/beast/websocket/close.cpp index 3e00ec80..d03b9914 100644 --- a/test/beast/websocket/close.cpp +++ b/test/beast/websocket/close.cpp @@ -27,12 +27,18 @@ public: pmd.client_enable = false; pmd.server_enable = false; - // normal close + // close doTest(pmd, [&](ws_type& ws) { w.close(ws, {}); }); + // close with code + doTest(pmd, [&](ws_type& ws) + { + w.close(ws, close_code::going_away); + }); + // double close { echo_server es{log}; diff --git a/test/beast/websocket/error.cpp b/test/beast/websocket/error.cpp index 2f9dc57a..bb2bea0f 100644 --- a/test/beast/websocket/error.cpp +++ b/test/beast/websocket/error.cpp @@ -37,10 +37,10 @@ public: void run() override { - check("beast.websocket", error::closed); - check("beast.websocket", error::failed); - check("beast.websocket", error::handshake_failed); - check("beast.websocket", error::buffer_overflow); + check("boost.beast.websocket", error::closed); + check("boost.beast.websocket", error::failed); + check("boost.beast.websocket", error::handshake_failed); + check("boost.beast.websocket", error::buffer_overflow); } }; diff --git a/test/beast/websocket/read.cpp b/test/beast/websocket/read.cpp index 7ca92f09..ac894efc 100644 --- a/test/beast/websocket/read.cpp +++ b/test/beast/websocket/read.cpp @@ -19,18 +19,287 @@ namespace websocket { class stream_read_test : public websocket_test_suite { public: + template + 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 + 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 void doTestRead(Wrap const& w) { + using boost::asio::buffer; + permessage_deflate pmd; pmd.client_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 = [&](error_code ev, string_view s) { @@ -71,8 +340,24 @@ public: "\x88\x06\xfc\x15utf8"); } + // + // permessage-deflate + // + pmd.client_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 doTest(pmd, [&](ws_type& ws) @@ -99,18 +384,136 @@ public: 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 + 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 testRead() { - doTestRead(SyncClient{}); + using boost::asio::buffer; + doTestRead(SyncClient{}); yield_to([&](yield_context 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 { auto const check = @@ -144,6 +547,39 @@ public: check(error::closed, "\x88\x06\xfc\x15utf8"); } + + // suspend on write + { + echo_server es{log}; + error_code ec; + boost::asio::io_service ios; + 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 diff --git a/test/beast/websocket/stream.cpp b/test/beast/websocket/stream.cpp index 8b131ed3..c84b14d7 100644 --- a/test/beast/websocket/stream.cpp +++ b/test/beast/websocket/stream.cpp @@ -47,102 +47,6 @@ public: { 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 doTest(pmd, [&](ws_type& ws) { @@ -170,109 +74,6 @@ public: w.read(ws, b); 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(); +#if 0 auto const testStream = [this](permessage_deflate const& pmd) { @@ -336,6 +138,7 @@ public: pmd.compLevel = 1; pmd.memLevel = 1; testStream(pmd); +#endif } }; diff --git a/test/beast/websocket/test.hpp b/test/beast/websocket/test.hpp index d7700aa9..6926d312 100644 --- a/test/beast/websocket/test.hpp +++ b/test/beast/websocket/test.hpp @@ -70,8 +70,9 @@ public: , ws_(ts_) { permessage_deflate pmd; - pmd.client_enable = true; pmd.server_enable = true; + pmd.server_max_window_bits = 9; + pmd.compLevel = 1; ws_.set_option(pmd); switch(k) @@ -238,48 +239,6 @@ public: } }; - template - 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 - 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 void doTestLoop(Test const& f) diff --git a/test/beast/websocket/write.cpp b/test/beast/websocket/write.cpp index 67bc9d69..c4391093 100644 --- a/test/beast/websocket/write.cpp +++ b/test/beast/websocket/write.cpp @@ -23,10 +23,36 @@ public: void doTestWrite(Wrap const& w) { + using boost::asio::buffer; + permessage_deflate pmd; pmd.client_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 doTest(pmd, [&](ws_type& ws) { @@ -34,8 +60,8 @@ public: std::size_t const chop = 3; BOOST_ASSERT(chop < s.size()); w.write_some(ws, false, - boost::asio::buffer(s.data(), chop)); - w.write_some(ws, true, boost::asio::buffer( + buffer(s.data(), chop)); + w.write_some(ws, true, buffer( s.data() + chop, s.size() - chop)); flat_buffer b; w.read(ws, b); @@ -47,7 +73,7 @@ public: { ws.auto_fragment(false); std::string const s = "Hello"; - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -59,7 +85,7 @@ public: ws.auto_fragment(false); ws.write_buffer_size(16); std::string const s(32, '*'); - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -70,7 +96,7 @@ public: { ws.auto_fragment(true); std::string const s(16384, '*'); - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -88,7 +114,7 @@ public: w.accept(ws); ws.auto_fragment(false); std::string const s = "Hello"; - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -114,7 +140,7 @@ public: w.accept(ws); ws.auto_fragment(true); std::string const s(16384, '*'); - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -130,13 +156,14 @@ public: pmd.client_enable = true; pmd.server_enable = true; + pmd.compLevel = 1; // deflate doTest(pmd, [&](ws_type& ws) { auto const& s = random_string(); ws.binary(true); - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -151,8 +178,8 @@ public: // This call should produce no // output due to compression latency. w.write_some(ws, false, - boost::asio::buffer(s.data(), chop)); - w.write_some(ws, true, boost::asio::buffer( + buffer(s.data(), chop)); + w.write_some(ws, true, buffer( s.data() + chop, s.size() - chop)); flat_buffer b; w.read(ws, b); @@ -165,7 +192,7 @@ public: { auto const& s = random_string(); ws.binary(true); - w.write(ws, boost::asio::buffer(s)); + w.write(ws, buffer(s)); flat_buffer b; w.read(ws, b); BEAST_EXPECT(to_string(b.data()) == s); @@ -175,6 +202,8 @@ public: void testWrite() { + using boost::asio::buffer; + doTestWrite(SyncClient{}); yield_to([&](yield_context yield) @@ -249,7 +278,7 @@ public: std::size_t count = 0; std::string const s(16384, '*'); ws.auto_fragment(true); - ws.async_write(boost::asio::buffer(s), + ws.async_write(buffer(s), [&](error_code ec) { ++count; @@ -295,7 +324,7 @@ public: std::size_t count = 0; std::string const s(16384, '*'); ws.auto_fragment(true); - ws.async_write(boost::asio::buffer(s), + ws.async_write(buffer(s), [&](error_code ec) { ++count; @@ -338,6 +367,7 @@ public: { permessage_deflate pmd; pmd.client_enable = true; + pmd.compLevel = 1; ws.set_option(pmd); } ws.next_layer().connect(es.stream()); @@ -346,7 +376,7 @@ public: std::size_t count = 0; auto const& s = random_string(); ws.binary(true); - ws.async_write(boost::asio::buffer(s), + ws.async_write(buffer(s), [&](error_code ec) { ++count;