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 doTestPing(Wrap const & w)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 testPing()94 testPing() 95 { 96 doTestPing(SyncClient{}); 97 98 yield_to([&](yield_context yield) 99 { 100 doTestPing(AsyncClient{yield}); 101 }); 102 } 103 104 void testSuspend()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 testMoveOnly()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 operator ()boost::beast::websocket::ping_test::copyable_handler485 operator()(Args&&...) const 486 { 487 } 488 }; 489 490 #if BOOST_ASIO_HAS_CO_AWAIT testAwaitableCompiles(stream<test::stream> & s,ping_data & pdat)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 run()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 522