From 4c154f93dc03f1aacd4d533a2f1945acc3fe9fef Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 21 Mar 2023 10:57:40 +0100 Subject: [PATCH] ApiListener#NewClientHandlerInternal(): on basic_socket#cancel() (due to timeout) don't ssl::stream#async_shutdown() If a connection hangs for too long in ApiListener#NewClientHandler(), ApiListener#AddConnection()'s Timeout calls boost::asio::basic_socket#cancel() on that connection to trigger an exception which unwinds ApiListener#NewClientHandler(). Previously that unwind could trigger a Defer which called boost::asio::ssl::stream#async_shutdown() which extended the hang. --- lib/remote/apilistener.cpp | 90 +++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index cb122aa21..5017cd02c 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -734,46 +734,8 @@ void ApiListener::NewClientHandlerInternal( ClientType ctype; - if (role == RoleClient) { - JsonRpc::SendMessage(client, new Dictionary({ - { "jsonrpc", "2.0" }, - { "method", "icinga::Hello" }, - { "params", new Dictionary({ - { "version", (double)l_AppVersionInt }, - { "capabilities", (double)l_MyCapabilities } - }) } - }), yc); - - client->async_flush(yc); - - ctype = ClientJsonRpc; - } else { - { - boost::system::error_code ec; - - if (client->async_fill(yc[ec]) == 0u) { - if (identity.IsEmpty()) { - Log(LogInformation, "ApiListener") - << "No data received on new API connection " << conninfo << ". " - << "Ensure that the remote endpoints are properly configured in a cluster setup."; - } else { - Log(LogWarning, "ApiListener") - << "No data received on new API connection " << conninfo << " for identity '" << identity << "'. " - << "Ensure that the remote endpoints are properly configured in a cluster setup."; - } - - return; - } - } - - char firstByte = 0; - - { - asio::mutable_buffer firstByteBuf (&firstByte, 1); - client->peek(firstByteBuf); - } - - if (firstByte >= '0' && firstByte <= '9') { + try { + if (role == RoleClient) { JsonRpc::SendMessage(client, new Dictionary({ { "jsonrpc", "2.0" }, { "method", "icinga::Hello" }, @@ -787,8 +749,54 @@ void ApiListener::NewClientHandlerInternal( ctype = ClientJsonRpc; } else { - ctype = ClientHttp; + { + boost::system::error_code ec; + + if (client->async_fill(yc[ec]) == 0u) { + if (identity.IsEmpty()) { + Log(LogInformation, "ApiListener") + << "No data received on new API connection " << conninfo << ". " + << "Ensure that the remote endpoints are properly configured in a cluster setup."; + } else { + Log(LogWarning, "ApiListener") + << "No data received on new API connection " << conninfo << " for identity '" << identity << "'. " + << "Ensure that the remote endpoints are properly configured in a cluster setup."; + } + + return; + } + } + + char firstByte = 0; + + { + asio::mutable_buffer firstByteBuf (&firstByte, 1); + client->peek(firstByteBuf); + } + + if (firstByte >= '0' && firstByte <= '9') { + JsonRpc::SendMessage(client, new Dictionary({ + { "jsonrpc", "2.0" }, + { "method", "icinga::Hello" }, + { "params", new Dictionary({ + { "version", (double)l_AppVersionInt }, + { "capabilities", (double)l_MyCapabilities } + }) } + }), yc); + + client->async_flush(yc); + + ctype = ClientJsonRpc; + } else { + ctype = ClientHttp; + } } + } catch (const boost::system::system_error& systemError) { + if (systemError.code() == boost::asio::error::operation_aborted) { + shutDownIfNeeded.Cancel(); + } + + throw; } if (ctype == ClientJsonRpc) {