Skip to content

fix(e2e,chromedriverproxy): WebSocket/CDP scheme over a TLS-terminating ingress#286

Merged
rgarcia merged 3 commits into
mainfrom
fix/chromedriver-bidi-wss-ingress
Jun 16, 2026
Merged

fix(e2e,chromedriverproxy): WebSocket/CDP scheme over a TLS-terminating ingress#286
rgarcia merged 3 commits into
mainfrom
fix/chromedriver-bidi-wss-ingress

Conversation

@rgarcia

@rgarcia rgarcia commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

ChromeDriver BiDi and CDP over a TLS-terminating ingress (the hypeman :9224/:9222 ingresses; serve https/wss) were broken because the WebSocket/CDP URLs used the wrong scheme. Docker is plaintext (http/ws) so it never showed there. Validated in kernel-images-private #237: the full e2e suite (incl. all 5 TestBidi* and TestCDPProxyReconnect) now passes against the staging hypeman host over Tailscale.

Fixes

  1. server/e2e/container.goChromeDriverAddr only stripped http:// (returned https://host:9224 with scheme attached); ChromeDriverWSURL hardcoded ws:// (→ malformed ws://https://host:9224/session). Now strip any scheme and pick ws/wss from the ChromeDriver URL's transport.
  2. server/lib/chromedriverproxy/proxy.gorewriteWebSocketURL kept chromedriver's ws:// scheme, so the webSocketUrl from POST /session was unreachable through the TLS ingress. New clientWSScheme(r) returns wss when X-Forwarded-Proto: https (Caddy ingress) or r.TLS != nil; docker keeps ws. + regression test.
  3. server/e2e/e2e_cdp_reconnect_test.gofetchBrowserWebSocketURL hardcoded http:// for the CDP /json/version probe (fails against the TLS :9222 ingress). Derive the scheme from the CDP URL's transport (wss → https).

🤖 Generated with Claude Code


Note

Medium Risk
Touches ChromeDriver session response rewriting and e2e URL construction; incorrect scheme logic would break remote BiDi/CDP but changes are narrowly scoped with new regression tests.

Overview
Fixes BiDi and CDP when traffic goes through the hypeman TLS ingress (https/wss on :9224/:9222). Plain Docker http/ws behavior is unchanged.

ChromeDriver proxy — On POST /session, rewriteWebSocketURL now sets capabilities.webSocketUrl to wss:// when the client reached the proxy via TLS (X-Forwarded-Proto: https / r.TLS), instead of leaving ChromeDriver’s internal ws:// URL. New clientWSScheme handles comma-separated forwarded proto values. Regression test added.

E2E helpersChromeDriverAddr strips any URL scheme (not only http://); ChromeDriverWSURL picks ws vs wss from the ChromeDriver base URL. fetchBrowserWebSocketURL uses https for /json/version when CDPURL() is wss://.

Reviewed by Cursor Bugbot for commit ae8abde. Bugbot is set up for automated code reviews on this repo. Configure here.

…ing ingress

ChromeDriver WebDriver-BiDi over a TLS-terminating ingress (the hypeman
:9224 ingress; serves https/wss) was broken because the BiDi WebSocket
URLs used the wrong scheme. Docker is plaintext (http/ws) so it never
showed there.

Two shared-code fixes:

1. server/e2e/container.go:
   - ChromeDriverAddr only stripped "http://", so for an
     "https://host:9224" ChromeDriver URL it returned
     "https://host:9224" with the scheme still attached.
   - ChromeDriverWSURL hardcoded "ws://", producing the malformed
     "ws://https://host:9224/session".
   Fix: strip any scheme in ChromeDriverAddr; pick ws:// vs wss:// in
   ChromeDriverWSURL based on whether the ChromeDriver URL is https.

2. server/lib/chromedriverproxy/proxy.go:
   rewriteWebSocketURL kept chromedriver's ws:// scheme, so the
   webSocketUrl returned from POST /session was ws://host:9224/... —
   unreachable through the TLS ingress. Fix: a new clientWSScheme(r)
   helper returns wss when the request arrives with
   X-Forwarded-Proto: https (set by the Caddy ingress) or r.TLS != nil,
   and rewriteWebSocketURL applies it. Docker (plaintext, no forwarded
   proto) keeps ws://. Plus a regression test
   TestHandler_PostSession_WSSchemeFromForwardedProto.

Validated in kernel-images-private #237: all 5 TestBidi* (Selenium,
Puppeteer, HTTPSession, Vibium, WebSocket) now pass against the staging
hypeman host over Tailscale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@firetiger-agent

