You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by vi...@apache.org on 2014/09/13 18:08:30 UTC

svn commit: r1624760 - in /tomcat/tc7.0.x/trunk: ./ java/org/apache/tomcat/websocket/LocalStrings.properties java/org/apache/tomcat/websocket/Util.java test/org/apache/tomcat/websocket/TestUtil.java

Author: violetagg
Date: Sat Sep 13 16:08:30 2014
New Revision: 1624760

URL: http://svn.apache.org/r1624760
Log:
Merged revisions 1604781, 1604788 from tomcat/trunk:
- Add support for parsing the extension header
- Strengthen WebSocket extension parameter validation

Modified:
    tomcat/tc7.0.x/trunk/   (props changed)
    tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
    tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/Util.java
    tomcat/tc7.0.x/trunk/test/org/apache/tomcat/websocket/TestUtil.java

Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
  Merged /tomcat/trunk:r1604781,1604788

Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties?rev=1624760&r1=1624759&r2=1624760&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties Sat Sep 13 16:08:30 2014
@@ -28,6 +28,7 @@ asyncChannelWrapperSecure.wrongStateWrit
 
 backgroundProcessManager.processFailed=A background process failed
 
+util.notToken=An illegal extension parameter was specified with name [{0}] and value [{1}]
 util.invalidMessageHandler=The message handler provided does not have an onMessage(Object) method
 util.invalidType=Unable to coerce value [{0}] to type [{1}]. That type is not supported.
 util.unknownDecoderType=The Decoder type [{0}] is not recognized

Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/Util.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/Util.java?rev=1624760&r1=1624759&r2=1624760&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/Util.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/Util.java Sat Sep 13 16:08:30 2014
@@ -43,6 +43,7 @@ import javax.websocket.Decoder.TextStrea
 import javax.websocket.DeploymentException;
 import javax.websocket.Encoder;
 import javax.websocket.EndpointConfig;
+import javax.websocket.Extension;
 import javax.websocket.MessageHandler;
 import javax.websocket.PongMessage;
 import javax.websocket.Session;
@@ -352,7 +353,6 @@ public class Util {
     }
 
 
