VNC-275 implement explicit keepalive (#172)
* VNC-275 implement explicit keepalive * VNC-275 graceful disconnect --------- Co-authored-by: Matt McClaskey <matt@kasmweb.com>
This commit is contained in:
parent
0baf17a263
commit
887662b0ad
|
|
@ -1579,7 +1579,7 @@ const UI = {
|
||||||
}, 10000);
|
}, 10000);
|
||||||
} else {
|
} else {
|
||||||
//send keep-alive
|
//send keep-alive
|
||||||
UI.rfb.sendKey(1, null, false);
|
UI.rfb.sendKeepAlive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
@ -1856,7 +1856,11 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnectedRx(event) {
|
disconnectedRx(event) {
|
||||||
parent.postMessage({ action: 'disconnectrx', value: event.detail.reason}, '*' );
|
const detail = event.detail || {};
|
||||||
|
if (detail.serverNotice && detail.serverNotice.graceful) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent.postMessage({ action: 'disconnectrx', value: detail.reason}, '*' );
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleNav(){
|
toggleNav(){
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ export const encodings = {
|
||||||
pseudoEncodingVideoOutTimeLevel1: -1986,
|
pseudoEncodingVideoOutTimeLevel1: -1986,
|
||||||
pseudoEncodingVideoOutTimeLevel100: -1887,
|
pseudoEncodingVideoOutTimeLevel100: -1887,
|
||||||
pseudoEncodingQOI: -1886,
|
pseudoEncodingQOI: -1886,
|
||||||
|
pseudoEncodingKasmDisconnectNotify: -1885,
|
||||||
|
|
||||||
pseudoEncodingVMwareCursor: 0x574d5664,
|
pseudoEncodingVMwareCursor: 0x574d5664,
|
||||||
pseudoEncodingVMwareCursorPosition: 0x574d5666,
|
pseudoEncodingVMwareCursorPosition: 0x574d5666,
|
||||||
|
|
|
||||||
98
core/rfb.js
98
core/rfb.js
|
|
@ -43,6 +43,8 @@ import { toSignedRelative16bit } from './util/int.js';
|
||||||
// How many seconds to wait for a disconnect to finish
|
// How many seconds to wait for a disconnect to finish
|
||||||
const DISCONNECT_TIMEOUT = 3;
|
const DISCONNECT_TIMEOUT = 3;
|
||||||
const DEFAULT_BACKGROUND = 'rgb(40, 40, 40)';
|
const DEFAULT_BACKGROUND = 'rgb(40, 40, 40)';
|
||||||
|
const CLIENT_MSG_TYPE_KEEPALIVE = 184;
|
||||||
|
const SERVER_MSG_TYPE_DISCONNECT_NOTIFY = 185;
|
||||||
|
|
||||||
// Minimum wait (ms) between two mouse moves
|
// Minimum wait (ms) between two mouse moves
|
||||||
const MOUSE_MOVE_DELAY = 17;
|
const MOUSE_MOVE_DELAY = 17;
|
||||||
|
|
@ -105,6 +107,10 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._rfbInitState = '';
|
this._rfbInitState = '';
|
||||||
this._rfbAuthScheme = -1;
|
this._rfbAuthScheme = -1;
|
||||||
this._rfbCleanDisconnect = true;
|
this._rfbCleanDisconnect = true;
|
||||||
|
this._disconnectReason = null;
|
||||||
|
this._disconnectCode = null;
|
||||||
|
this._serverDisconnectNotice = null;
|
||||||
|
this._lastServerDisconnectNotice = null;
|
||||||
|
|
||||||
// Server capabilities
|
// Server capabilities
|
||||||
this._rfbVersion = 0;
|
this._rfbVersion = 0;
|
||||||
|
|
@ -996,6 +1002,16 @@ export default class RFB extends EventTargetMixin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendKeepAlive() {
|
||||||
|
if (this._rfbConnectionState !== 'connected') { return; }
|
||||||
|
|
||||||
|
if (this._isPrimaryDisplay) {
|
||||||
|
RFB.messages.keepAlive(this._sock);
|
||||||
|
} else {
|
||||||
|
this._proxyRFBMessage('keepAlive', []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
this._keyboard.focus();
|
this._keyboard.focus();
|
||||||
}
|
}
|
||||||
|
|
@ -1178,7 +1194,26 @@ export default class RFB extends EventTargetMixin {
|
||||||
}
|
}
|
||||||
msg += ")";
|
msg += ")";
|
||||||
}
|
}
|
||||||
switch (this._rfbConnectionState) {
|
if (typeof e.code === 'number') {
|
||||||
|
this._disconnectCode = e.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.reason) {
|
||||||
|
this._disconnectReason = e.reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._serverDisconnectNotice) {
|
||||||
|
const notice = this._serverDisconnectNotice;
|
||||||
|
if (notice.reason && !this._disconnectReason) {
|
||||||
|
this._disconnectReason = notice.reason;
|
||||||
|
}
|
||||||
|
this._rfbCleanDisconnect = !!notice.graceful;
|
||||||
|
this._lastServerDisconnectNotice = notice;
|
||||||
|
this._serverDisconnectNotice = null;
|
||||||
|
} else if (e.wasClean === false || e.code === 1006) {
|
||||||
|
this._rfbCleanDisconnect = false;
|
||||||
|
}
|
||||||
|
switch (this._rfbConnectionState) {
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
this._fail("Connection closed " + msg);
|
this._fail("Connection closed " + msg);
|
||||||
break;
|
break;
|
||||||
|
|
@ -1723,6 +1758,14 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
Log.Debug("New state '" + state + "', was '" + oldstate + "'.");
|
Log.Debug("New state '" + state + "', was '" + oldstate + "'.");
|
||||||
|
|
||||||
|
if (state === 'connecting') {
|
||||||
|
this._disconnectReason = null;
|
||||||
|
this._disconnectCode = null;
|
||||||
|
this._serverDisconnectNotice = null;
|
||||||
|
this._lastServerDisconnectNotice = null;
|
||||||
|
this._rfbCleanDisconnect = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._disconnTimer && state !== 'disconnecting') {
|
if (this._disconnTimer && state !== 'disconnecting') {
|
||||||
Log.Debug("Clearing disconnect timer");
|
Log.Debug("Clearing disconnect timer");
|
||||||
clearTimeout(this._disconnTimer);
|
clearTimeout(this._disconnTimer);
|
||||||
|
|
@ -1756,7 +1799,13 @@ export default class RFB extends EventTargetMixin {
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
this.dispatchEvent(new CustomEvent(
|
this.dispatchEvent(new CustomEvent(
|
||||||
"disconnect", { detail:
|
"disconnect", { detail:
|
||||||
{ clean: this._rfbCleanDisconnect } }));
|
{ clean: this._rfbCleanDisconnect,
|
||||||
|
reason: this._disconnectReason,
|
||||||
|
code: this._disconnectCode,
|
||||||
|
serverNotice: this._lastServerDisconnectNotice } }));
|
||||||
|
this._disconnectReason = null;
|
||||||
|
this._disconnectCode = null;
|
||||||
|
this._lastServerDisconnectNotice = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1781,6 +1830,9 @@ export default class RFB extends EventTargetMixin {
|
||||||
Log.Error("RFB failure: " + details);
|
Log.Error("RFB failure: " + details);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this._disconnectReason = details;
|
||||||
|
this._disconnectCode = null;
|
||||||
|
this._serverDisconnectNotice = null;
|
||||||
this._rfbCleanDisconnect = false; //This is sent to the UI
|
this._rfbCleanDisconnect = false; //This is sent to the UI
|
||||||
|
|
||||||
// Transition to disconnected without waiting for socket to close
|
// Transition to disconnected without waiting for socket to close
|
||||||
|
|
@ -1890,6 +1942,9 @@ export default class RFB extends EventTargetMixin {
|
||||||
RFB.messages.pointerEvent(this._sock, this._mousePos.x, this._mousePos.y, 0, event.data.args[2], event.data.args[3]);
|
RFB.messages.pointerEvent(this._sock, this._mousePos.x, this._mousePos.y, 0, event.data.args[2], event.data.args[3]);
|
||||||
this._setLastActive();
|
this._setLastActive();
|
||||||
break;
|
break;
|
||||||
|
case 'keepAlive':
|
||||||
|
RFB.messages.keepAlive(this._sock);
|
||||||
|
break;
|
||||||
case 'keyEvent':
|
case 'keyEvent':
|
||||||
RFB.messages.keyEvent(this._sock, ...event.data.args);
|
RFB.messages.keyEvent(this._sock, ...event.data.args);
|
||||||
this._setLastActive();
|
this._setLastActive();
|
||||||
|
|
@ -3163,6 +3218,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
encs.push(encodings.pseudoEncodingContinuousUpdates);
|
encs.push(encodings.pseudoEncodingContinuousUpdates);
|
||||||
encs.push(encodings.pseudoEncodingDesktopName);
|
encs.push(encodings.pseudoEncodingDesktopName);
|
||||||
encs.push(encodings.pseudoEncodingExtendedClipboard);
|
encs.push(encodings.pseudoEncodingExtendedClipboard);
|
||||||
|
encs.push(encodings.pseudoEncodingKasmDisconnectNotify);
|
||||||
if (this._hasWebp())
|
if (this._hasWebp())
|
||||||
encs.push(encodings.pseudoEncodingWEBP);
|
encs.push(encodings.pseudoEncodingWEBP);
|
||||||
if (this._enableQOI)
|
if (this._enableQOI)
|
||||||
|
|
@ -3692,6 +3748,8 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
case 183: // KASM unix relay data
|
case 183: // KASM unix relay data
|
||||||
return this._handleUnixRelay();
|
return this._handleUnixRelay();
|
||||||
|
case SERVER_MSG_TYPE_DISCONNECT_NOTIFY: // KASM disconnect notice
|
||||||
|
return this._handleDisconnectNotify();
|
||||||
|
|
||||||
case 248: // ServerFence
|
case 248: // ServerFence
|
||||||
return this._handleServerFenceMsg();
|
return this._handleServerFenceMsg();
|
||||||
|
|
@ -3857,6 +3915,32 @@ export default class RFB extends EventTargetMixin {
|
||||||
processRelay && processRelay(payload);
|
processRelay && processRelay(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleDisconnectNotify() {
|
||||||
|
if (this._sock.rQwait("DisconnectNotify header", 8, 1)) { return false; }
|
||||||
|
const flags = this._sock.rQshift8();
|
||||||
|
this._sock.rQskipBytes(3);
|
||||||
|
const reasonLength = this._sock.rQshift32();
|
||||||
|
if (reasonLength > 0 && this._sock.rQwait("DisconnectNotify reason", reasonLength, 8)) { return false; }
|
||||||
|
|
||||||
|
let reason = null;
|
||||||
|
if (reasonLength > 0) {
|
||||||
|
reason = this._sock.rQshiftStr(reasonLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
const graceful = (flags & 0x1) !== 0;
|
||||||
|
this._serverDisconnectNotice = { flags, reason, graceful };
|
||||||
|
|
||||||
|
if (reason !== null) {
|
||||||
|
this._disconnectReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (graceful) {
|
||||||
|
this._rfbCleanDisconnect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
_framebufferUpdate() {
|
_framebufferUpdate() {
|
||||||
if (this._FBU.rects === 0) {
|
if (this._FBU.rects === 0) {
|
||||||
if (this._sock.rQwait("FBU header", 3, 1)) { return false; }
|
if (this._sock.rQwait("FBU header", 3, 1)) { return false; }
|
||||||
|
|
@ -4425,6 +4509,16 @@ RFB.messages = {
|
||||||
sock.flush();
|
sock.flush();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
keepAlive(sock) {
|
||||||
|
const buff = sock._sQ;
|
||||||
|
const offset = sock._sQlen;
|
||||||
|
|
||||||
|
buff[offset] = CLIENT_MSG_TYPE_KEEPALIVE;
|
||||||
|
|
||||||
|
sock._sQlen += 1;
|
||||||
|
sock.flush();
|
||||||
|
},
|
||||||
|
|
||||||
// Used to build Notify and Request data.
|
// Used to build Notify and Request data.
|
||||||
_buildExtendedClipboardFlags(actions, formats) {
|
_buildExtendedClipboardFlags(actions, formats) {
|
||||||
let data = new Uint8Array(4);
|
let data = new Uint8Array(4);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue