From c0205653979794e3a5f26b8309d3f1e10cce401f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 8 Apr 2011 16:28:12 -0700 Subject: [PATCH] Added tunnel registry and support for multiple tunnels per session. --- .../guacamole/net/GuacamoleSession.java | 109 ++++-------------- .../guacamole/net/tunnel/GuacamoleTunnel.java | 53 +++++++++ .../net/tunnel/GuacamoleTunnelServlet.java | 47 +++++--- 3 files changed, 109 insertions(+), 100 deletions(-) create mode 100644 guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnel.java diff --git a/guacamole-common/src/main/java/net/sourceforge/guacamole/net/GuacamoleSession.java b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/GuacamoleSession.java index 430a4aae5..7cd90d93a 100644 --- a/guacamole-common/src/main/java/net/sourceforge/guacamole/net/GuacamoleSession.java +++ b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/GuacamoleSession.java @@ -19,54 +19,16 @@ package net.sourceforge.guacamole.net; * along with this program. If not, see . */ -import java.util.concurrent.locks.ReentrantLock; +import net.sourceforge.guacamole.net.tunnel.GuacamoleTunnel; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionBindingListener; -import net.sourceforge.guacamole.GuacamoleClient; -import net.sourceforge.guacamole.GuacamoleTCPClient; import net.sourceforge.guacamole.GuacamoleException; public class GuacamoleSession { private final HttpSession session; - private SessionClient client; - private ReentrantLock instructionStreamLock; - - public class SessionClient extends GuacamoleClient implements HttpSessionBindingListener { - - private GuacamoleClient client; - - public SessionClient(GuacamoleClient client) { - this.client = client; - } - - public void valueBound(HttpSessionBindingEvent event) { - // Do nothing - } - - public void valueUnbound(HttpSessionBindingEvent event) { - try { - disconnect(); - } - catch (GuacamoleException e) { - // Ignore - } - } - - public void write(char[] data, int off, int len) throws GuacamoleException { - client.write(data, off, len); - } - - public char[] read() throws GuacamoleException { - return client.read(); - } - - public void disconnect() throws GuacamoleException { - client.disconnect(); - } - - } + private ConcurrentMap tunnels; public GuacamoleSession(HttpSession session) throws GuacamoleException { @@ -77,57 +39,30 @@ public class GuacamoleSession { synchronized (session) { - client = (SessionClient) session.getAttribute("CLIENT"); - instructionStreamLock = (ReentrantLock) session.getAttribute("INSTRUCTION_STREAM_LOCK"); - - } - - } - - public void attachClient(GuacamoleTCPClient client) throws GuacamoleException { - - synchronized (session) { - - this.client = new SessionClient(client); - session.setAttribute("CLIENT", this.client); - - instructionStreamLock = new ReentrantLock(); - session.setAttribute("INSTRUCTION_STREAM_LOCK", instructionStreamLock); - - } - - } - - public SessionClient getClient() throws GuacamoleException { - synchronized (session) { - - if (client == null) - throw new GuacamoleException("Client not yet attached."); - - return client; - } - } - - public void invalidate() { - session.invalidate(); - } - - public void detachClient() throws GuacamoleException { - - synchronized (session) { - - if (client != null) { - client.disconnect(); - session.removeAttribute("CLIENT"); - client = null; + tunnels = (ConcurrentMap) session.getAttribute("GUAC_TUNNELS"); + if (tunnels == null) { + tunnels = new ConcurrentHashMap(); + session.setAttribute("GUAC_TUNNELS", tunnels); } } } - public ReentrantLock getInstructionStreamLock() { - return instructionStreamLock; + public void invalidate() { + session.invalidate(); + } + + public void attachTunnel(GuacamoleTunnel tunnel) throws GuacamoleException { + tunnels.put(tunnel.getUUID().toString(), tunnel); + } + + public void detachTunnel(GuacamoleTunnel tunnel) throws GuacamoleException { + tunnels.remove(tunnel.getUUID().toString()); + } + + public GuacamoleTunnel getTunnel(String tunnelUUID) { + return tunnels.get(tunnelUUID); } } diff --git a/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnel.java b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnel.java new file mode 100644 index 000000000..8f324141e --- /dev/null +++ b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnel.java @@ -0,0 +1,53 @@ + +package net.sourceforge.guacamole.net.tunnel; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import java.util.UUID; +import java.util.concurrent.locks.ReentrantLock; +import net.sourceforge.guacamole.GuacamoleClient; +import net.sourceforge.guacamole.GuacamoleException; + +public class GuacamoleTunnel { + + private UUID uuid; + private GuacamoleClient client; + private ReentrantLock instructionStreamLock; + + public GuacamoleTunnel(GuacamoleClient client) throws GuacamoleException { + + this.client = client; + instructionStreamLock = new ReentrantLock(); + uuid = UUID.randomUUID(); + + } + + public GuacamoleClient getClient() throws GuacamoleException { + return client; + } + + public ReentrantLock getInstructionStreamLock() { + return instructionStreamLock; + } + + public UUID getUUID() { + return uuid; + } + +} diff --git a/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnelServlet.java b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnelServlet.java index 6a09fa0f7..fa270fc86 100644 --- a/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnelServlet.java +++ b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/tunnel/GuacamoleTunnelServlet.java @@ -54,14 +54,25 @@ public abstract class GuacamoleTunnelServlet extends HttpServlet { if (query == null) throw new GuacamoleException("No query string provided."); - if (query.equals("connect")) - doConnect(request, response); + if (query.equals("connect")) { - else if(query.equals("read")) - doRead(request, response); + GuacamoleTunnel tunnel = doConnect(request); + if (tunnel != null) { + try { + response.getWriter().println(tunnel.getUUID().toString()); + } + catch (IOException e) { + throw new GuacamoleException(e); + } + } - else if(query.equals("write")) - doWrite(request, response); + } + + else if(query.startsWith("read:")) + doRead(request, response, query.substring(5)); + + else if(query.startsWith("write:")) + doWrite(request, response, query.substring(6)); else throw new GuacamoleException("Invalid tunnel operation: " + query); @@ -72,14 +83,18 @@ public abstract class GuacamoleTunnelServlet extends HttpServlet { } - protected abstract void doConnect(HttpServletRequest request, HttpServletResponse response) throws GuacamoleException; + protected abstract GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException; - protected void doRead(HttpServletRequest request, HttpServletResponse response) throws GuacamoleException { + protected void doRead(HttpServletRequest request, HttpServletResponse response, String tunnelUUID) throws GuacamoleException { HttpSession httpSession = request.getSession(false); GuacamoleSession session = new GuacamoleSession(httpSession); - ReentrantLock instructionStreamLock = session.getInstructionStreamLock(); + GuacamoleTunnel tunnel = session.getTunnel(tunnelUUID); + if (tunnel == null) + throw new GuacamoleException("No such tunnel."); + + ReentrantLock instructionStreamLock = tunnel.getInstructionStreamLock(); instructionStreamLock.lock(); try { @@ -94,7 +109,7 @@ public abstract class GuacamoleTunnelServlet extends HttpServlet { try { // Query new update from server - GuacamoleClient client = session.getClient(); + GuacamoleClient client = tunnel.getClient(); // For all messages, until another stream is ready (we send at least one message) char[] message; @@ -112,7 +127,7 @@ public abstract class GuacamoleTunnelServlet extends HttpServlet { } if (message == null) { - session.detachClient(); + session.detachTunnel(tunnel); throw new GuacamoleException("Disconnected."); } @@ -141,11 +156,15 @@ public abstract class GuacamoleTunnelServlet extends HttpServlet { } - protected void doWrite(HttpServletRequest request, HttpServletResponse response) 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 GuacamoleException("No such tunnel."); + // 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 // attempt to parse the result, even though the JavaScript client @@ -156,12 +175,14 @@ public abstract class GuacamoleTunnelServlet extends HttpServlet { // Send data try { + GuacamoleClient client = tunnel.getClient(); + Reader input = request.getReader(); char[] buffer = new char[8192]; int length; while ((length = input.read(buffer, 0, buffer.length)) != -1) - session.getClient().write(buffer, 0, length); + client.write(buffer, 0, length); } catch (IOException e) {