115 lines
2.5 KiB
JavaScript
115 lines
2.5 KiB
JavaScript
|
'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);
|
||
|
});
|
||
|
}
|