Merge pull request #10929 from Icinga/backport-10833-to-support/2.16
Some checks failed
Linux / alpine:bash (push) Has been cancelled
Linux / amazonlinux:2023 (push) Has been cancelled
Linux / debian:11 (linux/386) (push) Has been cancelled
Linux / debian:11 (push) Has been cancelled
Linux / debian:12 (linux/386) (push) Has been cancelled
Linux / debian:12 (push) Has been cancelled
Linux / debian:13 (push) Has been cancelled
Linux / fedora:41 (push) Has been cancelled
Linux / fedora:42 (push) Has been cancelled
Linux / fedora:43 (push) Has been cancelled
Linux / fedora:44 (push) Has been cancelled
Linux / fedora:45 (push) Has been cancelled
Linux / opensuse/leap:15.6 (push) Has been cancelled
Linux / opensuse/leap:16.0 (push) Has been cancelled
Linux / registry.suse.com/bci/bci-base:16.0 (push) Has been cancelled
Linux / registry.suse.com/suse/sle15:15.6 (push) Has been cancelled
Linux / registry.suse.com/suse/sle15:15.7 (push) Has been cancelled
Linux / rockylinux/rockylinux:10 (push) Has been cancelled
Linux / rockylinux:8 (push) Has been cancelled
Linux / rockylinux:9 (push) Has been cancelled
Linux / ubuntu:22.04 (push) Has been cancelled
Linux / ubuntu:24.04 (push) Has been cancelled
Linux / ubuntu:25.04 (push) Has been cancelled
Linux / ubuntu:25.10 (push) Has been cancelled
Linux / ubuntu:26.04 (push) Has been cancelled
Windows / Windows (push) Has been cancelled

[Backport support/2.16] Fix that NewClientHandler() could hang indefinitely, preventing new connection attempts
This commit is contained in:
Julian Brost 2026-07-10 13:26:39 +02:00 committed by GitHub
commit a202491672
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -767,16 +767,6 @@ void ApiListener::NewClientHandlerInternal(
return;
}
Defer shutdownSslConn ([&sslConn, &yc]() {
// Ignore the error, but do not throw an exception being swallowed at all cost.
// https://github.com/Icinga/icinga2/issues/7351
boost::system::error_code ec;
// Using async_shutdown() instead of AsioTlsStream::GracefulDisconnect() as this whole function
// is already guarded by a timeout based on the connect timeout.
sslConn.async_shutdown(yc[ec]);
});
std::shared_ptr<X509> cert (sslConn.GetPeerCertificate());
bool verify_ok = false;
String identity;
@ -828,8 +818,46 @@ void ApiListener::NewClientHandlerInternal(
ClientType ctype;
try {
if (role == RoleClient) {
if (role == RoleClient) {
JsonRpc::SendMessage(client, new Dictionary({
{ "jsonrpc", "2.0" },
{ "method", "icinga::Hello" },
{ "params", new Dictionary({
{ "version", (double)l_AppVersionInt },
{ "capabilities", (double)ApiCapabilities::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 << ": " << ec.message()
<< ". 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 << "': " << ec.message()
<< ". 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" },
@ -843,58 +871,15 @@ void ApiListener::NewClientHandlerInternal(
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 << ": " << ec.message()
<< ". 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 << "': " << ec.message()
<< ". 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)ApiCapabilities::MyCapabilities }
}) }
}), yc);
client->async_flush(yc);
ctype = ClientJsonRpc;
} else {
ctype = ClientHttp;
}
ctype = ClientHttp;
}
} catch (const boost::system::system_error& systemError) {
if (systemError.code() == boost::asio::error::operation_aborted) {
shutdownSslConn.Cancel();
}
throw;
}
std::shared_lock wgLock(*m_ListenerWaitGroup, std::try_to_lock);
if (!wgLock) {
// Close the connection cleanly, signals to the other endpoint that it wasn't closed due to an error.
client->GracefulDisconnect(*strand, yc);
return;
}
@ -929,20 +914,19 @@ void ApiListener::NewClientHandlerInternal(
<< "Ignoring anonymous JSON-RPC connection " << conninfo
<< ". Max connections (" << GetMaxAnonymousClients() << ") exceeded.";
aclient = nullptr;
// Close the connection cleanly, signals to the other endpoint that it wasn't closed due to an error.
client->GracefulDisconnect(*strand, yc);
return;
}
if (aclient) {
aclient->Start();
shutdownSslConn.Cancel();
}
aclient->Start();
} else {
Log(LogNotice, "ApiListener", "New HTTP client");
HttpServerConnection::Ptr aclient = new HttpServerConnection(m_WaitGroup, identity, verify_ok, client);
AddHttpClient(aclient);
aclient->Start();
shutdownSslConn.Cancel();
}
}