GUACAMOLE-422: Allow arbitrary protocol versions to be represented. Remove API-level assumption that protocol capabilities will have a minimum base version and remain present from that point forward.

This commit is contained in:
Michael Jumper
2019-06-08 12:34:03 -07:00
parent f2ae848b1b
commit 001918e2d7
3 changed files with 58 additions and 116 deletions

View File

@@ -61,7 +61,7 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
* it will be assumed that it operates at this version and certain features * it will be assumed that it operates at this version and certain features
* may be unavailable. * may be unavailable.
*/ */
private GuacamoleProtocolVersion protocol = private GuacamoleProtocolVersion protocolVersion =
GuacamoleProtocolVersion.VERSION_1_0_0; GuacamoleProtocolVersion.VERSION_1_0_0;
/** /**
@@ -151,11 +151,21 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
// Retrieve argument name // Retrieve argument name
String arg_name = arg_names.get(i); String arg_name = arg_names.get(i);
// Check for protocol version as first argument // Check for valid protocol version as first argument
if (i == 0 && arg_name.startsWith("VERSION_")) { if (i == 0) {
protocol = GuacamoleProtocolVersion.getVersion(arg_name); GuacamoleProtocolVersion version = GuacamoleProtocolVersion.parseVersion(arg_name);
arg_values[i] = protocol.toString(); if (version != null) {
continue;
// Use the lowest common version supported
if (version.atLeast(GuacamoleProtocolVersion.LATEST))
version = GuacamoleProtocolVersion.LATEST;
// Respond with the version selected
arg_values[i] = version.toString();
protocolVersion = version;
continue;
}
} }
// Get defined value for name // Get defined value for name
@@ -200,17 +210,11 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
info.getImageMimetypes().toArray(new String[0]) info.getImageMimetypes().toArray(new String[0])
)); ));
// Check for support for timezone handshake // Send client timezone, if supported and available
if (protocol.isSupported(GuacamoleProtocolCapability.TIMEZONE_HANDSHAKE)) { if (GuacamoleProtocolCapability.TIMEZONE_HANDSHAKE.isSupported(protocolVersion)) {
// Send client timezone, if available
String timezone = info.getTimezone(); String timezone = info.getTimezone();
if (timezone != null) { if (timezone != null)
writer.writeInstruction( writer.writeInstruction(new GuacamoleInstruction("timezone", info.getTimezone()));
new GuacamoleInstruction(
"timezone",
info.getTimezone()
));
}
} }
// Send args // Send args

View File

@@ -59,19 +59,23 @@ public enum GuacamoleProtocolCapability {
* The minimum required protocol version for supporting the * The minimum required protocol version for supporting the
* capability. * capability.
*/ */
GuacamoleProtocolCapability(GuacamoleProtocolVersion version) { private GuacamoleProtocolCapability(GuacamoleProtocolVersion version) {
this.version = version; this.version = version;
} }
/** /**
* Returns the minimum protocol version required to support this * Returns whether this capability is supported in the given Guacamole
* capability. * protocol version.
*
* @param version
* The Guacamole protocol version to check.
* *
* @return * @return
* The minimum protocol version required to support this capability. * true if this capability is supported by the given protocol version,
* false otherwise.
*/ */
public GuacamoleProtocolVersion getVersion() { public boolean isSupported(GuacamoleProtocolVersion version) {
return version; return version.atLeast(this.version);
} }
} }

View File

