From a0958f03f489bd3aadb4095030a8f72a35d8c9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Sat, 25 May 2019 20:19:24 +0200 Subject: [PATCH] fix(server): preserve random port when restarting (#5793) --- packages/server/src/listener.js | 13 +++++++++---- packages/server/test/listener.test.js | 28 ++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/server/src/listener.js b/packages/server/src/listener.js index dfbb0e549e..62ff1f8e80 100644 --- a/packages/server/src/listener.js +++ b/packages/server/src/listener.js @@ -5,6 +5,8 @@ import ip from 'ip' import consola from 'consola' import pify from 'pify' +let RANDOM_PORT = '0' + export default class Listener { constructor({ port, host, socket, https, app, dev, baseURL }) { // Options @@ -90,7 +92,7 @@ export default class Listener { this.listening = true } - serverErrorHandler(error) { + async serverErrorHandler(error) { // Detect if port is not available const addressInUse = error.code === 'EADDRINUSE' @@ -100,11 +102,14 @@ export default class Listener { error.message = `Address \`${address}\` is already in use.` // Listen to a random port on dev as a fallback - if (this.dev && !this.socket && this.port !== '0') { + if (this.dev && !this.socket && this.port !== RANDOM_PORT) { consola.warn(error.message) consola.info('Trying a random port...') - this.port = '0' - return this.close().then(() => this.listen()) + this.port = RANDOM_PORT + await this.close() + await this.listen() + RANDOM_PORT = this.port + return } } diff --git a/packages/server/test/listener.test.js b/packages/server/test/listener.test.js index f378421579..860daaf146 100644 --- a/packages/server/test/listener.test.js +++ b/packages/server/test/listener.test.js @@ -319,7 +319,7 @@ describe('server: listener', () => { const listener = new Listener({}) const error = new Error('server error') - expect(() => listener.serverErrorHandler(error)).toThrow(error) + expect(listener.serverErrorHandler(error)).rejects.toThrow(error) }) test('should throw address in use error', () => { @@ -329,7 +329,7 @@ describe('server: listener', () => { const addressInUse = new Error() addressInUse.code = 'EADDRINUSE' - expect(() => listener.serverErrorHandler(addressInUse)).toThrow('Address `localhost:3000` is already in use.') + expect(listener.serverErrorHandler(addressInUse)).rejects.toThrow('Address `localhost:3000` is already in use.') }) test('should throw address in use error for socket', () => { @@ -338,7 +338,7 @@ describe('server: listener', () => { const addressInUse = new Error() addressInUse.code = 'EADDRINUSE' - expect(() => listener.serverErrorHandler(addressInUse)).toThrow('Address `nuxt.socket` is already in use.') + expect(listener.serverErrorHandler(addressInUse)).rejects.toThrow('Address `nuxt.socket` is already in use.') }) test('should fallback to a random port in address in use error', async () => { @@ -362,6 +362,28 @@ describe('server: listener', () => { expect(listener.listen).toBeCalledTimes(1) }) + test('should reuse last random port', async () => { + const listener = new Listener({ dev: true, host: 'localhost', port: 3000 }) + listener.host = 'localhost' + listener.close = jest.fn() + listener.listen = function () { + if (this.port === '0') { + this.port = Math.random() + } + } + + const addressInUse = new Error() + addressInUse.code = 'EADDRINUSE' + + await listener.serverErrorHandler(addressInUse).catch(() => { }) + const port1 = listener.port + await listener.serverErrorHandler(addressInUse).catch(() => { }) + const port2 = listener.port + + expect(port1).not.toBe(3000) + expect(port2).toBe(port1) + }) + test('should close server', async () => { const listener = new Listener({}) const server = mockServer()