detect-port.js 3.0 KB

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