Author: violetagg Date: Tue Sep 16 20:41:29 2014 New Revision: 1625385 URL: http://svn.apache.org/r1625385 Log: Merged revision 1605454 from tomcat/trunk: Handle preference selection for multiple extension headers with the same name Tighten up parameter validation for permessage-deflate
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/PerMessageDeflate.java tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Propchange: tomcat/tc7.0.x/trunk/ ------------------------------------------------------------------------------ Merged /tomcat/trunk:r1605454 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=1625385&r1=1625384&r2=1625385&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 Tue Sep 16 20:41:29 2014 @@ -29,6 +29,7 @@ asyncChannelWrapperSecure.wrongStateWrit backgroundProcessManager.processFailed=A background process failed perMessageDeflate.deflateFailed=Failed to decompress a compressed WebSocket frame +perMessageDeflate.duplicateParameter=Duplicate definition of the [{0}] extension parameter perMessageDeflate.invalidWindowSize=An invalid windows of [{1}] size was specified for [{0}]. Valid values are whole numbers from 8 to 15 inclusive. perMessageDeflate.unknownParameter=An unknown extension parameter [{0}] was defined Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java?rev=1625385&r1=1625384&r2=1625385&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java Tue Sep 16 20:41:29 2014 @@ -41,54 +41,118 @@ public class PerMessageDeflate implement public static final String NAME = "permessage-deflate"; - private boolean serverContextTakeover = true; - private boolean clientContextTakeover = true; - - private final Inflater inflator; + private final boolean serverContextTakeover; + private final int serverMaxWindowBits; + private final boolean clientContextTakeover; + private final int clientMaxWindowBits; + private final Inflater inflator = new Inflater(true); private final ByteBuffer readBuffer = ByteBuffer.allocate(8192); private Transformation next; private boolean skipDecompression = false; - PerMessageDeflate(List<Parameter> params) { + static PerMessageDeflate negotiate(List<List<Parameter>> preferences) { - for (Parameter param : params) { - if (SERVER_NO_CONTEXT_TAKEOVER.equals(param.getName())) { - serverContextTakeover = false; - } else if (CLIENT_NO_CONTEXT_TAKEOVER.equals(param.getName())) { - clientContextTakeover = false; - } else if (SERVER_MAX_WINDOW_BITS.equals(param.getName())) { - int bits = Integer.parseInt(param.getValue()); - if (bits < 8 || bits > 15) { - throw new IllegalArgumentException(sm.getString( - "perMessageDeflate.invalidWindowSize", - SERVER_MAX_WINDOW_BITS, Integer.valueOf(bits))); - } - // Java SE API (as of Java 8) does not expose the API to control - // the Window size so decline this option by not including it in - // the response - } else if (CLIENT_MAX_WINDOW_BITS.equals(param.getName())) { - if (param.getValue() != null) { - int bits = Integer.parseInt(param.getValue()); - if (bits < 8 || bits > 15) { + + // Accept the first preference that the server is able to support + for (List<Parameter> preference : preferences) { + boolean ok = true; + boolean serverContextTakeover = true; + int serverMaxWindowBits = -1; + boolean clientContextTakeover = true; + int clientMaxWindowBits = -1; + + for (Parameter param : preference) { + if (SERVER_NO_CONTEXT_TAKEOVER.equals(param.getName())) { + if (serverContextTakeover) { + serverContextTakeover = false; + } else { + // Duplicate definition + throw new IllegalArgumentException(sm.getString( + "perMessageDeflate.duplicateParameter", + SERVER_NO_CONTEXT_TAKEOVER )); + } + } else if (CLIENT_NO_CONTEXT_TAKEOVER.equals(param.getName())) { + if (clientContextTakeover) { + clientContextTakeover = false; + } else { + // Duplicate definition + throw new IllegalArgumentException(sm.getString( + "perMessageDeflate.duplicateParameter", + CLIENT_NO_CONTEXT_TAKEOVER )); + } + } else if (SERVER_MAX_WINDOW_BITS.equals(param.getName())) { + if (serverMaxWindowBits == -1) { + serverMaxWindowBits = Integer.parseInt(param.getValue()); + if (serverMaxWindowBits < 8 || serverMaxWindowBits > 15) { + throw new IllegalArgumentException(sm.getString( + "perMessageDeflate.invalidWindowSize", + SERVER_MAX_WINDOW_BITS, + Integer.valueOf(serverMaxWindowBits))); + } + // Java SE API (as of Java 8) does not expose the API to + // control the Window size. It is effectively hard-coded + // to 15 + if (serverMaxWindowBits != 15) { + ok = false; + break; + } + } else { + // Duplicate definition + throw new IllegalArgumentException(sm.getString( + "perMessageDeflate.duplicateParameter", + SERVER_MAX_WINDOW_BITS )); + } + } else if (CLIENT_MAX_WINDOW_BITS.equals(param.getName())) { + if (clientMaxWindowBits == -1) { + if (param.getValue() == null) { + // Hint to server that the client supports this + // option. Java SE API (as of Java 8) does not + // expose the API to control the Window size. It is + // effectively hard-coded to 15 + clientMaxWindowBits = 15; + } else { + clientMaxWindowBits = Integer.parseInt(param.getValue()); + if (clientMaxWindowBits < 8 || clientMaxWindowBits > 15) { + throw new IllegalArgumentException(sm.getString( + "perMessageDeflate.invalidWindowSize", + CLIENT_MAX_WINDOW_BITS, + Integer.valueOf(clientMaxWindowBits))); + } + } + // Not a problem is client specified a window size less + // than 15 since the server will always use a larger + // window it will still work. + } else { + // Duplicate definition throw new IllegalArgumentException(sm.getString( - "perMessageDeflate.invalidWindowSize", - CLIENT_MAX_WINDOW_BITS, Integer.valueOf(bits))); + "perMessageDeflate.duplicateParameter", + CLIENT_MAX_WINDOW_BITS )); } + } else { + // Unknown parameter + throw new IllegalArgumentException(sm.getString( + "perMessageDeflate.unknownParameter", param.getName())); } - // Java SE API (as of Java 8) does not expose the API to control - // the Window size so decline this option by not including it in - // the response - } else { - // Unknown parameter - throw new IllegalArgumentException(sm.getString( - "perMessageDeflate.unknownParameter", param.getName())); + } + if (ok) { + return new PerMessageDeflate(serverContextTakeover, serverMaxWindowBits, + clientContextTakeover, clientMaxWindowBits); } } + // Failed to negotiate agreeable terms + return null; + } - inflator = new Inflater(true); + private PerMessageDeflate(boolean serverContextTakeover, int serverMaxWindowBits, + boolean clientContextTakeover, int clientMaxWindowBits) { + this.serverContextTakeover = serverContextTakeover; + this.serverMaxWindowBits = serverMaxWindowBits; + this.clientContextTakeover = clientContextTakeover; + this.clientMaxWindowBits = clientMaxWindowBits; } + @Override public TransformationResult getMoreData(byte opCode, boolean fin, int rsv, ByteBuffer dest) throws IOException { @@ -184,9 +248,17 @@ public class PerMessageDeflate implement if (!serverContextTakeover) { params.add(new WsExtensionParameter(SERVER_NO_CONTEXT_TAKEOVER, null)); } + if (serverMaxWindowBits != -1) { + params.add(new WsExtensionParameter(SERVER_MAX_WINDOW_BITS, + Integer.toString(serverMaxWindowBits))); + } if (!clientContextTakeover) { params.add(new WsExtensionParameter(CLIENT_NO_CONTEXT_TAKEOVER, null)); } + if (clientMaxWindowBits != -1) { + params.add(new WsExtensionParameter(CLIENT_MAX_WINDOW_BITS, + Integer.toString(clientMaxWindowBits))); + } return result; } Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java?rev=1625385&r1=1625384&r2=1625385&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java Tue Sep 16 20:41:29 2014 @@ -16,6 +16,8 @@ */ package org.apache.tomcat.websocket; +import java.util.List; + import javax.websocket.Extension; public class TransformationFactory { @@ -30,9 +32,9 @@ public class TransformationFactory { return factory; } - public Transformation create(Extension ext) { - if (PerMessageDeflate.NAME.equals(ext.getName())) { - return new PerMessageDeflate(ext.getParameters()); + public Transformation create(String name, List<List<Extension.Parameter>> preferences) { + if (PerMessageDeflate.NAME.equals(name)) { + return PerMessageDeflate.negotiate(preferences); } // TODO i18n throw new IllegalArgumentException("Unsupported extension"); Modified: tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java?rev=1625385&r1=1625384&r2=1625385&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java Tue Sep 16 20:41:29 2014 @@ -22,6 +22,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Enumeration; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -224,10 +225,30 @@ public class UpgradeUtil { TransformationFactory factory = TransformationFactory.getInstance(); + LinkedHashMap<String,List<List<Extension.Parameter>>> extensionPreferences = + new LinkedHashMap<String,List<List<Extension.Parameter>>>(); + + // Result will likely be smaller than this List<Transformation> result = new ArrayList<Transformation>(negotiatedExtensions.size()); for (Extension extension : negotiatedExtensions) { - result.add(factory.create(extension)); + List<List<Extension.Parameter>> preferences = + extensionPreferences.get(extension.getName()); + + if (preferences == null) { + preferences = new ArrayList<List<Extension.Parameter>>(); + extensionPreferences.put(extension.getName(), preferences); + } + + preferences.add(extension.getParameters()); + } + + for (Map.Entry<String,List<List<Extension.Parameter>>> entry : + extensionPreferences.entrySet()) { + Transformation transformation = factory.create(entry.getKey(), entry.getValue()); + if (transformation != null) { + result.add(transformation); + } } return result; } Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1625385&r1=1625384&r2=1625385&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Tue Sep 16 20:41:29 2014 @@ -245,6 +245,12 @@ the resulting <code>IllegalStateException</code> in a manner consistent with the handling of an <code>IOException</code>. (markt) </fix> + <add> + Add support for the <code>permessage-deflate</code> extension. This is + currently limited to decompressing incoming messages on the server side. + It is expected that support will be extended to outgoing messages and to + the client side shortly. (markt) + </add> </changelog> </subsection> <subsection name="Web applications"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org