]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/websocket/write.cpp
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / libs / beast / test / beast / websocket / write.cpp
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 <boost/asio/io_service.hpp>
14 #include <boost/asio/strand.hpp>
15
16 #include "test.hpp"
17
18 namespace boost {
19 namespace beast {
20 namespace websocket {
21
22 class write_test : public websocket_test_suite
23 {
24 public:
25 template<bool deflateSupported, class Wrap>
26 void
27 doTestWrite(Wrap const& w)
28 {
29 using boost::asio::buffer;
30
31 permessage_deflate pmd;
32 pmd.client_enable = false;
33 pmd.server_enable = false;
34
35 // already closed
36 {
37 echo_server es{log};
38 stream<test::stream> ws{ioc_};
39 ws.next_layer().connect(es.stream());
40 ws.handshake("localhost", "/");
41 ws.close({});
42 try
43 {
44 w.write(ws, sbuf(""));
45 fail("", __FILE__, __LINE__);
46 }
47 catch(system_error const& se)
48 {
49 BEAST_EXPECTS(
50 se.code() == boost::asio::error::operation_aborted,
51 se.code().message());
52 }
53 }
54
55 // message
56 doTest<deflateSupported>(pmd,
57 [&](ws_type_t<deflateSupported>& ws)
58 {
59 ws.auto_fragment(false);
60 ws.binary(false);
61 std::string const s = "Hello, world!";
62 w.write(ws, buffer(s));
63 multi_buffer b;
64 w.read(ws, b);
65 BEAST_EXPECT(ws.got_text());
66 BEAST_EXPECT(to_string(b.data()) == s);
67 });
68
69 // empty message
70 doTest<deflateSupported>(pmd,
71 [&](ws_type_t<deflateSupported>& ws)
72 {
73 ws.text(true);
74 w.write(ws, boost::asio::const_buffer{});
75 multi_buffer b;
76 w.read(ws, b);
77 BEAST_EXPECT(ws.got_text());
78 BEAST_EXPECT(b.size() == 0);
79 });
80
81 // fragmented message
82 doTest<deflateSupported>(pmd,
83 [&](ws_type_t<deflateSupported>& ws)
84 {
85 ws.auto_fragment(false);
86 ws.binary(false);
87 std::string const s = "Hello, world!";
88 w.write_some(ws, false, buffer(s.data(), 5));
89 w.write_some(ws, true, buffer(s.data() + 5, s.size() - 5));
90 multi_buffer b;
91 w.read(ws, b);
92 BEAST_EXPECT(ws.got_text());
93 BEAST_EXPECT(to_string(b.data()) == s);
94 });
95
96 // continuation
97 doTest<deflateSupported>(pmd,
98 [&](ws_type_t<deflateSupported>& ws)
99 {
100 std::string const s = "Hello";
101 std::size_t const chop = 3;
102 BOOST_ASSERT(chop < s.size());
103 w.write_some(ws, false,
104 buffer(s.data(), chop));
105 w.write_some(ws, true, buffer(
106 s.data() + chop, s.size() - chop));
107 flat_buffer b;
108 w.read(ws, b);
109 BEAST_EXPECT(to_string(b.data()) == s);
110 });
111
112 // mask
113 doTest<deflateSupported>(pmd,
114 [&](ws_type_t<deflateSupported>& ws)
115 {
116 ws.auto_fragment(false);
117 std::string const s = "Hello";
118 w.write(ws, buffer(s));
119 flat_buffer b;
120 w.read(ws, b);
121 BEAST_EXPECT(to_string(b.data()) == s);
122 });
123
124 // mask (large)
125 doTest<deflateSupported>(pmd,
126 [&](ws_type_t<deflateSupported>& ws)
127 {
128 ws.auto_fragment(false);
129 ws.write_buffer_size(16);
130 std::string const s(32, '*');
131 w.write(ws, buffer(s));
132 flat_buffer b;
133 w.read(ws, b);
134 BEAST_EXPECT(to_string(b.data()) == s);
135 });
136
137 // mask, autofrag
138 doTest<deflateSupported>(pmd,
139 [&](ws_type_t<deflateSupported>& ws)
140 {
141 ws.auto_fragment(true);
142 std::string const s(16384, '*');
143 w.write(ws, buffer(s));
144 flat_buffer b;
145 w.read(ws, b);
146 BEAST_EXPECT(to_string(b.data()) == s);
147 });
148
149 // nomask
150 doStreamLoop([&](test::stream& ts)
151 {
152 echo_server es{log, kind::async_client};
153 ws_type_t<deflateSupported> ws{ts};
154 ws.next_layer().connect(es.stream());
155 try
156 {
157 es.async_handshake();
158 w.accept(ws);
159 ws.auto_fragment(false);
160 std::string const s = "Hello";
161 w.write(ws, buffer(s));
162 flat_buffer b;
163 w.read(ws, b);
164 BEAST_EXPECT(to_string(b.data()) == s);
165 w.close(ws, {});
166 }
167 catch(...)
168 {
169 ts.close();
170 throw;
171 }
172 ts.close();
173 });
174
175 // nomask, autofrag
176 doStreamLoop([&](test::stream& ts)
177 {
178 echo_server es{log, kind::async_client};
179 ws_type_t<deflateSupported> ws{ts};
180 ws.next_layer().connect(es.stream());
181 try
182 {
183 es.async_handshake();
184 w.accept(ws);
185 ws.auto_fragment(true);
186 std::string const s(16384, '*');
187 w.write(ws, buffer(s));
188 flat_buffer b;
189 w.read(ws, b);
190 BEAST_EXPECT(to_string(b.data()) == s);
191 w.close(ws, {});
192 }
193 catch(...)
194 {
195 ts.close();
196 throw;
197 }
198 ts.close();
199 });
200 }
201
202 template<class Wrap>
203 void
204 doTestWriteDeflate(Wrap const& w)
205 {
206 using boost::asio::buffer;
207
208 permessage_deflate pmd;
209 pmd.client_enable = true;
210 pmd.server_enable = true;
211 pmd.compLevel = 1;
212
213 // deflate
214 doTest(pmd, [&](ws_type& ws)
215 {
216 auto const& s = random_string();
217 ws.binary(true);
218 w.write(ws, buffer(s));
219 flat_buffer b;
220 w.read(ws, b);
221 BEAST_EXPECT(to_string(b.data()) == s);
222 });
223
224 // deflate, continuation
225 doTest(pmd, [&](ws_type& ws)
226 {
227 std::string const s = "Hello";
228 std::size_t const chop = 3;
229 BOOST_ASSERT(chop < s.size());
230 // This call should produce no
231 // output due to compression latency.
232 w.write_some(ws, false,
233 buffer(s.data(), chop));
234 w.write_some(ws, true, buffer(
235 s.data() + chop, s.size() - chop));
236 flat_buffer b;
237 w.read(ws, b);
238 BEAST_EXPECT(to_string(b.data()) == s);
239 });
240
241 // deflate, no context takeover
242 pmd.client_no_context_takeover = true;
243 doTest(pmd, [&](ws_type& ws)
244 {
245 auto const& s = random_string();
246 ws.binary(true);
247 w.write(ws, buffer(s));
248 flat_buffer b;
249 w.read(ws, b);
250 BEAST_EXPECT(to_string(b.data()) == s);
251 });
252 }
253
254 void
255 testWrite()
256 {
257 using boost::asio::buffer;
258
259 doTestWrite<false>(SyncClient{});
260 doTestWrite<true>(SyncClient{});
261 doTestWriteDeflate(SyncClient{});
262
263 yield_to([&](yield_context yield)
264 {
265 doTestWrite<false>(AsyncClient{yield});
266 doTestWrite<true>(AsyncClient{yield});
267 doTestWriteDeflate(AsyncClient{yield});
268 });
269 }
270
271 void
272 testWriteSuspend()
273 {
274 using boost::asio::buffer;
275
276 // suspend on ping
277 doFailLoop([&](test::fail_counter& fc)
278 {
279 echo_server es{log};
280 boost::asio::io_context ioc;
281 stream<test::stream> ws{ioc, fc};
282 ws.next_layer().connect(es.stream());
283 ws.handshake("localhost", "/");
284 std::size_t count = 0;
285 ws.async_ping("",
286 [&](error_code ec)
287 {
288 ++count;
289 if(ec)
290 BOOST_THROW_EXCEPTION(
291 system_error{ec});
292 });
293 BEAST_EXPECT(ws.wr_block_.is_locked());
294 BEAST_EXPECT(count == 0);
295 ws.async_write(sbuf("*"),
296 [&](error_code ec, std::size_t n)
297 {
298 ++count;
299 if(ec)
300 BOOST_THROW_EXCEPTION(
301 system_error{ec});
302 BEAST_EXPECT(n == 1);
303 });
304 BEAST_EXPECT(count == 0);
305 ioc.run();
306 BEAST_EXPECT(count == 2);
307 });
308
309 // suspend on close
310 doFailLoop([&](test::fail_counter& fc)
311 {
312 echo_server es{log};
313 boost::asio::io_context ioc;
314 stream<test::stream> ws{ioc, fc};
315 ws.next_layer().connect(es.stream());
316 ws.handshake("localhost", "/");
317 std::size_t count = 0;
318 ws.async_close({},
319 [&](error_code ec)
320 {
321 ++count;
322 if(ec)
323 BOOST_THROW_EXCEPTION(
324 system_error{ec});
325 });
326 BEAST_EXPECT(ws.wr_block_.is_locked());
327 BEAST_EXPECT(count == 0);
328 ws.async_write(sbuf("*"),
329 [&](error_code ec, std::size_t)
330 {
331 ++count;
332 if(ec != boost::asio::error::operation_aborted)
333 BOOST_THROW_EXCEPTION(
334 system_error{ec});
335 });
336 BEAST_EXPECT(count == 0);
337 ioc.run();
338 BEAST_EXPECT(count == 2);
339 });
340
341 // suspend on read ping + message
342 doFailLoop([&](test::fail_counter& fc)
343 {
344 echo_server es{log};
345 boost::asio::io_context ioc;
346 stream<test::stream> ws{ioc, fc};
347 ws.next_layer().connect(es.stream());
348 ws.handshake("localhost", "/");
349 // add a ping and message to the input
350 ws.next_layer().append(string_view{
351 "\x89\x00" "\x81\x01*", 5});
352 std::size_t count = 0;
353 multi_buffer b;
354 ws.async_read(b,
355 [&](error_code ec, std::size_t)
356 {
357 ++count;
358 if(ec)
359 BOOST_THROW_EXCEPTION(
360 system_error{ec});
361 });
362 while(! ws.wr_block_.is_locked())
363 {
364 ioc.run_one();
365 if(! BEAST_EXPECT(! ioc.stopped()))
366 break;
367 }
368 BEAST_EXPECT(count == 0);
369 ws.async_write(sbuf("*"),
370 [&](error_code ec, std::size_t n)
371 {
372 ++count;
373 if(ec)
374 BOOST_THROW_EXCEPTION(
375 system_error{ec});
376 BEAST_EXPECT(n == 1);
377 });
378 BEAST_EXPECT(count == 0);
379 ioc.run();
380 BEAST_EXPECT(count == 2);
381 });
382
383 // suspend on ping: nomask, nofrag
384 doFailLoop([&](test::fail_counter& fc)
385 {
386 echo_server es{log, kind::async_client};
387 boost::asio::io_context ioc;
388 stream<test::stream> ws{ioc, fc};
389 ws.next_layer().connect(es.stream());
390 es.async_handshake();
391 ws.accept();
392 std::size_t count = 0;
393 std::string const s(16384, '*');
394 ws.auto_fragment(false);
395 ws.async_write(buffer(s),
396 [&](error_code ec, std::size_t n)
397 {
398 ++count;
399 if(ec)
400 BOOST_THROW_EXCEPTION(
401 system_error{ec});
402 BEAST_EXPECT(n == 16384);
403 });
404 BEAST_EXPECT(ws.wr_block_.is_locked());
405 ws.async_ping("",
406 [&](error_code ec)
407 {
408 ++count;
409 if(ec)
410 BOOST_THROW_EXCEPTION(
411 system_error{ec});
412 });
413 ioc.run();
414 BEAST_EXPECT(count == 2);
415 });
416
417 // suspend on ping: nomask, frag
418 doFailLoop([&](test::fail_counter& fc)
419 {
420 echo_server es{log, kind::async_client};
421 boost::asio::io_context ioc;
422 stream<test::stream> ws{ioc, fc};
423 ws.next_layer().connect(es.stream());
424 es.async_handshake();
425 ws.accept();
426 std::size_t count = 0;
427 std::string const s(16384, '*');
428 ws.auto_fragment(true);
429 ws.async_write(buffer(s),
430 [&](error_code ec, std::size_t n)
431 {
432 ++count;
433 if(ec)
434 BOOST_THROW_EXCEPTION(
435 system_error{ec});
436 BEAST_EXPECT(n == 16384);
437 });
438 BEAST_EXPECT(ws.wr_block_.is_locked());
439 ws.async_ping("",
440 [&](error_code ec)
441 {
442 ++count;
443 if(ec)
444 BOOST_THROW_EXCEPTION(
445 system_error{ec});
446 });
447 ioc.run();
448 BEAST_EXPECT(count == 2);
449 });
450
451 // suspend on ping: mask, nofrag
452 doFailLoop([&](test::fail_counter& fc)
453 {
454 echo_server es{log};
455 error_code ec;
456 boost::asio::io_context ioc;
457 stream<test::stream> ws{ioc, fc};
458 ws.next_layer().connect(es.stream());
459 ws.handshake("localhost", "/");
460 std::size_t count = 0;
461 std::string const s(16384, '*');
462 ws.auto_fragment(false);
463 ws.async_write(buffer(s),
464 [&](error_code ec, std::size_t n)
465 {
466 ++count;
467 if(ec)
468 BOOST_THROW_EXCEPTION(
469 system_error{ec});
470 BEAST_EXPECT(n == 16384);
471 });
472 BEAST_EXPECT(ws.wr_block_.is_locked());
473 ws.async_ping("",
474 [&](error_code ec)
475 {
476 ++count;
477 if(ec)
478 BOOST_THROW_EXCEPTION(
479 system_error{ec});
480 });
481 ioc.run();
482 });
483
484 // suspend on ping: mask, frag
485 doFailLoop([&](test::fail_counter& fc)
486 {
487 echo_server es{log};
488 error_code ec;
489 boost::asio::io_context ioc;
490 stream<test::stream> ws{ioc, fc};
491 ws.next_layer().connect(es.stream());
492 ws.handshake("localhost", "/");
493 std::size_t count = 0;
494 std::string const s(16384, '*');
495 ws.auto_fragment(true);
496 ws.async_write(buffer(s),
497 [&](error_code ec, std::size_t n)
498 {
499 ++count;
500 if(ec)
501 BOOST_THROW_EXCEPTION(
502 system_error{ec});
503 BEAST_EXPECT(n == 16384);
504 });
505 BEAST_EXPECT(ws.wr_block_.is_locked());
506 ws.async_ping("",
507 [&](error_code ec)
508 {
509 ++count;
510 if(ec)
511 BOOST_THROW_EXCEPTION(
512 system_error{ec});
513 });
514 ioc.run();
515 });
516
517 // suspend on ping: deflate
518 doFailLoop([&](test::fail_counter& fc)
519 {
520 echo_server es{log, kind::async};
521 boost::asio::io_context ioc;
522 stream<test::stream> ws{ioc, fc};
523 {
524 permessage_deflate pmd;
525 pmd.client_enable = true;
526 pmd.compLevel = 1;
527 ws.set_option(pmd);
528 }
529 ws.next_layer().connect(es.stream());
530 ws.handshake("localhost", "/");
531 std::size_t count = 0;
532 auto const& s = random_string();
533 ws.binary(true);
534 ws.async_write(buffer(s),
535 [&](error_code ec, std::size_t n)
536 {
537 ++count;
538 if(ec)
539 BOOST_THROW_EXCEPTION(
540 system_error{ec});
541 BEAST_EXPECT(n == s.size());
542 });
543 BEAST_EXPECT(ws.wr_block_.is_locked());
544 ws.async_ping("",
545 [&](error_code ec)
546 {
547 ++count;
548 if(ec)
549 BOOST_THROW_EXCEPTION(
550 system_error{ec});
551 });
552 ioc.run();
553 });
554 }
555
556 void
557 testAsyncWriteFrame()
558 {
559 for(int i = 0; i < 2; ++i)
560 {
561 for(;;)
562 {
563 echo_server es{log, i==1 ?
564 kind::async : kind::sync};
565 boost::asio::io_context ioc;
566 stream<test::stream> ws{ioc};
567 ws.next_layer().connect(es.stream());
568
569 error_code ec;
570 ws.handshake("localhost", "/", ec);
571 if(! BEAST_EXPECTS(! ec, ec.message()))
572 break;
573 ws.async_write_some(false,
574 boost::asio::const_buffer{},
575 [&](error_code, std::size_t)
576 {
577 fail();
578 });
579 if(! BEAST_EXPECTS(! ec, ec.message()))
580 break;
581 //
582 // Destruction of the io_context will cause destruction
583 // of the write_some_op without invoking the final handler.
584 //
585 break;
586 }
587 }
588 }
589
590 /*
591 https://github.com/boostorg/beast/issues/300
592
593 Write a message as two individual frames
594 */
595 void
596 testIssue300()
597 {
598 for(int i = 0; i < 2; ++i )
599 {
600 echo_server es{log, i==1 ?
601 kind::async : kind::sync};
602 boost::asio::io_context ioc;
603 stream<test::stream> ws{ioc};
604 ws.next_layer().connect(es.stream());
605
606 error_code ec;
607 ws.handshake("localhost", "/", ec);
608 if(! BEAST_EXPECTS(! ec, ec.message()))
609 return;
610 ws.write_some(false, sbuf("u"));
611 ws.write_some(true, sbuf("v"));
612 multi_buffer b;
613 ws.read(b, ec);
614 BEAST_EXPECTS(! ec, ec.message());
615 }
616 }
617
618 void
619 testContHook()
620 {
621 struct handler
622 {
623 void operator()(error_code) {}
624 };
625
626 char buf[32];
627 stream<test::stream> ws{ioc_};
628 stream<test::stream>::write_some_op<
629 boost::asio::const_buffer,
630 handler> op{handler{}, ws, true,
631 boost::asio::const_buffer{
632 buf, sizeof(buf)}};
633 using boost::asio::asio_handler_is_continuation;
634 asio_handler_is_continuation(&op);
635 }
636
637 void
638 testMoveOnly()
639 {
640 boost::asio::io_context ioc;
641 stream<test::stream> ws{ioc};
642 ws.async_write_some(
643 true, boost::asio::const_buffer{},
644 move_only_handler{});
645 }
646
647 struct copyable_handler
648 {
649 template<class... Args>
650 void
651 operator()(Args&&...) const
652 {
653 }
654 };
655
656 void
657 testAsioHandlerInvoke()
658 {
659 // make sure things compile, also can set a
660 // breakpoint in asio_handler_invoke to make sure
661 // it is instantiated.
662 {
663 boost::asio::io_context ioc;
664 boost::asio::io_service::strand s{ioc};
665 stream<test::stream> ws{ioc};
666 flat_buffer b;
667 ws.async_write(boost::asio::const_buffer{},
668 s.wrap(copyable_handler{}));
669 }
670 }
671
672 void
673 run() override
674 {
675 testWrite();
676 testWriteSuspend();
677 testAsyncWriteFrame();
678 testIssue300();
679 testContHook();
680 testMoveOnly();
681 }
682 };
683
684 BEAST_DEFINE_TESTSUITE(beast,websocket,write);
685
686 } // websocket
687 } // beast
688 } // boost