From 031ba24d7b3540fcc4b5aa6e08cc08a88ff0631e Mon Sep 17 00:00:00 2001 From: Vinicius Fortuna Date: Wed, 30 Nov 2022 17:26:38 -0500 Subject: [PATCH] fix(server): fix isPortUsed for IPv6 (#1238) --- src/shadowbox/infrastructure/get_port.spec.ts | 43 +++++++++++++------ src/shadowbox/infrastructure/get_port.ts | 2 +- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/shadowbox/infrastructure/get_port.spec.ts b/src/shadowbox/infrastructure/get_port.spec.ts index f3481008..72526250 100644 --- a/src/shadowbox/infrastructure/get_port.spec.ts +++ b/src/shadowbox/infrastructure/get_port.spec.ts @@ -34,8 +34,9 @@ describe('PortProvider', () => { const ports = new get_port.PortProvider(); const server = await listen(); const initialPort = (server.address() as net.AddressInfo).port; - expect(await ports.reserveFirstFreePort(initialPort)).toBeGreaterThan(initialPort); - server.close(); + const reservedPort = await ports.reserveFirstFreePort(initialPort); + await closeServer(server); + expect(reservedPort).toBeGreaterThan(initialPort); }); it('respects reserved ports', async () => { @@ -47,30 +48,42 @@ describe('PortProvider', () => { }); describe('reserveNewPort', () => { - it('Returns a port not in use', async (done) => { - for (let i = 0; i < 1000; ++i) { + it('Returns a port not in use', async () => { + // We run 100 times to try to trigger possible race conditions. + for (let i = 0; i < 100; ++i) { const port = await new get_port.PortProvider().reserveNewPort(); expect(await get_port.isPortUsed(port)).toBeFalsy(); } - done(); }); }); }); describe('isPortUsed', () => { - it('Identifies a port in use', async (done) => { + it('Identifies a port in use on IPV4', async () => { const port = 12345; const server = new net.Server(); - server.listen(port, async () => { - expect(await get_port.isPortUsed(port)).toBeTruthy(); - server.close(); - done(); + const isPortUsed = await new Promise((resolve) => { + server.listen(port, '127.0.0.1', () => { + resolve(get_port.isPortUsed(port)); + }); }); + await closeServer(server); + expect(isPortUsed).toBeTruthy(); }); - it('Identifies a port not in use', async (done) => { + it('Identifies a port in use on IPV6', async () => { + const port = 12345; + const server = new net.Server(); + const isPortUsed = await new Promise((resolve) => { + server.listen(port, '::1', () => { + resolve(get_port.isPortUsed(port)); + }); + }); + await closeServer(server); + expect(isPortUsed).toBeTruthy(); + }); + it('Identifies a port not in use', async () => { const port = await new get_port.PortProvider().reserveNewPort(); expect(await get_port.isPortUsed(port)).toBeFalsy(); - done(); }); }); @@ -82,3 +95,9 @@ function listen(): Promise { }); }); } + +function closeServer(server: net.Server): Promise { + return new Promise((resolve, reject) => { + server.close(err => err ? reject(err) : resolve()); + }); +} diff --git a/src/shadowbox/infrastructure/get_port.ts b/src/shadowbox/infrastructure/get_port.ts index 2f823d34..4b94c05c 100644 --- a/src/shadowbox/infrastructure/get_port.ts +++ b/src/shadowbox/infrastructure/get_port.ts @@ -75,7 +75,7 @@ export function isPortUsed(port: number): Promise { } server.close(); }); - server.listen({host: 'localhost', port, exclusive: true}, () => { + server.listen({port, exclusive: true}, () => { isUsed = false; server.close(); });