refactor(browser): split server context and unify CDP transport

This commit is contained in:
Peter Steinberger
2026-03-02 15:41:58 +00:00
parent 729ddfd7c8
commit 663c1858b8
11 changed files with 643 additions and 707 deletions

View File

@@ -1,7 +1,7 @@
import WebSocket from "ws";
import { isLoopbackHost } from "../gateway/net.js";
import { rawDataToString } from "../infra/ws.js";
import { getDirectAgentForCdp, withNoProxyForLocalhost } from "./cdp-proxy-bypass.js";
import { getDirectAgentForCdp, withNoProxyForCdpUrl } from "./cdp-proxy-bypass.js";
import { getChromeExtensionRelayAuthHeaders } from "./extension-relay.js";
export { isLoopbackHost };
@@ -114,17 +114,20 @@ function createCdpSender(ws: WebSocket) {
}
export async function fetchJson<T>(url: string, timeoutMs = 1500, init?: RequestInit): Promise<T> {
const res = await fetchChecked(url, timeoutMs, init);
const res = await fetchCdpChecked(url, timeoutMs, init);
return (await res.json()) as T;
}
async function fetchChecked(url: string, timeoutMs = 1500, init?: RequestInit): Promise<Response> {
export async function fetchCdpChecked(
url: string,
timeoutMs = 1500,
init?: RequestInit,
): Promise<Response> {
const ctrl = new AbortController();
const t = setTimeout(ctrl.abort.bind(ctrl), timeoutMs);
try {
const headers = getHeadersWithAuth(url, (init?.headers as Record<string, string>) || {});
// Bypass proxy for loopback CDP connections (#31219)
const res = await withNoProxyForLocalhost(() =>
const res = await withNoProxyForCdpUrl(url, () =>
fetch(url, { ...init, headers, signal: ctrl.signal }),
);
if (!res.ok) {
@@ -137,7 +140,24 @@ async function fetchChecked(url: string, timeoutMs = 1500, init?: RequestInit):
}
export async function fetchOk(url: string, timeoutMs = 1500, init?: RequestInit): Promise<void> {
await fetchChecked(url, timeoutMs, init);
await fetchCdpChecked(url, timeoutMs, init);
}
export function openCdpWebSocket(
wsUrl: string,
opts?: { headers?: Record<string, string>; handshakeTimeoutMs?: number },
): WebSocket {
const headers = getHeadersWithAuth(wsUrl, opts?.headers ?? {});
const handshakeTimeoutMs =
typeof opts?.handshakeTimeoutMs === "number" && Number.isFinite(opts.handshakeTimeoutMs)
? Math.max(1, Math.floor(opts.handshakeTimeoutMs))
: 5000;
const agent = getDirectAgentForCdp(wsUrl);
return new WebSocket(wsUrl, {
handshakeTimeout: handshakeTimeoutMs,
...(Object.keys(headers).length ? { headers } : {}),
...(agent ? { agent } : {}),
});
}
export async function withCdpSocket<T>(
@@ -145,18 +165,7 @@ export async function withCdpSocket<T>(
fn: (send: CdpSendFn) => Promise<T>,
opts?: { headers?: Record<string, string>; handshakeTimeoutMs?: number },
): Promise<T> {
const headers = getHeadersWithAuth(wsUrl, opts?.headers ?? {});
const handshakeTimeoutMs =
typeof opts?.handshakeTimeoutMs === "number" && Number.isFinite(opts.handshakeTimeoutMs)
? Math.max(1, Math.floor(opts.handshakeTimeoutMs))
: 5000;
// Bypass proxy for loopback CDP connections (#31219)
const agent = getDirectAgentForCdp(wsUrl);
const ws = new WebSocket(wsUrl, {
handshakeTimeout: handshakeTimeoutMs,
...(Object.keys(headers).length ? { headers } : {}),
...(agent ? { agent } : {}),
});
const ws = openCdpWebSocket(wsUrl, opts);
const { send, closeWithError } = createCdpSender(ws);
const openPromise = new Promise<void>((resolve, reject) => {