]>
Commit | Line | Data |
---|---|---|
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 | |
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 | |
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 | |
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 | |
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 | |
485 | operator()(Args&&...) const | |
486 | { | |
487 | } | |
488 | }; | |
489 | ||
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 | ||
505 | void | |
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 |