Skip to content

Commit a202491

Browse files
authored
Merge pull request #10929 from Icinga/backport-10833-to-support/2.16
[Backport support/2.16] Fix that NewClientHandler() could hang indefinitely, preventing new connection attempts
2 parents ec99295 + ed0c8a2 commit a202491

1 file changed

Lines changed: 49 additions & 65 deletions

File tree

lib/remote/apilistener.cpp

Lines changed: 49 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -767,16 +767,6 @@ void ApiListener::NewClientHandlerInternal(
767767
return;
768768
}
769769

770-
Defer shutdownSslConn ([&sslConn, &yc]() {
771-
// Ignore the error, but do not throw an exception being swallowed at all cost.
772-
// https://github.com/Icinga/icinga2/issues/7351
773-
boost::system::error_code ec;
774-
775-
// Using async_shutdown() instead of AsioTlsStream::GracefulDisconnect() as this whole function
776-
// is already guarded by a timeout based on the connect timeout.
777-
sslConn.async_shutdown(yc[ec]);
778-
});
779-
780770
std::shared_ptr<X509> cert (sslConn.GetPeerCertificate());
781771
bool verify_ok = false;
782772
String identity;
@@ -828,8 +818,46 @@ void ApiListener::NewClientHandlerInternal(
828818

829819
ClientType ctype;
830820

831-
try {
832-
if (role == RoleClient) {
821+
if (role == RoleClient) {
822+
JsonRpc::SendMessage(client, new Dictionary({
823+
{ "jsonrpc", "2.0" },
824+
{ "method", "icinga::Hello" },
825+
{ "params", new Dictionary({
826+
{ "version", (double)l_AppVersionInt },
827+
{ "capabilities", (double)ApiCapabilities::MyCapabilities }
828+
}) }
829+
}), yc);
830+
831+
client->async_flush(yc);
832+
833+
ctype = ClientJsonRpc;
834+
} else {
835+
{
836+
boost::system::error_code ec;
837+
838+
if (client->async_fill(yc[ec]) == 0u) {
839+
if (identity.IsEmpty()) {
840+
Log(LogInformation, "ApiListener")
841+
<< "No data received on new API connection " << conninfo << ": " << ec.message()
842+
<< ". Ensure that the remote endpoints are properly configured in a cluster setup.";
843+
} else {
844+
Log(LogWarning, "ApiListener")
845+
<< "No data received on new API connection " << conninfo << " for identity '" << identity << "': " << ec.message()
846+
<< ". Ensure that the remote endpoints are properly configured in a cluster setup.";
847+
}
848+
849+
return;
850+
}
851+
}
852+
853+
char firstByte = 0;
854+
855+
{
856+
asio::mutable_buffer firstByteBuf (&firstByte, 1);
857+
client->peek(firstByteBuf);
858+
}
859+
860+
if (firstByte >= '0' && firstByte <= '9') {
833861
JsonRpc::SendMessage(client, new Dictionary({
834862
{ "jsonrpc", "2.0" },
835863
{ "method", "icinga::Hello" },
@@ -843,58 +871,15 @@ void ApiListener::NewClientHandlerInternal(
843871

844872
ctype = ClientJsonRpc;
845873
} else {
846-
{
847-
boost::system::error_code ec;
848-
849-
if (client->async_fill(yc[ec]) == 0u) {
850-
if (identity.IsEmpty()) {
851-
Log(LogInformation, "ApiListener")
852-
<< "No data received on new API connection " << conninfo << ": " << ec.message()
853-
<< ". Ensure that the remote endpoints are properly configured in a cluster setup.";
854-
} else {
855-
Log(LogWarning, "ApiListener")
856-
<< "No data received on new API connection " << conninfo << " for identity '" << identity << "': " << ec.message()
857-
<< ". Ensure that the remote endpoints are properly configured in a cluster setup.";
858-
}
859-
860-
return;
861-
}
862-
}
863-
864-
char firstByte = 0;
865-
866-
{
867-
asio::mutable_buffer firstByteBuf (&firstByte, 1);
868-
client->peek(firstByteBuf);
869-
}
870-
871-
if (firstByte >= '0' && firstByte <= '9') {
872-
JsonRpc::SendMessage(client, new Dictionary({
873-
{ "jsonrpc", "2.0" },
874-
{ "method", "icinga::Hello" },
875-
{ "params", new Dictionary({
876-
{ "version", (double)l_AppVersionInt },
877-
{ "capabilities", (double)ApiCapabilities::MyCapabilities }
878-
}) }
879-
}), yc);
880-
881-
client->async_flush(yc);
882-
883-
ctype = ClientJsonRpc;
884-
} else {
885-
ctype = ClientHttp;
886-
}
887-
}
888-
} catch (const boost::system::system_error& systemError) {
889-
if (systemError.code() == boost::asio::error::operation_aborted) {
890-
shutdownSslConn.Cancel();
874+
ctype = ClientHttp;
891875
}
892-
893-
throw;
894876
}
895877

896878
std::shared_lock wgLock(*m_ListenerWaitGroup, std::try_to_lock);
897879
if (!wgLock) {
880+
// Close the connection cleanly, signals to the other endpoint that it wasn't closed due to an error.
881+
client->GracefulDisconnect(*strand, yc);
882+
898883
return;
899884
}
900885

@@ -929,20 +914,19 @@ void ApiListener::NewClientHandlerInternal(
929914
<< "Ignoring anonymous JSON-RPC connection " << conninfo
930915
<< ". Max connections (" << GetMaxAnonymousClients() << ") exceeded.";
931916

932-
aclient = nullptr;
933-
}
917+
// Close the connection cleanly, signals to the other endpoint that it wasn't closed due to an error.
918+
client->GracefulDisconnect(*strand, yc);
934919

935-
if (aclient) {
936-
aclient->Start();
937-
shutdownSslConn.Cancel();
920+
return;
938921
}
922+
923+
aclient->Start();
939924
} else {
940925
Log(LogNotice, "ApiListener", "New HTTP client");
941926

942927
HttpServerConnection::Ptr aclient = new HttpServerConnection(m_WaitGroup, identity, verify_ok, client);
943928
AddHttpClient(aclient);
944929
aclient->Start();
945-
shutdownSslConn.Cancel();
946930
}
947931
}
948932

0 commit comments

Comments
 (0)