From 3ebcab88300e25c4ae07b3d9db97fa1e2357c53e Mon Sep 17 00:00:00 2001 From: Olivier MICHAUD Date: Wed, 30 Sep 2020 22:20:02 +0200 Subject: [PATCH] Add a Port pool manager to handle specific port atribution --- lib/PortManager.js | 60 +++++++++++++++++++++++++++++++++++++++++ lib/PortManager.test.js | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 lib/PortManager.js create mode 100644 lib/PortManager.test.js diff --git a/lib/PortManager.js b/lib/PortManager.js new file mode 100644 index 0000000..cffc4ac --- /dev/null +++ b/lib/PortManager.js @@ -0,0 +1,60 @@ +import Debug from 'debug'; + +class PortManager { + constructor(opt) { + this.debug = Debug('lt:PortManager'); + this.range = opt.range || null; + this.first = null; + this.last = null; + this.pool = {}; + this.initializePool(); + } + + initializePool() { + if (this.range === null) { + return; + } + + if (!/^[0-9]+:[0-9]+$/.test(this.range)) { + throw new Error('Bad range expression: ' + this.range); + } + + [this.first, this.last] = this.range.split(':').map((port) => parseInt(port)); + + if (this.first > this.last) { + throw new Error('Bad range expression min > max: ' + this.range); + } + + for (let port = this.first; port <= this.last; port++) { + this.pool['_' + port] = null; + } + this.debug = Debug('lt:PortManager'); + this.debug('Pool initialized ' + JSON.stringify(this.pool)); + } + + release(port) { + if (this.range === null) { + return; + } + this.debug('Release port ' + port); + this.pool['_' + port] = null; + } + + getNextAvailable(clientId) { + if (this.range === null) { + return null; + } + + for (let port = this.first; port <= this.last; port++) { + if (this.pool['_' + port] === null) { + this.pool['_' + port] = clientId; + this.debug('Port found ' + port); + return port; + } + } + this.debug('No more ports available '); + throw new Error('No more ports available in range ' + this.range); + } +} + +export default PortManager; diff --git a/lib/PortManager.test.js b/lib/PortManager.test.js new file mode 100644 index 0000000..eb403e1 --- /dev/null +++ b/lib/PortManager.test.js @@ -0,0 +1,51 @@ +import assert from 'assert'; + +import PortManager from './PortManager'; + +describe('PortManager', () => { + it('should construct with no range', () => { + const portManager = new PortManager({}); + assert.equal(portManager.range, null); + assert.equal(portManager.first, null); + assert.equal(portManager.last, null); + }); + + it('should construct with range', () => { + const portManager = new PortManager({range: '10:20'}); + assert.equal(portManager.range, '10:20'); + assert.equal(portManager.first, 10); + assert.equal(portManager.last, 20); + }); + + it('should not construct with bad range expression', () => { + assert.throws(()=>{ + new PortManager({range: 'a1020'}); + }, /Bad range expression: a1020/) + }); + + it('should not construct with bad range max>min', () => { + assert.throws(()=>{ + new PortManager({range: '20:10'}); + }, /Bad range expression min > max: 20:10/) + }); + + it('should work has expected', async () => { + const portManager = new PortManager({range: '10:12'}); + assert.equal(10,portManager.getNextAvailable('a')); + assert.equal(11,portManager.getNextAvailable('b')); + assert.equal(12,portManager.getNextAvailable('c')); + + assert.throws(()=>{ + portManager.getNextAvailable(); + }, /No more ports available in range 10:12/) + + portManager.release(11); + assert.equal(11,portManager.getNextAvailable('bb')); + + portManager.release(10); + portManager.release(12); + + assert.equal(10,portManager.getNextAvailable('cc')); + assert.equal(12,portManager.getNextAvailable('dd')); + }); +});