mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-422: Merge allow arbitrary versions of the Guacamole protocol to be represented.
This commit is contained in:
@@ -61,7 +61,7 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
|
||||
* it will be assumed that it operates at this version and certain features
|
||||
* may be unavailable.
|
||||
*/
|
||||
private GuacamoleProtocolVersion protocol =
|
||||
private GuacamoleProtocolVersion protocolVersion =
|
||||
GuacamoleProtocolVersion.VERSION_1_0_0;
|
||||
|
||||
/**
|
||||
@@ -151,11 +151,21 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
|
||||
// Retrieve argument name
|
||||
String arg_name = arg_names.get(i);
|
||||
|
||||
// Check for protocol version as first argument
|
||||
if (i == 0 && arg_name.startsWith("VERSION_")) {
|
||||
protocol = GuacamoleProtocolVersion.getVersion(arg_name);
|
||||
arg_values[i] = protocol.toString();
|
||||
// Check for valid protocol version as first argument
|
||||
if (i == 0) {
|
||||
GuacamoleProtocolVersion version = GuacamoleProtocolVersion.parseVersion(arg_name);
|
||||
if (version != null) {
|
||||
|
||||
// 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
|
||||
@@ -200,17 +210,11 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
|
||||
info.getImageMimetypes().toArray(new String[0])
|
||||
));
|
||||
|
||||
// Check for support for timezone handshake
|
||||
if (protocol.isSupported(GuacamoleProtocolCapability.TIMEZONE_HANDSHAKE)) {
|
||||
// Send client timezone, if available
|
||||
// Send client timezone, if supported and available
|
||||
if (GuacamoleProtocolCapability.TIMEZONE_HANDSHAKE.isSupported(protocolVersion)) {
|
||||
String timezone = info.getTimezone();
|
||||
if (timezone != null) {
|
||||
writer.writeInstruction(
|
||||
new GuacamoleInstruction(
|
||||
"timezone",
|
||||
info.getTimezone()
|
||||
));
|
||||
}
|
||||
if (timezone != null)
|
||||
writer.writeInstruction(new GuacamoleInstruction("timezone", info.getTimezone()));
|
||||
}
|
||||
|
||||
// Send args
|
||||
@@ -249,6 +253,20 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the Guacamole protocol associated with the
|
||||
* Guacamole connection negotiated by this ConfiguredGuacamoleSocket. This
|
||||
* version is the lowest version common to both ConfiguredGuacamoleSocket
|
||||
* and the relevant Guacamole proxy instance (guacd).
|
||||
*
|
||||
* @return
|
||||
* The protocol version that this ConfiguredGuacamoleSocket will use to
|
||||
* communicate with guacd.
|
||||
*/
|
||||
public GuacamoleProtocolVersion getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleWriter getWriter() {
|
||||
return socket.getWriter();
|
||||
|
@@ -20,29 +20,33 @@
|
||||
package org.apache.guacamole.protocol;
|
||||
|
||||
/**
|
||||
* An enum that specifies protocol capabilities that can be used to help
|
||||
* detect whether or not a particular protocol version contains a capability.
|
||||
* Capabilities which may not be present in all versions of the Guacamole
|
||||
* protocol.
|
||||
*/
|
||||
public enum GuacamoleProtocolCapability {
|
||||
|
||||
/**
|
||||
* Whether or not the protocol supports arbitrary ordering of the
|
||||
* handshake instructions. This was introduced in VERSION_1_1_0.
|
||||
* The protocol does not require handshake instructions to be sent in a
|
||||
* specific order, nor that all handshake instructions be sent. Arbitrary
|
||||
* handshake order was introduced in
|
||||
* {@link GuacamoleProtocolVersion#VERSION_1_1_0}.
|
||||
*/
|
||||
ARBITRARY_HANDSHAKE_ORDER(GuacamoleProtocolVersion.VERSION_1_1_0),
|
||||
|
||||
/**
|
||||
* Whether or not the protocol supports the ability to dynamically
|
||||
* detect the version client and server are running in order to allow
|
||||
* compatibility between differing client and server versions. This
|
||||
* was introduced in VERSION_1_1_0.
|
||||
* Negotiation of Guacamole protocol version between client and server
|
||||
* during the protocol handshake. The ability to negotiate protocol
|
||||
* versions was introduced in
|
||||
* {@link GuacamoleProtocolVersion#VERSION_1_1_0}.
|
||||
*/
|
||||
PROTOCOL_VERSION_DETECTION(GuacamoleProtocolVersion.VERSION_1_1_0),
|
||||
|
||||
/**
|
||||
* Whether or not the protocol supports the timezone instruction during
|
||||
* the Client-Server handshake phase. This was introduced in
|
||||
* VERSION_1_1_0.
|
||||
* Support for the "timezone" handshake instruction. The "timezone"
|
||||
* instruction allows the client to request that the server forward their
|
||||
* local timezone for use within the remote desktop session. Support for
|
||||
* forwarding the client timezone was introduced in
|
||||
* {@link GuacamoleProtocolVersion#VERSION_1_1_0}.
|
||||
*/
|
||||
TIMEZONE_HANDSHAKE(GuacamoleProtocolVersion.VERSION_1_1_0);
|
||||
|
||||
@@ -59,19 +63,23 @@ public enum GuacamoleProtocolCapability {
|
||||
* The minimum required protocol version for supporting the
|
||||
* capability.
|
||||
*/
|
||||
GuacamoleProtocolCapability(GuacamoleProtocolVersion version) {
|
||||
private GuacamoleProtocolCapability(GuacamoleProtocolVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum protocol version required to support this
|
||||
* capability.
|
||||
* Returns whether this capability is supported in the given Guacamole
|
||||
* protocol version.
|
||||
*
|
||||
* @param version
|
||||
* The Guacamole protocol version to check.
|
||||
*
|
||||
* @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() {
|
||||
return version;
|
||||
public boolean isSupported(GuacamoleProtocolVersion version) {
|
||||
return version.atLeast(this.version);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,11 +23,12 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An enum that defines the available Guacamole protocol versions that can be
|
||||
* used between guacd and clients, and provides convenience methods for parsing
|
||||
* and comparing versions.
|
||||
* Representation of a Guacamole protocol version. Convenience methods are
|
||||
* provided for parsing and comparing versions, as is necessary when
|
||||
* 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
|
||||
@@ -36,14 +37,20 @@ public enum GuacamoleProtocolVersion {
|
||||
* lacks support for certain protocol-related features introduced in later
|
||||
* 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
|
||||
* detection, arbitrary handshake instruction order, and support
|
||||
* 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
|
||||
@@ -83,7 +90,7 @@ public enum GuacamoleProtocolVersion {
|
||||
* @param patch
|
||||
* 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.minor = minor;
|
||||
this.patch = patch;
|
||||
@@ -120,65 +127,31 @@ public enum GuacamoleProtocolVersion {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this object is greater than or equal to the
|
||||
* the version passed in to the method. Returns a boolean true if the
|
||||
* version is the same as or greater than the other version, otherwise
|
||||
* false.
|
||||
* Returns whether this GuacamoleProtocolVersion is at least as recent as
|
||||
* (greater than or equal to) the given version.
|
||||
*
|
||||
* @param otherVersion
|
||||
* The version to which this object should be compared.
|
||||
* The version to which this GuacamoleProtocolVersion should be compared.
|
||||
*
|
||||
* @return
|
||||
* True if this object is greater than or equal to the other version.
|
||||
* true if this object is at least as recent as the given version,
|
||||
* false if the given version is newer.
|
||||
*/
|
||||
private boolean atLeast(GuacamoleProtocolVersion otherVersion) {
|
||||
public boolean atLeast(GuacamoleProtocolVersion otherVersion) {
|
||||
|
||||
// If major is not the same, return inequality
|
||||
if (major != otherVersion.getMajor())
|
||||
return this.major > major;
|
||||
return this.major > otherVersion.getMajor();
|
||||
|
||||
// Major is the same, but minor is not, return minor inequality
|
||||
if (minor != otherVersion.getMinor())
|
||||
return this.minor > minor;
|
||||
return this.minor > otherVersion.getMinor();
|
||||
|
||||
// Major and minor are equal, so return patch inequality
|
||||
return patch >= otherVersion.getPatch();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the enum value matching that version. If no value is provided, return
|
||||
@@ -191,60 +164,48 @@ public enum GuacamoleProtocolVersion {
|
||||
* 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.
|
||||
*/
|
||||
public static GuacamoleProtocolVersion getVersion(String version) {
|
||||
public static GuacamoleProtocolVersion parseVersion(String version) {
|
||||
|
||||
// If nothing is passed in, return null
|
||||
if (version == null || version.isEmpty())
|
||||
return null;
|
||||
|
||||
// Check the string against the pattern matcher
|
||||
// Validate format of version string
|
||||
Matcher versionMatcher = VERSION_PATTERN.matcher(version);
|
||||
|
||||
// If there is no RegEx match, return null
|
||||
if (!versionMatcher.matches())
|
||||
return null;
|
||||
|
||||
try {
|
||||
// Try the valueOf function
|
||||
return valueOf(version);
|
||||
// Parse version number from version string
|
||||
return new GuacamoleProtocolVersion(
|
||||
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;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 61 * hash + this.major;
|
||||
hash = 61 * hash + this.minor;
|
||||
hash = 61 * hash + this.patch;
|
||||
return hash;
|
||||
}
|
||||
|
||||
return myVersion;
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj == null || !(obj instanceof GuacamoleProtocolVersion))
|
||||
return false;
|
||||
|
||||
// Versions are equal if all major/minor/patch components are identical
|
||||
final GuacamoleProtocolVersion otherVersion = (GuacamoleProtocolVersion) obj;
|
||||
return this.major == otherVersion.getMajor()
|
||||
&& this.minor == otherVersion.getMinor()
|
||||
&& this.patch == otherVersion.getPatch();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VERSION_" + getMajor() + "_" + getMinor() + "_" + getPatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.guacamole.protocol;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for GuacamoleProtocolVersion. Verifies that Guacamole protocol
|
||||
* version string parsing works as required.
|
||||
*/
|
||||
public class GuacamoleProtocolVersionTest {
|
||||
|
||||
/**
|
||||
* Verifies that valid version strings are parsed successfully.
|
||||
*/
|
||||
@Test
|
||||
public void testValidVersionParse() {
|
||||
GuacamoleProtocolVersion version = GuacamoleProtocolVersion.parseVersion("VERSION_012_102_398");
|
||||
Assert.assertNotNull(version);
|
||||
Assert.assertEquals(12, version.getMajor());
|
||||
Assert.assertEquals(102, version.getMinor());
|
||||
Assert.assertEquals(398, version.getPatch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that invalid version strings fail to parse.
|
||||
*/
|
||||
@Test
|
||||
public void testInvalidVersionParse() {
|
||||
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("potato"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION___"));
|
||||
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION__2_3"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1__3"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1_2_"));
|
||||
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_A_2_3"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1_B_3"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1_2_C"));
|
||||
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("_1_2_3"));
|
||||
Assert.assertNull(GuacamoleProtocolVersion.parseVersion("version_1_2_3"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the atLeast() function defined by GuacamoleProtocolVersion
|
||||
* behaves as required for a series of three versions which are in strictly
|
||||
* increasing order (a < b < c).
|
||||
*
|
||||
* @param a
|
||||
* The String representation of the version which is known to be the
|
||||
* smaller than versions b and c.
|
||||
*
|
||||
* @param b
|
||||
* The String representation of the version which is known to be
|
||||
* larger than version a but smaller than version c.
|
||||
*
|
||||
* @param c
|
||||
* The String representation of the version which is known to be the
|
||||
* larger than versions a and b.
|
||||
*/
|
||||
private void testVersionCompare(String a, String b, String c) {
|
||||
|
||||
GuacamoleProtocolVersion verA = GuacamoleProtocolVersion.parseVersion(a);
|
||||
GuacamoleProtocolVersion verB = GuacamoleProtocolVersion.parseVersion(b);
|
||||
GuacamoleProtocolVersion verC = GuacamoleProtocolVersion.parseVersion(c);
|
||||
|
||||
Assert.assertTrue(verC.atLeast(verB));
|
||||
Assert.assertTrue(verC.atLeast(verA));
|
||||
Assert.assertTrue(verB.atLeast(verA));
|
||||
|
||||
Assert.assertFalse(verB.atLeast(verC));
|
||||
Assert.assertFalse(verA.atLeast(verC));
|
||||
Assert.assertFalse(verA.atLeast(verB));
|
||||
|
||||
Assert.assertTrue(verA.atLeast(verA));
|
||||
Assert.assertTrue(verB.atLeast(verB));
|
||||
Assert.assertTrue(verC.atLeast(verC));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that version order comparisons using atLeast() behave as
|
||||
* required.
|
||||
*/
|
||||
@Test
|
||||
public void testVersionCompare() {
|
||||
testVersionCompare("VERSION_0_0_1", "VERSION_0_0_2", "VERSION_0_0_3");
|
||||
testVersionCompare("VERSION_0_1_0", "VERSION_0_2_0", "VERSION_0_3_0");
|
||||
testVersionCompare("VERSION_1_0_0", "VERSION_2_0_0", "VERSION_3_0_0");
|
||||
testVersionCompare("VERSION_1_2_3", "VERSION_1_3_3", "VERSION_2_0_0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that versions can be tested for equality using equals().
|
||||
*/
|
||||
@Test
|
||||
public void testVersionEquals() {
|
||||
|
||||
GuacamoleProtocolVersion version;
|
||||
|
||||
version = GuacamoleProtocolVersion.parseVersion("VERSION_012_102_398");
|
||||
Assert.assertTrue(version.equals(version));
|
||||
Assert.assertTrue(version.equals(new GuacamoleProtocolVersion(12, 102, 398)));
|
||||
Assert.assertFalse(version.equals(new GuacamoleProtocolVersion(12, 102, 399)));
|
||||
Assert.assertFalse(version.equals(new GuacamoleProtocolVersion(12, 103, 398)));
|
||||
Assert.assertFalse(version.equals(new GuacamoleProtocolVersion(11, 102, 398)));
|
||||
|
||||
version = GuacamoleProtocolVersion.parseVersion("VERSION_1_0_0");
|
||||
Assert.assertTrue(version.equals(GuacamoleProtocolVersion.VERSION_1_0_0));
|
||||
Assert.assertFalse(version.equals(GuacamoleProtocolVersion.VERSION_1_1_0));
|
||||
|
||||
version = GuacamoleProtocolVersion.parseVersion("VERSION_1_1_0");
|
||||
Assert.assertTrue(version.equals(GuacamoleProtocolVersion.VERSION_1_1_0));
|
||||
Assert.assertFalse(version.equals(GuacamoleProtocolVersion.VERSION_1_0_0));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that versions can be converted to their Guacamole protocol
|
||||
* representation through calling toString().
|
||||
*/
|
||||
@Test
|
||||
public void testToString() {
|
||||
Assert.assertEquals("VERSION_1_0_0", GuacamoleProtocolVersion.VERSION_1_0_0.toString());
|
||||
Assert.assertEquals("VERSION_1_1_0", GuacamoleProtocolVersion.VERSION_1_1_0.toString());
|
||||
Assert.assertEquals("VERSION_12_103_398", new GuacamoleProtocolVersion(12, 103, 398).toString());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user