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/stream.hpp> 14 #include <boost/beast/_experimental/test/tcp.hpp> 15 #include "test.hpp" 16 17 #include <boost/asio/io_context.hpp> 18 #include <boost/asio/strand.hpp> 19 #if BOOST_ASIO_HAS_CO_AWAIT 20 #include <boost/asio/use_awaitable.hpp> 21 #endif 22 23 namespace boost { 24 namespace beast { 25 namespace websocket { 26 27 class close_test : public websocket_test_suite 28 { 29 public: 30 template<class Wrap> 31 void doTestClose(Wrap const & w)32 doTestClose(Wrap const& w) 33 { 34 permessage_deflate pmd; 35 pmd.client_enable = false; 36 pmd.server_enable = false; 37 38 // close 39 doTest(pmd, [&](ws_type& ws) 40 { 41 w.close(ws, {}); 42 }); 43 44 // close with code 45 doTest(pmd, [&](ws_type& ws) 46 { 47 w.close(ws, close_code::going_away); 48 }); 49 50 // close with code and reason 51 doTest(pmd, [&](ws_type& ws) 52 { 53 w.close(ws, { 54 close_code::going_away, 55 "going away"}); 56 }); 57 58 // already closed 59 { 60 echo_server es{log}; 61 stream<test::stream> ws{ioc_}; 62 ws.next_layer().connect(es.stream()); 63 w.handshake(ws, "localhost", "/"); 64 w.close(ws, {}); 65 try 66 { 67 w.close(ws, {}); 68 fail("", __FILE__, __LINE__); 69 } 70 catch(system_error const& se) 71 { 72 BEAST_EXPECTS( 73 se.code() == net::error::operation_aborted, 74 se.code().message()); 75 } 76 } 77 78 // drain a message after close 79 doTest(pmd, [&](ws_type& ws) 80 { 81 ws.next_layer().append("\x81\x01\x2a"); 82 w.close(ws, {}); 83 }); 84 85 // drain a big message after close 86 { 87 std::string s; 88 s = "\x81\x7e\x10\x01"; 89 s.append(4097, '*'); 90 doTest(pmd, [&](ws_type& ws) 91 { 92 ws.next_layer().append(s); 93 w.close(ws, {}); 94 }); 95 } 96 97 // drain a ping after close 98 doTest(pmd, [&](ws_type& ws) 99 { 100 ws.next_layer().append("\x89\x01*"); 101 w.close(ws, {}); 102 }); 103 104 // drain invalid message frame after close 105 { 106 echo_server es{log}; 107 stream<test::stream> ws{ioc_}; 108 ws.next_layer().connect(es.stream()); 109 w.handshake(ws, "localhost", "/"); 110 ws.next_layer().append("\x81\x81\xff\xff\xff\xff*"); 111 try 112 { 113 w.close(ws, {}); 114 fail("", __FILE__, __LINE__); 115 } 116 catch(system_error const& se) 117 { 118 BEAST_EXPECTS( 119 se.code() == error::bad_masked_frame, 120 se.code().message()); 121 } 122 } 123 124 // drain invalid close frame after close 125 { 126 echo_server es{log}; 127 stream<test::stream> ws{ioc_}; 128 ws.next_layer().connect(es.stream()); 129 w.handshake(ws, "localhost", "/"); 130 ws.next_layer().append("\x88\x01*"); 131 try 132 { 133 w.close(ws, {}); 134 fail("", __FILE__, __LINE__); 135 } 136 catch(system_error const& se) 137 { 138 BEAST_EXPECTS( 139 se.code() == error::bad_close_size, 140 se.code().message()); 141 } 142 } 143 144 // drain masked close frame 145 { 146 echo_server es{log, kind::async_client}; 147 stream<test::stream> ws{ioc_}; 148 ws.next_layer().connect(es.stream()); 149 ws.set_option(pmd); 150 es.async_handshake(); 151 ws.accept(); 152 w.close(ws, {}); 153 } 154 155 // close with incomplete read message 156 doTest(pmd, [&](ws_type& ws) 157 { 158 ws.next_layer().append("\x81\x02**"); 159 static_buffer<1> b; 160 w.read_some(ws, 1, b); 161 w.close(ws, {}); 162 }); 163 } 164 165 void testClose()166 testClose() 167 { 168 doTestClose(SyncClient{}); 169 170 yield_to([&](yield_context yield) 171 { 172 doTestClose(AsyncClient{yield}); 173 }); 174 } 175 176 void testTimeout()177 testTimeout() 178 { 179 using tcp = net::ip::tcp; 180 181 net::io_context ioc; 182 183 // success 184 185 { 186 stream<tcp::socket> ws1(ioc); 187 stream<tcp::socket> ws2(ioc); 188 test::connect(ws1.next_layer(), ws2.next_layer()); 189 ws1.async_handshake("test", "/", test::success_handler()); 190 ws2.async_accept(test::success_handler()); 191 test::run(ioc); 192 193 ws1.async_close({}, test::success_handler()); 194 ws2.async_close({}, test::success_handler()); 195 test::run(ioc); 196 } 197 198 { 199 stream<test::stream> ws1(ioc); 200 stream<test::stream> ws2(ioc); 201 test::connect(ws1.next_layer(), ws2.next_layer()); 202 ws1.async_handshake("test", "/", test::success_handler()); 203 ws2.async_accept(test::success_handler()); 204 test::run(ioc); 205 206 ws1.async_close({}, test::success_handler()); 207 ws2.async_close({}, test::success_handler()); 208 test::run(ioc); 209 } 210 211 // success, timeout enabled 212 213 { 214 stream<tcp::socket> ws1(ioc); 215 stream<tcp::socket> ws2(ioc); 216 test::connect(ws1.next_layer(), ws2.next_layer()); 217 ws1.async_handshake("test", "/", test::success_handler()); 218 ws2.async_accept(test::success_handler()); 219 test::run(ioc); 220 221 ws1.set_option(stream_base::timeout{ 222 std::chrono::milliseconds(50), 223 stream_base::none(), 224 false}); 225 ws1.async_close({}, test::success_handler()); 226 ws2.async_close({}, test::success_handler()); 227 test::run(ioc); 228 } 229 230 { 231 stream<test::stream> ws1(ioc); 232 stream<test::stream> ws2(ioc); 233 test::connect(ws1.next_layer(), ws2.next_layer()); 234 ws1.async_handshake("test", "/", test::success_handler()); 235 ws2.async_accept(test::success_handler()); 236 test::run(ioc); 237 238 ws1.set_option(stream_base::timeout{ 239 std::chrono::milliseconds(50), 240 stream_base::none(), 241 false}); 242 ws1.async_close({}, test::success_handler()); 243 ws2.async_close({}, test::success_handler()); 244 test::run(ioc); 245 } 246 247 // timeout 248 249 { 250 stream<tcp::socket> ws1(ioc); 251 stream<tcp::socket> ws2(ioc); 252 test::connect(ws1.next_layer(), ws2.next_layer()); 253 ws1.async_handshake("test", "/", test::success_handler()); 254 ws2.async_accept(test::success_handler()); 255 test::run(ioc); 256 257 ws1.set_option(stream_base::timeout{ 258 std::chrono::milliseconds(50), 259 stream_base::none(), 260 false}); 261 ws1.async_close({}, test::fail_handler( 262 beast::error::timeout)); 263 test::run(ioc); 264 } 265 266 { 267 stream<test::stream> ws1(ioc); 268 stream<test::stream> ws2(ioc); 269 test::connect(ws1.next_layer(), ws2.next_layer()); 270 ws1.async_handshake("test", "/", test::success_handler()); 271 ws2.async_accept(test::success_handler()); 272 test::run(ioc); 273 274 ws1.set_option(stream_base::timeout{ 275 std::chrono::milliseconds(50), 276 stream_base::none(), 277 false}); 278 ws1.async_close({}, test::fail_handler( 279 beast::error::timeout)); 280 test::run(ioc); 281 } 282 } 283 284 void testSuspend()285 testSuspend() 286 { 287 // suspend on ping 288 doFailLoop([&](test::fail_count& fc) 289 { 290 echo_server es{log}; 291 net::io_context ioc; 292 stream<test::stream> ws{ioc, fc}; 293 ws.next_layer().connect(es.stream()); 294 ws.handshake("localhost", "/"); 295 std::size_t count = 0; 296 ws.async_ping("", 297 [&](error_code ec) 298 { 299 ++count; 300 if(ec) 301 BOOST_THROW_EXCEPTION( 302 system_error{ec}); 303 }); 304 BEAST_EXPECT(ws.impl_->wr_block.is_locked()); 305 BEAST_EXPECT(count == 0); 306 ws.async_close({}, 307 [&](error_code ec) 308 { 309 ++count; 310 if(ec) 311 BOOST_THROW_EXCEPTION( 312 system_error{ec}); 313 }); 314 BEAST_EXPECT(count == 0); 315 ioc.run(); 316 BEAST_EXPECT(count == 2); 317 }); 318 319 // suspend on write 320 doFailLoop([&](test::fail_count& fc) 321 { 322 echo_server es{log}; 323 net::io_context ioc; 324 stream<test::stream> ws{ioc, fc}; 325 ws.next_layer().connect(es.stream()); 326 ws.handshake("localhost", "/"); 327 std::size_t count = 0; 328 ws.async_write(sbuf("*"), 329 [&](error_code ec, std::size_t n) 330 { 331 ++count; 332 if(ec) 333 BOOST_THROW_EXCEPTION( 334 system_error{ec}); 335 BEAST_EXPECT(n == 1); 336 }); 337 BEAST_EXPECT(ws.impl_->wr_block.is_locked()); 338 BEAST_EXPECT(count == 0); 339 ws.async_close({}, 340 [&](error_code ec) 341 { 342 ++count; 343 if(ec) 344 BOOST_THROW_EXCEPTION( 345 system_error{ec}); 346 }); 347 BEAST_EXPECT(count == 0); 348 ioc.run(); 349 BEAST_EXPECT(count == 2); 350 }); 351 352 // suspend on read ping + message 353 doFailLoop([&](test::fail_count& fc) 354 { 355 echo_server es{log}; 356 multi_buffer b; 357 net::io_context ioc; 358 stream<test::stream> ws{ioc, fc}; 359 ws.next_layer().connect(es.stream()); 360 ws.handshake("localhost", "/"); 361 // add a ping and message to the input 362 ws.next_layer().append(string_view{ 363 "\x89\x00" "\x81\x01*", 5}); 364 std::size_t count = 0; 365 ws.async_read(b, 366 [&](error_code ec, std::size_t) 367 { 368 ++count; 369 if(ec) 370 BOOST_THROW_EXCEPTION( 371 system_error{ec}); 372 }); 373 while(! ws.impl_->wr_block.is_locked()) 374 { 375 ioc.run_one(); 376 if(! BEAST_EXPECT(! ioc.stopped())) 377 break; 378 } 379 BEAST_EXPECT(count == 0); 380 ws.async_close({}, 381 [&](error_code ec) 382 { 383 ++count; 384 if(ec) 385 BOOST_THROW_EXCEPTION( 386 system_error{ec}); 387 }); 388 BEAST_EXPECT(count == 0); 389 ioc.run(); 390 BEAST_EXPECT(count == 2); 391 }); 392 393 // suspend on read bad message 394 doFailLoop([&](test::fail_count& fc) 395 { 396 echo_server es{log}; 397 multi_buffer b; 398 net::io_context ioc; 399 stream<test::stream> ws{ioc, fc}; 400 ws.next_layer().connect(es.stream()); 401 ws.handshake("localhost", "/"); 402 // add an invalid frame to the input 403 ws.next_layer().append(string_view{ 404 "\x09\x00", 2}); 405 std::size_t count = 0; 406 ws.async_read(b, 407 [&](error_code ec, std::size_t) 408 { 409 if(ec != error::bad_control_fragment) 410 BOOST_THROW_EXCEPTION( 411 system_error{ec}); 412 BEAST_EXPECT(++count == 1); 413 }); 414 while(! ws.impl_->wr_block.is_locked()) 415 { 416 ioc.run_one(); 417 if(! BEAST_EXPECT(! ioc.stopped())) 418 break; 419 } 420 BEAST_EXPECT(count == 0); 421 ws.async_close({}, 422 [&](error_code ec) 423 { 424 if(ec != net::error::operation_aborted) 425 BOOST_THROW_EXCEPTION( 426 system_error{ec}); 427 BEAST_EXPECT(++count == 2); 428 }); 429 BEAST_EXPECT(count == 0); 430 ioc.run(); 431 BEAST_EXPECT(count == 2); 432 }); 433 434 // suspend on read close #1 435 doFailLoop([&](test::fail_count& fc) 436 { 437 echo_server es{log}; 438 multi_buffer b; 439 net::io_context ioc; 440 stream<test::stream> ws{ioc, fc}; 441 ws.next_layer().connect(es.stream()); 442 ws.handshake("localhost", "/"); 443 // add a close frame to the input 444 ws.next_layer().append(string_view{ 445 "\x88\x00", 2}); 446 std::size_t count = 0; 447 ws.async_read(b, 448 [&](error_code ec, std::size_t) 449 { 450 if(ec != error::closed) 451 BOOST_THROW_EXCEPTION( 452 system_error{ec}); 453 BEAST_EXPECT(++count == 1); 454 }); 455 while(! ws.impl_->wr_block.is_locked()) 456 { 457 ioc.run_one(); 458 if(! BEAST_EXPECT(! ioc.stopped())) 459 break; 460 } 461 BEAST_EXPECT(count == 0); 462 ws.async_close({}, 463 [&](error_code ec) 464 { 465 if(ec != net::error::operation_aborted) 466 BOOST_THROW_EXCEPTION( 467 system_error{ec}); 468 BEAST_EXPECT(++count == 2); 469 }); 470 BEAST_EXPECT(count == 0); 471 ioc.run(); 472 BEAST_EXPECT(count == 2); 473 }); 474 475 // teardown on received close 476 doFailLoop([&](test::fail_count& fc) 477 { 478 echo_server es{log}; 479 std::string const s = "Hello, world!"; 480 multi_buffer b; 481 net::io_context ioc; 482 stream<test::stream> ws{ioc, fc}; 483 ws.next_layer().connect(es.stream()); 484 ws.handshake("localhost", "/"); 485 // add a close frame to the input 486 ws.next_layer().append(string_view{ 487 "\x88\x00", 2}); 488 std::size_t count = 0; 489 ws.async_write(net::buffer(s), 490 [&](error_code ec, std::size_t n) 491 { 492 if(ec) 493 BOOST_THROW_EXCEPTION( 494 system_error{ec}); 495 BEAST_EXPECT(n == s.size()); 496 BEAST_EXPECT(++count == 1); 497 }); 498 ws.async_read(b, 499 [&](error_code ec, std::size_t) 500 { 501 if(ec != net::error::operation_aborted) 502 BOOST_THROW_EXCEPTION( 503 system_error{ec}); 504 BEAST_EXPECT(++count == 3); 505 }); 506 ws.async_close({}, 507 [&](error_code ec) 508 { 509 if(ec) 510 BOOST_THROW_EXCEPTION( 511 system_error{ec}); 512 BEAST_EXPECT(++count == 2); 513 }); 514 BEAST_EXPECT(count == 0); 515 ioc.run(); 516 BEAST_EXPECT(count == 3); 517 }); 518 519 // check for deadlock 520 doFailLoop([&](test::fail_count& fc) 521 { 522 echo_server es{log}; 523 multi_buffer b; 524 std::string const s = "Hello, world!"; 525 net::io_context ioc; 526 stream<test::stream> ws{ioc, fc}; 527 ws.next_layer().connect(es.stream()); 528 ws.handshake("localhost", "/"); 529 // add a ping frame to the input 530 ws.next_layer().append(string_view{ 531 "\x89\x00", 2}); 532 std::size_t count = 0; 533 ws.async_write(net::buffer(s), 534 [&](error_code ec, std::size_t n) 535 { 536 if(ec) 537 BOOST_THROW_EXCEPTION( 538 system_error{ec}); 539 BEAST_EXPECT(n == s.size()); 540 BEAST_EXPECT(++count == 1); 541 }); 542 ws.async_read(b, 543 [&](error_code ec, std::size_t) 544 { 545 if(ec != net::error::operation_aborted) 546 BOOST_THROW_EXCEPTION( 547 system_error{ec}); 548 BEAST_EXPECT(++count == 3); 549 }); 550 BEAST_EXPECT(ws.impl_->rd_block.is_locked()); 551 ws.async_close({}, 552 [&](error_code ec) 553 { 554 if(ec) 555 BOOST_THROW_EXCEPTION( 556 system_error{ec}); 557 BEAST_EXPECT(++count == 2); 558 }); 559 BEAST_EXPECT(ws.is_open()); 560 BEAST_EXPECT(ws.impl_->wr_block.is_locked()); 561 BEAST_EXPECT(count == 0); 562 ioc.run(); 563 BEAST_EXPECT(count == 3); 564 }); 565 566 // Four-way: close, read, write, ping 567 doFailLoop([&](test::fail_count& fc) 568 { 569 echo_server es{log}; 570 std::string const s = "Hello, world!"; 571 multi_buffer b; 572 net::io_context ioc; 573 stream<test::stream> ws{ioc, fc}; 574 ws.next_layer().connect(es.stream()); 575 ws.handshake("localhost", "/"); 576 std::size_t count = 0; 577 ws.async_close({}, 578 [&](error_code ec) 579 { 580 if(ec) 581 BOOST_THROW_EXCEPTION( 582 system_error{ec}); 583 BEAST_EXPECT(++count == 1); 584 }); 585 ws.async_read(b, 586 [&](error_code ec, std::size_t) 587 { 588 if(ec != net::error::operation_aborted) 589 BOOST_THROW_EXCEPTION( 590 system_error{ec}); 591 ++count; 592 }); 593 ws.async_write(net::buffer(s), 594 [&](error_code ec, std::size_t) 595 { 596 if(ec != net::error::operation_aborted) 597 BOOST_THROW_EXCEPTION( 598 system_error{ec}); 599 ++count; 600 }); 601 ws.async_ping({}, 602 [&](error_code ec) 603 { 604 if(ec != net::error::operation_aborted) 605 BOOST_THROW_EXCEPTION( 606 system_error{ec}); 607 ++count; 608 }); 609 BEAST_EXPECT(count == 0); 610 ioc.run(); 611 BEAST_EXPECT(count == 4); 612 }); 613 614 // Four-way: read, write, ping, close 615 doFailLoop([&](test::fail_count& fc) 616 { 617 echo_server es{log}; 618 multi_buffer b; 619 std::string const s = "Hello, world!"; 620 net::io_context ioc; 621 stream<test::stream> ws{ioc, fc}; 622 ws.next_layer().connect(es.stream()); 623 ws.handshake("localhost", "/"); 624 std::size_t count = 0; 625 ws.async_read(b, 626 [&](error_code ec, std::size_t) 627 { 628 if(ec && ec != net::error::operation_aborted) 629 { 630 BEAST_EXPECTS(ec, ec.message()); 631 BOOST_THROW_EXCEPTION( 632 system_error{ec}); 633 } 634 if(! ec) 635 BEAST_EXPECT(buffers_to_string(b.data()) == s); 636 ++count; 637 if(count == 4) 638 BEAST_EXPECT( 639 ec == net::error::operation_aborted); 640 }); 641 ws.async_write(net::buffer(s), 642 [&](error_code ec, std::size_t n) 643 { 644 if(ec) 645 BOOST_THROW_EXCEPTION( 646 system_error{ec}); 647 BEAST_EXPECT(n == s.size()); 648 ++count; 649 }); 650 ws.async_ping({}, 651 [&](error_code ec) 652 { 653 if(ec != net::error::operation_aborted) 654 { 655 BEAST_EXPECTS(ec, ec.message()); 656 BOOST_THROW_EXCEPTION( 657 system_error{ec}); 658 } 659 ++count; 660 }); 661 ws.async_close({}, 662 [&](error_code ec) 663 { 664 if(ec) 665 BOOST_THROW_EXCEPTION( 666 system_error{ec}); 667 ++count; 668 BEAST_EXPECT(count == 2 || count == 3); 669 }); 670 BEAST_EXPECT(count == 0); 671 ioc.run(); 672 BEAST_EXPECT(count == 4); 673 }); 674 675 // Four-way: ping, read, write, close 676 doFailLoop([&](test::fail_count& fc) 677 { 678 echo_server es{log}; 679 multi_buffer b; 680 std::string const s = "Hello, world!"; 681 net::io_context ioc; 682 stream<test::stream> ws{ioc, fc}; 683 ws.next_layer().connect(es.stream()); 684 ws.handshake("localhost", "/"); 685 std::size_t count = 0; 686 ws.async_ping({}, 687 [&](error_code ec) 688 { 689 if(ec) 690 BOOST_THROW_EXCEPTION( 691 system_error{ec}); 692 BEAST_EXPECT(++count == 1); 693 }); 694 ws.async_read(b, 695 [&](error_code ec, std::size_t) 696 { 697 if(ec != net::error::operation_aborted) 698 BOOST_THROW_EXCEPTION( 699 system_error{ec}); 700 ++count; 701 }); 702 ws.async_write(net::buffer(s), 703 [&](error_code ec, std::size_t) 704 { 705 if(ec != net::error::operation_aborted) 706 BOOST_THROW_EXCEPTION( 707 system_error{ec}); 708 ++count; 709 }); 710 ws.async_close({}, 711 [&](error_code ec) 712 { 713 if(ec) 714 BOOST_THROW_EXCEPTION( 715 system_error{ec}); 716 BEAST_EXPECT(++count == 2); 717 }); 718 BEAST_EXPECT(count == 0); 719 ioc.run(); 720 BEAST_EXPECT(count == 4); 721 }); 722 } 723 724 void testMoveOnly()725 testMoveOnly() 726 { 727 net::io_context ioc; 728 stream<test::stream> ws{ioc}; 729 ws.async_close({}, move_only_handler{}); 730 } 731 732 struct copyable_handler 733 { 734 template<class... Args> 735 void operator ()boost::beast::websocket::close_test::copyable_handler736 operator()(Args&&...) const 737 { 738 } 739 }; 740 741 #if BOOST_ASIO_HAS_CO_AWAIT testAwaitableCompiles(stream<test::stream> & s,close_reason cr)742 void testAwaitableCompiles(stream<test::stream>& s, close_reason cr ) 743 { 744 static_assert(std::is_same_v< 745 net::awaitable<void>, decltype( 746 s.async_close(cr, net::use_awaitable))>); 747 } 748 #endif 749 750 void run()751 run() override 752 { 753 testClose(); 754 testTimeout(); 755 testSuspend(); 756 testMoveOnly(); 757 #if BOOST_ASIO_HAS_CO_AWAIT 758 boost::ignore_unused(&close_test::testAwaitableCompiles); 759 #endif 760 } 761 }; 762 763 BEAST_DEFINE_TESTSUITE(beast,websocket,close); 764 765 } // websocket 766 } // beast 767 } // boost 768