• 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/tcp.hpp>
14 
15 #include "test.hpp"
16 
17 #include <boost/asio/ip/tcp.hpp>
18 #include <boost/asio/io_context.hpp>
19 #include <boost/asio/strand.hpp>
20 
21 #if BOOST_ASIO_HAS_CO_AWAIT
22 #include <boost/asio/use_awaitable.hpp>
23 #endif
24 
25 namespace boost {
26 namespace beast {
27 namespace websocket {
28 
29 class ping_test : public websocket_test_suite
30 {
31 public:
32     template<class Wrap>
33     void
doTestPing(Wrap const & w)34     doTestPing(Wrap const& w)
35     {
36         permessage_deflate pmd;
37         pmd.client_enable = false;
38         pmd.server_enable = false;
39 
40         // ping
41         doTest(pmd, [&](ws_type& ws)
42         {
43             w.ping(ws, {});
44         });
45 
46         // pong
47         doTest(pmd, [&](ws_type& ws)
48         {
49             w.pong(ws, {});
50         });
51 
52         // ping, already closed
53         {
54             echo_server es{log};
55             stream<test::stream> ws{ioc_};
56             ws.next_layer().connect(es.stream());
57             ws.handshake("localhost", "/");
58             ws.close({});
59             try
60             {
61                 w.ping(ws, {});
62                 fail("", __FILE__, __LINE__);
63             }
64             catch(system_error const& se)
65             {
66                 BEAST_EXPECTS(
67                     se.code() == net::error::operation_aborted,
68                     se.code().message());
69             }
70         }
71 
72         // pong, already closed
73         {
74             echo_server es{log};
75             stream<test::stream> ws{ioc_};
76             ws.next_layer().connect(es.stream());
77             ws.handshake("localhost", "/");
78             ws.close({});
79             try
80             {
81                 w.pong(ws, {});
82                 fail("", __FILE__, __LINE__);
83             }
84             catch(system_error const& se)
85             {
86                 BEAST_EXPECTS(
87                     se.code() == net::error::operation_aborted,
88                     se.code().message());
89             }
90         }
91     }
92 
93     void
testPing()94     testPing()
95     {
96         doTestPing(SyncClient{});
97 
98         yield_to([&](yield_context yield)
99         {
100             doTestPing(AsyncClient{yield});
101         });
102     }
103 
104     void
testSuspend()105     testSuspend()
106     {
107         // suspend on write
108         doFailLoop([&](test::fail_count& fc)
109         {
110             echo_server es{log};
111             net::io_context ioc;
112             stream<test::stream> ws{ioc, fc};
113             ws.next_layer().connect(es.stream());
114             ws.handshake("localhost", "/");
115             std::size_t count = 0;
116             ws.async_write(sbuf("Hello, world"),
117                 [&](error_code ec, std::size_t n)
118                 {
119                     ++count;
120                     if(ec)
121                         BOOST_THROW_EXCEPTION(
122                             system_error{ec});
123                     BEAST_EXPECT(n == 12);
124                 });
125             BEAST_EXPECT(ws.impl_->wr_block.is_locked());
126             BEAST_EXPECT(count == 0);
127             ws.async_ping({},
128                 [&](error_code ec)
129                 {
130                     ++count;
131                     if(ec)
132                         BOOST_THROW_EXCEPTION(
133                             system_error{ec});
134                 });
135             BEAST_EXPECT(count == 0);
136             ioc.run();
137             BEAST_EXPECT(count == 2);
138         });
139 
140         // suspend on close
141         doFailLoop([&](test::fail_count& fc)
142         {
143             echo_server es{log};
144             net::io_context ioc;
145             stream<test::stream> ws{ioc, fc};
146             ws.next_layer().connect(es.stream());
147             ws.handshake("localhost", "/");
148             std::size_t count = 0;
149             ws.async_close({},
150                 [&](error_code ec)
151                 {
152                     ++count;
153                     if(ec)
154                         BOOST_THROW_EXCEPTION(
155                             system_error{ec});
156                 });
157             BEAST_EXPECT(ws.impl_->wr_block.is_locked());
158             BEAST_EXPECT(count == 0);
159             ws.async_ping({},
160                 [&](error_code ec)
161                 {
162                     ++count;
163                     if(ec != net::error::operation_aborted)
164                         BOOST_THROW_EXCEPTION(
165                             system_error{ec});
166                 });
167             BEAST_EXPECT(count == 0);
168             ioc.run();
169             BEAST_EXPECT(count == 2);
170         });
171 
172         // suspend on read ping + message
173         doFailLoop([&](test::fail_count& fc)
174         {
175             echo_server es{log};
176             net::io_context ioc;
177             stream<test::stream> ws{ioc, fc};
178             ws.next_layer().connect(es.stream());
179             ws.handshake("localhost", "/");
180             // add a ping and message to the input
181             ws.next_layer().append(string_view{
182                 "\x89\x00" "\x81\x01*", 5});
183             std::size_t count = 0;
184             multi_buffer b;
185             ws.async_read(b,
186                 [&](error_code ec, std::size_t)
187                 {
188                     ++count;
189                     if(ec)
190                         BOOST_THROW_EXCEPTION(
191                             system_error{ec});
192                 });
193             while(! ws.impl_->wr_block.is_locked())
194             {
195                 ioc.run_one();
196                 if(! BEAST_EXPECT(! ioc.stopped()))
197                     break;
198             }
199             BEAST_EXPECT(count == 0);
200             ws.async_ping({},
201                 [&](error_code ec)
202                 {
203                     ++count;
204                     if(ec)
205                         BOOST_THROW_EXCEPTION(
206                             system_error{ec});
207                 });
208             BEAST_EXPECT(count == 0);
209             ioc.run();
210             BEAST_EXPECT(count == 2);
211         });
212 
213         // suspend on read bad message
214         doFailLoop([&](test::fail_count& fc)
215         {
216             echo_server es{log};
217             net::io_context ioc;
218             stream<test::stream> ws{ioc, fc};
219             ws.next_layer().connect(es.stream());
220             ws.handshake("localhost", "/");
221             // add an invalid frame to the input
222             ws.next_layer().append(string_view{
223                 "\x09\x00", 2});
224 
225             std::size_t count = 0;
226             multi_buffer b;
227             ws.async_read(b,
228                 [&](error_code ec, std::size_t)
229                 {
230                     ++count;
231                     if(ec != error::bad_control_fragment)
232                         BOOST_THROW_EXCEPTION(
233                             system_error{ec});
234                 });
235             while(! ws.impl_->wr_block.is_locked())
236             {
237                 ioc.run_one();
238                 if(! BEAST_EXPECT(! ioc.stopped()))
239                     break;
240             }
241             BEAST_EXPECT(count == 0);
242             ws.async_ping({},
243                 [&](error_code ec)
244                 {
245                     ++count;
246                     if(ec != net::error::operation_aborted)
247                         BOOST_THROW_EXCEPTION(
248                             system_error{ec});
249                 });
250             BEAST_EXPECT(count == 0);
251             ioc.run();
252             BEAST_EXPECT(count == 2);
253         });
254 
255         // suspend on read close #1
256         doFailLoop([&](test::fail_count& fc)
257         {
258             echo_server es{log};
259             net::io_context ioc;
260             stream<test::stream> ws{ioc, fc};
261             ws.next_layer().connect(es.stream());
262             ws.handshake("localhost", "/");
263             // add a close frame to the input
264             ws.next_layer().append(string_view{
265                 "\x88\x00", 2});
266             std::size_t count = 0;
267             multi_buffer b;
268             ws.async_read(b,
269                 [&](error_code ec, std::size_t)
270                 {
271                     ++count;
272                     if(ec != error::closed)
273                         BOOST_THROW_EXCEPTION(
274                             system_error{ec});
275                 });
276             while(! ws.impl_->wr_block.is_locked())
277             {
278                 ioc.run_one();
279                 if(! BEAST_EXPECT(! ioc.stopped()))
280                     break;
281             }
282             BEAST_EXPECT(count == 0);
283             ws.async_ping({},
284                 [&](error_code ec)
285                 {
286                     ++count;
287                     if(ec != net::error::operation_aborted)
288                         BOOST_THROW_EXCEPTION(
289                             system_error{ec});
290                 });
291             BEAST_EXPECT(count == 0);
292             ioc.run();
293             BEAST_EXPECT(count == 2);
294         });
295 
296         // suspend on read close #2
297         doFailLoop([&](test::fail_count& fc)
298         {
299             echo_server es{log, kind::async};
300             net::io_context ioc;
301             stream<test::stream> ws{ioc, fc};
302             ws.next_layer().connect(es.stream());
303             ws.handshake("localhost", "/");
304             // Cause close to be received
305             es.async_close();
306             std::size_t count = 0;
307             multi_buffer b;
308             ws.async_read(b,
309                 [&](error_code ec, std::size_t)
310                 {
311                     ++count;
312                     if(ec != error::closed)
313                         BOOST_THROW_EXCEPTION(
314                             system_error{ec});
315                 });
316             while(! ws.impl_->wr_block.is_locked())
317             {
318                 ioc.run_one();
319                 if(! BEAST_EXPECT(! ioc.stopped()))
320                     break;
321             }
322             BEAST_EXPECT(count == 0);
323             ws.async_ping({},
324                 [&](error_code ec)
325                 {
326                     ++count;
327                     if(ec != net::error::operation_aborted)
328                         BOOST_THROW_EXCEPTION(
329                             system_error{ec});
330                 });
331             BEAST_EXPECT(count == 0);
332             ioc.run();
333             BEAST_EXPECT(count == 2);
334         });
335 
336         // don't ping on close
337         doFailLoop([&](test::fail_count& fc)
338         {
339             echo_server es{log};
340             error_code ec;
341             net::io_context ioc;
342             stream<test::stream> ws{ioc, fc};
343             ws.next_layer().connect(es.stream());
344             ws.handshake("localhost", "/");
345             std::size_t count = 0;
346             ws.async_write(sbuf("*"),
347                 [&](error_code ec, std::size_t n)
348                 {
349                     ++count;
350                     if(ec)
351                         BOOST_THROW_EXCEPTION(
352                             system_error{ec});
353                     BEAST_EXPECT(n == 1);
354                 });
355             BEAST_EXPECT(ws.impl_->wr_block.is_locked());
356             ws.async_ping("",
357                 [&](error_code ec)
358                 {
359                     ++count;
360                     if(ec != net::error::operation_aborted)
361                         BOOST_THROW_EXCEPTION(
362                             system_error{ec});
363                 });
364             ws.async_close({},
365                 [&](error_code)
366                 {
367                     ++count;
368                     if(ec)
369                         BOOST_THROW_EXCEPTION(
370                             system_error{ec});
371                 });
372             ioc.run();
373             BEAST_EXPECT(count == 3);
374         });
375 
376         // suspend idle ping
377         {
378             using socket_type =
379                 net::basic_stream_socket<
380                     net::ip::tcp,
381                     net::any_io_executor>;
382             net::io_context ioc;
383             stream<socket_type> ws1(ioc);
384             stream<socket_type> ws2(ioc);
385             ws1.set_option(stream_base::timeout{
386                 stream_base::none(),
387                 std::chrono::seconds(0),
388                 true});
389             test::connect(
390                 ws1.next_layer(),
391                 ws2.next_layer());
392             ws1.async_handshake("localhost", "/",
393                 [](error_code){});
394             ws2.async_accept([](error_code){});
395             ioc.run();
396             ioc.restart();
397             flat_buffer b1;
398             auto mb = b1.prepare(65536);
399             std::memset(mb.data(), 0, mb.size());
400             b1.commit(65536);
401             ws1.async_write(b1.data(),
402                 [&](error_code, std::size_t){});
403             BEAST_EXPECT(
404                 ws1.impl_->wr_block.is_locked());
405             ws1.async_read_some(net::mutable_buffer{},
406                 [&](error_code, std::size_t){});
407             ioc.run();
408             ioc.restart();
409             flat_buffer b2;
410             ws2.async_read(b2,
411                 [&](error_code, std::size_t){});
412             ioc.run();
413         }
414         //);
415 
416         {
417             echo_server es{log, kind::async};
418             net::io_context ioc;
419             stream<test::stream> ws{ioc};
420             ws.next_layer().connect(es.stream());
421             ws.handshake("localhost", "/");
422 
423             // Cause close to be received
424             es.async_close();
425 
426             multi_buffer b;
427             std::size_t count = 0;
428             // Read a close frame.
429             // Sends a close frame, blocking writes.
430             ws.async_read(b,
431                 [&](error_code ec, std::size_t)
432                 {
433                     // Read should complete with error::closed
434                     ++count;
435                     BEAST_EXPECTS(ec == error::closed,
436                         ec.message());
437                 });
438             if(! BEAST_EXPECT(run_until(ioc, 100,
439                     [&]{ return ws.impl_->wr_close; })))
440                 return;
441             // Try to ping
442             ws.async_ping("payload",
443                 [&](error_code ec)
444                 {
445                     // Pings after a close are aborted
446                     ++count;
447                     BEAST_EXPECTS(ec == net::
448                         error::operation_aborted,
449                             ec.message());
450                     // Subsequent calls to close are aborted
451                     ws.async_close({},
452                         [&](error_code ec)
453                         {
454                             ++count;
455                             BEAST_EXPECTS(ec == net::
456                                 error::operation_aborted,
457                                     ec.message());
458                         });
459                 });
460             static std::size_t constexpr limit = 100;
461             std::size_t n;
462             for(n = 0; n < limit; ++n)
463             {
464                 if(count >= 3)
465                     break;
466                 ioc.run_one();
467             }
468             BEAST_EXPECT(n < limit);
469             ioc.run();
470         }
471     }
472 
473     void
testMoveOnly()474     testMoveOnly()
475     {
476         net::io_context ioc;
477         stream<test::stream> ws{ioc};
478         ws.async_ping({}, move_only_handler{});
479     }
480 
481     struct copyable_handler
482     {
483         template<class... Args>
484         void
operator ()boost::beast::websocket::ping_test::copyable_handler485         operator()(Args&&...) const
486         {
487         }
488     };
489 
490 #if BOOST_ASIO_HAS_CO_AWAIT
testAwaitableCompiles(stream<test::stream> & s,ping_data & pdat)491     void testAwaitableCompiles(
492         stream<test::stream>& s,
493         ping_data& pdat)
494     {
495         static_assert(std::is_same_v<
496             net::awaitable<void>, decltype(
497             s.async_ping(pdat, net::use_awaitable))>);
498 
499         static_assert(std::is_same_v<
500             net::awaitable<void>, decltype(
501             s.async_pong(pdat, net::use_awaitable))>);
502     }
503 #endif
504 
505     void
run()506     run() override
507     {
508         testPing();
509         testSuspend();
510         testMoveOnly();
511 #if BOOST_ASIO_HAS_CO_AWAIT
512         boost::ignore_unused(&ping_test::testAwaitableCompiles);
513 #endif
514     }
515 };
516 
517 BEAST_DEFINE_TESTSUITE(beast,websocket,ping);
518 
519 } // websocket
520 } // beast
521 } // boost
522