diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7aed43a778..4334e931b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -196,13 +196,13 @@ jobs: - toolset: clang install: clang-16 compiler: clang++-16 - cxxstd: "11,14,17,20,2b" + cxxstd: "11,14,17,20" # no 2b: https://github.com/llvm/llvm-project/issues/97842 os: ubuntu-24.04 supported: true - toolset: clang install: clang-17 compiler: clang++-17 - cxxstd: "11,14,17,20,2b" + cxxstd: "11,14,17,20" # no 2b: https://github.com/llvm/llvm-project/issues/97842 os: ubuntu-24.04 supported: true - toolset: clang diff --git a/include/boost/beast/websocket/impl/read.hpp b/include/boost/beast/websocket/impl/read.hpp index 037ad609c4..eae7846663 100644 --- a/include/boost/beast/websocket/impl/read.hpp +++ b/include/boost/beast/websocket/impl/read.hpp @@ -227,6 +227,8 @@ class stream::read_some_op // Handle ping frame if(impl.rd_fh.op == detail::opcode::ping) { + impl.update_timer(this->get_executor()); + if(impl.ctrl_cb) { if(! cont) diff --git a/include/boost/beast/websocket/impl/stream_impl.hpp b/include/boost/beast/websocket/impl/stream_impl.hpp index 5f3c5c5855..5113be912d 100644 --- a/include/boost/beast/websocket/impl/stream_impl.hpp +++ b/include/boost/beast/websocket/impl/stream_impl.hpp @@ -439,8 +439,12 @@ struct stream::impl_type if(timeout_opt.idle_timeout != none()) { idle_counter = 0; - timer.expires_after( - timeout_opt.idle_timeout / 2); + if(timeout_opt.keep_alive_pings) + timer.expires_after( + timeout_opt.idle_timeout / 2); + else + timer.expires_after( + timeout_opt.idle_timeout); BOOST_ASIO_HANDLER_LOCATION(( __FILE__, __LINE__, @@ -565,9 +569,9 @@ struct stream::impl_type if(impl.timeout_opt.idle_timeout == none()) return; - if( impl.idle_counter < 1 ) + if( impl.timeout_opt.keep_alive_pings && + impl.idle_counter < 1) { - if( impl.timeout_opt.keep_alive_pings ) { BOOST_ASIO_HANDLER_LOCATION(( __FILE__, __LINE__, diff --git a/test/beast/websocket/ping.cpp b/test/beast/websocket/ping.cpp index 113fdb9900..32d5f9cf2f 100644 --- a/test/beast/websocket/ping.cpp +++ b/test/beast/websocket/ping.cpp @@ -88,23 +88,36 @@ class ping_test : public websocket_test_suite se.code().message()); } } + } - // inactivity timeout doesn't happen when you get pings + void + testPing() + { + doTestPing(SyncClient{}); + + yield_to([&](yield_context yield) { - echo_server es{log}; - stream ws{ioc_}; + doTestPing(AsyncClient{yield}); + }); - // We have an inactivity timeout of 2s, but don't send pings - ws.set_option(stream_base::timeout{ + // inactivity timeout doesn't happen when you get pings + { + net::io_context ioc; + stream ws1(ioc); + stream ws2(ioc); + test::connect(ws1.next_layer(), ws2.next_layer()); + ws1.set_option(stream_base::timeout{ stream_base::none(), - std::chrono::milliseconds(2000), + std::chrono::milliseconds(150), false }); - ws.next_layer().connect(es.stream()); - ws.handshake("localhost", "/"); - flat_buffer b; + ws2.async_accept(test::success_handler()); + ws1.async_handshake("localhost", "/", test::success_handler()); + ioc.run(); + ioc.restart(); bool got_timeout = false; - ws.async_read(b, + flat_buffer b1; + ws1.async_read(b1, [&](error_code ec, std::size_t) { if(ec != beast::error::timeout) @@ -112,68 +125,51 @@ class ping_test : public websocket_test_suite system_error{ec}); got_timeout = true; }); - // We are connected, base state - BEAST_EXPECT(ws.impl_->idle_counter == 0); - - test::run_for(ioc_, std::chrono::milliseconds(1250)); - // After 1.25s idle, no timeout but idle counter is 1 - BEAST_EXPECT(ws.impl_->idle_counter == 1); - - es.async_ping(); - test::run_for(ioc_, std::chrono::milliseconds(500)); - // The server sent a ping at 1.25s mark, and we're now at 1.75s mark. - // We haven't hit the idle timer yet (happens at 1s, 2s, 3s) - BEAST_EXPECT(ws.impl_->idle_counter == 0); - BEAST_EXPECT(!got_timeout); - - test::run_for(ioc_, std::chrono::milliseconds(750)); - // At 2.5s total; should have triggered the idle timer - BEAST_EXPECT(ws.impl_->idle_counter == 1); + ioc.run_for(std::chrono::milliseconds(100)); + ioc.restart(); + ws2.async_ping("", test::success_handler()); + ioc.run_for(std::chrono::milliseconds(100)); + ioc.restart(); BEAST_EXPECT(!got_timeout); - - test::run_for(ioc_, std::chrono::milliseconds(750)); - // At 3s total; should have triggered the idle timer again without - // activity and triggered timeout. + ioc.run_for(std::chrono::milliseconds(100)); + ioc.restart(); BEAST_EXPECT(got_timeout); } // inactivity timeout doesn't happen when you send pings { - echo_server es{log}; - stream ws{ioc_}; - ws.set_option(stream_base::timeout{ + net::io_context ioc; + stream ws1(ioc); + stream ws2(ioc); + test::connect(ws1.next_layer(), ws2.next_layer()); + ws1.set_option(stream_base::timeout{ stream_base::none(), - std::chrono::milliseconds(600), + std::chrono::milliseconds(250), true }); unsigned n_pongs = 0; - ws.control_callback({[&](frame_type kind, string_view) + ws1.control_callback({[&](frame_type kind, string_view) { if (kind == frame_type::pong) ++n_pongs; }}); - ws.next_layer().connect(es.stream()); - ws.handshake("localhost", "/"); - flat_buffer b; - ws.async_read(b, test::fail_handler(asio::error::operation_aborted)); - // We are connected, base state - test::run_for(ioc_, std::chrono::seconds(1)); - // About a second later, we should have close to 5 pings/pongs, and no timeout - BEAST_EXPECTS(2 <= n_pongs && n_pongs <= 3, "Unexpected nr of pings: " + std::to_string(n_pongs)); + ws2.async_accept(test::success_handler()); + ws1.async_handshake("localhost", "/", test::success_handler()); + ioc.run(); + ioc.restart(); + flat_buffer b1, b2; + ws1.async_read(b1, test::fail_handler(asio::error::operation_aborted)); + ws2.async_read(b2, test::fail_handler(error::closed)); + ioc.run_for(std::chrono::milliseconds(300)); + ioc.restart(); + ws1.async_close({}, test::success_handler()); + ioc.run(); + ioc.restart(); + // We should have close to 2 pings/pongs, and no timeout + BEAST_EXPECTS(1 <= n_pongs && n_pongs <= 3, "Unexpected nr of pings: " + std::to_string(n_pongs)); } } - void - testPing() - { - doTestPing(SyncClient{}); - - yield_to([&](yield_context yield) - { - doTestPing(AsyncClient{yield}); - }); - } - void testSuspend() { diff --git a/test/beast/websocket/test.hpp b/test/beast/websocket/test.hpp index 3ea25ec23a..1575db32bb 100644 --- a/test/beast/websocket/test.hpp +++ b/test/beast/websocket/test.hpp @@ -134,12 +134,6 @@ class websocket_test_suite this)); } - void - async_ping() - { - ws_.async_ping("", [](error_code){}); - } - void async_close() {