Description
Starting with Selenium Grid 4.42.0, the Router fails to handle WebSocket upgrade requests for CDP (/session/{id}/se/cdp) when deployed behind a reverse proxy that uses HTTP/2 (e.g., Envoy/Contour). The same setup works correctly with 4.41.0.
Environment
- Selenium Grid version: 4.42.0-20260303 and 4.43.0-20260404 (both affected)
- Last working version: 4.41.0-20260222
- Deployment: Kubernetes (Helm chart), isolated components mode
- Reverse proxy: Contour (Envoy-based) with
enableWebsockets: true
- Client: Playwright 1.60.0 (uses
ws library internally)
- OS: Linux (amd64), AWS EKS
Steps to Reproduce
- Deploy Selenium Grid 4.42.0+ in Kubernetes with isolated components
- Place an HTTP/2-capable reverse proxy (Envoy/Contour/nginx with HTTP/2) in front of the Router
- Create a session via the external URL (HTTPS)
- Attempt a WebSocket connection to
/session/{sessionId}/se/cdp through the proxy
Expected Behavior
WebSocket upgrade succeeds with 101 Switching Protocols, and the CDP connection is established (as it does with 4.41.0).
Actual Behavior
Router returns 500 Internal Server Error with:
org.openqa.selenium.json.JsonException: Expected to read a START_MAP but instead have: END. Last 0 characters read:
at org.openqa.selenium.json.JsonInput.beginObject(JsonInput.java:363)
at org.openqa.selenium.remote.NewSessionPayload.isW3C(NewSessionPayload.java:306)
at org.openqa.selenium.remote.NewSessionPayload.<init>(NewSessionPayload.java:64)
at org.openqa.selenium.grid.data.SessionRequest.<init>(SessionRequest.java:63)
at org.openqa.selenium.grid.sessionqueue.NewSessionQueue.lambda$new$1(NewSessionQueue.java:67)
...
at org.openqa.selenium.grid.router.Router.execute(Router.java:89)
Root Cause Analysis
The stack trace shows the WebSocket upgrade request to /session/{id}/se/cdp is being incorrectly routed to NewSessionQueue (the POST /session handler) instead of the WebSocket handler.
This happens because:
- The client connects via HTTPS to the proxy, which negotiates HTTP/2 (ALPN)
- In HTTP/2, there are no
Connection: Upgrade or Upgrade: websocket headers — WebSocket upgrades use the extended CONNECT method (RFC 8441)
- When Envoy forwards the request to the Router (HTTP/1.1 backend), the WebSocket upgrade headers may not be properly reconstructed
- The Router's new WebSocket routing code (introduced in 4.42 via PR #17146 - transparent TCP tunnel) apparently requires these headers to identify WebSocket requests
- Without the headers, the Router falls through to the session queue handler, which tries to parse an empty request body as JSON →
JsonException
In 4.41.0, the Router matched WebSocket requests by URL path pattern (/session/{id}/se/cdp), which works regardless of headers.
Verification
| Version |
Result |
| 4.41.0-20260222 |
✅ Works — WebSocket CDP connections succeed |
| 4.42.0-20260303 |
❌ Fails — 500 Internal Server Error |
| 4.43.0-20260404 |
❌ Fails — 500 Internal Server Error |
All three versions were tested on the same cluster with the same proxy configuration.
Workaround
Pin Selenium Grid to version 4.41.0.
Suggested Fix
The Router should identify WebSocket requests by URL path pattern (e.g., /session/{id}/se/cdp) in addition to checking for Upgrade: websocket headers. This ensures compatibility with HTTP/2 proxies that may not forward traditional WebSocket upgrade headers.
Description
Starting with Selenium Grid 4.42.0, the Router fails to handle WebSocket upgrade requests for CDP (
/session/{id}/se/cdp) when deployed behind a reverse proxy that uses HTTP/2 (e.g., Envoy/Contour). The same setup works correctly with 4.41.0.Environment
enableWebsockets: truewslibrary internally)Steps to Reproduce
/session/{sessionId}/se/cdpthrough the proxyExpected Behavior
WebSocket upgrade succeeds with
101 Switching Protocols, and the CDP connection is established (as it does with 4.41.0).Actual Behavior
Router returns 500 Internal Server Error with:
Root Cause Analysis
The stack trace shows the WebSocket upgrade request to
/session/{id}/se/cdpis being incorrectly routed toNewSessionQueue(the POST /session handler) instead of the WebSocket handler.This happens because:
Connection: UpgradeorUpgrade: websocketheaders — WebSocket upgrades use the extended CONNECT method (RFC 8441)JsonExceptionIn 4.41.0, the Router matched WebSocket requests by URL path pattern (
/session/{id}/se/cdp), which works regardless of headers.Verification
All three versions were tested on the same cluster with the same proxy configuration.
Workaround
Pin Selenium Grid to version 4.41.0.
Suggested Fix
The Router should identify WebSocket requests by URL path pattern (e.g.,
/session/{id}/se/cdp) in addition to checking forUpgrade: websocketheaders. This ensures compatibility with HTTP/2 proxies that may not forward traditional WebSocket upgrade headers.