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