From 04ba001f15eb064512f1085ac2a0f7cf7b14e28c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 13 Oct 2014 03:25:31 -0700 Subject: [PATCH] GUAC-867: Add periodic keep-alive ping to ensure the session does not perish while a connection is active. --- .../guacamole/net/basic/SessionKeepAlive.java | 54 +++++++++++++++++++ guacamole/src/main/webapp/WEB-INF/web.xml | 11 ++++ .../src/main/webapp/scripts/client-ui.js | 30 ++++++++--- guacamole/src/main/webapp/scripts/service.js | 23 ++++++++ 4 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java new file mode 100644 index 000000000..0202d4977 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.basic; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Dummy servlet which provides an endpoint for arbitrary requests intended to + * simply keep the HTTP session from expiring. + * + * @author Michael Jumper + */ +public class SessionKeepAlive extends RestrictedHttpServlet { + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(SessionKeepAlive.class); + + @Override + protected void restrictedService( + UserContext context, + HttpServletRequest request, HttpServletResponse response) { + + // Do nothing + logger.trace("Keep-alive signal received."); + + } + +} diff --git a/guacamole/src/main/webapp/WEB-INF/web.xml b/guacamole/src/main/webapp/WEB-INF/web.xml index 285470352..6a960a194 100644 --- a/guacamole/src/main/webapp/WEB-INF/web.xml +++ b/guacamole/src/main/webapp/WEB-INF/web.xml @@ -86,6 +86,17 @@ /logout + + + Session keep-alive servlet. + SessionKeepAlive + org.glyptodon.guacamole.net.basic.SessionKeepAlive + + + SessionKeepAlive + /keep-alive + + Clipboard state servlet. diff --git a/guacamole/src/main/webapp/scripts/client-ui.js b/guacamole/src/main/webapp/scripts/client-ui.js index ff20eadde..a68bbfae0 100644 --- a/guacamole/src/main/webapp/scripts/client-ui.js +++ b/guacamole/src/main/webapp/scripts/client-ui.js @@ -195,10 +195,11 @@ GuacUI.Client = { }, /* Constants */ - - "KEYBOARD_AUTO_RESIZE_INTERVAL" : 30, /* milliseconds */ - "RECONNECT_PERIOD" : 15, /* seconds */ - "TEXT_INPUT_PADDING" : 128, /* characters */ + + "KEEP_ALIVE_INTERVAL" : 60000, /* milliseconds */ + "KEYBOARD_AUTO_RESIZE_INTERVAL" : 30, /* milliseconds */ + "RECONNECT_PERIOD" : 15, /* seconds */ + "TEXT_INPUT_PADDING" : 128, /* characters */ "TEXT_INPUT_PADDING_CODEPOINT" : 0x200B, /* Main application area */ @@ -1014,6 +1015,11 @@ GuacUI.Client.connect = function() { connect_string += "&video=" + encodeURIComponent(mimetype); }); + // Ping server every 10 seconds + var session_keep_alive = window.setInterval(function _session_keep_alive() { + GuacamoleService.KeepAlive.ping(); + }, GuacUI.Client.KEEP_ALIVE_INTERVAL); + // Show connection errors from tunnel tunnel.onerror = function(status) { var message = GuacUI.Client.tunnel_errors[status.code] || GuacUI.Client.tunnel_errors.DEFAULT; @@ -1021,10 +1027,20 @@ GuacUI.Client.connect = function() { GuacUI.Client.tunnel_auto_reconnect[status.code] && GuacUI.Client.RECONNECT_PERIOD); }; - // Notify of disconnections (if not already notified of something else) tunnel.onstatechange = function(state) { - if (state === Guacamole.Tunnel.State.CLOSED && !GuacUI.Client.visibleStatus) - GuacUI.Client.showStatus("Disconnected", "You have been disconnected. Reload the page to reconnect."); + + // Handle disconnect + if (state === Guacamole.Tunnel.State.CLOSED) { + + // No need for a keep-alive ping if the tunnel is closed + window.clearInterval(session_keep_alive); + + // Notify of disconnections (if not already notified of something else) + if (!GuacUI.Client.visibleStatus) + GuacUI.Client.showStatus("Disconnected", + "You have been disconnected. Reload the page to reconnect."); + } + }; // Connect diff --git a/guacamole/src/main/webapp/scripts/service.js b/guacamole/src/main/webapp/scripts/service.js index d9f09c126..fef93bd99 100644 --- a/guacamole/src/main/webapp/scripts/service.js +++ b/guacamole/src/main/webapp/scripts/service.js @@ -1438,3 +1438,26 @@ GuacamoleService.Clipboard = { } }; + +/** + * Collection of service functions which deal with the session keep-alive. Each + * function makes an explicit HTTP query to the server. In the case of the + * keep-alive ping, no response is expected, and any received response is + * ignored. + */ +GuacamoleService.KeepAlive = { + + "ping" : function(parameters) { + + // Construct request URL + var ping_url = "keep-alive"; + if (parameters) ping_url += "?" + parameters; + + // Send keep-alive "ping" + var xhr = new XMLHttpRequest(); + xhr.open("GET", ping_url, true); + xhr.send(null); + + } + +};