@@ -23,11 +23,12 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* An enum that defines the available Guacamole protocol versions that can be * Representation of a Guacamole protocol version. Convenience methods are
* used between guacd and clients, and provides convenience methods for parsing * provided for parsing and comparing versions, as is necessary when
* and comparing versions. * determining the version of the Guacamole protocol common to guacd and a
* client.
*/ */
public enum GuacamoleProtocolVersion { public class GuacamoleProtocolVersion {
/** /**
* Protocol version 1.0.0 and older. Any client that doesn't explicitly * Protocol version 1.0.0 and older. Any client that doesn't explicitly
@@ -36,14 +37,20 @@ public enum GuacamoleProtocolVersion {
* lacks support for certain protocol-related features introduced in later * lacks support for certain protocol-related features introduced in later
* versions. * versions.
*/ */
VERSION_1_0_0(1, 0, 0), public static final GuacamoleProtocolVersion VERSION_1_0_0 = new GuacamoleProtocolVersion(1, 0, 0);
/** /**
* Protocol version 1.1.0, which introduces Client-Server version * Protocol version 1.1.0, which introduces Client-Server version
* detection, arbitrary handshake instruction order, and support * detection, arbitrary handshake instruction order, and support
* for passing the client timezone to the server during the handshake. * for passing the client timezone to the server during the handshake.
*/ */
VERSION_1_1_0(1, 1, 0); public static final GuacamoleProtocolVersion VERSION_1_1_0 = new GuacamoleProtocolVersion(1, 1, 0);
/**
* The most recent version of the Guacamole protocol at the time this
* version of GuacamoleProtocolVersion was built.
*/
public static final GuacamoleProtocolVersion LATEST = VERSION_1_1_0;
/** /**
* A regular expression that matches the VERSION_X_Y_Z pattern, where * A regular expression that matches the VERSION_X_Y_Z pattern, where
@@ -83,7 +90,7 @@ public enum GuacamoleProtocolVersion {
* @param patch * @param patch
* The integer representation of the patch version component. * The integer representation of the patch version component.
*/ */
GuacamoleProtocolVersion(int major, int minor, int patch) { public GuacamoleProtocolVersion(int major, int minor, int patch) {
this.major = major; this.major = major;
this.minor = minor; this.minor = minor;
this.patch = patch; this.patch = patch;
@@ -131,7 +138,7 @@ public enum GuacamoleProtocolVersion {
* @return * @return
* True if this object is greater than or equal to the other version. * True if this object is greater than or equal to the other version.
*/ */
private boolean atLeast(GuacamoleProtocolVersion otherVersion) { public boolean atLeast(GuacamoleProtocolVersion otherVersion) {
// If major is not the same, return inequality // If major is not the same, return inequality
if (major != otherVersion.getMajor()) if (major != otherVersion.getMajor())
@@ -146,39 +153,6 @@ public enum GuacamoleProtocolVersion {
} }
/**
* Compare this version with the major, minor, and patch components
* provided to the method, and determine if this version is compatible
* with the provided version, returning a boolean true if it is compatible,
* otherwise false. This version is compatible with the version specified
* by the provided components if the major, minor, and patch components
* are equivalent or less than those provided.
*
* @param major
* The major version component to compare for compatibility.
*
* @param minor
* The minor version component to compare for compatibility.
*
* @param patch
* The patch version component to compare for compatibility.
*
* @return
* True if this version is compatibility with the version components
* provided, otherwise false.
*/
private boolean isCompatible(int major, int minor, int patch) {
if (this.major != major)
return this.major < major;
if (this.minor != minor)
return this.minor < minor;
return this.patch <= patch;
}
/** /**
* Parse the String format of the version provided and return the * Parse the String format of the version provided and return the
* the enum value matching that version. If no value is provided, return * the enum value matching that version. If no value is provided, return
@@ -191,59 +165,19 @@ public enum GuacamoleProtocolVersion {
* The enum value that matches the specified version, VERSION_1_0_0 * The enum value that matches the specified version, VERSION_1_0_0
* if no match is found, or null if no comparison version is provided. * if no match is found, or null if no comparison version is provided.
*/ */
public static GuacamoleProtocolVersion getVersion(String version) { public static GuacamoleProtocolVersion parseVersion(String version) {
// If nothing is passed in, return null // Validate format of version string
if (version == null || version.isEmpty())
return null;
// Check the string against the pattern matcher
Matcher versionMatcher = VERSION_PATTERN.matcher(version); Matcher versionMatcher = VERSION_PATTERN.matcher(version);
// If there is no RegEx match, return null
if (!versionMatcher.matches()) if (!versionMatcher.matches())
return null; return null;
try { // Parse version number from version string
// Try the valueOf function return new GuacamoleProtocolVersion(
return valueOf(version); Integer.parseInt(versionMatcher.group(1)),
Integer.parseInt(versionMatcher.group(2)),
} Integer.parseInt(versionMatcher.group(3))
);
// If nothing matches, find the closest compatible version.
catch (IllegalArgumentException e) {
int myMajor = Integer.parseInt(versionMatcher.group(1));
int myMinor = Integer.parseInt(versionMatcher.group(2));
int myPatch = Integer.parseInt(versionMatcher.group(3));
GuacamoleProtocolVersion myVersion = VERSION_1_0_0;
// Loop through possible versions, grabbing the latest compatible
for (GuacamoleProtocolVersion v : values()) {
if (v.isCompatible(myMajor, myMinor, myPatch))
myVersion = v;
}
return myVersion;
}
}
/**
* Returns true if the specified capability is supported in the current
* protocol version, otherwise false.
*
* @param capability
* The protocol capability that is being checked for support.
*
* @return
* True if the capability is supported in the current version,
* otherwise false.
*/
public boolean isSupported(GuacamoleProtocolCapability capability) {
return atLeast(capability.getVersion());
} }