2 // Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // Official repository: https://github.com/boostorg/beast
10 // Test that header file is self-contained.
11 #include <boost/beast/websocket/stream.hpp>
15 #include <boost/asio/io_service.hpp>
16 #include <boost/asio/strand.hpp>
22 class ping_test
: public websocket_test_suite
27 doTestPing(Wrap
const& w
)
29 permessage_deflate pmd
;
30 pmd
.client_enable
= false;
31 pmd
.server_enable
= false;
34 doTest(pmd
, [&](ws_type
& ws
)
40 doTest(pmd
, [&](ws_type
& ws
)
45 // ping, already closed
48 stream
<test::stream
> ws
{ioc_
};
49 ws
.next_layer().connect(es
.stream());
50 ws
.handshake("localhost", "/");
55 fail("", __FILE__
, __LINE__
);
57 catch(system_error
const& se
)
60 se
.code() == boost::asio::error::operation_aborted
,
65 // pong, already closed
68 stream
<test::stream
> ws
{ioc_
};
69 ws
.next_layer().connect(es
.stream());
70 ws
.handshake("localhost", "/");
75 fail("", __FILE__
, __LINE__
);
77 catch(system_error
const& se
)
80 se
.code() == boost::asio::error::operation_aborted
,
89 doTestPing(SyncClient
{});
91 yield_to([&](yield_context yield
)
93 doTestPing(AsyncClient
{yield
});
101 doFailLoop([&](test::fail_counter
& fc
)
104 boost::asio::io_context ioc
;
105 stream
<test::stream
> ws
{ioc
, fc
};
106 ws
.next_layer().connect(es
.stream());
107 ws
.handshake("localhost", "/");
108 std::size_t count
= 0;
109 ws
.async_write(sbuf("Hello, world"),
110 [&](error_code ec
, std::size_t n
)
114 BOOST_THROW_EXCEPTION(
116 BEAST_EXPECT(n
== 12);
118 BEAST_EXPECT(ws
.wr_block_
.is_locked());
119 BEAST_EXPECT(count
== 0);
125 BOOST_THROW_EXCEPTION(
128 BEAST_EXPECT(count
== 0);
130 BEAST_EXPECT(count
== 2);
134 doFailLoop([&](test::fail_counter
& fc
)
137 boost::asio::io_context ioc
;
138 stream
<test::stream
> ws
{ioc
, fc
};
139 ws
.next_layer().connect(es
.stream());
140 ws
.handshake("localhost", "/");
141 std::size_t count
= 0;
147 BOOST_THROW_EXCEPTION(
150 BEAST_EXPECT(ws
.wr_block_
.is_locked());
151 BEAST_EXPECT(count
== 0);
156 if(ec
!= boost::asio::error::operation_aborted
)
157 BOOST_THROW_EXCEPTION(
160 BEAST_EXPECT(count
== 0);
162 BEAST_EXPECT(count
== 2);
165 // suspend on read ping + message
166 doFailLoop([&](test::fail_counter
& fc
)
169 boost::asio::io_context ioc
;
170 stream
<test::stream
> ws
{ioc
, fc
};
171 ws
.next_layer().connect(es
.stream());
172 ws
.handshake("localhost", "/");
173 // add a ping and message to the input
174 ws
.next_layer().append(string_view
{
175 "\x89\x00" "\x81\x01*", 5});
176 std::size_t count
= 0;
179 [&](error_code ec
, std::size_t)
183 BOOST_THROW_EXCEPTION(
186 while(! ws
.wr_block_
.is_locked())
189 if(! BEAST_EXPECT(! ioc
.stopped()))
192 BEAST_EXPECT(count
== 0);
198 BOOST_THROW_EXCEPTION(
201 BEAST_EXPECT(count
== 0);
203 BEAST_EXPECT(count
== 2);
206 // suspend on read bad message
207 doFailLoop([&](test::fail_counter
& fc
)
210 boost::asio::io_context ioc
;
211 stream
<test::stream
> ws
{ioc
, fc
};
212 ws
.next_layer().connect(es
.stream());
213 ws
.handshake("localhost", "/");
214 // add an invalid frame to the input
215 ws
.next_layer().append(string_view
{
218 std::size_t count
= 0;
221 [&](error_code ec
, std::size_t)
224 if(ec
!= error::bad_control_fragment
)
225 BOOST_THROW_EXCEPTION(
228 while(! ws
.wr_block_
.is_locked())
231 if(! BEAST_EXPECT(! ioc
.stopped()))
234 BEAST_EXPECT(count
== 0);
239 if(ec
!= boost::asio::error::operation_aborted
)
240 BOOST_THROW_EXCEPTION(
243 BEAST_EXPECT(count
== 0);
245 BEAST_EXPECT(count
== 2);
248 // suspend on read close #1
249 doFailLoop([&](test::fail_counter
& fc
)
252 boost::asio::io_context ioc
;
253 stream
<test::stream
> ws
{ioc
, fc
};
254 ws
.next_layer().connect(es
.stream());
255 ws
.handshake("localhost", "/");
256 // add a close frame to the input
257 ws
.next_layer().append(string_view
{
259 std::size_t count
= 0;
262 [&](error_code ec
, std::size_t)
265 if(ec
!= error::closed
)
266 BOOST_THROW_EXCEPTION(
269 while(! ws
.wr_block_
.is_locked())
272 if(! BEAST_EXPECT(! ioc
.stopped()))
275 BEAST_EXPECT(count
== 0);
280 if(ec
!= boost::asio::error::operation_aborted
)
281 BOOST_THROW_EXCEPTION(
284 BEAST_EXPECT(count
== 0);
286 BEAST_EXPECT(count
== 2);
289 // suspend on read close #2
290 doFailLoop([&](test::fail_counter
& fc
)
292 echo_server es
{log
, kind::async
};
293 boost::asio::io_context ioc
;
294 stream
<test::stream
> ws
{ioc
, fc
};
295 ws
.next_layer().connect(es
.stream());
296 ws
.handshake("localhost", "/");
297 // Cause close to be received
299 std::size_t count
= 0;
302 [&](error_code ec
, std::size_t)
305 if(ec
!= error::closed
)
306 BOOST_THROW_EXCEPTION(
309 while(! ws
.wr_block_
.is_locked())
312 if(! BEAST_EXPECT(! ioc
.stopped()))
315 BEAST_EXPECT(count
== 0);
320 if(ec
!= boost::asio::error::operation_aborted
)
321 BOOST_THROW_EXCEPTION(
324 BEAST_EXPECT(count
== 0);
326 BEAST_EXPECT(count
== 2);
329 // don't ping on close
330 doFailLoop([&](test::fail_counter
& fc
)
334 boost::asio::io_context ioc
;
335 stream
<test::stream
> ws
{ioc
, fc
};
336 ws
.next_layer().connect(es
.stream());
337 ws
.handshake("localhost", "/");
338 std::size_t count
= 0;
339 ws
.async_write(sbuf("*"),
340 [&](error_code ec
, std::size_t n
)
344 BOOST_THROW_EXCEPTION(
346 BEAST_EXPECT(n
== 1);
348 BEAST_EXPECT(ws
.wr_block_
.is_locked());
353 if(ec
!= boost::asio::error::operation_aborted
)
354 BOOST_THROW_EXCEPTION(
362 BOOST_THROW_EXCEPTION(
366 BEAST_EXPECT(count
== 3);
370 echo_server es
{log
, kind::async
};
371 boost::asio::io_context ioc
;
372 stream
<test::stream
> ws
{ioc
};
373 ws
.next_layer().connect(es
.stream());
374 ws
.handshake("localhost", "/");
376 // Cause close to be received
380 std::size_t count
= 0;
381 // Read a close frame.
382 // Sends a close frame, blocking writes.
384 [&](error_code ec
, std::size_t)
386 // Read should complete with error::closed
388 BEAST_EXPECTS(ec
== error::closed
,
391 if(! BEAST_EXPECT(run_until(ioc
, 100,
392 [&]{ return ws
.wr_close_
; })))
395 ws
.async_ping("payload",
398 // Pings after a close are aborted
400 BEAST_EXPECTS(ec
== boost::asio::
401 error::operation_aborted
,
403 // Subsequent calls to close are aborted
408 BEAST_EXPECTS(ec
== boost::asio::
409 error::operation_aborted
,
413 static std::size_t constexpr limit
= 100;
415 for(n
= 0; n
< limit
; ++n
)
421 BEAST_EXPECT(n
< limit
);
431 void operator()(error_code
) {}
434 stream
<test::stream
> ws
{ioc_
};
435 stream
<test::stream
>::ping_op
<handler
> op
{
436 handler
{}, ws
, detail::opcode::ping
, {}};
437 using boost::asio::asio_handler_is_continuation
;
438 asio_handler_is_continuation(&op
);
444 boost::asio::io_context ioc
;
445 stream
<test::stream
> ws
{ioc
};
446 ws
.async_ping({}, move_only_handler
{});
449 struct copyable_handler
451 template<class... Args
>
453 operator()(Args
&&...) const
459 testAsioHandlerInvoke()
461 // make sure things compile, also can set a
462 // breakpoint in asio_handler_invoke to make sure
463 // it is instantiated.
464 boost::asio::io_context ioc
;
465 boost::asio::io_service::strand s
{ioc
};
466 stream
<test::stream
> ws
{ioc
};
467 ws
.async_ping({}, s
.wrap(copyable_handler
{}));
477 testAsioHandlerInvoke();
481 BEAST_DEFINE_TESTSUITE(beast
,websocket
,ping
);