mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-267: Failover to other connections within same group if upstream remote desktop errors are detected.
This commit is contained in:
@@ -39,8 +39,10 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionModel;
|
|||||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel;
|
import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel;
|
||||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel;
|
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.GuacamoleResourceConflictException;
|
||||||
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||||
import org.apache.guacamole.GuacamoleSecurityException;
|
import org.apache.guacamole.GuacamoleSecurityException;
|
||||||
|
import org.apache.guacamole.GuacamoleUpstreamException;
|
||||||
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
||||||
import org.apache.guacamole.auth.jdbc.connection.ConnectionMapper;
|
import org.apache.guacamole.auth.jdbc.connection.ConnectionMapper;
|
||||||
import org.apache.guacamole.environment.Environment;
|
import org.apache.guacamole.environment.Environment;
|
||||||
@@ -60,6 +62,9 @@ import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
|
|||||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
|
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
|
||||||
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel;
|
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel;
|
||||||
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
||||||
|
import org.apache.guacamole.protocol.FailoverGuacamoleSocket;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,6 +74,11 @@ import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelService {
|
public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger for this class.
|
||||||
|
*/
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(AbstractGuacamoleTunnelService.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The environment of the Guacamole server.
|
* The environment of the Guacamole server.
|
||||||
*/
|
*/
|
||||||
@@ -439,6 +449,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
* Information describing the Guacamole client connecting to the given
|
* Information describing the Guacamole client connecting to the given
|
||||||
* connection.
|
* connection.
|
||||||
*
|
*
|
||||||
|
* @param interceptErrors
|
||||||
|
* Whether errors from the upstream remote desktop should be
|
||||||
|
* intercepted and rethrown as GuacamoleUpstreamExceptions.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* A new GuacamoleTunnel which is configured and connected to the given
|
* A new GuacamoleTunnel which is configured and connected to the given
|
||||||
* connection.
|
* connection.
|
||||||
@@ -448,7 +462,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
* while connection configuration information is being retrieved.
|
* while connection configuration information is being retrieved.
|
||||||
*/
|
*/
|
||||||
private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection,
|
private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection,
|
||||||
GuacamoleClientInformation info) throws GuacamoleException {
|
GuacamoleClientInformation info, boolean interceptErrors) throws GuacamoleException {
|
||||||
|
|
||||||
// Record new active connection
|
// Record new active connection
|
||||||
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
|
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
|
||||||
@@ -488,7 +502,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
getUnconfiguredGuacamoleSocket(cleanupTask), config, info);
|
getUnconfiguredGuacamoleSocket(cleanupTask), config, info);
|
||||||
|
|
||||||
// Assign and return new tunnel
|
// Assign and return new tunnel
|
||||||
return activeConnection.assignGuacamoleTunnel(socket, socket.getConnectionID());
|
if (interceptErrors)
|
||||||
|
return activeConnection.assignGuacamoleTunnel(new FailoverGuacamoleSocket(socket), socket.getConnectionID());
|
||||||
|
else
|
||||||
|
return activeConnection.assignGuacamoleTunnel(socket, socket.getConnectionID());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,7 +650,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
// Connect only if the connection was successfully acquired
|
// Connect only if the connection was successfully acquired
|
||||||
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
||||||
connectionRecord.init(user, connection);
|
connectionRecord.init(user, connection);
|
||||||
return assignGuacamoleTunnel(connectionRecord, info);
|
return assignGuacamoleTunnel(connectionRecord, info, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,29 +670,50 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
if (connections.isEmpty())
|
if (connections.isEmpty())
|
||||||
throw new GuacamoleSecurityException("Permission denied.");
|
throw new GuacamoleSecurityException("Permission denied.");
|
||||||
|
|
||||||
// Acquire group
|
do {
|
||||||
acquire(user, connectionGroup);
|
|
||||||
|
|
||||||
// Attempt to acquire to any child
|
// Acquire group
|
||||||
ModeledConnection connection;
|
acquire(user, connectionGroup);
|
||||||
try {
|
|
||||||
connection = acquire(user, connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure connection group is always released if child acquire fails
|
// Attempt to acquire to any child
|
||||||
catch (GuacamoleException e) {
|
ModeledConnection connection;
|
||||||
release(user, connectionGroup);
|
try {
|
||||||
throw e;
|
connection = acquire(user, connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If session affinity is enabled, prefer this connection going forward
|
// Ensure connection group is always released if child acquire fails
|
||||||
if (connectionGroup.isSessionAffinityEnabled())
|
catch (GuacamoleException e) {
|
||||||
user.preferConnection(connection.getIdentifier());
|
release(user, connectionGroup);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to acquired child
|
try {
|
||||||
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
|
||||||
connectionRecord.init(user, connectionGroup, connection);
|
// Connect to acquired child
|
||||||
return assignGuacamoleTunnel(connectionRecord, info);
|
ActiveConnectionRecord connectionRecord = activeConnectionRecordProvider.get();
|
||||||
|
connectionRecord.init(user, connectionGroup, connection);
|
||||||
|
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, connections.size() > 1);
|
||||||
|
|
||||||
|
// If session affinity is enabled, prefer this connection going forward
|
||||||
|
if (connectionGroup.isSessionAffinityEnabled())
|
||||||
|
user.preferConnection(connection.getIdentifier());
|
||||||
|
|
||||||
|
return tunnel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If connection failed due to an upstream error, retry other
|
||||||
|
// connections
|
||||||
|
catch (GuacamoleUpstreamException e) {
|
||||||
|
logger.info("Upstream error intercepted for connection \"{}\". Failing over to next connection in group...", connection.getIdentifier());
|
||||||
|
logger.debug("Upstream remote desktop reported an error during connection.", e);
|
||||||
|
connections.remove(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (!connections.isEmpty());
|
||||||
|
|
||||||
|
// All connection possibilities have been exhausted
|
||||||
|
throw new GuacamoleResourceConflictException("Cannot connect. All upstream connections are unavailable.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,7 +741,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
|
|||||||
definition.getSharingProfile());
|
definition.getSharingProfile());
|
||||||
|
|
||||||
// Connect to shared connection described by the created record
|
// Connect to shared connection described by the created record
|
||||||
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info);
|
GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, false);
|
||||||
|
|
||||||
// Register tunnel, such that it is closed when the
|
// Register tunnel, such that it is closed when the
|
||||||
// SharedConnectionDefinition is invalidated
|
// SharedConnectionDefinition is invalidated
|
||||||
|
Reference in New Issue
Block a user