GUACAMOLE-504: Merge changes adding support for overriding HTTP/WebSocket status codes via custom exceptions.
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleException.java b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleException.java
index 1b2226d..d984226 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleException.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/GuacamoleException.java
@@ -68,5 +68,29 @@
     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();
+    }
     
 }
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/servlet/GuacamoleHTTPTunnelServlet.java b/guacamole-common/src/main/java/org/apache/guacamole/servlet/GuacamoleHTTPTunnelServlet.java
index fdbfb8e..be2da13 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/servlet/GuacamoleHTTPTunnelServlet.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/servlet/GuacamoleHTTPTunnelServlet.java
@@ -149,26 +149,29 @@
      * @param response
      *     The HTTP response to use to send the error.
      *
-     * @param guacStatus
-     *     The status to send
+     * @param guacamoleStatusCode
+     *     The GuacamoleStatus code to send.
+     *
+     * @param guacamoleHttpCode
+     *     The numeric HTTP code to send.
      *
      * @param message
-     *     A human-readable message that can be presented to the user.
+     *     The human-readable error message to send.
      *
      * @throws ServletException
      *     If an error prevents sending of the error code.
      */
-    protected void sendError(HttpServletResponse response,
-            GuacamoleStatus guacStatus, String message)
+    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(guacStatus.getGuacamoleStatusCode()));
+                response.addHeader("Guacamole-Status-Code", Integer.toString(guacamoleStatusCode));
                 response.addHeader("Guacamole-Error-Message", message);
-                response.sendError(guacStatus.getHttpStatusCode());
+                response.sendError(guacamoleHttpCode);
             }
 
         }
@@ -237,14 +240,14 @@
 
             // If read operation, call doRead() with tunnel UUID, ignoring any
             // characters following the tunnel UUID.
-            else if(query.startsWith(READ_PREFIX))
+            else if (query.startsWith(READ_PREFIX))
                 doRead(request, response, query.substring(
                         READ_PREFIX_LENGTH,
                         READ_PREFIX_LENGTH + UUID_LENGTH));
 
             // If write operation, call doWrite() with tunnel UUID, ignoring any
             // characters following the tunnel UUID.
-            else if(query.startsWith(WRITE_PREFIX))
+            else if (query.startsWith(WRITE_PREFIX))
                 doWrite(request, response, query.substring(
                         WRITE_PREFIX_LENGTH,
                         WRITE_PREFIX_LENGTH + UUID_LENGTH));
@@ -258,12 +261,14 @@
         // HTTP response, logging each error appropriately.
         catch (GuacamoleClientException e) {
             logger.warn("HTTP tunnel request rejected: {}", e.getMessage());
-            sendError(response, e.getStatus(), 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(), "Internal server error.");
+            sendError(response, e.getStatus().getGuacamoleStatusCode(),
+                    e.getStatus().getHttpStatusCode(), "Internal server error.");
         }
 
     }
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/websocket/GuacamoleWebSocketTunnelEndpoint.java b/guacamole-common/src/main/java/org/apache/guacamole/websocket/GuacamoleWebSocketTunnelEndpoint.java
index 0cae732..0e02622 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/websocket/GuacamoleWebSocketTunnelEndpoint.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/websocket/GuacamoleWebSocketTunnelEndpoint.java
@@ -66,17 +66,24 @@
     private GuacamoleTunnel tunnel;
     
     /**
-     * Sends the given status on the given WebSocket connection and closes the
-     * connection.
+     * Sends the numeric Guacaomle Status Code and Web Socket
+     * code and closes the connection.
      *
-     * @param session The outbound WebSocket connection to close.
-     * @param guac_status The status to send.
+     * @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, GuacamoleStatus guac_status) {
+    private void closeConnection(Session session, int guacamoleStatusCode,
+            int webSocketCode) {
 
         try {
-            CloseCode code = CloseReason.CloseCodes.getCloseCode(guac_status.getWebSocketCode());
-            String message = Integer.toString(guac_status.getGuacamoleStatusCode());
+            CloseCode code = CloseReason.CloseCodes.getCloseCode(webSocketCode);
+            String message = Integer.toString(guacamoleStatusCode);
             session.close(new CloseReason(code, message));
         }
         catch (IOException e) {
@@ -86,6 +93,21 @@
     }
 
     /**
+     * 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());
+    }
+
+    /**
      * Returns a new tunnel for the given session. How this tunnel is created
      * or retrieved is implementation-dependent.
      *
@@ -117,7 +139,8 @@
         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());
+            closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
+                    e.getWebSocketCode());
             return;
         }
 
@@ -181,7 +204,8 @@
                     catch (GuacamoleClientException e) {
                         logger.info("WebSocket connection terminated: {}", e.getMessage());
                         logger.debug("WebSocket connection terminated due to client error.", e);
-                        closeConnection(session, e.getStatus());
+                        closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
+                                e.getWebSocketCode());
                     }
                     catch (GuacamoleConnectionClosedException e) {
                         logger.debug("Connection to guacd closed.", e);
@@ -190,7 +214,8 @@
                     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());
+                        closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
+                                e.getWebSocketCode());
                     }
 
                 }
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java
index 28736e8..a0c756e 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java
@@ -173,7 +173,7 @@
         // Translate GuacamoleException subclasses to HTTP error codes
         catch (GuacamoleException e) {
             throw new APIException(
-                Response.Status.fromStatusCode(e.getStatus().getHttpStatusCode()),
+                Response.Status.fromStatusCode(e.getHttpStatusCode()),
                 e
             );
         }
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java
index 9769646..e5c5db9 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java
@@ -53,17 +53,42 @@
     private static final int BUFFER_SIZE = 8192;
 
     /**
-     * Sends the given status on the given WebSocket connection and closes the
+     * Sends the given numeric Guacamole and WebSocket status
+     * on the given WebSocket connection and closes the
      * connection.
      *
-     * @param connection The WebSocket connection to close.
-     * @param guac_status The status to send.
+     * @param connection
+     *     The WebSocket connection to close.
+     *
+     * @param guacamoleStatusCode
+     *     The numeric Guacamole Status code to send.
+     *
+     * @param webSocketCode
+     *     The numeric WebSocket status code to send.
      */
