You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2016/03/29 06:20:30 UTC
[21/51] [abbrv] incubator-guacamole-client git commit: GUACAMOLE-1:
Rename Basic* classes sensibly. Refactor tunnel classes into own packages.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java
new file mode 100644
index 0000000..3607b3d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2013 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.apache.guacamole.tunnel;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.List;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleSecurityException;
+import org.apache.guacamole.GuacamoleSecurityException;
+import org.apache.guacamole.GuacamoleSession;
+import org.apache.guacamole.GuacamoleUnauthorizedException;
+import org.apache.guacamole.GuacamoleUnauthorizedException;
+import org.apache.guacamole.net.DelegatingGuacamoleTunnel;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.rest.ObjectRetrievalService;
+import org.apache.guacamole.rest.auth.AuthenticationService;
+import org.apache.guacamole.protocol.GuacamoleClientInformation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class that takes a standard request from the Guacamole JavaScript
+ * client and produces the corresponding GuacamoleTunnel. The implementation
+ * of this utility is specific to the form of request used by the upstream
+ * Guacamole web application, and is not necessarily useful to applications
+ * that use purely the Guacamole API.
+ *
+ * @author Michael Jumper
+ * @author Vasily Loginov
+ */
+@Singleton
+public class TunnelRequestService {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(TunnelRequestService.class);
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * Service for convenient retrieval of objects.
+ */
+ @Inject
+ private ObjectRetrievalService retrievalService;
+
+ /**
+ * Reads and returns the client information provided within the given
+ * request.
+ *
+ * @param request
+ * The request describing tunnel to create.
+ *
+ * @return GuacamoleClientInformation
+ * An object containing information about the client sending the tunnel
+ * request.
+ *
+ * @throws GuacamoleException
+ * If the parameters of the tunnel request are invalid.
+ */
+ protected GuacamoleClientInformation getClientInformation(TunnelRequest request)
+ throws GuacamoleException {
+
+ // Get client information
+ GuacamoleClientInformation info = new GuacamoleClientInformation();
+
+ // Set width if provided
+ Integer width = request.getWidth();
+ if (width != null)
+ info.setOptimalScreenWidth(width);
+
+ // Set height if provided
+ Integer height = request.getHeight();
+ if (height != null)
+ info.setOptimalScreenHeight(height);
+
+ // Set resolution if provided
+ Integer dpi = request.getDPI();
+ if (dpi != null)
+ info.setOptimalResolution(dpi);
+
+ // Add audio mimetypes
+ List<String> audioMimetypes = request.getAudioMimetypes();
+ if (audioMimetypes != null)
+ info.getAudioMimetypes().addAll(audioMimetypes);
+
+ // Add video mimetypes
+ List<String> videoMimetypes = request.getVideoMimetypes();
+ if (videoMimetypes != null)
+ info.getVideoMimetypes().addAll(videoMimetypes);
+
+ // Add image mimetypes
+ List<String> imageMimetypes = request.getImageMimetypes();
+ if (imageMimetypes != null)
+ info.getImageMimetypes().addAll(imageMimetypes);
+
+ return info;
+ }
+
+ /**
+ * Creates a new tunnel using which is connected to the connection or
+ * connection group identifier by the given ID. Client information
+ * is specified in the {@code info} parameter.
+ *
+ * @param context
+ * The UserContext associated with the user for whom the tunnel is
+ * being created.
+ *
+ * @param type
+ * The type of object being connected to (connection or group).
+ *
+ * @param id
+ * The id of the connection or group being connected to.
+ *
+ * @param info
+ * Information describing the connected Guacamole client.
+ *
+ * @return
+ * A new tunnel, connected as required by the request.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while creating the tunnel.
+ */
+ protected GuacamoleTunnel createConnectedTunnel(UserContext context,
+ final TunnelRequest.Type type, String id,
+ GuacamoleClientInformation info)
+ throws GuacamoleException {
+
+ // Create connected tunnel from identifier
+ GuacamoleTunnel tunnel = null;
+ switch (type) {
+
+ // Connection identifiers
+ case CONNECTION: {
+
+ // Get connection directory
+ Directory<Connection> directory = context.getConnectionDirectory();
+
+ // Get authorized connection
+ Connection connection = directory.get(id);
+ if (connection == null) {
+ logger.info("Connection \"{}\" does not exist for user \"{}\".", id, context.self().getIdentifier());
+ throw new GuacamoleSecurityException("Requested connection is not authorized.");
+ }
+
+ // Connect tunnel
+ tunnel = connection.connect(info);
+ logger.info("User \"{}\" connected to connection \"{}\".", context.self().getIdentifier(), id);
+ break;
+ }
+
+ // Connection group identifiers
+ case CONNECTION_GROUP: {
+
+ // Get connection group directory
+ Directory<ConnectionGroup> directory = context.getConnectionGroupDirectory();
+
+ // Get authorized connection group
+ ConnectionGroup group = directory.get(id);
+ if (group == null) {
+ logger.info("Connection group \"{}\" does not exist for user \"{}\".", id, context.self().getIdentifier());
+ throw new GuacamoleSecurityException("Requested connection group is not authorized.");
+ }
+
+ // Connect tunnel
+ tunnel = group.connect(info);
+ logger.info("User \"{}\" connected to group \"{}\".", context.self().getIdentifier(), id);
+ break;
+ }
+
+ // Type is guaranteed to be one of the above
+ default:
+ assert(false);
+
+ }
+
+ return tunnel;
+
+ }
+
+ /**
+ * Associates the given tunnel with the given session, returning a wrapped
+ * version of the same tunnel which automatically handles closure and
+ * removal from the session.
+ *
+ * @param tunnel
+ * The connected tunnel to wrap and monitor.
+ *
+ * @param authToken
+ * The authentication token associated with the given session. If
+ * provided, this token will be automatically invalidated (and the
+ * corresponding session destroyed) if tunnel errors imply that the
+ * user is no longer authorized.
+ *
+ * @param session
+ * The Guacamole session to associate the tunnel with.
+ *
+ * @param type
+ * The type of object being connected to (connection or group).
+ *
+ * @param id
+ * The id of the connection or group being connected to.
+ *
+ * @return
+ * A new tunnel, associated with the given session, which delegates all
+ * functionality to the given tunnel while monitoring and automatically
+ * handling closure.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while obtaining the tunnel.
+ */
+ protected GuacamoleTunnel createAssociatedTunnel(GuacamoleTunnel tunnel,
+ final String authToken, final GuacamoleSession session,
+ final TunnelRequest.Type type, final String id)
+ throws GuacamoleException {
+
+ // Monitor tunnel closure and data
+ GuacamoleTunnel monitoredTunnel = new DelegatingGuacamoleTunnel(tunnel) {
+
+ /**
+ * The time the connection began, measured in milliseconds since
+ * midnight, January 1, 1970 UTC.
+ */
+ private final long connectionStartTime = System.currentTimeMillis();
+
+ @Override
+ public void close() throws GuacamoleException {
+
+ long connectionEndTime = System.currentTimeMillis();
+ long duration = connectionEndTime - connectionStartTime;
+
+ // Log closure
+ switch (type) {
+
+ // Connection identifiers
+ case CONNECTION:
+ logger.info("User \"{}\" disconnected from connection \"{}\". Duration: {} milliseconds",
+ session.getAuthenticatedUser().getIdentifier(), id, duration);
+ break;
+
+ // Connection group identifiers
+ case CONNECTION_GROUP:
+ logger.info("User \"{}\" disconnected from connection group \"{}\". Duration: {} milliseconds",
+ session.getAuthenticatedUser().getIdentifier(), id, duration);
+ break;
+
+ // Type is guaranteed to be one of the above
+ default:
+ assert(false);
+
+ }
+
+ try {
+
+ // Close and clean up tunnel
+ session.removeTunnel(getUUID().toString());
+ super.close();
+
+ }
+
+ // Ensure any associated session is invalidated if unauthorized
+ catch (GuacamoleUnauthorizedException e) {
+
+ // If there is an associated auth token, invalidate it
+ if (authenticationService.destroyGuacamoleSession(authToken))
+ logger.debug("Implicitly invalidated session for token \"{}\".", authToken);
+
+ // Continue with exception processing
+ throw e;
+
+ }
+
+ }
+
+ };
+
+ // Associate tunnel with session
+ session.addTunnel(monitoredTunnel);
+ return monitoredTunnel;
+
+ }
+
+ /**
+ * Creates a new tunnel using the parameters and credentials present in
+ * the given request.
+ *
+ * @param request
+ * The request describing the tunnel to create.
+ *
+ * @return
+ * The created tunnel, or null if the tunnel could not be created.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while creating the tunnel.
+ */
+ public GuacamoleTunnel createTunnel(TunnelRequest request)
+ throws GuacamoleException {
+
+ // Parse request parameters
+ String authToken = request.getAuthenticationToken();
+ String id = request.getIdentifier();
+ TunnelRequest.Type type = request.getType();
+ String authProviderIdentifier = request.getAuthenticationProviderIdentifier();
+ GuacamoleClientInformation info = getClientInformation(request);
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ try {
+
+ // Create connected tunnel using provided connection ID and client information
+ GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type, id, info);
+
+ // Associate tunnel with session
+ return createAssociatedTunnel(tunnel, authToken, session, type, id);
+
+ }
+
+ // Ensure any associated session is invalidated if unauthorized
+ catch (GuacamoleUnauthorizedException e) {
+
+ // If there is an associated auth token, invalidate it
+ if (authenticationService.destroyGuacamoleSession(authToken))
+ logger.debug("Implicitly invalidated session for token \"{}\".", authToken);
+
+ // Continue with exception processing
+ throw e;
+
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java
new file mode 100644
index 0000000..072c792
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.apache.guacamole.tunnel.http;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.guacamole.tunnel.TunnelRequest;
+
+/**
+ * HTTP-specific implementation of TunnelRequest.
+ *
+ * @author Michael Jumper
+ */
+public class HTTPTunnelRequest extends TunnelRequest {
+
+ /**
+ * A copy of the parameters obtained from the HttpServletRequest used to
+ * construct the HTTPTunnelRequest.
+ */
+ private final Map<String, List<String>> parameterMap =
+ new HashMap<String, List<String>>();
+
+ /**
+ * Creates a HTTPTunnelRequest which copies and exposes the parameters
+ * from the given HttpServletRequest.
+ *
+ * @param request
+ * The HttpServletRequest to copy parameter values from.
+ */
+ @SuppressWarnings("unchecked") // getParameterMap() is defined as returning Map<String, String[]>
+ public HTTPTunnelRequest(HttpServletRequest request) {
+
+ // For each parameter
+ for (Map.Entry<String, String[]> mapEntry : ((Map<String, String[]>)
+ request.getParameterMap()).entrySet()) {
+
+ // Get parameter name and corresponding values
+ String parameterName = mapEntry.getKey();
+ List<String> parameterValues = Arrays.asList(mapEntry.getValue());
+
+ // Store copy of all values in our own map
+ parameterMap.put(
+ parameterName,
+ new ArrayList<String>(parameterValues)
+ );
+
+ }
+
+ }
+
+ @Override
+ public String getParameter(String name) {
+ List<String> values = getParameterValues(name);
+
+ // Return the first value from the list if available
+ if (values != null && !values.isEmpty())
+ return values.get(0);
+
+ return null;
+ }
+
+ @Override
+ public List<String> getParameterValues(String name) {
+ return parameterMap.get(name);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java
new file mode 100644
index 0000000..986c684
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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.apache.guacamole.tunnel.http;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Connects users to a tunnel associated with the authorized connection
+ * having the given ID.
+ *
+ * @author Michael Jumper
+ */
+@Singleton
+public class RestrictedGuacamoleHTTPTunnelServlet extends GuacamoleHTTPTunnelServlet {
+
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleHTTPTunnelServlet.class);
+
+ @Override
+ protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
+
+ // Attempt to create HTTP tunnel
+ GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new HTTPTunnelRequest(request));
+
+ // If successful, warn of lack of WebSocket
+ logger.info("Using HTTP tunnel (not WebSocket). Performance may be sub-optimal.");
+
+ return tunnel;
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/http/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/http/package-info.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/package-info.java
new file mode 100644
index 0000000..6b732e2
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/http/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes which leverage Guacamole's built-in HTTP tunnel implementation.
+ */
+package org.apache.guacamole.tunnel.http;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/package-info.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/package-info.java
new file mode 100644
index 0000000..b06f8e4
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes which are common to all tunnel implementations.
+ */
+package org.apache.guacamole.tunnel;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java
new file mode 100644
index 0000000..09b7487
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java
@@ -0,0 +1,121 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket;
+
+import com.google.inject.Provider;
+import java.util.Map;
+import javax.websocket.EndpointConfig;
+import javax.websocket.HandshakeResponse;
+import javax.websocket.Session;
+import javax.websocket.server.HandshakeRequest;
+import javax.websocket.server.ServerEndpointConfig;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.tunnel.TunnelRequest;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+import org.apache.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint;
+import org.apache.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint;
+
+/**
+ * Tunnel implementation which uses WebSocket as a tunnel backend, rather than
+ * HTTP, properly parsing connection IDs included in the connection request.
+ */
+public class RestrictedGuacamoleWebSocketTunnelEndpoint extends GuacamoleWebSocketTunnelEndpoint {
+
+ /**
+ * Unique string which shall be used to store the TunnelRequest
+ * associated with a WebSocket connection.
+ */
+ private static final String TUNNEL_REQUEST_PROPERTY = "WS_GUAC_TUNNEL_REQUEST";
+
+ /**
+ * Unique string which shall be used to store the TunnelRequestService to
+ * be used for processing TunnelRequests.
+ */
+ private static final String TUNNEL_REQUEST_SERVICE_PROPERTY = "WS_GUAC_TUNNEL_REQUEST_SERVICE";
+
+ /**
+ * Configurator implementation which stores the requested GuacamoleTunnel
+ * within the user properties. The GuacamoleTunnel will be later retrieved
+ * during the connection process.
+ */
+ public static class Configurator extends ServerEndpointConfig.Configurator {
+
+ /**
+ * Provider which provides instances of a service for handling
+ * tunnel requests.
+ */
+ private final Provider<TunnelRequestService> tunnelRequestServiceProvider;
+
+ /**
+ * Creates a new Configurator which uses the given tunnel request
+ * service provider to retrieve the necessary service to handle new
+ * connections requests.
+ *
+ * @param tunnelRequestServiceProvider
+ * The tunnel request service provider to use for all new
+ * connections.
+ */
+ public Configurator(Provider<TunnelRequestService> tunnelRequestServiceProvider) {
+ this.tunnelRequestServiceProvider = tunnelRequestServiceProvider;
+ }
+
+ @Override
+ public void modifyHandshake(ServerEndpointConfig config,
+ HandshakeRequest request, HandshakeResponse response) {
+
+ super.modifyHandshake(config, request, response);
+
+ // Store tunnel request and tunnel request service for retrieval
+ // upon WebSocket open
+ Map<String, Object> userProperties = config.getUserProperties();
+ userProperties.clear();
+ userProperties.put(TUNNEL_REQUEST_PROPERTY, new WebSocketTunnelRequest(request));
+ userProperties.put(TUNNEL_REQUEST_SERVICE_PROPERTY, tunnelRequestServiceProvider.get());
+
+ }
+
+ }
+
+ @Override
+ protected GuacamoleTunnel createTunnel(Session session,
+ EndpointConfig config) throws GuacamoleException {
+
+ Map<String, Object> userProperties = config.getUserProperties();
+
+ // Get original tunnel request
+ TunnelRequest tunnelRequest = (TunnelRequest) userProperties.get(TUNNEL_REQUEST_PROPERTY);
+ if (tunnelRequest == null)
+ return null;
+
+ // Get tunnel request service
+ TunnelRequestService tunnelRequestService = (TunnelRequestService) userProperties.get(TUNNEL_REQUEST_SERVICE_PROPERTY);
+ if (tunnelRequestService == null)
+ return null;
+
+ // Create and return tunnel
+ return tunnelRequestService.createTunnel(tunnelRequest);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java
new file mode 100644
index 0000000..9183b4d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java
@@ -0,0 +1,104 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket;
+
+import com.google.inject.Provider;
+import com.google.inject.servlet.ServletModule;
+import java.util.Arrays;
+import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
+import javax.websocket.server.ServerEndpointConfig;
+import org.apache.guacamole.tunnel.TunnelLoader;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Loads the JSR-356 WebSocket tunnel implementation.
+ *
+ * @author Michael Jumper
+ */
+public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
+
+ @Override
+ public boolean isSupported() {
+
+ try {
+
+ // Attempt to find WebSocket servlet
+ Class.forName("javax.websocket.Endpoint");
+
+ // Support found
+ return true;
+
+ }
+
+ // If no such servlet class, this particular WebSocket support
+ // is not present
+ catch (ClassNotFoundException e) {}
+ catch (NoClassDefFoundError e) {}
+
+ // Support not found
+ return false;
+
+ }
+
+ @Override
+ public void configureServlets() {
+
+ logger.info("Loading JSR-356 WebSocket support...");
+
+ // Get container
+ ServerContainer container = (ServerContainer) getServletContext().getAttribute("javax.websocket.server.ServerContainer");
+ if (container == null) {
+ logger.warn("ServerContainer attribute required by JSR-356 is missing. Cannot load JSR-356 WebSocket support.");
+ return;
+ }
+
+ Provider<TunnelRequestService> tunnelRequestServiceProvider = getProvider(TunnelRequestService.class);
+
+ // Build configuration for WebSocket tunnel
+ ServerEndpointConfig config =
+ ServerEndpointConfig.Builder.create(RestrictedGuacamoleWebSocketTunnelEndpoint.class, "/websocket-tunnel")
+ .configurator(new RestrictedGuacamoleWebSocketTunnelEndpoint.Configurator(tunnelRequestServiceProvider))
+ .subprotocols(Arrays.asList(new String[]{"guacamole"}))
+ .build();
+
+ try {
+
+ // Add configuration to container
+ container.addEndpoint(config);
+
+ }
+ catch (DeploymentException e) {
+ logger.error("Unable to deploy WebSocket tunnel.", e);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java
new file mode 100644
index 0000000..e5d424d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket;
+
+import java.util.List;
+import java.util.Map;
+import javax.websocket.server.HandshakeRequest;
+import org.apache.guacamole.tunnel.TunnelRequest;
+
+/**
+ * WebSocket-specific implementation of TunnelRequest.
+ *
+ * @author Michael Jumper
+ */
+public class WebSocketTunnelRequest extends TunnelRequest {
+
+ /**
+ * All parameters passed via HTTP to the WebSocket handshake.
+ */
+ private final Map<String, List<String>> handshakeParameters;
+
+ /**
+ * Creates a TunnelRequest implementation which delegates parameter and
+ * session retrieval to the given HandshakeRequest.
+ *
+ * @param request The HandshakeRequest to wrap.
+ */
+ public WebSocketTunnelRequest(HandshakeRequest request) {
+ this.handshakeParameters = request.getParameterMap();
+ }
+
+ @Override
+ public String getParameter(String name) {
+
+ // Pull list of values, if present
+ List<String> values = getParameterValues(name);
+ if (values == null || values.isEmpty())
+ return null;
+
+ // Return first parameter value arbitrarily
+ return values.get(0);
+
+ }
+
+ @Override
+ public List<String> getParameterValues(String name) {
+ return handshakeParameters.get(name);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java
new file mode 100644
index 0000000..619c606
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2013 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.apache.guacamole.tunnel.websocket.jetty8;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.io.GuacamoleReader;
+import org.apache.guacamole.io.GuacamoleWriter;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.eclipse.jetty.websocket.WebSocket;
+import org.eclipse.jetty.websocket.WebSocket.Connection;
+import org.eclipse.jetty.websocket.WebSocketServlet;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleConnectionClosedException;
+import org.apache.guacamole.tunnel.http.HTTPTunnelRequest;
+import org.apache.guacamole.tunnel.TunnelRequest;
+import org.apache.guacamole.protocol.GuacamoleStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet.
+ *
+ * @author Michael Jumper
+ */
+public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class);
+
+ /**
+ * The default, minimum buffer size for instructions.
+ */
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * Sends the given status on the given WebSocket connection and closes the
+ * connection.
+ *
+ * @param connection The WebSocket connection to close.
+ * @param guac_status The status to send.
+ */
+ public static void closeConnection(Connection connection,
+ GuacamoleStatus guac_status) {
+
+ connection.close(guac_status.getWebSocketCode(),
+ Integer.toString(guac_status.getGuacamoleStatusCode()));
+
+ }
+
+ @Override
+ public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
+
+ final TunnelRequest tunnelRequest = new HTTPTunnelRequest(request);
+
+ // Return new WebSocket which communicates through tunnel
+ return new WebSocket.OnTextMessage() {
+
+ /**
+ * The GuacamoleTunnel associated with the connected WebSocket. If
+ * the WebSocket has not yet been connected, this will be null.
+ */
+ private GuacamoleTunnel tunnel = null;
+
+ @Override
+ public void onMessage(String string) {
+
+ // Ignore inbound messages if there is no associated tunnel
+ if (tunnel == null)
+ return;
+
+ GuacamoleWriter writer = tunnel.acquireWriter();
+
+ // Write message received
+ try {
+ writer.write(string.toCharArray());
+ }
+ catch (GuacamoleConnectionClosedException e) {
+ logger.debug("Connection to guacd closed.", e);
+ }
+ catch (GuacamoleException e) {
+ logger.debug("WebSocket tunnel write failed.", e);
+ }
+
+ tunnel.releaseWriter();
+
+ }
+
+ @Override
+ public void onOpen(final Connection connection) {
+
+ try {
+ tunnel = doConnect(tunnelRequest);
+ }
+ catch (GuacamoleException e) {
+ logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
+ logger.debug("Error connecting WebSocket tunnel.", e);
+ closeConnection(connection, e.getStatus());
+ return;
+ }
+
+ // Do not start connection if tunnel does not exist
+ if (tunnel == null) {
+ closeConnection(connection, GuacamoleStatus.RESOURCE_NOT_FOUND);
+ return;
+ }
+
+ Thread readThread = new Thread() {
+
+ @Override
+ public void run() {
+
+ StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
+ GuacamoleReader reader = tunnel.acquireReader();
+ char[] readMessage;
+
+ try {
+
+ try {
+
+ // Attempt to read
+ while ((readMessage = reader.read()) != null) {
+
+ // Buffer message
+ buffer.append(readMessage);
+
+ // Flush if we expect to wait or buffer is getting full
+ if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
+ connection.sendMessage(buffer.toString());
+ buffer.setLength(0);
+ }
+
+ }
+
+ // No more data
+ closeConnection(connection, GuacamoleStatus.SUCCESS);
+
+ }
+
+ // Catch any thrown guacamole exception and attempt
+ // to pass within the WebSocket connection, logging
+ // each error appropriately.
+ catch (GuacamoleClientException e) {
+ logger.info("WebSocket connection terminated: {}", e.getMessage());
+ logger.debug("WebSocket connection terminated due to client error.", e);
+ closeConnection(connection, e.getStatus());
+ }
+ catch (GuacamoleConnectionClosedException e) {
+ logger.debug("Connection to guacd closed.", e);
+ closeConnection(connection, GuacamoleStatus.SUCCESS);
+ }
+ catch (GuacamoleException e) {
+ logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
+ logger.debug("Internal error during connection to guacd.", e);
+ closeConnection(connection, e.getStatus());
+ }
+
+ }
+ catch (IOException e) {
+ logger.debug("WebSocket tunnel read failed due to I/O error.", e);
+ }
+
+ }
+
+ };
+
+ readThread.start();
+
+ }
+
+ @Override
+ public void onClose(int i, String string) {
+ try {
+ if (tunnel != null)
+ tunnel.close();
+ }
+ catch (GuacamoleException e) {
+ logger.debug("Unable to close connection to guacd.", e);
+ }
+ }
+
+ };
+
+ }
+
+ /**
+ * 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).
+ *
+ * @param request
+ * The TunnelRequest 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(TunnelRequest request)
+ throws GuacamoleException;
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java
new file mode 100644
index 0000000..b7a82b9
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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.apache.guacamole.tunnel.websocket.jetty8;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+import org.apache.guacamole.tunnel.TunnelRequest;
+
+/**
+ * Tunnel servlet implementation which uses WebSocket as a tunnel backend,
+ * rather than HTTP, properly parsing connection IDs included in the connection
+ * request.
+ */
+@Singleton
+public class RestrictedGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet {
+
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
+ @Override
+ protected GuacamoleTunnel doConnect(TunnelRequest request)
+ throws GuacamoleException {
+ return tunnelRequestService.createTunnel(request);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java
new file mode 100644
index 0000000..79a09db
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket.jetty8;
+
+import com.google.inject.servlet.ServletModule;
+import org.apache.guacamole.tunnel.TunnelLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Loads the Jetty 8 WebSocket tunnel implementation.
+ *
+ * @author Michael Jumper
+ */
+public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
+
+ @Override
+ public boolean isSupported() {
+
+ try {
+
+ // Attempt to find WebSocket servlet
+ Class.forName("org.apache.guacamole.websocket.jetty8.BasicGuacamoleWebSocketTunnelServlet");
+
+ // Support found
+ return true;
+
+ }
+
+ // If no such servlet class, this particular WebSocket support
+ // is not present
+ catch (ClassNotFoundException e) {}
+ catch (NoClassDefFoundError e) {}
+
+ // Support not found
+ return false;
+
+ }
+
+ @Override
+ public void configureServlets() {
+
+ logger.info("Loading Jetty 8 WebSocket support...");
+ serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java
new file mode 100644
index 0000000..85e5758
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+/**
+ * Jetty 8 WebSocket tunnel implementation. The classes here require Jetty 8.
+ */
+package org.apache.guacamole.tunnel.websocket.jetty8;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java
new file mode 100644
index 0000000..4d118fb
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java
@@ -0,0 +1,243 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket.jetty9;
+
+import java.io.IOException;
+import org.eclipse.jetty.websocket.api.CloseStatus;
+import org.eclipse.jetty.websocket.api.RemoteEndpoint;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketListener;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleConnectionClosedException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.io.GuacamoleReader;
+import org.apache.guacamole.io.GuacamoleWriter;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.protocol.GuacamoleStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * WebSocket listener implementation which provides a Guacamole tunnel
+ *
+ * @author Michael Jumper
+ */
+public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListener {
+
+ /**
+ * The default, minimum buffer size for instructions.
+ */
+ private static final int BUFFER_SIZE = 8192;
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleWebSocketTunnelServlet.class);
+
+ /**
+ * The underlying GuacamoleTunnel. WebSocket reads/writes will be handled
+ * as reads/writes to this tunnel.
+ */
+ private GuacamoleTunnel tunnel;
+
+ /**
+ * Sends the given status on the given WebSocket connection and closes the
+ * connection.
+ *
+ * @param session The outbound WebSocket connection to close.
+ * @param guac_status The status to send.
+ */
+ private void closeConnection(Session session, GuacamoleStatus guac_status) {
+
+ try {
+ int code = guac_status.getWebSocketCode();
+ String message = Integer.toString(guac_status.getGuacamoleStatusCode());
+ session.close(new CloseStatus(code, message));
+ }
+ catch (IOException e) {
+ logger.debug("Unable to close WebSocket connection.", e);
+ }
+
+ }
+
+ /**
+ * Returns a new tunnel for the given session. How this tunnel is created
+ * or retrieved is implementation-dependent.
+ *
+ * @param session The session associated with the active WebSocket
+ * connection.
+ * @return A connected tunnel, or null if no such tunnel exists.
+ * @throws GuacamoleException If an error occurs while retrieving the
+ * tunnel, or if access to the tunnel is denied.
+ */
+ protected abstract GuacamoleTunnel createTunnel(Session session)
+ throws GuacamoleException;
+
+ @Override
+ public void onWebSocketConnect(final Session session) {
+
+ try {
+
+ // Get tunnel
+ tunnel = createTunnel(session);
+ if (tunnel == null) {
+ closeConnection(session, GuacamoleStatus.RESOURCE_NOT_FOUND);
+ return;
+ }
+
+ }
+ catch (GuacamoleException e) {
+ logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
+ logger.debug("Error connecting WebSocket tunnel.", e);
+ closeConnection(session, e.getStatus());
+ return;
+ }
+
+ // Prepare read transfer thread
+ Thread readThread = new Thread() {
+
+ /**
+ * Remote (client) side of this connection
+ */
+ private final RemoteEndpoint remote = session.getRemote();
+
+ @Override
+ public void run() {
+
+ StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
+ GuacamoleReader reader = tunnel.acquireReader();
+ char[] readMessage;
+
+ try {
+
+ try {
+
+ // Attempt to read
+ while ((readMessage = reader.read()) != null) {
+
+ // Buffer message
+ buffer.append(readMessage);
+
+ // Flush if we expect to wait or buffer is getting full
+ if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
+ remote.sendString(buffer.toString());
+ buffer.setLength(0);
+ }
+
+ }
+
+ // No more data
+ closeConnection(session, GuacamoleStatus.SUCCESS);
+
+ }
+
+ // Catch any thrown guacamole exception and attempt
+ // to pass within the WebSocket connection, logging
+ // each error appropriately.
+ catch (GuacamoleClientException e) {
+ logger.info("WebSocket connection terminated: {}", e.getMessage());
+ logger.debug("WebSocket connection terminated due to client error.", e);
+ closeConnection(session, e.getStatus());
+ }
+ catch (GuacamoleConnectionClosedException e) {
+ logger.debug("Connection to guacd closed.", e);
+ closeConnection(session, GuacamoleStatus.SUCCESS);
+ }
+ catch (GuacamoleException e) {
+ logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
+ logger.debug("Internal error during connection to guacd.", e);
+ closeConnection(session, e.getStatus());
+ }
+
+ }
+ catch (IOException e) {
+ logger.debug("I/O error prevents further reads.", e);
+ }
+
+ }
+
+ };
+
+ readThread.start();
+
+ }
+
+ @Override
+ public void onWebSocketText(String message) {
+
+ // Ignore inbound messages if there is no associated tunnel
+ if (tunnel == null)
+ return;
+
+ GuacamoleWriter writer = tunnel.acquireWriter();
+
+ try {
+ // Write received message
+ writer.write(message.toCharArray());
+ }
+ catch (GuacamoleConnectionClosedException e) {
+ logger.debug("Connection to guacd closed.", e);
+ }
+ catch (GuacamoleException e) {
+ logger.debug("WebSocket tunnel write failed.", e);
+ }
+
+ tunnel.releaseWriter();
+
+ }
+
+ @Override
+ public void onWebSocketBinary(byte[] payload, int offset, int length) {
+ throw new UnsupportedOperationException("Binary WebSocket messages are not supported.");
+ }
+
+ @Override
+ public void onWebSocketError(Throwable t) {
+
+ logger.debug("WebSocket tunnel closing due to error.", t);
+
+ try {
+ if (tunnel != null)
+ tunnel.close();
+ }
+ catch (GuacamoleException e) {
+ logger.debug("Unable to close connection to guacd.", e);
+ }
+
+ }
+
+
+ @Override
+ public void onWebSocketClose(int statusCode, String reason) {
+
+ try {
+ if (tunnel != null)
+ tunnel.close();
+ }
+ catch (GuacamoleException e) {
+ logger.debug("Unable to close connection to guacd.", e);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java
new file mode 100644
index 0000000..c15215d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java
@@ -0,0 +1,72 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket.jetty9;
+
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.UpgradeResponse;
+import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+
+/**
+ * WebSocketCreator which selects the appropriate WebSocketListener
+ * implementation if the "guacamole" subprotocol is in use.
+ *
+ * @author Michael Jumper
+ */
+public class RestrictedGuacamoleWebSocketCreator implements WebSocketCreator {
+
+ /**
+ * Service for handling tunnel requests.
+ */
+ private final TunnelRequestService tunnelRequestService;
+
+ /**
+ * Creates a new WebSocketCreator which uses the given TunnelRequestService
+ * to create new GuacamoleTunnels for inbound requests.
+ *
+ * @param tunnelRequestService The service to use for inbound tunnel
+ * requests.
+ */
+ public RestrictedGuacamoleWebSocketCreator(TunnelRequestService tunnelRequestService) {
+ this.tunnelRequestService = tunnelRequestService;
+ }
+
+ @Override
+ public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {
+
+ // Validate and use "guacamole" subprotocol
+ for (String subprotocol : request.getSubProtocols()) {
+
+ if ("guacamole".equals(subprotocol)) {
+ response.setAcceptedSubProtocol(subprotocol);
+ return new RestrictedGuacamoleWebSocketTunnelListener(tunnelRequestService);
+ }
+
+ }
+
+ // Invalid protocol
+ return null;
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java
new file mode 100644
index 0000000..f24663d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java
@@ -0,0 +1,59 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket.jetty9;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+
+/**
+ * WebSocket listener implementation which properly parses connection IDs
+ * included in the connection request.
+ *
+ * @author Michael Jumper
+ */
+public class RestrictedGuacamoleWebSocketTunnelListener extends GuacamoleWebSocketTunnelListener {
+
+ /**
+ * Service for handling tunnel requests.
+ */
+ private final TunnelRequestService tunnelRequestService;
+
+ /**
+ * Creates a new WebSocketListener which uses the given TunnelRequestService
+ * to create new GuacamoleTunnels for inbound requests.
+ *
+ * @param tunnelRequestService The service to use for inbound tunnel
+ * requests.
+ */
+ public RestrictedGuacamoleWebSocketTunnelListener(TunnelRequestService tunnelRequestService) {
+ this.tunnelRequestService = tunnelRequestService;
+ }
+
+ @Override
+ protected GuacamoleTunnel createTunnel(Session session) throws GuacamoleException {
+ return tunnelRequestService.createTunnel(new WebSocketTunnelRequest(session.getUpgradeRequest()));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java
new file mode 100644
index 0000000..84b25b9
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.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.apache.guacamole.tunnel.websocket.jetty9;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
+import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
+import org.apache.guacamole.tunnel.TunnelRequestService;
+
+/**
+ * A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet.
+ *
+ * @author Michael Jumper
+ */
+@Singleton
+public class RestrictedGuacamoleWebSocketTunnelServlet extends WebSocketServlet {
+
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
+ @Override
+ public void configure(WebSocketServletFactory factory) {
+
+ // Register WebSocket implementation
+ factory.setCreator(new RestrictedGuacamoleWebSocketCreator(tunnelRequestService));
+
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java
new file mode 100644
index 0000000..3477894
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket.jetty9;
+
+import com.google.inject.servlet.ServletModule;
+import org.apache.guacamole.tunnel.TunnelLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Loads the Jetty 9 WebSocket tunnel implementation.
+ *
+ * @author Michael Jumper
+ */
+public class WebSocketTunnelModule extends ServletModule implements TunnelLoader {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class);
+
+ @Override
+ public boolean isSupported() {
+
+ try {
+
+ // Attempt to find WebSocket servlet
+ Class.forName("org.apache.guacamole.websocket.jetty9.BasicGuacamoleWebSocketTunnelServlet");
+
+ // Support found
+ return true;
+
+ }
+
+ // If no such servlet class, this particular WebSocket support
+ // is not present
+ catch (ClassNotFoundException e) {}
+ catch (NoClassDefFoundError e) {}
+
+ // Support not found
+ return false;
+
+ }
+
+ @Override
+ public void configureServlets() {
+
+ logger.info("Loading Jetty 9 WebSocket support...");
+ serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java
new file mode 100644
index 0000000..56c7726
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.apache.guacamole.tunnel.websocket.jetty9;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.apache.guacamole.tunnel.TunnelRequest;
+
+/**
+ * Jetty 9 WebSocket-specific implementation of TunnelRequest.
+ *
+ * @author Michael Jumper
+ */
+public class WebSocketTunnelRequest extends TunnelRequest {
+
+ /**
+ * All parameters passed via HTTP to the WebSocket handshake.
+ */
+ private final Map<String, String[]> handshakeParameters;
+
+ /**
+ * Creates a TunnelRequest implementation which delegates parameter and
+ * session retrieval to the given UpgradeRequest.
+ *
+ * @param request The UpgradeRequest to wrap.
+ */
+ public WebSocketTunnelRequest(UpgradeRequest request) {
+ this.handshakeParameters = request.getParameterMap();
+ }
+
+ @Override
+ public String getParameter(String name) {
+
+ // Pull list of values, if present
+ List<String> values = getParameterValues(name);
+ if (values == null || values.isEmpty())
+ return null;
+
+ // Return first parameter value arbitrarily
+ return values.get(0);
+
+ }
+
+ @Override
+ public List<String> getParameterValues(String name) {
+
+ String[] values = handshakeParameters.get(name);
+ if (values == null)
+ return null;
+
+ return Arrays.asList(values);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java
new file mode 100644
index 0000000..13f2f0b
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Jetty 9 WebSocket tunnel implementation. The classes here require at least
+ * Jetty 9, prior to Jetty 9.1 (when support for JSR 356 was implemented).
+ */
+package org.apache.guacamole.tunnel.websocket.jetty9;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/713fc7f8/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java
new file mode 100644
index 0000000..2c273a5
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Standard WebSocket tunnel implementation. The classes here require a recent
+ * servlet container that supports JSR 356.
+ */
+package org.apache.guacamole.tunnel.websocket;
+