You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
2.5 KiB
114 lines
2.5 KiB
'use strict'; |
|
|
|
const debug = require('debug')('detect-port'); |
|
const net = require('net'); |
|
const address = require('address'); |
|
|
|
module.exports = (port, host, callback) => { |
|
if (typeof port === 'function') { |
|
callback = port; |
|
port = null; |
|
} else if (typeof host === 'function') { |
|
callback = host; |
|
host = null; |
|
} |
|
port = parseInt(port) || 0; |
|
let maxPort = port + 10; |
|
if (maxPort > 65535) { |
|
maxPort = 65535; |
|
} |
|
debug('detect free port between [%s, %s)', port, maxPort); |
|
if (typeof callback === 'function') { |
|
return tryListen(host, port, maxPort, callback); |
|
} |
|
// promise |
|
return new Promise((resolve, reject) => { |
|
tryListen(host, port, maxPort, (error, realPort) => { |
|
if (error) { |
|
reject(error); |
|
} else { |
|
resolve(realPort); |
|
} |
|
}); |
|
}); |
|
}; |
|
|
|
function tryListen(host, port, maxPort, callback) { |
|
function handleError() { |
|
port++; |
|
if (port >= maxPort) { |
|
debug( |
|
'port: %s >= maxPort: %s, give up and use random port', |
|
port, |
|
maxPort |
|
); |
|
port = 0; |
|
maxPort = 0; |
|
} |
|
tryListen(host, port, maxPort, callback); |
|
} |
|
|
|
// 1. check specified host (or null) |
|
listen(port, host, (err, realPort) => { |
|
// ignore random listening |
|
if (port === 0) { |
|
return callback(err, realPort); |
|
} |
|
|
|
if (err) { |
|
return handleError(err); |
|
} |
|
|
|
// 2. check default host |
|
listen(port, null, err => { |
|
if (err) { |
|
return handleError(err); |
|
} |
|
|
|
// 3. check localhost |
|
listen(port, 'localhost', err => { |
|
if (err) { |
|
return handleError(err); |
|
} |
|
|
|
// 4. check current ip |
|
let ip; |
|
try { |
|
ip = address.ip(); |
|
} catch (err) { |
|
// Skip the `ip` check if `address.ip()` fails |
|
return callback(null, realPort); |
|
} |
|
|
|
listen(port, ip, (err, realPort) => { |
|
if (err) { |
|
return handleError(err); |
|
} |
|
|
|
callback(null, realPort); |
|
}); |
|
}); |
|
}); |
|
}); |
|
} |
|
|
|
function listen(port, hostname, callback) { |
|
const server = new net.Server(); |
|
|
|
server.on('error', err => { |
|
debug('listen %s:%s error: %s', hostname, port, err); |
|
server.close(); |
|
if (err.code === 'ENOTFOUND') { |
|
debug('ignore dns ENOTFOUND error, get free %s:%s', hostname, port); |
|
return callback(null, port); |
|
} |
|
return callback(err); |
|
}); |
|
|
|
server.listen(port, hostname, () => { |
|
port = server.address().port; |
|
server.close(); |
|
debug('get free %s:%s', hostname, port); |
|
return callback(null, port); |
|
}); |
|
}
|
|
|