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>
19 class close_test
: public websocket_test_suite
24 doTestClose(Wrap
const& w
)
26 permessage_deflate pmd
;
27 pmd
.client_enable
= false;
28 pmd
.server_enable
= false;
31 doTest(pmd
, [&](ws_type
& ws
)
37 doTest(pmd
, [&](ws_type
& ws
)
39 w
.close(ws
, close_code::going_away
);
42 // close with code and reason
43 doTest(pmd
, [&](ws_type
& ws
)
46 close_code::going_away
,
53 stream
<test::stream
> ws
{ioc_
};
54 ws
.next_layer().connect(es
.stream());
55 w
.handshake(ws
, "localhost", "/");
60 fail("", __FILE__
, __LINE__
);
62 catch(system_error
const& se
)
65 se
.code() == boost::asio::error::operation_aborted
,
70 // drain a message after close
71 doTest(pmd
, [&](ws_type
& ws
)
73 ws
.next_layer().append("\x81\x01\x2a");
77 // drain a big message after close
80 s
= "\x81\x7e\x10\x01";
82 doTest(pmd
, [&](ws_type
& ws
)
84 ws
.next_layer().append(s
);
89 // drain a ping after close
90 doTest(pmd
, [&](ws_type
& ws
)
92 ws
.next_layer().append("\x89\x01*");
96 // drain invalid message frame after close
99 stream
<test::stream
> ws
{ioc_
};
100 ws
.next_layer().connect(es
.stream());
101 w
.handshake(ws
, "localhost", "/");
102 ws
.next_layer().append("\x81\x81\xff\xff\xff\xff*");
106 fail("", __FILE__
, __LINE__
);
108 catch(system_error
const& se
)
111 se
.code() == error::failed
,
112 se
.code().message());
116 // drain invalid close frame after close
119 stream
<test::stream
> ws
{ioc_
};
120 ws
.next_layer().connect(es
.stream());
121 w
.handshake(ws
, "localhost", "/");
122 ws
.next_layer().append("\x88\x01*");
126 fail("", __FILE__
, __LINE__
);
128 catch(system_error
const& se
)
131 se
.code() == error::failed
,
132 se
.code().message());
136 // drain masked close frame
138 echo_server es
{log
, kind::async_client
};
139 stream
<test::stream
> ws
{ioc_
};
140 ws
.next_layer().connect(es
.stream());
142 es
.async_handshake();
147 // close with incomplete read message
148 doTest(pmd
, [&](ws_type
& ws
)
150 ws
.next_layer().append("\x81\x02**");
152 w
.read_some(ws
, 1, b
);
160 doTestClose(SyncClient
{});
162 yield_to([&](yield_context yield
)
164 doTestClose(AsyncClient
{yield
});
171 using boost::asio::buffer
;
174 doFailLoop([&](test::fail_counter
& fc
)
177 boost::asio::io_context ioc
;
178 stream
<test::stream
> ws
{ioc
, fc
};
179 ws
.next_layer().connect(es
.stream());
180 ws
.handshake("localhost", "/");
181 std::size_t count
= 0;
187 BOOST_THROW_EXCEPTION(
190 BEAST_EXPECT(ws
.wr_block_
);
191 BEAST_EXPECT(count
== 0);
197 BOOST_THROW_EXCEPTION(
200 BEAST_EXPECT(count
== 0);
202 BEAST_EXPECT(count
== 2);
206 doFailLoop([&](test::fail_counter
& fc
)
209 boost::asio::io_context ioc
;
210 stream
<test::stream
> ws
{ioc
, fc
};
211 ws
.next_layer().connect(es
.stream());
212 ws
.handshake("localhost", "/");
213 std::size_t count
= 0;
214 ws
.async_write(sbuf("*"),
215 [&](error_code ec
, std::size_t n
)
219 BOOST_THROW_EXCEPTION(
221 BEAST_EXPECT(n
== 1);
223 BEAST_EXPECT(ws
.wr_block_
);
224 BEAST_EXPECT(count
== 0);
230 BOOST_THROW_EXCEPTION(
233 BEAST_EXPECT(count
== 0);
235 BEAST_EXPECT(count
== 2);
238 // suspend on read ping + message
239 doFailLoop([&](test::fail_counter
& fc
)
242 boost::asio::io_context ioc
;
243 stream
<test::stream
> ws
{ioc
, fc
};
244 ws
.next_layer().connect(es
.stream());
245 ws
.handshake("localhost", "/");
246 // add a ping and message to the input
247 ws
.next_layer().append(string_view
{
248 "\x89\x00" "\x81\x01*", 5});
249 std::size_t count
= 0;
252 [&](error_code ec
, std::size_t)
256 BOOST_THROW_EXCEPTION(
259 while(! ws
.wr_block_
)
262 if(! BEAST_EXPECT(! ioc
.stopped()))
265 BEAST_EXPECT(count
== 0);
271 BOOST_THROW_EXCEPTION(
274 BEAST_EXPECT(count
== 0);
276 BEAST_EXPECT(count
== 2);
279 // suspend on read bad message
280 doFailLoop([&](test::fail_counter
& fc
)
283 boost::asio::io_context ioc
;
284 stream
<test::stream
> ws
{ioc
, fc
};
285 ws
.next_layer().connect(es
.stream());
286 ws
.handshake("localhost", "/");
287 // add an invalid frame to the input
288 ws
.next_layer().append(string_view
{
290 std::size_t count
= 0;
293 [&](error_code ec
, std::size_t)
295 if(ec
!= error::failed
)
296 BOOST_THROW_EXCEPTION(
298 BEAST_EXPECT(++count
== 1);
300 while(! ws
.wr_block_
)
303 if(! BEAST_EXPECT(! ioc
.stopped()))
306 BEAST_EXPECT(count
== 0);
310 if(ec
!= boost::asio::error::operation_aborted
)
311 BOOST_THROW_EXCEPTION(
313 BEAST_EXPECT(++count
== 2);
315 BEAST_EXPECT(count
== 0);
317 BEAST_EXPECT(count
== 2);
320 // suspend on read close #1
321 doFailLoop([&](test::fail_counter
& fc
)
324 boost::asio::io_context ioc
;
325 stream
<test::stream
> ws
{ioc
, fc
};
326 ws
.next_layer().connect(es
.stream());
327 ws
.handshake("localhost", "/");
328 // add a close frame to the input
329 ws
.next_layer().append(string_view
{
331 std::size_t count
= 0;
334 [&](error_code ec
, std::size_t)
336 if(ec
!= error::closed
)
337 BOOST_THROW_EXCEPTION(
339 BEAST_EXPECT(++count
== 1);
341 while(! ws
.wr_block_
)
344 if(! BEAST_EXPECT(! ioc
.stopped()))
347 BEAST_EXPECT(count
== 0);
351 if(ec
!= boost::asio::error::operation_aborted
)
352 BOOST_THROW_EXCEPTION(
354 BEAST_EXPECT(++count
== 2);
356 BEAST_EXPECT(count
== 0);
358 BEAST_EXPECT(count
== 2);
361 // teardown on received close
362 doFailLoop([&](test::fail_counter
& fc
)
365 boost::asio::io_context ioc
;
366 stream
<test::stream
> ws
{ioc
, fc
};
367 ws
.next_layer().connect(es
.stream());
368 ws
.handshake("localhost", "/");
369 // add a close frame to the input
370 ws
.next_layer().append(string_view
{
372 std::size_t count
= 0;
373 std::string
const s
= "Hello, world!";
374 ws
.async_write(buffer(s
),
375 [&](error_code ec
, std::size_t n
)
378 BOOST_THROW_EXCEPTION(
380 BEAST_EXPECT(n
== s
.size());
381 BEAST_EXPECT(++count
== 1);
385 [&](error_code ec
, std::size_t)
387 if(ec
!= boost::asio::error::operation_aborted
)
388 BOOST_THROW_EXCEPTION(
390 BEAST_EXPECT(++count
== 3);
396 BOOST_THROW_EXCEPTION(
398 BEAST_EXPECT(++count
== 2);
400 BEAST_EXPECT(count
== 0);
402 BEAST_EXPECT(count
== 3);
405 // check for deadlock
406 doFailLoop([&](test::fail_counter
& fc
)
409 boost::asio::io_context ioc
;
410 stream
<test::stream
> ws
{ioc
, fc
};
411 ws
.next_layer().connect(es
.stream());
412 ws
.handshake("localhost", "/");
413 // add a ping frame to the input
414 ws
.next_layer().append(string_view
{
416 std::size_t count
= 0;
418 std::string
const s
= "Hello, world!";
419 ws
.async_write(buffer(s
),
420 [&](error_code ec
, std::size_t n
)
423 BOOST_THROW_EXCEPTION(
425 BEAST_EXPECT(n
== s
.size());
426 BEAST_EXPECT(++count
== 1);
429 [&](error_code ec
, std::size_t)
431 if(ec
!= boost::asio::error::operation_aborted
)
432 BOOST_THROW_EXCEPTION(
434 BEAST_EXPECT(++count
== 3);
436 BEAST_EXPECT(ws
.rd_block_
);
441 BOOST_THROW_EXCEPTION(
443 BEAST_EXPECT(++count
== 2);
445 BEAST_EXPECT(ws
.is_open());
446 BEAST_EXPECT(ws
.wr_block_
);
447 BEAST_EXPECT(count
== 0);
449 BEAST_EXPECT(count
== 3);
452 // Four-way: close, read, write, ping
453 doFailLoop([&](test::fail_counter
& fc
)
456 boost::asio::io_context ioc
;
457 stream
<test::stream
> ws
{ioc
, fc
};
458 ws
.next_layer().connect(es
.stream());
459 ws
.handshake("localhost", "/");
460 std::size_t count
= 0;
461 std::string
const s
= "Hello, world!";
467 BOOST_THROW_EXCEPTION(
469 BEAST_EXPECT(++count
== 1);
472 [&](error_code ec
, std::size_t)
474 if(ec
!= boost::asio::error::operation_aborted
)
475 BOOST_THROW_EXCEPTION(
479 ws
.async_write(buffer(s
),
480 [&](error_code ec
, std::size_t)
482 if(ec
!= boost::asio::error::operation_aborted
)
483 BOOST_THROW_EXCEPTION(
490 if(ec
!= boost::asio::error::operation_aborted
)
491 BOOST_THROW_EXCEPTION(
495 BEAST_EXPECT(count
== 0);
497 BEAST_EXPECT(count
== 4);
500 // Four-way: read, write, ping, close
501 doFailLoop([&](test::fail_counter
& fc
)
504 boost::asio::io_context ioc
;
505 stream
<test::stream
> ws
{ioc
, fc
};
506 ws
.next_layer().connect(es
.stream());
507 ws
.handshake("localhost", "/");
508 std::size_t count
= 0;
509 std::string
const s
= "Hello, world!";
512 [&](error_code ec
, std::size_t)
514 if(ec
&& ec
!= boost::asio::error::operation_aborted
)
516 BEAST_EXPECTS(ec
, ec
.message());
517 BOOST_THROW_EXCEPTION(
521 BEAST_EXPECT(to_string(b
.data()) == s
);
525 ec
== boost::asio::error::operation_aborted
);
527 ws
.async_write(buffer(s
),
528 [&](error_code ec
, std::size_t n
)
531 BOOST_THROW_EXCEPTION(
533 BEAST_EXPECT(n
== s
.size());
534 BEAST_EXPECT(++count
== 1);
539 if(ec
!= boost::asio::error::operation_aborted
)
541 BEAST_EXPECTS(ec
, ec
.message());
542 BOOST_THROW_EXCEPTION(
551 BOOST_THROW_EXCEPTION(
554 BEAST_EXPECT(count
== 2 || count
== 3);
556 BEAST_EXPECT(count
== 0);
558 BEAST_EXPECT(count
== 4);
561 // Four-way: ping, read, write, close
562 doFailLoop([&](test::fail_counter
& fc
)
565 boost::asio::io_context ioc
;
566 stream
<test::stream
> ws
{ioc
, fc
};
567 ws
.next_layer().connect(es
.stream());
568 ws
.handshake("localhost", "/");
569 std::size_t count
= 0;
570 std::string
const s
= "Hello, world!";
576 BOOST_THROW_EXCEPTION(
578 BEAST_EXPECT(++count
== 1);
581 [&](error_code ec
, std::size_t)
583 if(ec
!= boost::asio::error::operation_aborted
)
584 BOOST_THROW_EXCEPTION(
588 ws
.async_write(buffer(s
),
589 [&](error_code ec
, std::size_t)
591 if(ec
!= boost::asio::error::operation_aborted
)
592 BOOST_THROW_EXCEPTION(
600 BOOST_THROW_EXCEPTION(
602 BEAST_EXPECT(++count
== 2);
604 BEAST_EXPECT(count
== 0);
606 BEAST_EXPECT(count
== 4);
615 void operator()(error_code
) {}
618 stream
<test::stream
> ws
{ioc_
};
619 stream
<test::stream
>::close_op
<handler
> op
{
621 using boost::asio::asio_handler_is_continuation
;
622 asio_handler_is_continuation(&op
);
634 BEAST_DEFINE_TESTSUITE(beast
,websocket
,close
);