-
     public static Set<MessageHandlerResult> getMessageHandlers(
             MessageHandler listener, EndpointConfig endpointConfig,
             Session session) {
@@ -448,6 +448,85 @@ public class Util {
     }
 
 
+    public static void parseExtensionHeader(List<Extension> extensions,
+            String header) {
+        // The relevant ABNF for the Sec-WebSocket-Extensions is as follows:
+        //      extension-list = 1#extension
+        //      extension = extension-token *( ";" extension-param )
+        //      extension-token = registered-token
+        //      registered-token = token
+        //      extension-param = token [ "=" (token | quoted-string) ]
+        //             ; When using the quoted-string syntax variant, the value
+        //             ; after quoted-string unescaping MUST conform to the
+        //             ; 'token' ABNF.
+        //
+        // The limiting of parameter values to tokens or "quoted tokens" makes
+        // the parsing of the header significantly simpler and allows a number
+        // of short-cuts to be taken.
+
+        // Step one, split the header into individual extensions using ',' as a
+        // separator
+        String unparsedExtensions[] = header.split(",");
+        for (String unparsedExtension : unparsedExtensions) {
+            // Step two, split the extension into the registered name and
+            // parameter/value pairs using ';' as a separator
+            String unparsedParameters[] = unparsedExtension.split(";");
+            WsExtension extension = new WsExtension(unparsedParameters[0].trim());
+
+            for (int i = 1; i < unparsedParameters.length; i++) {
+                int equalsPos = unparsedParameters[i].indexOf('=');
+                String name;
+                String value;
+                if (equalsPos == -1) {
+                    name = unparsedParameters[i].trim();
+                    value = null;
+                } else {
+                    name = unparsedParameters[i].substring(0, equalsPos).trim();
+                    value = unparsedParameters[i].substring(equalsPos + 1).trim();
+                    int len = value.length();
+                    if (len > 1) {
+                        if (value.charAt(0) == '\"' && value.charAt(len - 1) == '\"') {
+                            value = value.substring(1, value.length() - 1);
+                        }
+                    }
+                }
+                // Make sure value doesn't contain any of the delimiters since
+                // that would indicate something went wrong
+                if (containsDelims(name) || containsDelims(value)) {
+                    throw new IllegalArgumentException(sm.getString(
+                            "util.notToken", name, value));
+                }
+                if (value != null &&
+                        (value.indexOf(',') > -1 || value.indexOf(';') > -1 ||
+                        value.indexOf('\"') > -1 || value.indexOf('=') > -1)) {
+                    throw new IllegalArgumentException(sm.getString("", value));
+                }
+                extension.addParameter(new WsExtensionParameter(name, value));
+            }
+            extensions.add(extension);
+        }
+    }
+
+
+    private static boolean containsDelims(String input) {
+        if (input == null || input.length() == 0) {
+            return false;
+        }
+        for (char c : input.toCharArray()) {
+            switch (c) {
+                case ',':
+                case ';':
+                case '\"':
+                case '=':
+                    return true;
+                default:
+                    // NO_OP
+            }
+
+        }
+        return false;
+    }
+
     private static Method getOnMessageMethod(MessageHandler listener) {
         try {
             return listener.getClass().getMethod("onMessage", Object.class);
@@ -460,6 +539,7 @@ public class Util {
         }
     }
 
+
     public static class DecoderMatch {
 
         private final List<Class<? extends Decoder>> textDecoders =

Modified: tomcat/tc7.0.x/trunk/test/org/apache/tomcat/websocket/TestUtil.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/test/org/apache/tomcat/websocket/TestUtil.java?rev=1624760&r1=1624759&r2=1624760&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/test/org/apache/tomcat/websocket/TestUtil.java (original)
+++ tomcat/tc7.0.x/trunk/test/org/apache/tomcat/websocket/TestUtil.java Sat Sep 13 16:08:30 2014
@@ -16,11 +16,14 @@
  */
 package org.apache.tomcat.websocket;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import javax.websocket.EncodeException;
 import javax.websocket.Encoder;
 import javax.websocket.EndpointConfig;
+import javax.websocket.Extension;
+import javax.websocket.Extension.Parameter;
 import javax.websocket.MessageHandler;
 
 import org.junit.Assert;
@@ -370,4 +373,101 @@ public class TestUtil {
             return null;
         }
     }
+
+
+    @Test
+    public void testParseExtensionHeaderSimple01() {
+        doTestParseExtensionHeaderSimple("ext;a=1;b=2");
+    }
+
+    @Test
+    public void testParseExtensionHeaderSimple02() {
+        doTestParseExtensionHeaderSimple("ext;a=\"1\";b=2");
+    }
+
+    @Test
+    public void testParseExtensionHeaderSimple03() {
+        doTestParseExtensionHeaderSimple("ext;a=1;b=\"2\"");
+    }
+
+    @Test
+    public void testParseExtensionHeaderSimple04() {
+        doTestParseExtensionHeaderSimple(" ext ; a = 1 ; b = 2 ");
+    }
+
+    private void doTestParseExtensionHeaderSimple(String header) {
+        // Simple test
+        List<Extension> result = new ArrayList<Extension>();
+        Util.parseExtensionHeader(result, header);
+
+        Assert.assertEquals(1, result.size());
+
+        Extension ext = result.get(0);
+        Assert.assertEquals("ext", ext.getName());
+        List<Parameter> params = ext.getParameters();
+        Assert.assertEquals(2, params.size());
+        Parameter paramA = params.get(0);
+        Assert.assertEquals("a", paramA.getName());
+        Assert.assertEquals("1", paramA.getValue());
+        Parameter paramB = params.get(1);
+        Assert.assertEquals("b", paramB.getName());
+        Assert.assertEquals("2", paramB.getValue());
+    }
+
+
+    @Test
+    public void testParseExtensionHeaderMultiple01() {
+        doTestParseExtensionHeaderMultiple("ext;a=1;b=2,ext2;c;d=xyz,ext3");
+    }
+
+    @Test
+    public void testParseExtensionHeaderMultiple02() {
+        doTestParseExtensionHeaderMultiple(
+                " ext ; a = 1 ; b = 2 , ext2 ; c ; d = xyz , ext3 ");
+    }
+
+    private void doTestParseExtensionHeaderMultiple(String header) {
+        // Simple test
+        List<Extension> result = new ArrayList<Extension>();
+        Util.parseExtensionHeader(result, header);
+
+        Assert.assertEquals(3, result.size());
+
+        Extension ext = result.get(0);
+        Assert.assertEquals("ext", ext.getName());
+        List<Parameter> params = ext.getParameters();
+        Assert.assertEquals(2, params.size());
+        Parameter paramA = params.get(0);
+        Assert.assertEquals("a", paramA.getName());
+        Assert.assertEquals("1", paramA.getValue());
+        Parameter paramB = params.get(1);
+        Assert.assertEquals("b", paramB.getName());
+        Assert.assertEquals("2", paramB.getValue());
+
+        Extension ext2 = result.get(1);
+        Assert.assertEquals("ext2", ext2.getName());
+        List<Parameter> params2 = ext2.getParameters();
+        Assert.assertEquals(2, params2.size());
+        Parameter paramC = params2.get(0);
+        Assert.assertEquals("c", paramC.getName());
+        Assert.assertNull(paramC.getValue());
+        Parameter paramD = params2.get(1);
+        Assert.assertEquals("d", paramD.getName());
+        Assert.assertEquals("xyz", paramD.getValue());
+
+        Extension ext3 = result.get(2);
+        Assert.assertEquals("ext3", ext3.getName());
+        List<Parameter> params3 = ext3.getParameters();
+        Assert.assertEquals(0, params3.size());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseExtensionHeaderInvalid01() {
+        Util.parseExtensionHeader(new ArrayList<Extension>(), "ext;a=\"1;b=2");
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseExtensionHeaderInvalid02() {
+        Util.parseExtensionHeader(new ArrayList<Extension>(), "ext;a=1\";b=2");
+    }
 }



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