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