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/25 17:52:29 UTC
svn commit: r1605454 - in /tomcat/trunk: java/org/apache/tomcat/websocket/
java/org/apache/tomcat/websocket/server/ webapps/docs/
Author: markt
Date: Wed Jun 25 15:52:29 2014
New Revision: 1605454
URL: http://svn.apache.org/r1605454
Log:
Handle preference selection for multiple extension headers with the same name
Tighten up parameter validation for permessage-deflate
Modified:
tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java
tomcat/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java
tomcat/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java
tomcat/trunk/webapps/docs/changelog.xml
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=1605454&r1=1605453&r2=1605454&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties Wed Jun 25 15:52: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/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java?rev=1605454&r1=1605453&r2=1605454&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/PerMessageDeflate.java Wed Jun 25 15:52: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/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java?rev=1605454&r1=1605453&r2=1605454&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/TransformationFactory.java Wed Jun 25 15:52: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/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java?rev=1605454&r1=1605453&r2=1605454&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/server/UpgradeUtil.java Wed Jun 25 15:52: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;
@@ -213,10 +214,30 @@ public class UpgradeUtil {
TransformationFactory factory = TransformationFactory.getInstance();
+ LinkedHashMap<String,List<List<Extension.Parameter>>> extensionPreferences =
+ new LinkedHashMap<>();
+
+ // Result will likely be smaller than this
List<Transformation> result = new ArrayList<>(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<>();
+ 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/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1605454&r1=1605453&r2=1605454&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Wed Jun 25 15:52:29 2014
@@ -86,6 +86,16 @@
</fix>
</changelog>
</subsection>
+ <subsection name="WebSocket">
+ <changelog>
+ <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">
<changelog>
<fix>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org