Add .gitignore and .ratignore files for various directories
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
10
guacamole-common/.gitignore
vendored
Normal file
10
guacamole-common/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
# Compiled code
|
||||
target/
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
|
||||
# Generated docs
|
||||
doc/doxygen-output
|
||||
|
0
guacamole-common/.ratignore
Normal file
0
guacamole-common/.ratignore
Normal file
109
guacamole-common/pom.xml
Normal file
109
guacamole-common/pom.xml
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.6.0</version>
|
||||
<name>guacamole-common</name>
|
||||
<url>http://guacamole.apache.org/</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-client</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<description>
|
||||
The base Java API of the Guacamole project, providing Java support for
|
||||
the Guacamole stack.
|
||||
</description>
|
||||
|
||||
<!-- All applicable licenses -->
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<!-- Git repository -->
|
||||
<scm>
|
||||
<url>https://github.com/apache/guacamole-client</url>
|
||||
<connection>scm:git:https://git.wip-us.apache.org/repos/asf/guacamole-client.git</connection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<!-- Attach source and JavaDoc .jars -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Java servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JSR 356 WebSocket API -->
|
||||
<dependency>
|
||||
<groupId>javax.websocket</groupId>
|
||||
<artifactId>javax.websocket-api</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- SLF4J - logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when data has been submitted with an unsupported
|
||||
* mimetype.
|
||||
*/
|
||||
public class GuacamoleClientBadTypeException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientBadTypeException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientBadTypeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientBadTypeException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleClientBadTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientBadTypeException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientBadTypeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_BAD_TYPE;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* A generic exception thrown when part of the Guacamole API encounters
|
||||
* an error in the client's request. Such an error, if correctable, usually
|
||||
* requires correction on the client side, not the server.
|
||||
*/
|
||||
public class GuacamoleClientException extends GuacamoleException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleClientException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_BAD_REQUEST;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when the client has sent too much data. This
|
||||
* usually indicates that a server-side buffer is not large enough to
|
||||
* accommodate the data, or protocol specifications prohibit data of the size
|
||||
* received.
|
||||
*/
|
||||
public class GuacamoleClientOverrunException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientOverrunException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientOverrunException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientOverrunException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleClientOverrunException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientOverrunException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientOverrunException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_OVERRUN;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when the client is taking too long to respond.
|
||||
*/
|
||||
public class GuacamoleClientTimeoutException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientTimeoutException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientTimeoutException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientTimeoutException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleClientTimeoutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientTimeoutException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientTimeoutException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_TIMEOUT;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when too many requests have been received
|
||||
* by the current client, and further requests are being rejected, either
|
||||
* temporarily or permanently.
|
||||
*/
|
||||
public class GuacamoleClientTooManyException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientTooManyException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientTooManyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientTooManyException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleClientTooManyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleClientTooManyException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleClientTooManyException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_TOO_MANY;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when an operation cannot be performed because
|
||||
* its corresponding connection is closed.
|
||||
*/
|
||||
public class GuacamoleConnectionClosedException extends GuacamoleServerException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleConnectionClosedException with the given message
|
||||
* and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleConnectionClosedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleConnectionClosedException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleConnectionClosedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleConnectionClosedException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleConnectionClosedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SERVER_ERROR;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* A generic exception thrown when parts of the Guacamole API encounter
|
||||
* errors.
|
||||
*/
|
||||
public class GuacamoleException extends Exception {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Guacamole status associated with this exception. This status
|
||||
* can then be easily translated into an HTTP error code or Guacamole
|
||||
* protocol error code.
|
||||
*
|
||||
* @return The corresponding Guacamole status.
|
||||
*/
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SERVER_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most applicable HTTP status code that can be associated
|
||||
* with this exception.
|
||||
*
|
||||
* @return
|
||||
* An integer representing the most applicable HTTP status code
|
||||
* associated with this exception.
|
||||
*/
|
||||
public int getHttpStatusCode() {
|
||||
return getStatus().getHttpStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most applicable WebSocket status code that can be
|
||||
* associated with this exception.
|
||||
*
|
||||
* @return
|
||||
* An integer representing the most applicable WebSocket status
|
||||
* code associated with this exception.
|
||||
*/
|
||||
public int getWebSocketCode() {
|
||||
return getStatus().getWebSocketCode();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
/**
|
||||
* An exception which is thrown when a resource is no longer available because
|
||||
* it is closed.
|
||||
*/
|
||||
public class GuacamoleResourceClosedException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceClosedException with the given message
|
||||
* and cause.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleResourceClosedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceClosedException with the given message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*/
|
||||
public GuacamoleResourceClosedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceClosedException with the given cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleResourceClosedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.RESOURCE_CLOSED;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when a resource has been requested, but that
|
||||
* resource is locked or currently in use, and cannot be accessed by the
|
||||
* current user.
|
||||
*/
|
||||
public class GuacamoleResourceConflictException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceConflictException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleResourceConflictException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceConflictException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleResourceConflictException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceConflictException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleResourceConflictException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.RESOURCE_CONFLICT;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* A generic exception thrown when part of the Guacamole API fails to find
|
||||
* a requested resource, such as a configuration or tunnel.
|
||||
*/
|
||||
public class GuacamoleResourceNotFoundException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceNotFoundException with the given message
|
||||
* and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleResourceNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceNotFoundException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleResourceNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleResourceNotFoundException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleResourceNotFoundException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.RESOURCE_NOT_FOUND;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* A security-related exception thrown when parts of the Guacamole API is
|
||||
* denying access to a resource.
|
||||
*/
|
||||
public class GuacamoleSecurityException extends GuacamoleClientException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSecurityException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSecurityException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSecurityException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleSecurityException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSecurityException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSecurityException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_FORBIDDEN;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when the server is too busy to service the
|
||||
* request.
|
||||
*/
|
||||
public class GuacamoleServerBusyException extends GuacamoleServerException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleServerBusyException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleServerBusyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleServerBusyException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleServerBusyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleServerBusyException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleServerBusyException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SERVER_BUSY;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* A generic exception thrown when part of the Guacamole API encounters
|
||||
* an unexpected, internal error. An internal error, if correctable, would
|
||||
* require correction on the server side, not the client.
|
||||
*/
|
||||
public class GuacamoleServerException extends GuacamoleException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleServerException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleServerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleServerException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleServerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleServerException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleServerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SERVER_ERROR;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
/**
|
||||
* An exception which indicates that a session within an upstream server (such
|
||||
* as the remote desktop) has been forcibly terminated.
|
||||
*/
|
||||
public class GuacamoleSessionClosedException extends GuacamoleUpstreamException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionClosedException with the given message and
|
||||
* cause.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSessionClosedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionClosedException with the given message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*/
|
||||
public GuacamoleSessionClosedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionClosedException with the given cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSessionClosedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SESSION_CLOSED;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
/**
|
||||
* An exception which indicates that a session within an upstream server (such
|
||||
* as the remote desktop) has ended because it conflicted with another session.
|
||||
*/
|
||||
public class GuacamoleSessionConflictException extends GuacamoleUpstreamException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionConflictException with the given message
|
||||
* and cause.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSessionConflictException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionConflictException with the given message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*/
|
||||
public GuacamoleSessionConflictException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionConflictException with the given cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSessionConflictException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SESSION_CONFLICT;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
/**
|
||||
* An exception which indicates that a session within an upstream server (such
|
||||
* as the remote desktop) has ended because it appeared to be inactive.
|
||||
*/
|
||||
public class GuacamoleSessionTimeoutException extends GuacamoleUpstreamException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionTimeoutException with the given message
|
||||
* and cause.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSessionTimeoutException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionTimeoutException with the given message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*/
|
||||
public GuacamoleSessionTimeoutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleSessionTimeoutException with the given cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleSessionTimeoutException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.SESSION_TIMEOUT;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* A security-related exception thrown when parts of the Guacamole API is
|
||||
* denying access to a resource, but access MAY be granted were the user
|
||||
* authorized (logged in).
|
||||
*/
|
||||
public class GuacamoleUnauthorizedException extends GuacamoleSecurityException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUnauthorizedException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUnauthorizedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUnauthorizedException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleUnauthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUnauthorizedException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUnauthorizedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.CLIENT_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which is thrown when the requested operation is unsupported
|
||||
* or unimplemented.
|
||||
*/
|
||||
public class GuacamoleUnsupportedException extends GuacamoleServerException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUnsupportedException with the given message and cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUnsupportedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUnsupportedException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleUnsupportedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUnsupportedException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUnsupportedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.UNSUPPORTED;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which indicates than an upstream server (such as the remote
|
||||
* desktop) is returning an error or is otherwise unreachable.
|
||||
*/
|
||||
public class GuacamoleUpstreamException extends GuacamoleException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamException with the given message and
|
||||
* cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleUpstreamException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.UPSTREAM_ERROR;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
/**
|
||||
* An exception which indicates that an upstream server (such as the remote
|
||||
* desktop) does not appear to exist.
|
||||
*/
|
||||
public class GuacamoleUpstreamNotFoundException extends GuacamoleUpstreamException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamNotFoundException with the given message
|
||||
* and cause.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamNotFoundException with the given message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*/
|
||||
public GuacamoleUpstreamNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamNotFoundException with the given cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamNotFoundException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.UPSTREAM_NOT_FOUND;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
|
||||
/**
|
||||
* An exception which indicates than an upstream server (such as the remote
|
||||
* desktop) is taking too long to respond.
|
||||
*/
|
||||
public class GuacamoleUpstreamTimeoutException extends GuacamoleUpstreamException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamException with the given message and
|
||||
* cause.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamTimeoutException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamException with the given message.
|
||||
*
|
||||
* @param message A human readable description of the exception that
|
||||
* occurred.
|
||||
*/
|
||||
public GuacamoleUpstreamTimeoutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamException with the given cause.
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamTimeoutException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.UPSTREAM_TIMEOUT;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
|
||||
/**
|
||||
* An exception which indicates that an upstream server (such as the remote
|
||||
* desktop) is not available to service the request.
|
||||
*/
|
||||
public class GuacamoleUpstreamUnavailableException extends GuacamoleUpstreamException {
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamUnavailableException with the given
|
||||
* message and cause.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamUnavailableException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamUnavailableException with the given
|
||||
* message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the exception that occurred.
|
||||
*/
|
||||
public GuacamoleUpstreamUnavailableException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleUpstreamUnavailableException with the given
|
||||
* cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause of this exception.
|
||||
*/
|
||||
public GuacamoleUpstreamUnavailableException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleStatus getStatus() {
|
||||
return GuacamoleStatus.UPSTREAM_UNAVAILABLE;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
|
||||
/**
|
||||
* Provides abstract and raw character read access to a stream of Guacamole
|
||||
* instructions.
|
||||
*/
|
||||
public interface GuacamoleReader {
|
||||
|
||||
/**
|
||||
* Returns whether instruction data is available for reading. Note that
|
||||
* this does not guarantee an entire instruction is available. If a full
|
||||
* instruction is not available, this function can return true, and a call
|
||||
* to read() will still block.
|
||||
*
|
||||
* @return true if instruction data is available for reading, false
|
||||
* otherwise.
|
||||
* @throws GuacamoleException If an error occurs while checking for
|
||||
* available data.
|
||||
*/
|
||||
public boolean available() throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Reads at least one complete Guacamole instruction, returning a buffer
|
||||
* containing one or more complete Guacamole instructions and no
|
||||
* incomplete Guacamole instructions. This function will block until at
|
||||
* least one complete instruction is available.
|
||||
*
|
||||
* @return A buffer containing at least one complete Guacamole instruction,
|
||||
* or null if no more instructions are available for reading.
|
||||
* @throws GuacamoleException If an error occurs while reading from the
|
||||
* stream.
|
||||
*/
|
||||
public char[] read() throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Reads exactly one complete Guacamole instruction and returns the fully
|
||||
* parsed instruction.
|
||||
*
|
||||
* @return The next complete instruction from the stream, fully parsed, or
|
||||
* null if no more instructions are available for reading.
|
||||
* @throws GuacamoleException If an error occurs while reading from the
|
||||
* stream, or if the instruction cannot be
|
||||
* parsed.
|
||||
*/
|
||||
public GuacamoleInstruction readInstruction() throws GuacamoleException;
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
|
||||
/**
|
||||
* Provides abstract and raw character write access to a stream of Guacamole
|
||||
* instructions.
|
||||
*/
|
||||
public interface GuacamoleWriter {
|
||||
|
||||
/**
|
||||
* Writes a portion of the given array of characters to the Guacamole
|
||||
* instruction stream. The portion must contain only complete Guacamole
|
||||
* instructions.
|
||||
*
|
||||
* @param chunk An array of characters containing Guacamole instructions.
|
||||
* @param off The start offset of the portion of the array to write.
|
||||
* @param len The length of the portion of the array to write.
|
||||
* @throws GuacamoleException If an error occurred while writing the
|
||||
* portion of the array specified.
|
||||
*/
|
||||
public void write(char[] chunk, int off, int len) throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Writes the entire given array of characters to the Guacamole instruction
|
||||
* stream. The array must consist only of complete Guacamole instructions.
|
||||
*
|
||||
* @param chunk An array of characters consisting only of complete
|
||||
* Guacamole instructions.
|
||||
* @throws GuacamoleException If an error occurred while writing the
|
||||
* the specified array.
|
||||
*/
|
||||
public void write(char[] chunk) throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Writes the given fully parsed instruction to the Guacamole instruction
|
||||
* stream.
|
||||
*
|
||||
* @param instruction The Guacamole instruction to write.
|
||||
* @throws GuacamoleException If an error occurred while writing the
|
||||
* instruction.
|
||||
*/
|
||||
public void writeInstruction(GuacamoleInstruction instruction) throws GuacamoleException;
|
||||
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.apache.guacamole.protocol.GuacamoleParser;
|
||||
|
||||
/**
|
||||
* A GuacamoleReader which wraps a standard Java Reader, using that Reader as
|
||||
* the Guacamole instruction stream.
|
||||
*/
|
||||
public class ReaderGuacamoleReader implements GuacamoleReader {
|
||||
|
||||
/**
|
||||
* The GuacamoleParser instance for parsing instructions.
|
||||
*/
|
||||
private GuacamoleParser parser = new GuacamoleParser();
|
||||
|
||||
/**
|
||||
* Wrapped Reader to be used for all input.
|
||||
*/
|
||||
private Reader input;
|
||||
|
||||
/**
|
||||
* Creates a new ReaderGuacamoleReader which will use the given Reader as
|
||||
* the Guacamole instruction stream.
|
||||
*
|
||||
* @param input The Reader to use as the Guacamole instruction stream.
|
||||
*/
|
||||
public ReaderGuacamoleReader(Reader input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location within the received data buffer that parsing should begin
|
||||
* when more data is read.
|
||||
*/
|
||||
private int parseStart = 0;
|
||||
|
||||
/**
|
||||
* The buffer holding all received data.
|
||||
*/
|
||||
private char[] buffer = new char[20480];
|
||||
|
||||
/**
|
||||
* The number of characters currently used within the data buffer. All
|
||||
* other characters within the buffer are free space available for
|
||||
* future reads.
|
||||
*/
|
||||
private int usedLength = 0;
|
||||
|
||||
@Override
|
||||
public boolean available() throws GuacamoleException {
|
||||
try {
|
||||
return input.ready() || usedLength > parseStart || parser.hasNext();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] read() throws GuacamoleException {
|
||||
GuacamoleInstruction instruction = readInstruction();
|
||||
if (instruction == null)
|
||||
return null;
|
||||
|
||||
return instruction.toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction readInstruction() throws GuacamoleException {
|
||||
try {
|
||||
// Loop until the parser has prepared a full instruction
|
||||
while (!parser.hasNext()) {
|
||||
|
||||
// Parse as much data from the buffer as we can
|
||||
int parsed = 0;
|
||||
while (parseStart < usedLength && (parsed = parser.append(buffer, parseStart, usedLength - parseStart)) != 0) {
|
||||
parseStart += parsed;
|
||||
}
|
||||
|
||||
// If we still don't have a full instruction attempt to read more data into the buffer
|
||||
if (!parser.hasNext()) {
|
||||
|
||||
// If we have already parsed some of the buffer and the buffer is almost full then we can trim the parsed data off the buffer
|
||||
if (parseStart > 0 && buffer.length - usedLength < GuacamoleParser.INSTRUCTION_MAX_LENGTH) {
|
||||
System.arraycopy(buffer, parseStart, buffer, 0, usedLength - parseStart);
|
||||
usedLength -= parseStart;
|
||||
parseStart = 0;
|
||||
}
|
||||
|
||||
// Read more instruction data into the buffer
|
||||
int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
|
||||
if (numRead == -1)
|
||||
break;
|
||||
|
||||
usedLength += numRead;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return parser.next();
|
||||
|
||||
}
|
||||
catch (SocketTimeoutException e) {
|
||||
throw new GuacamoleUpstreamTimeoutException("Connection to guacd timed out.", e);
|
||||
}
|
||||
catch (SocketException e) {
|
||||
throw new GuacamoleConnectionClosedException("Connection to guacd is closed.", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
|
||||
/**
|
||||
* A GuacamoleWriter which wraps a standard Java Writer, using that Writer as
|
||||
* the Guacamole instruction stream.
|
||||
*/
|
||||
public class WriterGuacamoleWriter implements GuacamoleWriter {
|
||||
|
||||
/**
|
||||
* Wrapped Writer to be used for all output.
|
||||
*/
|
||||
private Writer output;
|
||||
|
||||
/**
|
||||
* Creates a new WriterGuacamoleWriter which will use the given Writer as
|
||||
* the Guacamole instruction stream.
|
||||
*
|
||||
* @param output The Writer to use as the Guacamole instruction stream.
|
||||
*/
|
||||
public WriterGuacamoleWriter(Writer output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] chunk, int off, int len) throws GuacamoleException {
|
||||
try {
|
||||
output.write(chunk, off, len);
|
||||
output.flush();
|
||||
}
|
||||
catch (SocketTimeoutException e) {
|
||||
throw new GuacamoleUpstreamTimeoutException("Connection to guacd timed out.", e);
|
||||
}
|
||||
catch (SocketException e) {
|
||||
throw new GuacamoleConnectionClosedException("Connection to guacd is closed.", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] chunk) throws GuacamoleException {
|
||||
write(chunk, 0, chunk.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInstruction(GuacamoleInstruction instruction) throws GuacamoleException {
|
||||
write(instruction.toString().toCharArray());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* All classes relating directly to data input or output.
|
||||
*/
|
||||
package org.apache.guacamole.io;
|
||||
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
|
||||
/**
|
||||
* Base GuacamoleTunnel implementation which synchronizes access to the
|
||||
* underlying reader and writer with reentrant locks. Implementations need only
|
||||
* provide the tunnel's UUID and socket.
|
||||
*/
|
||||
public abstract class AbstractGuacamoleTunnel implements GuacamoleTunnel {
|
||||
|
||||
/**
|
||||
* Lock acquired when a read operation is in progress.
|
||||
*/
|
||||
private final ReentrantLock readerLock;
|
||||
|
||||
/**
|
||||
* Lock acquired when a write operation is in progress.
|
||||
*/
|
||||
private final ReentrantLock writerLock;
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleTunnel which synchronizes access to the
|
||||
* Guacamole instruction stream associated with the underlying
|
||||
* GuacamoleSocket.
|
||||
*/
|
||||
public AbstractGuacamoleTunnel() {
|
||||
readerLock = new ReentrantLock();
|
||||
writerLock = new ReentrantLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires exclusive read access to the Guacamole instruction stream
|
||||
* and returns a GuacamoleReader for reading from that stream.
|
||||
*
|
||||
* @return A GuacamoleReader for reading from the Guacamole instruction
|
||||
* stream.
|
||||
*/
|
||||
@Override
|
||||
public GuacamoleReader acquireReader() {
|
||||
readerLock.lock();
|
||||
return getSocket().getReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relinquishes exclusive read access to the Guacamole instruction
|
||||
* stream. This function should be called whenever a thread finishes using
|
||||
* a GuacamoleTunnel's GuacamoleReader.
|
||||
*/
|
||||
@Override
|
||||
public void releaseReader() {
|
||||
readerLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there are threads waiting for read access to the
|
||||
* Guacamole instruction stream.
|
||||
*
|
||||
* @return true if threads are waiting for read access the Guacamole
|
||||
* instruction stream, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasQueuedReaderThreads() {
|
||||
return readerLock.hasQueuedThreads();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires exclusive write access to the Guacamole instruction stream
|
||||
* and returns a GuacamoleWriter for writing to that stream.
|
||||
*
|
||||
* @return A GuacamoleWriter for writing to the Guacamole instruction
|
||||
* stream.
|
||||
*/
|
||||
@Override
|
||||
public GuacamoleWriter acquireWriter() {
|
||||
writerLock.lock();
|
||||
return getSocket().getWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relinquishes exclusive write access to the Guacamole instruction
|
||||
* stream. This function should be called whenever a thread finishes using
|
||||
* a GuacamoleTunnel's GuacamoleWriter.
|
||||
*/
|
||||
@Override
|
||||
public void releaseWriter() {
|
||||
writerLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasQueuedWriterThreads() {
|
||||
return writerLock.hasQueuedThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws GuacamoleException {
|
||||
getSocket().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return getSocket().isOpen();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
|
||||
/**
|
||||
* GuacamoleSocket implementation which simply delegates all function calls to
|
||||
* an underlying GuacamoleSocket.
|
||||
*/
|
||||
public class DelegatingGuacamoleSocket implements GuacamoleSocket {
|
||||
|
||||
/**
|
||||
* The wrapped socket.
|
||||
*/
|
||||
private final GuacamoleSocket socket;
|
||||
|
||||
/**
|
||||
* Wraps the given GuacamoleSocket such that all function calls against
|
||||
* this DelegatingGuacamoleSocket will be delegated to it.
|
||||
*
|
||||
* @param socket
|
||||
* The GuacamoleSocket to wrap.
|
||||
*/
|
||||
public DelegatingGuacamoleSocket(GuacamoleSocket socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying GuacamoleSocket wrapped by this
|
||||
* DelegatingGuacamoleSocket.
|
||||
*
|
||||
* @return
|
||||
* The GuacamoleSocket wrapped by this DelegatingGuacamoleSocket.
|
||||
*/
|
||||
protected GuacamoleSocket getDelegateSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return socket.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleReader getReader() {
|
||||
return socket.getReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleWriter getWriter() {
|
||||
return socket.getWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws GuacamoleException {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return socket.isOpen();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
|
||||
/**
|
||||
* GuacamoleTunnel implementation which simply delegates all function calls to
|
||||
* an underlying GuacamoleTunnel.
|
||||
*/
|
||||
public class DelegatingGuacamoleTunnel implements GuacamoleTunnel {
|
||||
|
||||
/**
|
||||
* The wrapped GuacamoleTunnel.
|
||||
*/
|
||||
private final GuacamoleTunnel tunnel;
|
||||
|
||||
/**
|
||||
* Wraps the given tunnel such that all function calls against this tunnel
|
||||
* will be delegated to it.
|
||||
*
|
||||
* @param tunnel
|
||||
* The GuacamoleTunnel to wrap.
|
||||
*/
|
||||
public DelegatingGuacamoleTunnel(GuacamoleTunnel tunnel) {
|
||||
this.tunnel = tunnel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleReader acquireReader() {
|
||||
return tunnel.acquireReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseReader() {
|
||||
tunnel.releaseReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasQueuedReaderThreads() {
|
||||
return tunnel.hasQueuedReaderThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleWriter acquireWriter() {
|
||||
return tunnel.acquireWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseWriter() {
|
||||
tunnel.releaseWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasQueuedWriterThreads() {
|
||||
return tunnel.hasQueuedWriterThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return tunnel.getUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleSocket getSocket() {
|
||||
return tunnel.getSocket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws GuacamoleException {
|
||||
tunnel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return tunnel.isOpen();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
|
||||
/**
|
||||
* Provides abstract socket-like access to a Guacamole connection.
|
||||
*/
|
||||
public interface GuacamoleSocket {
|
||||
|
||||
/**
|
||||
* Returns the name of the protocol to be used. If the protocol is not
|
||||
* known or the implementation refuses to reveal the underlying protocol,
|
||||
* null is returned.
|
||||
*
|
||||
* <p>Implementations <strong>should</strong> aim to expose the name of the
|
||||
* underlying protocol, such that protocol-specific responses like the
|
||||
* "required" and "argv" instructions can be handled correctly by code
|
||||
* consuming the GuacamoleSocket.
|
||||
*
|
||||
* @return
|
||||
* The name of the protocol to be used, or null if this information is
|
||||
* not available.
|
||||
*/
|
||||
public default String getProtocol() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GuacamoleReader which can be used to read from the
|
||||
* Guacamole instruction stream associated with the connection
|
||||
* represented by this GuacamoleSocket.
|
||||
*
|
||||
* @return A GuacamoleReader which can be used to read from the
|
||||
* Guacamole instruction stream.
|
||||
*/
|
||||
public GuacamoleReader getReader();
|
||||
|
||||
/**
|
||||
* Returns a GuacamoleWriter which can be used to write to the
|
||||
* Guacamole instruction stream associated with the connection
|
||||
* represented by this GuacamoleSocket.
|
||||
*
|
||||
* @return A GuacamoleWriter which can be used to write to the
|
||||
* Guacamole instruction stream.
|
||||
*/
|
||||
public GuacamoleWriter getWriter();
|
||||
|
||||
/**
|
||||
* Releases all resources in use by the connection represented by this
|
||||
* GuacamoleSocket.
|
||||
*
|
||||
* @throws GuacamoleException If an error occurs while releasing resources.
|
||||
*/
|
||||
public void close() throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Returns whether this GuacamoleSocket is open and can be used for reading
|
||||
* and writing.
|
||||
*
|
||||
* @return true if this GuacamoleSocket is open, false otherwise.
|
||||
*/
|
||||
public boolean isOpen();
|
||||
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
|
||||
/**
|
||||
* Provides a unique identifier and synchronized access to the GuacamoleReader
|
||||
* and GuacamoleWriter associated with a GuacamoleSocket.
|
||||
*/
|
||||
public interface GuacamoleTunnel {
|
||||
|
||||
/**
|
||||
* The Guacamole protocol instruction opcode reserved for arbitrary
|
||||
* internal use by tunnel implementations. The value of this opcode is
|
||||
* guaranteed to be the empty string (""). Tunnel implementations may use
|
||||
* this opcode for any purpose. It is currently used by the HTTP tunnel to
|
||||
* mark the end of the HTTP response, and by the WebSocket tunnel to
|
||||
* transmit the tunnel UUID.
|
||||
*/
|
||||
static final String INTERNAL_DATA_OPCODE = "";
|
||||
|
||||
/**
|
||||
* Acquires exclusive read access to the Guacamole instruction stream
|
||||
* and returns a GuacamoleReader for reading from that stream.
|
||||
*
|
||||
* @return A GuacamoleReader for reading from the Guacamole instruction
|
||||
* stream.
|
||||
*/
|
||||
GuacamoleReader acquireReader();
|
||||
|
||||
/**
|
||||
* Relinquishes exclusive read access to the Guacamole instruction
|
||||
* stream. This function should be called whenever a thread finishes using
|
||||
* a GuacamoleTunnel's GuacamoleReader.
|
||||
*/
|
||||
void releaseReader();
|
||||
|
||||
/**
|
||||
* Returns whether there are threads waiting for read access to the
|
||||
* Guacamole instruction stream.
|
||||
*
|
||||
* @return true if threads are waiting for read access the Guacamole
|
||||
* instruction stream, false otherwise.
|
||||
*/
|
||||
boolean hasQueuedReaderThreads();
|
||||
|
||||
/**
|
||||
* Acquires exclusive write access to the Guacamole instruction stream
|
||||
* and returns a GuacamoleWriter for writing to that stream.
|
||||
*
|
||||
* @return A GuacamoleWriter for writing to the Guacamole instruction
|
||||
* stream.
|
||||
*/
|
||||
GuacamoleWriter acquireWriter();
|
||||
|
||||
/**
|
||||
* Relinquishes exclusive write access to the Guacamole instruction
|
||||
* stream. This function should be called whenever a thread finishes using
|
||||
* a GuacamoleTunnel's GuacamoleWriter.
|
||||
*/
|
||||
void releaseWriter();
|
||||
|
||||
/**
|
||||
* Returns whether there are threads waiting for write access to the
|
||||
* Guacamole instruction stream.
|
||||
*
|
||||
* @return true if threads are waiting for write access the Guacamole
|
||||
* instruction stream, false otherwise.
|
||||
*/
|
||||
boolean hasQueuedWriterThreads();
|
||||
|
||||
/**
|
||||
* Returns the unique identifier associated with this GuacamoleTunnel.
|
||||
*
|
||||
* @return The unique identifier associated with this GuacamoleTunnel.
|
||||
*/
|
||||
UUID getUUID();
|
||||
|
||||
/**
|
||||
* Returns the GuacamoleSocket used by this GuacamoleTunnel for reading
|
||||
* and writing.
|
||||
*
|
||||
* @return The GuacamoleSocket used by this GuacamoleTunnel.
|
||||
*/
|
||||
GuacamoleSocket getSocket();
|
||||
|
||||
/**
|
||||
* Release all resources allocated to this GuacamoleTunnel.
|
||||
*
|
||||
* @throws GuacamoleException if an error occurs while releasing
|
||||
* resources.
|
||||
*/
|
||||
void close() throws GuacamoleException;
|
||||
|
||||
/**
|
||||
* Returns whether this GuacamoleTunnel is open, or has been closed.
|
||||
*
|
||||
* @return true if this GuacamoleTunnel is open, false if it is closed.
|
||||
*/
|
||||
boolean isOpen();
|
||||
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.ReaderGuacamoleReader;
|
||||
import org.apache.guacamole.io.WriterGuacamoleWriter;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardSocketOptions;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides abstract socket-like access to a Guacamole connection over a given
|
||||
* hostname and port.
|
||||
*/
|
||||
public class InetGuacamoleSocket implements GuacamoleSocket {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private Logger logger = LoggerFactory.getLogger(InetGuacamoleSocket.class);
|
||||
|
||||
/**
|
||||
* The GuacamoleReader this socket should read from.
|
||||
*/
|
||||
private GuacamoleReader reader;
|
||||
|
||||
/**
|
||||
* The GuacamoleWriter this socket should write to.
|
||||
*/
|
||||
private GuacamoleWriter writer;
|
||||
|
||||
/**
|
||||
* The number of milliseconds to wait for data on the TCP socket before
|
||||
* timing out.
|
||||
*/
|
||||
private static final int SOCKET_TIMEOUT = 15000;
|
||||
|
||||
/**
|
||||
* The TCP socket that the GuacamoleReader and GuacamoleWriter exposed
|
||||
* by this class should affect.
|
||||
*/
|
||||
private Socket sock;
|
||||
|
||||
/**
|
||||
* Creates a new InetGuacamoleSocket which reads and writes instructions
|
||||
* to the Guacamole instruction stream of the Guacamole proxy server
|
||||
* running at the given hostname and port.
|
||||
*
|
||||
* @param hostname The hostname of the Guacamole proxy server to connect to.
|
||||
* @param port The port of the Guacamole proxy server to connect to.
|
||||
* @throws GuacamoleException If an error occurs while connecting to the
|
||||
* Guacamole proxy server.
|
||||
*/
|
||||
public InetGuacamoleSocket(String hostname, int port) throws GuacamoleException {
|
||||
|
||||
try {
|
||||
|
||||
logger.debug("Connecting to guacd at {}:{}.", hostname, port);
|
||||
|
||||
// Get address
|
||||
SocketAddress address = new InetSocketAddress(
|
||||
InetAddress.getByName(hostname),
|
||||
port
|
||||
);
|
||||
|
||||
// Connect with timeout
|
||||
sock = new Socket();
|
||||
sock.connect(address, SOCKET_TIMEOUT);
|
||||
|
||||
// Set read timeout
|
||||
sock.setSoTimeout(SOCKET_TIMEOUT);
|
||||
|
||||
// Set TCP_NODELAY to avoid any latency that would otherwise be
|
||||
// added by the networking stack and Nagle's algorithm
|
||||
sock.setTcpNoDelay(true);
|
||||
|
||||
// On successful connect, retrieve I/O streams
|
||||
reader = new ReaderGuacamoleReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
|
||||
writer = new WriterGuacamoleWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
|
||||
|
||||
}
|
||||
catch (SocketTimeoutException e) {
|
||||
throw new GuacamoleUpstreamTimeoutException("Connection timed out.", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws GuacamoleException {
|
||||
try {
|
||||
logger.debug("Closing socket to guacd.");
|
||||
sock.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleWriter getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return !sock.isClosed();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.io.ReaderGuacamoleReader;
|
||||
import org.apache.guacamole.io.WriterGuacamoleWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides abstract socket-like access to a Guacamole connection over SSL to
|
||||
* a given hostname and port.
|
||||
*/
|
||||
public class SSLGuacamoleSocket implements GuacamoleSocket {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private Logger logger = LoggerFactory.getLogger(SSLGuacamoleSocket.class);
|
||||
|
||||
/**
|
||||
* The GuacamoleReader this socket should read from.
|
||||
*/
|
||||
private GuacamoleReader reader;
|
||||
|
||||
/**
|
||||
* The GuacamoleWriter this socket should write to.
|
||||
*/
|
||||
private GuacamoleWriter writer;
|
||||
|
||||
/**
|
||||
* The number of milliseconds to wait for data on the TCP socket before
|
||||
* timing out.
|
||||
*/
|
||||
private static final int SOCKET_TIMEOUT = 15000;
|
||||
|
||||
/**
|
||||
* The TCP socket that the GuacamoleReader and GuacamoleWriter exposed
|
||||
* by this class should affect.
|
||||
*/
|
||||
private Socket sock;
|
||||
|
||||
/**
|
||||
* Creates a new SSLGuacamoleSocket which reads and writes instructions
|
||||
* to the Guacamole instruction stream of the Guacamole proxy server
|
||||
* running at the given hostname and port using SSL.
|
||||
*
|
||||
* @param hostname The hostname of the Guacamole proxy server to connect to.
|
||||
* @param port The port of the Guacamole proxy server to connect to.
|
||||
* @throws GuacamoleException If an error occurs while connecting to the
|
||||
* Guacamole proxy server.
|
||||
*/
|
||||
public SSLGuacamoleSocket(String hostname, int port) throws GuacamoleException {
|
||||
|
||||
// Get factory for SSL sockets
|
||||
SocketFactory socket_factory = SSLSocketFactory.getDefault();
|
||||
|
||||
try {
|
||||
|
||||
logger.debug("Connecting to guacd at {}:{} via SSL/TLS.",
|
||||
hostname, port);
|
||||
|
||||
// Get address
|
||||
SocketAddress address = new InetSocketAddress(
|
||||
InetAddress.getByName(hostname),
|
||||
port
|
||||
);
|
||||
|
||||
// Connect with timeout
|
||||
sock = socket_factory.createSocket();
|
||||
sock.connect(address, SOCKET_TIMEOUT);
|
||||
|
||||
// Set read timeout
|
||||
sock.setSoTimeout(SOCKET_TIMEOUT);
|
||||
|
||||
// On successful connect, retrieve I/O streams
|
||||
reader = new ReaderGuacamoleReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
|
||||
writer = new WriterGuacamoleWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws GuacamoleException {
|
||||
try {
|
||||
logger.debug("Closing socket to guacd.");
|
||||
sock.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleWriter getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return !sock.isClosed();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.net;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* GuacamoleTunnel implementation which uses a provided socket. The UUID of
|
||||
* the tunnel will be randomly generated.
|
||||
*/
|
||||
public class SimpleGuacamoleTunnel extends AbstractGuacamoleTunnel {
|
||||
|
||||
/**
|
||||
* The UUID associated with this tunnel. Every tunnel must have a
|
||||
* corresponding UUID such that tunnel read/write requests can be
|
||||
* directed to the proper tunnel.
|
||||
*/
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
/**
|
||||
* The GuacamoleSocket that tunnel should use for communication on
|
||||
* behalf of the connecting user.
|
||||
*/
|
||||
private final GuacamoleSocket socket;
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleTunnel which synchronizes access to the
|
||||
* Guacamole instruction stream associated with the given GuacamoleSocket.
|
||||
*
|
||||
* @param socket The GuacamoleSocket to provide synchronized access for.
|
||||
*/
|
||||
public SimpleGuacamoleTunnel(GuacamoleSocket socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleSocket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes which apply to network-specific concepts, such as low-level sockets
|
||||
* and tunnels.
|
||||
*/
|
||||
package org.apache.guacamole.net;
|
||||
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* All classes which apply generally across the Guacamole web application
|
||||
* and all other web applications which use the API provided by the
|
||||
* Guacamole project.
|
||||
*/
|
||||
package org.apache.guacamole;
|
||||
|
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.net.DelegatingGuacamoleSocket;
|
||||
import org.apache.guacamole.net.GuacamoleSocket;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A GuacamoleSocket which pre-configures the connection based on a given
|
||||
* GuacamoleConfiguration, completing the initial protocol handshake before
|
||||
* accepting data for read or write.
|
||||
*
|
||||
* This is useful for forcing a connection to the Guacamole proxy server with
|
||||
* a specific configuration while disallowing the client that will be using
|
||||
* this GuacamoleSocket from manually controlling the initial protocol
|
||||
* handshake.
|
||||
*/
|
||||
public class ConfiguredGuacamoleSocket extends DelegatingGuacamoleSocket {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ConfiguredGuacamoleSocket.class);
|
||||
|
||||
/**
|
||||
* The configuration to use when performing the Guacamole protocol
|
||||
* handshake.
|
||||
*/
|
||||
private GuacamoleConfiguration config;
|
||||
|
||||
/**
|
||||
* The unique identifier associated with this connection, as determined
|
||||
* by the "ready" instruction received from the Guacamole proxy.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* The protocol version that will be used to communicate with guacd. The
|
||||
* default is 1.0.0, and, if the server does not provide a specific version
|
||||
* it will be assumed that it operates at this version and certain features
|
||||
* may be unavailable.
|
||||
*/
|
||||
private GuacamoleProtocolVersion protocolVersion =
|
||||
GuacamoleProtocolVersion.VERSION_1_0_0;
|
||||
|
||||
/**
|
||||
* Parses the given "error" instruction, throwing a GuacamoleException that
|
||||
* corresponds to its status code and message.
|
||||
*
|
||||
* @param instruction
|
||||
* The "error" instruction to parse.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* A GuacamoleException that corresponds to the status code and message
|
||||
* present within the given "error" instruction.
|
||||
*/
|
||||
private static void handleReceivedError(GuacamoleInstruction instruction)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Provide reasonable default error message for invalid "error"
|
||||
// instructions that fail to provide one
|
||||
String message = "Internal error within guacd / protocol handling.";
|
||||
|
||||
// Consider all error instructions without a corresponding status code
|
||||
// to be server errors
|
||||
GuacamoleStatus status = GuacamoleStatus.SERVER_ERROR;
|
||||
|
||||
// Parse human-readable message from "error" instruction, warning if no
|
||||
// message was given
|
||||
List<String> args = instruction.getArgs();
|
||||
if (args.size() >= 1)
|
||||
message = args.get(0);
|
||||
else
|
||||
logger.debug("Received \"error\" instruction with no corresponding message.");
|
||||
|
||||
// Parse the status code from the received error instruction, warning
|
||||
// if the status code is missing or invalid
|
||||
if (args.size() >= 2) {
|
||||
try {
|
||||
|
||||
// Translate numeric status code into a GuacamoleStatus
|
||||
int statusCode = Integer.parseInt(args.get(1));
|
||||
GuacamoleStatus parsedStatus = GuacamoleStatus.fromGuacamoleStatusCode(statusCode);
|
||||
if (parsedStatus != null)
|
||||
status = parsedStatus;
|
||||
else
|
||||
logger.debug("Received \"error\" instruction with unknown/invalid status code: {}", statusCode);
|
||||
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
logger.debug("Received \"error\" instruction with non-numeric status code.", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
logger.debug("Received \"error\" instruction without status code.");
|
||||
|
||||
// Convert parsed status code and message to a GuacamoleException
|
||||
throw status.toException(message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the instruction having the given opcode, returning that
|
||||
* instruction once it has been read. If the instruction is never read,
|
||||
* an exception is thrown.
|
||||
*
|
||||
* Respects server control instructions that are allowed during the handshake
|
||||
* phase, namely {@code error} and {@code disconnect}.
|
||||
*
|
||||
* @param reader
|
||||
* The reader to read instructions from.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction we are expecting.
|
||||
*
|
||||
* @return
|
||||
* The instruction having the given opcode.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while reading, or if the expected instruction is
|
||||
* not read.
|
||||
*/
|
||||
private GuacamoleInstruction expect(GuacamoleReader reader, String opcode)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Wait for an instruction
|
||||
GuacamoleInstruction instruction = reader.readInstruction();
|
||||
if (instruction == null)
|
||||
throw new GuacamoleServerException("End of stream while waiting for \"" + opcode + "\".");
|
||||
|
||||
// Report connection closure if server explicitly disconnects
|
||||
if ("disconnect".equals(instruction.getOpcode()))
|
||||
throw new GuacamoleConnectionClosedException("Server disconnected while waiting for \"" + opcode + "\".");
|
||||
|
||||
// Pass through any received errors as GuacamoleExceptions
|
||||
if ("error".equals(instruction.getOpcode()))
|
||||
handleReceivedError(instruction);
|
||||
|
||||
// Ensure instruction has expected opcode
|
||||
if (!instruction.getOpcode().equals(opcode))
|
||||
throw new GuacamoleServerException("Expected \"" + opcode + "\" instruction but instead received \"" + instruction.getOpcode() + "\".");
|
||||
|
||||
return instruction;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConfiguredGuacamoleSocket which uses the given
|
||||
* GuacamoleConfiguration to complete the initial protocol handshake over
|
||||
* the given GuacamoleSocket. A default GuacamoleClientInformation object
|
||||
* is used to provide basic client information.
|
||||
*
|
||||
* @param socket The GuacamoleSocket to wrap.
|
||||
* @param config The GuacamoleConfiguration to use to complete the initial
|
||||
* protocol handshake.
|
||||
* @throws GuacamoleException If an error occurs while completing the
|
||||
* initial protocol handshake.
|
||||
*/
|
||||
public ConfiguredGuacamoleSocket(GuacamoleSocket socket,
|
||||
GuacamoleConfiguration config) throws GuacamoleException {
|
||||
this(socket, config, new GuacamoleClientInformation());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ConfiguredGuacamoleSocket which uses the given
|
||||
* GuacamoleConfiguration and GuacamoleClientInformation to complete the
|
||||
* initial protocol handshake over the given GuacamoleSocket.
|
||||
*
|
||||
* @param socket The GuacamoleSocket to wrap.
|
||||
* @param config The GuacamoleConfiguration to use to complete the initial
|
||||
* protocol handshake.
|
||||
* @param info The GuacamoleClientInformation to use to complete the initial
|
||||
* protocol handshake.
|
||||
* @throws GuacamoleException If an error occurs while completing the
|
||||
* initial protocol handshake.
|
||||
*/
|
||||
public ConfiguredGuacamoleSocket(GuacamoleSocket socket,
|
||||
GuacamoleConfiguration config,
|
||||
GuacamoleClientInformation info) throws GuacamoleException {
|
||||
|
||||
super(socket);
|
||||
this.config = config;
|
||||
|
||||
// Get reader and writer
|
||||
GuacamoleReader reader = socket.getReader();
|
||||
GuacamoleWriter writer = socket.getWriter();
|
||||
|
||||
// Get protocol / connection ID
|
||||
String select_arg = config.getConnectionID();
|
||||
if (select_arg == null)
|
||||
select_arg = config.getProtocol();
|
||||
|
||||
// Send requested protocol or connection ID
|
||||
writer.writeInstruction(new GuacamoleInstruction("select", select_arg));
|
||||
|
||||
// Wait for server args
|
||||
GuacamoleInstruction args = expect(reader, "args");
|
||||
|
||||
// Build args list off provided names and config
|
||||
List<String> arg_names = args.getArgs();
|
||||
String[] arg_values = new String[arg_names.size()];
|
||||
for (int i=0; i<arg_names.size(); i++) {
|
||||
|
||||
// Retrieve argument name
|
||||
String arg_name = arg_names.get(i);
|
||||
|
||||
// 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
|
||||
String value = config.getParameter(arg_name);
|
||||
|
||||
// If value defined, set that value
|
||||
if (value != null) arg_values[i] = value;
|
||||
|
||||
// Otherwise, leave value blank
|
||||
else arg_values[i] = "";
|
||||
|
||||
}
|
||||
|
||||
// Send size
|
||||
writer.writeInstruction(
|
||||
new GuacamoleInstruction(
|
||||
"size",
|
||||
Integer.toString(info.getOptimalScreenWidth()),
|
||||
Integer.toString(info.getOptimalScreenHeight()),
|
||||
Integer.toString(info.getOptimalResolution())
|
||||
)
|
||||
);
|
||||
|
||||
// Send supported audio formats
|
||||
writer.writeInstruction(
|
||||
new GuacamoleInstruction(
|
||||
"audio",
|
||||
info.getAudioMimetypes().toArray(new String[0])
|
||||
));
|
||||
|
||||
// Send supported video formats
|
||||
writer.writeInstruction(
|
||||
new GuacamoleInstruction(
|
||||
"video",
|
||||
info.getVideoMimetypes().toArray(new String[0])
|
||||
));
|
||||
|
||||
// Send supported image formats
|
||||
writer.writeInstruction(
|
||||
new GuacamoleInstruction(
|
||||
"image",
|
||||
info.getImageMimetypes().toArray(new String[0])
|
||||
));
|
||||
|
||||
// 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", timezone));
|
||||
}
|
||||
|
||||
// Send client name, if supported and available
|
||||
if (GuacamoleProtocolCapability.NAME_HANDSHAKE.isSupported(protocolVersion)) {
|
||||
String name = info.getName();
|
||||
if (name != null)
|
||||
writer.writeInstruction(new GuacamoleInstruction("name", name));
|
||||
}
|
||||
|
||||
// Send args
|
||||
writer.writeInstruction(new GuacamoleInstruction("connect", arg_values));
|
||||
|
||||
// Wait for ready, store ID
|
||||
GuacamoleInstruction ready = expect(reader, "ready");
|
||||
|
||||
List<String> ready_args = ready.getArgs();
|
||||
if (ready_args.isEmpty())
|
||||
throw new GuacamoleServerException("No connection ID received");
|
||||
|
||||
id = ready.getArgs().get(0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GuacamoleConfiguration used to configure this
|
||||
* ConfiguredGuacamoleSocket.
|
||||
*
|
||||
* @return The GuacamoleConfiguration used to configure this
|
||||
* ConfiguredGuacamoleSocket.
|
||||
*/
|
||||
public GuacamoleConfiguration getConfiguration() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique ID associated with the Guacamole connection
|
||||
* negotiated by this ConfiguredGuacamoleSocket. The ID is provided by
|
||||
* the "ready" instruction returned by the Guacamole proxy.
|
||||
*
|
||||
* @return The ID of the negotiated Guacamole connection.
|
||||
*/
|
||||
public String getConnectionID() {
|
||||
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 String getProtocol() {
|
||||
return getConfiguration().getProtocol();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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 java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamNotFoundException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamUnavailableException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.net.DelegatingGuacamoleSocket;
|
||||
import org.apache.guacamole.net.GuacamoleSocket;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* GuacamoleSocket which intercepts errors received early in the Guacamole
|
||||
* session. Upstream errors which are intercepted early enough result in
|
||||
* exceptions thrown immediately within the FailoverGuacamoleSocket's
|
||||
* constructor, allowing a different socket to be substituted prior to
|
||||
* fulfilling the connection.
|
||||
*/
|
||||
public class FailoverGuacamoleSocket extends DelegatingGuacamoleSocket {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(FailoverGuacamoleSocket.class);
|
||||
|
||||
/**
|
||||
* The default maximum number of characters of Guacamole instruction data
|
||||
* to store if no explicit limit is provided.
|
||||
*/
|
||||
private static final int DEFAULT_INSTRUCTION_QUEUE_LIMIT = 131072;
|
||||
|
||||
/**
|
||||
* Queue of all instructions read while this FailoverGuacamoleSocket was
|
||||
* being constructed.
|
||||
*/
|
||||
private final Queue<GuacamoleInstruction> instructionQueue =
|
||||
new LinkedList<GuacamoleInstruction>();
|
||||
|
||||
/**
|
||||
* Parses the given "error" instruction, throwing an exception if the
|
||||
* instruction represents an error from the upstream remote desktop.
|
||||
*
|
||||
* @param instruction
|
||||
* The "error" instruction to parse.
|
||||
*
|
||||
* @throws GuacamoleUpstreamException
|
||||
* If the "error" instruction represents an error from the upstream
|
||||
* remote desktop.
|
||||
*/
|
||||
private static void handleUpstreamErrors(GuacamoleInstruction instruction)
|
||||
throws GuacamoleUpstreamException {
|
||||
|
||||
// Ignore error instructions which are missing the status code
|
||||
List<String> args = instruction.getArgs();
|
||||
if (args.size() < 2) {
|
||||
logger.debug("Received \"error\" instruction without status code.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the status code from the received error instruction
|
||||
int statusCode;
|
||||
try {
|
||||
statusCode = Integer.parseInt(args.get(1));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
logger.debug("Received \"error\" instruction with non-numeric status code.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Translate numeric status code into a GuacamoleStatus
|
||||
GuacamoleStatus status = GuacamoleStatus.fromGuacamoleStatusCode(statusCode);
|
||||
if (status == null) {
|
||||
logger.debug("Received \"error\" instruction with unknown/invalid status code: {}", statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle error instructions related to the upstream remote desktop
|
||||
switch (status) {
|
||||
|
||||
// Generic upstream error
|
||||
case UPSTREAM_ERROR:
|
||||
throw new GuacamoleUpstreamException(args.get(0));
|
||||
|
||||
// Upstream is unreachable
|
||||
case UPSTREAM_NOT_FOUND:
|
||||
throw new GuacamoleUpstreamNotFoundException(args.get(0));
|
||||
|
||||
// Upstream did not respond
|
||||
case UPSTREAM_TIMEOUT:
|
||||
throw new GuacamoleUpstreamTimeoutException(args.get(0));
|
||||
|
||||
// Upstream is refusing the connection
|
||||
case UPSTREAM_UNAVAILABLE:
|
||||
throw new GuacamoleUpstreamUnavailableException(args.get(0));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FailoverGuacamoleSocket which reads Guacamole instructions
|
||||
* from the given socket, searching for errors from the upstream remote
|
||||
* desktop until the given instruction queue limit is reached. If an
|
||||
* upstream error is encountered, it is thrown as a
|
||||
* GuacamoleUpstreamException. This constructor will block until an error
|
||||
* is encountered, until insufficient space remains in the instruction
|
||||
* queue, or until the connection appears to have been successful.
|
||||
* Once the FailoverGuacamoleSocket has been created, all reads, writes,
|
||||
* etc. will be delegated to the provided socket.
|
||||
*
|
||||
* @param socket
|
||||
* The GuacamoleSocket of the Guacamole connection this
|
||||
* FailoverGuacamoleSocket should handle.
|
||||
*
|
||||
* @param instructionQueueLimit
|
||||
* The maximum number of characters of Guacamole instruction data to
|
||||
* store within the instruction queue while searching for errors. Once
|
||||
* this limit is exceeded, the connection is assumed to be successful.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while reading data from the provided socket.
|
||||
*
|
||||
* @throws GuacamoleUpstreamException
|
||||
* If the connection to guacd succeeded, but an error occurred while
|
||||
* connecting to the remote desktop.
|
||||
*/
|
||||
public FailoverGuacamoleSocket(GuacamoleSocket socket,
|
||||
final int instructionQueueLimit)
|
||||
throws GuacamoleException, GuacamoleUpstreamException {
|
||||
|
||||
super(socket);
|
||||
|
||||
int totalQueueSize = 0;
|
||||
|
||||
GuacamoleInstruction instruction;
|
||||
GuacamoleReader reader = socket.getReader();
|
||||
|
||||
// Continuously read instructions, searching for errors
|
||||
while ((instruction = reader.readInstruction()) != null) {
|
||||
|
||||
// Add instruction to tail of instruction queue
|
||||
instructionQueue.add(instruction);
|
||||
|
||||
// If instruction is a "sync" instruction, stop reading
|
||||
String opcode = instruction.getOpcode();
|
||||
if (opcode.equals("sync"))
|
||||
break;
|
||||
|
||||
// If instruction is an "error" instruction, parse its contents and
|
||||
// stop reading
|
||||
if (opcode.equals("error")) {
|
||||
handleUpstreamErrors(instruction);
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, track total data parsed, and assume connection is
|
||||
// successful if no error encountered within reasonable space
|
||||
totalQueueSize += instruction.toString().length();
|
||||
if (totalQueueSize >= instructionQueueLimit)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FailoverGuacamoleSocket which reads Guacamole instructions
|
||||
* from the given socket, searching for errors from the upstream remote
|
||||
* desktop until a maximum of 128KB of instruction data has been queued. If
|
||||
* an upstream error is encountered, it is thrown as a
|
||||
* GuacamoleUpstreamException. This constructor will block until an error
|
||||
* is encountered, until insufficient space remains in the instruction
|
||||
* queue, or until the connection appears to have been successful.
|
||||
* Once the FailoverGuacamoleSocket has been created, all reads, writes,
|
||||
* etc. will be delegated to the provided socket.
|
||||
*
|
||||
* @param socket
|
||||
* The GuacamoleSocket of the Guacamole connection this
|
||||
* FailoverGuacamoleSocket should handle.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while reading data from the provided socket.
|
||||
*
|
||||
* @throws GuacamoleUpstreamException
|
||||
* If the connection to guacd succeeded, but an error occurred while
|
||||
* connecting to the remote desktop.
|
||||
*/
|
||||
public FailoverGuacamoleSocket(GuacamoleSocket socket)
|
||||
throws GuacamoleException, GuacamoleUpstreamException {
|
||||
this(socket, DEFAULT_INSTRUCTION_QUEUE_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* GuacamoleReader which reads instructions from the queue populated when
|
||||
* the FailoverGuacamoleSocket was constructed. Once the queue has been
|
||||
* emptied, reads are delegated directly to the reader of the wrapped
|
||||
* socket.
|
||||
*/
|
||||
private final GuacamoleReader queuedReader = new GuacamoleReader() {
|
||||
|
||||
@Override
|
||||
public boolean available() throws GuacamoleException {
|
||||
return !instructionQueue.isEmpty() || getDelegateSocket().getReader().available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] read() throws GuacamoleException {
|
||||
|
||||
// Read instructions from queue before finally delegating to
|
||||
// underlying reader (received when FailoverGuacamoleSocket was
|
||||
// being constructed)
|
||||
if (!instructionQueue.isEmpty()) {
|
||||
GuacamoleInstruction instruction = instructionQueue.remove();
|
||||
return instruction.toString().toCharArray();
|
||||
}
|
||||
|
||||
return getDelegateSocket().getReader().read();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction readInstruction()
|
||||
throws GuacamoleException {
|
||||
|
||||
// Read instructions from queue before finally delegating to
|
||||
// underlying reader (received when FailoverGuacamoleSocket was
|
||||
// being constructed)
|
||||
if (!instructionQueue.isEmpty())
|
||||
return instructionQueue.remove();
|
||||
|
||||
return getDelegateSocket().getReader().readInstruction();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@Override
|
||||
public GuacamoleReader getReader() {
|
||||
return queuedReader;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
|
||||
/**
|
||||
* GuacamoleReader which applies a given GuacamoleFilter to observe or alter all
|
||||
* read instructions. Instructions may also be dropped or denied by the filter.
|
||||
*/
|
||||
public class FilteredGuacamoleReader implements GuacamoleReader {
|
||||
|
||||
/**
|
||||
* The wrapped GuacamoleReader.
|
||||
*/
|
||||
private final GuacamoleReader reader;
|
||||
|
||||
/**
|
||||
* The filter to apply when reading instructions.
|
||||
*/
|
||||
private final GuacamoleFilter filter;
|
||||
|
||||
/**
|
||||
* Wraps the given GuacamoleReader, applying the given filter to all read
|
||||
* instructions. Future reads will return only instructions which pass
|
||||
* the filter.
|
||||
*
|
||||
* @param reader The GuacamoleReader to wrap.
|
||||
* @param filter The filter which dictates which instructions are read, and
|
||||
* how.
|
||||
*/
|
||||
public FilteredGuacamoleReader(GuacamoleReader reader, GuacamoleFilter filter) {
|
||||
this.reader = reader;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean available() throws GuacamoleException {
|
||||
return reader.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] read() throws GuacamoleException {
|
||||
|
||||
GuacamoleInstruction filteredInstruction = readInstruction();
|
||||
if (filteredInstruction == null)
|
||||
return null;
|
||||
|
||||
return filteredInstruction.toString().toCharArray();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction readInstruction() throws GuacamoleException {
|
||||
|
||||
GuacamoleInstruction filteredInstruction;
|
||||
|
||||
// Read and filter instructions until no instructions are dropped
|
||||
do {
|
||||
|
||||
// Read next instruction
|
||||
GuacamoleInstruction unfilteredInstruction = reader.readInstruction();
|
||||
if (unfilteredInstruction == null)
|
||||
return null;
|
||||
|
||||
// Apply filter
|
||||
filteredInstruction = filter.filter(unfilteredInstruction);
|
||||
|
||||
} while (filteredInstruction == null);
|
||||
|
||||
return filteredInstruction;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.net.DelegatingGuacamoleSocket;
|
||||
import org.apache.guacamole.net.GuacamoleSocket;
|
||||
|
||||
/**
|
||||
* Implementation of GuacamoleSocket which allows individual instructions to be
|
||||
* intercepted, overridden, etc.
|
||||
*/
|
||||
public class FilteredGuacamoleSocket extends DelegatingGuacamoleSocket {
|
||||
|
||||
/**
|
||||
* A reader for the wrapped GuacamoleSocket which may be filtered.
|
||||
*/
|
||||
private final GuacamoleReader reader;
|
||||
|
||||
/**
|
||||
* A writer for the wrapped GuacamoleSocket which may be filtered.
|
||||
*/
|
||||
private final GuacamoleWriter writer;
|
||||
|
||||
/**
|
||||
* Creates a new FilteredGuacamoleSocket which uses the given filters to
|
||||
* determine whether instructions read/written are allowed through,
|
||||
* modified, etc. If reads or writes should be unfiltered, simply specify
|
||||
* null rather than a particular filter.
|
||||
*
|
||||
* @param socket The GuacamoleSocket to wrap.
|
||||
* @param readFilter The GuacamoleFilter to apply to all read instructions,
|
||||
* if any.
|
||||
* @param writeFilter The GuacamoleFilter to apply to all written
|
||||
* instructions, if any.
|
||||
*/
|
||||
public FilteredGuacamoleSocket(GuacamoleSocket socket, GuacamoleFilter readFilter, GuacamoleFilter writeFilter) {
|
||||
|
||||
super(socket);
|
||||
|
||||
// Apply filter to reader
|
||||
if (readFilter != null)
|
||||
reader = new FilteredGuacamoleReader(socket.getReader(), readFilter);
|
||||
else
|
||||
reader = socket.getReader();
|
||||
|
||||
// Apply filter to writer
|
||||
if (writeFilter != null)
|
||||
writer = new FilteredGuacamoleWriter(socket.getWriter(), writeFilter);
|
||||
else
|
||||
writer = socket.getWriter();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleWriter getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
|
||||
/**
|
||||
* GuacamoleWriter which applies a given GuacamoleFilter to observe or alter
|
||||
* all written instructions. Instructions may also be dropped or denied by
|
||||
* the filter.
|
||||
*/
|
||||
public class FilteredGuacamoleWriter implements GuacamoleWriter {
|
||||
|
||||
/**
|
||||
* The wrapped GuacamoleWriter.
|
||||
*/
|
||||
private final GuacamoleWriter writer;
|
||||
|
||||
/**
|
||||
* The filter to apply when writing instructions.
|
||||
*/
|
||||
private final GuacamoleFilter filter;
|
||||
|
||||
/**
|
||||
* Parser for reading instructions prior to writing, such that they can be
|
||||
* passed on to the filter.
|
||||
*/
|
||||
private final GuacamoleParser parser = new GuacamoleParser();
|
||||
|
||||
/**
|
||||
* Wraps the given GuacamoleWriter, applying the given filter to all written
|
||||
* instructions. Future writes will only write instructions which pass
|
||||
* the filter.
|
||||
*
|
||||
* @param writer The GuacamoleWriter to wrap.
|
||||
* @param filter The filter which dictates which instructions are written,
|
||||
* and how.
|
||||
*/
|
||||
public FilteredGuacamoleWriter(GuacamoleWriter writer, GuacamoleFilter filter) {
|
||||
this.writer = writer;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] chunk, int offset, int length) throws GuacamoleException {
|
||||
|
||||
// Write all data in chunk
|
||||
while (length > 0) {
|
||||
|
||||
// Pass as much data through the parser as possible
|
||||
int parsed;
|
||||
while ((parsed = parser.append(chunk, offset, length)) != 0) {
|
||||
offset += parsed;
|
||||
length -= parsed;
|
||||
}
|
||||
|
||||
// If no instruction is available, it must be incomplete
|
||||
if (!parser.hasNext())
|
||||
throw new GuacamoleServerException("Filtered write() contained an incomplete instruction.");
|
||||
|
||||
// Write single instruction through filter
|
||||
writeInstruction(parser.next());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] chunk) throws GuacamoleException {
|
||||
write(chunk, 0, chunk.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInstruction(GuacamoleInstruction instruction) throws GuacamoleException {
|
||||
|
||||
// Write instruction only if not dropped
|
||||
GuacamoleInstruction filteredInstruction = filter.filter(instruction);
|
||||
if (filteredInstruction != null)
|
||||
writer.writeInstruction(filteredInstruction);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An abstract representation of Guacamole client information, including all
|
||||
* information required by the Guacamole protocol during the preamble.
|
||||
*/
|
||||
public class GuacamoleClientInformation {
|
||||
|
||||
/**
|
||||
* The optimal screen width requested by the client, in pixels.
|
||||
*/
|
||||
private int optimalScreenWidth = 1024;
|
||||
|
||||
/**
|
||||
* The optimal screen height requested by the client, in pixels.
|
||||
*/
|
||||
private int optimalScreenHeight = 768;
|
||||
|
||||
/**
|
||||
* The resolution of the optimal dimensions given, in DPI.
|
||||
*/
|
||||
private int optimalResolution = 96;
|
||||
|
||||
/**
|
||||
* The list of audio mimetypes reported by the client to be supported.
|
||||
*/
|
||||
private final List<String> audioMimetypes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The list of video mimetypes reported by the client to be supported.
|
||||
*/
|
||||
private final List<String> videoMimetypes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The list of image mimetypes reported by the client to be supported.
|
||||
*/
|
||||
private final List<String> imageMimetypes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The name of the user reported by the client.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The timezone reported by the client.
|
||||
*/
|
||||
private String timezone;
|
||||
|
||||
/**
|
||||
* Returns the optimal screen width requested by the client, in pixels.
|
||||
* @return The optimal screen width requested by the client, in pixels.
|
||||
*/
|
||||
public int getOptimalScreenWidth() {
|
||||
return optimalScreenWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client's optimal screen width.
|
||||
* @param optimalScreenWidth The optimal screen width of the client.
|
||||
*/
|
||||
public void setOptimalScreenWidth(int optimalScreenWidth) {
|
||||
this.optimalScreenWidth = optimalScreenWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the optimal screen height requested by the client, in pixels.
|
||||
* @return The optimal screen height requested by the client, in pixels.
|
||||
*/
|
||||
public int getOptimalScreenHeight() {
|
||||
return optimalScreenHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client's optimal screen height.
|
||||
* @param optimalScreenHeight The optimal screen height of the client.
|
||||
*/
|
||||
public void setOptimalScreenHeight(int optimalScreenHeight) {
|
||||
this.optimalScreenHeight = optimalScreenHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolution of the screen if the optimal width and height are
|
||||
* used, in DPI.
|
||||
*
|
||||
* @return The optimal screen resolution.
|
||||
*/
|
||||
public int getOptimalResolution() {
|
||||
return optimalResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resolution of the screen if the optimal width and height are
|
||||
* used, in DPI.
|
||||
*
|
||||
* @param optimalResolution The optimal screen resolution in DPI.
|
||||
*/
|
||||
public void setOptimalResolution(int optimalResolution) {
|
||||
this.optimalResolution = optimalResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of audio mimetypes supported by the client. To add or
|
||||
* removed supported mimetypes, the list returned by this function can be
|
||||
* modified.
|
||||
*
|
||||
* @return The set of audio mimetypes supported by the client.
|
||||
*/
|
||||
public List<String> getAudioMimetypes() {
|
||||
return audioMimetypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of video mimetypes supported by the client. To add or
|
||||
* removed supported mimetypes, the list returned by this function can be
|
||||
* modified.
|
||||
*
|
||||
* @return The set of video mimetypes supported by the client.
|
||||
*/
|
||||
public List<String> getVideoMimetypes() {
|
||||
return videoMimetypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of image mimetypes supported by the client. To add or
|
||||
* removed supported mimetypes, the list returned by this function can be
|
||||
* modified.
|
||||
*
|
||||
* @return
|
||||
* The set of image mimetypes supported by the client.
|
||||
*/
|
||||
public List<String> getImageMimetypes() {
|
||||
return imageMimetypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the Guacamole user as reported by the client, or null
|
||||
* if the user name is not set.
|
||||
*
|
||||
* @return
|
||||
* A string value of the human-readable name reported by the client.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timezone as reported by the client, or null if the timezone
|
||||
* is not set. Valid timezones are specified in IANA zone key format,
|
||||
* also known as Olson time zone database or TZ Database.
|
||||
*
|
||||
* @return
|
||||
* A string value of the timezone reported by the client.
|
||||
*/
|
||||
public String getTimezone() {
|
||||
return timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the human-readable name of the user associated with this client.
|
||||
*
|
||||
* @param name
|
||||
* The human-readable name of the user associated with this client.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the string value of the timezone, or null if the timezone will not
|
||||
* be provided by the client. Valid timezones are specified in IANA zone
|
||||
* key format (aka Olson time zone database or tz database).
|
||||
*
|
||||
* @param timezone
|
||||
* The string value of the timezone reported by the client, in tz
|
||||
* database format, or null if the timezone is not provided by the
|
||||
* client.
|
||||
*/
|
||||
public void setTimezone(String timezone) {
|
||||
this.timezone = timezone;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* All information necessary to complete the initial protocol handshake of a
|
||||
* Guacamole session.
|
||||
*/
|
||||
public class GuacamoleConfiguration implements Serializable {
|
||||
|
||||
/**
|
||||
* Identifier unique to this version of GuacamoleConfiguration.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The ID of the connection being joined. If this value is present,
|
||||
* the protocol need not be specified.
|
||||
*/
|
||||
private String connectionID;
|
||||
|
||||
/**
|
||||
* The name of the protocol associated with this configuration.
|
||||
*/
|
||||
private String protocol;
|
||||
|
||||
/**
|
||||
* Map of all associated parameter values, indexed by parameter name.
|
||||
*/
|
||||
private final Map<String, String> parameters = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Creates a new, blank GuacamoleConfiguration with its protocol, connection
|
||||
* ID, and parameters unset.
|
||||
*/
|
||||
public GuacamoleConfiguration() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given GuacamoleConfiguration, creating a new, indepedent
|
||||
* GuacamoleConfiguration containing the same protocol, connection ID,
|
||||
* and parameter values, if any.
|
||||
*
|
||||
* @param config The GuacamoleConfiguration to copy.
|
||||
*/
|
||||
public GuacamoleConfiguration(GuacamoleConfiguration config) {
|
||||
|
||||
// Copy protocol and connection ID
|
||||
protocol = config.getProtocol();
|
||||
connectionID = config.getConnectionID();
|
||||
|
||||
// Copy parameter values
|
||||
for (String name : config.getParameterNames())
|
||||
parameters.put(name, config.getParameter(name));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the connection being joined, if any. If no connection
|
||||
* is being joined, this returns null, and the protocol must be set.
|
||||
*
|
||||
* @return The ID of the connection being joined, or null if no connection
|
||||
* is being joined.
|
||||
*/
|
||||
public String getConnectionID() {
|
||||
return connectionID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the connection being joined, if any. If no connection
|
||||
* is being joined, this value must be omitted.
|
||||
*
|
||||
* @param connectionID The ID of the connection being joined.
|
||||
*/
|
||||
public void setConnectionID(String connectionID) {
|
||||
this.connectionID = connectionID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the protocol to be used.
|
||||
*
|
||||
* @return
|
||||
* The name of the protocol to be used.
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the protocol to be used. If no connection is being
|
||||
* joined (a new connection is being established), this value must be set.
|
||||
*
|
||||
* <p>If a connection is being joined, <strong>this value should still be
|
||||
* set</strong> to ensure that protocol-specific responses like the
|
||||
* "required" and "argv" instructions can be understood in their proper
|
||||
* context by other code that may consume this GuacamoleConfiguration like
|
||||
* {@link ConfiguredGuacamoleSocket}.
|
||||
*
|
||||
* <p>If this value is unavailable or remains unset, it is still possible
|
||||
* to join an established connection using
|
||||
* {@link #setConnectionID(java.lang.String)}, however protocol-specific
|
||||
* responses like the "required" and "argv" instructions might not be
|
||||
* possible to handle correctly if the underlying protocol is not made
|
||||
* available through some other means to the client receiving those
|
||||
* responses.
|
||||
*
|
||||
* @param protocol
|
||||
* The name of the protocol to be used.
|
||||
*/
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set for the parameter with the given name, if any.
|
||||
* @param name The name of the parameter to return the value for.
|
||||
* @return The value of the parameter with the given name, or null if
|
||||
* that parameter has not been set.
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
return parameters.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the parameter with the given name.
|
||||
*
|
||||
* @param name The name of the parameter to set the value for.
|
||||
* @param value The value to set for the parameter with the given name.
|
||||
*/
|
||||
public void setParameter(String name, String value) {
|
||||
parameters.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value set for the parameter with the given name.
|
||||
*
|
||||
* @param name The name of the parameter to remove the value of.
|
||||
*/
|
||||
public void unsetParameter(String name) {
|
||||
parameters.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all currently defined parameter names. Each name
|
||||
* corresponds to a parameter that has a value set on this
|
||||
* GuacamoleConfiguration via setParameter().
|
||||
*
|
||||
* @return A set of all currently defined parameter names.
|
||||
*/
|
||||
public Set<String> getParameterNames() {
|
||||
return Collections.unmodifiableSet(parameters.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map which contains parameter name/value pairs as key/value
|
||||
* pairs. Changes to this map will affect the parameters stored within
|
||||
* this configuration.
|
||||
*
|
||||
* @return
|
||||
* A map which contains all parameter name/value pairs as key/value
|
||||
* pairs.
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all current parameters with the parameters defined within the
|
||||
* given map. Key/value pairs within the map represent parameter name/value
|
||||
* pairs.
|
||||
*
|
||||
* @param parameters
|
||||
* A map which contains all parameter name/value pairs as key/value
|
||||
* pairs.
|
||||
*/
|
||||
public void setParameters(Map<String, String> parameters) {
|
||||
this.parameters.clear();
|
||||
this.parameters.putAll(parameters);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.apache.guacamole.GuacamoleException;
|
||||
|
||||
/**
|
||||
* Interface which provides for the filtering of individual instructions. Each
|
||||
* filtered instruction may be allowed through untouched, modified, replaced,
|
||||
* dropped, or explicitly denied.
|
||||
*/
|
||||
public interface GuacamoleFilter {
|
||||
|
||||
/**
|
||||
* Applies the filter to the given instruction, returning the original
|
||||
* instruction, a modified version of the original, or null, depending
|
||||
* on the implementation.
|
||||
*
|
||||
* @param instruction The instruction to filter.
|
||||
* @return The original instruction, if the instruction is to be allowed,
|
||||
* a modified version of the instruction, if the instruction is
|
||||
* to be overridden, or null, if the instruction is to be dropped.
|
||||
* @throws GuacamoleException If an error occurs filtering the instruction,
|
||||
* or if the instruction must be explicitly
|
||||
* denied.
|
||||
*/
|
||||
public GuacamoleInstruction filter(GuacamoleInstruction instruction) throws GuacamoleException;
|
||||
|
||||
}
|
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An abstract representation of a Guacamole instruction, as defined by the
|
||||
* Guacamole protocol.
|
||||
*/
|
||||
public class GuacamoleInstruction {
|
||||
|
||||
/**
|
||||
* The opcode of this instruction.
|
||||
*/
|
||||
private final String opcode;
|
||||
|
||||
/**
|
||||
* All arguments of this instruction, in order.
|
||||
*/
|
||||
private final List<String> args;
|
||||
|
||||
/**
|
||||
* The cached String result of converting this GuacamoleInstruction to the
|
||||
* format used by the Guacamole protocol.
|
||||
*
|
||||
* @see #toString()
|
||||
*/
|
||||
private String rawString = null;
|
||||
|
||||
/**
|
||||
* The cached char[] result of converting this GuacamoleInstruction to the
|
||||
* format used by the Guacamole protocol.
|
||||
*
|
||||
* @see #toCharArray()
|
||||
*/
|
||||
private char[] rawChars = null;
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleInstruction having the given opcode and list of
|
||||
* argument values.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction to create.
|
||||
*
|
||||
* @param args
|
||||
* The list of argument values to provide in the new instruction, if
|
||||
* any.
|
||||
*/
|
||||
public GuacamoleInstruction(String opcode, String... args) {
|
||||
this.opcode = opcode;
|
||||
this.args = Collections.unmodifiableList(Arrays.asList(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleInstruction having the given opcode and list of
|
||||
* argument values. The list given will be used to back the internal list of
|
||||
* arguments and the list returned by {@link #getArgs()}.
|
||||
* <p>
|
||||
* The provided argument list may not be modified in any way after being
|
||||
* provided to this constructor. Doing otherwise will result in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction to create.
|
||||
*
|
||||
* @param args
|
||||
* The list of argument values to provide in the new instruction, if
|
||||
* any.
|
||||
*/
|
||||
public GuacamoleInstruction(String opcode, List<String> args) {
|
||||
this.opcode = opcode;
|
||||
this.args = Collections.unmodifiableList(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the opcode associated with this GuacamoleInstruction.
|
||||
*
|
||||
* @return
|
||||
* The opcode associated with this GuacamoleInstruction.
|
||||
*/
|
||||
public String getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a List of all argument values specified for this
|
||||
* GuacamoleInstruction. Note that the List returned is immutable.
|
||||
* Attempts to modify the list will result in exceptions.
|
||||
*
|
||||
* @return
|
||||
* An unmodifiable List of all argument values specified for this
|
||||
* GuacamoleInstruction.
|
||||
*/
|
||||
public List<String> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given value to the provided StringBuilder as a Guacamole
|
||||
* instruction element, including length prefix.
|
||||
*
|
||||
* @param buff
|
||||
* The StringBuilder to append the element to.
|
||||
*
|
||||
* @param element
|
||||
* The string value of the element to append.
|
||||
*/
|
||||
private static void appendElement(StringBuilder buff, String element) {
|
||||
buff.append(element.codePointCount(0, element.length()));
|
||||
buff.append('.');
|
||||
buff.append(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this GuacamoleInstruction in the form it would be sent over the
|
||||
* Guacamole protocol.
|
||||
*
|
||||
* @return
|
||||
* This GuacamoleInstruction in the form it would be sent over the
|
||||
* Guacamole protocol.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
// Avoid rebuilding Guacamole protocol form of instruction if already
|
||||
// known
|
||||
if (rawString == null) {
|
||||
|
||||
// Prefer to construct String from existing char array, rather than
|
||||
// reconstruct protocol from scratch
|
||||
if (rawChars != null)
|
||||
rawString = new String(rawChars);
|
||||
|
||||
// Reconstruct protocol details only if truly necessary
|
||||
else {
|
||||
|
||||
StringBuilder buff = new StringBuilder();
|
||||
|
||||
// Write opcode
|
||||
appendElement(buff, opcode);
|
||||
|
||||
// Write argument values
|
||||
for (String value : args) {
|
||||
buff.append(',');
|
||||
appendElement(buff, value);
|
||||
}
|
||||
|
||||
// Write terminator
|
||||
buff.append(';');
|
||||
|
||||
// Cache result for future calls
|
||||
rawString = buff.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rawString;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this GuacamoleInstruction in the form it would be sent over the
|
||||
* Guacamole protocol. The returned char[] MUST NOT be modified. If the
|
||||
* returned char[] is modified, the results of doing so are undefined.
|
||||
*
|
||||
* @return
|
||||
* This GuacamoleInstruction in the form it would be sent over the
|
||||
* Guacamole protocol. The returned char[] MUST NOT be modified.
|
||||
*/
|
||||
public char[] toCharArray() {
|
||||
|
||||
// Avoid rebuilding Guacamole protocol form of instruction if already
|
||||
// known
|
||||
if (rawChars == null)
|
||||
rawChars = toString().toCharArray();
|
||||
|
||||
return rawChars;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
|
||||
/**
|
||||
* Parser for the Guacamole protocol. Arbitrary instruction data is appended,
|
||||
* and instructions are returned as a result. Invalid instructions result in
|
||||
* exceptions.
|
||||
*/
|
||||
public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
||||
|
||||
/**
|
||||
* The maximum number of characters per instruction.
|
||||
*/
|
||||
public static final int INSTRUCTION_MAX_LENGTH = 8192;
|
||||
|
||||
/**
|
||||
* The maximum number of digits to allow per length prefix.
|
||||
*/
|
||||
public static final int INSTRUCTION_MAX_DIGITS = 5;
|
||||
|
||||
/**
|
||||
* The maximum number of elements per instruction, including the opcode.
|
||||
*/
|
||||
public static final int INSTRUCTION_MAX_ELEMENTS = 128;
|
||||
|
||||
/**
|
||||
* All possible states of the instruction parser.
|
||||
*/
|
||||
private enum State {
|
||||
|
||||
/**
|
||||
* The parser is currently waiting for data to complete the length prefix
|
||||
* of the current element of the instruction.
|
||||
*/
|
||||
PARSING_LENGTH,
|
||||
|
||||
/**
|
||||
* The parser has finished reading the length prefix and is currently
|
||||
* waiting for data to complete the content of the instruction.
|
||||
*/
|
||||
PARSING_CONTENT,
|
||||
|
||||
/**
|
||||
* The instruction has been fully parsed.
|
||||
*/
|
||||
COMPLETE,
|
||||
|
||||
/**
|
||||
* The instruction cannot be parsed because of a protocol error.
|
||||
*/
|
||||
ERROR
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The latest parsed instruction, if any.
|
||||
*/
|
||||
private GuacamoleInstruction parsedInstruction;
|
||||
|
||||
/**
|
||||
* The parse state of the instruction.
|
||||
*/
|
||||
private State state = State.PARSING_LENGTH;
|
||||
|
||||
/**
|
||||
* The length of the current element, if known, in Java characters. This
|
||||
* value may be adjusted as an element is parsed to take surrogates into
|
||||
* account.
|
||||
*/
|
||||
private int elementLength = 0;
|
||||
|
||||
/**
|
||||
* The length of the current element, if known, in Unicode codepoints. This
|
||||
* value will NOT change as an element is parsed.
|
||||
*/
|
||||
private int elementCodepoints;
|
||||
|
||||
/**
|
||||
* The number of elements currently parsed.
|
||||
*/
|
||||
private int elementCount = 0;
|
||||
|
||||
/**
|
||||
* All currently parsed elements.
|
||||
*/
|
||||
private final String elements[] = new String[INSTRUCTION_MAX_ELEMENTS];
|
||||
|
||||
/**
|
||||
* A copy of the raw protocol data that has been parsed for the current
|
||||
* instruction. This value is maintained by {@link #append(char[], int, int)}.
|
||||
*/
|
||||
private final char rawInstruction[] = new char[INSTRUCTION_MAX_LENGTH];
|
||||
|
||||
/**
|
||||
* The offset within {@link #rawInstruction} that new data should be
|
||||
* appended. This value is maintained by {@link #append(char[], int, int)}.
|
||||
*/
|
||||
private int rawInstructionOffset = 0;
|
||||
|
||||
/**
|
||||
* GuacamoleInstruction that efficiently exposes the originally parsed
|
||||
* character buffer for calls to {@link #toString()} and {@link #toCharArray()}
|
||||
* rather than regenerate the buffer from scratch.
|
||||
*/
|
||||
private static class ParsedGuacamoleInstruction extends GuacamoleInstruction {
|
||||
|
||||
/**
|
||||
* The original data parsed to produce this GuacamoleInstruction.
|
||||
*/
|
||||
private final char[] rawChars;
|
||||
|
||||
/**
|
||||
* A String containing the original data parsed to produce this
|
||||
* GuacamoleInstruction.
|
||||
*/
|
||||
private String rawString = null;
|
||||
|
||||
/**
|
||||
* Creates a new GuacamoleInstruction that efficiently exposes the
|
||||
* originally parsed character buffer rather than regenerating that
|
||||
* buffer from scratch for {@link #toString()} and {@link #toCharArray()}.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction to create.
|
||||
*
|
||||
* @param args
|
||||
* The list of argument values to provide in the new instruction, if
|
||||
* any.
|
||||
*
|
||||
* @param raw
|
||||
* The underlying representation of this instruction as would be sent
|
||||
* over the network via the Guacamole protocol.
|
||||
*/
|
||||
public ParsedGuacamoleInstruction(String opcode, List<String> args, char[] raw) {
|
||||
super(opcode, args);
|
||||
this.rawChars = raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
if (rawString == null)
|
||||
rawString = new String(rawChars);
|
||||
|
||||
return rawString;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] toCharArray() {
|
||||
return rawChars;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends data from the given buffer to the current instruction.
|
||||
*
|
||||
* @param chunk
|
||||
* The buffer containing the data to append.
|
||||
*
|
||||
* @param offset
|
||||
* The offset within the buffer where the data begins.
|
||||
*
|
||||
* @param length
|
||||
* The length of the data to append.
|
||||
*
|
||||
* @return
|
||||
* The number of characters appended, or 0 if complete instructions
|
||||
* have already been parsed and must be read via next() before more
|
||||
* data can be appended.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while parsing the new data.
|
||||
*/
|
||||
public int append(char chunk[], int offset, int length) throws GuacamoleException {
|
||||
|
||||
int originalOffset = offset;
|
||||
int originalLength = length;
|
||||
|
||||
// Process as much of the received chunk as possible
|
||||
while (length > 0) {
|
||||
|
||||
int appended = processElement(chunk, offset, length);
|
||||
if (appended == 0)
|
||||
break;
|
||||
|
||||
length -= appended;
|
||||
offset += appended;
|
||||
}
|
||||
|
||||
// Update the raw copy of the received instruction with whatever data
|
||||
// has now been processed
|
||||
int charsParsed = originalLength - length;
|
||||
if (charsParsed > 0) {
|
||||
|
||||
System.arraycopy(chunk, originalOffset, rawInstruction, rawInstructionOffset, charsParsed);
|
||||
rawInstructionOffset += charsParsed;
|
||||
|
||||
// If the instruction is now complete, we're good to store the
|
||||
// parsed instruction for future retrieval via next()
|
||||
if (state == State.COMPLETE) {
|
||||
parsedInstruction = new ParsedGuacamoleInstruction(elements[0],
|
||||
Arrays.asList(elements).subList(1, elementCount),
|
||||
Arrays.copyOf(rawInstruction, rawInstructionOffset));
|
||||
rawInstructionOffset = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return charsParsed;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes additional data from the given buffer, potentially adding
|
||||
* another element to the current instruction being parsed. This function
|
||||
* will need to be invoked multiple times per instruction until all data
|
||||
* for that instruction is ready.
|
||||
* <p>
|
||||
* This function DOES NOT update {@link #parsedInstruction}. The caller
|
||||
* ({@link #append(char[], int, int)}) must update this as necessary when
|
||||
* the parser {@link #state} indicates the instruction is complete.
|
||||
*
|
||||
* @param chunk
|
||||
* The buffer containing the data to append.
|
||||
*
|
||||
* @param offset
|
||||
* The offset within the buffer where the data begins.
|
||||
*
|
||||
* @param length
|
||||
* The length of the data to append.
|
||||
*
|
||||
* @return
|
||||
* The number of characters appended, or 0 if complete instructions
|
||||
* have already been parsed and must be read via next() before more
|
||||
* data can be appended.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while parsing the new data.
|
||||
*/
|
||||
private int processElement(char chunk[], int offset, int length) throws GuacamoleException {
|
||||
|
||||
int charsParsed = 0;
|
||||
|
||||
// Do not exceed maximum number of elements
|
||||
if (elementCount == INSTRUCTION_MAX_ELEMENTS && state != State.COMPLETE) {
|
||||
state = State.ERROR;
|
||||
throw new GuacamoleServerException("Instruction contains too many elements.");
|
||||
}
|
||||
|
||||
// Parse element length
|
||||
if (state == State.PARSING_LENGTH) {
|
||||
|
||||
int parsedLength = elementLength;
|
||||
while (charsParsed < length) {
|
||||
|
||||
// Pull next character
|
||||
char c = chunk[offset + charsParsed++];
|
||||
|
||||
// If digit, add to length
|
||||
if (c >= '0' && c <= '9')
|
||||
parsedLength = parsedLength*10 + c - '0';
|
||||
|
||||
// If period, switch to parsing content
|
||||
else if (c == '.') {
|
||||
state = State.PARSING_CONTENT;
|
||||
break;
|
||||
}
|
||||
|
||||
// If not digit, parse error
|
||||
else {
|
||||
state = State.ERROR;
|
||||
throw new GuacamoleServerException("Non-numeric character in element length.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If too long, parse error
|
||||
if (parsedLength > INSTRUCTION_MAX_LENGTH) {
|
||||
state = State.ERROR;
|
||||
throw new GuacamoleServerException("Instruction exceeds maximum length.");
|
||||
}
|
||||
|
||||
// Save length
|
||||
elementCodepoints = elementLength = parsedLength;
|
||||
|
||||
} // end parse length
|
||||
|
||||
// Parse element content, if available
|
||||
while (state == State.PARSING_CONTENT && charsParsed + elementLength + 1 <= length) {
|
||||
|
||||
// Read element (which may not match element length if surrogate
|
||||
// characters are present)
|
||||
String element = new String(chunk, offset + charsParsed, elementLength);
|
||||
|
||||
// Verify element contains the number of whole Unicode characters
|
||||
// expected, scheduling a future read if we don't yet have enough
|
||||
// characters
|
||||
int codepoints = element.codePointCount(0, element.length());
|
||||
if (codepoints < elementCodepoints) {
|
||||
elementLength += elementCodepoints - codepoints;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the current element ends with a character involving both
|
||||
// a high and low surrogate, elementLength points to the low
|
||||
// surrogate and NOT the element terminator. We must correct the
|
||||
// length and reevaluate.
|
||||
else if (Character.isSurrogatePair(chunk[offset + charsParsed + elementLength - 1],
|
||||
chunk[offset + charsParsed + elementLength])) {
|
||||
elementLength++;
|
||||
continue;
|
||||
}
|
||||
|
||||
charsParsed += elementLength;
|
||||
elementLength = 0;
|
||||
|
||||
// Add element to currently parsed elements
|
||||
elements[elementCount++] = element;
|
||||
|
||||
// Read terminator char following element
|
||||
char terminator = chunk[offset + charsParsed++];
|
||||
switch (terminator) {
|
||||
|
||||
// If semicolon, store end-of-instruction
|
||||
case ';':
|
||||
state = State.COMPLETE;
|
||||
break;
|
||||
|
||||
// If comma, move on to next element
|
||||
case ',':
|
||||
state = State.PARSING_LENGTH;
|
||||
break;
|
||||
|
||||
// Otherwise, parse error
|
||||
default:
|
||||
state = State.ERROR;
|
||||
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
|
||||
|
||||
}
|
||||
|
||||
} // end parse content
|
||||
|
||||
return charsParsed;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends data from the given buffer to the current instruction.
|
||||
*
|
||||
* @param chunk The data to append.
|
||||
* @return The number of characters appended, or 0 if complete instructions
|
||||
* have already been parsed and must be read via next() before
|
||||
* more data can be appended.
|
||||
* @throws GuacamoleException If an error occurs while parsing the new data.
|
||||
*/
|
||||
public int append(char chunk[]) throws GuacamoleException {
|
||||
return append(chunk, 0, chunk.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return state == State.COMPLETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction next() {
|
||||
|
||||
// No instruction to return if not yet complete
|
||||
if (state != State.COMPLETE)
|
||||
return null;
|
||||
|
||||
// Reset for next instruction.
|
||||
state = State.PARSING_LENGTH;
|
||||
elementCount = 0;
|
||||
elementLength = 0;
|
||||
|
||||
return parsedInstruction;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("GuacamoleParser does not support remove().");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Capabilities which may not be present in all versions of the Guacamole
|
||||
* protocol.
|
||||
*/
|
||||
public enum GuacamoleProtocolCapability {
|
||||
|
||||
/**
|
||||
* 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),
|
||||
|
||||
/**
|
||||
* Support for the "msg" instruction. The "msg" instruction allows the
|
||||
* server to send messages to the client. Support for this instruction was
|
||||
* introduced in {@link GuacamoleProtocolVersion#VERSION_1_5_0}.
|
||||
*/
|
||||
MSG_INSTRUCTION(GuacamoleProtocolVersion.VERSION_1_5_0),
|
||||
|
||||
/**
|
||||
* Support for the "name" handshake instruction, allowing clients to send
|
||||
* the name of the Guacamole user to be passed to guacd and associated with
|
||||
* connections. Support for this instruction was introduced in
|
||||
* {@link GuacamoleProtocolVersion#VERSION_1_5_0}.
|
||||
*/
|
||||
NAME_HANDSHAKE(GuacamoleProtocolVersion.VERSION_1_5_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),
|
||||
|
||||
/**
|
||||
* Support for the "required" instruction. The "required" instruction
|
||||
* allows the server to explicitly request connection parameters from the
|
||||
* client without which the connection cannot continue, such as user
|
||||
* credentials. Support for this instruction was introduced in
|
||||
* {@link GuacamoleProtocolVersion#VERSION_1_3_0}.
|
||||
*/
|
||||
REQUIRED_INSTRUCTION(GuacamoleProtocolVersion.VERSION_1_3_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);
|
||||
|
||||
/**
|
||||
* The minimum protocol version required to support this capability.
|
||||
*/
|
||||
private final GuacamoleProtocolVersion version;
|
||||
|
||||
/**
|
||||
* Create a new enum value with the given protocol version as the minimum
|
||||
* required to support the capability.
|
||||
*
|
||||
* @param version
|
||||
* The minimum required protocol version for supporting the
|
||||
* capability.
|
||||
*/
|
||||
private GuacamoleProtocolCapability(GuacamoleProtocolVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this capability is supported in the given Guacamole
|
||||
* protocol version.
|
||||
*
|
||||
* @param version
|
||||
* The Guacamole protocol version to check.
|
||||
*
|
||||
* @return
|
||||
* true if this capability is supported by the given protocol version,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean isSupported(GuacamoleProtocolVersion version) {
|
||||
return version.atLeast(this.version);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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 java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 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 class GuacamoleProtocolVersion {
|
||||
|
||||
/**
|
||||
* Protocol version 1.0.0 and older. Any client that doesn't explicitly
|
||||
* set the protocol version will negotiate down to this protocol version.
|
||||
* This requires that handshake instructions be ordered correctly, and
|
||||
* lacks support for certain protocol-related features introduced in later
|
||||
* versions.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public static final GuacamoleProtocolVersion VERSION_1_1_0 = new GuacamoleProtocolVersion(1, 1, 0);
|
||||
|
||||
/**
|
||||
* Protocol version 1.3.0, which introduces the "required" instruction
|
||||
* allowing the server to explicitly request connection parameters from the
|
||||
* client.
|
||||
*/
|
||||
public static final GuacamoleProtocolVersion VERSION_1_3_0 = new GuacamoleProtocolVersion(1, 3, 0);
|
||||
|
||||
/**
|
||||
* Protocol version 1.5.0, which introduces the "msg" instruction, allowing
|
||||
* the server to send message notifications that will be displayed on the
|
||||
* client. The version also adds support for the "name" handshake
|
||||
* instruction, allowing guacd to store the name of the user who is
|
||||
* accessing the connection.
|
||||
*/
|
||||
public static final GuacamoleProtocolVersion VERSION_1_5_0 = new GuacamoleProtocolVersion(1, 5, 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_5_0;
|
||||
|
||||
/**
|
||||
* A regular expression that matches the VERSION_X_Y_Z pattern, where
|
||||
* X is the major version component, Y is the minor version component,
|
||||
* and Z is the patch version component. This expression puts each of
|
||||
* the version components in their own group so that they can be easily
|
||||
* used later.
|
||||
*/
|
||||
private static final Pattern VERSION_PATTERN =
|
||||
Pattern.compile("^VERSION_([0-9]+)_([0-9]+)_([0-9]+)$");
|
||||
|
||||
/**
|
||||
* The major version component of the protocol version.
|
||||
*/
|
||||
private final int major;
|
||||
|
||||
/**
|
||||
* The minor version component of the protocol version.
|
||||
*/
|
||||
private final int minor;
|
||||
|
||||
/**
|
||||
* The patch version component of the protocol version.
|
||||
*/
|
||||
private final int patch;
|
||||
|
||||
/**
|
||||
* Generate a new GuacamoleProtocolVersion object with the given
|
||||
* major version, minor version, and patch version.
|
||||
*
|
||||
* @param major
|
||||
* The integer representation of the major version component.
|
||||
*
|
||||
* @param minor
|
||||
* The integer representation of the minor version component.
|
||||
*
|
||||
* @param patch
|
||||
* The integer representation of the patch version component.
|
||||
*/
|
||||
public GuacamoleProtocolVersion(int major, int minor, int patch) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the major version component of the protocol version.
|
||||
*
|
||||
* @return
|
||||
* The integer major version component.
|
||||
*/
|
||||
public int getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minor version component of the protocol version.
|
||||
*
|
||||
* @return
|
||||
* The integer minor version component.
|
||||
*/
|
||||
public int getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the patch version component of the protocol version.
|
||||
*
|
||||
* @return
|
||||
* The integer patch version component.
|
||||
*/
|
||||
public int getPatch() {
|
||||
return patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 GuacamoleProtocolVersion should be compared.
|
||||
*
|
||||
* @return
|
||||
* true if this object is at least as recent as the given version,
|
||||
* false if the given version is newer.
|
||||
*/
|
||||
public boolean atLeast(GuacamoleProtocolVersion otherVersion) {
|
||||
|
||||
// If major is not the same, return inequality
|
||||
if (major != otherVersion.getMajor())
|
||||
return this.major > otherVersion.getMajor();
|
||||
|
||||
// Major is the same, but minor is not, return minor inequality
|
||||
if (minor != otherVersion.getMinor())
|
||||
return this.minor > otherVersion.getMinor();
|
||||
|
||||
// Major and minor are equal, so return patch inequality
|
||||
return patch >= otherVersion.getPatch();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the String format of the version provided and return the
|
||||
* the enum value matching that version. If no value is provided, return
|
||||
* null.
|
||||
*
|
||||
* @param version
|
||||
* The String format of the version to parse.
|
||||
*
|
||||
* @return
|
||||
* 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 parseVersion(String version) {
|
||||
|
||||
// Validate format of version string
|
||||
Matcher versionMatcher = VERSION_PATTERN.matcher(version);
|
||||
if (!versionMatcher.matches())
|
||||
return null;
|
||||
|
||||
// Parse version number from version string
|
||||
return new GuacamoleProtocolVersion(
|
||||
Integer.parseInt(versionMatcher.group(1)),
|
||||
Integer.parseInt(versionMatcher.group(2)),
|
||||
Integer.parseInt(versionMatcher.group(3))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 61 * hash + this.major;
|
||||
hash = 61 * hash + this.minor;
|
||||
hash = 61 * hash + this.patch;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VERSION_" + getMajor() + "_" + getMinor() + "_" + getPatch();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
* 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.apache.guacamole.GuacamoleClientBadTypeException;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleClientOverrunException;
|
||||
import org.apache.guacamole.GuacamoleClientTimeoutException;
|
||||
import org.apache.guacamole.GuacamoleClientTooManyException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleResourceClosedException;
|
||||
import org.apache.guacamole.GuacamoleResourceConflictException;
|
||||
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||
import org.apache.guacamole.GuacamoleSecurityException;
|
||||
import org.apache.guacamole.GuacamoleServerBusyException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.GuacamoleSessionClosedException;
|
||||
import org.apache.guacamole.GuacamoleSessionConflictException;
|
||||
import org.apache.guacamole.GuacamoleSessionTimeoutException;
|
||||
import org.apache.guacamole.GuacamoleUnauthorizedException;
|
||||
import org.apache.guacamole.GuacamoleUnsupportedException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamNotFoundException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamUnavailableException;
|
||||
|
||||
/**
|
||||
* All possible statuses returned by various Guacamole instructions, each having
|
||||
* a corresponding code.
|
||||
*/
|
||||
public enum GuacamoleStatus {
|
||||
|
||||
/**
|
||||
* The operation succeeded.
|
||||
*/
|
||||
SUCCESS(200, 1000, 0x0000) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
throw new IllegalStateException("The Guacamole protocol SUCCESS "
|
||||
+ "status code cannot be converted into a "
|
||||
+ "GuacamoleException.", new GuacamoleServerException(message));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The requested operation is unsupported.
|
||||
*/
|
||||
UNSUPPORTED(501, 1011, 0x0100) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleUnsupportedException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed due to an internal failure.
|
||||
*/
|
||||
SERVER_ERROR(500, 1011, 0x0200) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleServerException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed as the server is busy.
|
||||
*/
|
||||
SERVER_BUSY(503, 1008, 0x0201) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleServerBusyException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed because the upstream server is not
|
||||
* responding.
|
||||
*/
|
||||
UPSTREAM_TIMEOUT(504, 1011, 0x0202) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleUpstreamTimeoutException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation was unsuccessful due to an error or otherwise unexpected
|
||||
* condition of the upstream server.
|
||||
*/
|
||||
UPSTREAM_ERROR(502, 1011, 0x0203) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleUpstreamException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed as the requested resource does not
|
||||
* exist.
|
||||
*/
|
||||
RESOURCE_NOT_FOUND(404, 1002, 0x0204) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleResourceNotFoundException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed as the requested resource is already
|
||||
* in use.
|
||||
*/
|
||||
RESOURCE_CONFLICT(409, 1008, 0x0205) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleResourceConflictException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed as the requested resource is now
|
||||
* closed.
|
||||
*/
|
||||
RESOURCE_CLOSED(404, 1002, 0x0206) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleResourceClosedException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed because the upstream server does
|
||||
* not appear to exist.
|
||||
*/
|
||||
UPSTREAM_NOT_FOUND(502, 1011, 0x0207) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleUpstreamNotFoundException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed because the upstream server is not
|
||||
* available to service the request.
|
||||
*/
|
||||
UPSTREAM_UNAVAILABLE(502, 1011, 0x0208) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleUpstreamUnavailableException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The session within the upstream server has ended because it conflicted
|
||||
* with another session.
|
||||
*/
|
||||
SESSION_CONFLICT(409, 1008, 0x0209) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleSessionConflictException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The session within the upstream server has ended because it appeared to
|
||||
* be inactive.
|
||||
*/
|
||||
SESSION_TIMEOUT(408, 1002, 0x020A) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleSessionTimeoutException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The session within the upstream server has been forcibly terminated.
|
||||
*/
|
||||
SESSION_CLOSED(404, 1002, 0x020B) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleSessionClosedException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation could not be performed because bad parameters were given.
|
||||
*/
|
||||
CLIENT_BAD_REQUEST(400, 1002, 0x0300) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleClientException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Permission was denied to perform the operation, as the user is not yet
|
||||
* authorized (not yet logged in, for example). As HTTP 401 has implications
|
||||
* for HTTP-specific authorization schemes, this status continues to map to
|
||||
* HTTP 403 ("Forbidden"). To do otherwise would risk unintended effects.
|
||||
*/
|
||||
CLIENT_UNAUTHORIZED(403, 1008, 0x0301) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleUnauthorizedException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Permission was denied to perform the operation, and this operation will
|
||||
* not be granted even if the user is authorized.
|
||||
*/
|
||||
CLIENT_FORBIDDEN(403, 1008, 0x0303) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleSecurityException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The client took too long to respond.
|
||||
*/
|
||||
CLIENT_TIMEOUT(408, 1002, 0x0308) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleClientTimeoutException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The client sent too much data.
|
||||
*/
|
||||
CLIENT_OVERRUN(413, 1009, 0x030D) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleClientOverrunException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The client sent data of an unsupported or unexpected type.
|
||||
*/
|
||||
CLIENT_BAD_TYPE(415, 1003, 0x030F) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleClientBadTypeException(message);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The operation failed because the current client is already using too
|
||||
* many resources.
|
||||
*/
|
||||
CLIENT_TOO_MANY(429, 1008, 0x031D) {
|
||||
|
||||
@Override
|
||||
public GuacamoleException toException(String message) {
|
||||
return new GuacamoleClientTooManyException(message);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The most applicable HTTP error code.
|
||||
*/
|
||||
private final int http_code;
|
||||
|
||||
/**
|
||||
* The most applicable WebSocket error code.
|
||||
*/
|
||||
private final int websocket_code;
|
||||
|
||||
/**
|
||||
* The Guacamole protocol status code.
|
||||
*/
|
||||
private final int guac_code;
|
||||
|
||||
/**
|
||||
* Initializes a GuacamoleStatusCode with the given HTTP and Guacamole
|
||||
* status/error code values.
|
||||
*
|
||||
* @param http_code The most applicable HTTP error code.
|
||||
* @param websocket_code The most applicable WebSocket error code.
|
||||
* @param guac_code The Guacamole protocol status code.
|
||||
*/
|
||||
private GuacamoleStatus(int http_code, int websocket_code, int guac_code) {
|
||||
this.http_code = http_code;
|
||||
this.websocket_code = websocket_code;
|
||||
this.guac_code = guac_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most applicable HTTP error code.
|
||||
*
|
||||
* @return The most applicable HTTP error code.
|
||||
*/
|
||||
public int getHttpStatusCode() {
|
||||
return http_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most applicable HTTP error code.
|
||||
*
|
||||
* @return The most applicable HTTP error code.
|
||||
*/
|
||||
public int getWebSocketCode() {
|
||||
return websocket_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding Guacamole protocol status code.
|
||||
*
|
||||
* @return The corresponding Guacamole protocol status code.
|
||||
*/
|
||||
public int getGuacamoleStatusCode() {
|
||||
return guac_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GuacamoleStatus corresponding to the given Guacamole
|
||||
* protocol status code. If no such GuacamoleStatus is defined, null is
|
||||
* returned.
|
||||
*
|
||||
* @param code
|
||||
* The Guacamole protocol status code to translate into a
|
||||
* GuacamoleStatus.
|
||||
*
|
||||
* @return
|
||||
* The GuacamoleStatus corresponding to the given Guacamole protocol
|
||||
* status code, or null if no such GuacamoleStatus is defined.
|
||||
*/
|
||||
public static GuacamoleStatus fromGuacamoleStatusCode(int code) {
|
||||
|
||||
// Search for a GuacamoleStatus having the given status code
|
||||
for (GuacamoleStatus status : values()) {
|
||||
if (status.getGuacamoleStatusCode() == code)
|
||||
return status;
|
||||
}
|
||||
|
||||
// No such status found
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the {@link GuacamoleException} subclass
|
||||
* corresponding to this Guacamole protocol status code. All status codes
|
||||
* have a corresponding GuacamoleException except for {@link SUCCESS}. The
|
||||
* returned GuacamoleException will have the provided human-readable
|
||||
* message.
|
||||
*
|
||||
* @param message
|
||||
* A human readable description of the error that occurred.
|
||||
*
|
||||
* @return
|
||||
* An instance of the {@link GuacamoleException} subclass that
|
||||
* corresponds to this status code and has the provided human-readable
|
||||
* message.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* If invoked on {@link SUCCESS}, which has no corresponding
|
||||
* GuacamoleException.
|
||||
*/
|
||||
public abstract GuacamoleException toException(String message);
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes relating directly to the Guacamole protocol.
|
||||
*/
|
||||
package org.apache.guacamole.protocol;
|
||||
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.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.
|
||||
*/
|
||||
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,211 @@
|
||||
/*
|
||||
* 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.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.
|
||||
*/
|
||||
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 their tunnel-specific
|
||||
* session tokens.
|
||||
*/
|
||||
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 associated with the given tunnel-specific
|
||||
* session token, wrapped within a GuacamoleHTTPTunnel. If the no tunnel
|
||||
* is associated with the given token, null is returned.
|
||||
*
|
||||
* @param tunnelSessionToken
|
||||
* The tunnel-specific session token of the HTTP tunnel to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The GuacamoleTunnel associated with the given tunnel-specific
|
||||
* session token, wrapped within a GuacamoleHTTPTunnel, if such a
|
||||
* tunnel exists, or null if there is no such tunnel.
|
||||
*/
|
||||
public GuacamoleHTTPTunnel get(String tunnelSessionToken) {
|
||||
|
||||
// Update the last access time
|
||||
GuacamoleHTTPTunnel tunnel = tunnelMap.get(tunnelSessionToken);
|
||||
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 tunnelSessionToken
|
||||
* The tunnel-specific session token of the HTTP tunnel being added
|
||||
* (registered).
|
||||
*
|
||||
* @param tunnel
|
||||
* The GuacamoleTunnel being registered, its associated connection
|
||||
* having just been established via HTTP.
|
||||
*/
|
||||
public void put(String tunnelSessionToken, GuacamoleTunnel tunnel) {
|
||||
tunnelMap.put(tunnelSessionToken, new GuacamoleHTTPTunnel(tunnel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the GuacamoleTunnel associated with the given tunnel-specific
|
||||
* session token, if such a tunnel exists. The original tunnel is returned
|
||||
* wrapped within a GuacamoleHTTPTunnel.
|
||||
*
|
||||
* @param tunnelSessionToken
|
||||
* The tunnel-specific session token of the HTTP tunnel to remove
|
||||
* (deregister).
|
||||
*
|
||||
* @return
|
||||
* The GuacamoleTunnel having the given tunnel-specific session token,
|
||||
* 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 tunnelSessionToken) {
|
||||
return tunnelMap.remove(tunnelSessionToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down this tunnel map, disallowing future tunnels from being
|
||||
* registered and reclaiming any resources.
|
||||
*/
|
||||
public void shutdown() {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,594 @@
|
||||
/*
|
||||
* 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.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 java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
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.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.
|
||||
*/
|
||||
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
|
||||
* session token.
|
||||
*/
|
||||
private final GuacamoleHTTPTunnelMap tunnels = new GuacamoleHTTPTunnelMap();
|
||||
|
||||
/**
|
||||
* The name of the HTTP header that contains the tunnel-specific session
|
||||
* token identifying each active and distinct HTTP tunnel connection.
|
||||
*/
|
||||
private static final String TUNNEL_TOKEN_HEADER_NAME = "Guacamole-Tunnel-Token";
|
||||
|
||||
/**
|
||||
* 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:";
|
||||
|
||||
/**
|
||||
* Instance of SecureRandom for generating the session token specific to
|
||||
* each distinct HTTP tunnel connection.
|
||||
*/
|
||||
private final SecureRandom secureRandom = new SecureRandom();
|
||||
|
||||
/**
|
||||
* Instance of Base64.Encoder for encoding random session tokens as
|
||||
* strings.
|
||||
*/
|
||||
private final Base64.Encoder encoder = Base64.getEncoder();
|
||||
|
||||
/**
|
||||
* Generates a new, securely-random session token that may be used to
|
||||
* represent the ongoing communication session of a distinct HTTP tunnel
|
||||
* connection.
|
||||
*
|
||||
* @return
|
||||
* A new, securely-random session token.
|
||||
*/
|
||||
protected String generateToken() {
|
||||
byte[] bytes = new byte[33];
|
||||
secureRandom.nextBytes(bytes);
|
||||
return encoder.encodeToString(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given tunnel such that future read/write requests to that
|
||||
* tunnel will be properly directed.
|
||||
*
|
||||
* @deprecated
|
||||
* This function has been deprecated in favor of {@link #registerTunnel(java.lang.String, org.apache.guacamole.net.GuacamoleTunnel)},
|
||||
* which decouples identification of HTTP tunnel sessions from the
|
||||
* tunnel UUID.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to register.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void registerTunnel(GuacamoleTunnel tunnel) {
|
||||
registerTunnel(tunnel.getUUID().toString(), tunnel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given HTTP tunnel such that future read/write requests
|
||||
* including the given tunnel-specific session token will be properly
|
||||
* directed. The session token must be unpredictable (securely-random) and
|
||||
* unique across all active HTTP tunnels. It is recommended that each HTTP
|
||||
* tunnel session token be obtained through calling {@link #generateToken()}.
|
||||
*
|
||||
* @param tunnelSessionToken
|
||||
* The tunnel-specific session token to associate with the HTTP tunnel
|
||||
* being registered.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to register.
|
||||
*/
|
||||
protected void registerTunnel(String tunnelSessionToken, GuacamoleTunnel tunnel) {
|
||||
tunnels.put(tunnelSessionToken, tunnel);
|
||||
logger.debug("Registered tunnel \"{}\".", tunnel.getUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters the given tunnel such that future read/write requests to
|
||||
* that tunnel will be rejected.
|
||||
*
|
||||
* @deprecated
|
||||
* This function has been deprecated in favor of {@link #deregisterTunnel(java.lang.String)},
|
||||
* which decouples identification of HTTP tunnel sessions from the
|
||||
* tunnel UUID.
|
||||
*
|
||||
* @param tunnel
|
||||
* The tunnel to deregister.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void deregisterTunnel(GuacamoleTunnel tunnel) {
|
||||
deregisterTunnel(tunnel.getUUID().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregisters the HTTP tunnel associated with the given tunnel-specific
|
||||
* session token such that future read/write requests to that tunnel will
|
||||
* be rejected. Each HTTP tunnel must be associated with a session token
|
||||
* unique to that tunnel via a call {@link #registerTunnel(java.lang.String, org.apache.guacamole.net.GuacamoleTunnel)}.
|
||||
*
|
||||
* @param tunnelSessionToken
|
||||
* The tunnel-specific session token associated with the HTTP tunnel
|
||||
* being deregistered.
|
||||
*/
|
||||
protected void deregisterTunnel(String tunnelSessionToken) {
|
||||
GuacamoleTunnel tunnel = tunnels.remove(tunnelSessionToken);
|
||||
if (tunnel != null)
|
||||
logger.debug("Deregistered tunnel \"{}\".", tunnel.getUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tunnel associated with the given tunnel-specific session
|
||||
* token, if it has been registered with {@link #registerTunnel(java.lang.String, org.apache.guacamole.net.GuacamoleTunnel)}
|
||||
* and not yet deregistered with {@link #deregisterTunnel(java.lang.String)}.
|
||||
*
|
||||
* @param tunnelSessionToken
|
||||
* The tunnel-specific session token associated with the HTTP tunnel to
|
||||
* be retrieved.
|
||||
*
|
||||
* @return
|
||||
* The tunnel corresponding to the given session token.
|
||||
*
|
||||
* @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 tunnelSessionToken)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Pull tunnel from map
|
||||
GuacamoleTunnel tunnel = tunnels.get(tunnelSessionToken);
|
||||
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 guacamoleStatusCode
|
||||
* The GuacamoleStatus code to send.
|
||||
*
|
||||
* @param guacamoleHttpCode
|
||||
* The numeric HTTP code to send.
|
||||
*
|
||||
* @param message
|
||||
* The human-readable error message to send.
|
||||
*
|
||||
* @throws ServletException
|
||||
* If an error prevents sending of the error code.
|
||||
*/
|
||||
protected void sendError(HttpServletResponse response, int guacamoleStatusCode,
|
||||
int guacamoleHttpCode, String message)
|
||||
throws ServletException {
|
||||
|
||||
try {
|
||||
|
||||
// If response not committed, send error code and message
|
||||
if (!response.isCommitted()) {
|
||||
response.addHeader("Guacamole-Status-Code", Integer.toString(guacamoleStatusCode));
|
||||
response.addHeader("Guacamole-Error-Message", message);
|
||||
response.sendError(guacamoleHttpCode);
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
// session token and UUID in response
|
||||
if (query.equals("connect")) {
|
||||
|
||||
GuacamoleTunnel tunnel = doConnect(request);
|
||||
if (tunnel == null)
|
||||
throw new GuacamoleResourceNotFoundException("No tunnel created.");
|
||||
|
||||
// Register newly-created tunnel
|
||||
String tunnelSessionToken = generateToken();
|
||||
registerTunnel(tunnelSessionToken, tunnel);
|
||||
|
||||
try {
|
||||
// Ensure buggy browsers do not cache response
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
|
||||
// Include tunnel session token for future requests
|
||||
response.setHeader(TUNNEL_TOKEN_HEADER_NAME, tunnelSessionToken);
|
||||
|
||||
// Send UUID to client
|
||||
response.getWriter().print(tunnel.getUUID().toString());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
}
|
||||
|
||||
// Connection successful
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Pull tunnel-specific session token from request
|
||||
String tunnelSessionToken = request.getHeader(TUNNEL_TOKEN_HEADER_NAME);
|
||||
if (tunnelSessionToken == null)
|
||||
throw new GuacamoleClientException("The HTTP tunnel session "
|
||||
+ "token is required for all requests after "
|
||||
+ "connecting.");
|
||||
|
||||
// Dispatch valid tunnel read/write operations
|
||||
if (query.startsWith(READ_PREFIX))
|
||||
doRead(request, response, tunnelSessionToken);
|
||||
else if (query.startsWith(WRITE_PREFIX))
|
||||
doWrite(request, response, tunnelSessionToken);
|
||||
|
||||
// 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().getGuacamoleStatusCode(),
|
||||
e.getStatus().getHttpStatusCode(), 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().getGuacamoleStatusCode(),
|
||||
e.getStatus().getHttpStatusCode(), "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 tunnelSessionToken
|
||||
* The tunnel-specific session token of the HTTP tunnel to read from,
|
||||
* as specified in the read 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 tunnelSessionToken)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Get tunnel, ensure tunnel exists
|
||||
GuacamoleTunnel tunnel = getTunnel(tunnelSessionToken);
|
||||
|
||||
// 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(tunnelSessionToken);
|
||||
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(tunnelSessionToken);
|
||||
tunnel.close();
|
||||
|
||||
// End-of-instructions marker
|
||||
out.write("0.;");
|
||||
out.flush();
|
||||
response.flushBuffer();
|
||||
|
||||
}
|
||||
|
||||
catch (GuacamoleException e) {
|
||||
|
||||
// Deregister and close
|
||||
deregisterTunnel(tunnelSessionToken);
|
||||
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(tunnelSessionToken);
|
||||
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 tunnelSessionToken
|
||||
* The tunnel-specific session token of the HTTP 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 tunnelSessionToken)
|
||||
throws GuacamoleException {
|
||||
|
||||
GuacamoleTunnel tunnel = getTunnel(tunnelSessionToken);
|
||||
|
||||
// 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(tunnelSessionToken);
|
||||
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,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes which build upon the Java Servlet API, providing an HTTP-based
|
||||
* tunnel and session management.
|
||||
*/
|
||||
package org.apache.guacamole.servlet;
|
||||
|
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.CloseReason.CloseCode;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.OnClose;
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.RemoteEndpoint;
|
||||
import javax.websocket.Session;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.net.GuacamoleTunnel;
|
||||
import org.apache.guacamole.GuacamoleClientException;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.protocol.FilteredGuacamoleWriter;
|
||||
import org.apache.guacamole.protocol.GuacamoleFilter;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.apache.guacamole.protocol.GuacamoleStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A WebSocket implementation of GuacamoleTunnel functionality, compatible with
|
||||
* the Guacamole.WebSocketTunnel object included with the JavaScript API.
|
||||
* Messages sent/received are simply chunks of the Guacamole protocol
|
||||
* instruction stream.
|
||||
*/
|
||||
public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
|
||||
|
||||
/**
|
||||
* The default, minimum buffer size for instructions.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* The opcode of the instruction used to indicate a connection stability
|
||||
* test ping request or response. Note that this instruction is
|
||||
* encapsulated within an internal tunnel instruction (with the opcode
|
||||
* being the empty string), thus this will actually be the value of the
|
||||
* first element of the received instruction.
|
||||
*/
|
||||
private static final String PING_OPCODE = "ping";
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelEndpoint.class);
|
||||
|
||||
/**
|
||||
* The underlying GuacamoleTunnel. WebSocket reads/writes will be handled
|
||||
* as reads/writes to this tunnel. This value may be null if no connection
|
||||
* has been established.
|
||||
*/
|
||||
private GuacamoleTunnel tunnel;
|
||||
|
||||
/**
|
||||
* Remote (client) side of this connection. This value will always be
|
||||
* non-null if tunnel is non-null.
|
||||
*/
|
||||
private RemoteEndpoint.Basic remote;
|
||||
|
||||
/**
|
||||
* Sends the numeric Guacaomle Status Code and Web Socket
|
||||
* code and closes the connection.
|
||||
*
|
||||
* @param session
|
||||
* The outbound WebSocket connection to close.
|
||||
*
|
||||
* @param guacamoleStatusCode
|
||||
* The numeric Guacamole status to send.
|
||||
*
|
||||
* @param webSocketCode
|
||||
* The numeric WebSocket status to send.
|
||||
*/
|
||||
private void closeConnection(Session session, int guacamoleStatusCode,
|
||||
int webSocketCode) {
|
||||
|
||||
try {
|
||||
CloseCode code = CloseReason.CloseCodes.getCloseCode(webSocketCode);
|
||||
String message = Integer.toString(guacamoleStatusCode);
|
||||
session.close(new CloseReason(code, message));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("Unable to close WebSocket connection.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given Guacaomle Status and closes the given
|
||||
* connection.
|
||||
*
|
||||
* @param session
|
||||
* The outbound WebSocket connection to close.
|
||||
*
|
||||
* @param guacStatus
|
||||
* The status to use for the connection.
|
||||
*/
|
||||
private void closeConnection(Session session, GuacamoleStatus guacStatus) {
|
||||
closeConnection(session, guacStatus.getGuacamoleStatusCode(),
|
||||
guacStatus.getWebSocketCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Guacamole instruction along the outbound WebSocket connection to
|
||||
* the connected Guacamole client. If an instruction is already in the
|
||||
* process of being sent by another thread, this function will block until
|
||||
* in-progress instructions are complete.
|
||||
*
|
||||
* @param instruction
|
||||
* The instruction to send.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs preventing the given instruction from being
|
||||
* sent.
|
||||
*/
|
||||
private void sendInstruction(String instruction)
|
||||
throws IOException {
|
||||
|
||||
// NOTE: Synchronization on the non-final remote field here is
|
||||
// intentional. The remote (the outbound websocket connection) is only
|
||||
// sensitive to simultaneous attempts to send messages with respect to
|
||||
// itself. If the remote changes, then the outbound websocket
|
||||
// connection has changed, and synchronization need only be performed
|
||||
// in context of the new remote.
|
||||
synchronized (remote) {
|
||||
remote.sendText(instruction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Guacamole instruction along the outbound WebSocket connection to
|
||||
* the connected Guacamole client. If an instruction is already in the
|
||||
* process of being sent by another thread, this function will block until
|
||||
* in-progress instructions are complete.
|
||||
*
|
||||
* @param instruction
|
||||
* The instruction to send.
|
||||
*
|
||||
* @throws IOException
|
||||
* If an I/O error occurs preventing the given instruction from being
|
||||
* sent.
|
||||
*/
|
||||
private void sendInstruction(GuacamoleInstruction instruction)
|
||||
throws IOException {
|
||||
sendInstruction(instruction.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param config Configuration information associated with the instance of
|
||||
* the endpoint created for handling this single 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, EndpointConfig config)
|
||||
throws GuacamoleException;
|
||||
|
||||
@Override
|
||||
@OnOpen
|
||||
public void onOpen(final Session session, EndpointConfig config) {
|
||||
|
||||
// Store underlying remote for future use via sendInstruction()
|
||||
remote = session.getBasicRemote();
|
||||
|
||||
try {
|
||||
|
||||
// Get tunnel
|
||||
tunnel = createTunnel(session, config);
|
||||
if (tunnel == null) {
|
||||
closeConnection(session, GuacamoleStatus.RESOURCE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
|
||||
logger.debug("Error connecting WebSocket tunnel.", e);
|
||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||
e.getWebSocketCode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Manually register message handler
|
||||
session.addMessageHandler(new MessageHandler.Whole<String>() {
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
GuacamoleWebSocketTunnelEndpoint.this.onMessage(message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Prepare read transfer thread
|
||||
Thread readThread = new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
|
||||
GuacamoleReader reader = tunnel.acquireReader();
|
||||
char[] readMessage;
|
||||
|
||||
try {
|
||||
|
||||
// Send tunnel UUID
|
||||
sendInstruction(new GuacamoleInstruction(
|
||||
GuacamoleTunnel.INTERNAL_DATA_OPCODE,
|
||||
tunnel.getUUID().toString()
|
||||
));
|
||||
|
||||
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) {
|
||||
sendInstruction(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.info("WebSocket connection terminated: {}", e.getMessage());
|
||||
logger.debug("WebSocket connection terminated due to client error.", e);
|
||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||
e.getWebSocketCode());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
closeConnection(session, GuacamoleStatus.SUCCESS);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
|
||||
logger.debug("Internal error during connection to guacd.", e);
|
||||
closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
|
||||
e.getWebSocketCode());
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("I/O error prevents further reads.", e);
|
||||
closeConnection(session, GuacamoleStatus.SERVER_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
readThread.start();
|
||||
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(String message) {
|
||||
|
||||
// Ignore inbound messages if there is no associated tunnel
|
||||
if (tunnel == null)
|
||||
return;
|
||||
|
||||
// Filter received instructions, handling tunnel-internal instructions
|
||||
// without passing through to guacd
|
||||
GuacamoleWriter writer = new FilteredGuacamoleWriter(tunnel.acquireWriter(), new GuacamoleFilter() {
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction filter(GuacamoleInstruction instruction)
|
||||
throws GuacamoleException {
|
||||
|
||||
// Filter out all tunnel-internal instructions
|
||||
if (instruction.getOpcode().equals(GuacamoleTunnel.INTERNAL_DATA_OPCODE)) {
|
||||
|
||||
// Respond to ping requests
|
||||
List<String> args = instruction.getArgs();
|
||||
if (args.size() >= 2 && args.get(0).equals(PING_OPCODE)) {
|
||||
|
||||
try {
|
||||
sendInstruction(new GuacamoleInstruction(
|
||||
GuacamoleTunnel.INTERNAL_DATA_OPCODE,
|
||||
PING_OPCODE, args.get(1)
|
||||
));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.debug("Unable to send \"ping\" response for WebSocket tunnel.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
// Pass through all non-internal instructions untouched
|
||||
return instruction;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
try {
|
||||
// Write received message
|
||||
writer.write(message.toCharArray());
|
||||
}
|
||||
catch (GuacamoleConnectionClosedException e) {
|
||||
logger.debug("Connection to guacd closed.", e);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("WebSocket tunnel write failed.", e);
|
||||
}
|
||||
|
||||
tunnel.releaseWriter();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnClose
|
||||
public void onClose(Session session, CloseReason closeReason) {
|
||||
|
||||
try {
|
||||
if (tunnel != null)
|
||||
tunnel.close();
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
logger.debug("Unable to close WebSocket tunnel.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import java.io.StringReader;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests the ReaderGuacamoleReader implementation of GuacamoleReader, validating
|
||||
* that instructions are parsed correctly.
|
||||
*/
|
||||
public class ReaderGuacamoleReaderTest {
|
||||
|
||||
/**
|
||||
* Test of ReaderGuacamoleReader parsing.
|
||||
*
|
||||
* @throws GuacamoleException If a parse error occurs while parsing the
|
||||
* known-good test string.
|
||||
*/
|
||||
@Test
|
||||
public void testReader() throws GuacamoleException {
|
||||
|
||||
// Test string
|
||||
final String test = "1.a,2.bc,3.def,10.helloworld;4.test,5.test2;0.;3.foo;1.\uD83E\uDD79;";
|
||||
|
||||
GuacamoleReader reader = new ReaderGuacamoleReader(new StringReader(test));
|
||||
|
||||
GuacamoleInstruction instruction;
|
||||
|
||||
// Validate first test instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals(3, instruction.getArgs().size());
|
||||
assertEquals("a", instruction.getOpcode());
|
||||
assertEquals("bc", instruction.getArgs().get(0));
|
||||
assertEquals("def", instruction.getArgs().get(1));
|
||||
assertEquals("helloworld", instruction.getArgs().get(2));
|
||||
|
||||
// Validate second test instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals(1, instruction.getArgs().size());
|
||||
assertEquals("test", instruction.getOpcode());
|
||||
assertEquals("test2", instruction.getArgs().get(0));
|
||||
|
||||
// Validate third test instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals(0, instruction.getArgs().size());
|
||||
assertEquals("", instruction.getOpcode());
|
||||
|
||||
// Validate fourth test instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals(0, instruction.getArgs().size());
|
||||
assertEquals("foo", instruction.getOpcode());
|
||||
|
||||
// Validate fifth test instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals(0, instruction.getArgs().size());
|
||||
assertEquals("\uD83E\uDD79", instruction.getOpcode());
|
||||
|
||||
// There should be no more instructions
|
||||
instruction = reader.readInstruction();
|
||||
assertNull(instruction);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of ReaderGuacamoleReader's read method.
|
||||
*
|
||||
* @throws GuacamoleException If an error occurs while reading the instructions.
|
||||
*/
|
||||
@Test
|
||||
public void testRead() throws GuacamoleException {
|
||||
// Test string containing multiple instructions
|
||||
final String test = "3.foo,3.bar;2.az,4.bazz;";
|
||||
|
||||
ReaderGuacamoleReader reader = new ReaderGuacamoleReader(new StringReader(test));
|
||||
|
||||
// Expected character arrays for the instructions
|
||||
char[] expectedFirstInstruction = "3.foo,3.bar;".toCharArray();
|
||||
char[] expectedSecondInstruction = "2.az,4.bazz;".toCharArray();
|
||||
|
||||
// Read first instruction and verify
|
||||
char[] firstInstructionChars = reader.read();
|
||||
assertNotNull(firstInstructionChars);
|
||||
assertArrayEquals(expectedFirstInstruction, firstInstructionChars);
|
||||
|
||||
// Read second instruction and verify
|
||||
char[] secondInstructionChars = reader.read();
|
||||
assertNotNull(secondInstructionChars);
|
||||
assertArrayEquals(expectedSecondInstruction, secondInstructionChars);
|
||||
|
||||
// Verify that there are no more instructions
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 java.io.StringReader;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleReader;
|
||||
import org.apache.guacamole.io.ReaderGuacamoleReader;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test which validates filtering of Guacamole instructions with
|
||||
* FilteredGuacamoleReader.
|
||||
*/
|
||||
public class FilteredGuacamoleReaderTest {
|
||||
|
||||
/**
|
||||
* Filter which allows through "yes" instructions but drops all others.
|
||||
*/
|
||||
private static class TestFilter implements GuacamoleFilter {
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction filter(GuacamoleInstruction instruction) throws GuacamoleException {
|
||||
|
||||
if (instruction.getOpcode().equals("yes"))
|
||||
return instruction;
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter() throws Exception {
|
||||
|
||||
// Test string
|
||||
final String test = "3.yes,1.A;2.no,1.B;3.yes,1.C;3.yes,1.D;4.nope,1.E;";
|
||||
|
||||
GuacamoleReader reader = new FilteredGuacamoleReader(new ReaderGuacamoleReader(new StringReader(test)),
|
||||
new TestFilter());
|
||||
|
||||
GuacamoleInstruction instruction;
|
||||
|
||||
// Validate first instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals("yes", instruction.getOpcode());
|
||||
assertEquals(1, instruction.getArgs().size());
|
||||
assertEquals("A", instruction.getArgs().get(0));
|
||||
|
||||
// Validate second instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals("yes", instruction.getOpcode());
|
||||
assertEquals(1, instruction.getArgs().size());
|
||||
assertEquals("C", instruction.getArgs().get(0));
|
||||
|
||||
// Validate third instruction
|
||||
instruction = reader.readInstruction();
|
||||
assertNotNull(instruction);
|
||||
assertEquals("yes", instruction.getOpcode());
|
||||
assertEquals(1, instruction.getArgs().size());
|
||||
assertEquals("D", instruction.getArgs().get(0));
|
||||
|
||||
// Should be done now
|
||||
instruction = reader.readInstruction();
|
||||
assertNull(instruction);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 java.io.StringWriter;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.io.GuacamoleWriter;
|
||||
import org.apache.guacamole.io.WriterGuacamoleWriter;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test which validates filtering of Guacamole instructions with
|
||||
* FilteredGuacamoleWriter.
|
||||
*/
|
||||
public class FilteredGuacamoleWriterTest {
|
||||
|
||||
/**
|
||||
* Filter which allows through "yes" instructions but drops all others.
|
||||
*/
|
||||
private static class TestFilter implements GuacamoleFilter {
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction filter(GuacamoleInstruction instruction) throws GuacamoleException {
|
||||
|
||||
if (instruction.getOpcode().equals("yes"))
|
||||
return instruction;
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter() throws Exception {
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
GuacamoleWriter writer = new FilteredGuacamoleWriter(new WriterGuacamoleWriter(stringWriter),
|
||||
new TestFilter());
|
||||
|
||||
// Write a few chunks of complete instructions
|
||||
writer.write("3.yes,1.A;2.no,1.B;3.yes,1.C;3.yes,1.D;4.nope,1.E;".toCharArray());
|
||||
writer.write("1.n,3.abc;3.yes,5.hello;2.no,4.test;3.yes,5.world;".toCharArray());
|
||||
|
||||
// Validate filtered results
|
||||
assertEquals("3.yes,1.A;3.yes,1.C;3.yes,1.D;3.yes,5.hello;3.yes,5.world;", stringWriter.toString());
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for GuacamoleParser. Verifies that parsing of the Guacamole
|
||||
* protocol works as required.
|
||||
*/
|
||||
public class GuacamoleInstructionTest {
|
||||
|
||||
/**
|
||||
* A single test case for verifying that Guacamole protocol implementations
|
||||
* correctly parse or encode Guacamole instructions.
|
||||
*/
|
||||
public static class TestCase extends GuacamoleInstruction {
|
||||
|
||||
/**
|
||||
* The full and correct Guacamole protocol representation of this
|
||||
* instruction.
|
||||
*/
|
||||
public final String UNPARSED;
|
||||
|
||||
/**
|
||||
* The opcode that should be present in the Guacamole instruction;
|
||||
*/
|
||||
public final String OPCODE;
|
||||
|
||||
/**
|
||||
* All arguments that should be present in the Guacamole instruction;
|
||||
*/
|
||||
public final List<String> ARGS;
|
||||
|
||||
/**
|
||||
* Creates a new TestCase representing the given Guacamole instruction.
|
||||
*
|
||||
* @param unparsed
|
||||
* The full and correct Guacamole protocol representation of this
|
||||
* instruction.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the Guacamole instruction.
|
||||
*
|
||||
* @param args
|
||||
* The arguments of the Guacamole instruction, if any.
|
||||
*/
|
||||
public TestCase(String unparsed, String opcode, String... args) {
|
||||
super(opcode, Arrays.copyOf(args, args.length));
|
||||
this.UNPARSED = unparsed;
|
||||
this.OPCODE = opcode;
|
||||
this.ARGS = Collections.unmodifiableList(Arrays.asList(args));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A single Unicode high surrogate character (any character between U+D800
|
||||
* and U+DB7F).
|
||||
*/
|
||||
public static final String HIGH_SURROGATE = "\uD802";
|
||||
|
||||
/**
|
||||
* A single Unicode low surrogate character (any character between U+DC00
|
||||
* and U+DFFF).
|
||||
*/
|
||||
public static final String LOW_SURROGATE = "\uDF00";
|
||||
|
||||
/**
|
||||
* A Unicode surrogate pair, consisting of a high and low surrogate.
|
||||
*/
|
||||
public static final String SURROGATE_PAIR = HIGH_SURROGATE + LOW_SURROGATE;
|
||||
|
||||
/**
|
||||
* A 4-character test string containing Unicode characters that require
|
||||
* multiple bytes when encoded as UTF-8, including at least one character
|
||||
* that is encoded as a surrogate pair in UTF-16.
|
||||
*/
|
||||
public static final String UTF8_MULTIBYTE = "\u72AC" + SURROGATE_PAIR + "z\u00C1";
|
||||
|
||||
/**
|
||||
* Pre-defined set of test cases for verifying Guacamole instructions are
|
||||
* correctly parsed and encoded.
|
||||
*/
|
||||
public static List<TestCase> TEST_CASES = Collections.unmodifiableList(Arrays.asList(
|
||||
|
||||
// Empty instruction
|
||||
new TestCase(
|
||||
"0.;",
|
||||
""
|
||||
),
|
||||
|
||||
// Instruction using basic Latin characters
|
||||
new TestCase(
|
||||
|
||||
"5.test2,"
|
||||
+ "10.hellohello,"
|
||||
+ "15.worldworldworld;",
|
||||
|
||||
"test2",
|
||||
"hellohello",
|
||||
"worldworldworld"
|
||||
|
||||
),
|
||||
|
||||
// Instruction using characters requiring multiple bytes in UTF-8 and
|
||||
// surrogate pairs in UTF-16, including an element ending with a surrogate
|
||||
// pair
|
||||
new TestCase(
|
||||
|
||||
"4.ab" + HIGH_SURROGATE + HIGH_SURROGATE + ","
|
||||
+ "6.a" + UTF8_MULTIBYTE + "b,"
|
||||
+ "5.12345,"
|
||||
+ "10.a" + UTF8_MULTIBYTE + UTF8_MULTIBYTE + "c;",
|
||||
|
||||
"ab" + HIGH_SURROGATE + HIGH_SURROGATE,
|
||||
"a" + UTF8_MULTIBYTE + "b",
|
||||
"12345",
|
||||
"a" + UTF8_MULTIBYTE + UTF8_MULTIBYTE + "c"
|
||||
|
||||
),
|
||||
|
||||
// Instruction with an element values ending with an incomplete surrogate
|
||||
// pair (high or low surrogate only)
|
||||
new TestCase(
|
||||
|
||||
"4.test,"
|
||||
+ "5.1234" + HIGH_SURROGATE + ","
|
||||
+ "5.4567" + LOW_SURROGATE + ";",
|
||||
|
||||
"test",
|
||||
"1234" + HIGH_SURROGATE,
|
||||
"4567" + LOW_SURROGATE
|
||||
|
||||
),
|
||||
|
||||
// Instruction with element values containing incomplete surrogate pairs
|
||||
new TestCase(
|
||||
|
||||
"5.te" + LOW_SURROGATE + "st,"
|
||||
+ "5.12" + HIGH_SURROGATE + "3" + LOW_SURROGATE + ","
|
||||
+ "6.5" + LOW_SURROGATE + LOW_SURROGATE + "4" + HIGH_SURROGATE + HIGH_SURROGATE + ","
|
||||
+ "10." + UTF8_MULTIBYTE + HIGH_SURROGATE + UTF8_MULTIBYTE + HIGH_SURROGATE + ";",
|
||||
|
||||
"te" + LOW_SURROGATE + "st",
|
||||
"12" + HIGH_SURROGATE + "3" + LOW_SURROGATE,
|
||||
"5" + LOW_SURROGATE + LOW_SURROGATE + "4" + HIGH_SURROGATE + HIGH_SURROGATE,
|
||||
UTF8_MULTIBYTE + HIGH_SURROGATE + UTF8_MULTIBYTE + HIGH_SURROGATE
|
||||
|
||||
)
|
||||
|
||||
));
|
||||
|
||||
/**
|
||||
* Verifies that instruction opcodes are represented correctly.
|
||||
*/
|
||||
@Test
|
||||
public void testGetOpcode() {
|
||||
for (TestCase testCase : TEST_CASES) {
|
||||
assertEquals(testCase.OPCODE, testCase.getOpcode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that instruction arguments are represented correctly.
|
||||
*/
|
||||
@Test
|
||||
public void testGetArgs() {
|
||||
for (TestCase testCase : TEST_CASES) {
|
||||
assertEquals(testCase.ARGS, testCase.getArgs());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that instructions are encoded correctly.
|
||||
*/
|
||||
@Test
|
||||
public void testToString() {
|
||||
for (TestCase testCase : TEST_CASES) {
|
||||
assertEquals(testCase.UNPARSED, testCase.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.apache.guacamole.GuacamoleException;
|
||||
import static org.apache.guacamole.protocol.GuacamoleInstructionTest.TEST_CASES;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstructionTest.TestCase;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for GuacamoleParser. Verifies that parsing of the Guacamole
|
||||
* protocol works as required.
|
||||
*/
|
||||
public class GuacamoleParserTest {
|
||||
|
||||
/**
|
||||
* The GuacamoleParser instance being tested.
|
||||
*/
|
||||
private final GuacamoleParser parser = new GuacamoleParser();
|
||||
|
||||
/**
|
||||
* Verify that GuacamoleParser correctly parses each of the instruction
|
||||
* test cases included in the GuacamoleInstruction test.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If a parse error occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testParser() throws GuacamoleException {
|
||||
|
||||
// Build buffer containing all of the instruction test cases, one after
|
||||
// the other
|
||||
StringBuilder allTestCases = new StringBuilder();
|
||||
for (TestCase testCase : TEST_CASES)
|
||||
allTestCases.append(testCase.UNPARSED);
|
||||
|
||||
// Prepare buffer and offsets for feeding the data into the parser as
|
||||
// if received over the network
|
||||
char buffer[] = allTestCases.toString().toCharArray();
|
||||
int offset = 0;
|
||||
int length = buffer.length;
|
||||
|
||||
// Verify that each of the expected instructions is received in order
|
||||
for (TestCase testCase : TEST_CASES) {
|
||||
|
||||
// Feed data into parser until parser refuses to receive more data
|
||||
int parsed;
|
||||
while (length > 0 && (parsed = parser.append(buffer, offset, length)) != 0) {
|
||||
offset += parsed;
|
||||
length -= parsed;
|
||||
}
|
||||
|
||||
// An instruction should now be parsed and ready for retrieval
|
||||
assertTrue(parser.hasNext());
|
||||
|
||||
// Verify instruction contains expected opcode and args
|
||||
GuacamoleInstruction instruction = parser.next();
|
||||
assertNotNull(instruction);
|
||||
assertEquals(testCase.OPCODE, instruction.getOpcode());
|
||||
assertEquals(testCase.ARGS, instruction.getArgs());
|
||||
|
||||
}
|
||||
|
||||
// There should be no more instructions
|
||||
assertFalse(parser.hasNext());
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.apache.guacamole.GuacamoleException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for GuacamoleStatus that verifies exception translation functions
|
||||
* as required.
|
||||
*/
|
||||
public class GuacamoleStatusTest {
|
||||
|
||||
/**
|
||||
* Verifies that the {@link SUCCESS} status code does NOT translate to a
|
||||
* GuacamoleException, but instead throws an IllegalStateException.
|
||||
*/
|
||||
@Test
|
||||
public void testSuccessHasNoException() {
|
||||
|
||||
try {
|
||||
GuacamoleStatus.SUCCESS.toException("Test message");
|
||||
Assert.fail("GuacamoleStatus.SUCCESS must throw "
|
||||
+ "IllegalStateException for toException().");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that each non-success GuacamoleStatus maps to a
|
||||
* GuacamoleException associated with that GuacamoleStatus.
|
||||
*/
|
||||
@Test
|
||||
public void testStatusExceptionMapping() {
|
||||
|
||||
for (GuacamoleStatus status : GuacamoleStatus.values()) {
|
||||
|
||||
// Ignore SUCCESS status (tested via testSuccessHasNoException())
|
||||
if (status == GuacamoleStatus.SUCCESS)
|
||||
continue;
|
||||
|
||||
String message = "Test message: " + status;
|
||||
GuacamoleException e = status.toException(message);
|
||||
|
||||
Assert.assertEquals("toException() should return a "
|
||||
+ "GuacamoleException that maps to the same status.",
|
||||
status, e.getStatus());
|
||||
|
||||
Assert.assertEquals("toException() should return a "
|
||||
+ "GuacamoleException that uses the provided message.",
|
||||
message, e.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user