mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUAC-878: Add support for Jetty 9.0.3 WebSocket.
This commit is contained in:
		| @@ -56,6 +56,7 @@ public class WebSocketSupportLoader implements ServletContextListener { | ||||
|      */ | ||||
|     private static final String[] WEBSOCKET_CLASSES = { | ||||
|         "org.glyptodon.guacamole.net.basic.websocket.jetty8.BasicGuacamoleWebSocketTunnelServlet", | ||||
|         "org.glyptodon.guacamole.net.basic.websocket.jetty9.BasicGuacamoleWebSocketTunnelServlet", | ||||
|         "org.glyptodon.guacamole.net.basic.websocket.tomcat.BasicGuacamoleWebSocketTunnelServlet" | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -1,181 +0,0 @@ | ||||
| /* | ||||
|  * 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.glyptodon.guacamole.net.basic.websocket.jetty; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import org.glyptodon.guacamole.GuacamoleException; | ||||
| import org.glyptodon.guacamole.io.GuacamoleReader; | ||||
| import org.glyptodon.guacamole.io.GuacamoleWriter; | ||||
| import org.glyptodon.guacamole.net.GuacamoleTunnel; | ||||
| import org.eclipse.jetty.websocket.WebSocket; | ||||
| import org.eclipse.jetty.websocket.WebSocket.Connection; | ||||
| import org.eclipse.jetty.websocket.WebSocketServlet; | ||||
| import org.glyptodon.guacamole.GuacamoleClientException; | ||||
| import org.glyptodon.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) { | ||||
|  | ||||
|         // Get tunnel | ||||
|         final GuacamoleTunnel tunnel; | ||||
|  | ||||
|         try { | ||||
|             tunnel = doConnect(request); | ||||
|         } | ||||
|         catch (GuacamoleException e) { | ||||
|             logger.error("Error connecting WebSocket tunnel.", e); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // Return new WebSocket which communicates through tunnel | ||||
|         return new WebSocket.OnTextMessage() { | ||||
|  | ||||
|             @Override | ||||
|             public void onMessage(String string) { | ||||
|                 GuacamoleWriter writer = tunnel.acquireWriter(); | ||||
|  | ||||
|                 // Write message received | ||||
|                 try { | ||||
|                     writer.write(string.toCharArray()); | ||||
|                 } | ||||
|                 catch (GuacamoleException e) { | ||||
|                     logger.debug("Tunnel write failed.", e); | ||||
|                 } | ||||
|  | ||||
|                 tunnel.releaseWriter(); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onOpen(final Connection connection) { | ||||
|  | ||||
|                 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.warn("Client request rejected: {}", e.getMessage()); | ||||
|                                 closeConnection(connection, e.getStatus()); | ||||
|                             } | ||||
|                             catch (GuacamoleException e) { | ||||
|                                 logger.error("Internal server error.", e); | ||||
|                                 closeConnection(connection, e.getStatus()); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                         catch (IOException e) { | ||||
|                             logger.debug("Tunnel read failed due to I/O error.", e); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 }; | ||||
|  | ||||
|                 readThread.start(); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onClose(int i, String string) { | ||||
|                 try { | ||||
|                     tunnel.close(); | ||||
|                 } | ||||
|                 catch (GuacamoleException e) { | ||||
|                     logger.debug("Unable to close WebSocket tunnel.", e); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected abstract GuacamoleTunnel doConnect(HttpServletRequest request) | ||||
|             throws GuacamoleException; | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Glyptodon LLC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package org.glyptodon.guacamole.net.basic.websocket.jetty9; | ||||
|  | ||||
| import org.eclipse.jetty.websocket.api.UpgradeRequest; | ||||
| import org.eclipse.jetty.websocket.api.UpgradeResponse; | ||||
| import org.eclipse.jetty.websocket.servlet.WebSocketCreator; | ||||
|  | ||||
| /** | ||||
|  * WebSocketCreator which selects the appropriate WebSocketListener | ||||
|  * implementation if the "guacamole" subprotocol is in use. | ||||
|  *  | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| public class BasicGuacamoleWebSocketCreator implements WebSocketCreator { | ||||
|  | ||||
|     @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 BasicGuacamoleWebSocketTunnelListener(); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // Invalid protocol | ||||
|         return null; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2013 Glyptodon LLC | ||||
|  * 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 | ||||
| @@ -20,25 +20,24 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package org.glyptodon.guacamole.net.basic.websocket.jetty; | ||||
| package org.glyptodon.guacamole.net.basic.websocket.jetty9; | ||||
| 
 | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import org.eclipse.jetty.websocket.api.Session; | ||||
| import org.glyptodon.guacamole.GuacamoleException; | ||||
| import org.glyptodon.guacamole.net.GuacamoleTunnel; | ||||
| import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility; | ||||
| import org.glyptodon.guacamole.net.basic.HTTPTunnelRequest; | ||||
| 
 | ||||
| /** | ||||
|  * Tunnel servlet implementation which uses WebSocket as a tunnel backend, | ||||
|  * rather than HTTP, properly parsing connection IDs included in the connection | ||||
|  * request. | ||||
|  * WebSocket listener implementation which properly parses connection IDs | ||||
|  * included in the connection request. | ||||
|  *  | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| public class BasicGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet { | ||||
| public class BasicGuacamoleWebSocketTunnelListener extends GuacamoleWebSocketTunnelListener { | ||||
| 
 | ||||
|     @Override | ||||
|     protected GuacamoleTunnel doConnect(HttpServletRequest request) | ||||
|             throws GuacamoleException { | ||||
|         return BasicTunnelRequestUtility.createTunnel(new HTTPTunnelRequest(request)); | ||||
|     protected GuacamoleTunnel createTunnel(Session session) throws GuacamoleException { | ||||
|         return BasicTunnelRequestUtility.createTunnel(new WebSocketTunnelRequest(session.getUpgradeRequest())); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Glyptodon LLC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package org.glyptodon.guacamole.net.basic.websocket.jetty9; | ||||
|  | ||||
| import org.eclipse.jetty.websocket.servlet.WebSocketServlet; | ||||
| import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; | ||||
|  | ||||
| /** | ||||
|  * A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet. | ||||
|  * | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| public class BasicGuacamoleWebSocketTunnelServlet extends WebSocketServlet { | ||||
|  | ||||
|     @Override | ||||
|     public void configure(WebSocketServletFactory factory) { | ||||
|  | ||||
|         // Register WebSocket implementation | ||||
|         factory.setCreator(new BasicGuacamoleWebSocketCreator()); | ||||
|          | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,236 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Glyptodon LLC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package org.glyptodon.guacamole.net.basic.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.glyptodon.guacamole.GuacamoleClientException; | ||||
| import org.glyptodon.guacamole.GuacamoleException; | ||||
| import org.glyptodon.guacamole.io.GuacamoleReader; | ||||
| import org.glyptodon.guacamole.io.GuacamoleWriter; | ||||
| import org.glyptodon.guacamole.net.GuacamoleTunnel; | ||||
| import org.glyptodon.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(BasicGuacamoleWebSocketTunnelServlet.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.error("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("Error connecting WebSocket tunnel.", e); | ||||
|             closeConnection(session, e.getStatus()); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Set accepted subprotocol | ||||
|         for (String subprotocol : session.getUpgradeRequest().getSubProtocols()) { | ||||
|             if ("guacamole".equals(subprotocol)) { | ||||
|                 session.getUpgradeResponse().setAcceptedSubProtocol(subprotocol); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 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.warn("Client request rejected: {}", e.getMessage()); | ||||
|                         closeConnection(session, e.getStatus()); | ||||
|                     } | ||||
|                     catch (GuacamoleException e) { | ||||
|                         logger.error("Internal server error.", 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) { | ||||
|  | ||||
|         GuacamoleWriter writer = tunnel.acquireWriter(); | ||||
|  | ||||
|         try { | ||||
|             // Write received message | ||||
|             writer.write(message.toCharArray()); | ||||
|         } | ||||
|         catch (GuacamoleException e) { | ||||
|             logger.debug("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 WebSocket tunnel.", e); | ||||
|         } | ||||
|  | ||||
|      } | ||||
|  | ||||
|     | ||||
|     @Override | ||||
|     public void onWebSocketClose(int statusCode, String reason) { | ||||
|  | ||||
|         try { | ||||
|             if (tunnel != null) | ||||
|                 tunnel.close(); | ||||
|         } | ||||
|         catch (GuacamoleException e) { | ||||
|             logger.debug("Unable to close WebSocket tunnel.", e); | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,83 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Glyptodon LLC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package org.glyptodon.guacamole.net.basic.websocket.jetty9; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import javax.servlet.http.HttpSession; | ||||
| import org.eclipse.jetty.websocket.api.UpgradeRequest; | ||||
| import org.glyptodon.guacamole.net.basic.TunnelRequest; | ||||
|  | ||||
| /** | ||||
|  * Jetty 9 WebSocket-specific implementation of TunnelRequest. | ||||
|  * | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| public class WebSocketTunnelRequest implements TunnelRequest { | ||||
|  | ||||
|     /** | ||||
|      * The wrapped UpgradeRequest. | ||||
|      */ | ||||
|     private final UpgradeRequest request; | ||||
|  | ||||
|     /** | ||||
|      * 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.request = request; | ||||
|         this.handshakeParameters = request.getParameterMap(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public HttpSession getSession() { | ||||
|         return (HttpSession) request.getSession(); | ||||
|     } | ||||
|  | ||||
|     @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 Arrays.asList(handshakeParameters.get(name)); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2013 Glyptodon LLC | ||||
|  * 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 | ||||
| @@ -21,9 +21,8 @@ | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Jetty WebSocket tunnel implementation. The classes here require at least | ||||
|  * Jetty 8, and may change significantly as there is no common WebSocket | ||||
|  * API for Java yet. | ||||
|  * 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.glyptodon.guacamole.net.basic.websocket.jetty; | ||||
| package org.glyptodon.guacamole.net.basic.websocket.jetty9; | ||||
| 
 | ||||
		Reference in New Issue
	
	Block a user