Skip to content

fix(browser): resolve HTTP CDP URLs#113

Open
MagMueller wants to merge 1 commit into
mainfrom
resolve-cdp-url
Open

fix(browser): resolve HTTP CDP URLs#113
MagMueller wants to merge 1 commit into
mainfrom
resolve-cdp-url

Conversation

@MagMueller

@MagMueller MagMueller commented Jul 23, 2026

Copy link
Copy Markdown

Summary

  • treat BU_CDP_URL=http://... as a DevTools HTTP endpoint and resolve its WebSocket URL through /json/version
  • retry discovery within the caller’s existing connection timeout so a browser that is still starting can become ready
  • surface HTTP 403 as an immediate remote-debugging permission error
  • preserve BU_CDP_URL=ws://... compatibility and keep BU_CDP_WS as the highest-priority direct WebSocket setting

Previously an HTTP BU_CDP_URL was passed directly to new WebSocket, which attempted an upgrade on the HTTP root instead of querying /json/version. This matches the public browser-harness endpoint contract.

Validation

  • bun test in packages/bcode-browser: 16 pass, 8 environment-gated skips
  • deterministic tests cover HTTP discovery, cold-start retries, permission denial, WebSocket compatibility, and env precedence
  • package typecheck and root filtered typecheck: pass
  • bunx oxlint packages/bcode-browser/test/connect-env.test.ts: clean

Summary by cubic

Fixes connection to Chrome when BU_CDP_URL is an HTTP DevTools endpoint by resolving its WebSocket URL via /json/version, preventing failed upgrades on the HTTP root. Adds retry until the caller’s timeout and clearer 403 permission errors, while keeping BU_CDP_WS priority and BU_CDP_URL WebSocket compatibility.

  • Bug Fixes
    • Resolve HTTP BU_CDP_URL through /json/version to obtain webSocketDebuggerUrl.
    • Retry discovery within the provided timeout to handle cold-starting browsers.
    • Surface HTTP 403 as an immediate remote-debugging permission error.
    • Preserve precedence: BU_CDP_WS over BU_CDP_URL; accept WebSocket values in BU_CDP_URL.

Written for commit 2946376. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/bcode-browser/src/cdp/session.ts">

<violation number="1" location="packages/bcode-browser/src/cdp/session.ts:95">
P2: An HTTP `BU_CDP_URL` connection can exceed the caller's `timeoutMs`: discovery consumes up to that timeout, then the WebSocket phase starts a fresh full timeout. A browser that becomes discoverable just before the deadline but whose WebSocket is unavailable can therefore take roughly twice the requested timeout to fail. Carry one absolute deadline through discovery and `openWs`, or pass only the remaining time to the second phase.</violation>

<violation number="2" location="packages/bcode-browser/src/cdp/session.ts:96">
P2: A valid HTTP `BU_CDP_URL` with an uppercase scheme bypasses `/json/version` discovery because the prefix check is case-sensitive, so the endpoint is treated as a direct WebSocket URL and the connection fails against the HTTP root. Case-insensitive scheme detection (or parsing with `new URL(...).protocol`) would keep equivalent HTTP URL spellings on the discovery path.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment on lines +96 to +98
const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
: envCdpUrl;

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A valid HTTP BU_CDP_URL with an uppercase scheme bypasses /json/version discovery because the prefix check is case-sensitive, so the endpoint is treated as a direct WebSocket URL and the connection fails against the HTTP root. Case-insensitive scheme detection (or parsing with new URL(...).protocol) would keep equivalent HTTP URL spellings on the discovery path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/bcode-browser/src/cdp/session.ts, line 96:

<comment>A valid HTTP `BU_CDP_URL` with an uppercase scheme bypasses `/json/version` discovery because the prefix check is case-sensitive, so the endpoint is treated as a direct WebSocket URL and the connection fails against the HTTP root. Case-insensitive scheme detection (or parsing with `new URL(...).protocol`) would keep equivalent HTTP URL spellings on the discovery path.</comment>

<file context>
@@ -85,11 +86,19 @@ export class Session implements Transport {
     }
+    const envCdpUrl = process.env.BU_CDP_URL;
+    if (envCdpUrl) {
+      const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
+        ? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
+        : envCdpUrl;
</file context>
Suggested change
const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
: envCdpUrl;
const wsUrl = /^https?:\/\//i.test(envCdpUrl)
? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
: envCdpUrl;
Fix with cubic

Comment on lines +95 to +101
if (envCdpUrl) {
const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
: envCdpUrl;
await this.openWs(wsUrl, timeoutMs);
return;
}

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: An HTTP BU_CDP_URL connection can exceed the caller's timeoutMs: discovery consumes up to that timeout, then the WebSocket phase starts a fresh full timeout. A browser that becomes discoverable just before the deadline but whose WebSocket is unavailable can therefore take roughly twice the requested timeout to fail. Carry one absolute deadline through discovery and openWs, or pass only the remaining time to the second phase.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/bcode-browser/src/cdp/session.ts, line 95:

<comment>An HTTP `BU_CDP_URL` connection can exceed the caller's `timeoutMs`: discovery consumes up to that timeout, then the WebSocket phase starts a fresh full timeout. A browser that becomes discoverable just before the deadline but whose WebSocket is unavailable can therefore take roughly twice the requested timeout to fail. Carry one absolute deadline through discovery and `openWs`, or pass only the remaining time to the second phase.</comment>

<file context>
@@ -85,11 +86,19 @@ export class Session implements Transport {
       return;
     }
+    const envCdpUrl = process.env.BU_CDP_URL;
+    if (envCdpUrl) {
+      const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
+        ? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
</file context>
Suggested change
if (envCdpUrl) {
const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
? await resolveHttpCdpUrl(envCdpUrl, timeoutMs)
: envCdpUrl;
await this.openWs(wsUrl, timeoutMs);
return;
}
if (envCdpUrl) {
const deadline = Date.now() + timeoutMs;
const wsUrl = envCdpUrl.startsWith('http://') || envCdpUrl.startsWith('https://')
? await resolveHttpCdpUrl(envCdpUrl, Math.max(1, deadline - Date.now()))
: envCdpUrl;
await this.openWs(wsUrl, Math.max(1, deadline - Date.now()));
return;
}
Fix with cubic

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.

1 participant