]> git.proxmox.com Git - ceph.git/blob - ceph/src/Beast/test/websocket/stream.cpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / test / websocket / stream.cpp
1 //
2 // Copyright (c) 2013-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
8 // Test that header file is self-contained.
9 #include <beast/websocket/stream.hpp>
10
11 #include "websocket_async_echo_server.hpp"
12 #include "websocket_sync_echo_server.hpp"
13
14 #include <beast/core/streambuf.hpp>
15 #include <beast/core/to_string.hpp>
16 #include <beast/test/fail_stream.hpp>
17 #include <beast/test/string_istream.hpp>
18 #include <beast/test/yield_to.hpp>
19 #include <beast/unit_test/suite.hpp>
20 #include <boost/asio.hpp>
21 #include <boost/asio/spawn.hpp>
22 #include <boost/optional.hpp>
23 #include <mutex>
24 #include <condition_variable>
25
26 namespace beast {
27 namespace websocket {
28
29 class stream_test
30 : public beast::unit_test::suite
31 , public test::enable_yield_to
32 {
33 public:
34 using self = stream_test;
35 using endpoint_type = boost::asio::ip::tcp::endpoint;
36 using address_type = boost::asio::ip::address;
37 using socket_type = boost::asio::ip::tcp::socket;
38
39 struct con
40 {
41 stream<socket_type> ws;
42
43 con(endpoint_type const& ep, boost::asio::io_service& ios)
44 : ws(ios)
45 {
46 ws.next_layer().connect(ep);
47 ws.handshake("localhost", "/");
48 }
49 };
50
51 template<std::size_t N>
52 class cbuf_helper
53 {
54 std::array<std::uint8_t, N> v_;
55 boost::asio::const_buffer cb_;
56
57 public:
58 using value_type = decltype(cb_);
59 using const_iterator = value_type const*;
60
61 template<class... Vn>
62 explicit
63 cbuf_helper(Vn... vn)
64 : v_({{ static_cast<std::uint8_t>(vn)... }})
65 , cb_(v_.data(), v_.size())
66 {
67 }
68
69 const_iterator
70 begin() const
71 {
72 return &cb_;
73 }
74
75 const_iterator
76 end() const
77 {
78 return begin()+1;
79 }
80 };
81
82 template<class... Vn>
83 cbuf_helper<sizeof...(Vn)>
84 cbuf(Vn... vn)
85 {
86 return cbuf_helper<sizeof...(Vn)>(vn...);
87 }
88
89 template<std::size_t N>
90 static
91 boost::asio::const_buffers_1
92 sbuf(const char (&s)[N])
93 {
94 return boost::asio::const_buffers_1(&s[0], N-1);
95 }
96
97 template<class Pred>
98 static
99 bool
100 run_until(boost::asio::io_service& ios,
101 std::size_t limit, Pred&& pred)
102 {
103 for(std::size_t i = 0; i < limit; ++i)
104 {
105 if(pred())
106 return true;
107 ios.run_one();
108 }
109 return false;
110 }
111
112 struct test_decorator
113 {
114 int& what;
115
116 test_decorator(test_decorator const&) = default;
117
118 test_decorator(int& what_)
119 : what(what_)
120 {
121 what = 0;
122 }
123
124 template<class Fields>
125 void
126 operator()(http::header<true, Fields>&) const
127 {
128 what |= 1;
129 }
130
131 template<class Fields>
132 void
133 operator()(http::header<false, Fields>&) const
134 {
135 what |= 2;
136 }
137 };
138
139 void
140 testOptions()
141 {
142 stream<socket_type> ws(ios_);
143 ws.set_option(auto_fragment{true});
144 ws.set_option(keep_alive{false});
145 ws.set_option(write_buffer_size{2048});
146 ws.set_option(message_type{opcode::text});
147 ws.set_option(read_buffer_size{8192});
148 ws.set_option(read_message_max{1 * 1024 * 1024});
149 try
150 {
151 ws.set_option(write_buffer_size{7});
152 fail();
153 }
154 catch(std::exception const&)
155 {
156 pass();
157 }
158 try
159 {
160 message_type{opcode::close};
161 fail();
162 }
163 catch(std::exception const&)
164 {
165 pass();
166 }
167 }
168
169 void testAccept()
170 {
171 {
172 static std::size_t constexpr limit = 100;
173 std::size_t n;
174 for(n = 0; n < limit; ++n)
175 {
176 // valid
177 http::request_header req;
178 req.method = "GET";
179 req.url = "/";
180 req.version = 11;
181 req.fields.insert("Host", "localhost");
182 req.fields.insert("Upgrade", "websocket");
183 req.fields.insert("Connection", "upgrade");
184 req.fields.insert("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==");
185 req.fields.insert("Sec-WebSocket-Version", "13");
186 stream<test::fail_stream<
187 test::string_istream>> ws(n, ios_, "");
188 try
189 {
190 ws.accept(req);
191 break;
192 }
193 catch(system_error const&)
194 {
195 }
196 }
197 BEAST_EXPECT(n < limit);
198 }
199 {
200 // valid
201 stream<test::string_istream> ws(ios_,
202 "GET / HTTP/1.1\r\n"
203 "Host: localhost:80\r\n"
204 "Upgrade: WebSocket\r\n"
205 "Connection: upgrade\r\n"
206 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
207 "Sec-WebSocket-Version: 13\r\n"
208 "\r\n"
209 );
210 try
211 {
212 ws.accept();
213 pass();
214 }
215 catch(system_error const&)
216 {
217 fail();
218 }
219 }
220 {
221 // invalid
222 stream<test::string_istream> ws(ios_,
223 "GET / HTTP/1.0\r\n"
224 "\r\n"
225 );
226 try
227 {
228 ws.accept();
229 fail();
230 }
231 catch(system_error const&)
232 {
233 pass();
234 }
235 }
236 }
237
238 void testBadHandshakes()
239 {
240 auto const check =
241 [&](error_code const& ev, std::string const& s)
242 {
243 for(std::size_t i = 0; i < s.size(); ++i)
244 {
245 stream<test::string_istream> ws(ios_,
246 s.substr(i, s.size() - i));
247 ws.set_option(keep_alive{true});
248 try
249 {
250 ws.accept(boost::asio::buffer(
251 s.substr(0, i), i));
252 BEAST_EXPECTS(! ev, ev.message());
253 }
254 catch(system_error const& se)
255 {
256 BEAST_EXPECTS(se.code() == ev, se.what());
257 }
258 }
259 };
260 // wrong version
261 check(error::handshake_failed,
262 "GET / HTTP/1.0\r\n"
263 "Host: localhost:80\r\n"
264 "Upgrade: WebSocket\r\n"
265 "Connection: keep-alive,upgrade\r\n"
266 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
267 "Sec-WebSocket-Version: 13\r\n"
268 "\r\n"
269 );
270 // wrong method
271 check(error::handshake_failed,
272 "POST / HTTP/1.1\r\n"
273 "Host: localhost:80\r\n"
274 "Upgrade: WebSocket\r\n"
275 "Connection: keep-alive,upgrade\r\n"
276 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
277 "Sec-WebSocket-Version: 13\r\n"
278 "\r\n"
279 );
280 // missing Host
281 check(error::handshake_failed,
282 "GET / HTTP/1.1\r\n"
283 "Upgrade: WebSocket\r\n"
284 "Connection: keep-alive,upgrade\r\n"
285 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
286 "Sec-WebSocket-Version: 13\r\n"
287 "\r\n"
288 );
289 // missing Sec-WebSocket-Key
290 check(error::handshake_failed,
291 "GET / HTTP/1.1\r\n"
292 "Host: localhost:80\r\n"
293 "Upgrade: WebSocket\r\n"
294 "Connection: keep-alive,upgrade\r\n"
295 "Sec-WebSocket-Version: 13\r\n"
296 "\r\n"
297 );
298 // missing Sec-WebSocket-Version
299 check(error::handshake_failed,
300 "GET / HTTP/1.1\r\n"
301 "Host: localhost:80\r\n"
302 "Upgrade: WebSocket\r\n"
303 "Connection: keep-alive,upgrade\r\n"
304 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
305 "\r\n"
306 );
307 // wrong Sec-WebSocket-Version
308 check(error::handshake_failed,
309 "GET / HTTP/1.1\r\n"
310 "Host: localhost:80\r\n"
311 "Upgrade: WebSocket\r\n"
312 "Connection: keep-alive,upgrade\r\n"
313 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
314 "Sec-WebSocket-Version: 1\r\n"
315 "\r\n"
316 );
317 // missing upgrade token
318 check(error::handshake_failed,
319 "GET / HTTP/1.1\r\n"
320 "Host: localhost:80\r\n"
321 "Upgrade: HTTP/2\r\n"
322 "Connection: upgrade\r\n"
323 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
324 "Sec-WebSocket-Version: 13\r\n"
325 "\r\n"
326 );
327 // missing connection token
328 check(error::handshake_failed,
329 "GET / HTTP/1.1\r\n"
330 "Host: localhost:80\r\n"
331 "Upgrade: WebSocket\r\n"
332 "Connection: keep-alive\r\n"
333 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
334 "Sec-WebSocket-Version: 13\r\n"
335 "\r\n"
336 );
337 // valid request
338 check({},
339 "GET / HTTP/1.1\r\n"
340 "Host: localhost:80\r\n"
341 "Upgrade: WebSocket\r\n"
342 "Connection: upgrade\r\n"
343 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
344 "Sec-WebSocket-Version: 13\r\n"
345 "\r\n"
346 );
347 }
348
349 void testBadResponses()
350 {
351 auto const check =
352 [&](std::string const& s)
353 {
354 stream<test::string_istream> ws(ios_, s);
355 try
356 {
357 ws.handshake("localhost:80", "/");
358 fail();
359 }
360 catch(system_error const& se)
361 {
362 BEAST_EXPECT(se.code() == error::response_failed);
363 }
364 };
365 // wrong HTTP version
366 check(
367 "HTTP/1.0 101 Switching Protocols\r\n"
368 "Server: beast\r\n"
369 "Upgrade: WebSocket\r\n"
370 "Connection: upgrade\r\n"
371 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
372 "Sec-WebSocket-Version: 13\r\n"
373 "\r\n"
374 );
375 // wrong status
376 check(
377 "HTTP/1.1 200 OK\r\n"
378 "Server: beast\r\n"
379 "Upgrade: WebSocket\r\n"
380 "Connection: upgrade\r\n"
381 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
382 "Sec-WebSocket-Version: 13\r\n"
383 "\r\n"
384 );
385 // missing upgrade token
386 check(
387 "HTTP/1.1 101 Switching Protocols\r\n"
388 "Server: beast\r\n"
389 "Upgrade: HTTP/2\r\n"
390 "Connection: upgrade\r\n"
391 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
392 "Sec-WebSocket-Version: 13\r\n"
393 "\r\n"
394 );
395 // missing connection token
396 check(
397 "HTTP/1.1 101 Switching Protocols\r\n"
398 "Server: beast\r\n"
399 "Upgrade: WebSocket\r\n"
400 "Connection: keep-alive\r\n"
401 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
402 "Sec-WebSocket-Version: 13\r\n"
403 "\r\n"
404 );
405 // missing accept key
406 check(
407 "HTTP/1.1 101 Switching Protocols\r\n"
408 "Server: beast\r\n"
409 "Upgrade: WebSocket\r\n"
410 "Connection: upgrade\r\n"
411 "Sec-WebSocket-Version: 13\r\n"
412 "\r\n"
413 );
414 // wrong accept key
415 check(
416 "HTTP/1.1 101 Switching Protocols\r\n"
417 "Server: beast\r\n"
418 "Upgrade: WebSocket\r\n"
419 "Connection: upgrade\r\n"
420 "Sec-WebSocket-Accept: *\r\n"
421 "Sec-WebSocket-Version: 13\r\n"
422 "\r\n"
423 );
424 }
425
426 void
427 testDecorator(endpoint_type const& ep)
428 {
429 error_code ec;
430 socket_type sock{ios_};
431 sock.connect(ep, ec);
432 if(! BEAST_EXPECTS(! ec, ec.message()))
433 return;
434 stream<socket_type&> ws{sock};
435 int what;
436 ws.set_option(decorate(test_decorator{what}));
437 BEAST_EXPECT(what == 0);
438 ws.handshake("localhost", "/", ec);
439 if(! BEAST_EXPECTS(! ec, ec.message()))
440 return;
441 BEAST_EXPECT(what == 1);
442 }
443
444 void testMask(endpoint_type const& ep,
445 yield_context do_yield)
446 {
447 {
448 std::vector<char> v;
449 for(char n = 0; n < 20; ++n)
450 {
451 error_code ec;
452 socket_type sock(ios_);
453 sock.connect(ep, ec);
454 if(! BEAST_EXPECTS(! ec, ec.message()))
455 break;
456 stream<socket_type&> ws(sock);
457 ws.handshake("localhost", "/", ec);
458 if(! BEAST_EXPECTS(! ec, ec.message()))
459 break;
460 ws.write(boost::asio::buffer(v), ec);
461 if(! BEAST_EXPECTS(! ec, ec.message()))
462 break;
463 opcode op;
464 streambuf db;
465 ws.read(op, db, ec);
466 if(! BEAST_EXPECTS(! ec, ec.message()))
467 break;
468 BEAST_EXPECT(to_string(db.data()) ==
469 std::string(v.data(), v.size()));
470 v.push_back(n+1);
471 }
472 }
473 {
474 std::vector<char> v;
475 for(char n = 0; n < 20; ++n)
476 {
477 error_code ec;
478 socket_type sock(ios_);
479 sock.connect(ep, ec);
480 if(! BEAST_EXPECTS(! ec, ec.message()))
481 break;
482 stream<socket_type&> ws(sock);
483 ws.handshake("localhost", "/", ec);
484 if(! BEAST_EXPECTS(! ec, ec.message()))
485 break;
486 ws.async_write(boost::asio::buffer(v), do_yield[ec]);
487 if(! BEAST_EXPECTS(! ec, ec.message()))
488 break;
489 opcode op;
490 streambuf db;
491 ws.async_read(op, db, do_yield[ec]);
492 if(! BEAST_EXPECTS(! ec, ec.message()))
493 break;
494 BEAST_EXPECT(to_string(db.data()) ==
495 std::string(v.data(), v.size()));
496 v.push_back(n+1);
497 }
498 }
499 }
500
501 void testClose(endpoint_type const& ep, yield_context)
502 {
503 {
504 // payload length 1
505 con c(ep, ios_);
506 boost::asio::write(c.ws.next_layer(),
507 cbuf(0x88, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00));
508 }
509 {
510 // invalid close code 1005
511 con c(ep, ios_);
512 boost::asio::write(c.ws.next_layer(),
513 cbuf(0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x12));
514 }
515 {
516 // invalid utf8
517 con c(ep, ios_);
518 boost::asio::write(c.ws.next_layer(),
519 cbuf(0x88, 0x86, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x15,
520 0x0f, 0xd7, 0x73, 0x43));
521 }
522 {
523 // good utf8
524 con c(ep, ios_);
525 boost::asio::write(c.ws.next_layer(),
526 cbuf(0x88, 0x86, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x15,
527 'u', 't', 'f', '8'));
528 }
529 }
530
531 #if 0
532 void testInvokable1(endpoint_type const& ep)
533 {
534 boost::asio::io_service ios;
535 stream<socket_type> ws(ios);
536 ws.next_layer().connect(ep);
537 ws.handshake("localhost", "/");
538
539 // Make remote send a ping frame
540 ws.set_option(message_type(opcode::text));
541 ws.write(buffer_cat(sbuf("PING"), sbuf("ping")));
542
543 std::size_t count = 0;
544
545 // Write a text message
546 ++count;
547 ws.async_write(sbuf("Hello"),
548 [&](error_code ec)
549 {
550 --count;
551 });
552
553 // Read
554 opcode op;
555 streambuf db;
556 ++count;
557 ws.async_read(op, db,
558 [&](error_code ec)
559 {
560 --count;
561 });
562 // Run until the read_op writes a close frame.
563 while(! ws.wr_block_)
564 ios.run_one();
565 // Write a text message, leaving
566 // the write_op suspended as invokable.
567 ws.async_write(sbuf("Hello"),
568 [&](error_code ec)
569 {
570 ++count;
571 // Send is canceled because close received.
572 BEAST_EXPECT(ec == boost::asio::
573 error::operation_aborted,
574 ec.message());
575 // Writes after close are aborted.
576 ws.async_write(sbuf("World"),
577 [&](error_code ec)
578 {
579 ++count;
580 BEAST_EXPECT(ec == boost::asio::
581 error::operation_aborted,
582 ec.message());
583 });
584 });
585 // Run until all completions are delivered.
586 static std::size_t constexpr limit = 100;
587 std::size_t n;
588 for(n = 0; n < limit; ++n)
589 {
590 if(count >= 4)
591 break;
592 ios.run_one();
593 }
594 BEAST_EXPECT(n < limit);
595 ios.run();
596 }
597 #endif
598
599 void testInvokable2(endpoint_type const& ep)
600 {
601 boost::asio::io_service ios;
602 stream<socket_type> ws(ios);
603 ws.next_layer().connect(ep);
604 ws.handshake("localhost", "/");
605
606 // Make remote send a text message with bad utf8.
607 ws.set_option(message_type(opcode::binary));
608 ws.write(buffer_cat(sbuf("TEXT"),
609 cbuf(0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc)));
610 opcode op;
611 streambuf db;
612 std::size_t count = 0;
613 // Read text message with bad utf8.
614 // Causes a close to be sent, blocking writes.
615 ws.async_read(op, db,
616 [&](error_code ec)
617 {
618 // Read should fail with protocol error
619 ++count;
620 BEAST_EXPECTS(
621 ec == error::failed, ec.message());
622 // Reads after failure are aborted
623 ws.async_read(op, db,
624 [&](error_code ec)
625 {
626 ++count;
627 BEAST_EXPECTS(ec == boost::asio::
628 error::operation_aborted,
629 ec.message());
630 });
631 });
632 // Run until the read_op writes a close frame.
633 while(! ws.wr_block_)
634 ios.run_one();
635 // Write a text message, leaving
636 // the write_op suspended as invokable.
637 ws.async_write(sbuf("Hello"),
638 [&](error_code ec)
639 {
640 ++count;
641 // Send is canceled because close received.
642 BEAST_EXPECTS(ec == boost::asio::
643 error::operation_aborted,
644 ec.message());
645 // Writes after close are aborted.
646 ws.async_write(sbuf("World"),
647 [&](error_code ec)
648 {
649 ++count;
650 BEAST_EXPECTS(ec == boost::asio::
651 error::operation_aborted,
652 ec.message());
653 });
654 });
655 // Run until all completions are delivered.
656 static std::size_t constexpr limit = 100;
657 std::size_t n;
658 for(n = 0; n < limit; ++n)
659 {
660 if(count >= 4)
661 break;
662 ios.run_one();
663 }
664 BEAST_EXPECT(n < limit);
665 ios.run();
666 }
667
668 void testInvokable3(endpoint_type const& ep)
669 {
670 boost::asio::io_service ios;
671 stream<socket_type> ws(ios);
672 ws.next_layer().connect(ep);
673 ws.handshake("localhost", "/");
674
675 // Cause close to be received
676 ws.set_option(message_type(opcode::binary));
677 ws.write(sbuf("CLOSE"));
678 opcode op;
679 streambuf db;
680 std::size_t count = 0;
681 // Read a close frame.
682 // Sends a close frame, blocking writes.
683 ws.async_read(op, db,
684 [&](error_code ec)
685 {
686 // Read should complete with error::closed
687 ++count;
688 BEAST_EXPECTS(ec == error::closed,
689 ec.message());
690 // Pings after a close are aborted
691 ws.async_ping("",
692 [&](error_code ec)
693 {
694 ++count;
695 BEAST_EXPECTS(ec == boost::asio::
696 error::operation_aborted,
697 ec.message());
698 });
699 });
700 if(! BEAST_EXPECT(run_until(ios, 100,
701 [&]{ return ws.wr_close_; })))
702 return;
703 // Try to ping
704 ws.async_ping("payload",
705 [&](error_code ec)
706 {
707 // Pings after a close are aborted
708 ++count;
709 BEAST_EXPECTS(ec == boost::asio::
710 error::operation_aborted,
711 ec.message());
712 // Subsequent calls to close are aborted
713 ws.async_close({},
714 [&](error_code ec)
715 {
716 ++count;
717 BEAST_EXPECTS(ec == boost::asio::
718 error::operation_aborted,
719 ec.message());
720 });
721 });
722 static std::size_t constexpr limit = 100;
723 std::size_t n;
724 for(n = 0; n < limit; ++n)
725 {
726 if(count >= 4)
727 break;
728 ios.run_one();
729 }
730 BEAST_EXPECT(n < limit);
731 ios.run();
732 }
733
734 void testInvokable4(endpoint_type const& ep)
735 {
736 boost::asio::io_service ios;
737 stream<socket_type> ws(ios);
738 ws.next_layer().connect(ep);
739 ws.handshake("localhost", "/");
740
741 // Cause close to be received
742 ws.set_option(message_type(opcode::binary));
743 ws.write(sbuf("CLOSE"));
744 opcode op;
745 streambuf db;
746 std::size_t count = 0;
747 ws.async_read(op, db,
748 [&](error_code ec)
749 {
750 ++count;
751 BEAST_EXPECTS(ec == error::closed,
752 ec.message());
753 });
754 while(! ws.wr_block_)
755 ios.run_one();
756 // try to close
757 ws.async_close("payload",
758 [&](error_code ec)
759 {
760 ++count;
761 BEAST_EXPECTS(ec == boost::asio::
762 error::operation_aborted,
763 ec.message());
764 });
765 static std::size_t constexpr limit = 100;
766 std::size_t n;
767 for(n = 0; n < limit; ++n)
768 {
769 if(count >= 2)
770 break;
771 ios.run_one();
772 }
773 BEAST_EXPECT(n < limit);
774 ios.run();
775 }
776
777 #if 0
778 void testInvokable5(endpoint_type const& ep)
779 {
780 boost::asio::io_service ios;
781 stream<socket_type> ws(ios);
782 ws.next_layer().connect(ep);
783 ws.handshake("localhost", "/");
784
785 ws.async_write(sbuf("CLOSE"),
786 [&](error_code ec)
787 {
788 BEAST_EXPECT(! ec);
789 ws.async_write(sbuf("PING"),
790 [&](error_code ec)
791 {
792 BEAST_EXPECT(! ec);
793 });
794 });
795 opcode op;
796 streambuf db;
797 ws.async_read(op, db,
798 [&](error_code ec)
799 {
800 BEAST_EXPECTS(ec == error::closed, ec.message());
801 });
802 if(! BEAST_EXPECT(run_until(ios, 100,
803 [&]{ return ios.stopped(); })))
804 return;
805 }
806 #endif
807
808 /*
809 https://github.com/vinniefalco/Beast/issues/300
810
811 Write a message as two individual frames
812 */
813 void
814 testWriteFrames(endpoint_type const& ep)
815 {
816 error_code ec;
817 socket_type sock{ios_};
818 sock.connect(ep, ec);
819 if(! BEAST_EXPECTS(! ec, ec.message()))
820 return;
821 stream<socket_type&> ws{sock};
822 ws.handshake("localhost", "/", ec);
823 if(! BEAST_EXPECTS(! ec, ec.message()))
824 return;
825 ws.write_frame(false, sbuf("u"));
826 ws.write_frame(true, sbuf("v"));
827 streambuf sb;
828 opcode op;
829 ws.read(op, sb, ec);
830 if(! BEAST_EXPECTS(! ec, ec.message()))
831 return;
832 }
833
834 void
835 testAsyncWriteFrame(endpoint_type const& ep)
836 {
837 for(;;)
838 {
839 boost::asio::io_service ios;
840 error_code ec;
841 socket_type sock(ios);
842 sock.connect(ep, ec);
843 if(! BEAST_EXPECTS(! ec, ec.message()))
844 break;
845 stream<socket_type&> ws(sock);
846 ws.handshake("localhost", "/", ec);
847 if(! BEAST_EXPECTS(! ec, ec.message()))
848 break;
849 ws.async_write_frame(false,
850 boost::asio::null_buffers{},
851 [&](error_code)
852 {
853 fail();
854 });
855 ws.next_layer().cancel(ec);
856 if(! BEAST_EXPECTS(! ec, ec.message()))
857 break;
858 //
859 // Destruction of the io_service will cause destruction
860 // of the write_frame_op without invoking the final handler.
861 //
862 break;
863 }
864 }
865
866 struct SyncClient
867 {
868 template<class NextLayer>
869 void
870 handshake(stream<NextLayer>& ws,
871 boost::string_ref const& uri,
872 boost::string_ref const& path) const
873 {
874 ws.handshake(uri, path);
875 }
876
877 template<class NextLayer>
878 void
879 ping(stream<NextLayer>& ws,
880 ping_data const& payload) const
881 {
882 ws.ping(payload);
883 }
884
885 template<class NextLayer>
886 void
887 pong(stream<NextLayer>& ws,
888 ping_data const& payload) const
889 {
890 ws.pong(payload);
891 }
892
893 template<class NextLayer>
894 void
895 close(stream<NextLayer>& ws,
896 close_reason const& cr) const
897 {
898 ws.close(cr);
899 }
900
901 template<
902 class NextLayer, class DynamicBuffer>
903 void
904 read(stream<NextLayer>& ws,
905 opcode& op, DynamicBuffer& dynabuf) const
906 {
907 ws.read(op, dynabuf);
908 }
909
910 template<
911 class NextLayer, class ConstBufferSequence>
912 void
913 write(stream<NextLayer>& ws,
914 ConstBufferSequence const& buffers) const
915 {
916 ws.write(buffers);
917 }
918
919 template<
920 class NextLayer, class ConstBufferSequence>
921 void
922 write_frame(stream<NextLayer>& ws, bool fin,
923 ConstBufferSequence const& buffers) const
924 {
925 ws.write_frame(fin, buffers);
926 }
927
928 template<
929 class NextLayer, class ConstBufferSequence>
930 void
931 write_raw(stream<NextLayer>& ws,
932 ConstBufferSequence const& buffers) const
933 {
934 boost::asio::write(
935 ws.next_layer(), buffers);
936 }
937 };
938
939 class AsyncClient
940 {
941 yield_context& yield_;
942
943 public:
944 explicit
945 AsyncClient(yield_context& yield)
946 : yield_(yield)
947 {
948 }
949
950 template<class NextLayer>
951 void
952 handshake(stream<NextLayer>& ws,
953 boost::string_ref const& uri,
954 boost::string_ref const& path) const
955 {
956 error_code ec;
957 ws.async_handshake(uri, path, yield_[ec]);
958 if(ec)
959 throw system_error{ec};
960 }
961
962 template<class NextLayer>
963 void
964 ping(stream<NextLayer>& ws,
965 ping_data const& payload) const
966 {
967 error_code ec;
968 ws.async_ping(payload, yield_[ec]);
969 if(ec)
970 throw system_error{ec};
971 }
972
973 template<class NextLayer>
974 void
975 pong(stream<NextLayer>& ws,
976 ping_data const& payload) const
977 {
978 error_code ec;
979 ws.async_pong(payload, yield_[ec]);
980 if(ec)
981 throw system_error{ec};
982 }
983
984 template<class NextLayer>
985 void
986 close(stream<NextLayer>& ws,
987 close_reason const& cr) const
988 {
989 error_code ec;
990 ws.async_close(cr, yield_[ec]);
991 if(ec)
992 throw system_error{ec};
993 }
994
995 template<
996 class NextLayer, class DynamicBuffer>
997 void
998 read(stream<NextLayer>& ws,
999 opcode& op, DynamicBuffer& dynabuf) const
1000 {
1001 error_code ec;
1002 ws.async_read(op, dynabuf, yield_[ec]);
1003 if(ec)
1004 throw system_error{ec};
1005 }
1006
1007 template<
1008 class NextLayer, class ConstBufferSequence>
1009 void
1010 write(stream<NextLayer>& ws,
1011 ConstBufferSequence const& buffers) const
1012 {
1013 error_code ec;
1014 ws.async_write(buffers, yield_[ec]);
1015 if(ec)
1016 throw system_error{ec};
1017 }
1018
1019 template<
1020 class NextLayer, class ConstBufferSequence>
1021 void
1022 write_frame(stream<NextLayer>& ws, bool fin,
1023 ConstBufferSequence const& buffers) const
1024 {
1025 error_code ec;
1026 ws.async_write_frame(fin, buffers, yield_[ec]);
1027 if(ec)
1028 throw system_error{ec};
1029 }
1030
1031 template<
1032 class NextLayer, class ConstBufferSequence>
1033 void
1034 write_raw(stream<NextLayer>& ws,
1035 ConstBufferSequence const& buffers) const
1036 {
1037 error_code ec;
1038 boost::asio::async_write(
1039 ws.next_layer(), buffers, yield_[ec]);
1040 if(ec)
1041 throw system_error{ec};
1042 }
1043 };
1044
1045 struct abort_test
1046 {
1047 };
1048
1049 template<class Client>
1050 void
1051 testEndpoint(Client const& c,
1052 endpoint_type const& ep, permessage_deflate const& pmd)
1053 {
1054 using boost::asio::buffer;
1055 static std::size_t constexpr limit = 200;
1056 std::size_t n;
1057 for(n = 0; n <= limit; ++n)
1058 {
1059 stream<test::fail_stream<socket_type>> ws{n, ios_};
1060 ws.set_option(pmd);
1061 auto const restart =
1062 [&](error_code ev)
1063 {
1064 try
1065 {
1066 opcode op;
1067 streambuf db;
1068 c.read(ws, op, db);
1069 fail();
1070 throw abort_test{};
1071 }
1072 catch(system_error const& se)
1073 {
1074 if(se.code() != ev)
1075 throw;
1076 }
1077 error_code ec;
1078 ws.lowest_layer().connect(ep, ec);
1079 if(! BEAST_EXPECTS(! ec, ec.message()))
1080 throw abort_test{};
1081 c.handshake(ws, "localhost", "/");
1082 };
1083 try
1084 {
1085 {
1086 // connect
1087 error_code ec;
1088 ws.lowest_layer().connect(ep, ec);
1089 if(! BEAST_EXPECTS(! ec, ec.message()))
1090 return;
1091 }
1092 c.handshake(ws, "localhost", "/");
1093
1094 // send message
1095 ws.set_option(auto_fragment{false});
1096 ws.set_option(message_type(opcode::text));
1097 c.write(ws, sbuf("Hello"));
1098 {
1099 // receive echoed message
1100 opcode op;
1101 streambuf db;
1102 c.read(ws, op, db);
1103 BEAST_EXPECT(op == opcode::text);
1104 BEAST_EXPECT(to_string(db.data()) == "Hello");
1105 }
1106
1107 // close, no payload
1108 c.close(ws, {});
1109 restart(error::closed);
1110
1111 // close with code
1112 c.close(ws, close_code::going_away);
1113 restart(error::closed);
1114
1115 // close with code and reason string
1116 c.close(ws, {close_code::going_away, "Going away"});
1117 restart(error::closed);
1118
1119 // send ping and message
1120 bool pong = false;
1121 ws.set_option(ping_callback{
1122 [&](bool is_pong, ping_data const& payload)
1123 {
1124 BEAST_EXPECT(is_pong);
1125 BEAST_EXPECT(! pong);
1126 pong = true;
1127 BEAST_EXPECT(payload == "");
1128 }});
1129 c.ping(ws, "");
1130 ws.set_option(message_type(opcode::binary));
1131 c.write(ws, sbuf("Hello"));
1132 {
1133 // receive echoed message
1134 opcode op;
1135 streambuf db;
1136 c.read(ws, op, db);
1137 BEAST_EXPECT(pong == 1);
1138 BEAST_EXPECT(op == opcode::binary);
1139 BEAST_EXPECT(to_string(db.data()) == "Hello");
1140 }
1141 ws.set_option(ping_callback{});
1142
1143 // send ping and fragmented message
1144 ws.set_option(ping_callback{
1145 [&](bool is_pong, ping_data const& payload)
1146 {
1147 BEAST_EXPECT(is_pong);
1148 BEAST_EXPECT(payload == "payload");
1149 }});
1150 ws.ping("payload");
1151 c.write_frame(ws, false, sbuf("Hello, "));
1152 c.write_frame(ws, false, sbuf(""));
1153 c.write_frame(ws, true, sbuf("World!"));
1154 {
1155 // receive echoed message
1156 opcode op;
1157 streambuf db;
1158 c.read(ws, op, db);
1159 BEAST_EXPECT(pong == 1);
1160 BEAST_EXPECT(to_string(db.data()) == "Hello, World!");
1161 }
1162 ws.set_option(ping_callback{});
1163
1164 // send pong
1165 c.pong(ws, "");
1166
1167 // send auto fragmented message
1168 ws.set_option(auto_fragment{true});
1169 ws.set_option(write_buffer_size{8});
1170 c.write(ws, sbuf("Now is the time for all good men"));
1171 {
1172 // receive echoed message
1173 opcode op;
1174 streambuf sb;
1175 c.read(ws, op, sb);
1176 BEAST_EXPECT(to_string(sb.data()) == "Now is the time for all good men");
1177 }
1178 ws.set_option(auto_fragment{false});
1179 ws.set_option(write_buffer_size{4096});
1180
1181 // send message with write buffer limit
1182 {
1183 std::string s(2000, '*');
1184 ws.set_option(write_buffer_size(1200));
1185 c.write(ws, buffer(s.data(), s.size()));
1186 {
1187 // receive echoed message
1188 opcode op;
1189 streambuf db;
1190 c.read(ws, op, db);
1191 BEAST_EXPECT(to_string(db.data()) == s);
1192 }
1193 }
1194
1195 // cause ping
1196 ws.set_option(message_type(opcode::binary));
1197 c.write(ws, sbuf("PING"));
1198 ws.set_option(message_type(opcode::text));
1199 c.write(ws, sbuf("Hello"));
1200 {
1201 // receive echoed message
1202 opcode op;
1203 streambuf db;
1204 c.read(ws, op, db);
1205 BEAST_EXPECT(op == opcode::text);
1206 BEAST_EXPECT(to_string(db.data()) == "Hello");
1207 }
1208
1209 // cause close
1210 ws.set_option(message_type(opcode::binary));
1211 c.write(ws, sbuf("CLOSE"));
1212 restart(error::closed);
1213
1214 // send bad utf8
1215 ws.set_option(message_type(opcode::binary));
1216 c.write(ws, buffer_cat(sbuf("TEXT"),
1217 cbuf(0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc)));
1218 restart(error::failed);
1219
1220 // cause bad utf8
1221 ws.set_option(message_type(opcode::binary));
1222 c.write(ws, buffer_cat(sbuf("TEXT"),
1223 cbuf(0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc)));
1224 c.write(ws, sbuf("Hello"));
1225 restart(error::failed);
1226
1227 // cause bad close
1228 ws.set_option(message_type(opcode::binary));
1229 c.write(ws, buffer_cat(sbuf("RAW"),
1230 cbuf(0x88, 0x02, 0x03, 0xed)));
1231 restart(error::failed);
1232
1233 // unexpected cont
1234 c.write_raw(ws,
1235 cbuf(0x80, 0x80, 0xff, 0xff, 0xff, 0xff));
1236 restart(error::closed);
1237
1238 // invalid fixed frame header
1239 c.write_raw(ws,
1240 cbuf(0x8f, 0x80, 0xff, 0xff, 0xff, 0xff));
1241 restart(error::closed);
1242
1243 // cause non-canonical extended size
1244 c.write(ws, buffer_cat(sbuf("RAW"),
1245 cbuf(0x82, 0x7e, 0x00, 0x01, 0x00)));
1246 restart(error::failed);
1247
1248 if(! pmd.client_enable)
1249 {
1250 // expected cont
1251 c.write_frame(ws, false, boost::asio::null_buffers{});
1252 c.write_raw(ws,
1253 cbuf(0x81, 0x80, 0xff, 0xff, 0xff, 0xff));
1254 restart(error::closed);
1255
1256 // message size above 2^64
1257 c.write_frame(ws, false, cbuf(0x00));
1258 c.write_raw(ws,
1259 cbuf(0x80, 0xff, 0xff, 0xff, 0xff, 0xff,
1260 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
1261 restart(error::closed);
1262
1263 // message size exceeds max
1264 ws.set_option(read_message_max{1});
1265 c.write(ws, cbuf(0x00, 0x00));
1266 restart(error::failed);
1267 ws.set_option(read_message_max{16*1024*1024});
1268 }
1269 }
1270 catch(system_error const&)
1271 {
1272 continue;
1273 }
1274 break;
1275 }
1276 BEAST_EXPECT(n < limit);
1277 }
1278
1279 void run() override
1280 {
1281 static_assert(std::is_constructible<
1282 stream<socket_type>, boost::asio::io_service&>::value, "");
1283
1284 static_assert(std::is_move_constructible<
1285 stream<socket_type>>::value, "");
1286
1287 static_assert(std::is_move_assignable<
1288 stream<socket_type>>::value, "");
1289
1290 static_assert(std::is_constructible<
1291 stream<socket_type&>, socket_type&>::value, "");
1292
1293 static_assert(std::is_move_constructible<
1294 stream<socket_type&>>::value, "");
1295
1296 static_assert(! std::is_move_assignable<
1297 stream<socket_type&>>::value, "");
1298
1299 log << "sizeof(websocket::stream) == " <<
1300 sizeof(websocket::stream<boost::asio::ip::tcp::socket&>) << std::endl;
1301
1302 auto const any = endpoint_type{
1303 address_type::from_string("127.0.0.1"), 0};
1304
1305 testOptions();
1306 testAccept();
1307 testBadHandshakes();
1308 testBadResponses();
1309
1310 permessage_deflate pmd;
1311 pmd.client_enable = false;
1312 pmd.server_enable = false;
1313
1314 {
1315 error_code ec;
1316 ::websocket::sync_echo_server server{nullptr};
1317 server.set_option(pmd);
1318 server.open(any, ec);
1319 BEAST_EXPECTS(! ec, ec.message());
1320 auto const ep = server.local_endpoint();
1321 testDecorator(ep);
1322 //testInvokable1(ep);
1323 testInvokable2(ep);
1324 testInvokable3(ep);
1325 testInvokable4(ep);
1326 //testInvokable5(ep);
1327 testWriteFrames(ep);
1328 testAsyncWriteFrame(ep);
1329 }
1330
1331 {
1332 error_code ec;
1333 ::websocket::async_echo_server server{nullptr, 4};
1334 server.open(any, ec);
1335 BEAST_EXPECTS(! ec, ec.message());
1336 auto const ep = server.local_endpoint();
1337 testAsyncWriteFrame(ep);
1338 }
1339
1340 auto const doClientTests =
1341 [this, any](permessage_deflate const& pmd)
1342 {
1343 {
1344 error_code ec;
1345 ::websocket::sync_echo_server server{nullptr};
1346 server.set_option(pmd);
1347 server.open(any, ec);
1348 BEAST_EXPECTS(! ec, ec.message());
1349 auto const ep = server.local_endpoint();
1350 testEndpoint(SyncClient{}, ep, pmd);
1351 yield_to(
1352 [&](yield_context yield)
1353 {
1354 testEndpoint(
1355 AsyncClient{yield}, ep, pmd);
1356 });
1357 }
1358 {
1359 error_code ec;
1360 ::websocket::async_echo_server server{nullptr, 4};
1361 server.set_option(pmd);
1362 server.open(any, ec);
1363 BEAST_EXPECTS(! ec, ec.message());
1364 auto const ep = server.local_endpoint();
1365 testEndpoint(SyncClient{}, ep, pmd);
1366 yield_to(
1367 [&](yield_context yield)
1368 {
1369 testEndpoint(
1370 AsyncClient{yield}, ep, pmd);
1371 });
1372 }
1373 };
1374
1375 pmd.client_enable = false;
1376 pmd.server_enable = false;
1377 doClientTests(pmd);
1378
1379 pmd.client_enable = true;
1380 pmd.server_enable = true;
1381 pmd.client_max_window_bits = 10;
1382 pmd.client_no_context_takeover = false;
1383 doClientTests(pmd);
1384
1385 pmd.client_enable = true;
1386 pmd.server_enable = true;
1387 pmd.client_max_window_bits = 10;
1388 pmd.client_no_context_takeover = true;
1389 doClientTests(pmd);
1390 }
1391 };
1392
1393 BEAST_DEFINE_TESTSUITE(stream,websocket,beast);
1394
1395 } // websocket
1396 } // beast