-    public static void closeConnection(Connection connection,
-            GuacamoleStatus guac_status) {
+    private static void closeConnection(Connection connection,
+            int guacamoleStatusCode, int webSocketCode) {
 
-        connection.close(guac_status.getWebSocketCode(),
-                Integer.toString(guac_status.getGuacamoleStatusCode()));
+        connection.close(webSocketCode,
+                Integer.toString(guacamoleStatusCode));
+
+    }
+
+    /**
+     * Sends the given status on the given WebSocket connection
+     * and closes the connection.
+     *
+     * @param connection
+     *     The WebSocket connection to close.
+     *
+     * @param guacStatus
+     *     The status to send.
+     */
+    private static void closeConnection(Connection connection,
+            GuacamoleStatus guacStatus) {
+
+        closeConnection(connection, guacStatus.getGuacamoleStatusCode(),
+                guacStatus.getWebSocketCode());
 
     }
 
@@ -114,7 +139,8 @@
                 catch (GuacamoleException e) {
                     logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
                     logger.debug("Error connecting WebSocket tunnel.", e);
-                    closeConnection(connection, e.getStatus());
+                    closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
+                            e.getWebSocketCode());
                     return;
                 }
 
@@ -168,7 +194,8 @@
                             catch (GuacamoleClientException e) {
                                 logger.info("WebSocket connection terminated: {}", e.getMessage());
                                 logger.debug("WebSocket connection terminated due to client error.", e);
-                                closeConnection(connection, e.getStatus());
+                                closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
+                                        e.getWebSocketCode());
                             }
                             catch (GuacamoleConnectionClosedException e) {
                                 logger.debug("Connection to guacd closed.", e);
@@ -177,7 +204,8 @@
                             catch (GuacamoleException e) {
                                 logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
                                 logger.debug("Internal error during connection to guacd.", e);
-                                closeConnection(connection, e.getStatus());
+                                closeConnection(connection, e.getStatus().getGuacamoleStatusCode(),
+                                        e.getWebSocketCode());
                             }
 
                         }
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java
index 5375d75..0594d06 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java
@@ -57,18 +57,25 @@
     private GuacamoleTunnel tunnel;
  
     /**
-     * Sends the given status on the given WebSocket connection and closes the
+     * Sends the given numeric Guacamole and WebSocket status
+     * codes on the given WebSocket connection and closes the
      * connection.
      *
-     * @param session The outbound WebSocket connection to close.
-     * @param guac_status The status to send.
+     * @param session
+     *     The outbound WebSocket connection to close.
+     *
+     * @param guacamoleStatusCode
+     *     The numeric Guacamole status code to send.
+     *
+     * @param webSocketCode
+     *     The numeric WebSocket status code to send.
      */
