You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by kp...@apache.org on 2013/10/08 18:01:28 UTC
svn commit: r1530325 - in
/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard:
Client.java DrawboardEndpoint.java Room.java
wsmessages/CloseWebsocketMessage.java
Author: kpreisser
Date: Tue Oct 8 16:01:28 2013
New Revision: 1530325
URL: http://svn.apache.org/r1530325
Log:
Improve Drawboard Example:
- Check if buffered messages exceed a specific size, to avoid a DoS.
- Combine buffered string message to reduce TCP overhead when sending them.
Added:
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java (with props)
Modified:
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java
tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java?rev=1530325&r1=1530324&r2=1530325&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java Tue Oct 8 16:01:28 2013
@@ -16,22 +16,28 @@
*/
package websocket.drawboard;
+import java.io.IOException;
import java.util.LinkedList;
-import javax.websocket.RemoteEndpoint;
+import javax.websocket.CloseReason;
+import javax.websocket.CloseReason.CloseCodes;
+import javax.websocket.RemoteEndpoint.Async;
import javax.websocket.SendHandler;
import javax.websocket.SendResult;
+import javax.websocket.Session;
import websocket.drawboard.wsmessages.AbstractWebsocketMessage;
import websocket.drawboard.wsmessages.BinaryWebsocketMessage;
+import websocket.drawboard.wsmessages.CloseWebsocketMessage;
import websocket.drawboard.wsmessages.StringWebsocketMessage;
/**
- * Represents a client with methods to send messages.
+ * Represents a client with methods to send messages asynchronously.
*/
public class Client {
- private final RemoteEndpoint.Async async;
+ private final Session session;
+ private final Async async;
/**
* Contains the messages wich are buffered until the previous
@@ -43,11 +49,30 @@ public class Client {
* If this client is currently sending a messages asynchronously.
*/
private volatile boolean isSendingMessage = false;
+ /**
+ * If this client is closing. If <code>true</code>, new messages to
+ * send will be ignored.
+ */
+ private volatile boolean isClosing = false;
+ /**
+ * The length of all current buffered messages, to avoid iterating
+ * over a linked list.
+ */
+ private volatile long messagesToSendLength = 0;
- public Client(RemoteEndpoint.Async async) {
- this.async = async;
+ public Client(Session session) {
+ this.session = session;
+ this.async = session.getAsyncRemote();
}
+ /**
+ * Asynchronously closes the Websocket session. This will wait until all
+ * remaining messages have been sent to the Client and then close
+ * the Websocket session.
+ */
+ public void close() {
+ sendMessage(new CloseWebsocketMessage());
+ }
/**
* Sends the given message asynchronously to the client.
@@ -59,23 +84,71 @@ public class Client {
*/
public void sendMessage(AbstractWebsocketMessage msg) {
synchronized (messagesToSend) {
- if (isSendingMessage) {
- // TODO: Check if the buffered messages exceed
- // a specific amount - in that case, disconnect the client
- // to prevent DoS.
-
- // TODO: Check if the last message is a
- // String message - in that case we should concatenate them
- // to reduce TCP overhead (using ";" as separator).
-
- messagesToSend.add(msg);
- } else {
- isSendingMessage = true;
- internalSendMessageAsync(msg);
+ if (!isClosing) {
+ // Check if we have a Close message
+ if (msg instanceof CloseWebsocketMessage) {
+ isClosing = true;
+ }
+
+ if (isSendingMessage) {
+ // Check if the buffered messages exceed
+ // a specific amount - in that case, disconnect the client
+ // to prevent DoS.
+ // In this case we check if there are >= 1000 messages
+ // or length(of all messages) >= 1000000 bytes.
+ if (messagesToSend.size() >= 1000
+ || messagesToSendLength >= 1000000) {
+ isClosing = true;
+
+ // Discard the new message and close the session immediately.
+ CloseReason cr = new CloseReason(
+ CloseCodes.VIOLATED_POLICY,
+ "Send Buffer exceeded");
+ try {
+ session.close(cr);
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ } else {
+
+ // Check if the last message and the new message are
+ // String messages - in that case we concatenate them
+ // to reduce TCP overhead (using ";" as separator).
+ if (msg instanceof StringWebsocketMessage
+ && !messagesToSend.isEmpty()
+ && messagesToSend.getLast()
+ instanceof StringWebsocketMessage) {
+
+ StringWebsocketMessage ms =
+ (StringWebsocketMessage) messagesToSend.removeLast();
+ messagesToSendLength -= calculateMessageLength(ms);
+
+ String concatenated = ms.getString() + ";" +
+ ((StringWebsocketMessage) msg).getString();
+ msg = new StringWebsocketMessage(concatenated);
+ }
+
+ messagesToSend.add(msg);
+ messagesToSendLength += calculateMessageLength(msg);
+ }
+ } else {
+ isSendingMessage = true;
+ internalSendMessageAsync(msg);
+ }
}
+ }
+ }
+ private long calculateMessageLength(AbstractWebsocketMessage msg) {
+ if (msg instanceof BinaryWebsocketMessage) {
+ return ((BinaryWebsocketMessage) msg).getBytes().capacity();
+ } else if (msg instanceof StringWebsocketMessage) {
+ return ((StringWebsocketMessage) msg).getString().length() * 2;
}
+
+ return 0;
}
/**
@@ -91,8 +164,12 @@ public class Client {
} else if (msg instanceof BinaryWebsocketMessage) {
BinaryWebsocketMessage bMsg = (BinaryWebsocketMessage) msg;
async.sendBinary(bMsg.getBytes(), sendHandler);
+
+ } else if (msg instanceof CloseWebsocketMessage) {
+ // Close the session.
+ session.close();
}
- } catch (IllegalStateException ex) {
+ } catch (IllegalStateException|IOException ex) {
// Trying to write to the client when the session has
// already been closed.
// Ignore
@@ -111,6 +188,8 @@ public class Client {
if (!messagesToSend.isEmpty()) {
AbstractWebsocketMessage msg = messagesToSend.remove();
+ messagesToSendLength -= calculateMessageLength(msg);
+
internalSendMessageAsync(msg);
} else {
Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java?rev=1530325&r1=1530324&r2=1530325&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java Tue Oct 8 16:01:28 2013
@@ -70,7 +70,7 @@ public final class DrawboardEndpoint ext
// Set maximum messages size to 10.000 bytes.
session.setMaxTextMessageBufferSize(10000);
session.addMessageHandler(stringHandler);
- final Client client = new Client(session.getAsyncRemote());
+ final Client client = new Client(session);
room.invoke(new Runnable() {
@Override
Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java?rev=1530325&r1=1530324&r2=1530325&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java Tue Oct 8 16:01:28 2013
@@ -389,8 +389,10 @@ public final class Room {
* the client disconnects.
*/
public void removeFromRoom() {
- room.internalRemovePlayer(this);
- room = null;
+ if (room != null) {
+ room.internalRemovePlayer(this);
+ room = null;
+ }
}
Added: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java?rev=1530325&view=auto
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java (added)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java Tue Oct 8 16:01:28 2013
@@ -0,0 +1,8 @@
+package websocket.drawboard.wsmessages;
+
+/**
+ * Represents a "close" message that closes the session.
+ */
+public class CloseWebsocketMessage extends AbstractWebsocketMessage {
+
+}
Propchange: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org