]>
Commit | Line | Data |
---|---|---|
b32b8144 | 1 | // |
92f5a8d4 | 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
b32b8144 FG |
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 | ||
92f5a8d4 TL |
13 | #include <boost/beast/_experimental/test/tcp.hpp> |
14 | ||
b32b8144 FG |
15 | #include "test.hpp" |
16 | ||
92f5a8d4 TL |
17 | #include <boost/asio/ip/tcp.hpp> |
18 | #include <boost/asio/io_context.hpp> | |
11fdf7f2 TL |
19 | #include <boost/asio/strand.hpp> |
20 | ||
20effc67 TL |
21 | #if BOOST_ASIO_HAS_CO_AWAIT |
22 | #include <boost/asio/use_awaitable.hpp> | |
23 | #endif | |
24 | ||
b32b8144 FG |
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 | |
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( | |
92f5a8d4 | 67 | se.code() == net::error::operation_aborted, |
b32b8144 FG |
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( | |
92f5a8d4 | 87 | se.code() == net::error::operation_aborted, |
b32b8144 FG |
88 | se.code().message()); |
89 | } | |
90 | } | |
91 | } | |
92 | ||
93 | void | |
94 | testPing() | |
95 | { | |
96 | doTestPing(SyncClient{}); | |
97 | ||
98 | yield_to([&](yield_context yield) | |
99 | { | |
100 | doTestPing(AsyncClient{yield}); | |
101 | }); | |
102 | } | |
103 | ||
104 | void | |
105 | testSuspend() | |
106 | { | |
107 | // suspend on write | |
92f5a8d4 | 108 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
109 | { |
110 | echo_server es{log}; | |
92f5a8d4 | 111 | net::io_context ioc; |
b32b8144 FG |
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 | }); | |
92f5a8d4 | 125 | BEAST_EXPECT(ws.impl_->wr_block.is_locked()); |
b32b8144 FG |
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 | |
92f5a8d4 | 141 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
142 | { |
143 | echo_server es{log}; | |
92f5a8d4 | 144 | net::io_context ioc; |
b32b8144 FG |
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 | }); | |
92f5a8d4 | 157 | BEAST_EXPECT(ws.impl_->wr_block.is_locked()); |
b32b8144 FG |
158 | BEAST_EXPECT(count == 0); |
159 | ws.async_ping({}, | |
160 | [&](error_code ec) | |
161 | { | |
162 | ++count; | |
92f5a8d4 | 163 | if(ec != net::error::operation_aborted) |
b32b8144 FG |
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 | |
92f5a8d4 | 173 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
174 | { |
175 | echo_server es{log}; | |
92f5a8d4 | 176 | net::io_context ioc; |
b32b8144 FG |
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 | }); | |
92f5a8d4 | 193 | while(! ws.impl_->wr_block.is_locked()) |
b32b8144 FG |
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 | |
92f5a8d4 | 214 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
215 | { |
216 | echo_server es{log}; | |
92f5a8d4 | 217 | net::io_context ioc; |
b32b8144 FG |
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; | |
11fdf7f2 | 231 | if(ec != error::bad_control_fragment) |
b32b8144 FG |
232 | BOOST_THROW_EXCEPTION( |
233 | system_error{ec}); | |
234 | }); | |
92f5a8d4 | 235 | while(! ws.impl_->wr_block.is_locked()) |
b32b8144 FG |
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; | |
92f5a8d4 | 246 | if(ec != net::error::operation_aborted) |
b32b8144 FG |
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 | |
92f5a8d4 | 256 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
257 | { |
258 | echo_server es{log}; | |
92f5a8d4 | 259 | net::io_context ioc; |
b32b8144 FG |
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 | }); | |
92f5a8d4 | 276 | while(! ws.impl_->wr_block.is_locked()) |
b32b8144 FG |
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; | |
92f5a8d4 | 287 | if(ec != net::error::operation_aborted) |
b32b8144 FG |
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 | |
92f5a8d4 | 297 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
298 | { |
299 | echo_server es{log, kind::async}; | |
92f5a8d4 | 300 | net::io_context ioc; |
b32b8144 FG |
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 | }); | |
92f5a8d4 | 316 | while(! ws.impl_->wr_block.is_locked()) |
b32b8144 FG |
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; | |
92f5a8d4 | 327 | if(ec != net::error::operation_aborted) |
b32b8144 FG |
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 | |
92f5a8d4 | 337 | doFailLoop([&](test::fail_count& fc) |
b32b8144 FG |
338 | { |
339 | echo_server es{log}; | |
340 | error_code ec; | |
92f5a8d4 | 341 | net::io_context ioc; |
b32b8144 FG |
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 | }); | |
92f5a8d4 | 355 | BEAST_EXPECT(ws.impl_->wr_block.is_locked()); |
b32b8144 FG |
356 | ws.async_ping("", |
357 | [&](error_code ec) | |
358 | { | |
359 | ++count; | |
92f5a8d4 | 360 | if(ec != net::error::operation_aborted) |
b32b8144 FG |
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 | ||
92f5a8d4 TL |
376 | // suspend idle ping |
377 | { | |
378 | using socket_type = | |
379 | net::basic_stream_socket< | |
380 | net::ip::tcp, | |
20effc67 | 381 | net::any_io_executor>; |
92f5a8d4 TL |
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 | ||
b32b8144 FG |
416 | { |
417 | echo_server es{log, kind::async}; | |
92f5a8d4 | 418 | net::io_context ioc; |
b32b8144 FG |
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()); | |
b32b8144 FG |
437 | }); |
438 | if(! BEAST_EXPECT(run_until(ioc, 100, | |
92f5a8d4 | 439 | [&]{ return ws.impl_->wr_close; }))) |
b32b8144 FG |
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; | |
92f5a8d4 | 447 | BEAST_EXPECTS(ec == net:: |
b32b8144 FG |
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; | |
92f5a8d4 | 455 | BEAST_EXPECTS(ec == net:: |
b32b8144 FG |
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 | { | |
11fdf7f2 | 464 | if(count >= 3) |
b32b8144 FG |
465 | break; |
466 | ioc.run_one(); | |
467 | } | |
468 | BEAST_EXPECT(n < limit); | |
469 | ioc.run(); | |
470 | } | |
471 | } | |
472 | ||
11fdf7f2 TL |
473 | void |
474 | testMoveOnly() | |
475 | { | |
92f5a8d4 | 476 | net::io_context ioc; |
11fdf7f2 TL |
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 | |
485 | operator()(Args&&...) const | |
486 | { | |
487 | } | |
488 | }; | |
489 | ||
20effc67 TL |
490 | #if BOOST_ASIO_HAS_CO_AWAIT |
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 | ||
b32b8144 FG |
505 | void |
506 | run() override | |
507 | { | |
508 | testPing(); | |
509 | testSuspend(); | |
11fdf7f2 | 510 | testMoveOnly(); |
20effc67 TL |
511 | #if BOOST_ASIO_HAS_CO_AWAIT |
512 | boost::ignore_unused(&ping_test::testAwaitableCompiles); | |
513 | #endif | |
b32b8144 FG |
514 | } |
515 | }; | |
516 | ||
517 | BEAST_DEFINE_TESTSUITE(beast,websocket,ping); | |
518 | ||
519 | } // websocket | |
520 | } // beast | |
521 | } // boost |