[java] Do not leak the HttpClient on startup failure
diff --git a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java index f3ba3e7..5c2ab0e 100644 --- a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java
@@ -23,6 +23,7 @@ import static org.openqa.selenium.remote.DriverCommand.QUIT; import static org.openqa.selenium.remote.HttpSessionId.getSessionId; +import java.io.Closeable; import java.io.IOException; import java.net.URL; import java.util.Collections; @@ -39,7 +40,7 @@ import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.http.HttpResponse; -public class HttpCommandExecutor implements CommandExecutor { +public class HttpCommandExecutor implements CommandExecutor, Closeable { private final URL remoteServer; protected final Map<String, CommandInfo> additionalCommands; @@ -241,4 +242,9 @@ public Response execute(Command command) throws IOException { throw e; } } + + @Override + public void close() { + client.close(); + } }
diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index 20bd09a..e5c6cee 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java
@@ -98,7 +98,6 @@ import org.openqa.selenium.remote.http.Contents; import org.openqa.selenium.remote.http.HttpClient; import org.openqa.selenium.remote.http.jdk.ConnectionException; -import org.openqa.selenium.remote.service.DriverCommandExecutor; import org.openqa.selenium.remote.tracing.TracedHttpClient; import org.openqa.selenium.remote.tracing.opentelemetry.OpenTelemetryTracer; import org.openqa.selenium.virtualauthenticator.Credential; @@ -320,15 +319,12 @@ protected void startSession(Capabilities capabilities) { sessionId = new SessionId(response.getSessionId()); this.biDi = createBiDi(); } catch (Exception e) { - // If session creation fails, stop the driver service to prevent zombie processes - if (executor instanceof DriverCommandExecutor) { - try { - ((DriverCommandExecutor) executor).close(); - } catch (Exception ignored) { - // Ignore cleanup exceptions, we'll propagate the original failure - } + // If session creation fails, stop the driver service to prevent zombie processes or zombie + // http clients + try (var hce = + executor instanceof HttpCommandExecutor ? (HttpCommandExecutor) executor : null) { + throw e; } - throw e; } } @@ -556,7 +552,8 @@ public void quit() { return; } - try { + try (HttpCommandExecutor httpCommandExecutor = + (executor instanceof HttpCommandExecutor) ? (HttpCommandExecutor) executor : null) { if (this instanceof HasDevTools) { ((HasDevTools) this).maybeGetDevTools().ifPresent(DevTools::close); }
diff --git a/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java b/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java index 177e89d..131a3c6 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/service/DriverCommandExecutor.java
@@ -219,6 +219,14 @@ Response invokeExecute(Command command) throws IOException { @Override public void close() { - shutdownGracefully(NAME, executorService); + try (var closeSuper = + new Closeable() { + @Override + public void close() { + DriverCommandExecutor.super.close(); + } + }) { + shutdownGracefully(NAME, executorService); + } } }