From be923374db624c13a7a3ac29a4662c2212f7a002 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 14 Apr 2020 12:43:16 +1000 Subject: [PATCH 1/4] Handle EADDRINUSE by asking for another port number. Fixes #97. --- src/commands/start.ts | 44 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/commands/start.ts b/src/commands/start.ts index 26d70a2b..16cca6ae 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -1,4 +1,5 @@ import { Argv } from 'yargs'; +import inquirer from 'inquirer'; import checkNodejsVersion from '../checks/nodejs-version'; import checkProjectStructure from '../checks/project-structure'; import { getConfigFromCli, StartCliFlags } from '../config/start'; @@ -12,6 +13,10 @@ import { getFullCommand } from './utils'; const debug = getDebugFunction('twilio-run:start'); +type ServerError = Error & { + code: string; +}; + export async function handler( argv: StartCliFlags, externalCliOptions?: ExternalCliOptions @@ -41,11 +46,44 @@ export async function handler( const app = await createServer(config.port, config); debug('Start server on port %d', config.port); - return new Promise(resolve => { - app.listen(config.port, async () => { + return new Promise((resolve, reject) => { + const serverStartedSuccessfully = async () => { printRouteInfo(config); resolve(); - }); + }; + const handleServerError = async (error: ServerError) => { + if (error.code === 'EADDRINUSE') { + const answers = await inquirer.prompt([ + { + type: 'input', + default: config.port + 1, + name: 'newPortNumber', + message: `Port ${config.port} is already in use. Choose a new port number:`, + validate: input => { + const newPortNumber = parseInt(input, 10); + if ( + !Number.isNaN(newPortNumber) && + newPortNumber <= 65535 && + newPortNumber > 0 + ) { + return true; + } + return 'Please enter a port number between 0 and 65535.'; + }, + }, + ]); + const server = app.listen( + answers.newPortNumber, + serverStartedSuccessfully + ); + server.on('error', handleServerError); + } else { + reject(error); + } + }; + + const server = app.listen(config.port, serverStartedSuccessfully); + server.on('error', handleServerError); }); } From 630e7f86dc11dcda5a043e07c26407ecb0044195 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 20 Apr 2020 10:21:53 +1000 Subject: [PATCH 2/4] Suggested new port number is now random. --- src/commands/start.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/commands/start.ts b/src/commands/start.ts index 16cca6ae..30b5c0ef 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -17,6 +17,11 @@ type ServerError = Error & { code: string; }; +function randomPort() { + // Returns a random port number higher than 1024 and lower than 65536. + return Math.floor(Math.random() * (65535 - 1025) + 1025); +} + export async function handler( argv: StartCliFlags, externalCliOptions?: ExternalCliOptions @@ -56,7 +61,7 @@ export async function handler( const answers = await inquirer.prompt([ { type: 'input', - default: config.port + 1, + default: randomPort(), name: 'newPortNumber', message: `Port ${config.port} is already in use. Choose a new port number:`, validate: input => { From 5434e272aba32bb1b07216bd0c5400be8e5468f5 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Mon, 20 Apr 2020 10:40:33 +1000 Subject: [PATCH 3/4] Allows 3 attempts at starting the server before quitting. --- src/commands/start.ts | 55 +++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/commands/start.ts b/src/commands/start.ts index 30b5c0ef..d2d4dbc2 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -10,6 +10,7 @@ import { getDebugFunction, setLogLevelByName } from '../utils/logger'; import { ExternalCliOptions, sharedCliOptions } from './shared'; import { CliInfo } from './types'; import { getFullCommand } from './utils'; +import { logger } from '../utils/logger'; const debug = getDebugFunction('twilio-run:start'); @@ -52,36 +53,44 @@ export async function handler( const app = await createServer(config.port, config); debug('Start server on port %d', config.port); return new Promise((resolve, reject) => { + let attempts = 1; + const MAX_ATTEMPTS = 3; const serverStartedSuccessfully = async () => { printRouteInfo(config); resolve(); }; const handleServerError = async (error: ServerError) => { if (error.code === 'EADDRINUSE') { - const answers = await inquirer.prompt([ - { - type: 'input', - default: randomPort(), - name: 'newPortNumber', - message: `Port ${config.port} is already in use. Choose a new port number:`, - validate: input => { - const newPortNumber = parseInt(input, 10); - if ( - !Number.isNaN(newPortNumber) && - newPortNumber <= 65535 && - newPortNumber > 0 - ) { - return true; - } - return 'Please enter a port number between 0 and 65535.'; + if (attempts > MAX_ATTEMPTS) { + logger.info('Too many retries. Please check your available ports.'); + process.exit(1); + } else { + const answers = await inquirer.prompt([ + { + type: 'input', + default: randomPort(), + name: 'newPortNumber', + message: `Port ${config.port} is already in use. Choose a new port number:`, + validate: input => { + const newPortNumber = parseInt(input, 10); + if ( + !Number.isNaN(newPortNumber) && + newPortNumber <= 65535 && + newPortNumber > 0 + ) { + return true; + } + return 'Please enter a port number between 0 and 65535.'; + }, }, - }, - ]); - const server = app.listen( - answers.newPortNumber, - serverStartedSuccessfully - ); - server.on('error', handleServerError); + ]); + attempts += 1; + const server = app.listen( + answers.newPortNumber, + serverStartedSuccessfully + ); + server.on('error', handleServerError); + } } else { reject(error); } From b8d2cda90535077bade0e25657cf73d1284267db Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 21 Apr 2020 10:28:09 +1000 Subject: [PATCH 4/4] Extract port validation function and made sure port numbers aren't below 1025 --- src/commands/start.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/commands/start.ts b/src/commands/start.ts index d2d4dbc2..00d933a5 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -23,6 +23,18 @@ function randomPort() { return Math.floor(Math.random() * (65535 - 1025) + 1025); } +function validatePortNumber(input: string) { + const newPortNumber = parseInt(input, 10); + if ( + !Number.isNaN(newPortNumber) && + newPortNumber <= 65535 && + newPortNumber > 1024 + ) { + return true; + } + return 'Please enter a port number between 1025 and 65535.'; +} + export async function handler( argv: StartCliFlags, externalCliOptions?: ExternalCliOptions @@ -71,17 +83,7 @@ export async function handler( default: randomPort(), name: 'newPortNumber', message: `Port ${config.port} is already in use. Choose a new port number:`, - validate: input => { - const newPortNumber = parseInt(input, 10); - if ( - !Number.isNaN(newPortNumber) && - newPortNumber <= 65535 && - newPortNumber > 0 - ) { - return true; - } - return 'Please enter a port number between 0 and 65535.'; - }, + validate: validatePortNumber, }, ]); attempts += 1;