added udp toggle in UI, added err handling (#33)

Co-authored-by: Matt McClaskey <matt@kasmweb.com>
This commit is contained in:
Matt McClaskey 2022-09-07 05:40:29 -04:00 committed by GitHub
parent 5a8d8f24b4
commit 842d7ad938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 9 deletions

View File

@ -30,8 +30,8 @@ window.updateSetting = (name, value) => {
} }
} }
import "core-js/stable"; //import "core-js/stable";
import "regenerator-runtime/runtime"; //import "regenerator-runtime/runtime";
import * as Log from '../core/util/logging.js'; import * as Log from '../core/util/logging.js';
import _, { l10n } from './localization.js'; import _, { l10n } from './localization.js';
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox, isWindows, isIOS, supportsPointerLock } import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox, isWindows, isIOS, supportsPointerLock }
@ -245,7 +245,8 @@ const UI = {
UI.initSetting('toggle_control_panel', false); UI.initSetting('toggle_control_panel', false);
UI.initSetting('enable_perf_stats', false); UI.initSetting('enable_perf_stats', false);
UI.initSetting('virtual_keyboard_visible', false); UI.initSetting('virtual_keyboard_visible', false);
UI.initSetting('enable_ime', false) UI.initSetting('enable_ime', false);
UI.initSetting('enable_webrtc', true);
UI.toggleKeyboardControls(); UI.toggleKeyboardControls();
if (WebUtil.isInsideKasmVDI()) { if (WebUtil.isInsideKasmVDI()) {
@ -542,6 +543,8 @@ const UI = {
UI.addSettingChangeHandler('virtual_keyboard_visible', UI.toggleKeyboardControls); UI.addSettingChangeHandler('virtual_keyboard_visible', UI.toggleKeyboardControls);
UI.addSettingChangeHandler('enable_ime'); UI.addSettingChangeHandler('enable_ime');
UI.addSettingChangeHandler('enable_ime', UI.toggleIMEMode); UI.addSettingChangeHandler('enable_ime', UI.toggleIMEMode);
UI.addSettingChangeHandler('enable_webrtc');
UI.addSettingChangeHandler('enable_webrtc', UI.toggleWebRTC);
}, },
addFullscreenHandlers() { addFullscreenHandlers() {
@ -1411,6 +1414,7 @@ const UI = {
UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless'); UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless');
UI.rfb.keyboard.enableIME = UI.getSetting('enable_ime'); UI.rfb.keyboard.enableIME = UI.getSetting('enable_ime');
UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless; UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless;
UI.rfb.enableWebRTC = UI.getSetting('enable_webrtc');
//Only explicitly request permission to clipboard on browsers that support binary clipboard access //Only explicitly request permission to clipboard on browsers that support binary clipboard access
if (supportsBinaryClipboard()) { if (supportsBinaryClipboard()) {
@ -1661,6 +1665,18 @@ const UI = {
UI.toggleIMEMode(); UI.toggleIMEMode();
} }
break; break;
case 'enable_webrtc':
if (!UI.getSetting('enable_webrtc')) {
UI.forceSetting('enable_webrtc', true, false);
UI.toggleWebRTC();
}
break;
case 'disable_webrtc':
if (UI.getSetting('enable_webrtc')) {
UI.forceSetting('enable_webrtc', false, false);
UI.toggleWebRTC();
}
break;
} }
} }
}, },
@ -2087,6 +2103,16 @@ const UI = {
} }
}, },
toggleWebRTC() {
if (UI.rfb) {
if (UI.getSetting('enable_webrtc')) {
UI.rfb.enableWebRTC = true;
} else {
UI.rfb.enableWebRTC = false;
}
}
},
showKeyboardControls() { showKeyboardControls() {
document.getElementById('noVNC_keyboard_control').classList.add("is-visible"); document.getElementById('noVNC_keyboard_control').classList.add("is-visible");
}, },

View File

@ -37,7 +37,7 @@ export default class TightDecoder {
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
if ((this._ctl >> i) & 1) { if ((this._ctl >> i) & 1) {
this._zlibs[i].reset(); this._zlibs[i].reset();
Log.Info("Reset zlib stream " + i); Log.Debug("Reset zlib stream " + i);
} }
} }

View File

@ -92,7 +92,7 @@ export default class UDPDecoder {
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
if ((zlibs_flags >> i) & 1) { if ((zlibs_flags >> i) & 1) {
this._zlibs[i].reset(); this._zlibs[i].reset();
Log.Info("Reset zlib stream " + i); Log.Debug("Reset zlib stream " + i);
} }
} }

View File

