From 64e732d0acc9a5d8a524a028cdcb8ae66addd526 Mon Sep 17 00:00:00 2001 From: NianJiuZst <3235467914@qq.com> Date: Fri, 10 Jul 2026 22:16:06 +0800 Subject: [PATCH 1/3] fix(extension): harden websocket reconnect lifecycle --- .../__tests__/connection-controller.test.ts | 56 ++++++++++++- .../src/lib/connection-controller.ts | 78 ++++++++++++++++--- .../src/transport/__tests__/handshake.test.ts | 19 +++++ .../transport/__tests__/ws-transport.test.ts | 32 ++++++++ apps/extension/src/transport/handshake.ts | 15 +++- apps/extension/src/transport/ws-transport.ts | 38 +++++++-- 6 files changed, 215 insertions(+), 23 deletions(-) diff --git a/apps/extension/src/lib/__tests__/connection-controller.test.ts b/apps/extension/src/lib/__tests__/connection-controller.test.ts index b3eb9ae..9291cb5 100644 --- a/apps/extension/src/lib/__tests__/connection-controller.test.ts +++ b/apps/extension/src/lib/__tests__/connection-controller.test.ts @@ -1,7 +1,7 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { MIN_COMPATIBLE_PROTOCOL } from "../../transport/handshake"; -import type { ConnectionStateHandler, Transport } from "../../transport/transport"; -import type { ConnectionState, HandshakeResult } from "../../transport/types"; +import type { ConnectionStateHandler, FrameHandler, Transport } from "../../transport/transport"; +import type { ConnectionState, HandshakeResult, ProtocolFrame } from "../../transport/types"; import { __testing__, ConnectionController } from "../connection-controller"; vi.mock("../instance-id", () => ({ @@ -97,6 +97,7 @@ describe("computeConnectedState (protocol-based compat)", () => { function makeMockTransport(initialState: ConnectionState = "disconnected") { let state = initialState; const stateHandlers = new Set(); + const messageHandlers = new Set(); const transport = { get state() { return state; @@ -110,7 +111,10 @@ function makeMockTransport(initialState: ConnectionState = "disconnected") { for (const h of stateHandlers) h("disconnected"); }), send: vi.fn(), - onMessage: vi.fn(() => ({ dispose: () => {} })), + onMessage: vi.fn((handler: FrameHandler) => { + messageHandlers.add(handler); + return { dispose: () => messageHandlers.delete(handler) }; + }), onConnectionStateChange: vi.fn((handler: ConnectionStateHandler) => { stateHandlers.add(handler); return { dispose: () => stateHandlers.delete(handler) }; @@ -119,6 +123,9 @@ function makeMockTransport(initialState: ConnectionState = "disconnected") { state = next; for (const h of stateHandlers) h(next); }, + emitMessage(frame: ProtocolFrame) { + for (const handler of messageHandlers) handler(frame); + }, }; return transport as typeof transport & Transport; } @@ -128,6 +135,10 @@ describe("ConnectionController connectionEnabled", () => { vi.clearAllMocks(); }); + afterEach(() => { + vi.useRealTimers(); + }); + it("does not connect on attach when connection is disabled", async () => { const controller = new ConnectionController(); const transport = makeMockTransport(); @@ -171,4 +182,41 @@ describe("ConnectionController connectionEnabled", () => { expect(controller.snapshot().state).toBe("disconnected"); }); + + it("disconnects and retries when handshake fails while the socket is still open", async () => { + vi.useFakeTimers(); + const controller = new ConnectionController(); + const transport = makeMockTransport(); + await controller.attach(transport, { name: "Chrome", version: "120" }, true); + const request = transport.send.mock.calls[0]?.[0] as { id: string }; + + transport.emitMessage({ + id: request.id, + error: { code: "protocol_error", message: "bad handshake" }, + }); + await vi.waitFor(() => expect(transport.disconnect).toHaveBeenCalledTimes(1)); + expect(controller.snapshot().state).toBe("disconnected"); + + await vi.advanceTimersByTimeAsync(1_000); + expect(transport.connect).toHaveBeenCalledTimes(2); + }); + + it("binds each handshake to the connection that initiated it", async () => { + const controller = new ConnectionController(); + const transport = makeMockTransport(); + await controller.attach(transport, { name: "Chrome", version: "120" }, true); + const first = transport.send.mock.calls[0]?.[0] as { id: string }; + + transport.emitState("disconnected"); + transport.emitState("connected"); + const second = transport.send.mock.calls[1]?.[0] as { id: string }; + expect(second.id).not.toBe(first.id); + + transport.emitMessage({ id: first.id, result: handshake("1.0", "1.0") }); + await Promise.resolve(); + expect(controller.snapshot().state).not.toBe("connected"); + + transport.emitMessage({ id: second.id, result: handshake("1.0", "1.0") }); + await vi.waitFor(() => expect(controller.snapshot().state).toBe("connected")); + }); }); diff --git a/apps/extension/src/lib/connection-controller.ts b/apps/extension/src/lib/connection-controller.ts index bc0187b..940f038 100644 --- a/apps/extension/src/lib/connection-controller.ts +++ b/apps/extension/src/lib/connection-controller.ts @@ -9,6 +9,8 @@ import type { ConnectionState, HandshakeResult } from "../transport/types"; import { getLabel, getOrCreateInstanceId } from "./instance-id"; import { compareProtocol, parseProtocolMajor } from "./semver"; +const HANDSHAKE_RETRY_DELAY_MS = 1_000; + export interface SnapshotInfo { state: ConnectionState; instanceId: string; @@ -36,7 +38,9 @@ export class ConnectionController { private lastError: string | null = null; private connectionEnabled = true; private listeners = new Set(); - private handshakeInFlight = false; + private connectionGeneration = 0; + private handshakeAbort: AbortController | null = null; + private handshakeRetryTimer: ReturnType | null = null; get isConnectionEnabled(): boolean { return this.connectionEnabled; @@ -77,10 +81,11 @@ export class ConnectionController { transport.onConnectionStateChange((s) => { if (!this.connectionEnabled) return; if (s === "connected") { - void this.runHandshake(browser); + this.startHandshake(browser); return; } if (s === "disconnected") { + this.cancelHandshake(); this.handshake = null; } this.setState(s); @@ -126,18 +131,33 @@ export class ConnectionController { this.fire(); } - private async runHandshake(browser: { name: string; version: string }): Promise { + private startHandshake(browser: { name: string; version: string }): void { + this.cancelHandshake(); + const generation = ++this.connectionGeneration; + const abort = new AbortController(); + this.handshakeAbort = abort; + void this.runHandshake(browser, generation, abort.signal); + } + + private async runHandshake( + browser: { name: string; version: string }, + generation: number, + signal: AbortSignal, + ): Promise { if (!this.transport) return; if (!this.connectionEnabled) return; - if (this.handshakeInFlight) return; - this.handshakeInFlight = true; this.setState("connecting"); try { - const outcome = await performHandshake(this.transport, { - instanceId: this.instanceId, - browser, - label: this.label, - }); + const outcome = await performHandshake( + this.transport, + { + instanceId: this.instanceId, + browser, + label: this.label, + }, + { signal }, + ); + if (generation !== this.connectionGeneration || signal.aborted) return; this.handshake = outcome.result; const verdict = computeConnectedState(outcome.result); if (verdict.kind === "rejected") { @@ -147,18 +167,24 @@ export class ConnectionController { this.setState("disconnected"); return; } + this.clearHandshakeRetry(); this.lastError = null; this.setState(verdict.kind); } catch (err) { + if (generation !== this.connectionGeneration || signal.aborted || isAbortError(err)) return; this.handshake = null; this.lastError = err instanceof Error ? err.message : String(err); + await this.transport.disconnect().catch(() => {}); this.setState("disconnected"); + this.scheduleHandshakeRetry(browser); } finally { - this.handshakeInFlight = false; + if (generation === this.connectionGeneration) this.handshakeAbort = null; } } private async applyDisabledState(): Promise { + this.cancelHandshake(); + this.clearHandshakeRetry(); this.handshake = null; this.lastError = null; if (this.transport) { @@ -169,6 +195,30 @@ export class ConnectionController { if (was === "disconnected") this.fire(); } + private cancelHandshake(): void { + this.connectionGeneration += 1; + this.handshakeAbort?.abort(); + this.handshakeAbort = null; + } + + private scheduleHandshakeRetry(browser: { name: string; version: string }): void { + if (this.handshakeRetryTimer || !this.connectionEnabled) return; + this.handshakeRetryTimer = setTimeout(() => { + this.handshakeRetryTimer = null; + if (!this.connectionEnabled || !this.transport) return; + void this.transport.connect().catch((err) => { + this.lastError = err instanceof Error ? err.message : String(err); + this.setState("disconnected"); + }); + }, HANDSHAKE_RETRY_DELAY_MS); + } + + private clearHandshakeRetry(): void { + if (!this.handshakeRetryTimer) return; + clearTimeout(this.handshakeRetryTimer); + this.handshakeRetryTimer = null; + } + private setState(next: ConnectionState): void { if (this.currentState === next) return; this.currentState = next; @@ -187,6 +237,12 @@ export class ConnectionController { } } +function isAbortError(err: unknown): boolean { + return ( + typeof err === "object" && err !== null && (err as { name?: string }).name === "AbortError" + ); +} + /** * Verdict of the symmetric post-handshake compat check. */ diff --git a/apps/extension/src/transport/__tests__/handshake.test.ts b/apps/extension/src/transport/__tests__/handshake.test.ts index 3e41666..b511453 100644 --- a/apps/extension/src/transport/__tests__/handshake.test.ts +++ b/apps/extension/src/transport/__tests__/handshake.test.ts @@ -198,6 +198,25 @@ describe("performHandshake", () => { result: { server: "browser-skill-daemon", protocol_version: "1.0" }, }); }); + + it("stops waiting immediately when the connection generation is aborted", async () => { + const { transport } = deferredFakeTransport(); + const controller = new AbortController(); + const pending = performHandshake( + transport, + { + instanceId: "x", + browser: { name: "chrome", version: "131" }, + label: "", + rpcId: "hs-abort", + }, + { signal: controller.signal, timeoutMs: 60_000 }, + ); + + controller.abort(); + + await expect(pending).rejects.toMatchObject({ name: "AbortError" }); + }); }); describe("detectBrowserMeta", () => { diff --git a/apps/extension/src/transport/__tests__/ws-transport.test.ts b/apps/extension/src/transport/__tests__/ws-transport.test.ts index 4227ba0..566ab8a 100644 --- a/apps/extension/src/transport/__tests__/ws-transport.test.ts +++ b/apps/extension/src/transport/__tests__/ws-transport.test.ts @@ -49,6 +49,10 @@ class FakeSocket { this.emit("close", { code, reason: "server-gone" }); } + emitClose(code = 1006): void { + this.emit("close", { code, reason: "delayed-close" }); + } + private emit(type: string, ev: unknown): void { for (const l of this.listeners[type] ?? []) { // biome-ignore lint/suspicious/noExplicitAny: minimal fake mirrors WebSocket Event shape @@ -202,6 +206,34 @@ describe("WSTransport", () => { expect(FakeSocket.instances.length).toBe(beforeCount + 1); }); + it("cancels pending backoff and ignores delayed events from a replaced socket", async () => { + const t = new WSTransport({ + url: "ws://127.0.0.1:52800", + webSocketFactory: (url) => new FakeSocket(url) as unknown as WebSocket, + }); + const initial = t.connect(); + const first = lastSocket(); + first.open(); + await initial; + + first.serverClose(); + const reconnect = t.connect(); + expect(FakeSocket.instances).toHaveLength(2); + const second = lastSocket(); + second.open(); + await reconnect; + + // A real browser can deliver the old close callback after the replacement + // socket is already open. It must not clear or disconnect the new socket. + first.emitClose(); + t.send({ id: "current", method: "system.ping" }); + expect(second.sent).toEqual([JSON.stringify({ id: "current", method: "system.ping" })]); + expect(t.state).toBe("connected"); + + await vi.advanceTimersByTimeAsync(5_000); + expect(FakeSocket.instances).toHaveLength(2); + }); + it("ignores malformed inbound messages (non-JSON) without throwing", async () => { const handler = vi.fn(); const t = new WSTransport({ diff --git a/apps/extension/src/transport/handshake.ts b/apps/extension/src/transport/handshake.ts index 3172595..3da56fd 100644 --- a/apps/extension/src/transport/handshake.ts +++ b/apps/extension/src/transport/handshake.ts @@ -57,7 +57,7 @@ function ridToString(): string { export function performHandshake( transport: Transport, input: HandshakeInput, - options: { timeoutMs?: number } = {}, + options: { timeoutMs?: number; signal?: AbortSignal } = {}, ): Promise { const id = input.rpcId ?? ridToString(); const params: HandshakeParams = { @@ -75,6 +75,12 @@ export function performHandshake( const timeoutMs = options.timeoutMs ?? 10_000; return new Promise((resolve, reject) => { + const onAbort = () => { + cleanup(); + const err = new Error("[handshake] aborted because the connection changed"); + err.name = "AbortError"; + reject(err); + }; const timer = setTimeout(() => { cleanup(); reject(new Error("[handshake] timed out waiting for daemon response")); @@ -97,7 +103,14 @@ export function performHandshake( function cleanup() { clearTimeout(timer); sub.dispose(); + options.signal?.removeEventListener("abort", onAbort); + } + + if (options.signal?.aborted) { + onAbort(); + return; } + options.signal?.addEventListener("abort", onAbort, { once: true }); try { transport.send(req); diff --git a/apps/extension/src/transport/ws-transport.ts b/apps/extension/src/transport/ws-transport.ts index 68d9b2a..2f57782 100644 --- a/apps/extension/src/transport/ws-transport.ts +++ b/apps/extension/src/transport/ws-transport.ts @@ -50,6 +50,7 @@ export class WSTransport implements Transport { private explicitlyClosed = false; private reconnectAttempt = 0; private reconnectTimer: ReturnType | null = null; + private socketGeneration = 0; private connectingPromise: Promise | null = null; private resolveConnect: ((value: void) => void) | null = null; private rejectConnect: ((reason: Error) => void) | null = null; @@ -69,16 +70,26 @@ export class WSTransport implements Transport { } connect(): Promise { - if (this.connectingPromise) return this.connectingPromise; if (this.currentState === "connected") return Promise.resolve(); this.explicitlyClosed = false; - this.openSocket(); + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + + if (this.connectingPromise) { + // A previous physical attempt closed before reaching OPEN. Keep the + // original caller's promise, but start the next socket generation. + if (!this.socket) this.openSocket(); + return this.connectingPromise; + } this.connectingPromise = new Promise((resolve, reject) => { this.resolveConnect = resolve; this.rejectConnect = reject; }); + this.openSocket(); return this.connectingPromise; } @@ -88,9 +99,12 @@ export class WSTransport implements Transport { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } - if (this.socket) { + const socket = this.socket; + this.socket = null; + this.socketGeneration += 1; + if (socket) { try { - this.socket.close(); + socket.close(); } catch { // ignore } @@ -135,10 +149,12 @@ export class WSTransport implements Transport { private openSocket(): void { this.setState("connecting"); + const generation = ++this.socketGeneration; const socket = this.factory(this.url); this.socket = socket; socket.addEventListener("open", () => { + if (!this.isCurrentSocket(socket, generation)) return; this.reconnectAttempt = 0; this.setState("connected"); const resolve = this.resolveConnect; @@ -149,11 +165,12 @@ export class WSTransport implements Transport { }); socket.addEventListener("message", (ev: MessageEvent) => { + if (!this.isCurrentSocket(socket, generation)) return; this.handleInbound((ev as unknown as MessageLikeEvent).data); }); socket.addEventListener("close", (ev: Event) => { - this.handleClose(ev as unknown as CloseLikeEvent); + this.handleClose(socket, generation, ev as unknown as CloseLikeEvent); }); socket.addEventListener("error", () => { @@ -179,7 +196,8 @@ export class WSTransport implements Transport { } } - private handleClose(_ev: CloseLikeEvent): void { + private handleClose(socket: WebSocket, generation: number, _ev: CloseLikeEvent): void { + if (!this.isCurrentSocket(socket, generation)) return; this.socket = null; if (this.explicitlyClosed) { this.setState("disconnected"); @@ -196,10 +214,16 @@ export class WSTransport implements Transport { this.reconnectTimer = setTimeout(() => { this.reconnectTimer = null; if (this.explicitlyClosed) return; - this.openSocket(); + void this.connect().catch((err) => { + console.debug("[WSTransport] reconnect attempt failed", err); + }); }, delay); } + private isCurrentSocket(socket: WebSocket, generation: number): boolean { + return this.socket === socket && this.socketGeneration === generation; + } + private setState(next: ConnectionState): void { if (this.currentState === next) return; this.currentState = next; From 5910b011f27dd5ae15644c3c6827b1b0b1f5972a Mon Sep 17 00:00:00 2001 From: NianJiuZst <3235467914@qq.com> Date: Fri, 10 Jul 2026 22:40:16 +0800 Subject: [PATCH 2/3] chore(ci): match Biome 2.4 formatting --- apps/extension/vitest.config.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/extension/vitest.config.ts b/apps/extension/vitest.config.ts index 2e1c157..00060bf 100644 --- a/apps/extension/vitest.config.ts +++ b/apps/extension/vitest.config.ts @@ -21,9 +21,7 @@ export default defineConfig({ }, define: { __BSK_EXT_VERSION__: JSON.stringify(pkg.version), - __BSK_DAEMON_WS_URL__: JSON.stringify( - process.env.BSK_DAEMON_WS_URL ?? "ws://127.0.0.1:52800", - ), + __BSK_DAEMON_WS_URL__: JSON.stringify(process.env.BSK_DAEMON_WS_URL ?? "ws://127.0.0.1:52800"), }, resolve: { alias: { From 6720734fc52e1fe8c4fdfc0b672a28c30da388eb Mon Sep 17 00:00:00 2001 From: NianJiuZst <3235467914@qq.com> Date: Fri, 10 Jul 2026 23:06:02 +0800 Subject: [PATCH 3/3] test: bind daemon ports without probe races --- crates/bsk-cli/tests/browser_list_ordering.rs | 5 +---- crates/bsk-cli/tests/browser_wait.rs | 5 +---- crates/bsk-cli/tests/cancel_forwarding.rs | 5 +---- crates/bsk-cli/tests/handshake_compat.rs | 5 +---- crates/bsk-cli/tests/per_session_queue.rs | 5 +---- crates/bsk-cli/tests/session_user_interrupt.rs | 5 +---- crates/bsk-cli/tests/sessions_ipc.rs | 5 +---- crates/bsk-cli/tests/tools_ipc.rs | 5 +---- crates/bsk-cli/tests/tools_m7_ipc.rs | 5 +---- crates/bsk-cli/tests/tools_m8_ipc.rs | 5 +---- crates/bsk-cli/tests/tools_m9_ipc.rs | 5 +---- crates/bsk-cli/tests/ws_handshake.rs | 5 +---- 12 files changed, 12 insertions(+), 48 deletions(-) diff --git a/crates/bsk-cli/tests/browser_list_ordering.rs b/crates/bsk-cli/tests/browser_list_ordering.rs index 1c5199b..f19f320 100644 --- a/crates/bsk-cli/tests/browser_list_ordering.rs +++ b/crates/bsk-cli/tests/browser_list_ordering.rs @@ -16,7 +16,6 @@ use bsk_protocol::system::BrowserStatusEntry; use bsk_protocol::{ErrorCode, Method}; use rand::Rng; use serde::Deserialize; -use tokio::net::TcpListener; use tokio::sync::mpsc; fn tempfile_path(prefix: &str) -> PathBuf { @@ -30,9 +29,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port); let sock = tempfile_path("bsk-test-browser-list"); diff --git a/crates/bsk-cli/tests/browser_wait.rs b/crates/bsk-cli/tests/browser_wait.rs index 632a7e8..16513e0 100644 --- a/crates/bsk-cli/tests/browser_wait.rs +++ b/crates/bsk-cli/tests/browser_wait.rs @@ -15,7 +15,6 @@ use bsk_protocol::system::{BrowserListParams, HandshakeParams, HandshakeResult, use bsk_protocol::{BrowserPeerInfo, Method, RequestFrame, ResponseBody, ResponseFrame}; use futures_util::{SinkExt, StreamExt}; use rand::Rng; -use tokio::net::TcpListener; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; use tokio_tungstenite::tungstenite::protocol::Message; @@ -77,9 +76,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port); let sock = tempfile_path("bsk-test-browser-wait"); diff --git a/crates/bsk-cli/tests/cancel_forwarding.rs b/crates/bsk-cli/tests/cancel_forwarding.rs index b9913ca..e8a0aaf 100644 --- a/crates/bsk-cli/tests/cancel_forwarding.rs +++ b/crates/bsk-cli/tests/cancel_forwarding.rs @@ -20,7 +20,6 @@ use bsk_protocol::{ use futures_util::{SinkExt, StreamExt}; use rand::Rng; use serde_json::json; -use tokio::net::TcpListener; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; use tokio_tungstenite::tungstenite::protocol::Message; @@ -40,9 +39,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port); let sock = tempfile_path("bsk-test-cancel"); diff --git a/crates/bsk-cli/tests/handshake_compat.rs b/crates/bsk-cli/tests/handshake_compat.rs index 07e14e4..596d35e 100644 --- a/crates/bsk-cli/tests/handshake_compat.rs +++ b/crates/bsk-cli/tests/handshake_compat.rs @@ -10,7 +10,6 @@ use bsk_protocol::system::{HandshakeParams, HandshakeResult, StatusResult}; use bsk_protocol::{BrowserPeerInfo, ErrorCode, Method, RequestFrame, ResponseBody, ResponseFrame}; use futures_util::{SinkExt, StreamExt}; use rand::Rng; -use tokio::net::TcpListener; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; use tokio_tungstenite::tungstenite::protocol::Message; @@ -28,9 +27,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port); let sock = tempfile_path("bsk-test-handshake"); diff --git a/crates/bsk-cli/tests/per_session_queue.rs b/crates/bsk-cli/tests/per_session_queue.rs index f548ee6..79b5355 100644 --- a/crates/bsk-cli/tests/per_session_queue.rs +++ b/crates/bsk-cli/tests/per_session_queue.rs @@ -27,7 +27,6 @@ use futures_util::{SinkExt, StreamExt}; use rand::Rng; use serde::Deserialize; use serde_json::json; -use tokio::net::TcpListener; use tokio::sync::{Mutex, mpsc}; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; @@ -46,9 +45,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let sock = tempfile_path("bsk-test-queue"); let handle = daemon::run(DaemonConfig::new(port), Some(sock.clone())) .await diff --git a/crates/bsk-cli/tests/session_user_interrupt.rs b/crates/bsk-cli/tests/session_user_interrupt.rs index 2960814..b7d6e61 100644 --- a/crates/bsk-cli/tests/session_user_interrupt.rs +++ b/crates/bsk-cli/tests/session_user_interrupt.rs @@ -26,7 +26,6 @@ use bsk_protocol::{ use futures_util::{SinkExt, StreamExt}; use rand::Rng; use serde_json::json; -use tokio::net::TcpListener; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; use tokio_tungstenite::tungstenite::protocol::Message; @@ -46,9 +45,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port); let sock = tempfile_path("bsk-test-user-interrupt"); diff --git a/crates/bsk-cli/tests/sessions_ipc.rs b/crates/bsk-cli/tests/sessions_ipc.rs index a686520..99b1535 100644 --- a/crates/bsk-cli/tests/sessions_ipc.rs +++ b/crates/bsk-cli/tests/sessions_ipc.rs @@ -15,7 +15,6 @@ use bsk_protocol::tools::{SessionStartParams, SessionStartResult, SessionStopPar use bsk_protocol::{BrowserPeerInfo, Frame, Method, RequestFrame, ResponseBody, ResponseFrame}; use futures_util::{SinkExt, StreamExt}; use rand::Rng; -use tokio::net::TcpListener; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; use tokio_tungstenite::tungstenite::protocol::Message; @@ -39,9 +38,7 @@ async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { } async fn spawn_daemon_with_connect_wait(connect_wait: Duration) -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port).with_extension_connect_wait(connect_wait); let sock = tempfile_path("bsk-test-ipc"); diff --git a/crates/bsk-cli/tests/tools_ipc.rs b/crates/bsk-cli/tests/tools_ipc.rs index 32b7732..2857c62 100644 --- a/crates/bsk-cli/tests/tools_ipc.rs +++ b/crates/bsk-cli/tests/tools_ipc.rs @@ -21,7 +21,6 @@ use futures_util::stream::{SplitSink, SplitStream}; use futures_util::{SinkExt, StreamExt}; use rand::Rng; use serde_json::{Value, json}; -use tokio::net::TcpListener; use tokio::sync::Mutex; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; @@ -40,9 +39,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let sock = tempfile_path("bsk-test-tools"); let handle = daemon::run(DaemonConfig::new(port), Some(sock.clone())) .await diff --git a/crates/bsk-cli/tests/tools_m7_ipc.rs b/crates/bsk-cli/tests/tools_m7_ipc.rs index acb9481..ee74b5e 100644 --- a/crates/bsk-cli/tests/tools_m7_ipc.rs +++ b/crates/bsk-cli/tests/tools_m7_ipc.rs @@ -25,7 +25,6 @@ use futures_util::stream::{SplitSink, SplitStream}; use futures_util::{SinkExt, StreamExt}; use rand::Rng; use serde_json::{Value, json}; -use tokio::net::TcpListener; use tokio::sync::Mutex; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; @@ -44,9 +43,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let sock = tempfile_path("bsk-test-tools-m7"); let handle = daemon::run(DaemonConfig::new(port), Some(sock.clone())) .await diff --git a/crates/bsk-cli/tests/tools_m8_ipc.rs b/crates/bsk-cli/tests/tools_m8_ipc.rs index 5308f7b..14e7c72 100644 --- a/crates/bsk-cli/tests/tools_m8_ipc.rs +++ b/crates/bsk-cli/tests/tools_m8_ipc.rs @@ -24,7 +24,6 @@ use futures_util::stream::{SplitSink, SplitStream}; use futures_util::{SinkExt, StreamExt}; use rand::Rng; use serde_json::Value; -use tokio::net::TcpListener; use tokio::sync::Mutex; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; @@ -43,9 +42,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let sock = tempfile_path("bsk-test-tools-m8"); let handle = daemon::run(DaemonConfig::new(port), Some(sock.clone())) .await diff --git a/crates/bsk-cli/tests/tools_m9_ipc.rs b/crates/bsk-cli/tests/tools_m9_ipc.rs index 6dd5d33..2f8b46d 100644 --- a/crates/bsk-cli/tests/tools_m9_ipc.rs +++ b/crates/bsk-cli/tests/tools_m9_ipc.rs @@ -30,7 +30,6 @@ use rand::Rng; use serde_json::{Value, json}; #[cfg(unix)] use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; -use tokio::net::TcpListener; #[cfg(unix)] use tokio::net::UnixStream; use tokio::sync::Mutex; @@ -53,9 +52,7 @@ fn tempfile_path(prefix: &str) -> PathBuf { } async fn spawn_daemon() -> (daemon::DaemonHandle, PathBuf) { - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let sock = tempfile_path("bsk-test-tools-m9"); let handle = daemon::run(DaemonConfig::new(port), Some(sock.clone())) .await diff --git a/crates/bsk-cli/tests/ws_handshake.rs b/crates/bsk-cli/tests/ws_handshake.rs index 146ef33..c6e2da1 100644 --- a/crates/bsk-cli/tests/ws_handshake.rs +++ b/crates/bsk-cli/tests/ws_handshake.rs @@ -8,7 +8,6 @@ use bsk::daemon::{self, DaemonConfig}; use bsk_protocol::system::{HandshakeParams, HandshakeResult}; use bsk_protocol::{BrowserPeerInfo, Method, RequestFrame, ResponseBody, ResponseFrame}; use futures_util::{SinkExt, StreamExt}; -use tokio::net::TcpListener; use tokio_tungstenite::tungstenite::handshake::client::generate_key; use tokio_tungstenite::tungstenite::http::Request; use tokio_tungstenite::tungstenite::protocol::Message; @@ -17,9 +16,7 @@ pub const TEST_EXT_ID: &str = "abcdefghijklmnopabcdefghijklmnop"; // 32 chars in pub async fn spawn_daemon() -> daemon::DaemonHandle { // Bind to any free TCP port. - let probe = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let port = probe.local_addr().unwrap().port(); - drop(probe); + let port = 0; let config = DaemonConfig::new(port); daemon::run(config, None).await.unwrap()