mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-10 07:01:21 +00:00
GUACAMOLE-1: Refactor org.glyptodon package/groupId to org.apache.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* 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.servlet;
|
||||
|
||||
import org.apache.guacamole.net.DelegatingGuacamoleTunnel;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
|
||||
/**
|
||||
* Tracks the last time a particular GuacamoleTunnel was accessed. This
|
||||
* information is not necessary for tunnels associated with WebSocket
|
||||
* connections, as each WebSocket connection has its own read thread which
|
||||
* continuously checks the state of the tunnel and which will automatically
|
||||
* timeout when the underlying socket times out, but the HTTP tunnel has no
|
||||
* such thread. Because the HTTP tunnel requires the stream to be split across
|
||||
* multiple requests, tracking of activity on the tunnel must be performed
|
||||
* independently of the HTTP requests.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
class GuacamoleHTTPTunnel extends DelegatingGuacamoleTunnel {
|
||||
|
||||
/**
|
||||
* The last time this tunnel was accessed.
|
||||
*/
|
||||
private long lastAccessedTime;
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleHTTPTunnel which wraps the given tunnel.
|
||||
* Absolutely all function calls on this new GuacamoleHTTPTunnel will be
|
||||
* delegated to the underlying GuacamoleTunnel.
|
||||
*
|
||||
* @param wrappedTunnel
|
||||
* The GuacamoleTunnel to wrap within this GuacamoleHTTPTunnel.
|
||||
*/
|
||||
public GuacamoleHTTPTunnel(GuacamoleTunnel wrappedTunnel) {
|
||||
super(wrappedTunnel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this tunnel, marking it as recently accessed.
|
||||
*/
|
||||
public void access() {
|
||||
lastAccessedTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time this tunnel was last accessed, as the number of
|
||||
* milliseconds since midnight January 1, 1970 GMT. Tunnel access must
|
||||
* be explicitly marked through calls to the access() function.
|
||||
*
|
||||
* @return
|
||||
* The time this tunnel was last accessed.
|
||||
*/
|
||||
public long getLastAccessedTime() {
|
||||
return lastAccessedTime;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* 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.servlet;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Map-style object which tracks in-use HTTP tunnels, automatically removing
|
||||
* and closing tunnels which have not been used recently. This class is
|
||||
* intended for use only within the GuacamoleHTTPTunnelServlet implementation,
|
||||
* and has no real utility outside that implementation.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
class GuacamoleHTTPTunnelMap {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(GuacamoleHTTPTunnelMap.class);
|
||||
|
||||
/**
|
||||
* The number of seconds to wait between tunnel accesses before timing out
|
||||
* Note that this will be enforced only within a factor of 2. If a tunnel
|
||||
* is unused, it will take between TUNNEL_TIMEOUT and TUNNEL_TIMEOUT*2
|
||||
* seconds before that tunnel is closed and removed.
|
||||
*/
|
||||
private static final int TUNNEL_TIMEOUT = 15;
|
||||
|
||||
/**
|
||||
* Executor service which runs the periodic tunnel timeout task.
|
||||
*/
|
||||
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
|
||||
|
||||
/**
|
||||
* Map of all tunnels that are using HTTP, indexed by tunnel UUID.
|
||||
*/
|
||||
private final ConcurrentMap<String, GuacamoleHTTPTunnel> tunnelMap =
|
||||
new ConcurrentHashMap<String, GuacamoleHTTPTunnel>();
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleHTTPTunnelMap which automatically closes and
|
||||
* removes HTTP tunnels which are no longer in use.
|
||||
*/
|
||||
public GuacamoleHTTPTunnelMap() {
|
||||
|
||||
// Check for unused tunnels every few seconds
|
||||
executor.scheduleAtFixedRate(
|
||||
new TunnelTimeoutTask(TUNNEL_TIMEOUT * 1000l),
|
||||
TUNNEL_TIMEOUT, TUNNEL_TIMEOUT, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Task which iterates through all registered tunnels, removing and those
|
||||
* tunnels which have not been accessed for a given number of milliseconds.
|
||||
*/
|
||||
private class TunnelTimeoutTask implements Runnable {
|
||||
|
||||
/**
|
||||
* The maximum amount of time to allow between accesses to any one
|
||||
* HTTP tunnel, in milliseconds.
|
||||
*/
|
||||
private final long tunnelTimeout;
|
||||
|
||||
/**
|
||||
* Creates a new task which automatically closes and removes tunnels
|
||||
* which have not been accessed for at least the given number of
|
||||
* milliseconds.
|
||||
*
|
||||
* @param tunnelTimeout
|
||||
* The maximum amount of time to allow between separate tunnel
|
||||
* read/write requests, in milliseconds.
|
||||
*/
|
||||
public TunnelTimeoutTask(long tunnelTimeout) {
|
||||
this.tunnelTimeout = tunnelTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// Get current time
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
// For each tunnel, close and remove any tunnels which have expired
|
||||
Iterator<Map.Entry<String, GuacamoleHTTPTunnel>> entries = tunnelMap.entrySet().iterator();
|
||||
while (entries.hasNext()) {
|
||||
|
||||
Map.Entry<String, GuacamoleHTTPTunnel> entry = entries.next();
|
||||
GuacamoleHTTPTunnel tunnel = entry.getValue();
|
||||
|
||||
// Get elapsed time since last access
|
||||
long age = now - tunnel.getLastAccessedTime();
|
||||
|
||||
// If tunnel is too old, close and remove it
|
||||
if (age >= tunnelTimeout) {
|
||||
|
||||
// Remove old entry
|
||||
logger.debug("HTTP tunnel \"{}\" has timed out.", entry.getKey());
|
||||
entries.remove();
|
||||
|
||||
// Attempt to close tunnel
|
||||
try {
|
||||
tunnel.close();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("Unable to close expired HTTP tunnel.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // end for each tunnel
|
||||
|
||||
} // end timeout task run()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GuacamoleTunnel having the given UUID, wrapped within a
|
||||
* GuacamoleHTTPTunnel. If the no tunnel having the given UUID is
|
||||
* available, null is returned.
|
||||
*
|
||||
* @param uuid
|
||||
* The UUID of the tunnel to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The GuacamoleTunnel having the given UUID, wrapped within a
|
||||
* GuacamoleHTTPTunnel, if such a tunnel exists, or null if there is no
|
||||
* such tunnel.
|
||||
*/
|
||||
public GuacamoleHTTPTunnel get(String uuid) {
|
||||
|
||||
// Update the last access time
|
||||
GuacamoleHTTPTunnel tunnel = tunnelMap.get(uuid);
|
||||
if (tunnel != null)
|
||||
tunnel.access();
|
||||
|
||||
// Return tunnel, if any
|
||||
return tunnel;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers that a new connection has been established using HTTP via the
|
||||
* given GuacamoleTunnel.
|
||||
*
|
||||
* @param uuid
|
||||
* The UUID of the tunnel being added (registered).
|
||||
*
|
||||
* @param tunnel
|
||||
* The GuacamoleTunnel being registered, its associated connection
|
||||
* having just been established via HTTP.
|
||||
*/
|
||||
public void put(String uuid, GuacamoleTunnel tunnel) {
|
||||
tunnelMap.put(uuid, new GuacamoleHTTPTunnel(tunnel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the GuacamoleTunnel having the given UUID, if such a tunnel
|
||||
* exists. The original tunnel is returned wrapped within a
|
||||
* GuacamoleHTTPTunnel.
|
||||
*
|
||||
* @param uuid
|
||||
* The UUID of the tunnel to remove (deregister).
|
||||
*
|
||||
* @return
|
||||
* The GuacamoleTunnel having the given UUID, if such a tunnel exists,
|
||||
* wrapped within a GuacamoleHTTPTunnel, or null if no such tunnel
|
||||
* exists and no removal was performed.
|
||||
*/
|
||||
public GuacamoleHTTPTunnel remove(String uuid) {
|
||||
return tunnelMap.remove(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down this tunnel map, disallowing future tunnels from being
|
||||
* registered and reclaiming any resources.
|
||||
*/
|
||||
public void shutdown() {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* 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.servlet;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
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;
|
||||
|
||||
/**
|
||||
* A HttpServlet implementing and abstracting the operations required by the
|
||||
* HTTP implementation of the JavaScript Guacamole client's tunnel.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GuacamoleHTTPTunnelServlet.class);
|
||||
|
||||
/**
|
||||
* Map of absolutely all active tunnels using HTTP, indexed by tunnel UUID.
|
||||
*/
|
||||
private final GuacamoleHTTPTunnelMap tunnels = new GuacamoleHTTPTunnelMap();
|
||||
|
||||
/**
|
||||
* The prefix of the query string which denotes a tunnel read operation.
|
||||
*/
|
||||
private static final String READ_PREFIX = "read:";
|
||||
|
||||
/**
|
||||
* The prefix of the query string which denotes a tunnel write operation.
|
||||
*/
|
||||
private static final String WRITE_PREFIX = "write:";
|
||||
|
||||
/**
|
||||
* The length of the read prefix, in characters.
|
||||
*/
|
||||
private static final int READ_PREFIX_LENGTH = READ_PREFIX.length();
|
||||
|
||||
/**
|
||||
* The length of the write prefix, in characters.
|
||||
*/
|
||||
private static final int WRITE_PREFIX_LENGTH = WRITE_PREFIX.length();
|
||||
|
||||
/**
|
||||
* The length of every tunnel UUID, in characters.
|
||||
*/
|
||||
private static final int UUID_LENGTH = 36;
|
||||
|
||||
/**
|
||||
* Registers the given tunnel such that future read/write requests to that
|
||||
* tunnel will be properly directed.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to register.
|
||||
*/
|
||||
protected void registerTunnel(GuacamoleTunnel tunnel) {
|
||||
tunnels.put(tunnel.getUUID().toString(), tunnel);
|
||||
logger.debug("Registered tunnel \"{}\".", tunnel.getUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters the given tunnel such that future read/write requests to
|
||||
* that tunnel will be rejected.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to deregister.
|
||||
*/
|
||||
protected void deregisterTunnel(GuacamoleTunnel tunnel) {
|
||||
tunnels.remove(tunnel.getUUID().toString());
|
||||
logger.debug("Deregistered tunnel \"{}\".", tunnel.getUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tunnel with the given UUID, if it has been registered with
|
||||
* registerTunnel() and not yet deregistered with deregisterTunnel().
|
||||
*
|
||||
* @param tunnelUUID
|
||||
* The UUID of registered tunnel.
|
||||
*
|
||||
* @return
|
||||
* The tunnel corresponding to the given UUID.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the requested tunnel does not exist because it has not yet been
|
||||
* registered or it has been deregistered.
|
||||
*/
|
||||
protected GuacamoleTunnel getTunnel(String tunnelUUID)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Pull tunnel from map
|
||||
GuacamoleTunnel tunnel = tunnels.get(tunnelUUID);
|
||||
if (tunnel == null)
|
||||
throw new GuacamoleResourceNotFoundException("No such tunnel.");
|
||||
|
||||
return tunnel;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
|
||||
handleTunnelRequest(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
|
||||
handleTunnelRequest(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an error on the given HTTP response using the information within
|
||||
* the given GuacamoleStatus.
|
||||
*
|
||||
* @param response
|
||||
* The HTTP response to use to send the error.
|
||||
*
|
||||
* @param guacStatus
|
||||
* The status to send
|
||||
*
|
||||
* @param message
|
||||
* A human-readable message that can be presented to the user.
|
||||
*
|
||||
* @throws ServletException
|
||||
* If an error prevents sending of the error code.
|
||||
*/
|
||||
protected void sendError(HttpServletResponse response,
|
||||
GuacamoleStatus guacStatus, String message)
|
||||
throws ServletException {
|
||||
|
||||
try {
|
||||
|
||||
// If response not committed, send error code and message
|
||||
if (!response.isCommitted()) {
|
||||
response.addHeader("Guacamole-Status-Code", Integer.toString(guacStatus.getGuacamoleStatusCode()));
|
||||
response.addHeader("Guacamole-Error-Message", message);
|
||||
response.sendError(guacStatus.getHttpStatusCode());
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
|
||||
// If unable to send error at all due to I/O problems,
|
||||
// rethrow as servlet exception
|
||||
throw new ServletException(ioe);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches every HTTP GET and POST request to the appropriate handler
|
||||
* function based on the query string.
|
||||
*
|
||||
* @param request
|
||||
* The HttpServletRequest associated with the GET or POST request
|
||||
* received.
|
||||
*
|
||||
* @param response
|
||||
* The HttpServletResponse associated with the GET or POST request
|
||||
* received.
|
||||
*
|
||||
* @throws ServletException
|
||||
* If an error occurs while servicing the request.
|
||||
*/
|
||||
protected void handleTunnelRequest(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException {
|
||||
|
||||
try {
|
||||
|
||||
String query = request.getQueryString();
|
||||
if (query == null)
|
||||
throw new GuacamoleClientException("No query string provided.");
|
||||
|
||||
// If connect operation, call doConnect() and return tunnel UUID
|
||||
// in response.
|
||||
if (query.equals("connect")) {
|
||||
|
||||
GuacamoleTunnel tunnel = doConnect(request);
|
||||
if (tunnel != null) {
|
||||
|
||||
// Register newly-created tunnel
|
||||
registerTunnel(tunnel);
|
||||
|
||||
try {
|
||||
// Ensure buggy browsers do not cache response
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
|
||||
// Send UUID to client
|
||||
response.getWriter().print(tunnel.getUUID().toString());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Failed to connect
|
||||
else
|
||||
throw new GuacamoleResourceNotFoundException("No tunnel created.");
|
||||
|
||||
}
|
||||
|
||||
// If read operation, call doRead() with tunnel UUID, ignoring any
|
||||
// characters following the tunnel UUID.
|
||||
else if(query.startsWith(READ_PREFIX))
|
||||
doRead(request, response, query.substring(
|
||||
READ_PREFIX_LENGTH,
|
||||
READ_PREFIX_LENGTH + UUID_LENGTH));
|
||||
|
||||
// If write operation, call doWrite() with tunnel UUID, ignoring any
|
||||
// characters following the tunnel UUID.
|
||||
else if(query.startsWith(WRITE_PREFIX))
|
||||
doWrite(request, response, query.substring(
|
||||
WRITE_PREFIX_LENGTH,
|
||||
WRITE_PREFIX_LENGTH + UUID_LENGTH));
|
||||
|
||||
// Otherwise, invalid operation
|
||||
else
|
||||
throw new GuacamoleClientException("Invalid tunnel operation: " + query);
|
||||
}
|
||||
|
||||
// Catch any thrown guacamole exception and attempt to pass within the
|
||||
// HTTP response, logging each error appropriately.
|
||||
catch (GuacamoleClientException e) {
|
||||
logger.warn("HTTP tunnel request rejected: {}", e.getMessage());
|
||||
sendError(response, e.getStatus(), e.getMessage());
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("HTTP tunnel request failed: {}", e.getMessage());
|
||||
logger.debug("Internal error in HTTP tunnel.", e);
|
||||
sendError(response, e.getStatus(), "Internal server error.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the JavaScript Guacamole client makes a connection
|
||||
* request via HTTP. It it up to the implementor of this function to define
|
||||
* what conditions must be met for a tunnel to be configured and returned
|
||||
* as a result of this connection request (whether some sort of credentials
|
||||
* must be specified, for example).
|
||||
*
|
||||
* @param request
|
||||
* The HttpServletRequest associated with the connection request
|
||||
* received. Any parameters specified along with the connection request
|
||||
* can be read from this object.
|
||||
*
|
||||
* @return
|
||||
* A newly constructed GuacamoleTunnel if successful, null otherwise.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while constructing the GuacamoleTunnel, or if the
|
||||
* conditions required for connection are not met.
|
||||
*/
|
||||
protected abstract GuacamoleTunnel doConnect(HttpServletRequest request)
|
||||
throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Called whenever the JavaScript Guacamole client makes a read request.
|
||||
* This function should in general not be overridden, as it already
|
||||
* contains a proper implementation of the read operation.
|
||||
*
|
||||
* @param request
|
||||
* The HttpServletRequest associated with the read request received.
|
||||
*
|
||||
* @param response
|
||||
* The HttpServletResponse associated with the write request received.
|
||||
* Any data to be sent to the client in response to the write request
|
||||
* should be written to the response body of this HttpServletResponse.
|
||||
*
|
||||
* @param tunnelUUID
|
||||
* The UUID of the tunnel to read from, as specified in the write
|
||||
* request. This tunnel must have been created by a previous call to
|
||||
* doConnect().
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while handling the read request.
|
||||
*/
|
||||
protected void doRead(HttpServletRequest request,
|
||||
HttpServletResponse response, String tunnelUUID)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Get tunnel, ensure tunnel exists
|
||||
GuacamoleTunnel tunnel = getTunnel(tunnelUUID);
|
||||
|
||||
// Ensure tunnel is open
|
||||
if (!tunnel.isOpen())
|
||||
throw new GuacamoleResourceNotFoundException("Tunnel is closed.");
|
||||
|
||||
// Obtain exclusive read access
|
||||
GuacamoleReader reader = tunnel.acquireReader();
|
||||
|
||||
try {
|
||||
|
||||
// Note that although we are sending text, Webkit browsers will
|
||||
// buffer 1024 bytes before starting a normal stream if we use
|
||||
// anything but application/octet-stream.
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
|
||||
// Get writer for response
|
||||
Writer out = new BufferedWriter(new OutputStreamWriter(
|
||||
response.getOutputStream(), "UTF-8"));
|
||||
|
||||
// Stream data to response, ensuring output stream is closed
|
||||
try {
|
||||
|
||||
// Deregister tunnel and throw error if we reach EOF without
|
||||
// having ever sent any data
|
||||
char[] message = reader.read();
|
||||
if (message == null)
|
||||
throw new GuacamoleConnectionClosedException("Tunnel reached end of stream.");
|
||||
|
||||
// For all messages, until another stream is ready (we send at least one message)
|
||||
do {
|
||||
|
||||
// Get message output bytes
|
||||
out.write(message, 0, message.length);
|
||||
|
||||
// Flush if we expect to wait
|
||||
if (!reader.available()) {
|
||||
out.flush();
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
// No more messages another stream can take over
|
||||
if (tunnel.hasQueuedReaderThreads())
|
||||
break;
|
||||
|
||||
} while (tunnel.isOpen() && (message = reader.read()) != null);
|
||||
|
||||
// Close tunnel immediately upon EOF
|
||||
if (message == null) {
|
||||
deregisterTunnel(tunnel);
|
||||
tunnel.close();
|
||||
}
|
||||
|
||||
// End-of-instructions marker
|
||||
out.write("0.;");
|
||||
out.flush();
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
// Send end-of-stream marker and close tunnel if connection is closed
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
|
||||
// Deregister and close
|
||||
deregisterTunnel(tunnel);
|
||||
tunnel.close();
|
||||
|
||||
// End-of-instructions marker
|
||||
out.write("0.;");
|
||||
out.flush();
|
||||
response.flushBuffer();
|
||||
|
||||
}
|
||||
|
||||
catch (GuacamoleException e) {
|
||||
|
||||
// Deregister and close
|
||||
deregisterTunnel(tunnel);
|
||||
tunnel.close();
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Always close output stream
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
||||
// Log typically frequent I/O error if desired
|
||||
logger.debug("Error writing to servlet output stream", e);
|
||||
|
||||
// Deregister and close
|
||||
deregisterTunnel(tunnel);
|
||||
tunnel.close();
|
||||
|
||||
}
|
||||
finally {
|
||||
tunnel.releaseReader();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the JavaScript Guacamole client makes a write request.
|
||||
* This function should in general not be overridden, as it already
|
||||
* contains a proper implementation of the write operation.
|
||||
*
|
||||
* @param request
|
||||
* The HttpServletRequest associated with the write request received.
|
||||
* Any data to be written will be specified within the body of this
|
||||
* request.
|
||||
*
|
||||
* @param response
|
||||
* The HttpServletResponse associated with the write request received.
|
||||
*
|
||||
* @param tunnelUUID
|
||||
* The UUID of the tunnel to write to, as specified in the write
|
||||
* request. This tunnel must have been created by a previous call to
|
||||
* doConnect().
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while handling the write request.
|
||||
*/
|
||||
protected void doWrite(HttpServletRequest request,
|
||||
HttpServletResponse response, String tunnelUUID)
|
||||
throws GuacamoleException {
|
||||
|
||||
GuacamoleTunnel tunnel = getTunnel(tunnelUUID);
|
||||
|
||||
// We still need to set the content type to avoid the default of
|
||||
// text/html, as such a content type would cause some browsers to
|
||||
// attempt to parse the result, even though the JavaScript client
|
||||
// does not explicitly request such parsing.
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setContentLength(0);
|
||||
|
||||
// Send data
|
||||
try {
|
||||
|
||||
// Get writer from tunnel
|
||||
GuacamoleWriter writer = tunnel.acquireWriter();
|
||||
|
||||
// Get input reader for HTTP stream
|
||||
Reader input = new InputStreamReader(
|
||||
request.getInputStream(), "UTF-8");
|
||||
|
||||
// Transfer data from input stream to tunnel output, ensuring
|
||||
// input is always closed
|
||||
try {
|
||||
|
||||
// Buffer
|
||||
int length;
|
||||
char[] buffer = new char[8192];
|
||||
|
||||
// Transfer data using buffer
|
||||
while (tunnel.isOpen() &&
|
||||
(length = input.read(buffer, 0, buffer.length)) != -1)
|
||||
writer.write(buffer, 0, length);
|
||||
|
||||
}
|
||||
|
||||
// Close input stream in all cases
|
||||
finally {
|
||||
input.close();
|
||||
}
|
||||
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
||||
// Deregister and close
|
||||
deregisterTunnel(tunnel);
|
||||
tunnel.close();
|
||||
|
||||
throw new GuacamoleServerException("I/O Error sending data to server: " + e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
tunnel.releaseWriter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
tunnels.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* \example ExampleTunnelServlet.java
|
||||
*
|
||||
* A basic example demonstrating extending GuacamoleTunnelServlet and
|
||||
* implementing doConnect() to configure the Guacamole connection as
|
||||
* desired.
|
||||
*/
|
||||
|
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* 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.servlet;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides abstract access to the tunnels associated with a Guacamole session.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
@Deprecated
|
||||
public class GuacamoleSession {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GuacamoleSession.class);
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSession. In prior versions of Guacamole, the
|
||||
* GuacamoleSession object stored the tunnels associated with a particular
|
||||
* user's use of the HTTP tunnel. The HTTP tunnel now stores all of these
|
||||
* tunnels itself, and thus this class is no longer necessary. Its use will
|
||||
* result in a warning being logged, and its functions will have no effect.
|
||||
*
|
||||
* @param session
|
||||
* The HttpSession that older versions of Guacamole would use as tunnel
|
||||
* storage. This parameter is now ignored, and the GuacamoleSession
|
||||
* class overall is deprecated.
|
||||
*/
|
||||
public GuacamoleSession(HttpSession session) {
|
||||
logger.warn("GuacamoleSession is deprecated. It is no longer "
|
||||
+ "necessary and its use will have no effect.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the given tunnel to this GuacamoleSession. The GuacamoleSession
|
||||
* class is now deprecated, and this function has no effect.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to attach to this GucacamoleSession.
|
||||
*/
|
||||
public void attachTunnel(GuacamoleTunnel tunnel) {
|
||||
// Deprecated - no effect
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the given tunnel to this GuacamoleSession. The GuacamoleSession
|
||||
* class is now deprecated, and this function has no effect.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to detach to this GucacamoleSession.
|
||||
*/
|
||||
public void detachTunnel(GuacamoleTunnel tunnel) {
|
||||
// Deprecated - no effect
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tunnel with the given UUID attached to this GuacamoleSession,
|
||||
* if any. The GuacamoleSession class is now deprecated, and this function
|
||||
* has no effect. It will ALWAYS return null.
|
||||
*
|
||||
* @param tunnelUUID
|
||||
* The UUID of an attached tunnel.
|
||||
*
|
||||
* @return
|
||||
* The tunnel corresponding to the given UUID, if attached, or null if
|
||||
* if no such tunnel is attached.
|
||||
*/
|
||||
public GuacamoleTunnel getTunnel(String tunnelUUID) {
|
||||
|
||||
// Deprecated - no effect
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes which build upon the Java Servlet API, providing an HTTP-based
|
||||
* tunnel and session management.
|
||||
*/
|
||||
package org.apache.guacamole.servlet;
|
||||
|
Reference in New Issue
Block a user