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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to