Files

522 lines
16 KiB
C++
Raw Permalink 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-05-04 08:23:40 -07:00
#include <boost/beast/_experimental/test/tcp.hpp>
2017-08-24 06:21:30 -07:00
#include "test.hpp"
2019-05-04 08:23:40 -07:00
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
#if BOOST_ASIO_HAS_CO_AWAIT
#include <boost/asio/use_awaitable.hpp>
#endif
2017-08-24 06:21:30 -07:00
namespace boost {
namespace beast {
namespace websocket {
2017-08-26 06:13:11 -07:00
class ping_test : public websocket_test_suite
2017-08-24 06:21:30 -07:00
{
public:
template<class Wrap>
void
doTestPing(Wrap const& w)
{
permessage_deflate pmd;
pmd.client_enable = false;
pmd.server_enable = false;
// ping
doTest(pmd, [&](ws_type& ws)
{
w.ping(ws, {});
});
// pong
doTest(pmd, [&](ws_type& ws)
{
w.pong(ws, {});
});
// ping, already closed
{
2017-08-26 06:13:11 -07:00
echo_server es{log};
stream<test::stream> ws{ioc_};
2017-08-26 06:13:11 -07:00
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.close({});
try
{
w.ping(ws, {});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == net::error::operation_aborted,
2017-08-26 06:13:11 -07:00
se.code().message());
}
2017-08-24 06:21:30 -07:00
}
// pong, already closed
{
2017-08-26 06:13:11 -07:00
echo_server es{log};
stream<test::stream> ws{ioc_};
2017-08-26 06:13:11 -07:00
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
ws.close({});
try
{
w.pong(ws, {});
fail("", __FILE__, __LINE__);
}
catch(system_error const& se)
{
BEAST_EXPECTS(
se.code() == net::error::operation_aborted,
2017-08-26 06:13:11 -07:00
se.code().message());
}
2017-08-24 06:21:30 -07:00
}
2017-08-26 06:13:11 -07:00
}
void
testPing()
{
doTestPing(SyncClient{});
2017-08-24 06:21:30 -07:00
2017-08-26 06:13:11 -07:00
yield_to([&](yield_context yield)
2017-08-24 06:21:30 -07:00
{
2017-08-26 06:13:11 -07:00
doTestPing(AsyncClient{yield});
});
2017-08-24 06:21:30 -07:00
}
void
testSuspend()
2017-08-24 06:21:30 -07:00
{
// 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("Hello, world"),
[&](error_code ec, std::size_t n)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
BEAST_EXPECT(n == 12);
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
BEAST_EXPECT(count == 0);
ws.async_ping({},
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
// suspend on 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;
ws.async_close({},
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(ws.impl_->wr_block.is_locked());
BEAST_EXPECT(count == 0);
ws.async_ping({},
[&](error_code ec)
{
++count;
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
// suspend on read ping + 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 a ping and message to the input
ws.next_layer().append(string_view{
"\x89\x00" "\x81\x01*", 5});
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
while(! ws.impl_->wr_block.is_locked())
2017-08-24 06:21:30 -07:00
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
ws.async_ping({},
[&](error_code ec)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(count == 0);
ioc.run();
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)
{
++count;
if(ec != error::bad_control_fragment)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
while(! ws.impl_->wr_block.is_locked())
2017-08-24 06:21:30 -07:00
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
ws.async_ping({},
[&](error_code ec)
{
++count;
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
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)
2017-08-24 06:21:30 -07:00
{
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)
{
++count;
if(ec != error::closed)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
while(! ws.impl_->wr_block.is_locked())
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
ws.async_ping({},
[&](error_code ec)
{
++count;
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
// suspend on read close #2
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log, kind::async};
net::io_context ioc;
stream<test::stream> ws{ioc, fc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// Cause close to be received
es.async_close();
std::size_t count = 0;
multi_buffer b;
ws.async_read(b,
[&](error_code ec, std::size_t)
{
++count;
if(ec != error::closed)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
while(! ws.impl_->wr_block.is_locked())
{
ioc.run_one();
if(! BEAST_EXPECT(! ioc.stopped()))
break;
}
BEAST_EXPECT(count == 0);
ws.async_ping({},
[&](error_code ec)
{
++count;
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
BEAST_EXPECT(count == 0);
ioc.run();
BEAST_EXPECT(count == 2);
});
// don't ping on close
2018-05-01 14:19:35 -07:00
doFailLoop([&](test::fail_count& fc)
{
echo_server es{log};
error_code ec;
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());
ws.async_ping("",
[&](error_code ec)
{
++count;
if(ec != net::error::operation_aborted)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
ws.async_close({},
[&](error_code)
{
++count;
if(ec)
BOOST_THROW_EXCEPTION(
system_error{ec});
});
ioc.run();
BEAST_EXPECT(count == 3);
});
2019-05-04 08:23:40 -07:00
// suspend idle ping
{
using socket_type =
net::basic_stream_socket<
net::ip::tcp,
net::any_io_executor>;
2019-05-04 08:23:40 -07:00
net::io_context ioc;
stream<socket_type> ws1(ioc);
stream<socket_type> ws2(ioc);
ws1.set_option(stream_base::timeout{
stream_base::none(),
std::chrono::seconds(0),
true});
test::connect(
ws1.next_layer(),
ws2.next_layer());
ws1.async_handshake("localhost", "/",
[](error_code){});
ws2.async_accept([](error_code){});
ioc.run();
ioc.restart();
flat_buffer b1;
auto mb = b1.prepare(65536);
std::memset(mb.data(), 0, mb.size());
b1.commit(65536);
ws1.async_write(b1.data(),
[&](error_code, std::size_t){});
BEAST_EXPECT(
ws1.impl_->wr_block.is_locked());
ws1.async_read_some(net::mutable_buffer{},
[&](error_code, std::size_t){});
ioc.run();
ioc.restart();
flat_buffer b2;
ws2.async_read(b2,
[&](error_code, std::size_t){});
ioc.run();
}
//);
{
echo_server es{log, kind::async};
net::io_context ioc;
stream<test::stream> ws{ioc};
ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/");
// Cause close to be received
es.async_close();
multi_buffer b;
std::size_t count = 0;
// Read a close frame.
// Sends a close frame, blocking writes.
ws.async_read(b,
[&](error_code ec, std::size_t)
{
// Read should complete with error::closed
++count;
BEAST_EXPECTS(ec == error::closed,
ec.message());
});
if(! BEAST_EXPECT(run_until(ioc, 100,
[&]{ return ws.impl_->wr_close; })))
return;
// Try to ping
ws.async_ping("payload",
[&](error_code ec)
{
// Pings after a close are aborted
++count;
BEAST_EXPECTS(ec == net::
error::operation_aborted,
ec.message());
// Subsequent calls to close are aborted
ws.async_close({},
[&](error_code ec)
{
++count;
BEAST_EXPECTS(ec == net::
error::operation_aborted,
ec.message());
});
});
static std::size_t constexpr limit = 100;
std::size_t n;
for(n = 0; n < limit; ++n)
{
2018-02-18 18:46:12 -08:00
if(count >= 3)
break;
ioc.run_one();
}
BEAST_EXPECT(n < limit);
ioc.run();
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_ping({}, move_only_handler{});
}
struct copyable_handler
{
template<class... Args>
void
operator()(Args&&...) const
{
}
};
#if BOOST_ASIO_HAS_CO_AWAIT
void testAwaitableCompiles(
stream<test::stream>& s,
ping_data& pdat)
{
static_assert(std::is_same_v<
net::awaitable<void>, decltype(
s.async_ping(pdat, net::use_awaitable))>);
static_assert(std::is_same_v<
net::awaitable<void>, decltype(
s.async_pong(pdat, net::use_awaitable))>);
}
#endif
2017-08-24 06:21:30 -07:00
void
run() override
{
testPing();
testSuspend();
2018-02-28 13:36:47 -08:00
testMoveOnly();
#if BOOST_ASIO_HAS_CO_AWAIT
boost::ignore_unused(&ping_test::testAwaitableCompiles);
#endif
2017-08-24 06:21:30 -07:00
}
};
2017-08-26 06:13:11 -07:00
BEAST_DEFINE_TESTSUITE(beast,websocket,ping);
2017-08-24 06:21:30 -07:00
} // websocket
} // beast
} // boost