#518: Send roughly-appropriate codes and close the WebSocket connection when errors are encountered.

This commit is contained in:
Michael Jumper
2014-02-03 11:36:27 -08:00
parent fab6b9f035
commit f57d2c167e
2 changed files with 73 additions and 34 deletions

View File

@@ -31,6 +31,9 @@ import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocket.Connection; import org.eclipse.jetty.websocket.WebSocket.Connection;
import org.eclipse.jetty.websocket.WebSocketServlet; import org.eclipse.jetty.websocket.WebSocketServlet;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -44,7 +47,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
/** /**
* Logger for this class. * Logger for this class.
*/ */
private static Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class); private static final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class);
/** /**
* The default, minimum buffer size for instructions. * The default, minimum buffer size for instructions.
@@ -96,25 +99,46 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
char[] readMessage; char[] readMessage;
try { try {
while ((readMessage = reader.read()) != null) {
// Buffer message try {
buffer.append(readMessage); 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) {
connection.sendMessage(buffer.toString());
buffer.setLength(0);
}
// Flush if we expect to wait or buffer is getting full
if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
connection.sendMessage(buffer.toString());
buffer.setLength(0);
} }
} }
// Catch any thrown guacamole exception and attempt
// to pass within the WebSocket connection, logging
// each error appropriately.
catch (GuacamoleSecurityException e) {
logger.warn("Authorization failed.", e);
connection.close(1008, null); // Policy violation
}
catch (GuacamoleResourceNotFoundException e) {
logger.debug("Resource not found.", e);
connection.close(1002, null); // Protocol error
}
catch (GuacamoleClientException e) {
logger.warn("Error in client request.", e);
connection.close(1002, null); // Protocol error
}
catch (GuacamoleException e) {
logger.error("Server error in tunnel", e);
connection.close(1011, null); // Server error
}
} }
catch (IOException e) { catch (IOException e) {
logger.debug("Tunnel read failed due to I/O error.", e); logger.debug("Tunnel read failed due to I/O error.", e);
} }
catch (GuacamoleException e) {
logger.debug("Tunnel read failed.", e);
}
} }

View File

@@ -27,6 +27,7 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.websocket.Constants;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.io.GuacamoleReader; import org.glyptodon.guacamole.io.GuacamoleReader;
import org.glyptodon.guacamole.io.GuacamoleWriter; import org.glyptodon.guacamole.io.GuacamoleWriter;
@@ -34,6 +35,9 @@ import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.apache.catalina.websocket.StreamInbound; import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet; import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound; import org.apache.catalina.websocket.WsOutbound;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -52,7 +56,7 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
/** /**
* Logger for this class. * Logger for this class.
*/ */
private Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class); private final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class);
@Override @Override
public StreamInbound createWebSocketInbound(String protocol, HttpServletRequest request) { public StreamInbound createWebSocketInbound(String protocol, HttpServletRequest request) {
@@ -101,40 +105,51 @@ public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet {
@Override @Override
public void run() { public void run() {
CharBuffer charBuffer = CharBuffer.allocate(BUFFER_SIZE);
StringBuilder buffer = new StringBuilder(BUFFER_SIZE); StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
GuacamoleReader reader = tunnel.acquireReader(); GuacamoleReader reader = tunnel.acquireReader();
char[] readMessage; char[] readMessage;
try { try {
while ((readMessage = reader.read()) != null) {
// Buffer message // Attempt to read
buffer.append(readMessage); try {
while ((readMessage = reader.read()) != null) {
// Flush if we expect to wait or buffer is getting full // Buffer message
if (!reader.available() || buffer.length() >= BUFFER_SIZE) { buffer.append(readMessage);
// Reallocate buffer if necessary // Flush if we expect to wait or buffer is getting full
if (buffer.length() > charBuffer.length()) if (!reader.available() || buffer.length() >= BUFFER_SIZE) {
charBuffer = CharBuffer.allocate(buffer.length()); outbound.writeTextMessage(CharBuffer.wrap(buffer));
else buffer.setLength(0);
charBuffer.clear(); }
charBuffer.put(buffer.toString().toCharArray());
charBuffer.flip();
outbound.writeTextMessage(charBuffer);
buffer.setLength(0);
} }
} }
// Catch any thrown guacamole exception and attempt
// to pass within the WebSocket connection, logging
// each error appropriately.
catch (GuacamoleSecurityException e) {
logger.warn("Authorization failed.", e);
outbound.close(Constants.STATUS_POLICY_VIOLATION, null);
}
catch (GuacamoleResourceNotFoundException e) {
logger.debug("Resource not found.", e);
outbound.close(Constants.STATUS_PROTOCOL_ERROR, null);
}
catch (GuacamoleClientException e) {
logger.warn("Error in client request.", e);
outbound.close(Constants.STATUS_PROTOCOL_ERROR, null);
}
catch (GuacamoleException e) {
logger.error("Server error in tunnel", e);
outbound.close(Constants.STATUS_UNEXPECTED_CONDITION, null);
}
} }
catch (IOException e) { catch (IOException e) {
logger.debug("Tunnel read failed due to I/O error.", e); logger.debug("I/O error prevents further reads.", e);
}
catch (GuacamoleException e) {
logger.debug("Tunnel read failed.", e);
} }
} }