implemented timeout over websocket connection

This commit is contained in:
tigerbot 2017-04-10 11:39:27 -06:00
parent 304c3200a0
commit 06867530d8
2 changed files with 59 additions and 9 deletions

View File

@ -51,6 +51,6 @@
"jsonwebtoken": "^7.1.9", "jsonwebtoken": "^7.1.9",
"sni": "^1.0.0", "sni": "^1.0.0",
"tunnel-packer": "^1.1.0", "tunnel-packer": "^1.1.0",
"ws": "^1.1.1" "ws": "^2.2.3"
} }
} }

View File

@ -7,6 +7,9 @@ var Packer = require('tunnel-packer');
function run(copts) { function run(copts) {
var tunnelUrl = copts.stunneld.replace(/\/$/, '') + '/?access_token=' + copts.token; var tunnelUrl = copts.stunneld.replace(/\/$/, '') + '/?access_token=' + copts.token;
var activityTimeout = copts.activityTimeout || 2*60*1000;
var pongTimeout = copts.pongTimeout || 10*1000;
var wstunneler; var wstunneler;
var authenticated = false; var authenticated = false;
@ -164,23 +167,65 @@ function run(copts) {
}; };
var retry = true; var retry = true;
var retryTimeout; var lastActivity;
var timeoutId;
var wsHandlers = { var wsHandlers = {
onOpen: function () { refreshTimeout: function () {
lastActivity = Date.now();
}
, checkTimeout: function () {
if (!wstunneler) {
console.warn('checkTimeout called when websocket already closed');
return;
}
// Determine how long the connection has been "silent", ie no activity.
var silent = Date.now() - lastActivity;
// If we have had activity within the last activityTimeout then all we need to do is
// call this function again at the soonest time when the connection could be timed out.
if (silent < activityTimeout) {
timeoutId = setTimeout(wsHandlers.checkTimeout, activityTimeout-silent);
}
// Otherwise we check to see if the pong has also timed out, and if not we send a ping
// and call this function again when the pong will have timed out.
else if (silent < activityTimeout + pongTimeout) {
console.log('pinging tunnel server');
try {
wstunneler.ping();
} catch (err) {
console.warn('failed to ping tunnel server', err);
}
timeoutId = setTimeout(wsHandlers.checkTimeout, pongTimeout);
}
// Last case means the ping we sent before didn't get a response soon enough, so we
// need to close the websocket connection.
else {
console.log('connection timed out');
wstunneler.close(1000, 'connection timeout');
}
}
, onOpen: function () {
console.info("[open] connected to '" + copts.stunneld + "'"); console.info("[open] connected to '" + copts.stunneld + "'");
wsHandlers.refreshTimeout();
timeoutId = setTimeout(wsHandlers.checkTimeout, activityTimeout);
} }
, onClose: function () { , onClose: function () {
console.log('ON CLOSE'); console.log('ON CLOSE');
clearTimeout(timeoutId);
wstunneler = null; wstunneler = null;
clientHandlers.closeAll(); clientHandlers.closeAll();
if (!authenticated) { if (!authenticated) {
console.info('[close] failed on first attempt... check authentication.'); console.info('[close] failed on first attempt... check authentication.');
timeoutId = null;
} }
else if (retry) { else if (retry) {
console.info('[retry] disconnected and waiting...'); console.info('[retry] disconnected and waiting...');
retryTimeout = setTimeout(connect, 5000); timeoutId = setTimeout(connect, 5000);
} }
} }
@ -192,7 +237,7 @@ function run(copts) {
, sendMessage: function (msg) { , sendMessage: function (msg) {
if (wstunneler) { if (wstunneler) {
try { try {
wstunneler.send(msg, {binary: true}) wstunneler.send(msg, {binary: true});
} catch (err) { } catch (err) {
console.warn('[sendMessage] error sending websocket message', err); console.warn('[sendMessage] error sending websocket message', err);
} }
@ -204,7 +249,7 @@ function run(copts) {
if (!retry) { if (!retry) {
return; return;
} }
retryTimeout = null; timeoutId = null;
var machine = require('tunnel-packer').create(packerHandlers); var machine = require('tunnel-packer').create(packerHandlers);
console.info("[connect] '" + copts.stunneld + "'"); console.info("[connect] '" + copts.stunneld + "'");
@ -212,7 +257,12 @@ function run(copts) {
wstunneler.on('open', wsHandlers.onOpen); wstunneler.on('open', wsHandlers.onOpen);
wstunneler.on('close', wsHandlers.onClose); wstunneler.on('close', wsHandlers.onClose);
wstunneler.on('error', wsHandlers.onError); wstunneler.on('error', wsHandlers.onError);
// Our library will automatically handle sending the pong respose to ping requests.
wstunneler.on('ping', wsHandlers.refreshTimeout);
wstunneler.on('pong', wsHandlers.refreshTimeout);
wstunneler.on('message', function (data, flags) { wstunneler.on('message', function (data, flags) {
wsHandlers.refreshTimeout();
if (data.error || '{' === data[0]) { if (data.error || '{' === data[0]) {
console.log(data); console.log(data);
return; return;
@ -231,9 +281,9 @@ function run(copts) {
process.removeListener('SIGINT', sigHandler); process.removeListener('SIGINT', sigHandler);
retry = false; retry = false;
if (retryTimeout) { if (timeoutId) {
clearTimeout(retryTimeout); clearTimeout(timeoutId);
retryTimeout = null; timeoutId = null;
} }
if (wstunneler) { if (wstunneler) {