Author: markt
Date: Wed Dec 26 00:39:54 2012
New Revision: 1425800
URL: http://svn.apache.org/viewvc?rev=1425800&view=rev
Log:
Decode bytes to text sooner so UTF-8 encoding issues are caught early.
Modified:
tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java
Modified: tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties?rev=1425800&r1=1425799&r2=1425800&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
(original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties Wed
Dec 26 00:39:54 2012
@@ -27,4 +27,5 @@ wsFrame.invalidOpCode= A WebSocket frame
wsFrame.invalidUtf8=A WebSocket text frame was received that could not be
decoded to UTF-8 because it contained invalid byte sequences
wsFrame.noContinuation=A new message was started when a continuation frame was
expected
wsFrame.notMasked=The client frame was not masked but all client frames must
be masked
+wsFrame.textMessageTooBig=The decoded text message was too big to fit in the
output text message buffer and the endpoint does not support delivery of
partial messages
wsFrame.wrongRsv=The client frame set the reserved bits to [{0}] which was not
supported by this endpoint
\ No newline at end of file
Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java?rev=1425800&r1=1425799&r2=1425800&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java Wed Dec 26
00:39:54 2012
@@ -20,7 +20,9 @@ import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
import javax.servlet.ServletInputStream;
import javax.websocket.CloseReason;
@@ -52,7 +54,9 @@ public class WsFrame {
// Attributes of the current message
private final ByteBuffer messageBufferBinary;
private final CharBuffer messageBufferText;
- private final Utf8Decoder utf8Decoder = new Utf8Decoder();
+ private final CharsetDecoder utf8Decoder = new Utf8Decoder().
+ onMalformedInput(CodingErrorAction.REPORT).
+ onUnmappableCharacter(CodingErrorAction.REPORT);
private boolean continuationExpected = false;
private boolean textMessage = false;
@@ -146,6 +150,14 @@ public class WsFrame {
CloseCodes.PROTOCOL_ERROR,
sm.getString("wsFrame.controlFragmented")));
}
+ if (opCode != Constants.OPCODE_PING &&
+ opCode != Constants.OPCODE_PONG &&
+ opCode != Constants.OPCODE_CLOSE) {
+ throw new WsIOException(new CloseReason(
+ CloseCodes.PROTOCOL_ERROR,
+ sm.getString("wsFrame.invalidOpCode",
+ Integer.valueOf(opCode))));
+ }
} else {
if (continuationExpected) {
if (opCode != Constants.OPCODE_CONTINUATION) {
@@ -222,106 +234,155 @@ public class WsFrame {
private boolean processData() throws IOException {
checkRoomPayload();
if (isControl()) {
- appendPayloadToMessage(controlBuffer);
- if (writePos < frameStart + headerLength + payloadLength) {
- return false;
- }
- controlBuffer.flip();
- if (opCode == Constants.OPCODE_CLOSE) {
- String reason = null;
- int code = CloseCodes.NORMAL_CLOSURE.getCode();
- if (controlBuffer.remaining() > 1) {
- code = controlBuffer.getShort();
- if (controlBuffer.remaining() > 0) {
- reason = new String(controlBuffer.array(),
- controlBuffer.arrayOffset() +
controlBuffer.position(),
- controlBuffer.remaining(), "UTF8");
- }
- }
- wsSession.onClose(
- new CloseReason(Util.getCloseCode(code), reason));
- } else if (opCode == Constants.OPCODE_PING) {
- wsSession.getRemote().sendPong(controlBuffer);
- } else if (opCode == Constants.OPCODE_PONG) {
- MessageHandler.Basic<PongMessage> mhPong =
wsSession.getPongMessageHandler();
- if (mhPong != null) {
- mhPong.onMessage(new WsPongMessage(controlBuffer));
- }
- } else {
- throw new WsIOException(new CloseReason(
- CloseCodes.PROTOCOL_ERROR,
- sm.getString("wsFrame.invalidOpCode",
- Integer.valueOf(opCode))));
- }
- controlBuffer.clear();
- newFrame();
- return true;
+ return processDataControl();
}
- appendPayloadToMessage(messageBufferBinary);
-
- if (payloadWritten == payloadLength) {
- if (continuationExpected) {
- if (usePartial()) {
- messageBufferBinary.flip();
- sendMessage(false);
- messageBufferBinary.clear();
- }
- newFrame();
- return true;
- } else {
- messageBufferBinary.flip();
- sendMessage(true);
- newMessage();
- return true;
- }
- } else {
- if (usePartial()) {
- messageBufferBinary.flip();
- sendMessage(false);
- messageBufferBinary.clear();
- }
- return false;
- }
- }
+ // Unmask data
+ appendPayloadToMessage(messageBufferBinary);
- @SuppressWarnings("unchecked")
- private void sendMessage(boolean last) throws IOException {
if (textMessage) {
- MessageHandler mh = wsSession.getTextMessageHandler();
- if (mh != null) {
+ // Convert the bytes to text as early as possible to catch any
+ // conversion issues
+ messageBufferBinary.flip();
+ boolean last = false;
+ while (true) {
CoderResult cr = utf8Decoder.decode(
messageBufferBinary, messageBufferText, last);
if (cr.isError()) {
throw new WsIOException(new CloseReason(
CloseCodes.NOT_CONSISTENT,
sm.getString("wsFrame.invalidUtf8")));
- }
- messageBufferText.flip();
- if (mh instanceof MessageHandler.Async<?>) {
- ((MessageHandler.Async<String>) mh).onMessage(
- messageBufferText.toString(), last);
+ } else if (cr.isOverflow()) {
+ if (usePartial()) {
+ messageBufferText.flip();
+ sendMessageText(false);
+ messageBufferText.clear();
+ } else {
+ throw new WsIOException(new CloseReason(
+ CloseCodes.TOO_BIG,
+ sm.getString("wsFrame.textMessageTooBig")));
+ }
+ } else if (cr.isUnderflow() && !last) {
+ // Need more data
+ messageBufferBinary.compact();
+ if (payloadWritten == payloadLength) {
+ if (continuationExpected) {
+ newFrame();
+ return true;
+ } else {
+ messageBufferBinary.flip();
+ last = true;
+ }
+ } else {
+ return false;
+ }
} else {
- ((MessageHandler.Basic<String>) mh).onMessage(
- messageBufferText.toString());
+ // End of input
+ messageBufferText.flip();
+ sendMessageText(true);
+ messageBufferText.clear();
+ newMessage();
+ return true;
}
- messageBufferText.clear();
}
} else {
- MessageHandler mh = wsSession.getBinaryMessageHandler();
- if (mh != null) {
- if (mh instanceof MessageHandler.Async<?>) {
- ((MessageHandler.Async<ByteBuffer>) mh).onMessage(
- messageBufferBinary, last);
+ if (payloadWritten == payloadLength) {
+ if (continuationExpected) {
+ if (usePartial()) {
+ messageBufferBinary.flip();
+ sendMessageBinary(false);
+ messageBufferBinary.clear();
+ }
+ newFrame();
+ return true;
} else {
- ((MessageHandler.Basic<ByteBuffer>) mh).onMessage(
- messageBufferBinary);
+ messageBufferBinary.flip();
+ sendMessageBinary(true);
+ newMessage();
+ return true;
}
+ } else {
+ if (usePartial()) {
+ messageBufferBinary.flip();
+ sendMessageBinary(false);
+ messageBufferBinary.clear();
+ }
+ return false;
}
}
}
+ private boolean processDataControl() throws IOException {
+ appendPayloadToMessage(controlBuffer);
+ if (writePos < frameStart + headerLength + payloadLength) {
+ return false;
+ }
+ controlBuffer.flip();
+ if (opCode == Constants.OPCODE_CLOSE) {
+ String reason = null;
+ int code = CloseCodes.NORMAL_CLOSURE.getCode();
+ if (controlBuffer.remaining() > 1) {
+ code = controlBuffer.getShort();
+ if (controlBuffer.remaining() > 0) {
+ reason = new String(controlBuffer.array(),
+ controlBuffer.arrayOffset() +
controlBuffer.position(),
+ controlBuffer.remaining(), "UTF8");
+ }
+ }
+ wsSession.onClose(
+ new CloseReason(Util.getCloseCode(code), reason));
+ } else if (opCode == Constants.OPCODE_PING) {
+ wsSession.getRemote().sendPong(controlBuffer);
+ } else if (opCode == Constants.OPCODE_PONG) {
+ MessageHandler.Basic<PongMessage> mhPong =
+ wsSession.getPongMessageHandler();
+ if (mhPong != null) {
+ mhPong.onMessage(new WsPongMessage(controlBuffer));
+ }
+ } else {
+ // Should have caught this earlier but just in case...
+ throw new WsIOException(new CloseReason(
+ CloseCodes.PROTOCOL_ERROR,
+ sm.getString("wsFrame.invalidOpCode",
+ Integer.valueOf(opCode))));
+ }
+ controlBuffer.clear();
+ newFrame();
+ return true;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private void sendMessageText(boolean last) {
+ MessageHandler mh = wsSession.getTextMessageHandler();
+ if (mh != null) {
+ if (mh instanceof MessageHandler.Async<?>) {
+ ((MessageHandler.Async<String>) mh).onMessage(
+ messageBufferText.toString(), last);
+ } else {
+ ((MessageHandler.Basic<String>) mh).onMessage(
+ messageBufferText.toString());
+ }
+ messageBufferText.clear();
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private void sendMessageBinary(boolean last) {
+ MessageHandler mh = wsSession.getBinaryMessageHandler();
+ if (mh != null) {
+ if (mh instanceof MessageHandler.Async<?>) {
+ ((MessageHandler.Async<ByteBuffer>) mh).onMessage(
+ messageBufferBinary, last);
+ } else {
+ ((MessageHandler.Basic<ByteBuffer>) mh).onMessage(
+ messageBufferBinary);
+ }
+ }
+ }
+
private void newMessage() {
messageBufferBinary.clear();
messageBufferText.clear();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]