2022-08-31 10:03:35 +08:00
|
|
|
//
|
|
|
|
|
// 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 <boost/beast/websocket/stream.hpp>
|
|
|
|
|
#include <boost/beast/core/tcp_stream.hpp>
|
|
|
|
|
|
|
|
|
|
#include <boost/beast/_experimental/test/stream.hpp>
|
|
|
|
|
#include <boost/beast/_experimental/test/tcp.hpp>
|
|
|
|
|
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
|
|
|
|
#include <boost/asio/strand.hpp>
|
|
|
|
|
#include <boost/asio/bind_cancellation_slot.hpp>
|
|
|
|
|
#include <boost/asio/deferred.hpp>
|
|
|
|
|
#include <boost/asio/redirect_error.hpp>
|
|
|
|
|
|
|
|
|
|
#include "test.hpp"
|
|
|
|
|
|
|
|
|
|
namespace boost {
|
|
|
|
|
namespace beast {
|
|
|
|
|
namespace websocket {
|
|
|
|
|
|
|
|
|
|
struct async_all_server_op : boost::asio::coroutine
|
|
|
|
|
{
|
|
|
|
|
stream<asio::ip::tcp::socket> & ws;
|
|
|
|
|
|
|
|
|
|
async_all_server_op(stream<asio::ip::tcp::socket> & ws) : ws(ws) {}
|
|
|
|
|
|
|
|
|
|
template<typename Self>
|
2024-03-29 06:47:11 +00:00
|
|
|
void operator()(Self && self, error_code ec = {}, std::size_t = 0)
|
2022-08-31 10:03:35 +08:00
|
|
|
{
|
|
|
|
|
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_TPARAM1 CompletionToken>
|
|
|
|
|
BOOST_BEAST_ASYNC_RESULT1(CompletionToken)
|
|
|
|
|
async_all_server(
|
|
|
|
|
stream<asio::ip::tcp::socket> & ws,
|
|
|
|
|
CompletionToken && token)
|
|
|
|
|
{
|
|
|
|
|
return net::async_compose<CompletionToken, void(error_code)>
|
|
|
|
|
(
|
|
|
|
|
async_all_server_op{ws},
|
|
|
|
|
token, ws
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct async_all_client_op : boost::asio::coroutine
|
|
|
|
|
{
|
|
|
|
|
stream<asio::ip::tcp::socket> & ws;
|
|
|
|
|
|
|
|
|
|
async_all_client_op(stream<asio::ip::tcp::socket> & ws) : ws(ws) {}
|
|
|
|
|
struct impl_t
|
|
|
|
|
{
|
|
|
|
|
impl_t () = default;
|
|
|
|
|
std::string res;
|
|
|
|
|
net::dynamic_string_buffer<char, std::char_traits<char>, std::allocator<char>> buf =
|
|
|
|
|
net::dynamic_buffer(res);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<impl_t> impl{std::make_shared<impl_t>()};
|
|
|
|
|
|
|
|
|
|
template<typename Self>
|
2024-03-29 06:47:11 +00:00
|
|
|
void operator()(Self && self, error_code ec = {}, std::size_t = 0)
|
2022-08-31 10:03:35 +08:00
|
|
|
{
|
|
|
|
|
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_TPARAM1 CompletionToken>
|
|
|
|
|
BOOST_BEAST_ASYNC_RESULT1(CompletionToken)
|
|
|
|
|
async_all_client(
|
|
|
|
|
stream<asio::ip::tcp::socket> & ws,
|
|
|
|
|
CompletionToken && token)
|
|
|
|
|
{
|
|
|
|
|
return net::async_compose<CompletionToken, void(error_code)>
|
|
|
|
|
(
|
|
|
|
|
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<tcp::socket> ws1(ioc.get_executor());
|
|
|
|
|
stream<tcp::socket> 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<std::size_t>(-1)))
|
|
|
|
|
cancel_counter++;
|
|
|
|
|
|
|
|
|
|
BEAST_EXPECTS(ec == net::error::operation_aborted
|
2023-06-20 14:20:02 +08:00
|
|
|
// freebsd does this in tests.
|
|
|
|
|
|| ec == net::error::connection_reset
|
2022-08-31 10:03:35 +08:00
|
|
|
|| 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<std::size_t>(-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
|