/* * portfinder.js: A simple tool to find an open port on the current machine. * * (C) 2011, Charlie Robbins * */ var fs = require('fs'), net = require('net'), path = require('path'), async = require('async'), mkdirp = require('mkdirp').mkdirp; // // ### @basePort {Number} // The lowest port to begin any port search from // exports.basePort = 8000; // // ### @basePath {string} // Default path to begin any socket search from // exports.basePath = '/tmp/portfinder' // // ### function getPort (options, callback) // #### @options {Object} Settings to use when finding the necessary port // #### @callback {function} Continuation to respond to when complete. // Responds with a unbound port on the current machine. // exports.getPort = function (options, callback) { if (!callback) { callback = options; options = {}; } options.port = options.port || exports.basePort; options.host = options.host || null; options.server = options.server || net.createServer(function () { // // Create an empty listener for the port testing server. // }); function onListen () { options.server.removeListener('error', onError); options.server.close(); callback(null, options.port) } function onError (err) { options.server.removeListener('listening', onListen); if (err.code !== 'EADDRINUSE' && err.code !== 'EACCES') { return callback(err); } exports.getPort({ port: exports.nextPort(options.port), host: options.host, server: options.server }, callback); } options.server.once('error', onError); options.server.once('listening', onListen); options.server.listen(options.port, options.host); }; // // ### function getPorts (count, options, callback) // #### @count {Number} The number of ports to find // #### @options {Object} Settings to use when finding the necessary port // #### @callback {function} Continuation to respond to when complete. // Responds with an array of unbound ports on the current machine. // exports.getPorts = function (count, options, callback) { if (!callback) { callback = options; options = {}; } var lastPort = null; async.timesSeries(count, function(index, asyncCallback) { if (lastPort) { options.port = exports.nextPort(lastPort); } exports.getPort(options, function (err, port) { if (err) { asyncCallback(err); } else { lastPort = port; asyncCallback(null, port); } }); }, callback); }; // // ### function getSocket (options, callback) // #### @options {Object} Settings to use when finding the necessary port // #### @callback {function} Continuation to respond to when complete. // Responds with a unbound socket using the specified directory and base // name on the current machine. // exports.getSocket = function (options, callback) { if (!callback) { callback = options; options = {}; } options.mod = options.mod || 0755; options.path = options.path || exports.basePath + '.sock'; // // Tests the specified socket // function testSocket () { fs.stat(options.path, function (err) { // // If file we're checking doesn't exist (thus, stating it emits ENOENT), // we should be OK with listening on this socket. // if (err) { if (err.code == 'ENOENT') { callback(null, options.path); } else { callback(err); } } else { // // This file exists, so it isn't possible to listen on it. Lets try // next socket. // options.path = exports.nextSocket(options.path); exports.getSocket(options, callback); } }); } // // Create the target `dir` then test connection // against the socket. // function createAndTestSocket (dir) { mkdirp(dir, options.mod, function (err) { if (err) { return callback(err); } options.exists = true; testSocket(); }); } // // Check if the parent directory of the target // socket path exists. If it does, test connection // against the socket. Otherwise, create the directory // then test connection. // function checkAndTestSocket () { var dir = path.dirname(options.path); fs.stat(dir, function (err, stats) { if (err || !stats.isDirectory()) { return createAndTestSocket(dir); } options.exists = true; testSocket(); }); } // // If it has been explicitly stated that the // target `options.path` already exists, then // simply test the socket. // return options.exists ? testSocket() : checkAndTestSocket(); }; // // ### function nextPort (port) // #### @port {Number} Port to increment from. // Gets the next port in sequence from the // specified `port`. // exports.nextPort = function (port) { return port + 1; }; // // ### function nextSocket (socketPath) // #### @socketPath {string} Path to increment from // Gets the next socket path in sequence from the // specified `socketPath`. // exports.nextSocket = function (socketPath) { var dir = path.dirname(socketPath), name = path.basename(socketPath, '.sock'), match = name.match(/^([a-zA-z]+)(\d*)$/i), index = parseInt(match[2]), base = match[1]; if (isNaN(index)) { index = 0; } index += 1; return path.join(dir, base + index + '.sock'); };