1//
2// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
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)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10// Test that header file is self-contained.
11#include <boost/beast/websocket/stream.hpp>
12
13#include <boost/beast/_experimental/test/stream.hpp>
14#include <boost/beast/_experimental/test/tcp.hpp>
15#include <boost/beast/_experimental/test/immediate_executor.hpp>
16#include "test.hpp"
17
18#include <boost/asio/io_context.hpp>
19#include <boost/asio/strand.hpp>
20#include <boost/asio/bind_immediate_executor.hpp>
21#if BOOST_ASIO_HAS_CO_AWAIT
22#include <boost/asio/use_awaitable.hpp>
23#endif
24
25namespace boost {
26namespace beast {
27namespace websocket {
28
29class close_test : public websocket_test_suite
30{
31public:
32 template<class Wrap>
33 void
34 doTestClose(Wrap const& w)
35 {
36 permessage_deflate pmd;
37 pmd.client_enable = false;
38 pmd.server_enable = false;
39
40 // close
41 doTest(pmd, [&](ws_type& ws)
42 {
43 w.close(ws, {});
44 });
45
46 // close with code
47 doTest(pmd, [&](ws_type& ws)
48 {
49 w.close(ws, close_code::going_away);
50 });
51
52 // close with code and reason
53 doTest(pmd, [&](ws_type& ws)
54 {
55 w.close(ws, {
56 close_code::going_away,
57 "going away"});
58 });
59
60 // already closed
61 {
62 echo_server es{log};
63 stream<test::stream> ws{ioc_};
64 ws.next_layer().connect(remote&: es.stream());
65 w.handshake(ws, "localhost", "/");
66 w.close(ws, {});
67 try
68 {
69 w.close(ws, {});
70 fail(reason: "", __FILE__, __LINE__);
71 }
72 catch(system_error const& se)
73 {
74 BEAST_EXPECTS(
75 se.code() == net::error::operation_aborted,
76 se.code().message());
77 }
78 }
79
80 // drain a message after close
81 doTest(pmd, [&](ws_type& ws)
82 {
83 ws.next_layer().append(s: "\x81\x01\x2a");
84 w.close(ws, {});
85 });
86
87 // drain a big message after close
88 {
89 std::string s;
90 s = "\x81\x7e\x10\x01";
91 s.append(n: 4097, c: '*');
92 doTest(pmd, [&](ws_type& ws)
93 {
94 ws.next_layer().append(s);
95 w.close(ws, {});
96 });
97 }
98
99 // drain a ping after close
100 doTest(pmd, [&](ws_type& ws)
101 {
102 ws.next_layer().append(s: "\x89\x01*");
103 w.close(ws, {});
104 });
105
106 // drain invalid message frame after close
107 {
108 echo_server es{log};
109 stream<test::stream> ws{ioc_};
110 ws.next_layer().connect(remote&: es.stream());
111 w.handshake(ws, "localhost", "/");
112 ws.next_layer().append(s: "\x81\x81\xff\xff\xff\xff*");
113 try
114 {
115 w.close(ws, {});
116 fail(reason: "", __FILE__, __LINE__);
117 }
118 catch(system_error const& se)
119 {
120 BEAST_EXPECTS(
121 se.code() == error::bad_masked_frame,
122 se.code().message());
123 }
124 }
125
126 // drain invalid close frame after close
127 {
128 echo_server es{log};
129 stream<test::stream> ws{ioc_};
130 ws.next_layer().connect(remote&: es.stream());
131 w.handshake(ws, "localhost", "/");
132 ws.next_layer().append(s: "\x88\x01*");
133 try
134 {
135 w.close(ws, {});
136 fail(reason: "", __FILE__, __LINE__);
137 }
138 catch(system_error const& se)
139 {
140 BEAST_EXPECTS(
141 se.code() == error::bad_close_size,
142 se.code().message());
143 }
144 }
145
146 // drain masked close frame
147 {
148 echo_server es{log, kind::async_client};
149 stream<test::stream> ws{ioc_};
150 ws.next_layer().connect(remote&: es.stream());
151 ws.set_option(pmd);
152 es.async_handshake();
153 ws.accept();
154 w.close(ws, {});
155 }
156
157 // close with incomplete read message
158 doTest(pmd, [&](ws_type& ws)
159 {
160 ws.next_layer().append(s: "\x81\x02**");
161 static_buffer<1> b;
162 w.read_some(ws, 1, b);
163 w.close(ws, {});
164 });
165 }
166
167 void
168 testClose()
169 {
170 doTestClose(w: SyncClient{});
171
172 yield_to([&](yield_context yield)
173 {
174 doTestClose(w: AsyncClient{yield});
175 });
176 }
177
178 void
179 testTimeout()
180 {
181 using tcp = net::ip::tcp;
182
183 net::io_context ioc;
184
185 // success
186
187 {
188 stream<tcp::socket> ws1(ioc);
189 stream<tcp::socket> ws2(ioc);
190 test::connect(s1&: ws1.next_layer(), s2&: ws2.next_layer());
191 ws1.async_handshake(host: "test", target: "/", handler: test::success_handler());
192 ws2.async_accept(handler: test::success_handler());
193 test::run(ioc);
194
195 ws1.async_close(cr: {}, handler: test::success_handler());
196 ws2.async_close(cr: {}, handler: test::success_handler());
197 test::run(ioc);
198 }
199
200 {
201 stream<test::stream> ws1(ioc);
202 stream<test::stream> ws2(ioc);
203 test::connect(s1&: ws1.next_layer(), s2&: ws2.next_layer());
204 ws1.async_handshake(host: "test", target: "/", handler: test::success_handler());
205 ws2.async_accept(handler: test::success_handler());
206 test::run(ioc);
207
208 ws1.async_close(cr: {}, handler: test::success_handler());
209 ws2.async_close(cr: {}, handler: test::success_handler());
210 test::run(ioc);
211 }
212
213 // success, timeout enabled
214
215 {
216 stream<tcp::socket> ws1(ioc);
217 stream<tcp::socket> ws2(ioc);
218 test::connect(s1&: ws1.next_layer(), s2&: ws2.next_layer());
219 ws1.async_handshake(host: "test", target: "/", handler: test::success_handler());
220 ws2.async_accept(handler: test::success_handler());
221 test::run(ioc);
222
223 ws1.set_option(stream_base::timeout{
224 .handshake_timeout: std::chrono::milliseconds(50),
225 .idle_timeout: stream_base::none(),
226 .keep_alive_pings: false});
227 ws1.async_close(cr: {}, handler: test::success_handler());
228 ws2.async_close(cr: {}, handler: test::success_handler());
229 test::run(ioc);
230 }
231
232 {
233 stream<test::stream> ws1(ioc);
234 stream<test::stream> ws2(ioc);
235 test::connect(s1&: ws1.next_layer(), s2&: ws2.next_layer());
236 ws1.async_handshake(host: "test", target: "/", handler: test::success_handler());
237 ws2.async_accept(handler: test::success_handler());
238 test::run(ioc);
239
240 ws1.set_option(stream_base::timeout{
241 .handshake_timeout: std::chrono::milliseconds(50),
242 .idle_timeout: stream_base::none(),
243 .keep_alive_pings: false});
244 ws1.async_close(cr: {}, handler: test::success_handler());
245 ws2.async_close(cr: {}, handler: test::success_handler());
246 test::run(ioc);
247 }
248
249 // timeout
250
251 {
252 stream<tcp::socket> ws1(ioc);
253 stream<tcp::socket> ws2(ioc);
254 test::connect(s1&: ws1.next_layer(), s2&: ws2.next_layer());
255 ws1.async_handshake(host: "test", target: "/", handler: test::success_handler());
256 ws2.async_accept(handler: test::success_handler());
257 test::run(ioc);
258
259 ws1.set_option(stream_base::timeout{
260 .handshake_timeout: std::chrono::milliseconds(50),
261 .idle_timeout: stream_base::none(),
262 .keep_alive_pings: false});
263 ws1.async_close(cr: {}, handler: test::fail_handler(
264 ec: beast::error::timeout));
265 test::run(ioc);
266 }
267
268 {
269 stream<test::stream> ws1(ioc);
270 stream<test::stream> ws2(ioc);
271 test::connect(s1&: ws1.next_layer(), s2&: ws2.next_layer());
272 ws1.async_handshake(host: "test", target: "/", handler: test::success_handler());
273 ws2.async_accept(handler: test::success_handler());
274 test::run(ioc);
275
276 ws1.set_option(stream_base::timeout{
277 .handshake_timeout: std::chrono::milliseconds(50),
278 .idle_timeout: stream_base::none(),
279 .keep_alive_pings: false});
280 ws1.async_close(cr: {}, handler: test::fail_handler(
281 ec: beast::error::timeout));
282 test::run(ioc);
283 }
284 }
285
286 void
287 testSuspend()
288 {
289 // suspend on ping
290 doFailLoop(f: [&](test::fail_count& fc)
291 {
292 echo_server es{log};
293 net::io_context ioc;
294 stream<test::stream> ws{ioc, fc};
295 ws.next_layer().connect(remote&: es.stream());
296 ws.handshake(host: "localhost", target: "/");
297 std::size_t count = 0;
298 ws.async_ping(payload: "",
299 handler: [&](error_code ec)
300 {
301 ++count;
302 if(ec)
303 BOOST_THROW_EXCEPTION(
304 system_error{ec});
305 });
306 BEAST_EXPECT(ws.impl_->wr_block.is_locked());
307 BEAST_EXPECT(count == 0);
308 ws.async_close(cr: {},
309 handler: [&](error_code ec)
310 {
311 ++count;
312 if(ec)
313 BOOST_THROW_EXCEPTION(
314 system_error{ec});
315 });
316 BEAST_EXPECT(count == 0);
317 ioc.run();
318 BEAST_EXPECT(count == 2);
319 });
320
321 // suspend on write
322 doFailLoop(f: [&](test::fail_count& fc)
323 {
324 echo_server es{log};
325 net::io_context ioc;
326 stream<test::stream> ws{ioc, fc};
327 ws.next_layer().connect(remote&: es.stream());
328 ws.handshake(host: "localhost", target: "/");
329 std::size_t count = 0;
330 ws.async_write(bs: sbuf(s: "*"),
331 handler: [&](error_code ec, std::size_t n)
332 {
333 ++count;
334 if(ec)
335 BOOST_THROW_EXCEPTION(
336 system_error{ec});
337 BEAST_EXPECT(n == 1);
338 });
339 BEAST_EXPECT(ws.impl_->wr_block.is_locked());
340 BEAST_EXPECT(count == 0);
341 ws.async_close(cr: {},
342 handler: [&](error_code ec)
343 {
344 ++count;
345 if(ec)
346 BOOST_THROW_EXCEPTION(
347 system_error{ec});
348 });
349 BEAST_EXPECT(count == 0);
350 ioc.run();
351 BEAST_EXPECT(count == 2);
352 });
353
354 // suspend on read ping + message
355 doFailLoop(f: [&](test::fail_count& fc)
356 {
357 echo_server es{log};
358 multi_buffer b;
359 net::io_context ioc;
360 stream<test::stream> ws{ioc, fc};
361 ws.next_layer().connect(remote&: es.stream());
362 ws.handshake(host: "localhost", target: "/");
363 // add a ping and message to the input
364 ws.next_layer().append(s: string_view{
365 "\x89\x00" "\x81\x01*", 5});
366 std::size_t count = 0;
367 ws.async_read(buffer&: b,
368 handler: [&](error_code ec, std::size_t)
369 {
370 ++count;
371 if(ec)
372 BOOST_THROW_EXCEPTION(
373 system_error{ec});
374 });
375 while(! ws.impl_->wr_block.is_locked())
376 {
377 ioc.run_one();
378 if(! BEAST_EXPECT(! ioc.stopped()))
379 break;
380 }
381 BEAST_EXPECT(count == 0);
382 ws.async_close(cr: {},
383 handler: [&](error_code ec)
384 {
385 ++count;
386 if(ec)
387 BOOST_THROW_EXCEPTION(
388 system_error{ec});
389 });
390 BEAST_EXPECT(count == 0);
391 ioc.run();
392 BEAST_EXPECT(count == 2);
393 });
394
395 // suspend on read bad message
396 doFailLoop(f: [&](test::fail_count& fc)
397 {
398 echo_server es{log};
399 multi_buffer b;
400 net::io_context ioc;
401 stream<test::stream> ws{ioc, fc};
402 ws.next_layer().connect(remote&: es.stream());
403 ws.handshake(host: "localhost", target: "/");
404 // add an invalid frame to the input
405 ws.next_layer().append(s: string_view{
406 "\x09\x00", 2});
407 std::size_t count = 0;
408 ws.async_read(buffer&: b,
409 handler: [&](error_code ec, std::size_t)
410 {
411 if(ec != error::bad_control_fragment)
412 BOOST_THROW_EXCEPTION(
413 system_error{ec});
414 BEAST_EXPECT(++count == 1);
415 });
416 while(! ws.impl_->wr_block.is_locked())
417 {
418 ioc.run_one();
419 if(! BEAST_EXPECT(! ioc.stopped()))
420 break;
421 }
422 BEAST_EXPECT(count == 0);
423 ws.async_close(cr: {},
424 handler: [&](error_code ec)
425 {
426 if(ec != net::error::operation_aborted)
427 BOOST_THROW_EXCEPTION(
428 system_error{ec});
429 BEAST_EXPECT(++count == 2);
430 });
431 BEAST_EXPECT(count == 0);
432 ioc.run();
433 BEAST_EXPECT(count == 2);
434 });
435
436 // suspend on read close #1
437 doFailLoop(f: [&](test::fail_count& fc)
438 {
439 echo_server es{log};
440 multi_buffer b;
441 net::io_context ioc;
442 stream<test::stream> ws{ioc, fc};
443 ws.next_layer().connect(remote&: es.stream());
444 ws.handshake(host: "localhost", target: "/");
445 // add a close frame to the input
446 ws.next_layer().append(s: string_view{
447 "\x88\x00", 2});
448 std::size_t count = 0;
449 ws.async_read(buffer&: b,
450 handler: [&](error_code ec, std::size_t)
451 {
452 if(ec != error::closed)
453 BOOST_THROW_EXCEPTION(
454 system_error{ec});
455 BEAST_EXPECT(++count == 1);
456 });
457 while(! ws.impl_->wr_block.is_locked())
458 {
459 ioc.run_one();
460 if(! BEAST_EXPECT(! ioc.stopped()))
461 break;
462 }
463 BEAST_EXPECT(count == 0);
464 ws.async_close(cr: {},
465 handler: [&](error_code ec)
466 {
467 if(ec != net::error::operation_aborted)
468 BOOST_THROW_EXCEPTION(
469 system_error{ec});
470 BEAST_EXPECT(++count == 2);
471 });
472 BEAST_EXPECT(count == 0);
473 ioc.run();
474 BEAST_EXPECT(count == 2);
475 });
476
477 // teardown on received close
478 doFailLoop(f: [&](test::fail_count& fc)
479 {
480 echo_server es{log};
481 std::string const s = "Hello, world!";
482 multi_buffer b;
483 net::io_context ioc;
484 stream<test::stream> ws{ioc, fc};
485 ws.next_layer().connect(remote&: es.stream());
486 ws.handshake(host: "localhost", target: "/");
487 // add a close frame to the input
488 ws.next_layer().append(s: string_view{
489 "\x88\x00", 2});
490 std::size_t count = 0;
491 ws.async_write(bs: net::buffer(data: s),
492 handler: [&](error_code ec, std::size_t n)
493 {
494 if(ec)
495 BOOST_THROW_EXCEPTION(
496 system_error{ec});
497 BEAST_EXPECT(n == s.size());
498 BEAST_EXPECT(++count == 1);
499 });
500 ws.async_read(buffer&: b,
501 handler: [&](error_code ec, std::size_t)
502 {
503 if(ec != net::error::operation_aborted)
504 BOOST_THROW_EXCEPTION(
505 system_error{ec});
506 BEAST_EXPECT(++count == 3);
507 });
508 ws.async_close(cr: {},
509 handler: [&](error_code ec)
510 {
511 if(ec)
512 BOOST_THROW_EXCEPTION(
513 system_error{ec});
514 BEAST_EXPECT(++count == 2);
515 });
516 BEAST_EXPECT(count == 0);
517 ioc.run();
518 BEAST_EXPECT(count == 3);
519 });
520
521 // check for deadlock
522 doFailLoop(f: [&](test::fail_count& fc)
523 {
524 echo_server es{log};
525 multi_buffer b;
526 std::string const s = "Hello, world!";
527 net::io_context ioc;
528 stream<test::stream> ws{ioc, fc};
529 ws.next_layer().connect(remote&: es.stream());
530 ws.handshake(host: "localhost", target: "/");
531 // add a ping frame to the input
532 ws.next_layer().append(s: string_view{
533 "\x89\x00", 2});
534 std::size_t count = 0;
535 ws.async_write(bs: net::buffer(data: s),
536 handler: [&](error_code ec, std::size_t n)
537 {
538 if(ec)
539 BOOST_THROW_EXCEPTION(
540 system_error{ec});
541 BEAST_EXPECT(n == s.size());
542 BEAST_EXPECT(++count == 1);
543 });
544 ws.async_read(buffer&: b,
545 handler: [&](error_code ec, std::size_t)
546 {
547 if(ec != net::error::operation_aborted)
548 BOOST_THROW_EXCEPTION(
549 system_error{ec});
550 BEAST_EXPECT(++count == 3);
551 });
552 BEAST_EXPECT(ws.impl_->rd_block.is_locked());
553 ws.async_close(cr: {},
554 handler: [&](error_code ec)
555 {
556 if(ec)
557 BOOST_THROW_EXCEPTION(
558 system_error{ec});
559 BEAST_EXPECT(++count == 2);
560 });
561 BEAST_EXPECT(ws.is_open());
562 BEAST_EXPECT(ws.impl_->wr_block.is_locked());
563 BEAST_EXPECT(count == 0);
564 ioc.run();
565 BEAST_EXPECT(count == 3);
566 });
567
568 // Four-way: close, read, write, ping
569 doFailLoop(f: [&](test::fail_count& fc)
570 {
571 echo_server es{log};
572 std::string const s = "Hello, world!";
573 multi_buffer b;
574 net::io_context ioc;
575 stream<test::stream> ws{ioc, fc};
576 ws.next_layer().connect(remote&: es.stream());
577 ws.handshake(host: "localhost", target: "/");
578 std::size_t count = 0;
579 ws.async_close(cr: {},
580 handler: [&](error_code ec)
581 {
582 if(ec)
583 BOOST_THROW_EXCEPTION(
584 system_error{ec});
585 BEAST_EXPECT(++count == 1);
586 });
587 ws.async_read(buffer&: b,
588 handler: [&](error_code ec, std::size_t)
589 {
590 if(ec != net::error::operation_aborted)
591 BOOST_THROW_EXCEPTION(
592 system_error{ec});
593 ++count;
594 });
595 ws.async_write(bs: net::buffer(data: s),
596 handler: [&](error_code ec, std::size_t)
597 {
598 if(ec != net::error::operation_aborted)
599 BOOST_THROW_EXCEPTION(
600 system_error{ec});
601 ++count;
602 });
603 ws.async_ping(payload: {},
604 handler: [&](error_code ec)
605 {
606 if(ec != net::error::operation_aborted)
607 BOOST_THROW_EXCEPTION(
608 system_error{ec});
609 ++count;
610 });
611 BEAST_EXPECT(count == 0);
612 ioc.run();
613 BEAST_EXPECT(count == 4);
614 });
615
616 // Four-way: read, write, ping, close
617 doFailLoop(f: [&](test::fail_count& fc)
618 {
619 echo_server es{log};
620 multi_buffer b;
621 std::string const s = "Hello, world!";
622 net::io_context ioc;
623 stream<test::stream> ws{ioc, fc};
624 ws.next_layer().connect(remote&: es.stream());
625 ws.handshake(host: "localhost", target: "/");
626 std::size_t count = 0;
627 ws.async_read(buffer&: b,
628 handler: [&](error_code ec, std::size_t)
629 {
630 if(ec && ec != net::error::operation_aborted)
631 {
632 BEAST_EXPECTS(ec, ec.message());
633 BOOST_THROW_EXCEPTION(
634 system_error{ec});
635 }
636 if(! ec)
637 BEAST_EXPECT(buffers_to_string(b.data()) == s);
638 ++count;
639 if(count == 4)
640 BEAST_EXPECT(
641 ec == net::error::operation_aborted);
642 });
643 ws.async_write(bs: net::buffer(data: s),
644 handler: [&](error_code ec, std::size_t n)
645 {
646 if(ec)
647 BOOST_THROW_EXCEPTION(
648 system_error{ec});
649 BEAST_EXPECT(n == s.size());
650 ++count;
651 });
652 ws.async_ping(payload: {},
653 handler: [&](error_code ec)
654 {
655 if(ec != net::error::operation_aborted)
656 {
657 BEAST_EXPECTS(ec, ec.message());
658 BOOST_THROW_EXCEPTION(
659 system_error{ec});
660 }
661 ++count;
662 });
663 ws.async_close(cr: {},
664 handler: [&](error_code ec)
665 {
666 if(ec)
667 BOOST_THROW_EXCEPTION(
668 system_error{ec});
669 ++count;
670 BEAST_EXPECT(count == 2 || count == 3);
671 });
672 BEAST_EXPECT(count == 0);
673 ioc.run();
674 BEAST_EXPECT(count == 4);
675 });
676
677 // Four-way: ping, read, write, close
678 doFailLoop(f: [&](test::fail_count& fc)
679 {
680 echo_server es{log};
681 multi_buffer b;
682 std::string const s = "Hello, world!";
683 net::io_context ioc;
684 stream<test::stream> ws{ioc, fc};
685 ws.next_layer().connect(remote&: es.stream());
686 ws.handshake(host: "localhost", target: "/");
687 std::size_t count = 0;
688 ws.async_ping(payload: {},
689 handler: [&](error_code ec)
690 {
691 if(ec)
692 BOOST_THROW_EXCEPTION(
693 system_error{ec});
694 BEAST_EXPECT(++count == 1);
695 });
696 ws.async_read(buffer&: b,
697 handler: [&](error_code ec, std::size_t)
698 {
699 if(ec != net::error::operation_aborted)
700 BOOST_THROW_EXCEPTION(
701 system_error{ec});
702 ++count;
703 });
704 ws.async_write(bs: net::buffer(data: s),
705 handler: [&](error_code ec, std::size_t)
706 {
707 if(ec != net::error::operation_aborted)
708 BOOST_THROW_EXCEPTION(
709 system_error{ec});
710 ++count;
711 });
712 ws.async_close(cr: {},
713 handler: [&](error_code ec)
714 {
715 if(ec)
716 BOOST_THROW_EXCEPTION(
717 system_error{ec});
718 BEAST_EXPECT(++count == 2);
719 });
720 BEAST_EXPECT(count == 0);
721 ioc.run();
722 BEAST_EXPECT(count == 4);
723 });
724 }
725
726 void
727 testMoveOnly()
728 {
729 net::io_context ioc;
730 stream<test::stream> ws{ioc};
731 ws.async_close(cr: {}, handler: move_only_handler{});
732 }
733
734 void
735 testImmediate()
736 {
737 doFailLoop(f: [&](test::fail_count& fc)
738 {
739 std::string const s = "Hello, world!";
740 multi_buffer b;
741 net::io_context ioc;
742 stream<test::stream> ws{ioc, fc};
743 std::size_t count = 0;
744 std::size_t ic = 0u;
745 test::immediate_executor imex{ic};
746
747 ws.async_read(buffer&: b,
748 handler: asio::bind_immediate_executor(e: imex,
749 t: [&](error_code ec, std::size_t)
750 {
751 if(ec != net::error::operation_aborted)
752 BOOST_THROW_EXCEPTION(
753 system_error{ec});
754 ++count;
755 }));
756
757 ws.async_write(bs: net::buffer(data: s),
758 handler: asio::bind_immediate_executor(e: imex,
759 t: [&](error_code ec, std::size_t)
760 {
761 if(ec != net::error::operation_aborted)
762 BOOST_THROW_EXCEPTION(
763 system_error{ec});
764 ++count;
765 }));
766 ws.async_ping(payload: {},
767 handler: asio::bind_immediate_executor(e: imex,
768 t: [&](error_code ec)
769 {
770 if(ec != net::error::operation_aborted)
771 BOOST_THROW_EXCEPTION(
772 system_error{ec});
773 ++count;
774 }));
775
776 BEAST_EXPECT(ioc.run() == 0);
777 BEAST_EXPECT(count == 3);
778 BEAST_EXPECT(ic == 3);
779 });
780 }
781
782
783 struct copyable_handler
784 {
785 template<class... Args>
786 void
787 operator()(Args&&...) const
788 {
789 }
790 };
791
792#if BOOST_ASIO_HAS_CO_AWAIT
793 void testAwaitableCompiles(stream<test::stream>& s, close_reason cr )
794 {
795 static_assert(std::is_same_v<
796 net::awaitable<void>, decltype(
797 s.async_close(cr, net::use_awaitable))>);
798 }
799#endif
800
801 void
802 run() override
803 {
804 testClose();
805 testTimeout();
806 testSuspend();
807 testMoveOnly();
808 testImmediate();
809#if BOOST_ASIO_HAS_CO_AWAIT
810 boost::ignore_unused(&close_test::testAwaitableCompiles);
811#endif
812 }
813};
814
815BEAST_DEFINE_TESTSUITE(beast,websocket,close);
816
817} // websocket
818} // beast
819} // boost
820

source code of boost/libs/beast/test/beast/websocket/close.cpp