UNPKG

3.02 kBJavaScriptView Raw
1'use strict';
2
3const net = require('net');
4const address = require('address');
5const debug = require('debug')('detect-port');
6
7module.exports = (port, callback) => {
8 let hostname = '';
9
10 if (typeof port === 'object' && port) {
11 hostname = port.hostname;
12 callback = port.callback;
13 port = port.port;
14 } else {
15 if (typeof port === 'function') {
16 callback = port;
17 port = null;
18 }
19 }
20
21 port = parseInt(port) || 0;
22 let maxPort = port + 10;
23 if (maxPort > 65535) {
24 maxPort = 65535;
25 }
26 debug('detect free port between [%s, %s)', port, maxPort);
27 if (typeof callback === 'function') {
28 return tryListen(port, maxPort, hostname, callback);
29 }
30 // promise
31 return new Promise(resolve => {
32 tryListen(port, maxPort, hostname, (_, realPort) => {
33 resolve(realPort);
34 });
35 });
36};
37
38function tryListen(port, maxPort, hostname, callback) {
39 function handleError() {
40 port++;
41 if (port >= maxPort) {
42 debug('port: %s >= maxPort: %s, give up and use random port', port, maxPort);
43 port = 0;
44 maxPort = 0;
45 }
46 tryListen(port, maxPort, hostname, callback);
47 }
48
49 // use user hostname
50 if (hostname) {
51 listen(port, hostname, (err, realPort) => {
52 if (err) {
53 if (err.code === 'EADDRNOTAVAIL') {
54 return callback(new Error('the ip that is not unknown on the machine'));
55 }
56 return handleError();
57 }
58
59 callback(null, realPort);
60 });
61 } else {
62 // 1. check null
63 listen(port, null, (err, realPort) => {
64 // ignore random listening
65 if (port === 0) {
66 return callback(err, realPort);
67 }
68
69 if (err) {
70 return handleError(err);
71 }
72
73 // 2. check 0.0.0.0
74 listen(port, '0.0.0.0', err => {
75 if (err) {
76 return handleError(err);
77 }
78
79 // 3. check localhost
80 listen(port, 'localhost', err => {
81 // if localhost refer to the ip that is not unkonwn on the machine, you will see the error EADDRNOTAVAIL
82 // https://stackoverflow.com/questions/10809740/listen-eaddrnotavail-error-in-node-js
83 if (err && err.code !== 'EADDRNOTAVAIL') {
84 return handleError(err);
85 }
86
87 // 4. check current ip
88 listen(port, address.ip(), (err, realPort) => {
89 if (err) {
90 return handleError(err);
91 }
92
93 callback(null, realPort);
94 });
95 });
96 });
97 });
98 }
99}
100
101function listen(port, hostname, callback) {
102 const server = new net.Server();
103
104 server.on('error', err => {
105 debug('listen %s:%s error: %s', hostname, port, err);
106 server.close();
107 if (err.code === 'ENOTFOUND') {
108 debug('ignore dns ENOTFOUND error, get free %s:%s', hostname, port);
109 return callback(null, port);
110 }
111 return callback(err);
112 });
113
114 server.listen(port, hostname, () => {
115 port = server.address().port;
116 server.close();
117 debug('get free %s:%s', hostname, port);
118 return callback(null, port);
119 });
120}