Description
Description
Title
WebSocket headers not applied in JdkHttpClient.openSocket β breaks DevTools behind authenticated proxy
Description
Hi Selenium team,
This might be a bug report (though please correct me if there's a better way to approach it!).
π§© Problem Statement
When using DevTools with a Selenium Grid that sits behind an authentication proxy, the connection to DevTools via WebSocket fails because headers from the original request are not applied in the openSocket
method of JdkHttpClient
.
π Our Setup
- We're running Selenium Grid behind an authentication proxy that supports OIDC + AppToAppAuth.
- The grid requires an
Authorization
header for all requests, including WebSocket connections used by DevTools. - Weβve successfully handled normal HTTP requests using:
- A custom
org.openqa.selenium.remote.http.Filter
to inject authentication headers. - A custom
HttpClient.Factory
provided via-Dwebdriver.http.factory
.
- A custom
π§ͺ Behavior Observed
All standard WebDriver HTTP requests work fine with the injected headers, but WebSocket connections from SeleniumCdpConnection
fail.
Upon inspection of the openSocket
method in JdkHttpClient
, we found:
WebSocket.Builder builder = client.newWebSocketBuilder()
.connectTimeout(connectTimeout);
// no headers are applied here!
builder.buildAsync(uri, listener);
While the HttpRequest
passed to openSocket
has the proper headers, those headers are not applied to the Java WebSocket builder via .header(String, String)
.
π οΈ Impact
This breaks the DevTools usage entirely when running behind an authenticated proxy.
π What We're Looking For
- Could the WebSocket headers from the
HttpRequest
be applied to theWebSocket.Builder
beforebuildAsync()
? - Alternatively, is there a recommended workaround to allow injecting headers into WebSocket connections?
Thanks so much for your time and for maintaining Selenium π
Happy to provide more details or run test cases if needed!
Reproducible Code
mport org.openqa.selenium.remote.http.*;
public class AuthenticatedHttpClient implements HttpClient {
private final HttpClient delegate;
public AuthenticatedHttpClient(HttpClient delegate) {
this.delegate = delegate;
}
@Override
public HttpResponse execute(HttpRequest request) {
injectHeaders(request);
return delegate.execute(request);
}
@Override
public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) {
injectHeaders(request);
return delegate.openSocket(request, listener);
}
private void injectHeaders(HttpRequest request) {
if (request.getHeader("Authorization") == null) {
request.addHeader("Authorization", "Bearer " + SeleniumA3AuthenticationConnection.getA3BearerToken());
request.addHeader("X-A3-Other-App", SeleniumA3AuthenticationConnection.SELENIUM_AUTOMATION_APPLICATION_ID);
request.addHeader("X-A3-Context", SeleniumA3AuthenticationConnection.CONTEXT);
}
}
@Override
public void close() {
delegate.close();
}
}
import org.openqa.selenium.remote.http.*;
import org.openqa.selenium.remote.http.HttpClientName;
import org.openqa.selenium.remote.http.jdk.JdkHttpClient;
@HttpClientName("a3-auth-factory")
public class AuthenticatedHttpClientFactory implements HttpClient.Factory {
private final HttpClient.Factory baseFactory;
public AuthenticatedHttpClientFactory() {
this.baseFactory = new JdkHttpClient.Factory();
// this.baseFactory = new CustomClient.Factory();
}
@Override
public HttpClient createClient(ClientConfig config) {
return new AuthenticatedHttpClient(baseFactory.createClient(config));
}
@Override
public void cleanupIdleClients() {
baseFactory.cleanupIdleClients();
}
}
Debugging Logs
>>> Injecting Headers for Selenium Request to: wss://<grid-ul>/MY-AUTH-GRID/session/29806f3bba4e70387b90ceef9a9b9a90/se/cdp
authorization -> Bearer XXX+XXX=:XXX:1745124943319:1
x-a3-context -> XXX
x-a3-other-app -> XXX
Exception in thread "main" org.openqa.selenium.remote.http.ConnectionFailedException: JdkWebSocket initial request execution error
Build info: version: '4.31.0', revision: '1ef9f18787*'
System info: os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '15.4.1', java.version: '21.0.1'
Driver info: driver.version: unknown
at org.openqa.selenium.remote.http.jdk.JdkHttpClient.openSocket(JdkHttpClient.java:250)
at com.org.testapp.selenium.AuthenticatedHttpClient.openSocket(AuthenticatedHttpClient.java:22)
at org.openqa.selenium.devtools.Connection.<init>(Connection.java:89)
at org.openqa.selenium.devtools.SeleniumCdpConnection.<init>(SeleniumCdpConnection.java:36)
at org.openqa.selenium.devtools.SeleniumCdpConnection.lambda$create$2(SeleniumCdpConnection.java:103)
at java.base/java.util.Optional.map(Optional.java:260)
at org.openqa.selenium.devtools.SeleniumCdpConnection.create(SeleniumCdpConnection.java:103)
at org.openqa.selenium.devtools.SeleniumCdpConnection.create(SeleniumCdpConnection.java:49)
at org.openqa.selenium.devtools.DevToolsProvider.getImplementation(DevToolsProvider.java:50)
at org.openqa.selenium.devtools.DevToolsProvider.getImplementation(DevToolsProvider.java:29)
at org.openqa.selenium.remote.Augmenter.augment(Augmenter.java:207)
at org.openqa.selenium.remote.Augmenter.augment(Augmenter.java:178)
at com.org.testapp.Playground.useDevTools(Playground.java:31)
at com.org.testapp.Playground.main(Playground.java:26)
Caused by: java.net.http.WebSocketHandshakeException
at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.resultFrom(OpeningHandshake.java:224)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.postFire(CompletableFuture.java:614)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:844)
at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
Caused by: jdk.internal.net.http.websocket.CheckFailedException: Unexpected HTTP response status code 302
at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.checkFailed(OpeningHandshake.java:341)
at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.handleResponse(OpeningHandshake.java:250)
at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.resultFrom(OpeningHandshake.java:220)
... 10 more
What version of Selenium are you currently using?
4.25.0 and 4.31
The following statements are true
- This applies to the most recent version of Selenium (we can't fix old versions)
- This hasn't already been reported (I searched and didn't find it)
- All information necessary to reproduce the issue has been provided above
Did this work for you before?
Not sure, this is my first time trying it
If yes, what version of Selenium did it work with?
No response
Operating System
--
Selenium Language Binding
Java
Which browsers are you experiencing the issue with?
No response
Are you using Selenium Grid?
Yes