test: assert bridge does not add loopback listener

This commit is contained in:
Peter Steinberger
2025-12-25 01:41:09 +00:00
parent 81e11c1d91
commit 9fb74cb58a
2 changed files with 36 additions and 6 deletions

View File

@@ -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<void>((resolve, reject) => {
socket.once("connect", resolve);

View File

@@ -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<void>((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<void>((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();