pick-port
Advanced tools
Comparing version 2.0.0 to 2.0.1
@@ -1,2 +0,2 @@ | ||
export type Type = 'udp' | 'tcp'; | ||
export type Type = 'tcp' | 'udp'; | ||
export declare function pickPort({ type, ip, minPort, maxPort, reserveTimeout, }: { | ||
@@ -3,0 +3,0 @@ type: Type; |
@@ -5,2 +5,3 @@ "use strict"; | ||
const net = require("node:net"); | ||
const crypto = require("node:crypto"); | ||
const Logger_1 = require("./Logger"); | ||
@@ -40,7 +41,5 @@ const tcp_1 = require("./tcp"); | ||
// Take a random port in the range. | ||
// NOTE: Use last reserved port as initial value since it will be incremented | ||
// at the end of the loop below. | ||
let port = lastReservedPort === undefined | ||
? Math.floor(Math.random() * (maxPort + 1 - minPort)) + minPort | ||
: lastReservedPort; | ||
// NOTE: Use last reserved port (if any) as initial value since it will be | ||
// incremented at the end of the loop below. | ||
let port = lastReservedPort ?? crypto.randomInt(minPort, maxPort + 1); | ||
let retries = maxPort - minPort + 1; | ||
@@ -52,6 +51,9 @@ while (--retries >= 0) { | ||
} | ||
const hash = generateHash(type, ip, port); | ||
// If current port is reserved, try next one. | ||
if (isReserved(type, ip, port)) { | ||
if (isReserved(hash)) { | ||
continue; | ||
} | ||
// Optimistically reserve the port. | ||
reserve(hash); | ||
try { | ||
@@ -68,8 +70,10 @@ switch (type) { | ||
} | ||
reserve(type, ip, port, reserveTimeout); | ||
logger.debug(`pickPort() | got available port [type:${type}, ip:${ip}, port:${port}]`); | ||
lastReservedPort = port; | ||
logger.debug(`pickPort() | got available port [type:${type}, ip:${ip}, port:${port}]`); | ||
// Unreserve the reserved port after given timeout. | ||
setTimeout(() => unreserve(hash), reserveTimeout * 1000); | ||
return port; | ||
} | ||
catch (error) { | ||
unreserve(hash); | ||
if (error.code === 'EADDRINUSE') { | ||
@@ -89,13 +93,19 @@ logger.debug(`pickPort() | port in use [type:${type}, ip:${ip}, port:${port}]`); | ||
exports.pickPort = pickPort; | ||
function reserve(type, ip, port, reserveTimeout) { | ||
const hash = generateHash(type, ip, port); | ||
function generateHash(type, ip, port) { | ||
return `${type}:${ip}:${port}`; | ||
} | ||
function reserve(hash) { | ||
if (isReserved(hash)) { | ||
throw new Error(`reserve() | hash '${hash}' is already reserved`); | ||
} | ||
reserved.add(hash); | ||
setTimeout(() => reserved.delete(hash), reserveTimeout * 1000); | ||
} | ||
function isReserved(type, ip, port) { | ||
const hash = generateHash(type, ip, port); | ||
function unreserve(hash) { | ||
if (!isReserved(hash)) { | ||
throw new Error(`unreserve() | hash '${hash}' is not reserved`); | ||
} | ||
reserved.delete(hash); | ||
} | ||
function isReserved(hash) { | ||
return reserved.has(hash); | ||
} | ||
function generateHash(type, ip, port) { | ||
return `${type}:${ip}:${port}`; | ||
} |
@@ -70,8 +70,9 @@ "use strict"; | ||
}); | ||
expect([port1, port2]).toEqual(expect.arrayContaining([minPort, maxPort])); | ||
// No more ports available during 1 second so this should reject. | ||
expect([port1, port2]).toEqual(expect.arrayContaining([2001, 2002])); | ||
// No more ports available during reserve time second so this should | ||
// reject. | ||
await expect((0, __1.pickPort)({ type, ip, minPort, maxPort, reserveTimeout })).rejects.toThrow(); | ||
// However it should work if a separate range is given. | ||
await expect((0, __1.pickPort)({ type, ip, minPort: 3001, maxPort: 3002, reserveTimeout })).resolves.toBeNumber(); | ||
// After 1 second, ports should be available again. | ||
// After reserve time, ports should be available again. | ||
await new Promise(resolve => setTimeout(resolve, reserveTimeout * 1000)); | ||
@@ -81,2 +82,33 @@ await expect((0, __1.pickPort)({ type, ip, minPort, maxPort, reserveTimeout })).resolves.toBeNumber(); | ||
}, 4000); | ||
test('pick N ports at the same time succeeds', async () => { | ||
for (const type of allTypes) { | ||
const ip = '127.0.0.1'; | ||
const minPort = 3001; | ||
const maxPort = 3003; | ||
const reserveTimeout = 1; | ||
await expect(Promise.all([ | ||
(0, __1.pickPort)({ | ||
type, | ||
ip, | ||
minPort, | ||
maxPort, | ||
reserveTimeout, | ||
}), | ||
(0, __1.pickPort)({ | ||
type, | ||
ip, | ||
minPort, | ||
maxPort, | ||
reserveTimeout, | ||
}), | ||
(0, __1.pickPort)({ | ||
type, | ||
ip, | ||
minPort, | ||
maxPort, | ||
reserveTimeout, | ||
}), | ||
])).resolves.toEqual(expect.arrayContaining([3001, 3002, 3003])); | ||
} | ||
}, 2000); | ||
/** | ||
@@ -83,0 +115,0 @@ * Not all reported IPs are bindable. Verify it by binding on them in a random |
{ | ||
"name": "pick-port", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "Get a free TCP or UDP port for the given IP address", | ||
@@ -12,3 +12,3 @@ "author": "José Luis Millán <jmillan@aliax.net> (https://github.com/jmillan)", | ||
"type": "git", | ||
"url": "https://github.com/versatica/pick-port.git" | ||
"url": "git+https://github.com/versatica/pick-port.git" | ||
}, | ||
@@ -71,3 +71,3 @@ "bugs": { | ||
"@types/jest": "^29.5.11", | ||
"@types/node": "^20.10.8", | ||
"@types/node": "^20.11.0", | ||
"@typescript-eslint/eslint-plugin": "^6.18.1", | ||
@@ -77,4 +77,4 @@ "@typescript-eslint/parser": "^6.18.1", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-jest": "^27.6.1", | ||
"eslint-plugin-prettier": "^5.1.2", | ||
"eslint-plugin-jest": "^27.6.2", | ||
"eslint-plugin-prettier": "^5.1.3", | ||
"jest": "^29.7.0", | ||
@@ -81,0 +81,0 @@ "jest-extended": "^4.0.2", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
22110
465