Copy link
Copy Markdown

Created a monitoring plan for this PR.

What this PR does: Fixes WebDriver BiDi connections that were broken when accessed through the Hypeman TLS-terminating ingress — users automating with the BiDi protocol over the secure :9224 endpoint can now establish sessions successfully.

Intended effect:

  • webSocketUrl scheme in POST /session response: baseline was ws:// (wrong, unreachable through TLS ingress); confirmed working if wss:// is returned when X-Forwarded-Proto: https is present — directly verified by the new regression test TestHandler_PostSession_WSSchemeFromForwardedProto in CI.
  • Docker/plaintext path: baseline unchanged (ws://); confirmed working if Docker-backend BiDi tests (TestBidiHTTPSession, TestBidiPuppeteer, TestBidiVibium, TestBidiSelenium) continue to pass in CI.

Risks:

  • Docker-path regression — Docker BiDi tests in CI; alert if any of TestBidiHTTPSession / TestBidiPuppeteer / TestBidiVibium / TestBidiSelenium fails post-merge.
  • X-Forwarded-Proto header absent at ingress — would silently fall back to ws:// (original broken state); no in-VM OTEL signal exists, but recurrence surfaces as BiDi session failures for users on the Hypeman ingress. Confirm Caddy ingress header injection is in place on first post-deploy BiDi session attempt.
  • POST /browsers 500 spike — baseline 0–2/hr; alert if sustained >10/hr for 2+ consecutive hours post image rollout (check opentelemetry/logs/api error count by hour).

Status updates will be posted automatically on this PR as monitoring progresses.

View monitor

fetchBrowserWebSocketURL hardcoded http:// for the CDP /json/version probe, so
it failed against a TLS-terminating CDP ingress (an http:// request hits a TLS
listener and errors); plaintext (docker) works. Derive the HTTP scheme from the
CDP URL's transport (wss -> https, ws -> http), matching the ChromeDriver
accessor scheme handling in this PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rgarcia rgarcia changed the title fix(chromedriverproxy,e2e): BiDi WebSocket scheme over a TLS-terminating ingress fix(e2e,chromedriverproxy): WebSocket/CDP scheme over a TLS-terminating ingress Jun 11, 2026
@rgarcia rgarcia requested a review from tnsardesai June 15, 2026 21:03

@tnsardesai tnsardesai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed — clean, well-scoped fix with a focused regression test, and the docker plaintext path stays untouched via the "" scheme sentinel. lgtm. one thing worth a look:

Questions

  • server/lib/chromedriverproxy/proxy.go:192X-Forwarded-Proto can be comma-separated (https, http) when a request traverses multiple proxies. r.Header.Get returns the raw first header value, so EqualFold(proto, "https") won't match "https, http" and falls through to "ws" — silently reintroducing the bug this PR fixes. worth confirming the Caddy ingress only ever sets a single value; if there's any doubt, splitting on , and taking the first token is cheap insurance.

X-Forwarded-Proto can be a comma-separated list ("https, http") when a request
traverses multiple proxies. clientWSScheme used the raw header value, so it
wouldn't match "https" and fell back to ws:// — silently reintroducing the
TLS-ingress bug this PR fixes. Take the first (client-facing) token before
comparing. Adds a "https, http" case to the regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rgarcia

rgarcia commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @tnsardesai — good catch. Fixed in ae8abde: clientWSScheme now splits X-Forwarded-Proto on , and uses the first (client-facing) token before comparing, so a multi-proxy "https, http" value still yields wss. Added a "https, http" case to TestHandler_PostSession_WSSchemeFromForwardedProto. (Mirrored the same fix into the private hypeman-e2e PR.)

@rgarcia rgarcia merged commit 3cba2a0 into main Jun 16, 2026
10 checks passed
@rgarcia rgarcia deleted the fix/chromedriver-bidi-wss-ingress branch June 16, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants