diff --git a/doc/guacamole-example/src/main/java/org/glyptodon/guacamole/net/example/DummyGuacamoleTunnelServlet.java b/doc/guacamole-example/src/main/java/org/glyptodon/guacamole/net/example/DummyGuacamoleTunnelServlet.java index a34d47633..58479ac7b 100644 --- a/doc/guacamole-example/src/main/java/org/glyptodon/guacamole/net/example/DummyGuacamoleTunnelServlet.java +++ b/doc/guacamole-example/src/main/java/org/glyptodon/guacamole/net/example/DummyGuacamoleTunnelServlet.java @@ -23,7 +23,6 @@ package org.glyptodon.guacamole.net.example; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.GuacamoleTunnel; @@ -32,7 +31,6 @@ import org.glyptodon.guacamole.net.SimpleGuacamoleTunnel; import org.glyptodon.guacamole.protocol.ConfiguredGuacamoleSocket; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; import org.glyptodon.guacamole.servlet.GuacamoleHTTPTunnelServlet; -import org.glyptodon.guacamole.servlet.GuacamoleSession; /** * Simple tunnel example with hard-coded configuration parameters. @@ -44,8 +42,6 @@ public class DummyGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet { @Override protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException { - HttpSession httpSession = request.getSession(true); - // guacd connection information String hostname = "localhost"; int port = 4822; @@ -65,11 +61,6 @@ public class DummyGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet { // Create tunnel from now-configured socket GuacamoleTunnel tunnel = new SimpleGuacamoleTunnel(socket); - - // Attach tunnel - GuacamoleSession session = new GuacamoleSession(httpSession); - session.attachTunnel(tunnel); - return tunnel; } diff --git a/guacamole-common/doc/example/ExampleTunnelServlet.java b/guacamole-common/doc/example/ExampleTunnelServlet.java index 367ceda4c..88fd57b1c 100644 --- a/guacamole-common/doc/example/ExampleTunnelServlet.java +++ b/guacamole-common/doc/example/ExampleTunnelServlet.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * Copyright (C) 2015 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 @@ -21,7 +21,6 @@ */ import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.properties.GuacamoleProperties; import net.sourceforge.guacamole.net.GuacamoleSocket; @@ -29,7 +28,6 @@ import net.sourceforge.guacamole.net.GuacamoleTunnel; import net.sourceforge.guacamole.net.InetGuacamoleSocket; import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; import net.sourceforge.guacamole.protocol.ConfiguredGuacamoleSocket; -import net.sourceforge.guacamole.servlet.GuacamoleSession; import net.sourceforge.guacamole.servlet.GuacamoleHTTPTunnelServlet; public class ExampleTunnelServlet extends GuacamoleHTTPTunnelServlet { @@ -38,8 +36,6 @@ public class ExampleTunnelServlet extends GuacamoleHTTPTunnelServlet { protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException { - HttpSession httpSession = request.getSession(true); - String hostname = GuacamoleProperties.getProperty( GuacamoleProperties.GUACD_HOSTNAME); @@ -57,12 +53,8 @@ public class ExampleTunnelServlet extends GuacamoleHTTPTunnelServlet { config ); + // Create and return tunnel GuacamoleTunnel tunnel = new GuacamoleTunnel(socket); - - // Attach tunnel - GuacamoleSession session = new GuacamoleSession(httpSession); - session.attachTunnel(tunnel); - return tunnel; } diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleHTTPTunnelServlet.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleHTTPTunnelServlet.java index 977438b48..3ff5119cb 100644 --- a/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleHTTPTunnelServlet.java +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleHTTPTunnelServlet.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * Copyright (C) 2015 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 @@ -22,18 +22,18 @@ package org.glyptodon.guacamole.servlet; - import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleConnectionClosedException; import org.glyptodon.guacamole.GuacamoleException; @@ -57,7 +57,13 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { /** * Logger for this class. */ - private Logger logger = LoggerFactory.getLogger(GuacamoleHTTPTunnelServlet.class); + private final Logger logger = LoggerFactory.getLogger(GuacamoleHTTPTunnelServlet.class); + + /** + * Map of absolutely all active tunnels using HTTP, indexed by tunnel UUID. + */ + private final ConcurrentMap tunnels = + new ConcurrentHashMap(); /** * The prefix of the query string which denotes a tunnel read operation. @@ -84,6 +90,56 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { */ private static final int UUID_LENGTH = 36; + /** + * Registers the given tunnel such that future read/write requests to that + * tunnel will be properly directed. + * + * @param tunnel + * The tunnel to register. + */ + protected void registerTunnel(GuacamoleTunnel tunnel) { + tunnels.put(tunnel.getUUID().toString(), tunnel); + logger.debug("Registered tunnel \"{}\".", tunnel.getUUID()); + } + + /** + * Deregisters the given tunnel such that future read/write requests to + * that tunnel will be rejected. + * + * @param tunnel + * The tunnel to deregister. + */ + protected void deregisterTunnel(GuacamoleTunnel tunnel) { + tunnels.remove(tunnel.getUUID().toString()); + logger.debug("Deregistered tunnel \"{}\".", tunnel.getUUID()); + } + + /** + * Returns the tunnel with the given UUID, if it has been registered with + * registerTunnel() and not yet deregistered with deregisterTunnel(). + * + * @param tunnelUUID + * The UUID of registered tunnel. + * + * @return + * The tunnel corresponding to the given UUID. + * + * @throws GuacamoleException + * If the requested tunnel does not exist because it has not yet been + * registered or it has been deregistered. + */ + protected GuacamoleTunnel getTunnel(String tunnelUUID) + throws GuacamoleException { + + // Pull tunnel from map + GuacamoleTunnel tunnel = tunnels.get(tunnelUUID); + if (tunnel == null) + throw new GuacamoleResourceNotFoundException("No such tunnel."); + + return tunnel; + + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException { handleTunnelRequest(request, response); @@ -98,24 +154,29 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { * Sends an error on the given HTTP response using the information within * the given GuacamoleStatus. * - * @param response The HTTP response to use to send the error. - * @param guac_status The status to send - * @param message A human-readable message that can be presented to the - * user. - * @throws ServletException If an error prevents sending of the error - * code. + * @param response + * The HTTP response to use to send the error. + * + * @param guacStatus + * The status to send + * + * @param message + * A human-readable message that can be presented to the user. + * + * @throws ServletException + * If an error prevents sending of the error code. */ - public static void sendError(HttpServletResponse response, - GuacamoleStatus guac_status, String message) + protected void sendError(HttpServletResponse response, + GuacamoleStatus guacStatus, String message) throws ServletException { try { // If response not committed, send error code and message if (!response.isCommitted()) { - response.addHeader("Guacamole-Status-Code", Integer.toString(guac_status.getGuacamoleStatusCode())); + response.addHeader("Guacamole-Status-Code", Integer.toString(guacStatus.getGuacamoleStatusCode())); response.addHeader("Guacamole-Error-Message", message); - response.sendError(guac_status.getHttpStatusCode()); + response.sendError(guacStatus.getHttpStatusCode()); } } @@ -133,13 +194,19 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { * Dispatches every HTTP GET and POST request to the appropriate handler * function based on the query string. * - * @param request The HttpServletRequest associated with the GET or POST - * request received. - * @param response The HttpServletResponse associated with the GET or POST - * request received. - * @throws ServletException If an error occurs while servicing the request. + * @param request + * The HttpServletRequest associated with the GET or POST request + * received. + * + * @param response + * The HttpServletResponse associated with the GET or POST request + * received. + * + * @throws ServletException + * If an error occurs while servicing the request. */ - protected void handleTunnelRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException { + protected void handleTunnelRequest(HttpServletRequest request, + HttpServletResponse response) throws ServletException { try { @@ -154,12 +221,8 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { GuacamoleTunnel tunnel = doConnect(request); if (tunnel != null) { - // Get session - HttpSession httpSession = request.getSession(true); - GuacamoleSession session = new GuacamoleSession(httpSession); - - // Attach tunnel to session - session.attachTunnel(tunnel); + // Register newly-created tunnel + registerTunnel(tunnel); try { // Ensure buggy browsers do not cache response @@ -215,48 +278,53 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { /** * Called whenever the JavaScript Guacamole client makes a connection - * request. It it up to the implementor of this function to define what - * conditions must be met for a tunnel to be configured and returned as a - * result of this connection request (whether some sort of credentials must - * be specified, for example). + * request via HTTP. It it up to the implementor of this function to define + * what conditions must be met for a tunnel to be configured and returned + * as a result of this connection request (whether some sort of credentials + * must be specified, for example). * - * @param request The HttpServletRequest associated with the connection - * request received. Any parameters specified along with - * the connection request can be read from this object. - * @return A newly constructed GuacamoleTunnel if successful, - * null otherwise. - * @throws GuacamoleException If an error occurs while constructing the - * GuacamoleTunnel, or if the conditions - * required for connection are not met. + * @param request + * The HttpServletRequest associated with the connection request + * received. Any parameters specified along with the connection request + * can be read from this object. + * + * @return + * A newly constructed GuacamoleTunnel if successful, null otherwise. + * + * @throws GuacamoleException + * If an error occurs while constructing the GuacamoleTunnel, or if the + * conditions required for connection are not met. */ - protected abstract GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException; + protected abstract GuacamoleTunnel doConnect(HttpServletRequest request) + throws GuacamoleException; /** * Called whenever the JavaScript Guacamole client makes a read request. * This function should in general not be overridden, as it already * contains a proper implementation of the read operation. * - * @param request The HttpServletRequest associated with the read request - * received. - * @param response The HttpServletResponse associated with the write request - * received. Any data to be sent to the client in response - * to the write request should be written to the response - * body of this HttpServletResponse. - * @param tunnelUUID The UUID of the tunnel to read from, as specified in - * the write request. This tunnel must be attached to - * the Guacamole session. - * @throws GuacamoleException If an error occurs while handling the read - * request. + * @param request + * The HttpServletRequest associated with the read request received. + * + * @param response + * The HttpServletResponse associated with the write request received. + * Any data to be sent to the client in response to the write request + * should be written to the response body of this HttpServletResponse. + * + * @param tunnelUUID + * The UUID of the tunnel to read from, as specified in the write + * request. This tunnel must have been created by a previous call to + * doConnect(). + * + * @throws GuacamoleException + * If an error occurs while handling the read request. */ - protected void doRead(HttpServletRequest request, HttpServletResponse response, String tunnelUUID) throws GuacamoleException { - - HttpSession httpSession = request.getSession(false); - GuacamoleSession session = new GuacamoleSession(httpSession); + protected void doRead(HttpServletRequest request, + HttpServletResponse response, String tunnelUUID) + throws GuacamoleException { // Get tunnel, ensure tunnel exists - GuacamoleTunnel tunnel = session.getTunnel(tunnelUUID); - if (tunnel == null) - throw new GuacamoleResourceNotFoundException("No such tunnel."); + GuacamoleTunnel tunnel = getTunnel(tunnelUUID); // Ensure tunnel is open if (!tunnel.isOpen()) @@ -280,8 +348,8 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { // Stream data to response, ensuring output stream is closed try { - // Detach tunnel and throw error if EOF (and we haven't sent any - // data yet. + // Deregister tunnel and throw error if we reach EOF without + // having ever sent any data char[] message = reader.read(); if (message == null) throw new GuacamoleConnectionClosedException("Tunnel reached end of stream."); @@ -306,7 +374,7 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { // Close tunnel immediately upon EOF if (message == null) { - session.detachTunnel(tunnel); + deregisterTunnel(tunnel); tunnel.close(); } @@ -319,8 +387,8 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { // Send end-of-stream marker and close tunnel if connection is closed catch (GuacamoleConnectionClosedException e) { - // Detach and close - session.detachTunnel(tunnel); + // Deregister and close + deregisterTunnel(tunnel); tunnel.close(); // End-of-instructions marker @@ -332,8 +400,8 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { catch (GuacamoleException e) { - // Detach and close - session.detachTunnel(tunnel); + // Deregister and close + deregisterTunnel(tunnel); tunnel.close(); throw e; @@ -350,8 +418,8 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { // Log typically frequent I/O error if desired logger.debug("Error writing to servlet output stream", e); - // Detach and close - session.detachTunnel(tunnel); + // Deregister and close + deregisterTunnel(tunnel); tunnel.close(); } @@ -366,25 +434,27 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { * This function should in general not be overridden, as it already * contains a proper implementation of the write operation. * - * @param request The HttpServletRequest associated with the write request - * received. Any data to be written will be specified within - * the body of this request. - * @param response The HttpServletResponse associated with the write request - * received. - * @param tunnelUUID The UUID of the tunnel to write to, as specified in - * the write request. This tunnel must be attached to - * the Guacamole session. - * @throws GuacamoleException If an error occurs while handling the write - * request. + * @param request + * The HttpServletRequest associated with the write request received. + * Any data to be written will be specified within the body of this + * request. + * + * @param response + * The HttpServletResponse associated with the write request received. + * + * @param tunnelUUID + * The UUID of the tunnel to write to, as specified in the write + * request. This tunnel must have been created by a previous call to + * doConnect(). + * + * @throws GuacamoleException + * If an error occurs while handling the write request. */ - protected void doWrite(HttpServletRequest request, HttpServletResponse response, String tunnelUUID) throws GuacamoleException { + protected void doWrite(HttpServletRequest request, + HttpServletResponse response, String tunnelUUID) + throws GuacamoleException { - HttpSession httpSession = request.getSession(false); - GuacamoleSession session = new GuacamoleSession(httpSession); - - GuacamoleTunnel tunnel = session.getTunnel(tunnelUUID); - if (tunnel == null) - throw new GuacamoleResourceNotFoundException("No such tunnel."); + GuacamoleTunnel tunnel = getTunnel(tunnelUUID); // We still need to set the content type to avoid the default of // text/html, as such a content type would cause some browsers to @@ -430,8 +500,8 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet { } catch (IOException e) { - // Detach and close - session.detachTunnel(tunnel); + // Deregister and close + deregisterTunnel(tunnel); tunnel.close(); throw new GuacamoleServerException("I/O Error sending data to server: " + e.getMessage(), e); diff --git a/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleSession.java b/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleSession.java index 965112de9..7099cc855 100644 --- a/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleSession.java +++ b/guacamole-common/src/main/java/org/glyptodon/guacamole/servlet/GuacamoleSession.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * Copyright (C) 2015 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 @@ -22,12 +22,7 @@ package org.glyptodon.guacamole.servlet; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import javax.servlet.http.HttpSession; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,75 +32,70 @@ import org.slf4j.LoggerFactory; * * @author Michael Jumper */ +@Deprecated public class GuacamoleSession { /** * Logger for this class. */ - private Logger logger = LoggerFactory.getLogger(GuacamoleSession.class); + private final Logger logger = LoggerFactory.getLogger(GuacamoleSession.class); /** - * Map of all currently attached tunnels, indexed by tunnel UUID. - */ - private ConcurrentMap tunnels; - - /** - * Creates a new GuacamoleSession, storing and retrieving tunnels from the - * given HttpSession. Note that the true Guacamole session is tied to the - * HttpSession provided, thus creating a new GuacamoleSession does not - * create a new Guacamole session; it merely creates a new object for - * accessing the tunnels of an existing Guacamole session represented by - * the provided HttpSession. + * Creates a new GuacamoleSession. In prior versions of Guacamole, the + * GuacamoleSession object stored the tunnels associated with a particular + * user's use of the HTTP tunnel. The HTTP tunnel now stores all of these + * tunnels itself, and thus this class is no longer necessary. Its use will + * result in a warning being logged, and its functions will have no effect. * - * @param session The HttpSession to use as tunnel storage. - * @throws GuacamoleException If session is null. + * @param session + * The HttpSession that older versions of Guacamole would use as tunnel + * storage. This parameter is now ignored, and the GuacamoleSession + * class overall is deprecated. */ - @SuppressWarnings("unchecked") - public GuacamoleSession(HttpSession session) throws GuacamoleException { - - if (session == null) - throw new GuacamoleSecurityException("User has no session."); - - synchronized (session) { - - tunnels = (ConcurrentMap) session.getAttribute("GUAC_TUNNELS"); - if (tunnels == null) { - tunnels = new ConcurrentHashMap(); - session.setAttribute("GUAC_TUNNELS", tunnels); - } - - } - + public GuacamoleSession(HttpSession session) { + logger.warn("GuacamoleSession is deprecated. It is no longer " + + "necessary and its use will have no effect."); } /** - * Attaches the given tunnel to this GuacamoleSession. - * @param tunnel The tunnel to attach to this GucacamoleSession. + * Attaches the given tunnel to this GuacamoleSession. The GuacamoleSession + * class is now deprecated, and this function has no effect. + * + * @param tunnel + * The tunnel to attach to this GucacamoleSession. */ public void attachTunnel(GuacamoleTunnel tunnel) { - tunnels.put(tunnel.getUUID().toString(), tunnel); - logger.debug("Attached tunnel {}.", tunnel.getUUID()); + // Deprecated - no effect } /** - * Detaches the given tunnel to this GuacamoleSession. - * @param tunnel The tunnel to detach to this GucacamoleSession. + * Detaches the given tunnel to this GuacamoleSession. The GuacamoleSession + * class is now deprecated, and this function has no effect. + * + * @param tunnel + * The tunnel to detach to this GucacamoleSession. */ public void detachTunnel(GuacamoleTunnel tunnel) { - tunnels.remove(tunnel.getUUID().toString()); - logger.debug("Detached tunnel {}.", tunnel.getUUID()); + // Deprecated - no effect } /** * Returns the tunnel with the given UUID attached to this GuacamoleSession, - * if any. + * if any. The GuacamoleSession class is now deprecated, and this function + * has no effect. It will ALWAYS return null. * - * @param tunnelUUID The UUID of an attached tunnel. - * @return The tunnel corresponding to the given UUID, if attached, or null - * if no such tunnel is attached. + * @param tunnelUUID + * The UUID of an attached tunnel. + * + * @return + * The tunnel corresponding to the given UUID, if attached, or null if + * if no such tunnel is attached. */ public GuacamoleTunnel getTunnel(String tunnelUUID) { - return tunnels.get(tunnelUUID); + + // Deprecated - no effect + return null; + } }