From 59cd161ab4c4cf48823d95f2ad10104ebafb89ab Mon Sep 17 00:00:00 2001 From: Sharang Gupta Date: Wed, 28 Oct 2026 22:21:22 +0000 Subject: [PATCH] Close the socket when the PG server refuses SSL With sslMode PREFER or REQUIRE against a server that answers 'N' to the SSL request, the connection attempt failed but its socket stayed open, and the server kept a backend waiting for a startup message until its authentication timeout. PREFER then opened a second socket, so every such connection left one dangling. Signed-off-by: Sharang Gupta Assisted-by: Anthropic Claude (claude-opus-5-5) --- .../pgclient/impl/InitiateSslHandler.java | 3 ++ .../java/io/vertx/tests/pgclient/TLSTest.java | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/InitiateSslHandler.java b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/InitiateSslHandler.java index 402c3d2d86..4d08162d74 100644 --- a/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/InitiateSslHandler.java +++ b/vertx-pg-client/src/main/java/io/vertx/pgclient/impl/InitiateSslHandler.java @@ -75,10 +75,13 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception } case PgProtocolConstants.MESSAGE_TYPE_SSL_NO: { upgradePromise.fail(new Exception("Postgres Server does not handle SSL connection")); + // The server waits for a startup message on this socket: close it, PREFER reconnects without SSL + ctx.close(); break; } default: upgradePromise.fail(new Exception("Invalid SSL connection message")); + ctx.close(); break; } } diff --git a/vertx-pg-client/src/test/java/io/vertx/tests/pgclient/TLSTest.java b/vertx-pg-client/src/test/java/io/vertx/tests/pgclient/TLSTest.java index 9c1178979e..59be2d2b3f 100644 --- a/vertx-pg-client/src/test/java/io/vertx/tests/pgclient/TLSTest.java +++ b/vertx-pg-client/src/test/java/io/vertx/tests/pgclient/TLSTest.java @@ -24,9 +24,12 @@ import io.vertx.pgclient.*; import io.vertx.sqlclient.Tuple; import io.vertx.tests.pgclient.junit.ContainerPgRule; +import io.vertx.tests.sqlclient.ProxyServer; import org.junit.*; import org.junit.runner.RunWith; +import java.util.concurrent.atomic.AtomicInteger; + @RunWith(VertxUnitRunner.class) public class TLSTest { @@ -150,6 +153,32 @@ public void testSslModePreferFallback(TestContext ctx) { })); } + @Test + public void testSslModeRequireClosesRefusedSocket(TestContext ctx) { + PgConnectOptions options = ruleSllOff.options() + .setSslMode(SslMode.REQUIRE) + .setSslOptions(new ClientSSLOptions().setTrustAll(true)); + AtomicInteger openSockets = new AtomicInteger(); + ProxyServer proxy = ProxyServer.create(vertx, options.getPort(), options.getHost()); + proxy.proxyHandler(conn -> { + openSockets.incrementAndGet(); + conn.clientCloseHandler(v -> openSockets.decrementAndGet()); + conn.connect(); + }); + Async listen = ctx.async(); + proxy.listen(8080, "localhost", ctx.asyncAssertSuccess(v -> listen.complete())); + listen.awaitSuccess(10000); + options.setPort(8080).setHost("localhost"); + Async async = ctx.async(); + PgConnection.connect(vertx, options).onComplete(ctx.asyncAssertFailure(err -> { + // The server refused SSL: the client must not keep the socket it opened to ask for it + vertx.setTimer(2000, id -> { + ctx.assertEquals(0, openSockets.get()); + async.complete(); + }); + })); + } + @Test public void testSslModeVerifyCaConf(TestContext ctx) { PgConnectOptions options = ruleOptionalSll.options()