diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b1fa3d7..1f5b34cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Version 275: * Use automatically deduced return types for all async operations * Support Concepts for completion token params * https_get example sends the Host header +* Fix async_close error code when async_read times out -------------------------------------------------------------------------------- diff --git a/include/boost/beast/websocket/impl/stream_impl.hpp b/include/boost/beast/websocket/impl/stream_impl.hpp index 8940566c..0513dfeb 100644 --- a/include/boost/beast/websocket/impl/stream_impl.hpp +++ b/include/boost/beast/websocket/impl/stream_impl.hpp @@ -210,6 +210,14 @@ struct stream::impl_type timer.cancel(); } + void + time_out() + { + timed_out = true; + change_status(status::closed); + close_socket(get_lowest_layer(stream())); + } + // Called just before sending // the first frame of each message void @@ -516,8 +524,7 @@ private: switch(impl.status_) { case status::handshake: - impl.timed_out = true; - close_socket(get_lowest_layer(impl.stream())); + impl.time_out(); return; case status::open: @@ -537,14 +544,11 @@ private: return; } - // timeout - impl.timed_out = true; - close_socket(get_lowest_layer(impl.stream())); + impl.time_out(); return; case status::closing: - impl.timed_out = true; - close_socket(get_lowest_layer(impl.stream())); + impl.time_out(); return; case status::closed: diff --git a/test/beast/websocket/timer.cpp b/test/beast/websocket/timer.cpp index e2b6e85d..f3507c1d 100644 --- a/test/beast/websocket/timer.cpp +++ b/test/beast/websocket/timer.cpp @@ -155,11 +155,40 @@ struct timer_test : unit_test::suite } } + // https://github.com/boostorg/beast/issues/1729#issuecomment-540481056 + void + testCloseWhileRead() + { + net::io_context ioc; + stream ws1(ioc); + stream ws2(ioc); + test::connect(ws1.next_layer(), ws2.next_layer()); + + ws1.set_option(websocket::stream_base::timeout{ + std::chrono::milliseconds(50), + websocket::stream_base::none(), + false}); + + ws1.async_accept(net::detached); + ws2.async_handshake( + "localhost", "/", net::detached); + ioc.run(); + ioc.restart(); + + flat_buffer b; + ws1.async_read(b, test::fail_handler( + beast::error::timeout)); + ws1.async_close({}, test::fail_handler( + net::error::operation_aborted)); + ioc.run(); + } + void run() override { testIdlePing(); testIssue1729(); + testCloseWhileRead(); } };