From 9fb74cb58a35eaf98c0cc503222ec876401a8e2a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 25 Dec 2025 01:41:09 +0000 Subject: [PATCH] test: assert bridge does not add loopback listener --- src/infra/bridge/server.test.ts | 24 ++++++++++++++++++++++++ src/infra/bridge/server.ts | 18 ++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/infra/bridge/server.test.ts b/src/infra/bridge/server.test.ts index 8125db1ee..eff8263aa 100644 --- a/src/infra/bridge/server.test.ts +++ b/src/infra/bridge/server.test.ts @@ -88,6 +88,26 @@ describe("node bridge server", () => { await server.close(); }); + it("does not add a loopback listener when bind already includes loopback", async () => { + const loopback = await startNodeBridgeServer({ + host: "127.0.0.1", + port: 0, + pairingBaseDir: baseDir, + }); + expect(loopback.listeners).toHaveLength(1); + expect(loopback.listeners[0]?.host).toBe("127.0.0.1"); + await loopback.close(); + + const wildcard = await startNodeBridgeServer({ + host: "0.0.0.0", + port: 0, + pairingBaseDir: baseDir, + }); + expect(wildcard.listeners).toHaveLength(1); + expect(wildcard.listeners[0]?.host).toBe("0.0.0.0"); + await wildcard.close(); + }); + it("also listens on loopback when bound to a non-loopback host", async () => { const host = pickNonLoopbackIPv4(); if (!host) return; @@ -98,6 +118,10 @@ describe("node bridge server", () => { pairingBaseDir: baseDir, }); + expect(server.listeners.map((l) => l.host).sort()).toEqual( + [host, "127.0.0.1"].sort(), + ); + const socket = net.connect({ host: "127.0.0.1", port: server.port }); await new Promise((resolve, reject) => { socket.once("connect", resolve); diff --git a/src/infra/bridge/server.ts b/src/infra/bridge/server.ts index 2cd1f0b53..77681cb35 100644 --- a/src/infra/bridge/server.ts +++ b/src/infra/bridge/server.ts @@ -117,6 +117,7 @@ export type NodeBridgeServer = { payloadJSON?: string | null; }) => void; listConnected: () => NodeBridgeClientInfo[]; + listeners: Array<{ host: string; port: number }>; }; export type NodeBridgeClientInfo = { @@ -177,6 +178,7 @@ export async function startNodeBridgeServer( }, sendEvent: () => {}, listConnected: () => [], + listeners: [], }; } @@ -672,7 +674,7 @@ export async function startNodeBridgeServer( }); }; - const servers: net.Server[] = []; + const listeners: Array<{ host: string; server: net.Server }> = []; const primary = net.createServer(onConnection); await new Promise((resolve, reject) => { const onError = (err: Error) => reject(err); @@ -682,7 +684,10 @@ export async function startNodeBridgeServer( resolve(); }); }); - servers.push(primary); + listeners.push({ + host: String(opts.host ?? "").trim() || "(default)", + server: primary, + }); const address = primary.address(); const port = @@ -699,7 +704,7 @@ export async function startNodeBridgeServer( resolve(); }); }); - servers.push(loopback); + listeners.push({ host: loopbackHost, server: loopback }); } catch { try { loopback.close(); @@ -721,15 +726,16 @@ export async function startNodeBridgeServer( } connections.clear(); await Promise.all( - servers.map( - (s) => + listeners.map( + (l) => new Promise((resolve, reject) => - s.close((err) => (err ? reject(err) : resolve())), + l.server.close((err) => (err ? reject(err) : resolve())), ), ), ); }, listConnected: () => [...connections.values()].map((c) => c.nodeInfo), + listeners: listeners.map((l) => ({ host: l.host, port })), sendEvent: ({ nodeId, event, payloadJSON }) => { const normalizedNodeId = String(nodeId ?? "").trim(); const normalizedEvent = String(event ?? "").trim();