// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/beast // // Test that header file is self-contained. #include #include #include #include #include #include #include #include #include #include "test.hpp" namespace boost { namespace beast { namespace websocket { struct async_all_server_op : boost::asio::coroutine { stream & ws; async_all_server_op(stream & ws) : ws(ws) {} template void operator()(Self && self, error_code ec = {}, std::size_t sz = 0) { if (ec) return self.complete(ec); BOOST_ASIO_CORO_REENTER(*this) { self.reset_cancellation_state([](net::cancellation_type ct){return ct;}); BOOST_ASIO_CORO_YIELD ws.async_handshake("test", "/", std::move(self)); BOOST_ASIO_CORO_YIELD ws.async_ping("", std::move(self)); BOOST_ASIO_CORO_YIELD ws.async_write_some(false, net::buffer("FOO", 3), std::move(self)); BOOST_ASIO_CORO_YIELD ws.async_write_some(true, net::buffer("BAR", 3), std::move(self)); BOOST_ASIO_CORO_YIELD ws.async_close("testing", std::move(self)); self.complete({}); } } }; template BOOST_BEAST_ASYNC_RESULT1(CompletionToken) async_all_server( stream & ws, CompletionToken && token) { return net::async_compose ( async_all_server_op{ws}, token, ws ); } struct async_all_client_op : boost::asio::coroutine { stream & ws; async_all_client_op(stream & ws) : ws(ws) {} struct impl_t { impl_t () = default; std::string res; net::dynamic_string_buffer, std::allocator> buf = net::dynamic_buffer(res); }; std::shared_ptr impl{std::make_shared()}; template void operator()(Self && self, error_code ec = {}, std::size_t sz = 0) { if (ec) return self.complete(ec); BOOST_ASIO_CORO_REENTER(*this) { // let everything pass self.reset_cancellation_state([](net::cancellation_type ct){return ct;}); BOOST_ASIO_CORO_YIELD ws.async_accept(std::move(self)); BOOST_ASIO_CORO_YIELD ws.async_pong("", std::move(self)); BOOST_ASIO_CORO_YIELD ws.async_read(impl->buf, std::move(self)); BEAST_EXPECTS(impl->res == "FOOBAR", impl->res); BOOST_ASIO_CORO_YIELD ws.async_read(impl->buf, std::move(self)); BEAST_EXPECTS(ec == websocket::error::closed || ec == net::error::connection_reset // hard coded winapi error (WSAECONNRESET), same as connection_reset, but asio delivers it with system_category || (ec.value() == 10054 && ec.category() == system::system_category()) || ec == net::error::not_connected, ec.message()); self.complete({}); } } }; template BOOST_BEAST_ASYNC_RESULT1(CompletionToken) async_all_client( stream & ws, CompletionToken && token) { return net::async_compose ( async_all_client_op{ws}, token, ws ); } class cancel_test : public websocket_test_suite { public: std::size_t run_impl(net::cancellation_signal &sl, net::io_context &ctx, std::size_t trigger = -1, net::cancellation_type tp = net::cancellation_type::terminal) { std::size_t cnt = 0; std::size_t res = 0u; while ((res = ctx.run_one()) != 0) if (trigger == cnt ++) { sl.emit(tp); } return cnt; } std::size_t testAll(std::size_t & cancel_counter, bool cancel_server = false, std::size_t trigger = -1) { net::cancellation_signal sig1, sig2; net::io_context ioc; using tcp = net::ip::tcp; stream ws1(ioc.get_executor()); stream ws2(ioc.get_executor()); test::connect(ws1.next_layer(), ws2.next_layer()); async_all_server(ws1, net::bind_cancellation_slot(sig1.slot(), [&](system::error_code ec) { if (ec) { if (ec == net::error::operation_aborted && (cancel_server || trigger == static_cast(-1))) cancel_counter++; BEAST_EXPECTS(ec == net::error::operation_aborted // freebsd does this in tests. || ec == net::error::connection_reset || ec == net::error::broken_pipe // winapi WSAECONNRESET, as system_category || ec == error_code(10054, boost::system::system_category()) || ec == net::error::bad_descriptor || ec == net::error::eof, ec.message()); get_lowest_layer(ws1).close(); } })); async_all_client( ws2, net::bind_cancellation_slot(sig2.slot(), [&](system::error_code ec) { if (ec) { if (ec == net::error::operation_aborted && (!cancel_server || trigger == static_cast(-1))) cancel_counter++; BEAST_EXPECTS(ec == net::error::operation_aborted || ec == error::closed || ec == net::error::broken_pipe || ec == net::error::connection_reset || ec == net::error::not_connected // winapi WSAECONNRESET, as system_category || ec == error_code(10054, boost::system::system_category()) || ec == net::error::eof, ec.message()); get_lowest_layer(ws1).close(); } })); return run_impl(cancel_server ? sig1 : sig2, ioc, trigger); } void brute_force() { std::size_t cancel_counter = 0; const auto init = testAll(cancel_counter); BEAST_EXPECT(cancel_counter == 0u); for (std::size_t cnt = 0; cnt < init; cnt ++) testAll(cancel_counter, true, cnt); BEAST_EXPECT(cancel_counter > 0u); cancel_counter = 0u; for (std::size_t cnt = 0; cnt < init; cnt ++) testAll(cancel_counter, false, cnt); BEAST_EXPECT(cancel_counter > 0u); } void run() override { brute_force(); } }; BEAST_DEFINE_TESTSUITE(beast,websocket,cancel); } // websocket } // beast } // boost