Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finished up work started in PR (#263) auto incrementing the port if it is already in use #278

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,31 +194,33 @@ _Consult the [migration guide](MIGRATION_GUIDE.md) for help updating reload acro

##### Table of options for reload opts parameter

| Parameter Name | Type | Description | Optional | Default |
|--------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------|
| Parameter Name | Type | Description | Optional | Default |
|:------------------------:|:-------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------:|:-------:|
| port | number | Port to run reload on. | ✓ | 9856 |
| webSocketServerWaitStart | boolean | When enabled will delay starting and opening WebSocket server when requiring reload. After enabling use the startWebSocketServer function returned in the object provided by the API to start the WebSocket. Note: Failing to call the returned function with this option enabled will cause reload not to work. See return API for more information | ✓ | FALSE |
| autoIncrementPort | boolean | Auto increments the reload port if the port is already in use | ✓ | TRUE |
| webSocketServerWaitStart | boolean | When enabled will delay starting and opening WebSocket server when requiring reload. After enabling use the startWebSocketServer function returned in the object provided by the API to start the WebSocket. Note: Failing to call the returned function with this option enabled will cause reload not to work. See return API for more information | ✓ | FALSE |
| route | string | Route that reload should use to serve the client side script file. Changing the route will require the script tag URL to change. Reload will always strip any occurrence of reload.js and append reload.js for you. This is to ensure case, order, and use of / is correct. For example specifying newRoutePath as the route will give reload a route of newRoutePath/reload.js. (Recommend not modifying). | ✓ | reload |
| forceWss | boolean | Forces reload client connections to always use `wss` (secure websockerts) even when the window location is HTTP | ✓ | FALSE |
| forceWss | boolean | Forces reload client connections to always use wss (secure websockerts) even when the window location is HTTP | ✓ | FALSE |
| https | object | HTTP options object. When defined runs reload in HTTPS mode | ✓ | {} |
| https.certAndKey | object | Object that holds configuration for HTTPS key and cert configuration | ✓ | {} |
| https.certAndKey.key | string | File path to HTTP key (not optional when defining an HTTPS object) | | null |
| https.certAndKey.cert | string | File path to HTTP cert (not optional when defining an HTTPS object) | | null |
| https.p12 | object | Object that holds configuration for HTTPS P12 configuration | ✓ | {} |
| https.p12.p12Path | string | File path or file contents as string (Not optional when using P12 configuration | | null |
| https.passphrase | string | Shared passphrase used for a single private key and/or p12. | ✓ | null |
| https.passphrase | string | Shared passphrase used for a single private key and/or p12. | ✓ | null |
| verbose | boolean | If set to true, will show logging on the server and client side. | ✓ | FALSE |

#### Returns

An **object** containing:

| Name | Type | Description |
|----------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| reload | function | A function that when called reloads all connected clients. For more information see manually firing server-side reload events. |
| startWebSocketServer | function | Returns a promise. Starts and opens the WebSocket server required for reload. Only **defined** when using the optional parameter `webSocketServerWaitStart`. Read the [parameters](#parameters) for more information |
| closeServer | function | Returns a promise. Closes Reload WebSocket server |
| wss | object | Web socket server |
| Name | Type | Description |
|:--------------------:|:--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| reload | function | A function that when called reloads all connected clients. For more information see manually firing server-side reload events. |
| startWebSocketServer | function | Returns a promise. Starts and opens the WebSocket server required for reload. Only defined when using the optional parameter webSocketServerWaitStart. Read the parameters for more information |
| closeServer | function | Returns a promise. Closes Reload WebSocket server |
| port | number | Port reload is running on. Useful when reload auto increments the port if the default/specified port is in use |
| wss | object | Web socket server |

Using reload as a command line application
---
Expand Down
47 changes: 38 additions & 9 deletions lib/reload.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ module.exports = function reload (app, opts, server) {

return new Promise(function (resolve, reject) {
// Parameters variables
const port = opts.port || 9856
var port = opts.port || 9856
alallier marked this conversation as resolved.
Show resolved Hide resolved
const httpsOption = opts.https || null
const httpServerOrPort = server || port
const forceWss = opts.forceWss || false
const autoIncrementPort = opts.autoIncrementPort === false ? false : true // eslint-disable-line no-unneeded-ternary
const verboseLogging = opts.verbose || false
const webSocketServerWaitStart = opts.webSocketServerWaitStart || false

Expand All @@ -28,9 +29,11 @@ module.exports = function reload (app, opts, server) {
let wss

// General variables
const socketPortSpecified = server ? null : port
const commandLineUsingServer = server
const connections = {}
let httpOrHttpsServer
let hadToIncrement = false
var incrementTimer

if (argumentCache[0] === undefined) {
return reject(new Error('Lack of/invalid arguments provided to reload'))
Expand Down Expand Up @@ -96,9 +99,8 @@ module.exports = function reload (app, opts, server) {
reloadCode = reloadCode.replace('verboseLogging = false', 'verboseLogging = true')
}

const webSocketString = forceWss ? 'wss://$3' : 'ws$2://$3'

reloadCode = reloadCode.replace('socketUrl.replace()', 'socketUrl.replace(/(^http(s?):\\/\\/)(.*:)(.*)/,' + (socketPortSpecified ? '\'' + webSocketString + socketPortSpecified : '\'' + webSocketString + '$4') + '\')')
const webSocketString = forceWss ? 'wss://$3' : 'ws$2://$3$4'
reloadCode = reloadCode.replace('socketUrl.replace()', `socketUrl.replace(/(^http(s?):\\/\\/)(.*:)(.*)/, '${webSocketString}')`)
}

// Websocket server setup
Expand All @@ -111,7 +113,7 @@ module.exports = function reload (app, opts, server) {
console.log('Starting WebSocket Server')
}

if (socketPortSpecified) { // Use custom user specified port
if (!commandLineUsingServer) { // Default mode for reload. Starts server on default/specified port. The else is used for command line reload and attaches to a server
wss = new WebSocketServer({ noServer: true })

if (httpsOption) { // HTTPS
Expand Down Expand Up @@ -165,8 +167,23 @@ module.exports = function reload (app, opts, server) {
httpOrHttpsServer = http.createServer()
}

httpOrHttpsServer.listen(port, function () {
resolve(getReloadReturn())
runServer(port)

// Wait to see if a EADDRINUSE port already in use error occurs so we can increment before resolving the promise. Time is restarted if error case hits.
incrementTimer = setTimeout(resolvePromise, 100)

httpOrHttpsServer.on('error', function (err) {
/* istanbul ignore else */
if (err.code === 'EADDRINUSE' && autoIncrementPort) {
hadToIncrement = true
port = port + 1
clearTimeout(incrementTimer)
incrementTimer = setTimeout(resolvePromise, 100)
runServer(port)
} else {
clearTimeout(incrementTimer)
reject(err)
}
})

httpOrHttpsServer.on('upgrade', (request, socket, head) => {
Expand All @@ -191,6 +208,17 @@ module.exports = function reload (app, opts, server) {
})
}

function resolvePromise () {
if (hadToIncrement && verboseLogging) {
console.log('Incremented port number. Server running on:', port)
}
resolve(getReloadReturn())
}

function runServer (port) {
httpOrHttpsServer.listen(port)
}

function sendMessage (message) {
if (verboseLogging) {
console.log('Sending message to ' + (wss.clients.size) + ' connection(s): ' + message)
Expand Down Expand Up @@ -249,7 +277,8 @@ module.exports = function reload (app, opts, server) {
}
httpOrHttpsServer.close(resolve)
})
}
},
port: port
}

// Only define the function and make it available if the WebSocket is waiting in the first place
Expand Down
40 changes: 40 additions & 0 deletions test/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,46 @@ describe('API', function () {
assert.strictEqual(result, true, 'Could not connect to WebSocket')
})

it('Should increment if default/specified port is unavailable', async () => {
const net = require('net')
const server = net.createServer()

server.listen(9856)

var app = express()

try {
var reloadReturned = await reload(app)
} catch (err) {

}

var result = await helperFunction.testWebSocket(reloadReturned.port)

await helperFunction.closeReloadSocket(reloadReturned)

assert.strictEqual(result, true, 'Could not connect to WebSocket')

server.close()
})

it('Should *not* increment if default/specified port is unavailable and autoIncrementOption is set to false', async () => {
const net = require('net')
const server = net.createServer()

server.listen(9856)

var app = express()

try {
await reload(app, { autoIncrementPort: false })
} catch (err) {
assert(err)
}

server.close()
})

it('Should error if unable to attach route to express app', async () => {
try {
await reload(function () {})
Expand Down
29 changes: 29 additions & 0 deletions test/api/verbose.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,35 @@ describe('Verbose', function () {
console.error.restore()
})

it('Should verbose log if increment was required if default/specified port is unavailable', async () => {
sinon.stub(console, 'log').returns(0)
sinon.stub(console, 'error').returns(0)

const net = require('net')
const server = net.createServer()

server.listen(9856)

var app = express()

try {
var reloadReturned = await reload(app, { verbose: true })
} catch (err) {

}

const foundLog = helperFunction.checkForConsoleLog(console.log.args, 'Incremented port number. Server running on:', reloadReturned.port)

await helperFunction.closeReloadSocket(reloadReturned)

server.close()

assert(foundLog, 'Incremented port number. Server running on: ' + reloadReturned.port + ' not found in console logging')

console.log.restore()
console.error.restore()
})

it('Should error if verbose logging option is not a boolean', async () => {
var app = express()

Expand Down