GUAC-558: Ensure state/error handlers are called ONLY on the last tunnel. Rewrite to clean program flow.

This commit is contained in:
Michael Jumper
2014-03-21 18:54:04 -07:00
parent f183e29586
commit 7cb933c9c1

View File

@@ -168,15 +168,15 @@ Guacamole.HTTPTunnel = function(tunnelURL) {
if (tunnel.state === Guacamole.Tunnel.State.CLOSED) if (tunnel.state === Guacamole.Tunnel.State.CLOSED)
return; return;
// If connection closed abnormally, signal error.
if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror)
tunnel.onerror(status);
// Mark as closed // Mark as closed
tunnel.state = Guacamole.Tunnel.State.CLOSED; tunnel.state = Guacamole.Tunnel.State.CLOSED;
if (tunnel.onstatechange) if (tunnel.onstatechange)
tunnel.onstatechange(tunnel.state); tunnel.onstatechange(tunnel.state);
// If connection closed abnormally, signal error.
if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror)
tunnel.onerror(status);
} }
@@ -602,15 +602,15 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
if (tunnel.state === Guacamole.Tunnel.State.CLOSED) if (tunnel.state === Guacamole.Tunnel.State.CLOSED)
return; return;
// If connection closed abnormally, signal error.
if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror)
tunnel.onerror(status);
// Mark as closed // Mark as closed
tunnel.state = Guacamole.Tunnel.State.CLOSED; tunnel.state = Guacamole.Tunnel.State.CLOSED;
if (tunnel.onstatechange) if (tunnel.onstatechange)
tunnel.onstatechange(tunnel.state); tunnel.onstatechange(tunnel.state);
// If connection closed abnormally, signal error.
if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror)
tunnel.onerror(status);
socket.close(); socket.close();
} }
@@ -759,12 +759,6 @@ Guacamole.ChainedTunnel = function(tunnel_chain) {
*/ */
var chained_tunnel = this; var chained_tunnel = this;
/**
* The currently wrapped tunnel, if any.
* @private
*/
var current_tunnel = null;
/** /**
* Data passed in via connect(), to be used for * Data passed in via connect(), to be used for
* wrapped calls to other tunnels' connect() functions. * wrapped calls to other tunnels' connect() functions.
@@ -791,39 +785,75 @@ Guacamole.ChainedTunnel = function(tunnel_chain) {
*/ */
function attach(tunnel) { function attach(tunnel) {
// Clear handlers of current tunnel, if any // Set own functions to tunnel's functions
if (current_tunnel) { chained_tunnel.disconnect = tunnel.disconnect;
current_tunnel.onerror = null; chained_tunnel.sendMessage = tunnel.sendMessage;
current_tunnel.oninstruction = null;
current_tunnel.onstatechange = null; /**
* Fails the currently-attached tunnel, attaching a new tunnel if
* possible.
*
* @private
* @return {Guacamole.Tunnel} The next tunnel, or null if there are no
* more tunnels to try.
*/
function fail_tunnel() {
// Get next tunnel
var next_tunnel = tunnels.shift();
// If there IS a next tunnel, try using it.
if (next_tunnel) {
tunnel.onerror = null;
tunnel.oninstruction = null;
tunnel.onstatechange = null;
attach(next_tunnel);
}
return next_tunnel;
} }
// Set own functions to tunnel's functions /**
chained_tunnel.disconnect = tunnel.disconnect; * Use the current tunnel from this point forward. Do not try any more
chained_tunnel.sendMessage = tunnel.sendMessage; * tunnels, even if the current tunnel fails.
*
// Record current tunnel * @private
current_tunnel = tunnel; */
function commit_tunnel() {
tunnel.onstatechange = chained_tunnel.onstatechange;
tunnel.oninstruction = chained_tunnel.oninstruction;
tunnel.onerror = chained_tunnel.onerror;
}
// Wrap own onstatechange within current tunnel // Wrap own onstatechange within current tunnel
current_tunnel.onstatechange = function(state) { tunnel.onstatechange = function(state) {
// Invoke handler
if (chained_tunnel.onstatechange)
chained_tunnel.onstatechange(state);
// Use handlers permanently from now on switch (state) {
if (state === Guacamole.Tunnel.State.OPEN) {
current_tunnel.onstatechange = chained_tunnel.onstatechange; // If open, use this tunnel from this point forward.
current_tunnel.oninstruction = chained_tunnel.oninstruction; case Guacamole.Tunnel.State.OPEN:
current_tunnel.onerror = chained_tunnel.onerror; commit_tunnel();
if (chained_tunnel.onstatechange)
chained_tunnel.onstatechange(state);
break;
// If closed, mark failure, attempt next tunnel
case Guacamole.Tunnel.State.CLOSED:
if (!fail_tunnel() && chained_tunnel.onstatechange)
chained_tunnel.onstatechange(state);
break;
} }
}; };
// Wrap own oninstruction within current tunnel // Wrap own oninstruction within current tunnel
current_tunnel.oninstruction = function(opcode, elements) { tunnel.oninstruction = function(opcode, elements) {
// Accept current tunnel
commit_tunnel();
// Invoke handler // Invoke handler
if (chained_tunnel.oninstruction) if (chained_tunnel.oninstruction)
chained_tunnel.oninstruction(opcode, elements); chained_tunnel.oninstruction(opcode, elements);
@@ -831,34 +861,17 @@ Guacamole.ChainedTunnel = function(tunnel_chain) {
}; };
// Attach next tunnel on error // Attach next tunnel on error
current_tunnel.onerror = function(status) { tunnel.onerror = function(status) {
// Get next tunnel // Mark failure, attempt next tunnel
var next_tunnel = tunnels.shift(); if (!fail_tunnel() && chained_tunnel.onerror)
// If there IS a next tunnel, try using it.
if (next_tunnel)
attach(next_tunnel);
// Otherwise, call error handler
else if (chained_tunnel.onerror)
chained_tunnel.onerror(status); chained_tunnel.onerror(status);
}; };
try { // Attempt connection
tunnel.connect(connect_data);
// Attempt connection
current_tunnel.connect(connect_data);
}
catch (status) {
// Call error handler of current tunnel on error
current_tunnel.onerror(status);
}
} }
this.connect = function(data) { this.connect = function(data) {