Files
boost_beast/test/beast/websocket/close.cpp

753 lines
24 KiB
C++
Raw Normal View History

2017-08-24 06:21:30 -07:00
//
2019-02-21 07:00:31 -08:00
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
2017-08-24 06:21:30 -07:00
//
// 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>
2019-02-13 08:00:07 -08:00
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/_experimental/test/tcp.hpp>
2017-08-24 06:21:30 -07:00
#include "test.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
2017-08-24 06:21:30 -07:00
namespace boost {
namespace beast {
namespace websocket {
2017-08-26 06:13:11 -07:00
class close_test : public websocket_test_suite
2017-08-24 06:21:30 -07:00
{
public:
template<class Wrap>
void
doTestClose(Wrap const& w)
{
permessage_deflate pmd;
pmd.client_enable = false;
pmd.server_enable = false;
2017-08-24 07:41:27 -07:00
// close
2017-08-24 06:21:30 -07:00
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, {});
});
2017-08-24 07:41:27 -07:00
// close with code
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, close_code::going_away);
});
// close with code and reason
doTest(pmd, [&](ws_type& ws)
{
w.close(ws, {
close_code::going_away,
"going away"});
});
2017-08-26 06:13:11 -07:00
// already closed
2017-08-24 06:21:30 -07:00
{
echo_server es{log};
stream<test::stream> ws{ioc_};
2017-08-24 06:21:30 -07:00
ws.next_layer().connect(es.stream());
w.handshake(ws, "localhost", "/");
w.close(ws, {});
try
{
w.close(ws, {});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == net::error::operation_aborted,
2017-08-24 06:21:30 -07:00
se.code().message());
}
}
// drain a message after close
doTest(pmd, [&](ws_type& ws)
{
ws.next_layer().append("\x81\x01\x2a");
w.close(ws, {});
});
// drain a big message after close
{
std::string s;
s = "\x81\x7e\x10\x01";
s.append(4097, '*');
doTest(pmd, [&](ws_type& ws)
{
ws.next_layer().append(s);
w.close(ws, {});
});
}
// drain a ping after close
doTest(pmd, [&](ws_type& ws)
{
ws.next_layer().append("\x89\x01*");
w.close(ws, {});
});
// drain invalid message frame after close
{
echo_server es{log};
stream<test::stream> ws{ioc_};
2017-08-24 06:21:30 -07:00
ws.next_layer().connect(es.stream());
w.handshake(ws, "localhost", "/");
ws.next_layer().append("\x81\x81\xff\xff\xff\xff*");
try
{
w.close(ws, {});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == error::bad_masked_frame,
2017-08-24 06:21:30 -07:00
se.code().message());
}
}
// drain invalid close frame after close
{
echo_server es{log};
stream<test::stream> ws{ioc_};
2017-08-24 06:21:30 -07:00
ws.next_layer().connect(es.stream());
w.handshake(ws, "localhost", "/");
ws.next_layer().append("\x88\x01*");
try
{
w.close(ws, {});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == error::bad_close_size,
2017-08-24 06:21:30 -07:00
se.code().message());
}
}
// drain masked close frame
{
echo_server es{log, kind::async_client};
stream<test::stream> ws{ioc_};
ws.next_layer().connect(es.stream());
ws.set_option(pmd);
es.async_handshake();
ws.accept();
w.close(ws, {});
}
2017-08-24 06:21:30 -07:00
// close with incomplete read message
doTest(pmd, [&](ws_type& ws)
{
ws.next_layer().append("\x81\x02**");
static_buffer<1> b;
w.read_some(ws, 1, b);
w.close(ws, {});
});
}
void
testClose()
{
doTestClose(SyncClient{});
yield_to([&](yield_context yield)
{
doTestClose(AsyncClient{yield});
});
}
2017-08-24 06:21:30 -07:00
2019-02-13 08:00:07 -08:00
void
testTimeout()
{
using tcp = net::ip::tcp;
net::io_context ioc;
// success
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
// success, timeout enabled
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::success_handler());
ws2.async_close({}, test::success_handler());
test::run(ioc);
}
// timeout
{
stream<tcp::socket> ws1(ioc);
stream<tcp::socket> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::fail_handler(
beast::error::timeout));
test::run(ioc);
}
{
stream<test::stream> ws1(ioc);
stream<test::stream> ws2(ioc);
test::connect(ws1.next_layer(), ws2.next_layer());
ws1.async_handshake("test", "/", test::success_handler());
ws2.async_accept(test::success_handler());
test::run(ioc);
ws1.set_option(stream_base::timeout{
std::chrono::milliseconds(50),
stream_base::none(),
false});
ws1.async_close({}, test::fail_handler(
beast::error::timeout));
test::run(ioc);
}
}
void
testSuspend()
{
// suspend on ping
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
2017-08-24 06:21:30 -07:00
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
2017-08-24 06:21:30 -07:00
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
2017-08-24 06:21:30 -07:00
std::size_t count = 0;
ws.async_ping("",
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
2017-08-24 06:21:30 -07:00
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
BEAST_EXPECT(count == 0);
2017-08-24 06:21:30 -07:00
ws.async_close({},
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
2017-08-24 06:21:30 -07:00
});
BEAST_EXPECT(count == 0);
ioc.run();
2017-08-24 06:21:30 -07:00
BEAST_EXPECT(count == 2);
});
// suspend on write
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
ws.async_write(sbuf("*"),
[&](error_code ec, std::size_t n)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(n == 1);
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
BEAST_EXPECT(count == 0);
ws.async_close({},
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
2017-08-24 06:21:30 -07:00
// suspend on read ping + message
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
2017-08-24 06:21:30 -07:00
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
2017-08-24 06:21:30 -07:00
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// add a ping and message to the input
ws.next_layer().append(string_view{
"\x89\x00" "\x81\x01*", 5});
2017-08-24 06:21:30 -07:00
std::size_t count = 0;
multi_buffer b;
2017-08-24 06:21:30 -07:00
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
2017-08-24 06:21:30 -07:00
});
while(! ws.impl_->wr_block.is_locked())
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
2017-08-24 06:21:30 -07:00
ws.async_close({},
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
2017-08-24 06:21:30 -07:00
});
BEAST_EXPECT(count == 0);
ioc.run();
2017-08-24 06:21:30 -07:00
BEAST_EXPECT(count == 2);
});
// suspend on read bad message
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// add an invalid frame to the input
ws.next_layer().append(string_view{
"\x09\x00", 2});
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != error::bad_control_fragment)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
while(! ws.impl_->wr_block.is_locked())
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
ws.async_close({},
[&](error_code ec)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
// suspend on read close #1
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// add a close frame to the input
ws.next_layer().append(string_view{
"\x88\x00", 2});
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != error::closed)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
while(! ws.impl_->wr_block.is_locked())
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
ws.async_close({},
[&](error_code ec)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
// teardown on received close
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// add a close frame to the input
ws.next_layer().append(string_view{
"\x88\x00", 2});
std::size_t count = 0;
std::string const s = "Hello, world!";
2019-02-02 20:44:04 -08:00
ws.async_write(net::buffer(s),
[&](error_code ec, std::size_t n)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(n == s.size());
BEAST_EXPECT(++count == 1);
});
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 3);
});
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 3);
});
// check for deadlock
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// add a ping frame to the input
ws.next_layer().append(string_view{
"\x89\x00", 2});
std::size_t count = 0;
multi_buffer b;
std::string const s = "Hello, world!";
2019-02-02 20:44:04 -08:00
ws.async_write(net::buffer(s),
[&](error_code ec, std::size_t n)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(n == s.size());
BEAST_EXPECT(++count == 1);
});
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 3);
});
BEAST_EXPECT(ws.impl_->rd_block.is_locked());
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(ws.is_open());
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 3);
});
// Four-way: close, read, write, ping
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
std::string const s = "Hello, world!";
multi_buffer b;
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
++count;
});
2019-02-02 20:44:04 -08:00
ws.async_write(net::buffer(s),
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
++count;
});
ws.async_ping({},
[&](error_code ec)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
++count;
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 4);
});
// Four-way: read, write, ping, close
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
std::string const s = "Hello, world!";
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec && ec != net::error::operation_aborted)
{
BEAST_EXPECTS(ec, ec.message());
BOOST_THROW_EXCEPTION(
system_error{ec});
}
if(! ec)
BEAST_EXPECT(buffers_to_string(b.data()) == s);
++count;
if(count == 4)
BEAST_EXPECT(
ec == net::error::operation_aborted);
});
2019-02-02 20:44:04 -08:00
ws.async_write(net::buffer(s),
[&](error_code ec, std::size_t n)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(n == s.size());
BEAST_EXPECT(++count == 1);
});
ws.async_ping({},
[&](error_code ec)
{
if(ec != net::error::operation_aborted)
{
BEAST_EXPECTS(ec, ec.message());
BOOST_THROW_EXCEPTION(
system_error{ec});
}
++count;
});
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
++count;
BEAST_EXPECT(count == 2 || count == 3);
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 4);
});
// Four-way: ping, read, write, close
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
std::size_t count = 0;
std::string const s = "Hello, world!";
multi_buffer b;
ws.async_ping({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 1);
});
ws.async_read(b,
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
++count;
});
2019-02-02 20:44:04 -08:00
ws.async_write(net::buffer(s),
[&](error_code ec, std::size_t)
{
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
++count;
});
ws.async_close({},
[&](error_code ec)
{
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(++count == 2);
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 4);
});
2017-08-24 06:21:30 -07:00
}
2018-02-28 13:36:47 -08:00
void
testMoveOnly()
{
net::io_context ioc;
2018-02-28 13:36:47 -08:00
stream<test::stream> ws{ioc};
ws.async_close({}, move_only_handler{});
}
struct copyable_handler
{
template<class... Args>
void
operator()(Args&&...) const
{
}
};
2017-08-24 06:21:30 -07:00
void
run() override
{
testClose();
2019-02-13 08:00:07 -08:00
testTimeout();
testSuspend();
2018-02-28 13:36:47 -08:00
testMoveOnly();
2017-08-24 06:21:30 -07:00
}
};
2017-08-26 06:13:11 -07:00
BEAST_DEFINE_TESTSUITE(beast,websocket,close);
2017-08-24 06:21:30 -07:00
} // websocket
} // beast
} // boost