You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2014/06/24 12:40:56 UTC

svn commit: r1605054 - in /tomcat/trunk/java/org/apache/tomcat/websocket: PerMessageDeflate.java Transformation.java TransformationResult.java WsFrameBase.java

Author: markt
Date: Tue Jun 24 10:40:56 2014
New Revision: 1605054

URL: http://svn.apache.org/r1605054
Log:
Refactor transformations to enable the handing of overflow (the previous code ignored this possibility).

Added:
    tomcat/trunk/java/org/apache/tomcat/websocket/TransformationResult.java   (with props)
Modified:
    tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java
    tomcat/trunk/java/org/apache/tomcat/websocket/Transformation.java
    tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java?rev=1605054&r1=1605053&r2=1605054&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java Tue Jun 24 10:40:56 2014
@@ -89,41 +89,51 @@ public class PerMessageDeflate implement
     }
 
     @Override
-    public boolean getMoreData(byte opCode, int rsv, ByteBuffer dest) throws IOException {
+    public TransformationResult getMoreData(byte opCode, int rsv, ByteBuffer dest) throws IOException {
 
-        // Control frames are never compressed
+        // Control frames are never compressed. Pass control frames and
+        // uncompressed frames straight through.
         if (Util.isControl(opCode) || (rsv & RSV_BITMASK) == 0) {
             return next.getMoreData(opCode, rsv, dest);
         }
 
-        boolean endOfInputFrame = false;
+        int written;
+        boolean usedEomBytes = false;
 
-        if (inflator.needsInput()) {
-            readBuffer.clear();
-            endOfInputFrame = next.getMoreData(opCode, (rsv ^ RSV_BITMASK), readBuffer);
-            inflator.setInput(readBuffer.array(), readBuffer.arrayOffset(), readBuffer.position());
-        }
-
-        int written = 0;
-        try {
-            written = inflator.inflate(dest.array(), dest.arrayOffset() + dest.position(), dest.remaining());
-            if (endOfInputFrame && !inflator.finished()) {
-                inflator.setInput(EOM_BYTES);
-                inflator.inflate(dest.array(), dest.arrayOffset() + dest.position(), dest.remaining());
+        while (dest.remaining() > 0) {
+            // Space available in destination. Try and fill it.
+            try {
+                written = inflator.inflate(
+                        dest.array(), dest.arrayOffset() + dest.position(), dest.remaining());
+            } catch (DataFormatException e) {
+                throw new IOException(sm.getString("perMessageDeflate.deflateFailed"), e);
             }
-        } catch (DataFormatException e) {
-            throw new IOException(sm.getString("perMessageDeflate.deflateFailed"), e);
-        }
-        dest.position(dest.position() + written);
-
+            dest.position(dest.position() + written);
 
-        if (endOfInputFrame && !clientContextTakeover) {
-            inflator.reset();
+            if (inflator.needsInput() && !usedEomBytes ) {
+                if (dest.hasRemaining()) {
+                    readBuffer.clear();
+                    TransformationResult nextResult =
+                            next.getMoreData(opCode, (rsv ^ RSV_BITMASK), readBuffer);
+                    inflator.setInput(
+                            readBuffer.array(), readBuffer.arrayOffset(), readBuffer.position());
+                    if (TransformationResult.UNDERFLOW.equals(nextResult)) {
+                        return nextResult;
+                    } else if (TransformationResult.END_OF_FRAME.equals(nextResult) &&
+                            readBuffer.position() == 0) {
+                        inflator.setInput(EOM_BYTES);
+                        usedEomBytes = true;
+                    }
+                }
+            } else if (written == 0) {
+                return TransformationResult.END_OF_FRAME;
+            }
         }
 
-        return endOfInputFrame;
+        return TransformationResult.OVERFLOW;
     }
 
+
     @Override
     public boolean validateRsv(int rsv, byte opCode) {
         if (Util.isControl(opCode)) {

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/Transformation.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/Transformation.java?rev=1605054&r1=1605053&r2=1605054&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/Transformation.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/Transformation.java Tue Jun 24 10:40:56 2014
@@ -36,11 +36,8 @@ public interface Transformation {
      * @param rsv       The reserved bits for the frame currently being
      *                      processed
      * @param dest      The buffer in which the data is to be written
-     *
-     * @return <code>true</code> if the data source has been fully read
-     *         otherwise <code>false</code>
      */
-    boolean getMoreData(byte opCode, int rsv, ByteBuffer dest) throws IOException;
+    TransformationResult getMoreData(byte opCode, int rsv, ByteBuffer dest) throws IOException;
 
     /**
      * Validates the RSV and opcode combination (assumed to have been extracted

Added: tomcat/trunk/java/org/apache/tomcat/websocket/TransformationResult.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/TransformationResult.java?rev=1605054&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/TransformationResult.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/TransformationResult.java Tue Jun 24 10:40:56 2014
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+public enum TransformationResult {
+    /**
+     * The end of the available data was reached before the WebSocket frame was
+     * completely read.
+     */
+    UNDERFLOW,
+
+    /**
+     * The provided destination buffer was filled before all of the available
+     * data from the WebSocket frame could be processed.
+     */
+    OVERFLOW,
+
+    /**
+     * The end of the WebSocket frame was reached and all the data from that
+     * frame processed into the provided destination buffer.
+     */
+    END_OF_FRAME
+}

Propchange: tomcat/trunk/java/org/apache/tomcat/websocket/TransformationResult.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java?rev=1605054&r1=1605053&r2=1605054&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsFrameBase.java Tue Jun 24 10:40:56 2014
@@ -300,9 +300,13 @@ public abstract class WsFrameBase {
 
 
     private boolean processDataControl() throws IOException {
-        if (!transformation.getMoreData(opCode, rsv, controlBufferBinary)) {
+        TransformationResult tr = transformation.getMoreData(opCode, rsv, messageBufferBinary);
+        if (TransformationResult.UNDERFLOW.equals(tr)) {
             return false;
         }
+        // Control messages have fixed message size so
+        // TransformationResult.OVERFLOW is not possible here
+
         controlBufferBinary.flip();
         if (opCode == Constants.OPCODE_CLOSE) {
             open = false;
@@ -398,7 +402,8 @@ public abstract class WsFrameBase {
 
     private boolean processDataText() throws IOException {
         // Copy the available data to the buffer
-        while (!transformation.getMoreData(opCode, rsv, messageBufferBinary)) {
+        TransformationResult tr = transformation.getMoreData(opCode, rsv, messageBufferBinary);
+        while (!TransformationResult.END_OF_FRAME.equals(tr)) {
             // Frame not complete - we ran out of something
             // Convert bytes to UTF-8
             messageBufferBinary.flip();
@@ -421,21 +426,24 @@ public abstract class WsFrameBase {
                                 sm.getString("wsFrame.textMessageTooBig")));
                     }
                 } else if (cr.isUnderflow()) {
-                    // Need more input
                     // Compact what we have to create as much space as possible
                     messageBufferBinary.compact();
 
+                    // Need more input
                     // What did we run out of?
-                    if (readPos == writePos) {
-                        // Ran out of input data - get some more
-                        return false;
-                    } else {
+                    if (TransformationResult.OVERFLOW.equals(tr)) {
                         // Ran out of message buffer - exit inner loop and
                         // refill
                         break;
+                    } else {
+                        // TransformationResult.UNDERFLOW
+                        // Ran out of input data - get some more
+                        return false;
                     }
                 }
             }
+            // Read more input data
+            tr = transformation.getMoreData(opCode, rsv, messageBufferBinary);
         }
 
         messageBufferBinary.flip();
@@ -493,29 +501,32 @@ public abstract class WsFrameBase {
 
     private boolean processDataBinary() throws IOException {
         // Copy the available data to the buffer
-        while (!transformation.getMoreData(opCode, rsv, messageBufferBinary)) {
+        TransformationResult tr = transformation.getMoreData(opCode, rsv, messageBufferBinary);
+        while (!TransformationResult.END_OF_FRAME.equals(tr)) {
             // Frame not complete - what did we run out of?
-            if (readPos == writePos) {
+            if (TransformationResult.UNDERFLOW.equals(tr)) {
                 // Ran out of input data - get some more
                 return false;
-            } else {
-                // Ran out of message buffer - flush it
-                if (!usePartial()) {
-                    CloseReason cr = new CloseReason(CloseCodes.TOO_BIG,
-                            sm.getString("wsFrame.bufferTooSmall",
-                                    Integer.valueOf(
-                                            messageBufferBinary.capacity()),
-                                    Long.valueOf(payloadLength)));
-                    throw new WsIOException(cr);
-                }
-                messageBufferBinary.flip();
-                ByteBuffer copy =
-                        ByteBuffer.allocate(messageBufferBinary.limit());
-                copy.put(messageBufferBinary);
-                copy.flip();
-                sendMessageBinary(copy, false);
-                messageBufferBinary.clear();
             }
+
+            // Ran out of message buffer - flush it
+            if (!usePartial()) {
+                CloseReason cr = new CloseReason(CloseCodes.TOO_BIG,
+                        sm.getString("wsFrame.bufferTooSmall",
+                                Integer.valueOf(
+                                        messageBufferBinary.capacity()),
+                                Long.valueOf(payloadLength)));
+                throw new WsIOException(cr);
+            }
+            messageBufferBinary.flip();
+            ByteBuffer copy =
+                    ByteBuffer.allocate(messageBufferBinary.limit());
+            copy.put(messageBufferBinary);
+            copy.flip();
+            sendMessageBinary(copy, false);
+            messageBufferBinary.clear();
+            // Read more data
+            tr = transformation.getMoreData(opCode, rsv, messageBufferBinary);
         }
 
         // Frame is fully received
@@ -724,7 +735,7 @@ public abstract class WsFrameBase {
     private final class NoopTransformation extends TerminalTransformation {
 
         @Override
-        public boolean getMoreData(byte opCode, int rsv, ByteBuffer dest) {
+        public TransformationResult getMoreData(byte opCode, int rsv, ByteBuffer dest) {
             // opCode is ignored as the transformation is the same for all
             // opCodes
             // rsv is ignored as it known to be zero at this point
@@ -735,7 +746,15 @@ public abstract class WsFrameBase {
             dest.put(inputBuffer, readPos, (int) toWrite);
             readPos += toWrite;
             payloadWritten += toWrite;
-            return (payloadWritten == payloadLength);
+
+            if (payloadWritten == payloadLength) {
+                return TransformationResult.END_OF_FRAME;
+            } else if (readPos == writePos) {
+                return TransformationResult.UNDERFLOW;
+            } else {
+                // !dest.hasRemaining()
+                return TransformationResult.OVERFLOW;
+            }
         }
     }
 
@@ -747,7 +766,7 @@ public abstract class WsFrameBase {
     private final class UnmaskTransformation extends TerminalTransformation {
 
         @Override
-        public boolean getMoreData(byte opCode, int rsv, ByteBuffer dest) {
+        public TransformationResult getMoreData(byte opCode, int rsv, ByteBuffer dest) {
             // opCode is ignored as the transformation is the same for all
             // opCodes
             // rsv is ignored as it known to be zero at this point
@@ -762,7 +781,14 @@ public abstract class WsFrameBase {
                 payloadWritten++;
                 dest.put(b);
             }
-            return (payloadWritten == payloadLength);
+            if (payloadWritten == payloadLength) {
+                return TransformationResult.END_OF_FRAME;
+            } else if (readPos == writePos) {
+                return TransformationResult.UNDERFLOW;
+            } else {
+                // !dest.hasRemaining()
+                return TransformationResult.OVERFLOW;
+            }
         }
     }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org