mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 21:27:40 +00:00
GUACAMOLE-1687: Merge change ensuring network will cause keep-alive pings to be sent.
This commit is contained in:
@@ -42,7 +42,36 @@ Guacamole.Client = function(tunnel) {
|
|||||||
var currentState = STATE_IDLE;
|
var currentState = STATE_IDLE;
|
||||||
|
|
||||||
var currentTimestamp = 0;
|
var currentTimestamp = 0;
|
||||||
var pingInterval = null;
|
|
||||||
|
/**
|
||||||
|
* The rough number of milliseconds to wait between sending keep-alive
|
||||||
|
* pings. This may vary depending on how frequently the browser allows
|
||||||
|
* timers to run, as well as how frequently the client receives messages
|
||||||
|
* from the server.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @constant
|
||||||
|
* @type {!number}
|
||||||
|
*/
|
||||||
|
var KEEP_ALIVE_FREQUENCY = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current keep-alive ping timeout ID, if any. This will only be set
|
||||||
|
* upon connecting.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
var keepAliveTimeout = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp of the point in time that the last keep-live ping was
|
||||||
|
* sent, in milliseconds elapsed since midnight of January 1, 1970 UTC.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type {!number}
|
||||||
|
*/
|
||||||
|
var lastSentKeepAlive = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translation from Guacamole protocol line caps to Layer line caps.
|
* Translation from Guacamole protocol line caps to Layer line caps.
|
||||||
@@ -1649,12 +1678,63 @@ Guacamole.Client = function(tunnel) {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a keep-alive ping to the Guacamole server, advising the server
|
||||||
|
* that the client is still connected and responding. The lastSentKeepAlive
|
||||||
|
* timestamp is automatically updated as a result of calling this function.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
var sendKeepAlive = function sendKeepAlive() {
|
||||||
|
tunnel.sendMessage('nop');
|
||||||
|
lastSentKeepAlive = new Date().getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules the next keep-alive ping based on the KEEP_ALIVE_FREQUENCY and
|
||||||
|
* the time that the last ping was sent, if ever. If enough time has
|
||||||
|
* elapsed that a ping should have already been sent, calling this function
|
||||||
|
* will send that ping immediately.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
var scheduleKeepAlive = function scheduleKeepAlive() {
|
||||||
|
|
||||||
|
window.clearTimeout(keepAliveTimeout);
|
||||||
|
|
||||||
|
var currentTime = new Date().getTime();
|
||||||
|
var keepAliveDelay = Math.max(lastSentKeepAlive + KEEP_ALIVE_FREQUENCY - currentTime, 0);
|
||||||
|
|
||||||
|
// Ping server regularly to keep connection alive, but send the ping
|
||||||
|
// immediately if enough time has elapsed that it should have already
|
||||||
|
// been sent
|
||||||
|
if (keepAliveDelay > 0)
|
||||||
|
keepAliveTimeout = window.setTimeout(sendKeepAlive, keepAliveDelay);
|
||||||
|
else
|
||||||
|
sendKeepAlive();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops sending any further keep-alive pings. If a keep-alive ping was
|
||||||
|
* scheduled to be sent, that ping is cancelled.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
var stopKeepAlive = function stopKeepAlive() {
|
||||||
|
window.clearTimeout(keepAliveTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
tunnel.oninstruction = function(opcode, parameters) {
|
tunnel.oninstruction = function(opcode, parameters) {
|
||||||
|
|
||||||
var handler = instructionHandlers[opcode];
|
var handler = instructionHandlers[opcode];
|
||||||
if (handler)
|
if (handler)
|
||||||
handler(parameters);
|
handler(parameters);
|
||||||
|
|
||||||
|
// Leverage network activity to ensure the next keep-alive ping is
|
||||||
|
// sent, even if the browser is currently throttling timers
|
||||||
|
scheduleKeepAlive();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1668,9 +1748,8 @@ Guacamole.Client = function(tunnel) {
|
|||||||
|
|
||||||
setState(STATE_DISCONNECTING);
|
setState(STATE_DISCONNECTING);
|
||||||
|
|
||||||
// Stop ping
|
// Stop sending keep-alive messages
|
||||||
if (pingInterval)
|
stopKeepAlive();
|
||||||
window.clearInterval(pingInterval);
|
|
||||||
|
|
||||||
// Send disconnect message and disconnect
|
// Send disconnect message and disconnect
|
||||||
tunnel.sendMessage("disconnect");
|
tunnel.sendMessage("disconnect");
|
||||||
@@ -1704,10 +1783,9 @@ Guacamole.Client = function(tunnel) {
|
|||||||
throw status;
|
throw status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ping every 5 seconds (ensure connection alive)
|
// Regularly send keep-alive ping to ensure the server knows we're
|
||||||
pingInterval = window.setInterval(function() {
|
// still here, even if not active
|
||||||
tunnel.sendMessage("nop");
|
scheduleKeepAlive();
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
setState(STATE_WAITING);
|
setState(STATE_WAITING);
|
||||||
};
|
};
|
||||||
|
@@ -356,12 +356,16 @@ Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a timeout which, if data is not received, causes the tunnel
|
* Resets the state of timers tracking network activity and stability. If
|
||||||
* to close with an error.
|
* those timers are not yet started, invoking this function starts them.
|
||||||
*
|
* This function should be invoked when the tunnel is established and every
|
||||||
|
* time there is network activity on the tunnel, such that the timers can
|
||||||
|
* safely assume the network and/or server are not responding if this
|
||||||
|
* function has not been invoked for a significant period of time.
|
||||||
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function reset_timeout() {
|
var resetTimers = function resetTimers() {
|
||||||
|
|
||||||
// Get rid of old timeouts (if any)
|
// Get rid of old timeouts (if any)
|
||||||
window.clearTimeout(receive_timeout);
|
window.clearTimeout(receive_timeout);
|
||||||
@@ -381,7 +385,7 @@ Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
|
|||||||
tunnel.setState(Guacamole.Tunnel.State.UNSTABLE);
|
tunnel.setState(Guacamole.Tunnel.State.UNSTABLE);
|
||||||
}, tunnel.unstableThreshold);
|
}, tunnel.unstableThreshold);
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this tunnel, signaling the given status and corresponding
|
* Closes this tunnel, signaling the given status and corresponding
|
||||||
@@ -491,7 +495,7 @@ Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
|
|||||||
message_xmlhttprequest.onreadystatechange = function() {
|
message_xmlhttprequest.onreadystatechange = function() {
|
||||||
if (message_xmlhttprequest.readyState === 4) {
|
if (message_xmlhttprequest.readyState === 4) {
|
||||||
|
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
// If an error occurs during send, handle it
|
// If an error occurs during send, handle it
|
||||||
if (message_xmlhttprequest.status !== 200)
|
if (message_xmlhttprequest.status !== 200)
|
||||||
@@ -581,7 +585,7 @@ Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
|
|||||||
if (xmlhttprequest.readyState === 3 ||
|
if (xmlhttprequest.readyState === 3 ||
|
||||||
xmlhttprequest.readyState === 4) {
|
xmlhttprequest.readyState === 4) {
|
||||||
|
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
// Also poll every 30ms (some browsers don't repeatedly call onreadystatechange for new data)
|
// Also poll every 30ms (some browsers don't repeatedly call onreadystatechange for new data)
|
||||||
if (pollingMode === POLLING_ENABLED) {
|
if (pollingMode === POLLING_ENABLED) {
|
||||||
@@ -742,7 +746,7 @@ Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
|
|||||||
this.connect = function(data) {
|
this.connect = function(data) {
|
||||||
|
|
||||||
// Start waiting for connect
|
// Start waiting for connect
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
// Mark the tunnel as connecting
|
// Mark the tunnel as connecting
|
||||||
tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
|
tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
|
||||||
@@ -760,7 +764,7 @@ Guacamole.HTTPTunnel = function(tunnelURL, crossDomain, extraTunnelHeaders) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
// Get UUID and HTTP-specific tunnel session token from response
|
// Get UUID and HTTP-specific tunnel session token from response
|
||||||
tunnel.setUUID(connect_xmlhttprequest.responseText);
|
tunnel.setUUID(connect_xmlhttprequest.responseText);
|
||||||
@@ -844,13 +848,13 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
var unstableTimeout = null;
|
var unstableTimeout = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current connection stability test ping interval ID, if any. This
|
* The current connection stability test ping timeout ID, if any. This
|
||||||
* will only be set upon successful connection.
|
* will only be set upon successful connection.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
var pingInterval = null;
|
var pingTimeout = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The WebSocket protocol corresponding to the protocol used for the current
|
* The WebSocket protocol corresponding to the protocol used for the current
|
||||||
@@ -874,6 +878,16 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
*/
|
*/
|
||||||
var PING_FREQUENCY = 500;
|
var PING_FREQUENCY = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timestamp of the point in time that the last connection stability
|
||||||
|
* test ping was sent, in milliseconds elapsed since midnight of January 1,
|
||||||
|
* 1970 UTC.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type {!number}
|
||||||
|
*/
|
||||||
|
var lastSentPing = 0;
|
||||||
|
|
||||||
// Transform current URL to WebSocket URL
|
// Transform current URL to WebSocket URL
|
||||||
|
|
||||||
// If not already a websocket URL
|
// If not already a websocket URL
|
||||||
@@ -908,16 +922,35 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a timeout which, if data is not received, causes the tunnel
|
* Sends an internal "ping" instruction to the Guacamole WebSocket
|
||||||
* to close with an error.
|
* endpoint, verifying network connection stability. If the network is
|
||||||
*
|
* stable, the Guacamole server will receive this instruction and respond
|
||||||
|
* with an identical ping.
|
||||||
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function reset_timeout() {
|
var sendPing = function sendPing() {
|
||||||
|
var currentTime = new Date().getTime();
|
||||||
|
tunnel.sendMessage(Guacamole.Tunnel.INTERNAL_DATA_OPCODE, 'ping', currentTime);
|
||||||
|
lastSentPing = currentTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the state of timers tracking network activity and stability. If
|
||||||
|
* those timers are not yet started, invoking this function starts them.
|
||||||
|
* This function should be invoked when the tunnel is established and every
|
||||||
|
* time there is network activity on the tunnel, such that the timers can
|
||||||
|
* safely assume the network and/or server are not responding if this
|
||||||
|
* function has not been invoked for a significant period of time.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
var resetTimers = function resetTimers() {
|
||||||
|
|
||||||
// Get rid of old timeouts (if any)
|
// Get rid of old timeouts (if any)
|
||||||
window.clearTimeout(receive_timeout);
|
window.clearTimeout(receive_timeout);
|
||||||
window.clearTimeout(unstableTimeout);
|
window.clearTimeout(unstableTimeout);
|
||||||
|
window.clearTimeout(pingTimeout);
|
||||||
|
|
||||||
// Clear unstable status
|
// Clear unstable status
|
||||||
if (tunnel.state === Guacamole.Tunnel.State.UNSTABLE)
|
if (tunnel.state === Guacamole.Tunnel.State.UNSTABLE)
|
||||||
@@ -933,7 +966,17 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
tunnel.setState(Guacamole.Tunnel.State.UNSTABLE);
|
tunnel.setState(Guacamole.Tunnel.State.UNSTABLE);
|
||||||
}, tunnel.unstableThreshold);
|
}, tunnel.unstableThreshold);
|
||||||
|
|
||||||
}
|
var currentTime = new Date().getTime();
|
||||||
|
var pingDelay = Math.max(lastSentPing + PING_FREQUENCY - currentTime, 0);
|
||||||
|
|
||||||
|
// Ping tunnel endpoint regularly to test connection stability, sending
|
||||||
|
// the ping immediately if enough time has already elapsed
|
||||||
|
if (pingDelay > 0)
|
||||||
|
pingTimeout = window.setTimeout(sendPing, pingDelay);
|
||||||
|
else
|
||||||
|
sendPing();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this tunnel, signaling the given status and corresponding
|
* Closes this tunnel, signaling the given status and corresponding
|
||||||
@@ -949,9 +992,7 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
// Get rid of old timeouts (if any)
|
// Get rid of old timeouts (if any)
|
||||||
window.clearTimeout(receive_timeout);
|
window.clearTimeout(receive_timeout);
|
||||||
window.clearTimeout(unstableTimeout);
|
window.clearTimeout(unstableTimeout);
|
||||||
|
window.clearTimeout(pingTimeout);
|
||||||
// Cease connection test pings
|
|
||||||
window.clearInterval(pingInterval);
|
|
||||||
|
|
||||||
// Ignore if already closed
|
// Ignore if already closed
|
||||||
if (tunnel.state === Guacamole.Tunnel.State.CLOSED)
|
if (tunnel.state === Guacamole.Tunnel.State.CLOSED)
|
||||||
@@ -1010,7 +1051,7 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
|
|
||||||
this.connect = function(data) {
|
this.connect = function(data) {
|
||||||
|
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
// Mark the tunnel as connecting
|
// Mark the tunnel as connecting
|
||||||
tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
|
tunnel.setState(Guacamole.Tunnel.State.CONNECTING);
|
||||||
@@ -1019,14 +1060,7 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
socket = new WebSocket(tunnelURL + "?" + data, "guacamole");
|
socket = new WebSocket(tunnelURL + "?" + data, "guacamole");
|
||||||
|
|
||||||
socket.onopen = function(event) {
|
socket.onopen = function(event) {
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
// Ping tunnel endpoint regularly to test connection stability
|
|
||||||
pingInterval = setInterval(function sendPing() {
|
|
||||||
tunnel.sendMessage(Guacamole.Tunnel.INTERNAL_DATA_OPCODE,
|
|
||||||
"ping", new Date().getTime());
|
|
||||||
}, PING_FREQUENCY);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onclose = function(event) {
|
socket.onclose = function(event) {
|
||||||
@@ -1048,7 +1082,7 @@ Guacamole.WebSocketTunnel = function(tunnelURL) {
|
|||||||
|
|
||||||
socket.onmessage = function(event) {
|
socket.onmessage = function(event) {
|
||||||
|
|
||||||
reset_timeout();
|
resetTimers();
|
||||||
|
|
||||||
var message = event.data;
|
var message = event.data;
|
||||||
var startIndex = 0;
|
var startIndex = 0;
|
||||||
|
Reference in New Issue
Block a user