-    private void closeConnection(Session session, GuacamoleStatus guac_status) {
+    private void closeConnection(Session session, int guacamoleStatusCode,
+            int webSocketCode) {
 
         try {
-            int code = guac_status.getWebSocketCode();
-            String message = Integer.toString(guac_status.getGuacamoleStatusCode());
-            session.close(new CloseStatus(code, message));
+            String message = Integer.toString(guacamoleStatusCode);
+            session.close(new CloseStatus(webSocketCode, message));
         }
         catch (IOException e) {
             logger.debug("Unable to close WebSocket connection.", e);
@@ -77,6 +84,24 @@
     }
 
     /**
+     * Sends the given status on the given WebSocket connection
+     * and closes the connection.
+     *
+     * @param session
+     *     The outbound WebSocket connection to close.
+     *
+     * @param guacStatus
+     *     The status to send.
+     */
+    private void closeConnection(Session session,
+            GuacamoleStatus guacStatus) {
+
+        closeConnection(session, guacStatus.getGuacamoleStatusCode(),
+                guacStatus.getWebSocketCode());
+
+    }
+
+    /**
      * Returns a new tunnel for the given session. How this tunnel is created
      * or retrieved is implementation-dependent.
      *
@@ -105,7 +130,7 @@
         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());
+            closeConnection(session, e.getStatus().getGuacamoleStatusCode(), e.getWebSocketCode());
             return;
         }
 
@@ -159,7 +184,8 @@
                     catch (GuacamoleClientException e) {
                         logger.info("WebSocket connection terminated: {}", e.getMessage());
                         logger.debug("WebSocket connection terminated due to client error.", e);
-                        closeConnection(session, e.getStatus());
+                        closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
+                                e.getWebSocketCode());
                     }
                     catch (GuacamoleConnectionClosedException e) {
                         logger.debug("Connection to guacd closed.", e);
@@ -168,7 +194,8 @@
                     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());
+                        closeConnection(session, e.getStatus().getGuacamoleStatusCode(),
+                                e.getWebSocketCode());
                     }
 
                 }
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java
index 986650e..a2e8b39 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java
@@ -58,17 +58,25 @@
     private final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class);
 
     /**
-     * Sends the given status on the given WebSocket connection and closes the
+     * Sends the given Guacamole and WebSocket numeric status
+     * on the given WebSocket connection and closes the
      * connection.
      *
-     * @param outbound The outbound WebSocket connection to close.
-     * @param guac_status The status to send.
+     * @param outbound
+     *     The outbound WebSocket connection to close.
+     *
+     * @param guacamoleStatusCode
+     *     The status to send.
+     *
+     * @param webSocketCode
+     *     The numeric WebSocket status code to send.
      */
-    public void closeConnection(WsOutbound outbound, GuacamoleStatus guac_status) {
+    private void closeConnection(WsOutbound outbound, int guacamoleStatusCode,
+            int webSocketCode) {
 
         try {
-            byte[] message = Integer.toString(guac_status.getGuacamoleStatusCode()).getBytes("UTF-8");
-            outbound.close(guac_status.getWebSocketCode(), ByteBuffer.wrap(message));
+            byte[] message = Integer.toString(guacamoleStatusCode).getBytes("UTF-8");
+            outbound.close(webSocketCode, ByteBuffer.wrap(message));
         }
         catch (IOException e) {
             logger.debug("Unable to close WebSocket tunnel.", e);
@@ -76,6 +84,24 @@
 
     }
 
+    /**
+     * Sends the given status on the given WebSocket connection
+     * and closes the connection.
+     *
+     * @param outbound
+     *     The outbound WebSocket connection to close.
+     *
+     * @param guacStatus
+     *     The status to send.
+     */
+    private void closeConnection(WsOutbound outbound,
+            GuacamoleStatus guacStatus) {
+
+        closeConnection(outbound, guacStatus.getGuacamoleStatusCode(),
+                guacStatus.getWebSocketCode());
+
+    }
+
     @Override
     protected String selectSubProtocol(List<String> subProtocols) {
 
@@ -142,7 +168,8 @@
                 catch (GuacamoleException e) {
                     logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage());
                     logger.debug("Error connecting WebSocket tunnel.", e);
-                    closeConnection(outbound, e.getStatus());
+                    closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
+                            e.getWebSocketCode());
                     return;
                 }
 
@@ -196,7 +223,8 @@
                             catch (GuacamoleClientException e) {
                                 logger.info("WebSocket connection terminated: {}", e.getMessage());
                                 logger.debug("WebSocket connection terminated due to client error.", e);
-                                closeConnection(outbound, e.getStatus());
+                                closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
+                                        e.getWebSocketCode());
                             }
                             catch (GuacamoleConnectionClosedException e) {
                                 logger.debug("Connection to guacd closed.", e);
@@ -205,7 +233,8 @@
                             catch (GuacamoleException e) {
                                 logger.error("Connection to guacd terminated abnormally: {}", e.getMessage());
                                 logger.debug("Internal error during connection to guacd.", e);
-                                closeConnection(outbound, e.getStatus());
+                                closeConnection(outbound, e.getStatus().getGuacamoleStatusCode(),
+                                        e.getWebSocketCode());
                             }
 
                         }