@ -138,6 +138,16 @@ export default class RFB extends EventTargetMixin {
this._maxVideoResolutionY = 540; this._maxVideoResolutionY = 540;
this._clipboardBinary = true; this._clipboardBinary = true;
this._useUdp = true; this._useUdp = true;
this.TransitConnectionStates = {
Tcp: Symbol("tcp"),
Udp: Symbol("udp"),
Upgrading: Symbol("upgrading"),
Downgrading: Symbol("downgrading"),
Failure: Symbol("failure")
}
this._transitConnectionState = this.TransitConnectionStates.Tcp;
this._udpConnectFailures = 0; //Failures in upgrading connection to udp
this._udpTransitFailures = 0; //Failures in transit after successful upgrade
this._trackFrameStats = false; this._trackFrameStats = false;
@ -698,6 +708,20 @@ export default class RFB extends EventTargetMixin {
get statsFps() { return this._display.fps; } get statsFps() { return this._display.fps; }
get enableWebRTC() { return this._useUdp; }
set enableWebRTC(value) {
this._useUdp = value;
if (!value) {
if (this._rfbConnectionState === 'connected' && this._transitConnectionState == this.TransitConnectionStates.Udp) {
this._sendUdpDowngrade();
}
} else {
if (this._rfbConnectionState === 'connected' && (this._transitConnectionState == this.TransitConnectionStates.Tcp)) {
this._sendUdpUpgrade();
}
}
}
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
/* /*
@ -900,6 +924,11 @@ export default class RFB extends EventTargetMixin {
// ===== PRIVATE METHODS ===== // ===== PRIVATE METHODS =====
_changeTransitConnectionState(value) {
Log.Debug("Transit state change from " + this._transitConnectionState.toString() + ' to ' + value.toString());
this._transitConnectionState = value;
}
_connect() { _connect() {
Log.Debug(">> RFB.connect"); Log.Debug(">> RFB.connect");
@ -1006,6 +1035,8 @@ export default class RFB extends EventTargetMixin {
this._udpChannel.onerror = function(e) { this._udpChannel.onerror = function(e) {
Log.Error("data channel error " + e.message); Log.Error("data channel error " + e.message);
this._udpTransitFailures+=1;
this._sendUdpDowngrade();
} }
let sock = this._sock; let sock = this._sock;
@ -3017,6 +3048,11 @@ export default class RFB extends EventTargetMixin {
} }
_sendUdpUpgrade() { _sendUdpUpgrade() {
if (this._transitConnectionState == this.TransitConnectionStates.Upgrading) {
return;
}
this._changeTransitConnectionState(this.TransitConnectionStates.Upgrading);
let peer = this._udpPeer; let peer = this._udpPeer;
let sock = this._sock; let sock = this._sock;
@ -3037,10 +3073,13 @@ export default class RFB extends EventTargetMixin {
sock.flush(); sock.flush();
}).catch(function(reason) { }).catch(function(reason) {
Log.Error("Failed to create offer " + reason); Log.Error("Failed to create offer " + reason);
this._changeTransitConnectionState(this.TransitConnectionStates.Tcp);
this._udpConnectFailures++;
}); });
} }
_sendUdpDowngrade() { _sendUdpDowngrade() {
this._changeTransitConnectionState(this.TransitConnectionStates.Downgrading);
const buff = sock._sQ; const buff = sock._sQ;
const offset = sock._sQlen; const offset = sock._sQlen;
@ -3062,16 +3101,22 @@ export default class RFB extends EventTargetMixin {
let peer = this._udpPeer; let peer = this._udpPeer;
var response = JSON.parse(payload); var response = JSON.parse(payload);
Log.Debug("UDP Upgrade recieved from server: " + payload);
peer.setRemoteDescription(new RTCSessionDescription(response.answer)).then(function() { peer.setRemoteDescription(new RTCSessionDescription(response.answer)).then(function() {
var candidate = new RTCIceCandidate(response.candidate); var candidate = new RTCIceCandidate(response.candidate);
peer.addIceCandidate(candidate).then(function() { peer.addIceCandidate(candidate).then(function() {
Log.Debug("success in addicecandidate"); Log.Debug("success in addicecandidate");
}).catch(function(err) { this._changeTransitConnectionState(this.TransitConnectionStates.Udp);
}.bind(this)).catch(function(err) {
Log.Error("Failure in addIceCandidate", err); Log.Error("Failure in addIceCandidate", err);
}); this._changeTransitConnectionState(this.TransitConnectionStates.Failure)
}).catch(function(e) { this._udpConnectFailures++;
}.bind(this));
}.bind(this)).catch(function(e) {
Log.Error("Failure in setRemoteDescription", e); Log.Error("Failure in setRemoteDescription", e);
}); this._changeTransitConnectionState(this.TransitConnectionStates.Failure)
this._udpConnectFailures++;
}.bind(this));
} }
_framebufferUpdate() { _framebufferUpdate() {
@ -3426,6 +3471,23 @@ export default class RFB extends EventTargetMixin {
} }
try { try {
if (this._transitConnectionState == this.TransitConnectionStates.Udp || this._transitConnectionState == this.TransitConnectionStates.Failure) {
if (this._transitConnectionState == this.TransitConnectionStates.Udp) {
Log.Warn("Implicit UDP Transit Failure, TCP rects recieved while in UDP mode.")
this._udpTransitFailures++;
}
this._changeTransitConnectionState(this.TransitConnectionStates.Tcp);
if (this._useUdp) {
if (this._udpConnectFailures < 3 && this._udpTransitFailures < 3) {
setTimeout(function() {
Log.Warn("Attempting to connect via UDP again after failure.")
this.enableWebRTC = true;
}.bind(this), 3000);
} else {
Log.Warn("UDP connection failures exceeded limit, remaining on TCP transit.")
}
}
}
return decoder.decodeRect(this._FBU.x, this._FBU.y, return decoder.decodeRect(this._FBU.x, this._FBU.y,
this._FBU.width, this._FBU.height, this._FBU.width, this._FBU.height,
this._sock, this._display, this._sock, this._display,

View File

@ -246,6 +246,12 @@
<span class="slider-label">Translate keyboard shurtcuts</span> <span class="slider-label">Translate keyboard shurtcuts</span>
</label> </label>
</li> </li>
<li>
<label class="switch"><input id="noVNC_setting_enable_webrtc" type="checkbox" />
<span class="slider round"></span>
<span class="slider-label">Enable WebRTC UDP Transit</span>
</label>
</li>
<li> <li>
<label class="switch"><input id="noVNC_setting_enable_webp" type="checkbox" /> <label class="switch"><input id="noVNC_setting_enable_webp" type="checkbox" />
<span class="slider round"></span> <span class="slider round"></span>