From 3a6a63cde31dfb58131d1f655636792373ecec79 Mon Sep 17 00:00:00 2001 From: Matt McClaskey Date: Fri, 11 Nov 2022 09:29:52 -0500 Subject: [PATCH] KASM-3102 Allow setting the idle timeout (#41) refactor idle timeout to use date diff --- app/ui.js | 86 ++++++++++++++++++++++++++--------------------------- core/rfb.js | 24 +++++++-------- 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/app/ui.js b/app/ui.js index 175f9b45..f43a9355 100644 --- a/app/ui.js +++ b/app/ui.js @@ -46,9 +46,6 @@ import * as WebUtil from "./webutil.js"; const PAGE_TITLE = "KasmVNC"; -var delta = 500; -var lastKeypressTime = 0; -var lastKeypressCode = -1; var currentEventCount = -1; var idleCounter = 0; @@ -1423,46 +1420,41 @@ const UI = { /**** * Kasm VDI specific *****/ - if (WebUtil.isInsideKasmVDI()) - { - if (window.addEventListener) { // Mozilla, Netscape, Firefox - //window.addEventListener('load', WindowLoad, false); - window.addEventListener('message', UI.receiveMessage, false); - } else if (window.attachEvent) { //IE - window.attachEvent('onload', WindowLoad); - window.attachEvent('message', UI.receiveMessage); - } - if (UI.rfb.clipboardDown){ - UI.rfb.addEventListener("clipboard", UI.clipboardRx); - } - UI.rfb.addEventListener("disconnect", UI.disconnectedRx); - document.getElementById('noVNC_control_bar_anchor').setAttribute('style', 'display: none'); - - //keep alive for websocket connection to stay open, since we may not control reverse proxies - //send a keep alive within a window that we control - setInterval(function() { - if (currentEventCount!=UI.rfb.sentEventsCounter) { - idleCounter=0; - currentEventCount=UI.rfb.sentEventsCounter; - } else { - idleCounter+=1; - var idleDisconnect = parseFloat(UI.rfb.idleDisconnect); - if ((idleCounter / 2) >= idleDisconnect) { - //idle for longer than the limit, disconnect - currentEventCount = -1; - idleCounter = 0; - parent.postMessage({ action: 'idle_session_timeout', value: 'Idle session timeout exceeded'}, '*' ); - //UI.rfb.disconnect(); - } else { - //send a keep alive - UI.rfb.sendKey(1, null, false); - currentEventCount=UI.rfb.sentEventsCounter; - } - } - }, 30000); - } else { + if (WebUtil.isInsideKasmVDI()) { + if (window.addEventListener) { // Mozilla, Netscape, Firefox + //window.addEventListener('load', WindowLoad, false); + window.addEventListener('message', UI.receiveMessage, false); + } else if (window.attachEvent) { //IE + window.attachEvent('onload', WindowLoad); + window.attachEvent('message', UI.receiveMessage); + } + if (UI.rfb.clipboardDown){ + UI.rfb.addEventListener("clipboard", UI.clipboardRx); + } + UI.rfb.addEventListener("disconnect", UI.disconnectedRx); + document.getElementById('noVNC_control_bar_anchor').setAttribute('style', 'display: none'); + + //keep alive for websocket connection to stay open, since we may not control reverse proxies + //send a keep alive within a window that we control + UI._sessionTimeoutInterval = setInterval(function() { + + const timeSinceLastActivityInS = (Date.now() - UI.rfb.lastActiveAt) / 1000; + let idleDisconnectInS = 1200; //20 minute default + if (Number.isFinite(UI.rfb.idleDisconnect)) { + idleDisconnectInS = parseFloat(UI.rfb.idleDisconnect) * 60; + } + console.log("Current idle time " + timeSinceLastActivityInS + " max value is " + idleDisconnectInS); + + if (timeSinceLastActivityInS > idleDisconnectInS) { + parent.postMessage({ action: 'idle_session_timeout', value: 'Idle session timeout exceeded'}, '*' ); + } else { + //send keep-alive + UI.rfb.sendKey(1, null, false); + } + }, 5000); + } else { document.getElementById('noVNC_status').style.visibility = "visible"; - } + } //key events for KasmVNC control document.addEventListener('keyup', function (event) { @@ -1493,7 +1485,7 @@ const UI = { UI.updateVisualState('disconnecting'); - // Don't display the connection settings until we're actually disconnected + clearInterval(UI._sessionTimeoutInterval); }, reconnect() { @@ -1680,6 +1672,14 @@ const UI = { UI.toggleWebRTC(); } break; + + case 'set_idle_timeout': + //message value in seconds + const idle_timeout_min = Math.ceil(event.data.value / 60); + UI.forceSetting('idle_disconnect', idle_timeout_min, false); + UI.rfb.idleDisconnect = idle_timeout_min; + console.log(`Updated the idle timeout to ${event.data.value}s`); + break; } } }, diff --git a/core/rfb.js b/core/rfb.js index 8c44dfa4..063603a7 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -340,7 +340,7 @@ export default class RFB extends EventTargetMixin { this.dragViewport = false; this.focusOnClick = true; - this.sentEventsCounter = 0; + this.lastActiveAt = Date.now(); this._viewOnly = false; this._clipViewport = false; @@ -813,8 +813,10 @@ export default class RFB extends EventTargetMixin { sendKey(keysym, code, down) { if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } - this.sentEventsCounter+=1; - + if (code !== null) { + this._setLastActive(); + } + if (down === undefined) { this.sendKey(keysym, code, true); this.sendKey(keysym, code, false); @@ -872,8 +874,6 @@ export default class RFB extends EventTargetMixin { if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } if (!(typeof text === 'string' && text.length > 0)) { return; } - this.sentEventsCounter+=1; - let data = new TextEncoder().encode(text); let h = hashUInt8Array(data); @@ -894,7 +894,6 @@ export default class RFB extends EventTargetMixin { async clipboardPasteDataFrom(clipdata) { if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } - this.sentEventsCounter+=1; let dataset = []; let mimes = []; @@ -965,6 +964,10 @@ export default class RFB extends EventTargetMixin { // ===== PRIVATE METHODS ===== + _setLastActive() { + this.lastActiveAt = Date.now(); + } + _changeTransitConnectionState(value) { Log.Info("Transit state change from " + this._transitConnectionState.toString() + ' to ' + value.toString()); this._transitConnectionState = value; @@ -977,7 +980,7 @@ export default class RFB extends EventTargetMixin { try { Log.Info(`connecting to ${this._url}`); this._sock.open(this._url, this._wsProtocols); - this.sentEventsCounter+=1; + this._setLastActive(); } catch (e) { if (e.name === 'SyntaxError') { this._fail("Invalid host or port (" + e + ")"); @@ -1301,8 +1304,6 @@ export default class RFB extends EventTargetMixin { Math.floor(size.w), Math.floor(size.h), this._screenID, this._screenFlags); - this.sentEventsCounter+=1; - Log.Debug('Requested new desktop size: ' + size.w + 'x' + size.h); } @@ -1582,6 +1583,7 @@ export default class RFB extends EventTargetMixin { this._canvas); } + this._setLastActive(); const mappedButton = this.mouseButtonMapper.get(ev.button); switch (ev.type) { case 'mousedown': @@ -1633,13 +1635,10 @@ export default class RFB extends EventTargetMixin { // Otherwise we treat this as a mouse click event. // Send the button down event here, as the button up // event is sent at the end of this function. - this.sentEventsCounter+=1; this._sendMouse(x, y, bmask); } } - this.sentEventsCounter+=1; - // Flush waiting move event first if (this._mouseMoveTimer !== null) { clearTimeout(this._mouseMoveTimer); @@ -3460,7 +3459,6 @@ export default class RFB extends EventTargetMixin { RFB.messages.setMaxVideoResolution(this._sock, this._maxVideoResolutionX, this._maxVideoResolutionY); - this.sentEventsCounter+=1; } this._sock.rQskipBytes(1); // number-of-screens