From f113bc7dfb651908cdd8b68db1a634c798f88e89 Mon Sep 17 00:00:00 2001 From: "Serhii Niukalov (GitHub)" <36993782+SNiukalov@users.noreply.github.com> Date: Fri, 10 Jan 2020 18:05:19 +0200 Subject: Fix crash in WebsocketSession on start. (#3191) A crash occurs because we use an overload of boost::beast::websocket::stream::write without error_code. After the error occurs we add an error message to the logger and continue working. In SendMessage_UnpreparedConnection_WithoutFall test we use sleep because we can't ability to catch processed event. This issue is that LoopThreadDelegate requires a WebsocketSession to work. WebsocketSession init LoopThreadDelegate with 'this', so we have no way to simulate it without additional installers. In future, to avoid sleep, we can: Create a wrapper class for the socket, then provide the socket layout for the WebsocketSession in unit tests and wrap the following interfaces: async_read write down get_executor async_accept --- .../hmi_message_handler/src/websocket_session.cc | 14 ++++---- .../test/hmi_message_handler_impl_test.cc | 40 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/components/hmi_message_handler/src/websocket_session.cc b/src/components/hmi_message_handler/src/websocket_session.cc index 3d18da19d2..ad9d5273e4 100644 --- a/src/components/hmi_message_handler/src/websocket_session.cc +++ b/src/components/hmi_message_handler/src/websocket_session.cc @@ -304,12 +304,14 @@ void WebsocketSession::LoopThreadDelegate::exitThreadMain() { } void WebsocketSession::LoopThreadDelegate::DrainQueue() { - while (!message_queue_.empty()) { - Message message_ptr; - message_queue_.pop(message_ptr); - if (!shutdown_) { - handler_.ws_.write(boost::asio::buffer(*message_ptr)); - }; + Message message_ptr; + while (!shutdown_ && message_queue_.pop(message_ptr)) { + boost::system::error_code ec; + handler_.ws_.write(boost::asio::buffer(*message_ptr), ec); + if (ec) { + LOG4CXX_ERROR(ws_logger_, + "A system error has occurred: " << ec.message()); + } } } diff --git a/src/components/hmi_message_handler/test/hmi_message_handler_impl_test.cc b/src/components/hmi_message_handler/test/hmi_message_handler_impl_test.cc index eaa274d0f4..f38eb019e2 100644 --- a/src/components/hmi_message_handler/test/hmi_message_handler_impl_test.cc +++ b/src/components/hmi_message_handler/test/hmi_message_handler_impl_test.cc @@ -189,6 +189,46 @@ TEST_F(HMIMessageHandlerImplTest, SendMessageToHMI_Success) { EXPECT_TRUE(waiter.WaitFor(1, 100)); } +TEST(WebsocketSessionTest, SendMessage_UnpreparedConnection_WithoutFall) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + auto send_message = []() { + auto message = + "{\"id\" : 1,\"jsonrpc\" : \"2.0\",\"method\" : " + "\"BasicCommunication.GetSystemInfo\"}"; + + Json::Reader reader; + Json::Value json_value; + + ASSERT_TRUE(reader.parse(message, json_value, false)); + + // Make unprepared connection + boost::asio::io_context ioc{1}; + boost::asio::ip::tcp::acceptor acceptor{ + ioc, {boost::asio::ip::make_address("127.0.0.1"), 8087}}; + boost::asio::ip::tcp::socket socket{ioc}; + + std::unique_ptr session( + new hmi_message_handler::WebsocketSession(std::move(socket), nullptr)); + + // Send message to unprepared connection + session->sendJsonMessage(json_value); + + // Wait for the message to be processed + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Stopping connection thread + session->Shutdown(); + session = nullptr; + + exit(0); + }; + + // Expected exit code 0, if test terminate by other signal(SIGABRT or + // SIGSEGV), we will get failed test + EXPECT_EXIT(send_message(), ::testing::ExitedWithCode(0), ""); +} + } // namespace hmi_message_handler_test } // namespace components } // namespace test -- cgit v1.2.1