diff --git a/packages/core/src/browser/cdp.ts b/packages/core/src/browser/cdp.ts index b3185cd6c..c955eadd6 100644 --- a/packages/core/src/browser/cdp.ts +++ b/packages/core/src/browser/cdp.ts @@ -10,7 +10,6 @@ */ import { fstatSync } from 'node:fs'; -import { SOCKET_OPTIONS } from '../client.ts'; type Listener = (params: Record, sessionId: string | undefined) => void; interface Waiting { resolve(value: Record): void; reject(error: Error): void; timer: ReturnType; method: string } @@ -95,7 +94,7 @@ export class Cdp { /** The Windows transport: the browser's WebSocket endpoint on 127.0.0.1. */ static connect(url: string, timeoutMs = 10_000): Promise { return new Promise((resolve, reject) => { - const socket = new WebSocket(url, SOCKET_OPTIONS as unknown as string[]); + const socket = new WebSocket(url); const timer = setTimeout(() => { socket.close(); reject(new CdpError(`the browser did not accept a DevTools connection within ${timeoutMs / 1000} seconds`)); }, timeoutMs); socket.addEventListener('error', () => { clearTimeout(timer); reject(new CdpError(`could not connect to the browser's DevTools at ${url}`)); }, { once: true }); socket.addEventListener('open', () => { diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 286c05930..8b2567b58 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -81,21 +81,12 @@ export class SocketClosedError extends Error { } } -/** - * What every WebSocket this code opens with Bun's client passes: no - * `permessage-deflate` offer. With the extension on, Bun 1.4.2 and 1.4.3 - * sometimes close a sound stream with 1002 ("control frame is fragmented", - * "unsupported control frame", "unexpected opcode"): against the core's own - * server and against Chrome's DevTools, whose bytes a relay checked frame by - * frame and found well-formed. Uncompressed, a message is larger on the wire; - * compressed, the connection drops. - */ -export const SOCKET_OPTIONS = { perMessageDeflate: false } as const; - export async function connect(url: string, token: string, options: ConnectOptions = {}): Promise { const timeoutMs = options.timeoutMs ?? 5000; const deadline = Date.now() + timeoutMs; - const socket = new WebSocket(socketUrl(url), { ...SOCKET_OPTIONS, ...(options.headers === undefined ? {} : { headers: options.headers }) } as unknown as string[]); + const socket = options.headers === undefined + ? new WebSocket(socketUrl(url)) + : new WebSocket(socketUrl(url), { headers: options.headers } as unknown as string[]); const pending = new Map(); const listeners = new Map void>>(); let nextId = 1; diff --git a/packages/core/src/group/relay.ts b/packages/core/src/group/relay.ts index 203fc27c2..afd1b2c9f 100644 --- a/packages/core/src/group/relay.ts +++ b/packages/core/src/group/relay.ts @@ -29,7 +29,6 @@ import type { ServerWebSocket } from 'bun'; import { FILE_ROUTE, GROUP_RELAY_ROUTE, PROTOCOL_VERSION, RPC_PATH, RpcErrorCode, VIEW_ROUTE } from '@boite/contracts'; -import { SOCKET_OPTIONS } from '../client.ts'; import type { Core } from '../core.ts'; import { messageOf } from '../errors.ts'; import { OUTBOUND_CONGESTION_TIMEOUT_MS, OUTBOUND_MAX_BUFFERED_BYTES } from '../server/connection.ts'; @@ -389,7 +388,7 @@ export class RelayPipe { const opened = await new Promise((resolve) => { let socket: Upstream; try { - socket = new WebSocket(`${target.replace(/^http/, 'ws')}${RPC_PATH}`, SOCKET_OPTIONS as unknown as string[]); + socket = new WebSocket(`${target.replace(/^http/, 'ws')}${RPC_PATH}`); } catch { resolve(null); return; diff --git a/packages/core/test/browser/fixture.ts b/packages/core/test/browser/fixture.ts index 8bd8a837c..03d59b68b 100644 --- a/packages/core/test/browser/fixture.ts +++ b/packages/core/test/browser/fixture.ts @@ -3,7 +3,7 @@ import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import type { RemoteBrowserFrame, RemoteBrowserUnchanged } from '@boite/contracts'; -import { connect, SOCKET_OPTIONS, type CoreClient } from '../../src/client.ts'; +import { connect, type CoreClient } from '../../src/client.ts'; import { chromiumArgs, findChromium, waitForEndpoint } from '../../src/browser/chromium.ts'; import { echoThread, startTestCore, type TestCore } from '../harness.ts'; @@ -148,7 +148,7 @@ export function createBrowserFixture() { try { const args = chromiumArgs(dir, 'linux').filter(arg => arg !== '--remote-debugging-pipe'); browser = Bun.spawn([findChromium().path!, ...args, '--remote-debugging-port=0', '--remote-debugging-address=127.0.0.1', ...extraArgs], { stdout: 'ignore', stderr: 'ignore' }); - const connectedSocket = socket = new WebSocket(await waitForEndpoint(dir, browser.exited), SOCKET_OPTIONS as unknown as string[]); + const connectedSocket = socket = new WebSocket(await waitForEndpoint(dir, browser.exited)); await new Promise((resolve, reject) => { const finish = (error?: Error) => { clearTimeout(timer); diff --git a/packages/core/test/socket-options.test.ts b/packages/core/test/socket-options.test.ts deleted file mode 100644 index 10fa3fa75..000000000 --- a/packages/core/test/socket-options.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { expect, test } from 'bun:test'; -import { Cdp } from '../src/browser/cdp.ts'; -import { connect } from '../src/client.ts'; - -// Bun's client with permessage-deflate on sometimes closes a sound stream with -// 1002 (see SOCKET_OPTIONS): no socket this code opens may offer it. -test('the core client and the DevTools socket never offer permessage-deflate', async () => { - const offers: Array = []; - const server = Bun.serve({ - hostname: '127.0.0.1', port: 0, - fetch(request, self) { - offers.push(request.headers.get('sec-websocket-extensions')); - return self.upgrade(request) ? undefined : new Response('upgrade required'); - }, - websocket: { perMessageDeflate: true, message() {} }, - }); - try { - const url = `http://127.0.0.1:${server.port}`; - for (const headers of [undefined, { origin: url }]) { - await expect(connect(url, 'token', { timeoutMs: 200, headers })).rejects.toThrow(); - } - const cdp = await Cdp.connect(`ws://127.0.0.1:${server.port}/devtools/browser/test`); - cdp.close(); - expect(offers).toEqual([null, null, null]); - } finally { - server.stop(true); - } -}); diff --git a/tests/e2e/group-devices.test.ts b/tests/e2e/group-devices.test.ts index 2c28d5968..e978aca31 100644 --- a/tests/e2e/group-devices.test.ts +++ b/tests/e2e/group-devices.test.ts @@ -8,7 +8,7 @@ import { startUi } from './lib/ui.ts'; import { startCore, type RunningCore } from './lib/core.ts'; import { mobileAction } from './lib/mobile.ts'; import { codeVideo } from './lib/qr-video.ts'; -import { connect, SOCKET_OPTIONS, type CoreClient } from '../../packages/core/src/client.ts'; +import { connect, type CoreClient } from '../../packages/core/src/client.ts'; /** * A phone is a device of the group, not of one machine. @@ -71,7 +71,7 @@ function proxy(core: RunningCore): ReturnType { websocket: { open(socket: ServerWebSocket) { const headers: Record = { host: socket.data.host, ...(socket.data.origin === null ? {} : { origin: socket.data.origin }) }; - const upstream = new WebSocket(`${core.url.replace(/^http/, 'ws')}${socket.data.path}`, { ...SOCKET_OPTIONS, headers } as unknown as string[]); + const upstream = new WebSocket(`${core.url.replace(/^http/, 'ws')}${socket.data.path}`, { headers } as unknown as string[]); upstream.binaryType = 'arraybuffer'; socket.data.upstream = upstream; upstream.onopen = () => { diff --git a/tests/e2e/lib/cdp.ts b/tests/e2e/lib/cdp.ts index 3bb2883c8..f03659017 100644 --- a/tests/e2e/lib/cdp.ts +++ b/tests/e2e/lib/cdp.ts @@ -4,7 +4,6 @@ import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { ONBOARDING_STORAGE_KEY, ONBOARDING_VERSION } from '../../../packages/ui/src/lib/onboarding.ts'; import type { Subprocess } from 'bun'; -import { SOCKET_OPTIONS } from '../../../packages/core/src/client.ts'; import { E2E_DIR_PREFIX, killProcessTreeAsync, removeDirectory, watchParentDeath, type ParentDeathWatch } from './cleanup.ts'; /** @@ -711,7 +710,7 @@ function openSocket(target: TargetInfo): Promise { const url = target.webSocketDebuggerUrl; if (url === undefined) throw new Error('the page target has no debugger url'); return new Promise((resolve, reject) => { - const socket = new WebSocket(url, SOCKET_OPTIONS as unknown as string[]); + const socket = new WebSocket(url); // A socket that never opened is closed with its refusal: nothing else // holds it, and a late open would keep its connection for the whole run. const fail = (message: string) => {