GUAC-906: Implement GuacamoleConnectionClosedException. Throw when read/write fails due to closure.

This commit is contained in:
Michael Jumper
2014-10-26 15:14:35 -07:00
parent 721d9125ab
commit b83c83c324
8 changed files with 140 additions and 10 deletions

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole;
import org.glyptodon.guacamole.protocol.GuacamoleStatus;
/**
* An exception which is thrown when an operation cannot be performed because
* its corresponding connection is closed.
*
* @author Michael Jumper
*/
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;
}
}

View File

@@ -25,9 +25,11 @@ package org.glyptodon.guacamole.io;
import java.io.IOException;
import java.io.Reader;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Deque;
import java.util.LinkedList;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleServerException;
import org.glyptodon.guacamole.GuacamoleUpstreamTimeoutException;
@@ -182,6 +184,9 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
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);
}

View File

@@ -25,8 +25,12 @@ package org.glyptodon.guacamole.io;
import java.io.IOException;
import java.io.Writer;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleServerException;
import org.glyptodon.guacamole.GuacamoleUpstreamTimeoutException;
import org.glyptodon.guacamole.protocol.GuacamoleInstruction;
/**
@@ -58,6 +62,12 @@ public class WriterGuacamoleWriter implements GuacamoleWriter {
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);
}

View File

@@ -35,9 +35,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
import org.glyptodon.guacamole.GuacamoleServerException;
import org.glyptodon.guacamole.io.GuacamoleReader;
import org.glyptodon.guacamole.io.GuacamoleWriter;
@@ -287,7 +287,7 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet {
// data yet.
char[] message = reader.read();
if (message == null)
throw new GuacamoleResourceNotFoundException("Tunnel reached end of stream.");
throw new GuacamoleConnectionClosedException("Tunnel reached end of stream.");
// For all messages, until another stream is ready (we send at least one message)
do {
@@ -317,20 +317,28 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet {
response.flushBuffer();
}
// Send end-of-stream marker if connection is closed
catch (GuacamoleConnectionClosedException e) {
out.write("0.;");
out.flush();
response.flushBuffer();
}
catch (GuacamoleException e) {
// Detach and close
session.detachTunnel(tunnel);
tunnel.close();
throw e;
}
// Always close output stream
finally {
out.close();
}
}
catch (GuacamoleException e) {
// Detach and close
session.detachTunnel(tunnel);
tunnel.close();
throw e;
}
catch (IOException e) {
// Log typically frequent I/O error if desired
@@ -411,6 +419,9 @@ public abstract class GuacamoleHTTPTunnelServlet extends HttpServlet {
}
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
}
catch (IOException e) {
// Detach and close

View File

@@ -38,6 +38,7 @@ import org.glyptodon.guacamole.io.GuacamoleReader;
import org.glyptodon.guacamole.io.GuacamoleWriter;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.protocol.GuacamoleStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -178,6 +179,10 @@ public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
logger.warn("Client request rejected: {}", e.getMessage());
closeConnection(session, e.getStatus());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
closeConnection(session, GuacamoleStatus.SUCCESS);
}
catch (GuacamoleException e) {
logger.error("Internal server error.", e);
closeConnection(session, e.getStatus());
@@ -205,6 +210,9 @@ public abstract class GuacamoleWebSocketTunnelEndpoint extends Endpoint {
// Write received message
writer.write(message.toCharArray());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
}
catch (GuacamoleException e) {
logger.debug("Tunnel write failed.", e);
}

View File

@@ -32,6 +32,7 @@ import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocket.Connection;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.protocol.GuacamoleStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -93,6 +94,9 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
try {
writer.write(string.toCharArray());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
}
catch (GuacamoleException e) {
logger.debug("Tunnel write failed.", e);
}
@@ -148,6 +152,10 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
logger.warn("Client request rejected: {}", e.getMessage());
closeConnection(connection, e.getStatus());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
closeConnection(connection, GuacamoleStatus.SUCCESS);
}
catch (GuacamoleException e) {
logger.error("Internal server error.", e);
closeConnection(connection, e.getStatus());

View File

@@ -28,6 +28,7 @@ import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.io.GuacamoleReader;
import org.glyptodon.guacamole.io.GuacamoleWriter;
@@ -164,6 +165,10 @@ public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListe
logger.warn("Client request rejected: {}", e.getMessage());
closeConnection(session, e.getStatus());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
closeConnection(session, GuacamoleStatus.SUCCESS);
}
catch (GuacamoleException e) {
logger.error("Internal server error.", e);
closeConnection(session, e.getStatus());
@@ -191,6 +196,9 @@ public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListe
// Write received message
writer.write(message.toCharArray());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
}
catch (GuacamoleException e) {
logger.debug("Tunnel write failed.", e);
}

View File

@@ -37,6 +37,7 @@ import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleConnectionClosedException;
import org.glyptodon.guacamole.protocol.GuacamoleStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -120,6 +121,9 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
writer.write(buffer, 0, num_read);
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
}
catch (GuacamoleException e) {
logger.debug("Tunnel write failed.", e);
}
@@ -180,6 +184,10 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
logger.warn("Client request rejected: {}", e.getMessage());
closeConnection(outbound, e.getStatus());
}
catch (GuacamoleConnectionClosedException e) {
logger.debug("Connection closed.", e);
closeConnection(outbound, GuacamoleStatus.SUCCESS);
}
catch (GuacamoleException e) {
logger.error("Internal server error.", e);
closeConnection(outbound, e.getStatus());