diff --git a/lib/cli/device/index.js b/lib/cli/device/index.js index e1158a83..7fdbc359 100644 --- a/lib/cli/device/index.js +++ b/lib/cli/device/index.js @@ -85,6 +85,12 @@ module.exports.builder = function(yargs) { , type: 'number' , default: process.env.SCREEN_JPEG_QUALITY || 80 }) + .option('screen-ping-interval', { + describe: 'The interval at which to send ping messages to keep the ' + + 'screen WebSocket alive.' + , type: 'number' + , default: 30000 + }) .option('screen-port', { describe: 'Port allocated to the screen WebSocket.' , type: 'number' @@ -129,9 +135,10 @@ module.exports.handler = function(argv) { , storageUrl: argv.storageUrl , adbHost: argv.adbHost , adbPort: argv.adbPort - , screenWsUrlPattern: argv.screenWsUrlPattern , screenJpegQuality: argv.screenJpegQuality + , screenPingInterval: argv.screenPingInterval , screenPort: argv.screenPort + , screenWsUrlPattern: argv.screenWsUrlPattern , connectUrlPattern: argv.connectUrlPattern , connectPort: argv.connectPort , vncPort: argv.vncPort diff --git a/lib/cli/provider/index.js b/lib/cli/provider/index.js index 7e044b68..e37d2670 100644 --- a/lib/cli/provider/index.js +++ b/lib/cli/provider/index.js @@ -104,6 +104,12 @@ module.exports.builder = function(yargs) { , type: 'number' , default: process.env.SCREEN_JPEG_QUALITY || 80 }) + .option('screen-ping-interval', { + describe: 'The interval at which to send ping messages to keep the ' + + 'screen WebSocket alive.' + , type: 'number' + , default: 30000 + }) .option('screen-ws-url-pattern', { describe: 'The URL pattern to use for the screen WebSocket.' , type: 'string' @@ -163,8 +169,9 @@ module.exports.handler = function(argv) { , '--storage-url', argv.storageUrl , '--adb-host', argv.adbHost , '--adb-port', argv.adbPort - , '--screen-ws-url-pattern', argv.screenWsUrlPattern , '--screen-jpeg-quality', argv.screenJpegQuality + , '--screen-ping-interval', argv.screenPingInterval + , '--screen-ws-url-pattern', argv.screenWsUrlPattern , '--connect-url-pattern', argv.connectUrlPattern , '--heartbeat-interval', argv.heartbeatInterval , '--boot-complete-timeout', argv.bootCompleteTimeout diff --git a/lib/units/device/plugins/screen/stream.js b/lib/units/device/plugins/screen/stream.js index 2da19b45..94f45289 100644 --- a/lib/units/device/plugins/screen/stream.js +++ b/lib/units/device/plugins/screen/stream.js @@ -502,22 +502,18 @@ module.exports = syrup.serial() wss.on('connection', function(ws) { var id = uuid.v4() + var pingTimer - function wsStartNotifier() { + function send(message, options) { return new Promise(function(resolve, reject) { - var message = util.format( - 'start %s' - , JSON.stringify(frameProducer.banner) - ) - switch (ws.readyState) { case WebSocket.OPENING: // This should never happen. - log.warn('Unable to send banner to OPENING client "%s"', id) + log.warn('Unable to send to OPENING client "%s"', id) break case WebSocket.OPEN: // This is what SHOULD happen. - ws.send(message, function(err) { + ws.send(message, options, function(err) { return err ? reject(err) : resolve() }) break @@ -527,41 +523,37 @@ module.exports = syrup.serial() break case WebSocket.CLOSED: // This should never happen. - log.warn('Unable to send banner to CLOSED client "%s"', id) + log.warn('Unable to send to CLOSED client "%s"', id) + clearInterval(pingTimer) broadcastSet.remove(id) break } }) } + function wsStartNotifier() { + return send(util.format( + 'start %s' + , JSON.stringify(frameProducer.banner) + )) + } + + function wsPingNotifier() { + return send('ping') + } + function wsFrameNotifier(frame) { - return new Promise(function(resolve, reject) { - switch (ws.readyState) { - case WebSocket.OPENING: - // This should never happen. - return reject(new Error(util.format( - 'Unable to send frame to OPENING client "%s"', id))) - case WebSocket.OPEN: - // This is what SHOULD happen. - ws.send(frame, { - binary: true - }, function(err) { - return err ? reject(err) : resolve() - }) - return - case WebSocket.CLOSING: - // Ok, a 'close' event should remove the client from the set - // soon. - return - case WebSocket.CLOSED: - // This should never happen. - broadcastSet.remove(id) - return reject(new Error(util.format( - 'Unable to send frame to CLOSED client "%s"', id))) - } + return send(frame, { + binary: true }) } + // Sending a ping message every now and then makes sure that + // reverse proxies like nginx don't time out the connection [1]. + // + // [1] http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout + pingTimer = setInterval(wsPingNotifier, options.screenPingInterval) + ws.on('message', function(data) { var match = /^(on|off|(size) ([0-9]+)x([0-9]+))$/.exec(data) if (match) { @@ -574,6 +566,7 @@ module.exports = syrup.serial() break case 'off': broadcastSet.remove(id) + // Keep pinging even when the screen is off. break case 'size': frameProducer.updateProjection( @@ -584,6 +577,7 @@ module.exports = syrup.serial() }) ws.on('close', function() { + clearInterval(pingTimer) broadcastSet.remove(id) }) })