rfb.js: state refactor, add 'disconnect' state.

Add a new state 'disconnect' to reflect that we are not truly
'disconnected' until we get an onclose event. Add a disconnect timer
to match.

Handle disconnected cleanup better in updateState(). Anytime we enter
in a disconnect/disconnected state, make sure all running state is
cleaned up (WebSocket, timers, canvas).
This commit is contained in:
Joel Martin 2010-09-11 15:31:50 -05:00
parent cc374cd61a
commit e3efeb32a7
1 changed files with 78 additions and 55 deletions

View File

@ -65,6 +65,8 @@ var that = {}, // Public API interface
ws = null, // Web Socket object
canvas = null, // Canvas object
sendTimer = null, // Send Queue check timer
connTimer = null, // connection timer
disconnTimer = null, // disconnection timer
msgTimer = null, // queued handle_message timer
// Receive and send queues
@ -137,6 +139,8 @@ Util.conf_default(conf, that, 'local_cursor', true, true);
// time to wait for connection
Util.conf_default(conf, that, 'connectTimeout', 2000);
// time to wait for disconnection
Util.conf_default(conf, that, 'disconnectTimeout', 3000);
// frequency to check for send/receive
Util.conf_default(conf, that, 'check_rate', 217);
// frequency to send frameBufferUpdate requests
@ -274,12 +278,14 @@ function init_ws() {
};
ws.onclose = function(e) {
Util.Debug(">> WebSocket.onclose");
if (rfb_state === 'normal') {
updateState('failed', 'Server disconnected');
if (rfb_state === 'disconnect') {
updateState('disconnected', 'VNC disconnected');
} else if (rfb_state === 'ProtocolVersion') {
updateState('failed', 'Failed to connect to server');
} else if (rfb_state in {'failed':1, 'disconnected':1}) {
Util.Error("Received onclose while disconnected");
} else {
updateState('disconnected', 'VNC disconnected');
updateState('failed', 'Server disconnected');
}
Util.Debug("<< WebSocket.onclose");
};
@ -289,12 +295,6 @@ function init_ws() {
Util.Debug("<< WebSocket.onerror");
};
setTimeout(function () {
if (ws.readyState === WebSocket.CONNECTING) {
updateState('failed', "Connect timeout");
}
}, conf.connectTimeout);
Util.Debug("<< RFB.init_ws");
}
@ -327,7 +327,7 @@ init_vars = function() {
* Page states:
* loaded - page load, equivalent to disconnected
* connect - starting initialization
* password - waiting for password
* disconnect - starting disconnect
* failed - abnormal transition to disconnected
* fatal - failed to load page, or fatal error
*
@ -335,17 +335,52 @@ init_vars = function() {
* ProtocolVersion
* Security
* Authentication
* password - waiting for password, not part of RFB
* SecurityResult
* ServerInitialization
*/
updateState = function(state, statusMsg) {
var func, cmsg, oldstate = rfb_state;
if (state === oldstate) {
/* Already here, ignore */
Util.Debug("Already in state '" + state + "', ignoring.");
return;
}
/*
* These are disconnected states. A previous connect may
* asynchronously cause a connection so make sure we are closed.
*/
if (state in {'disconnected':1, 'loaded':1, 'connect':1,
'disconnect':1, 'failed':1, 'fatal':1}) {
if (sendTimer) {
clearInterval(sendTimer);
sendTimer = null;
}
if (msgTimer) {
clearInterval(msgTimer);
msgTimer = null;
}
if (canvas && canvas.getContext()) {
canvas.stop();
if (! /__debug__$/i.test(document.location.href)) {
canvas.clear();
}
}
if (ws) {
if ((ws.readyState === WebSocket.OPEN) ||
(ws.readyState === WebSocket.CONNECTING)) {
Util.Info("Closing WebSocket connection");
ws.close();
}
ws.onmessage = function (e) { return; };
}
}
if (oldstate === 'fatal') {
Util.Error("Fatal error, cannot continue");
}
@ -356,9 +391,6 @@ updateState = function(state, statusMsg) {
func = Util.Warn;
}
cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
func("New state '" + state + "', was '" + oldstate + "'." + cmsg);
if ((oldstate === 'failed') && (state === 'disconnected')) {
// Do disconnect action, but stay in failed state.
rfb_state = 'failed';
@ -366,53 +398,50 @@ updateState = function(state, statusMsg) {
rfb_state = state;
}
cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg);
if (connTimer && (rfb_state !== 'connect')) {
Util.Debug("Clearing connect timer");
clearInterval(connTimer);
connTimer = null;
}
if (disconnTimer && (rfb_state !== 'disconnect')) {
Util.Debug("Clearing disconnect timer");
clearInterval(disconnTimer);
disconnTimer = null;
}
switch (state) {
case 'loaded':
case 'disconnected':
if (sendTimer) {
clearInterval(sendTimer);
sendTimer = null;
}
if (ws) {
if (ws.readyState === WebSocket.OPEN) {
ws.close();
}
ws.onmessage = function (e) { return; };
}
if (canvas && canvas.getContext()) {
canvas.stop();
if (! /__debug__$/i.test(document.location.href)) {
canvas.clear();
}
case 'normal':
if ((oldstate === 'disconnected') || (oldstate === 'failed')) {
Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'");
}
break;
case 'connect':
connTimer = setTimeout(function () {
updateState('failed', "Connect timeout");
}, conf.connectTimeout);
init_vars();
init_ws();
if ((ws) && (ws.readyState === WebSocket.OPEN)) {
ws.close();
}
init_ws(); // onopen transitions to 'ProtocolVersion'
// WebSocket.onopen transitions to 'ProtocolVersion'
break;
case 'password':
// Ignore password state by default
break;
case 'disconnect':
disconnTimer = setTimeout(function () {
updateState('failed', "Disconnect timeout");
}, conf.disconnectTimeout);
case 'normal':
if ((oldstate === 'disconnected') || (oldstate === 'failed')) {
Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'");
}
// WebSocket.onclose transitions to 'disconnected'
break;
@ -427,9 +456,6 @@ updateState = function(state, statusMsg) {
Util.Error("Error while initializing.");
}
if ((ws) && (ws.readyState === WebSocket.OPEN)) {
ws.close();
}
// Make sure we transition to disconnected
setTimeout(function() { updateState('disconnected'); }, 50);
@ -437,7 +463,7 @@ updateState = function(state, statusMsg) {
default:
// Invalid state transition
// No state change action to take
}
@ -469,11 +495,8 @@ function handle_message() {
}
switch (rfb_state) {
case 'disconnected':
Util.Error("Got data while disconnected");
break;
case 'failed':
Util.Warn("Giving up!");
that.disconnect();
Util.Error("Got data while disconnected");
break;
case 'normal':
if (normal_msg() && rQlen() > 0) {
@ -1556,7 +1579,7 @@ that.connect = function(host, port, password) {
that.disconnect = function() {
//Util.Debug(">> disconnect");
updateState('disconnected', 'Disconnected');
updateState('disconnect', 'Disconnecting');
//Util.Debug("<< disconnect");
};